初始化本地仓库

This commit is contained in:
2026-08-31 11:33:59 +08:00
commit 663f74b633
2390 changed files with 402918 additions and 0 deletions
+59
View File
@@ -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;
}
}
+27
View File
@@ -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;
}
}
+75
View File
@@ -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();
}
}
+33
View File
@@ -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;
}
}
+26
View File
@@ -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;
}
}
+53
View File
@@ -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']);
}
}
+173
View File
@@ -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
]);
}
}
+183
View File
@@ -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();
}
}
+160
View File
@@ -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');
}
}
+78
View File
@@ -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;
}
}
+250
View File
@@ -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();
}
}
+132
View File
@@ -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();
}
}
+36
View File
@@ -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);
}
}
+26
View File
@@ -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;
}
}
+133
View File
@@ -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);
// }
// }
}
+26
View File
@@ -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;
}
}
+25
View File
@@ -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'];
}
}
+114
View File
@@ -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;
}
}
+33
View File
@@ -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;
}
}
+288
View File
@@ -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;
}
}
+382
View File
@@ -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();
}
}
+27
View File
@@ -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;
}
}
+27
View File
@@ -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;
}
}
+48
View File
@@ -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);
}
}
+39
View File
@@ -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);
}
}
+112
View File
@@ -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]);
}
}
}
+43
View File
@@ -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);
}
}
+180
View File
@@ -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();
}
}
+33
View File
@@ -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;
}
}
+38
View File
@@ -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);
}
}
+21
View File
@@ -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;
}
}
+21
View File
@@ -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;
}
}
+21
View File
@@ -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;
}
}