初始化本地仓库
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\crontab;
|
||||
|
||||
use app\model\crontab\Crontab;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class CrontabDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Crontab::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取列表
|
||||
*
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param string $order
|
||||
* @param array $with
|
||||
* @param bool $search
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
$result = parent::selectList($where, $field, $page, $limit, $order, [], $search,$withoutScopes);
|
||||
|
||||
$systemCrontabLogDao = new CrontabLogDao();
|
||||
if (!empty($result)) {
|
||||
foreach ($result as $item) {
|
||||
$item->rule_name .= '';
|
||||
$item->logs = $systemCrontabLogDao->getModel()
|
||||
->where(['crontab_id' => $item->id])
|
||||
->orderBy('created_at', 'desc')
|
||||
->first();
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\crontab;
|
||||
|
||||
|
||||
use app\model\crontab\CrontabLog;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class CrontabLogDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return CrontabLog::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\dict;
|
||||
|
||||
use app\model\dict\Dict;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class DictDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Dict::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过code获取字典项
|
||||
*
|
||||
* @param string $code
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function findItemsByCode(string $code): array
|
||||
{
|
||||
$map1 = [
|
||||
'code' => $code,
|
||||
'enabled' => 1,
|
||||
];
|
||||
$result = parent::get($map1, ['*'], ['items']);
|
||||
if (empty($result)) {
|
||||
return [];
|
||||
}
|
||||
$items = $result->getAttribute('items');
|
||||
if (empty($items)) {
|
||||
return [];
|
||||
}
|
||||
$dataType = $result->getAttribute('data_type');
|
||||
foreach ($items as $item) {
|
||||
$value = $item->getAttribute('value');
|
||||
if ($dataType == 1) {
|
||||
$item->value = (string)$value; // 转换为字符串
|
||||
} elseif ($dataType == 2) {
|
||||
$item->value = (int)$value; // 转换为整型
|
||||
}
|
||||
}
|
||||
|
||||
return $items->makeHidden(['id'])->map(function ($item) {
|
||||
return [
|
||||
'dict_id' => $item->dict_id,
|
||||
'code' => $item->code,
|
||||
'label' => $item->label,
|
||||
'value' => $item->value,
|
||||
'sort' => $item->sort,
|
||||
'is_default' => $item->is_default,
|
||||
'other_class' => $item->other_class,
|
||||
'color' => $item->color,
|
||||
'created_date' => $item->created_date,
|
||||
'remark' => $item->remark,
|
||||
];
|
||||
})->toArray();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\dict;
|
||||
|
||||
use app\model\dict\DictItem;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 字典项
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class DictItemDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return DictItem::class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\gateway;
|
||||
|
||||
use app\model\gateway\RateLimiter;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 限流
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class RateLimiterDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return RateLimiter::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\gateway;
|
||||
|
||||
use app\model\gateway\RateRestrictions;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
*
|
||||
* 限访名单
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class RateRestrictionsDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return RateRestrictions::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\generator;
|
||||
|
||||
use app\model\generator\GeneratorColumn;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 代码生成器-表数据访问层
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class GeneratorColumnDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return GeneratorColumn::class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\generator;
|
||||
|
||||
use app\model\generator\GeneratorTable;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 代码生成器-表数据访问层
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class GeneratorTableDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return GeneratorTable::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取代码生成器-表详情
|
||||
*
|
||||
* @param $id
|
||||
*
|
||||
* @return \app\model\generator\GeneratorTable|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getDetail($id): GeneratorTable|null
|
||||
{
|
||||
$model = $this->get($id, ['*'], ['columns' => function ($query) {
|
||||
$query->orderBy('sort', 'asc')
|
||||
->orderBy('created_at', 'asc');
|
||||
}]);
|
||||
if (!empty($model)) {
|
||||
$basic = [
|
||||
'table_name' => $model->table_name ?? '',
|
||||
'table_content' => $model->table_content ?? '',
|
||||
'plugin_name' => $model->plugin_name ?? '',
|
||||
'module_name' => $model->module_name ?? '',
|
||||
'class_name' => $model->class_name ?? '',
|
||||
];
|
||||
|
||||
$softDeleteField = null;
|
||||
$softDeleteStatus = 0;
|
||||
|
||||
$orderField = null;
|
||||
$orderType = 0; // 0=没有排序, 1=正序, 2=倒序
|
||||
|
||||
if (!empty($model->columns)) {
|
||||
foreach ($model->columns as $column) {
|
||||
// 检查is_delete字段是否为1
|
||||
if (isset($column->is_delete) && $column->is_delete == 1) {
|
||||
$softDeleteField = $column->column_name ?? '';
|
||||
$softDeleteStatus = 1; // 启用软删除
|
||||
}
|
||||
|
||||
// 检查is_order字段是否为1
|
||||
if (isset($column->is_order) && $column->is_order == 1) {
|
||||
$orderField = $column->column_name ?? '';
|
||||
$orderType = $model->order_type ?? 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果配置中明确指定了删除字段名,则优先使用配置
|
||||
if (!empty($model->delete_column_name)) {
|
||||
$softDeleteField = $model->delete_column_name;
|
||||
$softDeleteStatus = 1;
|
||||
}
|
||||
|
||||
// 如果配置中明确指定了排序字段名,则优先使用配置
|
||||
if (!empty($model->order_column_name)) {
|
||||
$orderField = $model->order_column_name;
|
||||
$orderType = $model->order_type ?? 0;
|
||||
}
|
||||
|
||||
$relations = [];
|
||||
if (!empty($model->relations)) {
|
||||
// 尝试解析JSON格式的relations
|
||||
$decodedRelations = json_decode($model->relations, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decodedRelations)) {
|
||||
$relations = $decodedRelations;
|
||||
} else {
|
||||
// 如果不是JSON格式,尝试其他解析方式或返回空数组
|
||||
$relations = [];
|
||||
}
|
||||
}
|
||||
|
||||
$config = [
|
||||
'is_delete' => $softDeleteStatus,
|
||||
'delete_column_name' => $softDeleteField ?? '',
|
||||
'edit_type' => $model->edit_type ?? 0,
|
||||
'order_column_name' => $orderField ?? '',
|
||||
'order_type' => $orderType,
|
||||
'parent_menu' => $model->parent_menu ?? '',
|
||||
'plugin_name' => $model->plugin_name ?? '',
|
||||
'relations' => $relations, // 添加转换后的relations数组
|
||||
];
|
||||
$model->setAttribute('basic', $basic);
|
||||
$model->setAttribute('config', $config);
|
||||
$model->setAttribute('relations', $relations);
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\logs;
|
||||
|
||||
use app\model\logs\LoginLog;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class LoginLogDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return LoginLog::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\logs;
|
||||
|
||||
use app\model\logs\OperateLog;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 行为日志Dao
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class OperateLogDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return OperateLog::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作日志列表
|
||||
*
|
||||
* @param array $where
|
||||
* @param string|array $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param string $order
|
||||
* @param array $with
|
||||
* @param bool $search
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
//注意不要输出result 要不然深度递归会卡死
|
||||
return parent::selectList($where, $field, $page, $limit, $order, $with, $search, $withoutScopes)->makeHidden(['result']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use core\base\BaseDao;
|
||||
use app\enum\common\EnabledStatus;
|
||||
use app\model\member\MemberAddress;
|
||||
|
||||
|
||||
/**
|
||||
* 会员地址数据访问对象
|
||||
*/
|
||||
class MemberAddressDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberAddress::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认地址
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function setDefault(MemberAddress $address): bool
|
||||
{
|
||||
// 先将该会员的所有地址设置为非默认
|
||||
$this->query()->where('member_id', $address->member_id)->update(['is_default' => 0]);
|
||||
|
||||
// 将当前地址设置为默认
|
||||
$address->is_default = 1;
|
||||
return $address->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员默认地址
|
||||
*/
|
||||
public function getDefaultAddress(int $memberId): ?MemberAddress
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('is_default', 1)
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员地址列表
|
||||
*/
|
||||
public function getMemberAddresses(int $memberId): array
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->orderBy('is_default', 'desc')
|
||||
->orderBy('updated_at', 'desc')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或更新地址
|
||||
*/
|
||||
public function saveAddress(int $memberId, array $data): MemberAddress
|
||||
{
|
||||
$addressId = $data['id'] ?? 0;
|
||||
|
||||
if ($addressId) {
|
||||
// 更新地址
|
||||
$address = $this->find($addressId);
|
||||
if (!$address || $address->member_id != $memberId) {
|
||||
throw new \Exception('地址不存在或无权限');
|
||||
}
|
||||
} else {
|
||||
// 创建新地址
|
||||
$address = $this->getModel();
|
||||
$address->member_id = $memberId;
|
||||
$address->enabled = EnabledStatus::ENABLED->value;
|
||||
}
|
||||
|
||||
// 填充数据
|
||||
$fillable = [
|
||||
'name',
|
||||
'phone',
|
||||
'province',
|
||||
'city',
|
||||
'district',
|
||||
'address',
|
||||
'zipcode',
|
||||
];
|
||||
|
||||
foreach ($fillable as $field) {
|
||||
if (isset($data[$field])) {
|
||||
$address->{$field} = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
// 设置默认地址
|
||||
if (isset($data['is_default']) && $data['is_default'] == 1) {
|
||||
// 先将其他地址设置为非默认
|
||||
$this->query()
|
||||
->where('member_id', $memberId)
|
||||
->update(['is_default' => 0]);
|
||||
$address->is_default = 1;
|
||||
} elseif (!$addressId) {
|
||||
// 新地址默认为非默认
|
||||
$address->is_default = 0;
|
||||
}
|
||||
|
||||
$address->save();
|
||||
return $address;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除地址
|
||||
*/
|
||||
public function deleteAddress(MemberAddress $address): bool
|
||||
{
|
||||
$address->enabled = EnabledStatus::DISABLED->value;
|
||||
return $address->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证地址数据
|
||||
*/
|
||||
public function validateAddress(array $data): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if (empty($data['name'])) {
|
||||
$errors[] = '收货人姓名不能为空';
|
||||
}
|
||||
|
||||
if (empty($data['phone'])) {
|
||||
$errors[] = '联系电话不能为空';
|
||||
} elseif (!preg_match('/^1[3-9]\d{9}$/', $data['phone'])) {
|
||||
$errors[] = '联系电话格式不正确';
|
||||
}
|
||||
|
||||
if (empty($data['province'])) {
|
||||
$errors[] = '省份不能为空';
|
||||
}
|
||||
|
||||
if (empty($data['city'])) {
|
||||
$errors[] = '城市不能为空';
|
||||
}
|
||||
|
||||
if (empty($data['district'])) {
|
||||
$errors[] = '区县不能为空';
|
||||
}
|
||||
|
||||
if (empty($data['address'])) {
|
||||
$errors[] = '详细地址不能为空';
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员地址数量
|
||||
*/
|
||||
public function getMemberAddressCount(int $memberId): int
|
||||
{
|
||||
return $this->count([
|
||||
'member_id' => $memberId,
|
||||
'enabled' => EnabledStatus::ENABLED->value
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use core\base\BaseDao;
|
||||
use app\model\member\MemberBill;
|
||||
use app\enum\member\BillType;
|
||||
use app\enum\member\BillStatus;
|
||||
|
||||
/**
|
||||
* 会员账单DAO
|
||||
*/
|
||||
class MemberBillDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberBill::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录账单
|
||||
*/
|
||||
public function record(int $memberId, int $type, int $category, float $amount, string $description, string $orderSn = '', int $status = BillStatus::SUCCESS->value): MemberBill
|
||||
{
|
||||
$bill = $this->getModel();
|
||||
$bill->member_id = $memberId;
|
||||
$bill->type = $type;
|
||||
$bill->category = $category;
|
||||
$bill->amount = $amount;
|
||||
$bill->description = $description;
|
||||
$bill->order_sn = $orderSn;
|
||||
$bill->status = $status;
|
||||
$bill->save();
|
||||
|
||||
return $bill;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员账单列表
|
||||
*/
|
||||
public function getMemberBills(int $memberId, array $params = []): array
|
||||
{
|
||||
$query = $this->query()->where('member_id', $memberId);
|
||||
|
||||
// 类型筛选
|
||||
if (isset($params['type'])) {
|
||||
$query->where('type', $params['type']);
|
||||
}
|
||||
|
||||
// 分类筛选
|
||||
if (isset($params['category'])) {
|
||||
$query->where('category', $params['category']);
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if (isset($params['start_date']) && isset($params['end_date'])) {
|
||||
$query->whereBetween('created_at', [
|
||||
$params['start_date'] . ' 00:00:00',
|
||||
$params['end_date'] . ' 23:59:59'
|
||||
]);
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (isset($params['status'])) {
|
||||
$query->where('status', $params['status']);
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
$page = $params['page'] ?? 1;
|
||||
$limit = $params['limit'] ?? 10;
|
||||
|
||||
return $query->orderBy('created_at', 'desc')
|
||||
->paginate($limit, ['*'], 'page', $page)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员账单统计
|
||||
*/
|
||||
public function getMemberBillStats(int $memberId, string $startDate, string $endDate): array
|
||||
{
|
||||
$stats = $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->whereBetween('created_at', [
|
||||
$startDate . ' 00:00:00',
|
||||
$endDate . ' 23:59:59'
|
||||
])
|
||||
->selectRaw('type, category, SUM(amount) as total_amount, COUNT(*) as count')
|
||||
->groupBy('type', 'category')
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$result = [
|
||||
'total_income' => 0,
|
||||
'total_expense' => 0,
|
||||
'category_stats' => [],
|
||||
];
|
||||
|
||||
foreach ($stats as $stat) {
|
||||
if ($stat['type'] == BillType::INCOME->value) {
|
||||
$result['total_income'] += $stat['total_amount'];
|
||||
} else {
|
||||
$result['total_expense'] += $stat['total_amount'];
|
||||
}
|
||||
|
||||
$result['category_stats'][$stat['category']] = [
|
||||
'total_amount' => $stat['total_amount'],
|
||||
'count' => $stat['count'],
|
||||
];
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最新账单
|
||||
*/
|
||||
public function getLatestBill(int $memberId): ?MemberBill
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->orderBy('created_at', 'desc')
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取今日账单统计
|
||||
*/
|
||||
public function getTodayStats(int $memberId): array
|
||||
{
|
||||
$today = date('Y-m-d');
|
||||
return $this->getMemberBillStats($memberId, $today, $today);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本月账单统计
|
||||
*/
|
||||
public function getMonthStats(int $memberId): array
|
||||
{
|
||||
$firstDay = date('Y-m-01');
|
||||
$lastDay = date('Y-m-t');
|
||||
return $this->getMemberBillStats($memberId, $firstDay, $lastDay);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单号更新账单状态
|
||||
*/
|
||||
public function updateStatusByOrderSn(string $orderSn, int $status): bool
|
||||
{
|
||||
return $this->query()
|
||||
->where('order_sn', $orderSn)
|
||||
->update(['status' => $status]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取账单总数
|
||||
*/
|
||||
public function getCount(int $memberId, array $params = []): int
|
||||
{
|
||||
$query = $this->query()->where('member_id', $memberId);
|
||||
|
||||
// 类型筛选
|
||||
if (isset($params['type'])) {
|
||||
$query->where('type', $params['type']);
|
||||
}
|
||||
|
||||
// 分类筛选
|
||||
if (isset($params['category'])) {
|
||||
$query->where('category', $params['category']);
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (isset($params['status'])) {
|
||||
$query->where('status', $params['status']);
|
||||
}
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use app\scope\global\AccessPermissionScope;
|
||||
use app\service\admin\member\MemberTagRelationService;
|
||||
use app\service\admin\member\MemberTagService;
|
||||
use core\base\BaseDao;
|
||||
use app\model\member\Member;
|
||||
use madong\query\QueryBuilderHelper;
|
||||
use madong\query\QueryParamConverter;
|
||||
use madong\query\enum\ParamFormat;
|
||||
use InvalidArgumentException;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 会员DAO
|
||||
*/
|
||||
class MemberDao extends BaseDao
|
||||
{
|
||||
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Member::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃用户
|
||||
*
|
||||
* @param int $limit
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getActiveUsers(int $limit = 5): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
// last_time 字段存储为时间戳格式(dateFormat = 'U')
|
||||
// 计算30天前的时间戳
|
||||
$thirtyDaysAgo = time() - (30 * 24 * 60 * 60);
|
||||
|
||||
return $this->query()->withoutGlobalScope(AccessPermissionScope::class)
|
||||
->where('last_login_time', '>=', $thirtyDaysAgo)
|
||||
->orderBy('last_login_time', 'desc')
|
||||
->limit($limit)
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据标签ID获取用户列表
|
||||
*
|
||||
* @param array $where 查询条件,支持多种格式自动转换
|
||||
* @param string $field 查询字段
|
||||
* @param int $page 页码
|
||||
* @param int $limit 每页数量
|
||||
* @param array|null $withoutScopes 移除的作用域
|
||||
*
|
||||
* @return array ['total' => int, 'items' => array]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getUsersListByTagId(array $where, string $field, int $page, int $limit, ?array $withoutScopes = null): array
|
||||
{
|
||||
try {
|
||||
//传入的是标准filters格式
|
||||
$tagId = QueryParamConverter::getValue($where, 'tag_id');
|
||||
if (!$tagId) {
|
||||
throw new InvalidArgumentException("Tag ID is required.");
|
||||
}
|
||||
// 从 where 中移除 tag_id
|
||||
$where = QueryParamConverter::removeFilter($where, 'tag_id');
|
||||
|
||||
$query = $this->query()->with(['tags' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}])
|
||||
->whereHas('tags', function ($query) use ($tagId) {
|
||||
$query->where('id', $tagId);
|
||||
});
|
||||
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
|
||||
// 应用剩余的 filters 条件
|
||||
if (!empty($where['filters'])) {
|
||||
QueryBuilderHelper::applyFiltersToQuery($query, $where['filters']);
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
|
||||
$items = $query->when($page > 0 && $limit > 0, function ($query) use ($page, $limit) {
|
||||
return $query->skip(($page - 1) * $limit)->take($limit);
|
||||
})
|
||||
->select(explode(',', $field))
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
return compact('total', 'items');
|
||||
} catch (\Throwable $e) {
|
||||
throw new \Exception("获取用户列表失败: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 排除标签ID-用户列表
|
||||
*
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return array
|
||||
* @throws \ReflectionException|\Exception
|
||||
*/
|
||||
public function getUsersExcludingTag(array $where, string $field, int $page, int $limit, ?array $withoutScopes = null): array
|
||||
{
|
||||
|
||||
// 使用 QueryParamConverter 获取 tag_id 值(自动识别格式)
|
||||
$tagId = QueryParamConverter::getValue($where, 'tag_id');
|
||||
|
||||
// 从 where 中移除 tag_id
|
||||
$where = QueryParamConverter::removeFilter($where, 'tag_id');
|
||||
// 获取排除的用户ID列表
|
||||
if ($tagId) {
|
||||
$memberTagRelationService = Container::make(MemberTagRelationService::class);
|
||||
|
||||
$excludedMemberUserIds = $memberTagRelationService->getColumn(['tag_id' => $tagId], 'member_id');
|
||||
} else {
|
||||
$excludedMemberUserIds = [];
|
||||
}
|
||||
|
||||
$query = $this->getModel()->whereNotIn('id', $excludedMemberUserIds)
|
||||
->with(['tags' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}]);
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
|
||||
// 应用剩余的 filters 条件
|
||||
if (!empty($where['filters'])) {
|
||||
QueryBuilderHelper::applyFiltersToQuery($query, $where['filters']);
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
|
||||
// 如果没有用户,则返回空结果
|
||||
if ($total === 0) {
|
||||
return ['total' => 0, 'items' => []];
|
||||
}
|
||||
|
||||
$items = $query->when($page > 0 && $limit > 0, function ($query) use ($page, $limit) {
|
||||
return $query->skip(($page - 1) * $limit)->take($limit);
|
||||
})->select($field)->get()->toArray();
|
||||
|
||||
return compact('total', 'items');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use core\base\BaseDao;
|
||||
use app\enum\common\EnabledStatus;
|
||||
use app\model\member\MemberLevel;
|
||||
|
||||
/**
|
||||
* 会员等级数据访问对象
|
||||
*/
|
||||
class MemberLevelDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberLevel::class;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 根据积分获取等级
|
||||
// */
|
||||
// public function getLevelByPoints(int $points): ?MemberLevel
|
||||
// {
|
||||
// return $this->query()
|
||||
// ->where('enabled', EnabledStatus::ENABLED->value)
|
||||
// ->where('min_points', '<=', $points)
|
||||
// ->where(function ($query) use ($points) {
|
||||
// $query->where('max_points', '>=', $points)
|
||||
// ->orWhere('max_points', 0);
|
||||
// })
|
||||
// ->orderBy('level', 'desc')
|
||||
// ->first();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取下一个等级
|
||||
// */
|
||||
// public function getNextLevel(MemberLevel $currentLevel): ?MemberLevel
|
||||
// {
|
||||
// return $this->query()
|
||||
// ->where('enabled', EnabledStatus::ENABLED->value)
|
||||
// ->where('level', '>', $currentLevel->level)
|
||||
// ->orderBy('level', 'asc')
|
||||
// ->first();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取等级进度
|
||||
// */
|
||||
// public function getLevelProgress(MemberLevel $currentLevel, int $points): array
|
||||
// {
|
||||
// $nextLevel = $this->getNextLevel($currentLevel);
|
||||
//
|
||||
// if (!$nextLevel) {
|
||||
// return [
|
||||
// 'current_points' => $points,
|
||||
// 'next_points' => 0,
|
||||
// 'progress' => 100,
|
||||
// 'is_max_level' => true,
|
||||
// ];
|
||||
// }
|
||||
//
|
||||
// $currentRange = $currentLevel->max_points - $currentLevel->min_points;
|
||||
// $currentPoints = $points - $currentLevel->min_points;
|
||||
// $progress = $currentRange > 0 ? round(($currentPoints / $currentRange) * 100, 2) : 0;
|
||||
//
|
||||
// return [
|
||||
// 'current_points' => $points,
|
||||
// 'next_points' => $nextLevel->min_points,
|
||||
// 'progress' => min($progress, 100),
|
||||
// 'is_max_level' => false,
|
||||
// ];
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use app\model\member\MemberPoints;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 会员积分DAO类
|
||||
*/
|
||||
class MemberPointsDao extends BaseDao
|
||||
{
|
||||
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
public function setModel(): string
|
||||
{
|
||||
return MemberPoints::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员积分记录
|
||||
*/
|
||||
public function getMemberPoints(int|string $memberId, int $limit = 20): array
|
||||
{
|
||||
return $this->selectList(
|
||||
[['member_id', '=', $memberId]],
|
||||
'*',
|
||||
0, $limit,
|
||||
'created_at desc'
|
||||
)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员积分余额
|
||||
*/
|
||||
public function getMemberBalance(int|string $memberId): float
|
||||
{
|
||||
$lastRecord = $this->query()
|
||||
->where('member_id', '=', $memberId)
|
||||
->orderBy('created_at', 'desc')
|
||||
->first();
|
||||
return $lastRecord ? $lastRecord->balance : 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use app\service\api\member\MemberService;
|
||||
use core\base\BaseDao;
|
||||
use app\model\member\MemberSign;
|
||||
use support\Container;
|
||||
|
||||
/**
|
||||
* 会员签到DAO
|
||||
*/
|
||||
class MemberSignDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberSign::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建签到记录
|
||||
*/
|
||||
public function created(int $memberId, string $signDate, int $points, int $continuousDays, array $deviceInfo = []): MemberSign
|
||||
{
|
||||
$sign = new MemberSign();
|
||||
$sign->member_id = $memberId;
|
||||
$sign->sign_date = $signDate;
|
||||
$sign->points = $points;
|
||||
$sign->continuous_days = $continuousDays;
|
||||
|
||||
$sign->save();
|
||||
|
||||
return $sign;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查今日是否已签到
|
||||
*/
|
||||
public function isSignedToday(int|string $memberId, string $today): bool
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('sign_date', $today)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近一次签到记录
|
||||
*/
|
||||
public function getLastSign(int|string $memberId): ?MemberSign
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->orderBy('sign_date', 'desc')
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员签到记录
|
||||
*/
|
||||
public function getMemberSignRecords(int|string $memberId, int $days = 30): array
|
||||
{
|
||||
$startDate = date('Y-m-d', strtotime("-$days days"));
|
||||
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('sign_date', '>=', $startDate)
|
||||
->orderBy('sign_date', 'asc')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本月签到天数
|
||||
*/
|
||||
public function getMonthSignDays(int|string $memberId): int
|
||||
{
|
||||
$firstDay = date('Y-m-01');
|
||||
$lastDay = date('Y-m-t');
|
||||
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->whereBetween('sign_date', [$firstDay, $lastDay])
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连续签到排行榜
|
||||
*/
|
||||
public function getContinuousSignRanking(int $limit = 10): array
|
||||
{
|
||||
return $this->query()
|
||||
->selectRaw('member_id, MAX(continuous_days) as max_continuous_days')
|
||||
->groupBy('member_id')
|
||||
->orderBy('max_continuous_days', 'desc')
|
||||
->limit($limit)
|
||||
->with('member')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签到统计
|
||||
*/
|
||||
public function getSignStatistics(int|string $memberId): array
|
||||
{
|
||||
$totalSignDays = $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->count();
|
||||
$monthSignDays = $this->getMonthSignDays($memberId);
|
||||
$continuousDays = $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->orderBy('sign_date', 'desc')
|
||||
->value('continuous_days') ?? 0;
|
||||
|
||||
// 获取用户积分
|
||||
$points = Container::make(MemberService::class)
|
||||
->get($memberId)
|
||||
->points ?? 0;
|
||||
|
||||
return [
|
||||
'total_sign_days' => $totalSignDays,
|
||||
'month_sign_days' => $monthSignDays,
|
||||
'continuous_days' => $continuousDays,
|
||||
'today_signed' => $this->isSignedToday($memberId, date('Y-m-d')),
|
||||
'points' => $points
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取年度签到统计
|
||||
*/
|
||||
public function getYearSignStatistics(int|string $memberId, int $year = null): array
|
||||
{
|
||||
if ($year === null) {
|
||||
$year = date('Y');
|
||||
}
|
||||
|
||||
$startDate = $year . '-01-01';
|
||||
$endDate = $year . '-12-31';
|
||||
|
||||
$totalSignDays = $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->whereBetween('sign_date', [$startDate, $endDate])
|
||||
->count();
|
||||
|
||||
// 统计每个月的签到天数
|
||||
$monthlyStats = [];
|
||||
for ($month = 1; $month <= 12; $month++) {
|
||||
$monthStart = $year . '-' . str_pad((string)$month, 2, '0', STR_PAD_LEFT) . '-01';
|
||||
$monthEnd = $year . '-' . str_pad((string)$month, 2, '0', STR_PAD_LEFT) . '-' . date('t', strtotime($monthStart));
|
||||
|
||||
$monthlyStats[$month] = $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->whereBetween('sign_date', [$monthStart, $monthEnd])
|
||||
->count();
|
||||
}
|
||||
|
||||
return [
|
||||
'year' => $year,
|
||||
'total_sign_days' => $totalSignDays,
|
||||
'monthly_stats' => $monthlyStats
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取周签到统计
|
||||
*/
|
||||
public function getWeekSignStatistics(int|string $memberId): array
|
||||
{
|
||||
$weekStart = date('Y-m-d', strtotime('this week'));
|
||||
$weekEnd = date('Y-m-d', strtotime('this week +6 days'));
|
||||
|
||||
$signDays = $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->whereBetween('sign_date', [$weekStart, $weekEnd])
|
||||
->count();
|
||||
|
||||
return [
|
||||
'week_start' => $weekStart,
|
||||
'week_end' => $weekEnd,
|
||||
'sign_days' => $signDays,
|
||||
'total_days' => 7
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取连续签到奖励
|
||||
*/
|
||||
public function getContinuousSignReward(int|string $continuousDays): int
|
||||
{
|
||||
// 连续签到奖励规则
|
||||
$rewards = [
|
||||
7 => 50, // 连续7天奖励50积分
|
||||
14 => 100, // 连续14天奖励100积分
|
||||
30 => 200, // 连续30天奖励200积分
|
||||
];
|
||||
|
||||
return $rewards[$continuousDays] ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取签到日历
|
||||
*/
|
||||
public function getSignCalendar(int|string $memberId, int $year, int $month): array
|
||||
{
|
||||
$firstDay = $year . '-' . str_pad((string)$month, 2, '0', STR_PAD_LEFT) . '-01';
|
||||
$lastDay = $year . '-' . str_pad((string)$month, 2, '0', STR_PAD_LEFT) . '-' . date('t', strtotime($firstDay));
|
||||
|
||||
$signs = $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->whereBetween('sign_date', [$firstDay, $lastDay])
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
$calendar = [];
|
||||
foreach ($signs as $sign) {
|
||||
$calendar[] = $sign['sign_date'];
|
||||
}
|
||||
|
||||
return $calendar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量获取会员签到统计
|
||||
*/
|
||||
public function batchGetSignStatistics(array $memberIds): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($memberIds as $memberId) {
|
||||
$result[$memberId] = $this->getSignStatistics($memberId);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期签到记录(保留最近一年的记录)
|
||||
*/
|
||||
public function cleanExpiredRecords(): int
|
||||
{
|
||||
$oneYearAgo = date('Y-m-d', strtotime('-1 year'));
|
||||
return $this->query()
|
||||
->where('sign_date', '<', $oneYearAgo)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use core\base\BaseDao;
|
||||
use app\enum\common\EnabledStatus;
|
||||
use app\model\member\MemberTag;
|
||||
|
||||
/**
|
||||
* 会员标签数据访问对象
|
||||
*/
|
||||
class MemberTagDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberTag::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签使用统计
|
||||
*/
|
||||
public function getUsageCount(int $tagId): int
|
||||
{
|
||||
$relationDao = new MemberTagRelationDao();
|
||||
return $relationDao->getTagMemberCount($tagId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取启用的标签列表
|
||||
*/
|
||||
public function getEnabledTags(): array
|
||||
{
|
||||
return $this->query()
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->orderBy('sort', 'asc')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签详情
|
||||
*/
|
||||
public function getTagById(int $tagId): ?MemberTag
|
||||
{
|
||||
return $this->find($tagId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取标签列表
|
||||
*/
|
||||
public function getTagList(array $params = []): array
|
||||
{
|
||||
$query = $this->query();
|
||||
|
||||
// 状态筛选
|
||||
if (isset($params['enabled'])) {
|
||||
$query->where('enabled', $params['enabled']);
|
||||
}
|
||||
|
||||
// 关键词搜索
|
||||
if (isset($params['keyword'])) {
|
||||
$query->where('name', 'like', '%' . $params['keyword'] . '%')
|
||||
->orWhere('description', 'like', '%' . $params['keyword'] . '%');
|
||||
}
|
||||
|
||||
return $query->orderBy('sort', 'asc')
|
||||
->orderBy('id', 'desc')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或更新标签
|
||||
*/
|
||||
public function saveTag(array $data): MemberTag
|
||||
{
|
||||
$tagId = $data['id'] ?? 0;
|
||||
|
||||
if ($tagId) {
|
||||
// 更新标签
|
||||
$tag = $this->find($tagId);
|
||||
if (!$tag) {
|
||||
throw new \Exception('标签不存在');
|
||||
}
|
||||
} else {
|
||||
// 创建新标签
|
||||
$tag = $this->getModel();
|
||||
$tag->enabled = EnabledStatus::ENABLED->value;
|
||||
}
|
||||
|
||||
// 填充数据
|
||||
$fillable = [
|
||||
'name',
|
||||
'color',
|
||||
'description',
|
||||
'sort',
|
||||
'enabled',
|
||||
];
|
||||
|
||||
foreach ($fillable as $field) {
|
||||
if (isset($data[$field])) {
|
||||
$tag->{$field} = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
$tag->save();
|
||||
return $tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除标签
|
||||
*/
|
||||
public function deleteTag(int $tagId): bool
|
||||
{
|
||||
// 检查标签是否被使用
|
||||
$usageCount = $this->getUsageCount($tagId);
|
||||
if ($usageCount > 0) {
|
||||
throw new \Exception('该标签已被使用,无法删除');
|
||||
}
|
||||
|
||||
$tag = $this->find($tagId);
|
||||
if (!$tag) {
|
||||
throw new \Exception('标签不存在');
|
||||
}
|
||||
|
||||
return $tag->delete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
|
||||
use app\model\member\MemberTagPermission;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class MemberTagPermissionDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberTagPermission::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use core\base\BaseDao;
|
||||
use app\model\member\MemberTagRelation;
|
||||
|
||||
/**
|
||||
* 会员标签关系数据访问对象
|
||||
*/
|
||||
class MemberTagRelationDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberTagRelation::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存标签关系(自动过滤已存在的记录)
|
||||
*
|
||||
* @param array $data 标签关系数据 [['member_id' => xx, 'tag_id' => yy], ...]
|
||||
* @return array 返回结果 ['saved' => int, 'duplicates' => int, 'failed' => int, 'errors' => array]
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function batchSave(array $data): array
|
||||
{
|
||||
if (empty($data)) {
|
||||
return [
|
||||
'saved' => 0,
|
||||
'duplicates' => 0,
|
||||
'failed' => 0,
|
||||
'errors' => []
|
||||
];
|
||||
}
|
||||
|
||||
// 验证数据格式
|
||||
foreach ($data as $index => $item) {
|
||||
if (!isset($item['member_id']) || !isset($item['tag_id'])) {
|
||||
throw new \Exception("数据格式错误: 第 {$index} 条记录缺少 member_id 或 tag_id");
|
||||
}
|
||||
}
|
||||
|
||||
// 检查已存在的记录
|
||||
$existing = $this->getExistingRelations($data);
|
||||
|
||||
// 过滤掉已存在的记录
|
||||
$newData = $this->filterExistingRelations($data, $existing);
|
||||
|
||||
$duplicates = count($data) - count($newData);
|
||||
|
||||
if (empty($newData)) {
|
||||
return [
|
||||
'saved' => 0,
|
||||
'duplicates' => $duplicates,
|
||||
'failed' => 0,
|
||||
'errors' => []
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用批量插入(性能更好)
|
||||
$result = $this->getModel()->insert($newData);
|
||||
|
||||
if (!$result) {
|
||||
throw new \Exception('批量插入失败');
|
||||
}
|
||||
|
||||
return [
|
||||
'saved' => count($newData),
|
||||
'duplicates' => $duplicates,
|
||||
'failed' => 0,
|
||||
'errors' => []
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
// 检查是否是唯一键冲突
|
||||
if ($this->isDuplicateEntryError($e)) {
|
||||
return [
|
||||
'saved' => 0,
|
||||
'duplicates' => count($data),
|
||||
'failed' => 0,
|
||||
'errors' => ['部分记录可能已存在']
|
||||
];
|
||||
}
|
||||
|
||||
throw new \Exception("批量保存失败: " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取已存在的关联关系
|
||||
*
|
||||
* @param array $data
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function getExistingRelations(array $data): array
|
||||
{
|
||||
if (empty($data)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$query = $this->query();
|
||||
|
||||
// 构建查询:WHERE (member_id = x AND tag_id = y) OR (member_id = a AND tag_id = b) ...
|
||||
$query->where(function($q) use ($data) {
|
||||
foreach ($data as $item) {
|
||||
$q->orWhere(function($subQ) use ($item) {
|
||||
$subQ->where('member_id', $item['member_id'])
|
||||
->where('tag_id', $item['tag_id']);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return $query->get()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤掉已存在的关联关系
|
||||
*
|
||||
* @param array $data
|
||||
* @param array $existing
|
||||
* @return array
|
||||
*/
|
||||
protected function filterExistingRelations(array $data, array $existing): array
|
||||
{
|
||||
if (empty($existing)) {
|
||||
return $data;
|
||||
}
|
||||
|
||||
// 构建已存在记录的 map
|
||||
$existingMap = [];
|
||||
foreach ($existing as $record) {
|
||||
$key = $record['member_id'] . ':' . $record['tag_id'];
|
||||
$existingMap[$key] = true;
|
||||
}
|
||||
|
||||
// 过滤掉已存在的记录
|
||||
$newData = [];
|
||||
foreach ($data as $item) {
|
||||
$key = $item['member_id'] . ':' . $item['tag_id'];
|
||||
if (!isset($existingMap[$key])) {
|
||||
$newData[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $newData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是重复记录错误
|
||||
*
|
||||
* @param \Throwable $e
|
||||
* @return bool
|
||||
*/
|
||||
protected function isDuplicateEntryError(\Throwable $e): bool
|
||||
{
|
||||
$message = $e->getMessage();
|
||||
return str_contains($message, 'Duplicate entry') ||
|
||||
str_contains($message, '1062') ||
|
||||
str_contains($message, 'Integrity constraint violation');
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 添加标签关系
|
||||
// */
|
||||
// public function addTagRelation(int $memberId, int $tagId): bool
|
||||
// {
|
||||
// $exists = $this->query()
|
||||
// ->where('member_id', $memberId)
|
||||
// ->where('tag_id', $tagId)
|
||||
// ->exists();
|
||||
//
|
||||
// if (!$exists) {
|
||||
// $relation = $this->getModel();
|
||||
// $relation->member_id = $memberId;
|
||||
// $relation->tag_id = $tagId;
|
||||
// return $relation->save();
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 移除标签关系
|
||||
// */
|
||||
// public function removeTagRelation(int $memberId, int $tagId): bool
|
||||
// {
|
||||
// return $this->query()
|
||||
// ->where('member_id', $memberId)
|
||||
// ->where('tag_id', $tagId)
|
||||
// ->delete() > 0;
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取会员的标签ID列表
|
||||
// */
|
||||
// public function getMemberTagIds(int $memberId): array
|
||||
// {
|
||||
// return $this->query()
|
||||
// ->where('member_id', $memberId)
|
||||
// ->pluck('tag_id')
|
||||
// ->toArray();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 获取标签的会员数量
|
||||
// */
|
||||
// public function getTagMemberCount(int $tagId): int
|
||||
// {
|
||||
// return $this->count(['tag_id' => $tagId]);
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use app\enum\common\EnabledStatus;
|
||||
use core\base\BaseDao;
|
||||
use app\model\member\MemberThirdParty;
|
||||
use app\model\member\Member;
|
||||
|
||||
/**
|
||||
* 会员第三方登录DAO
|
||||
*/
|
||||
class MemberThirdPartyDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberThirdParty::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定第三方账号
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function bind(int|string $memberId, int $platform, string $openid, array $data = []): MemberThirdParty
|
||||
{
|
||||
// 检查是否已绑定
|
||||
$thirdParty = $this->findByMemberAndPlatform($memberId, $platform);
|
||||
|
||||
if ($thirdParty) {
|
||||
// 更新绑定信息
|
||||
foreach ($data as $key => $value) {
|
||||
if (in_array($key, $thirdParty->fillable)) {
|
||||
$thirdParty->$key = $value;
|
||||
}
|
||||
}
|
||||
$thirdParty->openid = $openid;
|
||||
$thirdParty->enabled = EnabledStatus::ENABLED->value;
|
||||
$thirdParty->save();
|
||||
return $thirdParty;
|
||||
}
|
||||
|
||||
// 创建新绑定
|
||||
$thirdParty = new MemberThirdParty();
|
||||
$thirdParty->member_id = $memberId;
|
||||
$thirdParty->platform = $platform;
|
||||
$thirdParty->openid = $openid;
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
if (in_array($key, $thirdParty->fillable)) {
|
||||
$thirdParty->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$thirdParty->enabled = EnabledStatus::ENABLED->value;
|
||||
$thirdParty->save();
|
||||
|
||||
return $thirdParty;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据会员ID和平台查找第三方账号
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function findByMemberAndPlatform(int|string $memberId, int $platform): ?MemberThirdParty
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('platform', $platform)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据第三方账号查找会员
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function findMemberByThirdParty(int $platform, string $openid): ?Member
|
||||
{
|
||||
$thirdParty = $this->query()
|
||||
->where('platform', $platform)
|
||||
->where('openid', $openid)
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->with('member')
|
||||
->first();
|
||||
|
||||
/** @var TYPE_NAME $thirdParty */
|
||||
return $thirdParty?->member;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据unionid查找会员
|
||||
*/
|
||||
public function findMemberByUnionId(string $unionid): ?Member
|
||||
{
|
||||
$thirdParty = $this->query()
|
||||
->where('unionid', $unionid)
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->with('member')
|
||||
->first();
|
||||
|
||||
/** @var TYPE_NAME $thirdParty */
|
||||
return $thirdParty?->member;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员的第三方账号列表
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getMemberThirdParties(int|string $memberId): array
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解绑第三方账号
|
||||
*/
|
||||
public function unbind(MemberThirdParty $thirdParty): bool
|
||||
{
|
||||
$thirdParty->enabled = EnabledStatus::DISABLED->value;
|
||||
return $thirdParty->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查第三方账号是否已绑定
|
||||
*/
|
||||
public function isBound(int $platform, string $openid): bool
|
||||
{
|
||||
return $this->query()
|
||||
->where('platform', $platform)
|
||||
->where('openid', $openid)
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新第三方登录令牌
|
||||
*/
|
||||
public function updateToken(MemberThirdParty $thirdParty, string $accessToken, string $refreshToken = '', int $expiresIn = 0): bool
|
||||
{
|
||||
$thirdParty->access_token = $accessToken;
|
||||
if ($refreshToken) {
|
||||
$thirdParty->refresh_token = $refreshToken;
|
||||
}
|
||||
if ($expiresIn > 0) {
|
||||
$thirdParty->expires_at = date('Y-m-d H:i:s', time() + $expiresIn);
|
||||
}
|
||||
return $thirdParty->save();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use core\base\BaseDao;
|
||||
use app\enum\common\EnabledStatus;
|
||||
use app\enum\member\WithdrawAccountType;
|
||||
use app\model\member\MemberWithdrawAccount;
|
||||
|
||||
/**
|
||||
* 会员提现账号数据访问对象
|
||||
*/
|
||||
class MemberWithdrawAccountDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberWithdrawAccount::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认账号
|
||||
*/
|
||||
public function setDefault(MemberWithdrawAccount $account): bool
|
||||
{
|
||||
// 先将该会员的所有提现账号设置为非默认
|
||||
$this->query()
|
||||
->where('member_id', $account->member_id)
|
||||
->update(['is_default' => 0]);
|
||||
|
||||
// 将当前账号设置为默认
|
||||
$account->is_default = 1;
|
||||
return $account->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员默认提现账号
|
||||
*/
|
||||
public function getDefaultAccount(int $memberId): ?MemberWithdrawAccount
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('is_default', 1)
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员提现账号列表
|
||||
*/
|
||||
public function getMemberAccounts(int $memberId): array
|
||||
{
|
||||
return $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('enabled', EnabledStatus::ENABLED->value)
|
||||
->orderBy('is_default', 'desc')
|
||||
->orderBy('updated_at', 'desc')
|
||||
->get()
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建或更新提现账号
|
||||
*/
|
||||
public function saveAccount(int $memberId, array $data): MemberWithdrawAccount
|
||||
{
|
||||
$accountId = $data['id'] ?? 0;
|
||||
|
||||
if ($accountId) {
|
||||
// 更新账号
|
||||
$account = $this->find($accountId);
|
||||
if (!$account || $account->member_id != $memberId) {
|
||||
throw new \Exception('账号不存在或无权限');
|
||||
}
|
||||
} else {
|
||||
// 创建新账号
|
||||
$account = $this->getModel();
|
||||
$account->member_id = $memberId;
|
||||
$account->enabled = EnabledStatus::ENABLED->value;
|
||||
}
|
||||
|
||||
// 填充数据
|
||||
$fillable = [
|
||||
'type',
|
||||
'bank_name',
|
||||
'account_name',
|
||||
'account_number',
|
||||
'branch_name',
|
||||
];
|
||||
|
||||
foreach ($fillable as $field) {
|
||||
if (isset($data[$field])) {
|
||||
$account->{$field} = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
// 设置默认账号
|
||||
if (isset($data['is_default']) && $data['is_default'] == 1) {
|
||||
// 先将其他账号设置为非默认
|
||||
$this->query()
|
||||
->where('member_id', $memberId)
|
||||
->update(['is_default' => 0]);
|
||||
$account->is_default = 1;
|
||||
} elseif (!$accountId) {
|
||||
// 新账号默认为非默认
|
||||
$account->is_default = 0;
|
||||
}
|
||||
|
||||
$account->save();
|
||||
return $account;
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除提现账号
|
||||
*/
|
||||
public function deleteAccount(MemberWithdrawAccount $account): bool
|
||||
{
|
||||
// 检查是否有关联的提现记录
|
||||
$withdrawCount = $account->withdraws()->count();
|
||||
if ($withdrawCount > 0) {
|
||||
throw new \Exception('该账号有关联的提现记录,无法删除');
|
||||
}
|
||||
|
||||
$account->enabled = EnabledStatus::DISABLED->value;
|
||||
return $account->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证账号数据
|
||||
*/
|
||||
public function validateAccount(array $data): array
|
||||
{
|
||||
$errors = [];
|
||||
|
||||
if (empty($data['type'])) {
|
||||
$errors[] = '账号类型不能为空';
|
||||
}
|
||||
|
||||
if (empty($data['bank_name'])) {
|
||||
$errors[] = '银行名称不能为空';
|
||||
}
|
||||
|
||||
if (empty($data['account_name'])) {
|
||||
$errors[] = '账户姓名不能为空';
|
||||
}
|
||||
|
||||
if (empty($data['account_number'])) {
|
||||
$errors[] = '账号不能为空';
|
||||
}
|
||||
|
||||
// 根据账号类型进行特殊验证
|
||||
if (isset($data['type'])) {
|
||||
switch ($data['type']) {
|
||||
case WithdrawAccountType::BANK->value:
|
||||
if (empty($data['branch_name'])) {
|
||||
$errors[] = '开户行不能为空';
|
||||
}
|
||||
break;
|
||||
case WithdrawAccountType::ALIPAY->value:
|
||||
// 支付宝账号验证
|
||||
if (!preg_match('/^1[3-9]\d{9}$|^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/', $data['account_number'])) {
|
||||
$errors[] = '支付宝账号格式不正确(请输入手机号或邮箱)';
|
||||
}
|
||||
break;
|
||||
case WithdrawAccountType::WECHAT->value:
|
||||
// 微信账号验证
|
||||
if (!preg_match('/^[a-zA-Z0-9_-]{6,20}$/', $data['account_number'])) {
|
||||
$errors[] = '微信账号格式不正确';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员账号数量
|
||||
*/
|
||||
public function getMemberAccountCount(int $memberId): int
|
||||
{
|
||||
return $this->count([
|
||||
'member_id' => $memberId,
|
||||
'enabled' => EnabledStatus::ENABLED->value
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取账号使用次数
|
||||
*/
|
||||
public function getUsageCount(MemberWithdrawAccount $account): int
|
||||
{
|
||||
return $account->withdraws()->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\member;
|
||||
|
||||
use core\base\BaseDao;
|
||||
use app\model\member\MemberWithdraw;
|
||||
use app\enum\member\WithdrawStatus;
|
||||
|
||||
/**
|
||||
* 会员提现DAO
|
||||
*/
|
||||
class MemberWithdrawDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return MemberWithdraw::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成提现订单号
|
||||
*/
|
||||
public function generateOrderSn(): string
|
||||
{
|
||||
return 'WD' . date('YmdHis') . substr(implode('', array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建提现记录
|
||||
*/
|
||||
public function create(int $memberId, int $accountId, float $amount, float $fee, float $actualAmount, string $bankName, string $bankAccount, string $bankCardholder, string $orderSn, string $remark = ''): MemberWithdraw
|
||||
{
|
||||
$withdraw = $this->getModel();
|
||||
$withdraw->member_id = $memberId;
|
||||
$withdraw->account_id = $accountId;
|
||||
$withdraw->amount = $amount;
|
||||
$withdraw->fee = $fee;
|
||||
$withdraw->actual_amount = $actualAmount;
|
||||
$withdraw->status = WithdrawStatus::PENDING->value;
|
||||
$withdraw->bank_name = $bankName;
|
||||
$withdraw->bank_account = $bankAccount;
|
||||
$withdraw->bank_cardholder = $bankCardholder;
|
||||
$withdraw->order_sn = $orderSn;
|
||||
$withdraw->remark = $remark;
|
||||
$withdraw->save();
|
||||
|
||||
return $withdraw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据订单号查找提现记录
|
||||
*/
|
||||
public function findByOrderSn(string $orderSn): ?MemberWithdraw
|
||||
{
|
||||
return $this->query()
|
||||
->where('order_sn', $orderSn)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新提现状态
|
||||
*/
|
||||
public function updateStatus(MemberWithdraw $withdraw, int $status, string $auditRemark = ''): bool
|
||||
{
|
||||
$withdraw->status = $status;
|
||||
if ($auditRemark) {
|
||||
$withdraw->audit_remark = $auditRemark;
|
||||
}
|
||||
$withdraw->audit_at = date('Y-m-d H:i:s');
|
||||
return $withdraw->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会员提现列表
|
||||
*/
|
||||
public function getMemberWithdraws(int $memberId, array $params = []): array
|
||||
{
|
||||
$query = $this->query()->where('member_id', $memberId);
|
||||
|
||||
// 状态筛选
|
||||
if (isset($params['status'])) {
|
||||
$query->where('status', $params['status']);
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if (isset($params['start_date']) && isset($params['end_date'])) {
|
||||
$query->whereBetween('created_at', [
|
||||
$params['start_date'] . ' 00:00:00',
|
||||
$params['end_date'] . ' 23:59:59'
|
||||
]);
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
$page = $params['page'] ?? 1;
|
||||
$limit = $params['limit'] ?? 10;
|
||||
|
||||
return $query->orderBy('created_at', 'desc')
|
||||
->paginate($limit, ['*'], 'page', $page)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现统计
|
||||
*/
|
||||
public function getWithdrawStats(int $memberId): array
|
||||
{
|
||||
$totalApply = $this->count(['member_id' => $memberId]);
|
||||
$totalSuccess = $this->count([
|
||||
'member_id' => $memberId,
|
||||
'status' => WithdrawStatus::COMPLETED->value
|
||||
]);
|
||||
$totalAmount = $this->query()
|
||||
->where('member_id', $memberId)
|
||||
->where('status', WithdrawStatus::COMPLETED->value)
|
||||
->sum('actual_amount');
|
||||
|
||||
return [
|
||||
'total_apply' => $totalApply,
|
||||
'total_success' => $totalSuccess,
|
||||
'total_amount' => $totalAmount,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待审核提现列表
|
||||
*/
|
||||
public function getPendingWithdraws(array $params = []): array
|
||||
{
|
||||
$query = $this->query()
|
||||
->where('status', WithdrawStatus::PENDING->value);
|
||||
|
||||
// 会员ID筛选
|
||||
if (isset($params['member_id'])) {
|
||||
$query->where('member_id', $params['member_id']);
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
$page = $params['page'] ?? 1;
|
||||
$limit = $params['limit'] ?? 10;
|
||||
|
||||
return $query->orderBy('created_at', 'desc')
|
||||
->with('member')
|
||||
->paginate($limit, ['*'], 'page', $page)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取提现总数
|
||||
*/
|
||||
public function getCount(array $params = []): int
|
||||
{
|
||||
$query = $this->query();
|
||||
|
||||
// 会员ID筛选
|
||||
if (isset($params['member_id'])) {
|
||||
$query->where('member_id', $params['member_id']);
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if (isset($params['status'])) {
|
||||
$query->where('status', $params['status']);
|
||||
}
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\message;
|
||||
|
||||
use app\model\message\Message;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class MessageDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Message::class;
|
||||
}
|
||||
|
||||
public function get($id, ?array $field = null, ?array $with = [], string $order = '', ?array $withoutScopes = null): ?Message
|
||||
{
|
||||
return parent::get($id, $field, ['sender'], $order, $withoutScopes);
|
||||
}
|
||||
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return parent::selectList($where, $field, $page, $limit, 'created_at desc', ['sender'], $search, $withoutScopes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\notice;
|
||||
|
||||
use app\model\notice\Notice;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class NoticeDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Notice::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\org;
|
||||
|
||||
use app\model\org\Dept;
|
||||
use core\base\BaseDao;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class DeptDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Dept::class;
|
||||
}
|
||||
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return parent::selectList($where, $field, $page, $limit, $order, ['leader'], $search, $withoutScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* get
|
||||
*
|
||||
* @param $id
|
||||
* @param array|null $field
|
||||
* @param array|null $with
|
||||
* @param string $order
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return \app\model\system\SystemDept|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($id, ?array $field = [], ?array $with = [], string $order = '', ?array $withoutScopes = null): ?Dept
|
||||
{
|
||||
$model = parent::get($id, ['*'], ['leader'], $order, $withoutScopes);
|
||||
if (!empty($model)) {
|
||||
$leader = $model->leader;
|
||||
$model->set('leader_id_list', []);
|
||||
if (!empty($leader)) {
|
||||
$model->set('leader_id_list', array_column($leader->toArray() ?? [], 'id'));
|
||||
}
|
||||
}
|
||||
return $model;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门子级所有id
|
||||
*
|
||||
* @param string|int $parentId
|
||||
* @param true $includeSelf
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getChildIdsIncludingSelf(string|int $parentId, true $includeSelf = true): array
|
||||
{
|
||||
$allChildIds = new Collection();
|
||||
if ($includeSelf) {
|
||||
$this->addIdToCollection($parentId, $allChildIds);
|
||||
}
|
||||
$this->collectChildIds($parentId, $allChildIds);
|
||||
return $allChildIds->unique()->values()->all();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将子级id添加到collection
|
||||
*
|
||||
* @param $id
|
||||
* @param \Illuminate\Support\Collection $collection
|
||||
*/
|
||||
private function addIdToCollection($id, Collection &$collection): void
|
||||
{
|
||||
if (!$collection->contains($id)) {
|
||||
$collection->push($id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归获取子级
|
||||
*
|
||||
* @param $parentId
|
||||
* @param \Illuminate\Support\Collection $allChildIds
|
||||
*/
|
||||
private function collectChildIds($parentId, Collection &$allChildIds)
|
||||
{
|
||||
$childIds = Dept::where('pid', $parentId)->pluck('id');
|
||||
foreach ($childIds as $childId) {
|
||||
$this->addIdToCollection($childId, $allChildIds);
|
||||
$this->collectChildIds($childId, $allChildIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门及所有子部门ID(包括自身)
|
||||
*
|
||||
* @param int $deptId
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
// public function getChildIdsIncludingSelf(int $deptId): array
|
||||
// {
|
||||
// $ids = collect();
|
||||
// $this->collectChildIds($deptId, $ids);
|
||||
// return $ids->unique()->toArray();
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 递归收集部门ID
|
||||
// *
|
||||
// * @param int $deptId
|
||||
// * @param \Illuminate\Support\Collection $ids
|
||||
// */
|
||||
// private function collectChildIds(int $deptId, \Illuminate\Support\Collection &$ids): void
|
||||
// {
|
||||
// $ids->push($deptId);
|
||||
// $children = $this->model->where('pid', $deptId)->get();
|
||||
// foreach ($children as $child) {
|
||||
// $this->collectChildIds($child->id, $ids);
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\org;
|
||||
|
||||
use app\model\org\DeptLeader;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class DeptLeaderDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return DeptLeader::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
namespace app\dao\org;
|
||||
|
||||
use app\model\org\Post;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class PostDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Post::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\plugin;
|
||||
|
||||
use app\model\plugin\ApplicationAuth;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class ApplicationAuthDao extends BaseDao
|
||||
{
|
||||
protected function setModel(): string
|
||||
{
|
||||
return ApplicationAuth::class;
|
||||
}
|
||||
|
||||
public function get($id, ?array $field = null, ?array $with = ['createds', 'updateds'], string $order = '', ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Model
|
||||
{
|
||||
return parent::get($id, $field, $with, $order, $withoutScopes);
|
||||
}
|
||||
|
||||
protected function getKeywordFields(): array
|
||||
{
|
||||
return ['name', 'access_key', 'description'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: https://madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\plugin;
|
||||
|
||||
use app\model\plugin\Plugin;
|
||||
use core\base\BaseDao;
|
||||
use madong\query\QueryBuilderHelper;
|
||||
|
||||
/**
|
||||
* Plugin数据访问层
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class PluginDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Plugin::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取插件列表(分页)
|
||||
*
|
||||
* @param array $where 查询条件(标准filters格式)
|
||||
* @param int $page 页码
|
||||
* @param int $limit 每页数量
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getList(array $where = [], int $page = 1, int $limit = 15): array
|
||||
{
|
||||
$query = $this->query();
|
||||
|
||||
// 应用filters条件(标准格式:包含filters数组)
|
||||
if (isset($where['filters']) && is_array($where['filters'])) {
|
||||
QueryBuilderHelper::applyFiltersToQuery($query, $where['filters']);
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
$list = $query->orderBy('created_at', 'desc')
|
||||
->offset(($page - 1) * $limit)
|
||||
->limit($limit)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
return [
|
||||
'total' => $total,
|
||||
'items' => $list,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key查询插件
|
||||
*
|
||||
* @param string $key 插件标识
|
||||
*
|
||||
* @return Plugin|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function findByKey(string $key): ?Plugin
|
||||
{
|
||||
return $this->query()->where('key', $key)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有插件的key列表
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getAllKeys(): array
|
||||
{
|
||||
return $this->query()->pluck('key')->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量插入插件
|
||||
*
|
||||
* @param array $plugins 插件数据数组
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function batchInsert(array $plugins): bool
|
||||
{
|
||||
if (empty($plugins)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$model = $this->getModel();
|
||||
foreach ($plugins as $plugin) {
|
||||
// 检查插件是否已存在
|
||||
$exists = $model->where('key', $plugin['key'])->exists();
|
||||
if (!$exists) {
|
||||
$model->create($plugin);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\plugin;
|
||||
|
||||
use app\model\plugin\PluginLog;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 插件日志数据访问层
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class PluginLogDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return PluginLog::class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,288 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\review;
|
||||
|
||||
use app\enum\review\ReviewStatus;
|
||||
use app\model\review\Review;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 审核DAO
|
||||
*/
|
||||
class ReviewDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Review::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待审核数量
|
||||
*
|
||||
* @param string|null $reviewableType 审核类型
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getPendingCount(?string $reviewableType = null): int
|
||||
{
|
||||
$query = $this->getModel()->query()->where('status', ReviewStatus::PENDING->value);
|
||||
if ($reviewableType) {
|
||||
$query->where('reviewable_type', $reviewableType);
|
||||
}
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据关联对象获取审核记录
|
||||
*
|
||||
* @param string $reviewableType 模型类型
|
||||
* @param int|string $reviewableId 模型ID
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getByReviewable(string $reviewableType, int|string $reviewableId): ?\Illuminate\Database\Eloquent\Model
|
||||
{
|
||||
return $this->getOne([
|
||||
'reviewable_type' => $reviewableType,
|
||||
'reviewable_id' => $reviewableId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量审核通过
|
||||
*
|
||||
* @param array $ids 审核ID数组
|
||||
* @param int $reviewerId 审核人ID
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function batchApprove(array $ids, int $reviewerId): int
|
||||
{
|
||||
return $this->getModel()->query()
|
||||
->whereIn('id', $ids)
|
||||
->where('status', ReviewStatus::PENDING->value)
|
||||
->update([
|
||||
'status' => ReviewStatus::APPROVED->value,
|
||||
'reviewer_id' => $reviewerId,
|
||||
'reviewed_at' => time(),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量审核拒绝
|
||||
*
|
||||
* @param array $ids 审核ID数组
|
||||
* @param int $reviewerId 审核人ID
|
||||
* @param string $reason 拒绝原因
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function batchReject(array $ids, int $reviewerId, string $reason = ''): int
|
||||
{
|
||||
$data = [
|
||||
'status' => ReviewStatus::REJECTED->value,
|
||||
'reviewer_id' => $reviewerId,
|
||||
'reviewed_at' => time(),
|
||||
];
|
||||
if (!empty($reason)) {
|
||||
$data['reason'] = $reason;
|
||||
}
|
||||
return $this->getModel()->query()
|
||||
->whereIn('id', $ids)
|
||||
->where('status', ReviewStatus::PENDING->value)
|
||||
->update($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询列表(支持 extra_data 字段搜索)
|
||||
*
|
||||
* @param array $where
|
||||
* @param string|array $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param string $order
|
||||
* @param array $with
|
||||
* @param bool $search
|
||||
* @param array|null $withoutScopes
|
||||
* @return \Illuminate\Database\Eloquent\Collection|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
// 获取基础查询构建器
|
||||
$query = $this->getModel()->query();
|
||||
|
||||
// 处理作用域
|
||||
if (!empty($withoutScopes)) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}
|
||||
|
||||
// 处理 extra_data 字段搜索条件
|
||||
$this->applyExtraDataSearch($query, $where);
|
||||
|
||||
// 处理其他普通条件(排除已处理的LIKE_*条件)
|
||||
$normalWhere = $this->filterExtraDataConditions($where);
|
||||
if (!empty($normalWhere)) {
|
||||
$options = [
|
||||
'keyword_fields' => $this->getKeywordFields(),
|
||||
];
|
||||
|
||||
// $search 为 false 时,禁用搜索器
|
||||
if (!$search) {
|
||||
$options['scopes'] = [];
|
||||
}
|
||||
|
||||
$query = $this->applyQueryParams($query, $normalWhere, $options);
|
||||
}
|
||||
|
||||
// 应用字段选择
|
||||
$isWildcard = ($field === '*' || ($field === ['*']));
|
||||
if (!$isWildcard) {
|
||||
if (is_array($field)) {
|
||||
$field = implode(',', $field);
|
||||
}
|
||||
$query->selectRaw($field);
|
||||
}
|
||||
|
||||
// 应用排序
|
||||
if ($order !== '') {
|
||||
$query->orderByRaw($order);
|
||||
}
|
||||
|
||||
// 应用关联加载
|
||||
if (!empty($with)) {
|
||||
$query->with($with);
|
||||
}
|
||||
|
||||
// 应用分页或返回所有数据
|
||||
if ($page > 0 && $limit > 0) {
|
||||
return $query->paginate($limit, ['*'], 'page', $page)->getCollection();
|
||||
}
|
||||
return $query->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取数量(支持 extra_data 字段搜索)
|
||||
*
|
||||
* @param array $where
|
||||
* @param bool $search
|
||||
* @return int
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function count(array $where = [], bool $search = false): int
|
||||
{
|
||||
// 获取基础查询构建器
|
||||
$query = $this->getModel()->query();
|
||||
|
||||
// 处理 extra_data 字段搜索条件
|
||||
$this->applyExtraDataSearch($query, $where);
|
||||
|
||||
// 处理其他普通条件(排除已处理的LIKE_*条件)
|
||||
$normalWhere = $this->filterExtraDataConditions($where);
|
||||
if (!empty($normalWhere)) {
|
||||
$options = [
|
||||
'keyword_fields' => $this->getKeywordFields(),
|
||||
];
|
||||
|
||||
// $search 为 false 时,禁用搜索器
|
||||
if (!$search) {
|
||||
$options['scopes'] = [];
|
||||
}
|
||||
|
||||
$query = $this->applyQueryParams($query, $normalWhere, $options);
|
||||
}
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理 extra_data 字段搜索条件
|
||||
* 支持搜索 extra_data JSON 字段中的标题、申请人、内容等字段
|
||||
*
|
||||
* @param array $where
|
||||
* @return array
|
||||
*/
|
||||
protected function processExtraDataSearch(array $where): array
|
||||
{
|
||||
$processedWhere = [];
|
||||
|
||||
foreach ($where as $key => $value) {
|
||||
if (is_string($key) && preg_match('/^LIKE_(title|applicant|content)$/i', $key, $matches)) {
|
||||
// 处理 LIKE_title, LIKE_applicant, LIKE_content 等搜索条件
|
||||
// 这些字段存储在 extra_data JSON 中
|
||||
$fieldName = strtolower($matches[1]); // title, applicant, content
|
||||
|
||||
// 转换为 JSON 查询条件
|
||||
// JSON_EXTRACT(extra_data, '$.title') LIKE '%值%'
|
||||
$processedWhere[] = [
|
||||
'RAW',
|
||||
"JSON_EXTRACT(extra_data, ?) LIKE ?",
|
||||
["$.{$fieldName}", "%{$value}%"]
|
||||
];
|
||||
} else {
|
||||
// 其他条件保持不变
|
||||
$processedWhere[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $processedWhere;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接应用 extra_data 字段搜索到查询构建器(使用闭包查询)
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @param array $where
|
||||
* @return void
|
||||
*/
|
||||
protected function applyExtraDataSearch(\Illuminate\Database\Eloquent\Builder $query, array $where): void
|
||||
{
|
||||
$extraDataConditions = [];
|
||||
|
||||
// 收集所有extra_data搜索条件
|
||||
foreach ($where as $key => $value) {
|
||||
if (is_string($key) && preg_match('/^LIKE_(title|applicant|content)$/i', $key, $matches)) {
|
||||
$fieldName = strtolower($matches[1]); // title, applicant, content
|
||||
$extraDataConditions[] = [
|
||||
'field' => $fieldName,
|
||||
'value' => $value
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有extra_data搜索条件,使用闭包查询
|
||||
if (!empty($extraDataConditions)) {
|
||||
$query->where(function (\Illuminate\Database\Eloquent\Builder $subQuery) use ($extraDataConditions) {
|
||||
foreach ($extraDataConditions as $condition) {
|
||||
$subQuery->orWhereRaw(
|
||||
"JSON_EXTRACT(extra_data, ?) LIKE ?",
|
||||
["$.{$condition['field']}", "%{$condition['value']}%"]
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤掉已处理的 extra_data 搜索条件
|
||||
*
|
||||
* @param array $where
|
||||
* @return array
|
||||
*/
|
||||
protected function filterExtraDataConditions(array $where): array
|
||||
{
|
||||
$filteredWhere = [];
|
||||
|
||||
foreach ($where as $key => $value) {
|
||||
if (!is_string($key) || !preg_match('/^LIKE_(title|applicant|content)$/i', $key)) {
|
||||
$filteredWhere[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $filteredWhere;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
use app\model\system\Admin;
|
||||
use core\base\BaseDao;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class AdminDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Admin::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户详情
|
||||
*
|
||||
* @param $id
|
||||
* @param array|null $field
|
||||
* @param array|null $with
|
||||
* @param string $order
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return Admin|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function get($id, ?array $field = null, ?array $with = [], string $order = '', ?array $withoutScopes = null): Admin|null
|
||||
{
|
||||
$query = $this->getModel()->where('id', $id);
|
||||
// 应用withoutScopes参数
|
||||
if (!empty($withoutScopes)) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}
|
||||
|
||||
// 添加with关联
|
||||
$query->with([
|
||||
'depts' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
},
|
||||
'posts' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
},
|
||||
'roles' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
},
|
||||
]);
|
||||
|
||||
// 先获取结果,检查是否为null
|
||||
$result = $query->first();
|
||||
|
||||
// 如果结果为空,直接返回null
|
||||
if (!$result) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 隐藏密码字段
|
||||
$result->makeHidden(['password']);
|
||||
|
||||
// 初始化属性
|
||||
$result->role_id_list = [];
|
||||
$result->post_id_list = [];
|
||||
$result->dept_id = '';
|
||||
|
||||
// 处理角色关系
|
||||
if (!empty($result->roles)) {
|
||||
$result->role_id_list = $result->roles->pluck('id')->all();
|
||||
}
|
||||
|
||||
// 处理posts关系
|
||||
if (!empty($result->posts)) {
|
||||
$result->post_id_list = $result->posts->pluck('id')->all();
|
||||
}
|
||||
|
||||
// 处理depts关系
|
||||
if (!empty($result->depts) && $result->depts->isNotEmpty()) {
|
||||
$result->dept_id = $result->depts->first()->id;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表
|
||||
*
|
||||
* @param array $where 查询条件数组(标准格式或三元素数组)
|
||||
* @param string $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param string $order
|
||||
* @param array $with
|
||||
* @param bool $search
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getList(array $where, string $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): array
|
||||
{
|
||||
// 初始化部门ID
|
||||
$deptId = null;
|
||||
|
||||
// 兼容两种格式: 标准格式 ['filters' => [...]] 和三元素数组
|
||||
if (isset($where['filters']) && is_array($where['filters'])) {
|
||||
// 标准格式: 从filters中查找并移除dept_id条件
|
||||
foreach ($where['filters'] as $key => $value) {
|
||||
if ($key === 'dept_id') {
|
||||
// 解析 dept_id 值
|
||||
$parts = explode(':', $value, 2);
|
||||
$deptId = $parts[1] ?? $value;
|
||||
// 移除这个条件
|
||||
unset($where['filters'][$key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 三元素数组格式: 遍历查找 dept_id 条件
|
||||
foreach ($where as $index => $condition) {
|
||||
if (is_array($condition) && count($condition) >= 2) {
|
||||
$fieldName = $condition[0];
|
||||
if ($fieldName === 'dept_id') {
|
||||
$deptId = $condition[count($condition) - 1];
|
||||
// 移除这个条件
|
||||
unset($where[$index]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 重新索引数组
|
||||
$where = array_values($where);
|
||||
}
|
||||
|
||||
if (empty($with)) {
|
||||
$with = ['depts', 'posts', 'roles'];
|
||||
}
|
||||
|
||||
$query = parent::selectModel($where, $field, $page, $limit, $order, $with, $search, $withoutScopes);
|
||||
|
||||
// 如果存在deptId条件,则添加关联条件
|
||||
if (!is_null($deptId)) {
|
||||
$query->whereHas('depts', function ($q) use ($deptId) {
|
||||
$q->where('id', $deptId);
|
||||
});
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
$items = $query->get()->makeHidden(['password', 'backend_setting']);
|
||||
return [$total, $items];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户列表-角色id
|
||||
*
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getUsersListByRoleId(array $where, string $field, int $page, int $limit, ?array $withoutScopes = null): array
|
||||
{
|
||||
$roleId = $where['role_id'] ?? null;
|
||||
if (!$roleId) {
|
||||
throw new InvalidArgumentException("Role ID is required.");
|
||||
}
|
||||
$where['enabled'] = 1;//有效用户
|
||||
$where['is_super'] = 0;//非顶级管理员
|
||||
|
||||
$query = $this->getModel()->with(['roles' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}])
|
||||
->whereHas('roles', function ($query) use ($roleId) {
|
||||
$query->where('id', $roleId);
|
||||
});
|
||||
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
if (!empty($where)) {
|
||||
unset($where['role_id']);
|
||||
$query->where($where);
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
|
||||
$items = $query->when($page > 0 && $limit > 0, function ($query) use ($page, $limit) {
|
||||
return $query->skip(($page - 1) * $limit)->take($limit);
|
||||
})
|
||||
->select($field)
|
||||
->get()
|
||||
->toArray();
|
||||
|
||||
return compact('total', 'items');
|
||||
}
|
||||
|
||||
/**
|
||||
* 排除角色ID-用户列表
|
||||
*
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return array
|
||||
* @throws \ReflectionException
|
||||
*/
|
||||
public function getUsersExcludingRole(array $where, string $field, int $page, int $limit, ?array $withoutScopes = null): array
|
||||
{
|
||||
$roleId = $where['role_id'] ?? null;
|
||||
if (!$roleId) {
|
||||
throw new InvalidArgumentException("Role ID is required.");
|
||||
}
|
||||
|
||||
$where['enabled'] = 1;//有效用户
|
||||
$where['is_super'] = 0;//非顶级管理员
|
||||
|
||||
// 获取排除的用户ID列表
|
||||
$sysAdminRoleDao = new AdminRoleDao();
|
||||
$excludedAdminIds = $sysAdminRoleDao->getColumn(['role_id' => $roleId], 'admin_id');
|
||||
|
||||
// 查询构建器
|
||||
$query = $this->getModel()->whereNotIn('id', $excludedAdminIds)
|
||||
->with(['roles' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}]);
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
|
||||
// 如果有额外的条件,则添加到查询中
|
||||
if (!empty($where)) {
|
||||
unset($where['role_id']);
|
||||
$query->where($where);
|
||||
}
|
||||
|
||||
$total = $query->count();
|
||||
|
||||
// 如果没有用户,则返回空结果
|
||||
if ($total === 0) {
|
||||
return ['total' => 0, 'items' => []];
|
||||
}
|
||||
|
||||
$items = $query->when($page > 0 && $limit > 0, function ($query) use ($page, $limit) {
|
||||
return $query->skip(($page - 1) * $limit)->take($limit);
|
||||
})->select($field)->get()->toArray();
|
||||
|
||||
return compact('total', 'items');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|int $id
|
||||
*
|
||||
* @return \app\model\system\Admin|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
// public function getAdminById(string|int $id): ?Admin
|
||||
// {
|
||||
// $result = $this->getModel()
|
||||
// ->where('id', $id)
|
||||
// ->with(['depts', 'posts', 'casbin.roles', 'roles'])
|
||||
// ->first()
|
||||
// ->makeHidden(['password', 'backend_setting']);
|
||||
//
|
||||
// if ($result) {
|
||||
// $result->role_id_list = [];
|
||||
// $result->post_id_list = [];
|
||||
// $result->dept_id = '';
|
||||
//
|
||||
// // 处理 casbin 关系
|
||||
// if ($result->casbin) {
|
||||
// $roleIds = $result->casbin->flatMap(function ($item) {
|
||||
// return $item->roles->pluck('id');
|
||||
// })->unique()->values()->all();
|
||||
//
|
||||
// $result->role_id_list = $roleIds;
|
||||
// }
|
||||
//
|
||||
// // 处理 posts 关系
|
||||
// if ($result->posts) {
|
||||
// $result->post_id_list = $result->posts->pluck('id')->all();
|
||||
// }
|
||||
//
|
||||
// // 处理 depts 关系
|
||||
// if ($result->depts && $result->depts->isNotEmpty()) {
|
||||
// $result->dept_id = $result->depts->first()->id;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return $result;
|
||||
// }
|
||||
/**
|
||||
* @param string|int $id
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return \app\model\system\Admin|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getAdminById(string|int $id, ?array $withoutScopes = null): ?Admin
|
||||
{
|
||||
$query = $this->getModel()
|
||||
->where('id', $id)
|
||||
->with(['depts' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}, 'posts' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}, 'roles' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}, 'mainInfo']);
|
||||
|
||||
// 应用作用域移除
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
|
||||
$result = $query->first();
|
||||
|
||||
if ($result) {
|
||||
$result->makeHidden(['password', 'backend_setting']);
|
||||
$result->role_id_list = [];
|
||||
$result->post_id_list = [];
|
||||
$result->dept_id_list = [];
|
||||
$result->main_dept_id = null;
|
||||
$result->main_post_id = null;
|
||||
|
||||
if (!empty($result->roles)) {
|
||||
$result->role_id_list = $result->roles->pluck('id')->all();
|
||||
}
|
||||
|
||||
if ($result->posts) {
|
||||
$result->post_id_list = $result->posts->pluck('id')->all();
|
||||
}
|
||||
|
||||
if ($result->depts && $result->depts->isNotEmpty()) {
|
||||
$result->dept_id_list = $result->depts->pluck('id')->all();
|
||||
}
|
||||
|
||||
if ($result->mainInfo) {
|
||||
$result->main_dept_id = $result->mainInfo->main_dept_id;
|
||||
$result->main_post_id = $result->mainInfo->main_post_id;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户详情
|
||||
*
|
||||
* @param string $name
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return \app\model\system\Admin|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getAdminByName(string $name, ?array $withoutScopes = null): ?Admin
|
||||
{
|
||||
$query = $this->getModel()
|
||||
->where('user_name', $name)
|
||||
->with(['depts' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}, 'posts' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}, 'roles' => function ($query) use ($withoutScopes) {
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
}]);
|
||||
|
||||
// 应用作用域移除
|
||||
$this->applyScopeRemoval($query, $withoutScopes);
|
||||
return $query->first();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
|
||||
use app\model\system\AdminPost;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class AdminPostDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return AdminPost::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
|
||||
use app\model\system\AdminRole;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class AdminRoleDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return AdminRole::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
use app\model\system\Config;
|
||||
use app\scope\global\AccessPermissionScope;
|
||||
use core\base\BaseDao;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
|
||||
class ConfigDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Config::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置列表
|
||||
*
|
||||
* @param array $where
|
||||
* @param string|array $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param string $order
|
||||
* @param array $with
|
||||
* @param bool $search
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return Collection|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getList(array $where = [], string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes =null): ?Collection
|
||||
{
|
||||
return $this->selectList($where, $field, $page, $limit, $order, $with, $search, $withoutScopes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
use app\model\system\Feedback;
|
||||
use core\base\BaseDao;
|
||||
|
||||
class FeedbackDao extends BaseDao
|
||||
{
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Feedback::class;
|
||||
}
|
||||
|
||||
public function getKeywordFields(): array
|
||||
{
|
||||
return ['content', 'contact'];
|
||||
}
|
||||
|
||||
public function selectModel(
|
||||
array $where,
|
||||
array|string $field = '*',
|
||||
int $page = 0,
|
||||
int $limit = 0,
|
||||
string $order = '',
|
||||
array $with = [],
|
||||
bool $search = false,
|
||||
?array $withoutScopes = null
|
||||
): \Illuminate\Database\Eloquent\Builder|\Illuminate\Database\Query\Builder|\Illuminate\Pagination\LengthAwarePaginator|null {
|
||||
return parent::selectModel($where, $field, $page, $limit, $order, ['createds', 'replyAdmin'], $search, $withoutScopes);
|
||||
}
|
||||
|
||||
public function get($id, ?array $field = null, ?array $with = [], string $order = '', ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Model
|
||||
{
|
||||
return parent::get($id, $field, ['createds', 'replyAdmin'], $order, $withoutScopes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
use app\model\system\Menu;
|
||||
use core\base\BaseDao;
|
||||
use core\exception\handler\BaseException;
|
||||
use core\exception\handler\CommonException;
|
||||
|
||||
/**
|
||||
* 平台菜单
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class MenuDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Menu::class;
|
||||
}
|
||||
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
$order = 'sort';
|
||||
return parent::selectList($where, $field, $page, $limit, $order, [], $search, $withoutScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param bool $force
|
||||
*
|
||||
* @return mixed
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function deleteByPlugin(string $name, bool $force = true): mixed
|
||||
{
|
||||
$query = $this->getModel()->query();
|
||||
$query->where('plugin', $name);
|
||||
if (!$force) {
|
||||
$query->where('source', 'system');
|
||||
}
|
||||
return $query->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装菜单项
|
||||
*
|
||||
* @param array $menuItems
|
||||
* @param string $pluginName
|
||||
* @param string $parentId
|
||||
* @param string $menuType
|
||||
*
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function installMenuItems(array $menuItems, string $pluginName, string $parentId = '', string $menuType = 'admin'): void
|
||||
{
|
||||
try {
|
||||
foreach ($menuItems as $menuItem) {
|
||||
// 准备菜单数据
|
||||
$menuData = [
|
||||
'pid' => $parentId,
|
||||
'app' => $menuItem['app'] ?? $menuType,
|
||||
'title' => $menuItem['title'],
|
||||
'code' => $menuItem['code'],
|
||||
'level' => $menuItem['level'] ?? null,
|
||||
'type' => $menuItem['type'],
|
||||
'sort' => $menuItem['sort'] ?? 0,
|
||||
'path' => $menuItem['path'] ?? '',
|
||||
'component' => $menuItem['component'] ?? '',
|
||||
'redirect' => $menuItem['redirect'] ?? '',
|
||||
'icon' => $menuItem['icon'] ?? '',
|
||||
'is_show' => $menuItem['is_show'] ?? 1,
|
||||
'is_link' => $menuItem['is_link'] ?? 0,
|
||||
'link_url' => $menuItem['link_url'] ?? null,
|
||||
'open_type' => $menuItem['open_type'] ?? 0,
|
||||
'is_cache' => $menuItem['is_cache'] ?? 0,
|
||||
'is_sync' => $menuItem['is_sync'] ?? 1,
|
||||
'is_affix' => $menuItem['is_affix'] ?? 0,
|
||||
'methods' => $menuItem['methods'] ?? 'GET',
|
||||
'plugin' => $pluginName, // 记录插件名称
|
||||
'source' => 'system', // 标记为系统源
|
||||
'created_at' => time(),
|
||||
'updated_at' => time(),
|
||||
];
|
||||
|
||||
// 创建菜单模型
|
||||
$menu = new Menu($menuData);
|
||||
$menu->save();
|
||||
// 如果有子菜单,递归安装
|
||||
if (isset($menuItem['children']) && is_array($menuItem['children'])) {
|
||||
$this->installMenuItems($menuItem['children'], $pluginName, $menu->id, $menuType);
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $e) {
|
||||
throw new CommonException($e->getMessage(), ['errorCode' => $e->getCode() ?: -1]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
use app\model\system\RecycleBin;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 回收站
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class RecycleBinDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return RecycleBin::class;
|
||||
}
|
||||
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return parent::selectList($where, $field, $page, $limit, $order, ['operate'], $search, $withoutScopes);
|
||||
}
|
||||
|
||||
public function get($id, ?array $field = null, ?array $with = [], string $order = '', ?array $withoutScopes = null): RecycleBin
|
||||
{
|
||||
return parent::get($id, ['*'], ['operate'], '', $withoutScopes);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
use app\model\system\Role;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
*
|
||||
* 角色
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class RoleDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Role::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过角色获取菜单
|
||||
*
|
||||
* @param array $ids
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getMenuIdsByRoleIds(array $ids = []): array
|
||||
{
|
||||
if (empty($ids)) {
|
||||
return [];
|
||||
}
|
||||
$where = ['id' => $ids];
|
||||
return $this->selectList($where, '*', 0, 0, '', ['menus' => function ($query) {
|
||||
$query->where('enabled', 1);
|
||||
}], true)->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色权限集合-
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
* /* 最终结果
|
||||
* [
|
||||
* 'roleId1' => [
|
||||
* 'menu' => [101, 102],
|
||||
* 'button => [201],
|
||||
* 'api' => [301, 302]
|
||||
* ],
|
||||
* 'roleId2' => [
|
||||
* 'menu' => [103],
|
||||
* 'button' => [],
|
||||
* 'api' => [303]
|
||||
* ]
|
||||
* ]
|
||||
*/
|
||||
public function getRolePermissions(): array
|
||||
{
|
||||
$result = $this->getModel()
|
||||
->withoutGlobalScope('TenantScope')
|
||||
->where('enabled', 1)
|
||||
->with(['menus'])
|
||||
->orderBy('sort', 'asc')
|
||||
->get([
|
||||
"id",
|
||||
"tenant_id",
|
||||
"pid",
|
||||
"name",
|
||||
"code",
|
||||
"is_super_admin",
|
||||
"role_type",
|
||||
"data_scope",
|
||||
"enabled",
|
||||
"sort",
|
||||
])
|
||||
->toArray();
|
||||
|
||||
// 初始化结构化数组
|
||||
$rolePermissions = [];
|
||||
foreach ($result as $role) {
|
||||
$roleId = $role['id'];
|
||||
// 初始化当前角色的权限集合
|
||||
$rolePermissions[$roleId] = [
|
||||
'id' => $role['id'] ?? '',
|
||||
'name' => $role['name'] ?? '',
|
||||
'tenant_id' => $role['tenant_id'] ?? '',
|
||||
'data_scope' => $role['data_scope'] ?? 1,
|
||||
'menu' => [],
|
||||
'button' => [],
|
||||
'api' => [],
|
||||
];
|
||||
|
||||
// 遍历关联的 menus 数据
|
||||
if (!empty($role['menus'])) {
|
||||
foreach ($role['menus'] as $menu) {
|
||||
switch ($menu['type']) {
|
||||
case 1:
|
||||
case 2:
|
||||
// 类型 1 或 2 是菜单,添加到 menu
|
||||
$rolePermissions[$roleId]['menu'][] = $menu['id'];
|
||||
break;
|
||||
case 3:
|
||||
// 类型 3 是按钮,添加到 button
|
||||
$rolePermissions[$roleId]['button'][] = $menu['id'];
|
||||
break;
|
||||
case 4:
|
||||
// 类型 4 是接口,添加到 api
|
||||
$rolePermissions[$roleId]['api'][] = $menu['id'];
|
||||
break;
|
||||
default:
|
||||
// 未知类型,忽略或记录日志
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $rolePermissions ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色列表
|
||||
*
|
||||
* @param array $where
|
||||
* @param string $field
|
||||
* @param int $page
|
||||
* @param int $limit
|
||||
* @param string $order
|
||||
* @param array $with
|
||||
* @param bool $search
|
||||
* @param array|null $withoutScopes
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
// 不再加载 casbin 关联,使用默认的 with 参数
|
||||
return parent::selectList($where, $field, $page, $limit, $order, $with, $search, $withoutScopes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $id
|
||||
* @param string|null $tenantId
|
||||
*
|
||||
* @return \app\model\system\Role|null
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function getImplicitPermissionsForRole(int|string $id, string|null $tenantId = null): ?Role
|
||||
{
|
||||
// 获取 tenantId,如果未提供且租户功能启用,则从上下文中获取
|
||||
if (config('tenant.enabled', false) && empty($tenantId)) {
|
||||
$tenantId = TenantContext::getTenantId();
|
||||
}
|
||||
// 构建查询
|
||||
return $this->getModel()->query()
|
||||
->withoutGlobalScope('TenantScope')
|
||||
->where('enabled', 1)
|
||||
->when($tenantId, function ($query) use ($tenantId) {
|
||||
$query->where('tenant_id', $tenantId);
|
||||
})
|
||||
->with(['permissions'])
|
||||
->orderBy('sort')
|
||||
->first();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
|
||||
use app\model\system\RoleMenu;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
*
|
||||
* 角色菜单
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class RoleMenuDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return RoleMenu::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
use app\model\system\RoleScopeDept;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
*
|
||||
* 角色数据权限
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class RoleScopeDeptDao extends BaseDao
|
||||
{
|
||||
protected function setModel(): string
|
||||
{
|
||||
return RoleScopeDept::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/**
|
||||
*+------------------
|
||||
* madong
|
||||
*+------------------
|
||||
* Copyright (c) https://gitee.com/motion-code All rights reserved.
|
||||
*+------------------
|
||||
* Author: Mr. April (405784684@qq.com)
|
||||
*+------------------
|
||||
* Official Website: http://www.madong.tech
|
||||
*/
|
||||
|
||||
namespace app\dao\system;
|
||||
|
||||
use app\model\system\Upload;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 上传
|
||||
*
|
||||
* @author Mr.April
|
||||
* @since 1.0
|
||||
*/
|
||||
class UploadDao extends BaseDao
|
||||
{
|
||||
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Upload::class;
|
||||
}
|
||||
|
||||
public function selectList(array $where, string|array $field = '*', int $page = 0, int $limit = 0, string $order = '', array $with = [], bool $search = false, ?array $withoutScopes = null): ?\Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
//重写追加两个关联模型
|
||||
return parent::selectList($where, $field, $page, $limit, $order, ['createds', 'updateds'], $search, $withoutScopes);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\web;
|
||||
|
||||
use app\model\web\Adv;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 广告数据访问对象
|
||||
*/
|
||||
class AdvDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型类
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Adv::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\web;
|
||||
|
||||
use app\model\web\Link;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 友情链接数据访问对象
|
||||
*/
|
||||
class LinkDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型类
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Link::class;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace app\dao\web;
|
||||
|
||||
use app\model\web\Menu;
|
||||
use core\base\BaseDao;
|
||||
|
||||
/**
|
||||
* 菜单数据访问对象
|
||||
*/
|
||||
class MenuDao extends BaseDao
|
||||
{
|
||||
/**
|
||||
* 设置模型类
|
||||
*/
|
||||
protected function setModel(): string
|
||||
{
|
||||
return Menu::class;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user