diff --git a/app/admin/controller/RoleController.php b/app/admin/controller/RoleController.php
index f73f07d..a08ff7e 100644
--- a/app/admin/controller/RoleController.php
+++ b/app/admin/controller/RoleController.php
@@ -32,7 +32,8 @@ class RoleController extends BaseController
public function menuResource(Request $request)
{
try {
- $data = $this->validate($this->service->getValidator(), $request->post(), 'allocation');
+ // $data = $this->validate($this->service->getValidator(), $request->post(), 'allocation');
+ $data = $request->post();
$res = $this->service->menuResource($request, $data);
return $this->success($res);
} catch (\Throwable $e) {
diff --git a/app/admin/controller/UserController.php b/app/admin/controller/UserController.php
index 641eb6d..7a7f6eb 100644
--- a/app/admin/controller/UserController.php
+++ b/app/admin/controller/UserController.php
@@ -87,6 +87,19 @@ class UserController extends BaseController
$data['corp_name'] = Corp::where('id', $data['corp_id'])->value('name');
$data['customer_count'] = Customer::where('user_id', $data['id'])->count();
+
+ $data['role_ids'] = [];
+ $data['system_ids'] = [];
+ if (isset($data['roles']) && count($data['roles']) > 0) {
+ foreach ($data['roles'] as $role) {
+ $data['role_ids'][] = $role['id'];
+ }
+ }
+ if (isset($data['systems']) && count($data['systems']) > 0) {
+ foreach ($data['systems'] as $system) {
+ $data['system_ids'][] = $system['id'];
+ }
+ }
return $data;
}
diff --git a/app/admin/logic/UserLogic.php b/app/admin/logic/UserLogic.php
index e01da99..d70b6de 100644
--- a/app/admin/logic/UserLogic.php
+++ b/app/admin/logic/UserLogic.php
@@ -22,34 +22,30 @@ class UserLogic
$systemIds = $data['system_ids'] ?? [];
$userId = $data['user_id'] ?? 0;
- if (empty($roleId)) {
- return false;
- }
-
Db::beginTransaction();
try {
Db::table('user_roles')->where('user_id', $userId)->delete();
- if (!empty($roleIds)) {
- $insertData = [];
+ if (count($roleIds) > 0) {
+ $insertDataRole = [];
foreach ($roleIds as $roleId) {
- $insertData[] = [
+ $insertDataRole[] = [
'role_id' => $roleId,
'user_id' => $userId
];
}
- Db::table('user_roles')->insert($insertData);
+ Db::table('user_roles')->insert($insertDataRole);
}
Db::table('user_systems')->where('user_id', $userId)->delete();
- if (!empty($systemIds)) {
- $insertData = [];
+ if (count($systemIds) > 0) {
+ $insertDataSystem = [];
foreach ($systemIds as $systemId) {
- $insertData[] = [
+ $insertDataSystem[] = [
'system_id' => $systemId,
'user_id' => $userId
];
}
- Db::table('user_systems')->insert($insertData);
+ Db::table('user_systems')->insert($insertDataSystem);
}
Db::commit();
diff --git a/app/admin/service/RoleService.php b/app/admin/service/RoleService.php
index af86a04..f2ee190 100644
--- a/app/admin/service/RoleService.php
+++ b/app/admin/service/RoleService.php
@@ -38,4 +38,13 @@ class RoleService
{
return (new RoleLogic)->updateRoleResources($data);
}
+
+
+ public function getList()
+ {
+ $config['where']['status'] = 1;
+ $config['order']['sort'] = 'asc';
+ $config['order']['id'] = 'asc';
+ return $this->logicListAll($config);
+ }
}
\ No newline at end of file
diff --git a/app/admin/service/UserService.php b/app/admin/service/UserService.php
index fe499d5..1ef1a43 100644
--- a/app/admin/service/UserService.php
+++ b/app/admin/service/UserService.php
@@ -72,6 +72,11 @@ class UserService
}
$config['fields'] = ['id','sn', 'name', 'email', 'sex', 'phone', 'avatar', 'corp_id', 'country_id', 'position', 'report_to', 'status', 'created_at'];
+
+ $config['with']['roles'][] = 'id';
+ $config['with']['roles'][] = 'name';
+ $config['with']['systems'][] = 'id';
+ $config['with']['systems'][] = 'name';
break;
case 'show':
$config['where']['id'] = $p['id'];
diff --git a/app/command/CrmRotateUserCommand.php b/app/command/CrmRotateUserCommand.php
new file mode 100644
index 0000000..5defb24
--- /dev/null
+++ b/app/command/CrmRotateUserCommand.php
@@ -0,0 +1,105 @@
+setHelp('查询 user 表中 status=2 且 corp_id=3 的用户,
+然后查找其关联的 crm_customer 记录,
+按顺序将这些 crm_customer.user_id 轮流设置为 1、2、3。');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output): int
+ {
+ $corpId = 3;
+ $status = 2;
+ $roundRobinUserIds = [64,63,83,93,118,131,132,143,145,150,156,157,158,160];
+
+ $output->writeln('开始执行 crm:rotate-user');
+
+ try {
+ // 1) 找到 user 表中满足条件的用户ID,派卡离职用户
+ $userRows = Db::table('user')
+ ->where('corp_id', $corpId)
+ ->where('status', $status)
+ ->get(['id','name']);
+
+ $userIds = [];
+ $userNames = [];
+ foreach ($userRows as $row) {
+ $userIds[] = (int)$row->id;
+ $userNames[] = $row->name;
+ }
+
+ if (empty($userIds)) {
+ $output->writeln('未找到满足条件的用户(status=2 且 corp_id=3)。无需处理。');
+ return self::SUCCESS;
+ }
+
+ $output->writeln('匹配到用户数:' . count($userIds) .' '. implode(',',$userNames) . '');
+
+ // 2) 查找 crm_customer 中属于这些用户的记录
+ $customers = Db::table('crm_customer')
+ ->whereIn('user_id', $userIds)
+ ->orderBy('id', 'asc')
+ ->get(['id','user_id']);
+
+ if (!$customers || count($customers) === 0) {
+ $output->writeln('未找到相关的 crm_customer 记录。');
+ return self::SUCCESS;
+ }
+
+ $output->writeln('需要处理的 crm_customer 数量:' . count($customers) . '');
+
+ // 3) 事务中按顺序轮流设置
+ // 初始化 UID 轮询器
+ $uidRotator = new \app\common\utils\RedisUidRotator('email_parsed_result_20250804');
+ $uidRotator->setUids([64, 63, 83, 93, 118, 131, 132, 143, 145, 150, 156, 157, 158, 160], false);
+
+ Db::connection()->transaction(function () use ($customers, $roundRobinUserIds, $output, $uidRotator) {
+ $assignCount = 0;
+ $targetCount = count($roundRobinUserIds);
+
+ foreach ($customers as $index => $customer) {
+ // 获取下一个分配的UID
+ $newUserId = $uidRotator->next();
+ Db::table('crm_customer')
+ ->where('id', $customer->id)
+ ->update(['user_id' => $newUserId]);
+
+ $his = CustomerHis::create([
+ 'customer_id' => $customer->id,
+ 'user_his' => $customer->user_id,
+ 'user_id' => $newUserId,
+ ]);
+
+
+ $assignCount++;
+
+ if ($assignCount % 500 === 0) {
+ $output->writeln('已处理:' . $assignCount . '');
+ }
+ }
+
+ $output->writeln('处理完成,共更新 crm_customer:' . $assignCount . ' 条');
+ });
+
+ return self::SUCCESS;
+ } catch (\Throwable $e) {
+ $output->writeln('执行失败:' . $e->getMessage() . '');
+ return self::FAILURE;
+ }
+ }
+}
+
diff --git a/app/common/controller/PresetController.php b/app/common/controller/PresetController.php
index ad88a8f..37073c4 100644
--- a/app/common/controller/PresetController.php
+++ b/app/common/controller/PresetController.php
@@ -2,11 +2,14 @@
namespace app\common\controller;
+use app\admin\service\RoleService;
use support\Request;
use app\common\service\CorpService;
use app\common\service\CorpFeishuAppService;
use app\common\service\SystemsService;
use app\common\service\UserService;
+use support\Cache;
+use app\common\service\PermService;
class PresetController extends BaseController
{
@@ -61,4 +64,44 @@ class PresetController extends BaseController
return $this->error($e->getMessage());
}
}
+
+
+ public function roles()
+ {
+ try {
+ $result = (new RoleService)->getList();
+ return $this->success($result, '获取角色列表');
+ } catch (\Throwable $e) {
+ return $this->error($e->getMessage());
+ }
+ }
+
+
+ public function cacheClear()
+ {
+ try {
+ Cache::clear();
+ return $this->success([], '操作成功');
+ } catch (\Throwable $e) {
+ return $this->error($e->getMessage());
+ }
+ }
+
+ public function menus(Request $request)
+ {
+ try {
+ $uid = $request->user->uid;
+ $systemCode = $request->system_code;
+ $permService = new PermService();
+ $menuTree = $permService->getMenuTree($uid, $systemCode);
+ $permissionCodes = $permService->getPermissionCodes($uid, $systemCode);
+ $result = [
+ 'menu' => $menuTree,
+ 'permissions' => $permissionCodes
+ ];
+ return $this->success($result, '获取菜单权限');
+ } catch (\Throwable $e) {
+ return $this->error($e->getMessage());
+ }
+ }
}
\ No newline at end of file
diff --git a/app/common/logic/AuthLogic.php b/app/common/logic/AuthLogic.php
index b2a74fc..9857df8 100644
--- a/app/common/logic/AuthLogic.php
+++ b/app/common/logic/AuthLogic.php
@@ -118,6 +118,12 @@ class AuthLogic
}
// 是否登陆
$user->isLogin = false;
+
+ $options = [
+ 'corp_id' => $user->corp_id,
+ 'user_id' => $user->id
+ ];
+ \Webman\RedisQueue\Redis::send('perm-agt', $options);
} else {
$updated = [
'union_id' => $feishuUserInfo['union_id'],
diff --git a/app/common/logic/CorpFeishuAppLogic.php b/app/common/logic/CorpFeishuAppLogic.php
index 59838c3..2613c6a 100644
--- a/app/common/logic/CorpFeishuAppLogic.php
+++ b/app/common/logic/CorpFeishuAppLogic.php
@@ -14,4 +14,10 @@ class CorpFeishuAppLogic
*/
protected string $modelClass = CorpFeishuApp::class;
+
+ public function getFeishuApp($corpId, $systemId)
+ {
+ return CorpFeishuApp::where('corp_id', $corpId)->where('system_id', $systemId)->first(['id', 'corp_id', 'name', 'app_id', 'r_url', 'state']);
+ }
+
}
\ No newline at end of file
diff --git a/app/common/service/ResourceService.php b/app/common/service/ResourceService.php
index 5c73800..c8ef178 100644
--- a/app/common/service/ResourceService.php
+++ b/app/common/service/ResourceService.php
@@ -95,19 +95,32 @@ class ResourceService
protected function afterServiceListAll($data)
{
- $data = Tree::buildWithAuthListAndReference($data->toArray());
+ $data = Tree::buildMenuTreeWithReference($data->toArray());
return $data;
}
protected function afterServiceMenuList($data)
{
- $data = Tree::buildWithReference($data->toArray());
+ $data = Tree::buildMenuTree($data->toArray());
return $data;
}
protected function afterServiceMenuTreeList($data)
{
- $data = Tree::buildWithReference($data->toArray());
+ $data = Tree::buildMenuTree($data->toArray());
+ return $data;
+ }
+
+
+ public function beforeServiceStore($data, Request $request)
+ {
+ $data['parent_id'] = $data['parent_id'] != '' ? $data['parent_id'] : 0;
+ return $data;
+ }
+
+ public function beforeServiceUpdate($data, Request $request)
+ {
+ $data['parent_id'] = $data['parent_id'] != '' ? $data['parent_id'] : 0;
return $data;
}
}
\ No newline at end of file
diff --git a/app/common/service/UserService.php b/app/common/service/UserService.php
index bfcf345..d955de0 100644
--- a/app/common/service/UserService.php
+++ b/app/common/service/UserService.php
@@ -34,9 +34,9 @@ class UserService
public function getUserInfo(int $uid, string $systemCode)
{
$user = $this->logic->userInfo($uid);
- $permService = new PermService();
- $menuTree = $permService->getMenuTree($uid, $systemCode);
- $permissionCodes = $permService->getPermissionCodes($uid, $systemCode);
+ // $permService = new PermService();
+ // $menuTree = $permService->getMenuTree($uid, $systemCode);
+ // $permissionCodes = $permService->getPermissionCodes($uid, $systemCode);
return [
'user' => [
'id' => $user->id,
@@ -47,8 +47,8 @@ class UserService
'status' => $user->status,
'roles' => $user->roles->pluck('name')
],
- 'menu' => $menuTree,
- 'permissions' => $permissionCodes
+ // 'menu' => $menuTree,
+ // 'permissions' => $permissionCodes
];
}
diff --git a/app/common/traits/crud/CrudControllerTrait.php b/app/common/traits/crud/CrudControllerTrait.php
index 5960d77..ca30ba3 100644
--- a/app/common/traits/crud/CrudControllerTrait.php
+++ b/app/common/traits/crud/CrudControllerTrait.php
@@ -62,7 +62,6 @@ trait CrudControllerTrait
$res = $this->service->serviceStore($request, $data);
return $this->success($res, '创建成功');
} catch (\Throwable $e) {
- dpr($e);
return $this->error($e->getMessage());
}
}
diff --git a/app/common/utils/Tree.php b/app/common/utils/Tree.php
index 6322e36..b831185 100644
--- a/app/common/utils/Tree.php
+++ b/app/common/utils/Tree.php
@@ -4,30 +4,380 @@ namespace app\common\utils;
class Tree
{
+ // /**
+ // * 递归构建树形结构
+ // *
+ // * @param array $data 数据列表
+ // * @param int $parentId 父级ID
+ // * @param string $pidField 父级字段名
+ // * @param string $idField ID字段名
+ // * @param int $level 当前层级
+ // * @return array
+ // */
+ // public static function build(array $data, int $parentId = 0, string $pidField = 'parent_id', string $idField = 'id', int $level = 0): array
+ // {
+ // $tree = [];
+
+ // foreach ($data as $item) {
+ // if ($item[$pidField] == $parentId) {
+ // $item['level'] = $level;
+ // $children = static::build($data, $item[$idField], $pidField, $idField, $level + 1);
+
+ // if (!empty($children)) {
+ // $item['children'] = $children;
+ // }
+
+ // $tree[] = $item;
+ // }
+ // }
+
+ // return $tree;
+ // }
+
+ // /**
+ // * 使用引用方式构建树形结构(性能更好)
+ // *
+ // * @param array $data 数据列表
+ // * @param string $pidField 父级字段名
+ // * @param string $idField ID字段名
+ // * @return array
+ // */
+ // public static function buildWithReference(array $data, string $pidField = 'parent_id', string $idField = 'id'): array
+ // {
+ // $items = [];
+ // $tree = [];
+
+ // // 建立以id为索引的数组
+ // foreach ($data as $item) {
+ // $item['level'] = 0;
+ // $items[$item[$idField]] = $item;
+ // }
+
+ // // 构建树形结构
+ // foreach ($items as $id => $item) {
+ // if ($item[$pidField] == 0) {
+ // $tree[] = &$items[$id];
+ // } else {
+ // if (isset($items[$item[$pidField]])) {
+ // $items[$id]['level'] = $items[$item[$pidField]]['level'] + 1;
+
+ // if (!isset($items[$item[$pidField]]['children'])) {
+ // $items[$item[$pidField]]['children'] = [];
+ // }
+
+ // $items[$item[$pidField]]['children'][] = &$items[$id];
+ // }
+ // }
+ // }
+
+ // return $tree;
+ // }
+
+ // /**
+ // * 将树形结构扁平化为列表(带层级前缀)
+ // *
+ // * @param array $tree 树形结构数据
+ // * @param string $prefix 前缀字符
+ // * @param string $field 显示字段名
+ // * @param int $level 层级
+ // * @return array
+ // */
+ // public static function flattenWithPrefix(array $tree, string $prefix = '--', string $field = 'name', int $level = 0): array
+ // {
+ // $result = [];
+
+ // foreach ($tree as $item) {
+ // $item[$field . '_with_prefix'] = str_repeat($prefix, $item['level']) . $item[$field];
+ // $result[] = $item;
+
+ // if (isset($item['children']) && !empty($item['children'])) {
+ // $children = static::flattenWithPrefix($item['children'], $prefix, $field, $item['level'] + 1);
+ // $result = array_merge($result, $children);
+ // }
+ // }
+
+ // return $result;
+ // }
+
+ // /**
+ // * 根据父ID查找所有子节点(包括子孙节点)
+ // *
+ // * @param array $data 数据列表
+ // * @param int $parentId 父级ID
+ // * @param string $pidField 父级字段名
+ // * @param string $idField ID字段名
+ // * @return array
+ // */
+ // public static function findChildren(array $data, int $parentId, string $pidField = 'parent_id', string $idField = 'id'): array
+ // {
+ // $children = [];
+
+ // foreach ($data as $item) {
+ // if ($item[$pidField] == $parentId) {
+ // $children[] = $item;
+ // $grandChildren = static::findChildren($data, $item[$idField], $pidField, $idField);
+ // $children = array_merge($children, $grandChildren);
+ // }
+ // }
+
+ // return $children;
+ // }
+
+ // /**
+ // * 获取指定节点的所有父级节点
+ // *
+ // * @param array $data 数据列表
+ // * @param int $id 当前节点ID
+ // * @param string $pidField 父级字段名
+ // * @param string $idField ID字段名
+ // * @return array
+ // */
+ // public static function getParents(array $data, int $id, string $pidField = 'parent_id', string $idField = 'id'): array
+ // {
+ // $parents = [];
+
+ // foreach ($data as $item) {
+ // if ($item[$idField] == $id) {
+ // if ($item[$pidField] != 0) {
+ // $parents[] = $item;
+ // $parentParents = static::getParents($data, $item[$pidField], $pidField, $idField);
+ // $parents = array_merge($parents, $parentParents);
+ // }
+ // break;
+ // }
+ // }
+
+ // return $parents;
+ // }
+
+ // /**
+ // * 获取指定节点的子孙节点ID列表
+ // *
+ // * @param array $data 数据列表
+ // * @param int $parentId 父级ID
+ // * @param string $pidField 父级字段名
+ // * @param string $idField ID字段名
+ // * @return array
+ // */
+ // public static function getChildrenIds(array $data, int $parentId, string $pidField = 'parent_id', string $idField = 'id'): array
+ // {
+ // $ids = [];
+ // $children = static::findChildren($data, $parentId, $pidField, $idField);
+
+ // foreach ($children as $child) {
+ // $ids[] = $child[$idField];
+ // }
+
+ // return $ids;
+ // }
+
+ // /**
+ // * 检查是否有子节点
+ // *
+ // * @param array $data 数据列表
+ // * @param int $parentId 父级ID
+ // * @param string $pidField 父级字段名
+ // * @return bool
+ // */
+ // public static function hasChildren(array $data, int $parentId, string $pidField = 'parent_id'): bool
+ // {
+ // foreach ($data as $item) {
+ // if ($item[$pidField] == $parentId) {
+ // return true;
+ // }
+ // }
+
+ // return false;
+ // }
+
+
+ // /**
+ // * 构建带有权限列表的资源树(权限不显示在树结构中)
+ // *
+ // * @param array $data 资源数据列表
+ // * @param int $parentId 父级ID
+ // * @param string $pidField 父级字段名
+ // * @param string $idField ID字段名
+ // * @param string $typeField 类型字段名
+ // * @param string $permissionType 权限类型标识
+ // * @param int $level 当前层级
+ // * @return array
+ // */
+ // public static function buildWithAuthList(
+ // array $data,
+ // int $parentId = 0,
+ // string $pidField = 'parent_id',
+ // string $idField = 'id',
+ // string $typeField = 'type',
+ // string $permissionType = 'permission',
+ // int $level = 0
+ // ): array {
+ // $tree = [];
+
+ // foreach ($data as $item) {
+ // // 只处理指定父级的非权限节点
+ // if ($item[$pidField] == $parentId && $item[$typeField] != $permissionType) {
+ // $item['level'] = $level;
+
+ // // 递归处理子节点(非权限类型)
+ // $children = static::buildWithAuthList($data, $item[$idField], $pidField, $idField, $typeField, $permissionType, $level + 1);
+
+ // if (!empty($children)) {
+ // $item['children'] = $children;
+ // }
+
+ // // 收集当前节点下的所有权限
+ // $item['auth_list'] = static::collectDirectPermissions($data, $item[$idField], $pidField, $typeField, $permissionType);
+
+ // $tree[] = $item;
+ // }
+ // }
+
+ // return $tree;
+ // }
+
+ // /**
+ // * 收集指定节点下的直接权限项(只收集直接子节点中的权限)
+ // *
+ // * @param array $data 数据列表
+ // * @param int $parentId 父级ID
+ // * @param string $pidField 父级字段名
+ // * @param string $typeField 类型字段名
+ // * @param string $permissionType 权限类型标识
+ // * @return array
+ // */
+ // public static function collectDirectPermissions1(
+ // array $data,
+ // int $parentId,
+ // string $pidField = 'parent_id',
+ // string $typeField = 'type',
+ // string $permissionType = 'permission'
+ // ): array {
+ // $permissions = [];
+
+ // foreach ($data as $item) {
+ // // 收集直接子节点中的权限类型
+ // if ($item[$pidField] == $parentId && $item[$typeField] == $permissionType) {
+ // $permissions[] = $item;
+ // }
+ // }
+
+ // return $permissions;
+ // }
+
+ // /**
+ // * 使用引用方式构建带有权限列表的资源树(权限不显示在树结构中)
+ // *
+ // * @param array $data 资源数据列表
+ // * @param string $pidField 父级字段名
+ // * @param string $idField ID字段名
+ // * @param string $typeField 类型字段名
+ // * @param string $permissionType 权限类型标识
+ // * @return array
+ // */
+ // public static function buildWithAuthListAndReference(
+ // array $data,
+ // string $pidField = 'parent_id',
+ // string $idField = 'id',
+ // string $typeField = 'type',
+ // string $permissionType = 'permission'
+ // ): array {
+ // $items = [];
+ // $tree = [];
+
+ // // 建立以id为索引的数组(只包含非权限节点)
+ // foreach ($data as $item) {
+ // if ($item[$typeField] != $permissionType) {
+ // $item['level'] = 0;
+ // $items[$item[$idField]] = $item;
+ // }
+ // }
+
+ // // 构建树形结构
+ // foreach ($items as $id => $item) {
+ // if ($item[$pidField] == 0) {
+ // $tree[] = &$items[$id];
+ // } else {
+ // if (isset($items[$item[$pidField]])) {
+ // $items[$id]['level'] = $items[$item[$pidField]]['level'] + 1;
+
+ // if (!isset($items[$item[$pidField]]['children'])) {
+ // $items[$item[$pidField]]['children'] = [];
+ // }
+
+ // $items[$item[$pidField]]['children'][] = &$items[$id];
+ // }
+ // }
+ // }
+
+ // // 为每个节点添加权限列表
+ // foreach ($items as &$item) {
+ // $item['auth_list'] = static::collectDirectPermissions($data, $item[$idField], $pidField, $typeField, $permissionType);
+ // }
+
+ // return $tree;
+ // }
+
+
+
+ // 8.13 add
/**
- * 递归构建树形结构
+ * 构建前端需要的菜单树结构
*
- * @param array $data 数据列表
+ * @param array $data 服务端菜单数据
* @param int $parentId 父级ID
- * @param string $pidField 父级字段名
- * @param string $idField ID字段名
- * @param int $level 当前层级
* @return array
*/
- public static function build(array $data, int $parentId = 0, string $pidField = 'parent_id', string $idField = 'id', int $level = 0): array
+ public static function buildMenuTree(array $data, int $parentId = 0): array
{
$tree = [];
foreach ($data as $item) {
- if ($item[$pidField] == $parentId) {
- $item['level'] = $level;
- $children = static::build($data, $item[$idField], $pidField, $idField, $level + 1);
+ // 只处理非权限类型的节点
+ if ($item['parent_id'] == $parentId && $item['type'] != 'permission') {
+ // 转换数据结构以匹配前端需要的格式
+ $menuItem = [
+ 'id' => $item['id'],
+ 'path' => $item['url'] ?? '',
+ 'name' => $item['name'],
+ 'component' => $item['url'] ?? '',
+ 'meta' => [
+ 'title' => $item['name'],
+ 'icon' => $item['icon'] ?? '',
+ 'isHide' => !empty($item['is_hide']),
+ 'isHideTab' => !empty($item['is_hide_tab']),
+ 'keepAlive' => !empty($item['keep_alive']),
+ ]
+ ];
- if (!empty($children)) {
- $item['children'] = $children;
+ // 收集当前节点下的所有权限
+ $permissions = static::collectDirectPermissions($data, $item['id']);
+ if (!empty($permissions)) {
+ $authList = [];
+ foreach ($permissions as $permission) {
+ $authList[] = [
+ 'id' => $permission['id'],
+ 'title' => $permission['name'] ?? '',
+ 'authMark' => $permission['code'] ?? '',
+ 'selected' => isset($permission['selected']) && $permission['selected'] ? true : false
+ ];
+ }
+ $menuItem['meta']['authList'] = $authList;
}
- $tree[] = $item;
+ // 将服务端额外字段放入meta中
+ $extraFields = static::getExtraFields($item);
+ if (!empty($extraFields)) {
+ $menuItem['meta'] = array_merge($menuItem['meta'], $extraFields);
+ }
+
+ // 递归处理子菜单
+ $children = static::buildMenuTree($data, $item['id']);
+ if (!empty($children)) {
+ $menuItem['children'] = $children;
+ }
+
+ $tree[] = $menuItem;
}
}
@@ -35,201 +385,72 @@ class Tree
}
/**
- * 使用引用方式构建树形结构(性能更好)
+ * 使用引用方式构建前端需要的菜单树结构(性能更好)
*
- * @param array $data 数据列表
- * @param string $pidField 父级字段名
- * @param string $idField ID字段名
+ * @param array $data 服务端菜单数据
* @return array
*/
- public static function buildWithReference(array $data, string $pidField = 'parent_id', string $idField = 'id'): array
+ public static function buildMenuTreeWithReference(array $data): array
{
$items = [];
$tree = [];
- // 建立以id为索引的数组
- foreach ($data as $item) {
- $item['level'] = 0;
- $items[$item[$idField]] = $item;
+ // 过滤出菜单类型的节点并转换数据结构
+ $menuData = array_filter($data, function($item) {
+ return ($item['type'] ?? '') != 'permission';
+ });
+
+ // 建立以id为索引的数组并转换数据结构
+ foreach ($menuData as $item) {
+ $items[$item['id']] = [
+ 'id' => $item['id'],
+ 'path' => $item['url'] ?? '',
+ 'name' => $item['name'],
+ 'component' => $item['url'] ?? '',
+ 'meta' => [
+ 'title' => $item['name'],
+ 'icon' => $item['icon'] ?? '',
+ 'isHide' => !empty($item['is_hide']),
+ 'isHideTab' => !empty($item['is_hide_tab']),
+ 'keepAlive' => !empty($item['keep_alive']),
+ ]
+ ];
+
+ // 将服务端额外字段放入meta中
+ $extraFields = static::getExtraFields($item);
+ if (!empty($extraFields)) {
+ $items[$item['id']]['meta'] = array_merge($items[$item['id']]['meta'], $extraFields);
+ }
}
// 构建树形结构
- foreach ($items as $id => $item) {
- if ($item[$pidField] == 0) {
- $tree[] = &$items[$id];
+ foreach ($menuData as $item) {
+ if ($item['parent_id'] == 0) {
+ $tree[] = &$items[$item['id']];
} else {
- if (isset($items[$item[$pidField]])) {
- $items[$id]['level'] = $items[$item[$pidField]]['level'] + 1;
-
- if (!isset($items[$item[$pidField]]['children'])) {
- $items[$item[$pidField]]['children'] = [];
+ if (isset($items[$item['parent_id']])) {
+ if (!isset($items[$item['parent_id']]['children'])) {
+ $items[$item['parent_id']]['children'] = [];
}
-
- $items[$item[$pidField]]['children'][] = &$items[$id];
+ $items[$item['parent_id']]['children'][] = &$items[$item['id']];
}
}
}
- return $tree;
- }
-
- /**
- * 将树形结构扁平化为列表(带层级前缀)
- *
- * @param array $tree 树形结构数据
- * @param string $prefix 前缀字符
- * @param string $field 显示字段名
- * @param int $level 层级
- * @return array
- */
- public static function flattenWithPrefix(array $tree, string $prefix = '--', string $field = 'name', int $level = 0): array
- {
- $result = [];
-
- foreach ($tree as $item) {
- $item[$field . '_with_prefix'] = str_repeat($prefix, $item['level']) . $item[$field];
- $result[] = $item;
-
- if (isset($item['children']) && !empty($item['children'])) {
- $children = static::flattenWithPrefix($item['children'], $prefix, $field, $item['level'] + 1);
- $result = array_merge($result, $children);
- }
- }
-
- return $result;
- }
-
- /**
- * 根据父ID查找所有子节点(包括子孙节点)
- *
- * @param array $data 数据列表
- * @param int $parentId 父级ID
- * @param string $pidField 父级字段名
- * @param string $idField ID字段名
- * @return array
- */
- public static function findChildren(array $data, int $parentId, string $pidField = 'parent_id', string $idField = 'id'): array
- {
- $children = [];
-
- foreach ($data as $item) {
- if ($item[$pidField] == $parentId) {
- $children[] = $item;
- $grandChildren = static::findChildren($data, $item[$idField], $pidField, $idField);
- $children = array_merge($children, $grandChildren);
- }
- }
-
- return $children;
- }
-
- /**
- * 获取指定节点的所有父级节点
- *
- * @param array $data 数据列表
- * @param int $id 当前节点ID
- * @param string $pidField 父级字段名
- * @param string $idField ID字段名
- * @return array
- */
- public static function getParents(array $data, int $id, string $pidField = 'parent_id', string $idField = 'id'): array
- {
- $parents = [];
-
- foreach ($data as $item) {
- if ($item[$idField] == $id) {
- if ($item[$pidField] != 0) {
- $parents[] = $item;
- $parentParents = static::getParents($data, $item[$pidField], $pidField, $idField);
- $parents = array_merge($parents, $parentParents);
+ // 为每个节点添加权限列表
+ foreach ($items as &$item) {
+ $parentId = $item['id'];
+ $permissions = static::collectDirectPermissions($data, $parentId);
+ if (!empty($permissions)) {
+ $authList = [];
+ foreach ($permissions as $permission) {
+ $authList[] = [
+ 'id' => $permission['id'],
+ 'title' => $permission['name'] ?? '',
+ 'authMark' => $permission['code'] ?? ''
+ ];
}
- break;
- }
- }
-
- return $parents;
- }
-
- /**
- * 获取指定节点的子孙节点ID列表
- *
- * @param array $data 数据列表
- * @param int $parentId 父级ID
- * @param string $pidField 父级字段名
- * @param string $idField ID字段名
- * @return array
- */
- public static function getChildrenIds(array $data, int $parentId, string $pidField = 'parent_id', string $idField = 'id'): array
- {
- $ids = [];
- $children = static::findChildren($data, $parentId, $pidField, $idField);
-
- foreach ($children as $child) {
- $ids[] = $child[$idField];
- }
-
- return $ids;
- }
-
- /**
- * 检查是否有子节点
- *
- * @param array $data 数据列表
- * @param int $parentId 父级ID
- * @param string $pidField 父级字段名
- * @return bool
- */
- public static function hasChildren(array $data, int $parentId, string $pidField = 'parent_id'): bool
- {
- foreach ($data as $item) {
- if ($item[$pidField] == $parentId) {
- return true;
- }
- }
-
- return false;
- }
-
-
- /**
- * 构建带有权限列表的资源树(权限不显示在树结构中)
- *
- * @param array $data 资源数据列表
- * @param int $parentId 父级ID
- * @param string $pidField 父级字段名
- * @param string $idField ID字段名
- * @param string $typeField 类型字段名
- * @param string $permissionType 权限类型标识
- * @param int $level 当前层级
- * @return array
- */
- public static function buildWithAuthList(
- array $data,
- int $parentId = 0,
- string $pidField = 'parent_id',
- string $idField = 'id',
- string $typeField = 'type',
- string $permissionType = 'permission',
- int $level = 0
- ): array {
- $tree = [];
-
- foreach ($data as $item) {
- // 只处理指定父级的非权限节点
- if ($item[$pidField] == $parentId && $item[$typeField] != $permissionType) {
- $item['level'] = $level;
-
- // 递归处理子节点(非权限类型)
- $children = static::buildWithAuthList($data, $item[$idField], $pidField, $idField, $typeField, $permissionType, $level + 1);
-
- if (!empty($children)) {
- $item['children'] = $children;
- }
-
- // 收集当前节点下的所有权限
- $item['auth_list'] = static::collectDirectPermissions($data, $item[$idField], $pidField, $typeField, $permissionType);
-
- $tree[] = $item;
+ $item['meta']['authList'] = $authList;
}
}
@@ -241,23 +462,15 @@ class Tree
*
* @param array $data 数据列表
* @param int $parentId 父级ID
- * @param string $pidField 父级字段名
- * @param string $typeField 类型字段名
- * @param string $permissionType 权限类型标识
* @return array
*/
- public static function collectDirectPermissions(
- array $data,
- int $parentId,
- string $pidField = 'parent_id',
- string $typeField = 'type',
- string $permissionType = 'permission'
- ): array {
+ public static function collectDirectPermissions(array $data, int $parentId): array
+ {
$permissions = [];
foreach ($data as $item) {
// 收集直接子节点中的权限类型
- if ($item[$pidField] == $parentId && $item[$typeField] == $permissionType) {
+ if ($item['parent_id'] == $parentId && ($item['type'] ?? '') == 'permission') {
$permissions[] = $item;
}
}
@@ -266,55 +479,27 @@ class Tree
}
/**
- * 使用引用方式构建带有权限列表的资源树(权限不显示在树结构中)
+ * 获取需要放入meta的额外字段
*
- * @param array $data 资源数据列表
- * @param string $pidField 父级字段名
- * @param string $idField ID字段名
- * @param string $typeField 类型字段名
- * @param string $permissionType 权限类型标识
+ * @param array $item 菜单项
* @return array
*/
- public static function buildWithAuthListAndReference(
- array $data,
- string $pidField = 'parent_id',
- string $idField = 'id',
- string $typeField = 'type',
- string $permissionType = 'permission'
- ): array {
- $items = [];
- $tree = [];
+ public static function getExtraFields(array $item): array
+ {
+ $extraFields = [];
+ $standardFields = [
+ 'id', 'name', 'url', 'icon', 'is_hide', 'is_hide_tab', 'keep_alive', 'level'
+ ];
- // 建立以id为索引的数组(只包含非权限节点)
- foreach ($data as $item) {
- if ($item[$typeField] != $permissionType) {
- $item['level'] = 0;
- $items[$item[$idField]] = $item;
+ foreach ($item as $key => $value) {
+ // 如果不是标准字段且不是auth_list,则放入extraFields
+ if (!in_array($key, $standardFields) && $key !== 'auth_list') {
+ // 转换下划线命名到驼峰命名
+ $camelKey = lcfirst(str_replace(' ', '', ucwords(str_replace('_', ' ', $key))));
+ $extraFields[$camelKey] = $value;
}
}
- // 构建树形结构
- foreach ($items as $id => $item) {
- if ($item[$pidField] == 0) {
- $tree[] = &$items[$id];
- } else {
- if (isset($items[$item[$pidField]])) {
- $items[$id]['level'] = $items[$item[$pidField]]['level'] + 1;
-
- if (!isset($items[$item[$pidField]]['children'])) {
- $items[$item[$pidField]]['children'] = [];
- }
-
- $items[$item[$pidField]]['children'][] = &$items[$id];
- }
- }
- }
-
- // 为每个节点添加权限列表
- foreach ($items as &$item) {
- $item['auth_list'] = static::collectDirectPermissions($data, $item[$idField], $pidField, $typeField, $permissionType);
- }
-
- return $tree;
+ return $extraFields;
}
}
\ No newline at end of file
diff --git a/app/common/utils/scaffold/templates/workOrder.stub b/app/common/utils/scaffold/templates/workOrder.stub
new file mode 100644
index 0000000..c7b6ab0
--- /dev/null
+++ b/app/common/utils/scaffold/templates/workOrder.stub
@@ -0,0 +1,102 @@
+
+
+
+
+
+ 邮箱提醒
+
+
+
+
+
+
+
+
+
+
+ |
+ 你好,
+ 你有新的工单待处理。请及时查看:
+
+
+ - 工单编号:{{code}}
+ - 工单分类:{{category}}
+ - 问题描述:{{content}}
+
+
+ 请点击下方按钮立即查看详情:
+
+
+
+
+ |
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/common/validate/RoleValidate.php b/app/common/validate/RoleValidate.php
index 6b2294e..dec4431 100644
--- a/app/common/validate/RoleValidate.php
+++ b/app/common/validate/RoleValidate.php
@@ -27,6 +27,6 @@ class RoleValidate extends Validate
protected $scene = [
'store' => ['name', 'desc', 'sort'],
- 'update' => ['id', 'system_id', 'name', 'desc', 'sort', 'status'],
+ 'update' => ['id', 'name', 'desc', 'sort', 'status'],
];
}
diff --git a/app/controller/IndexController.php b/app/controller/IndexController.php
index 995f05e..e7ae182 100644
--- a/app/controller/IndexController.php
+++ b/app/controller/IndexController.php
@@ -106,13 +106,21 @@ class IndexController
public function test()
{
- $uidRotator = new \app\common\utils\RedisUidRotator('email_parsed_result_20250804');
- $uidRotator->setUids([64, 63, 83, 93, 108, 118, 131, 132, 145, 150, 143, 156, 157, 158]);
+ // $data = [
+ // 'code' => 'GD20250703-1DC80',
+ // 'category' => '账号类问题',
+ // 'content' => '这是一条测试工单内容。'
+ // ];
+ // \app\common\utils\email\MailSend::send('mzl@hnxinyouhe.com', '【XYH】新的工单提醒', replaceTpl($data, 'work_order'));
- $assignedUid = $uidRotator->next();
+ // $options = [
+ // 'corp_id' => 3,
+ // 'user_id' => 666
+ // ];
+ // \Webman\RedisQueue\Redis::send('perm-agt', $options);
- flg($assignedUid, false);
-
- return $assignedUid;
+ $res = Cache::delete("user_resources_74_fdesk");
+
+ return 22;
}
}
\ No newline at end of file
diff --git a/app/functions.php b/app/functions.php
index 59fdd24..299bad4 100644
--- a/app/functions.php
+++ b/app/functions.php
@@ -158,9 +158,10 @@ if (!function_exists('sendFeishuTextMessage')) {
* 替换模板
*/
if (!function_exists('replaceTpl')) {
- function replaceTpl(array $data = [], string $templatePath = 'common/utils/scaffold/templates/Email.stub')
+ function replaceTpl(array $data = [], string $tplKey = 'email')
{
- if (!$data || !$templatePath) return '';
+ if (!$data || !$tplKey) return '';
+ $templatePath = tplUrl($tplKey);
$templatePath = app_path($templatePath);
$content = file_get_contents($templatePath);
foreach ($data as $key => $value) {
@@ -170,6 +171,20 @@ if (!function_exists('replaceTpl')) {
}
}
+/**
+ * 模板路径
+ */
+if (!function_exists('tplUrl')) {
+ function tplUrl(string $tplKey = '')
+ {
+ $urlArray = [
+ 'email' => 'common/utils/scaffold/templates/Email.stub',
+ 'work_order' => 'common/utils/scaffold/templates/workOrder.stub'
+ ];
+ return $urlArray[$tplKey];
+ }
+}
+
/**
* 替换模板
*/
diff --git a/app/queue/redis/PermAgt.php b/app/queue/redis/PermAgt.php
new file mode 100644
index 0000000..42a4898
--- /dev/null
+++ b/app/queue/redis/PermAgt.php
@@ -0,0 +1,95 @@
+ $userId,
+ 'role_id' => 1
+ ],
+ [
+ 'user_id' => $userId,
+ 'role_id' => 2
+ ],
+ ];
+ $systems = [
+ [
+ 'user_id' => $userId,
+ 'system_id' => 1
+ ],
+ [
+ 'user_id' => $userId,
+ 'system_id' => 2
+ ],
+ [
+ 'user_id' => $userId,
+ 'system_id' => 3
+ ],
+ ];
+ } else {
+ $roles = [
+ 'user_id' => $userId,
+ 'role_id' => 2
+ ];
+ $systems = [
+ [
+ 'user_id' => $userId,
+ 'system_id' => 2
+ ],
+ [
+ 'user_id' => $userId,
+ 'system_id' => 3
+ ],
+ ];
+ }
+
+ Db::table('user_roles')->insert($roles);
+ Db::table('user_systems')->insert($systems);
+ return true;
+ } catch (\Throwable $th) {
+ throw new BusinessException($th->getFile()."|".$th->getLine()."|".$th->getMessage());
+ }
+ }
+
+ // 消费失败回调
+ /*
+ $package = [
+ 'id' => 1357277951, // 消息ID
+ 'time' => 1709170510, // 消息时间
+ 'delay' => 0, // 延迟时间
+ 'attempts' => 2, // 消费次数
+ 'queue' => 'send-mail', // 队列名
+ 'data' => ['to' => 'tom@gmail.com', 'content' => 'hello'], // 消息内容
+ 'max_attempts' => 5, // 最大重试次数
+ 'error' => '错误信息' // 错误信息
+ ]
+ */
+ public function onConsumeFailure(\Throwable $e, $package)
+ {
+ echo "consume failure\n";
+ echo $e->getMessage() . "\n";
+ // 无需反序列化
+ var_export($package);
+ }
+}
\ No newline at end of file
diff --git a/config/route.php b/config/route.php
index b932c74..3da8dd6 100644
--- a/config/route.php
+++ b/config/route.php
@@ -60,6 +60,9 @@ Route::group('/common', function () {
Route::any('/corps', [app\common\controller\PresetController::class, 'corps']);
Route::any('/feishu/app', [app\common\controller\PresetController::class, 'feishuApp']);
Route::any('/systems', [app\common\controller\PresetController::class, 'systems']);
+ Route::any('/roles', [app\common\controller\PresetController::class, 'roles']);
+ Route::any('/cache/clear', [app\common\controller\PresetController::class, 'cacheClear']);
+ Route::any('/menus', [app\common\controller\PresetController::class, 'menus']);
});
});
@@ -214,7 +217,7 @@ Route::group('/crm', function () {
Route::any('/upload/image', [app\common\controller\UploadController::class, 'uploadImage']);
- Route::any('/user/edit', [app\common\controller\UserController::class, 'edit']);
- Route::any('/user/savePassword', [app\common\controller\UserController::class, 'savePassword']);
+ Route::any('/user/edit', [app\common\controller\UserController::class, 'edit']);
+ Route::any('/user/savePassword', [app\common\controller\UserController::class, 'savePassword']);
});