Merge branch 'develop'

This commit is contained in:
mzl
2025-08-14 20:08:57 +08:00
parent f4a2966b1e
commit 39e840bba8
49 changed files with 2240 additions and 522 deletions
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\admin\service\CorpFeishuAppService;
class CorpFeishuAppController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new CorpFeishuAppService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
return $data;
}
}
@@ -0,0 +1,33 @@
<?php
namespace app\admin\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\admin\service\CountryService;
class CountryController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new CountryService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
}
+12 -86
View File
@@ -5,6 +5,7 @@ namespace app\admin\controller;
use app\common\controller\BaseController;
use app\admin\service\RoleService;
use app\common\traits\crud\CrudControllerTrait;
use support\Request;
class RoleController extends BaseController
{
@@ -27,91 +28,16 @@ class RoleController extends BaseController
return $data;
}
// /**
// * 获取角色列表
// */
// public function list(Request $request)
// {
// try {
// $params = $request->all();
// $result = (new RoleService())->getRoleList($params);
// // 定义后处理器
// $postProcessor = function($formattedData) {
// if (isset($formattedData['created_at']))
// $formattedData['created_at'] = date('Y-m-d H:i:s', $formattedData['created_at']);
// if (isset($formattedData['updated_at']))
// $formattedData['updated_at'] = date('Y-m-d H:i:s', $formattedData['updated_at']);
// return $formattedData;
// };
// return $this->success($this->transformData($result, $postProcessor), '角色列表获取成功');
// } catch (\Throwable $e) {
// return $this->error($e->getMessage());
// }
// }
// /**
// * 获取角色详情
// */
// public function detail(Request $request)
// {
// try {
// $roleId = $request->get('role_id');
// if (!$roleId) {
// return $this->error('缺少角色ID', 422);
// }
// $result = (new RoleService())->getRoleDetail($roleId);
// return $this->success($result, '角色详情获取成功');
// } catch (\Throwable $e) {
// return $this->error($e->getMessage());
// }
// }
// /**
// * 增加角色
// */
// public function create(Request $request)
// {
// try {
// $data = $this->validate(new RoleValidate(), $request->post(), 'role_create');
// $result = (new RoleService())->createRole($data);
// return $this->success($result, '角色创建成功');
// } catch (\think\exception\ValidateException $e) {
// return $this->error($e->getError(), 422);
// } catch (\Throwable $e) {
// return $this->error($e->getMessage());
// }
// }
// /**
// * 修改角色
// */
// public function update(Request $request)
// {
// try {
// $data = $this->validate(new RoleValidate(), $request->post(), 'role_update');
// $result = (new RoleService())->updateRole($data);
// return $this->success($result, '角色更新成功');
// } catch (\think\exception\ValidateException $e) {
// return $this->error($e->getError(), 422);
// } catch (\Throwable $e) {
// return $this->error($e->getMessage());
// }
// }
// /**
// * 删除角色
// */
// public function delete(Request $request)
// {
// try {
// $roleId = $request->get('role_id');
// if (!$roleId) {
// return $this->error('缺少角色ID', 422);
// }
// (new RoleService())->deleteRole($roleId);
// return $this->success([], '角色删除成功');
// } catch (\Throwable $e) {
// return $this->error($e->getMessage());
// }
// }
public function menuResource(Request $request)
{
try {
// $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) {
return $this->error($e->getMessage());
}
}
}
+8 -8
View File
@@ -22,12 +22,12 @@ class SystemsController extends BaseController
* @param array $data
* @return array
*/
// protected function indexFormat($data)
// {
// if (isset($data['created_at']))
// $data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
// if (isset($data['updated_at']))
// $data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
// return $data;
// }
protected function indexFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
return $data;
}
}
+128 -26
View File
@@ -6,6 +6,7 @@ use app\common\model\Corp;
use app\common\traits\crud\CrudControllerTrait;
use app\admin\service\UserService;
use app\crm\model\Customer;
use support\Request;
class UserController extends BaseController
{
@@ -17,6 +18,60 @@ class UserController extends BaseController
{
$this->service = new UserService();
}
/**
* 更新
*
* @param Request $request 请求对象
* @return mixed 返回更新成功的数据或信息
*/
public function update(Request $request)
{
try {
$data = $request->post();
$res = $this->service->serviceUpdateUser($request, $data);
return $this->success([], '更新成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 修改密码
* @param Request $request
* @return \support\Response
* @throws \Random\RandomException
*/
public function savePassword(Request $request)
{
try {
$data = $request->all();
if(empty($data['password'])){
return $this->error('密码不能为空');
}
$result = $this->service->savePassword($request,$data);;
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 更新
*
* @param Request $request 请求对象
* @return mixed 返回更新成功的数据或信息
*/
public function edit(Request $request)
{
try {
$data = $request->post();
$data['id'] = $data['id']??$request->user->uid;
$res = $this->service->serviceUpdate($request, $data);
return $this->success([], '更新成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 列表页数据格式化
@@ -24,32 +79,79 @@ class UserController extends BaseController
* @param array $data
* @return array
*/
protected function indexFormat($data)
{
if(isset($data['records'])){
return $data;
}
$data['sex_txt'] = '';
if (isset($data['sex']) && $data['sex'] == 1){
$data['sex_txt'] = '男';
}elseif(isset($data['sex']) && $data['sex'] == 2){
$data['sex_txt'] = '女';
}
protected function indexFormat($data)
{
if(isset($data['records'])){
return $data;
}
$data['sex_txt'] = '';
if (isset($data['sex']) && $data['sex'] == 1){
$data['sex_txt'] = '男';
}elseif(isset($data['sex']) && $data['sex'] == 2){
$data['sex_txt'] = '女';
}
switch ($data['status']){
case 1:
$data['status_txt'] = '正常';
break;
case 2:
$data['status_txt'] = '离职';
break;
default:
$data['status_txt'] = '';
break;
}
switch ($data['status']){
case 1:
$data['status_txt'] = '正常';
break;
case 2:
$data['status_txt'] = '离职';
break;
default:
$data['status_txt'] = '';
break;
}
$data['corp_name'] = Corp::where('id', $data['corp_id'])->value('name');
$data['customer_count'] = Customer::where('user_id', $data['id'])->count();
return $data;
}
$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;
}
/**
* 授权
*
* @param Request $request
* @return mixed
*/
public function authorize(Request $request)
{
try {
$data = $request->post();
$res = $this->service->authorize($request, $data);
return $this->success([], '分配成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 直接授权
*
* @param Request $request
* @return mixed
*/
public function updateResources(Request $request)
{
try {
$data = $request->post();
$res = $this->service->updateResources($request, $data);
return $this->success([], '分配成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
+76
View File
@@ -4,6 +4,7 @@ namespace app\admin\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\User;
use support\Db;
class UserLogic
{
@@ -14,4 +15,79 @@ class UserLogic
*/
protected string $modelClass = User::class;
public function updateRoleAndSystem(array $data): bool
{
$roleIds = $data['role_ids'] ?? [];
$systemIds = $data['system_ids'] ?? [];
$userId = $data['user_id'] ?? 0;
Db::beginTransaction();
try {
Db::table('user_roles')->where('user_id', $userId)->delete();
if (count($roleIds) > 0) {
$insertDataRole = [];
foreach ($roleIds as $roleId) {
$insertDataRole[] = [
'role_id' => $roleId,
'user_id' => $userId
];
}
Db::table('user_roles')->insert($insertDataRole);
}
Db::table('user_systems')->where('user_id', $userId)->delete();
if (count($systemIds) > 0) {
$insertDataSystem = [];
foreach ($systemIds as $systemId) {
$insertDataSystem[] = [
'system_id' => $systemId,
'user_id' => $userId
];
}
Db::table('user_systems')->insert($insertDataSystem);
}
Db::commit();
return true;
} catch (\Throwable $exception) {
Db::rollBack();
return false;
}
}
public function updateUserResources(array $data): bool
{
$userId = $data['user_id'] ?? 0;
$resources = $data['resources'] ?? [];
if (empty($userId)) {
return false;
}
Db::beginTransaction();
try {
// 删除原有资源关联
Db::table('user_resources')->where('user_id', $userId)->delete();
// 批量插入新角色资源关联
if (!empty($resources)) {
$insertData = [];
foreach ($resources as $resourceId) {
$insertData[] = [
'user_id' => $userId,
'resource_id' => $resourceId
];
}
Db::table('user_resources')->insert($insertData);
}
Db::commit();
return true;
} catch (\Throwable $exception) {
Db::rollBack();
return false;
}
}
}
@@ -0,0 +1,70 @@
<?php
namespace app\admin\service;
use app\common\validate\CorpFeishuAppValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\CorpFeishuApp;
use support\Request;
class CorpFeishuAppService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = CorpFeishuApp::class;
/**
* 验证器
*/
protected $validator = CorpFeishuAppValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
/**
* 构建查询条件
*/
protected function buildQueryConfig(Request $request, string $scene = 'index'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'index':
$config['where']['status'] = 1;
$config['order']['sort'] = 'desc';
$config['order']['id'] = 'desc';
$config['with']['corpInfo'][] = 'name';
$config['with']['systemInfo'][] = 'name';
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
$config['with']['corpInfo'][] = 'name';
$config['with']['systemInfo'][] = 'name';
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $config;
}
}
+31
View File
@@ -0,0 +1,31 @@
<?php
namespace app\admin\service;
use app\common\validate\CountryValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\CorpFeishuApp;
class CountryService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = CorpFeishuApp::class;
/**
* 验证器
*/
protected $validator = CountryValidate::class;
/**
* 获取验证器
*/
public function getValidator()
{
return new $this->validator;
}
}
+19
View File
@@ -2,9 +2,11 @@
namespace app\admin\service;
use app\common\logic\RoleLogic;
use app\common\validate\RoleValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Role;
use support\Db;
class RoleService
{
@@ -28,4 +30,21 @@ class RoleService
{
return new $this->validator;
}
/**
* 获取角色资源
*/
public function menuResource($request, $data)
{
return (new RoleLogic)->updateRoleResources($data);
}
public function getList()
{
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
return $this->logicListAll($config);
}
}
+69 -2
View File
@@ -2,9 +2,11 @@
namespace app\admin\service;
use app\admin\validate\UserValidate;
use app\admin\logic\UserLogic;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\User;
use app\common\validate\UserValidate;
use support\exception\BusinessException;
use support\Request;
class UserService
@@ -30,6 +32,25 @@ class UserService
return new $this->validator;
}
/** 更新 */
public function serviceUpdateUser(Request $request, array $data = [])
{
$data = $this->preProcess('Update', $data, $request);
$config = $this->buildQueryConfig($request, 'update');
$updated = $this->logicUpdateBy($config, $data);
return $this->postProcess('Update', $data);
}
/** 更新 */
public function serviceUpdate(Request $request, array $data = [])
{
unset($data['roles']);
unset($data['corp_name']);
$data = $this->preProcess('Update', $data, $request);
$config = $this->buildQueryConfig($request, 'edit');
$updated = $this->logicUpdateBy($config, $data);
return $this->postProcess('Update', $data);
}
/**
* 构造 CrudUtil 查询配置
@@ -61,7 +82,12 @@ class UserService
$config['where']['id'] = $p['id'];
}
$config['fields'] = ['id','sn', 'name', 'email', 'sex', 'phone', 'avatar', 'corp_id', 'country_id', 'position', 'status', 'created_at'];
$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'];
@@ -74,8 +100,49 @@ class UserService
case 'destroy':
$config['where']['id'] = $p['id'];
break;
case 'savePassword':
$config['where']['id'] = $p['id'];
break;
case 'edit':
$config['where']['id'] = $request->user->uid;
break;
}
return $config;
}
/**
* 授权
*
* @param Request $request
* @param array $data
* @return bool
*/
public function authorize(Request $request, array $data = [])
{
return (new UserLogic())->updateRoleAndSystem($data);
}
/**
* 更新用户资源权限
*
* @param Request $request
* @param array $data
* @return bool
*/
public function updateResources(Request $request, array $data = [])
{
return (new UserLogic())->updateUserResources($data);
}
/**
* 修改用户密码
*/
public function savePassword(Request $request, array $data)
{
$config = $this->buildQueryConfig($request, 'savePassword');
$data['password'] = password_hash($data['password'], PASSWORD_DEFAULT);
$user = $this->logicUpdateBy($config,$data);
return $user;
}
}
+2 -1
View File
@@ -44,7 +44,8 @@ class CustomerCodeCommand extends SymfonyCommand
$code = 'CM' . $unique;
}
$obtain = $customer->created_at>strtotime('2025-07-29')?$customer->created_at:strtotime('2025-07-29');
$created_at = $customer->created_at->getTimestamp();
$obtain = $created_at > strtotime('2025-07-29')?$created_at:strtotime('2025-07-29');
$customer->code = $code;
$customer->obtain = $obtain;
@@ -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());
}
}
}
@@ -0,0 +1,82 @@
<?php
namespace app\common\controller;
use app\common\controller\BaseController;
use app\common\traits\crud\CrudControllerTrait;
use app\common\service\ResourceService;
use support\Request;
class ResourceController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new ResourceService();
}
/**
* 列表页数据格式化
*
* @param array $data
* @return array
*/
protected function allFormat($data)
{
if (isset($data['created_at']))
$data['created_at'] = date('Y-m-d H:i:s', $data['created_at']);
if (isset($data['updated_at']))
$data['updated_at'] = date('Y-m-d H:i:s', $data['updated_at']);
return $data;
}
/**
* 资源树形结构
*/
public function listTree(Request $request)
{
try {
$raw = $this->service->serviceListAll($request);
$data = $this->transformData($raw, method_exists($this, 'allFormat')
? [$this, 'allFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 菜单
*/
public function menuList(Request $request)
{
try {
$raw = $this->service->serviceMenuList($request);
$data = $this->transformData($raw, method_exists($this, 'allFormat')
? [$this, 'allFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 菜单
*/
public function menuTreeList(Request $request)
{
try {
$raw = $this->service->serviceMenuTreeList($request);
$data = $this->transformData($raw, method_exists($this, 'allFormat')
? [$this, 'allFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
+99 -50
View File
@@ -2,14 +2,63 @@
namespace app\common\controller;
use app\common\traits\crud\CrudControllerTrait;
use support\Request;
use app\common\service\UserService;
class UserController extends BaseController
{
use CrudControllerTrait;
protected $service;
public function __construct()
{
$this->service = new UserService();
}
/**
* 用户信息
* 获取组员列表
*
* @param Request $request 请求对象
* @return mixed 返回分页数据及总数
*/
public function team(Request $request)
{
try {
$raw = $this->service->serviceTeam($request);
$data = $this->transformData($raw, method_exists($this, 'indexFormat')
? [$this, 'indexFormat']
: null);
return $this->success($data);
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 标记组员离职
*
* @param Request $request 请求对象
* @return mixed 返回分页数据及总数
*/
public function resignMember(Request $request)
{
try {
$userId = $request->get('user_id');
if(empty($userId)){
return $this->error('组员id不能为空');
}
$user = $this->service->resignMember($request);
return $this->success($user, '获取用户信息');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 用户信息(登录后调用)
*/
public function info(Request $request)
{
@@ -23,57 +72,57 @@ class UserController extends BaseController
}
}
/**
* 用户信息
*/
public function getUser(Request $request)
{
try {
$uid = $request->user->uid;
$corp_abbr = $request->basic_data['corp_abbr'];
$result = (new UserService())->getUser($uid,$corp_abbr);
return $this->success($result, '获取用户信息');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 用户信息
*/
public function getUser(Request $request)
{
try {
$uid = $request->user->uid;
$corp_abbr = $request->basic_data['corp_abbr'];
$result = (new UserService())->getUser($uid,$corp_abbr);
return $this->success($result, '获取用户信息');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 编辑用户
*/
public function edit(Request $request)
{
try {
$uid = $request->user->uid;
$data = $request->all();
$result = (new UserService())->edit($uid,$data);
unset($result['password']);
unset($result['union_id']);
unset($result['open_id']);
unset($result['tenant_key']);
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 编辑用户
*/
public function edit(Request $request)
{
try {
$uid = $request->user->uid;
$data = $request->all();
$result = (new UserService())->edit($uid,$data);
unset($result['password']);
unset($result['union_id']);
unset($result['open_id']);
unset($result['tenant_key']);
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 修改密码
* @param Request $request
* @return \support\Response
* @throws \Random\RandomException
*/
public function savePassword(Request $request)
{
try {
$uid = $request->user->uid;
$data = $request->all();
$result = (new UserService())->savePassword($uid,$data);;
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
/**
* 修改密码
* @param Request $request
* @return \support\Response
* @throws \Random\RandomException
*/
public function savePassword(Request $request)
{
try {
$uid = $request->user->uid;
$data = $request->all();
$result = (new UserService())->savePassword($uid,$data);;
return $this->success($result, '编辑成功');
} catch (\Throwable $e) {
return $this->error($e->getMessage());
}
}
}
+6
View File
@@ -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'],
+7
View File
@@ -2,10 +2,17 @@
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\CorpFeishuApp;
class CorpFeishuAppLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = CorpFeishuApp::class;
public function getFeishuApp($corpId, $systemId)
+17
View File
@@ -0,0 +1,17 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\CorpFeishuApp;
class CountryLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = CorpFeishuApp::class;
}
+8 -150
View File
@@ -23,13 +23,16 @@ class PermLogic
}
$all = collect();
// 合并角色资源,排除不需要权限认证的资源
// 遍历用户的所有角色,合并每个角色所拥有的资源
foreach ($user->roles as $role) {
$all = $all->merge(
// 过滤当前角色的资源列表
$role->resources->filter(function ($resource) use($system) {
// var_dump($resource->toArray());
// 只保留需要权限认证的资源(或公共资源)
// 资源过滤条件:
// 1. !$resource->no_auth:资源需要权限认证(排除公共资源)
// 2. 系统匹配条件:
// - $system->id == $resource->system_id:资源属于当前系统
// - $resource->system_id == 0:资源为全局公共资源(所有系统可用)
return !$resource->no_auth && ($system->id == $resource->system_id || $resource->system_id == 0);
})
);
@@ -64,151 +67,6 @@ class PermLogic
* @param string $parentPath 父级完整路径(用于递归)
* @return array
*/
public function buildTree_bak($resources, int $parentId = 0, string $parentPath = ''): array
{
$tree = [];
foreach ($resources as $node) {
// 只处理目录和菜单,不把 permission 当作菜单节点
if ($node->parent_id !== $parentId || $node->type === 'permission') {
continue;
}
// —— 拼接当前节点的完整路径 ——
$currentPath = rtrim($parentPath, '/');
if (!empty($node->url)) {
$urlSegment = ltrim($node->url, '/');
$currentPath = $currentPath === ''
? $urlSegment
: "{$currentPath}/{$urlSegment}";
}
// —— 递归构建子节点 ——
// 这里只会继续收目录和菜单,permission 永远不会进入此方法
$children = $this->buildTree($resources, $node->id, $currentPath);
// —— 本级权限列表 ——
$authList = $resources
->filter(fn($r) => $r->type === 'permission' && $r->parent_id === $node->id)
->map(fn($r) => [
'id' => $r->id,
'title' => $r->name,
'auth_mark' => $r->code,
])
->values()
->all();
// —— 判断是否有子目录或子菜单 ——
$hasChildrenMenu = ! empty($children);
// —— 计算 component 字段 ——
if ($hasChildrenMenu) {
if ($node->type === 'menu' && !empty($node->url)) {
// menu 类型且自身有 url,用自身 url(加前导 slash)
$componentValue = '/' . ltrim($node->url, '/');
} else {
// 其它含子项的节点,固定 Layout 组件
$componentValue = '/index/index';
}
} else {
// 叶子节点:拼接路径,确保以 slash 开头
$componentValue = $currentPath !== ''
? '/' . ltrim($currentPath, '/')
: '';
}
$tree[] = [
'id' => $node->id,
'name' => $node->name,
'path' => $node->url,
'component' => $componentValue,
'meta' => [
'title' => $node->name,
'icon' => $node->icon,
'keepAlive' => true,
'authList' => $authList,
'openType' => $node->open_type,
'isHide' => $node->is_hide == 0 ? false : true,
'isHideTab' => $node->is_hide_tab == 0 ? false : true
],
'children' => $children
];
}
return $tree;
}
public function buildTree_bak2($resources, int $parentId = 0, string $parentPath = ''): array
{
$tree = [];
foreach ($resources as $node) {
if ($node->parent_id !== $parentId) {
continue;
}
// —— 拼接当前节点的完整路径 ——
$currentPath = $parentPath;
// 去掉末尾多余 slash
$currentPath = rtrim($currentPath, '/');
if (!empty($node->url)) {
// 如果当前节点有 url,则拼接
$urlSegment = ltrim($node->url, '/');
$currentPath = $currentPath === ''
? $urlSegment
: "{$currentPath}/{$urlSegment}";
}
// —— 递归构建子节点 ——
$children = [];
if ($node->type === 'directory') {
$children = $this->buildTree($resources, $node->id, $currentPath);
}
// —— 收集本级的 permission 权限列表 ——
$authList = $resources
->filter(fn($r) => $r->type === 'permission' && $r->parent_id === $node->id)
->map(fn($r) => [
'id' => $r->id,
'title' => $r->name,
'auth_mark' => $r->code,
])
->values()
->all();
// —— 判断是否有子菜单/目录 ——
$hasChildrenMenu = $resources
->where('parent_id', $node->id)
->whereIn('type', ['directory', 'menu'])
->isNotEmpty();
// —— 计算 component 字段 ——
if ($hasChildrenMenu) {
// 只要有子项,就一律显示顶层 Layout 组件
$componentValue = '/index/index';
} else {
// 叶子节点:如果定义了 url,就使用拼接后的路径;否则用占位符(这里用空串,你可替换)
$componentValue = $currentPath !== '' ? $currentPath : '';
}
$tree[] = [
'id' => $node->id,
'name' => $node->name,
'path' => $node->url,
'component' => $componentValue,
'meta' => [
'title' => $node->name,
'icon' => $node->icon,
'keepAlive' => true,
'authList' => $authList,
'openType' => $node->open_type,
],
'children' => $children,
];
}
return $tree;
}
public function buildTree($resources, int $parentId = 0, string $parentPath = ''): array
{
$tree = [];
@@ -258,7 +116,7 @@ class PermLogic
'meta' => [
'title' => $node->name,
'icon' => $node->icon,
'keepAlive' => true,
'keepAlive' => $node->keep_alive == 1 ? true : false,
'authList' => $authList,
'openType' => $node->open_type,
'isHide' => $node->is_hide == 0 ? false : true,
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace app\common\logic;
use app\common\traits\crud\CrudLogicTrait;
use app\common\model\Resource;
use support\Request;
class ResourceLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = Resource::class;
public function logicListAllResources(Request $request, array $config)
{
$data = $this->logicListAll($config);
if ($request->get('role_id')) {
$selected = (new RoleLogic())->getRoleResources($request->get('role_id'));
} else if ($request->get('user_id')) {
$selected = (new RoleLogic())->getUserResources($request->get('user_id'));
} else {
$selected = [];
}
foreach ($data as &$item) {
$item['selected'] = in_array($item['id'], $selected) ? true : false;
}
return $data;
}
}
+74 -3
View File
@@ -2,13 +2,84 @@
namespace app\common\logic;
use app\common\model\Role;
use support\exception\BusinessException;
use support\Log;
use support\Db;
class RoleLogic
{
/**
* 更新角色资源权限
*
* @param array $data 包含 role_id 和 resources 的数据
* @return bool 操作是否成功
*/
public function updateRoleResources(array $data): bool
{
$roleId = $data['role_id'] ?? 0;
$resources = $data['resources'] ?? [];
if (empty($roleId)) {
return false;
}
Db::beginTransaction();
try {
// 删除原有角色资源关联
Db::table('role_resources')->where('role_id', $roleId)->delete();
// 批量插入新角色资源关联
if (!empty($resources)) {
$insertData = [];
foreach ($resources as $resourceId) {
$insertData[] = [
'role_id' => $roleId,
'resource_id' => $resourceId
];
}
Db::table('role_resources')->insert($insertData);
}
Db::commit();
return true;
} catch (\Throwable $exception) {
Db::rollBack();
return false;
}
}
/**
* 获取角色资源
*
* @param int $roleId 角色ID
* @return array 资源ID列表
*/
public function getRoleResources(int $roleId): array
{
if (empty($roleId)) {
return [];
}
return Db::table('role_resources')
->where('role_id', $roleId)
->pluck('resource_id')
->toArray();
}
/**
* 获取用户资源
*
* @param int $userId 用户ID
* @return array 资源ID列表
*/
public function getUserResources(int $userId): array
{
if (empty($userId)) {
return [];
}
return Db::table('user_resources')
->where('user_id', $userId)
->pluck('resource_id')
->toArray();
}
}
+8
View File
@@ -3,10 +3,18 @@
namespace app\common\logic;
use app\common\model\User;
use app\common\traits\crud\CrudLogicTrait;
use support\exception\BusinessException;
class UserLogic
{
use CrudLogicTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = User::class;
/**
* 用户信息
*/
+22 -18
View File
@@ -4,24 +4,6 @@ namespace app\common\model;
use support\Model;
/**
* corp_feishu_app 企业-飞书应用
* @property integer $id ID(主键)
* @property integer $corp_id 公司ID
* @property string $name 名称
* @property string $tenant_key 企业标识
* @property string $app_id 应用ID
* @property string $app_secret 应用秘钥
* @property string $base_uri 基础域
* @property string $r_url 授权地址
* @property string $scopes 授权范围
* @property string $state 携带参数
* @property string $system_code 绑定系统码
* @property integer $sort 排序
* @property integer $status 状态
* @property integer $created_at 创建时间
* @property integer $updated_at 更新时间
*/
class CorpFeishuApp extends Model
{
/**
@@ -53,4 +35,26 @@ class CorpFeishuApp extends Model
public $timestamps = true;
protected $dateFormat = 'U';
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'corp_id', 'system_id', 'name', 'tenant_key', 'app_id', 'app_secret', 'base_uri', 'r_url', 'scopes', 'state', 'sort', 'status', 'created_at', 'updated_at'];
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
public function corpInfo()
{
return $this->hasOne(Corp::class, 'id', 'corp_id');
}
public function systemInfo()
{
return $this->hasOne(Systems::class, 'id', 'system_id');
}
}
+27
View File
@@ -46,4 +46,31 @@ class CrmCustomer extends Model
* 不允许自动填充模型字段
*/
protected $guarded = [];
protected static function booted()
{
static::saved(function ($customer) {
if (empty($customer->code)) {
$customer->code = self::generateCustomerCode($customer);
// 使用saveQuietly避免递归调用
$customer->saveQuietly();
}
});
}
public static function generateCustomerCode($customer)
{
// 生成唯一16位code,CM+14位
$unique = strtoupper(substr(md5($customer->id . microtime(true) . mt_rand(1000, 9999)), 8, 6));
$code = 'CM' . date('Ymd') .$unique;
return $code;
}
// 静默保存方法,不触发事件
public function saveQuietly(array $options = [])
{
return static::withoutEvents(function () use ($options) {
return $this->save($options);
});
}
}
+1 -1
View File
@@ -62,7 +62,7 @@ class InquiryLog extends Model
public function recipientInfo()
{
return $this->hasOne(User::class, 'id', 'recipient');
return $this->hasOne(User::class, 'id', 'recipient')->where('status', 1);
}
public function channelInfo()
+9 -40
View File
@@ -4,20 +4,6 @@ namespace app\common\model;
use support\Model;
/**
* resources 资源表:目录/菜单/权限
* @property integer $id 资源ID(主键)
* @property string $name 资源名称
* @property string $type 资源类型
* @property string $code 资源编码(权限标识)
* @property integer $parent_id 父资源ID
* @property string $url 路由 path 或 外链 URL
* @property string $icon 图标,仅目录/菜单有效
* @property string $open_type 菜单打开方式
* @property integer $sort 排序
* @property string $created_at
* @property string $updated_at
*/
class Resource extends Model
{
/**
@@ -49,32 +35,15 @@ class Resource extends Model
public $timestamps = true;
// 所属系统
public function system()
{
return $this->belongsTo(Systems::class, 'system_id', 'id');
}
protected $dateFormat = 'U';
// 拥有该资源的角色
public function roles()
{
return $this->belongsToMany(Role::class, 'role_resources', 'resource_id', 'role_id');
}
/**
* 允许自动填充模型字段
*/
protected $fillable = ['id', 'system_id', 'name', 'desc', 'type', 'code', 'parent_id', 'url', 'icon', 'open_type', 'is_hide', 'is_hide_tab', 'no_auth', 'is_home', 'instance', 'sort', 'status', 'created_at', 'updated_at'];
// 拥有该资源的用户(特殊补权)
public function users()
{
return $this->belongsToMany(User::class, 'user_resources', 'resource_id', 'user_id');
}
// 递归
public function parent()
{
return $this->belongsTo(self::class, 'parent_id');
}
public function children()
{
return $this->hasMany(self::class, 'parent_id');
}
/**
* 不允许自动填充模型字段
*/
protected $guarded = [];
}
+2
View File
@@ -15,6 +15,7 @@ use support\Model;
* @property string $email 邮箱
* @property integer $status 状态
* @property string $system_code 系统码
* @property string $report_to 上级用户ID
* @property string $created_at
* @property string $updated_at
*/
@@ -69,6 +70,7 @@ class User extends Model
'position',
'socialize',
'socialize_pic',
'report_to',
];
// 用户所有角色
+109 -63
View File
@@ -2,82 +2,128 @@
namespace app\common\service;
use app\model\User;
use app\model\Role;
use app\model\Resource;
use app\common\logic\ResourceLogic;
use app\common\validate\ResourceValidate;
use app\common\traits\crud\CrudServiceTrait;
use app\common\model\Resource;
use app\common\utils\Tree;
use support\Request;
class ResourceService
{
/**
* 获取用户可见菜单树
*/
public static function getMenuTreeForUser($userId)
{
$menuList = self::getUserMenus($userId);
// 转树
return self::buildTree($menuList);
}
use CrudServiceTrait;
/**
* 获取用户所有有权限的菜单资源(type=menu)
* 指定当前要操作的模型
*/
public static function getUserMenus($userId)
{
$user = User::with('roles.resources')->find($userId);
if (!$user) return [];
// 合并所有角色下的菜单资源
$allMenus = [];
foreach ($user->roles as $role) {
foreach ($role->resources as $res) {
if ($res->type === 'menu') {
$allMenus[$res->id] = $res;
}
}
}
return array_values($allMenus); // 去重后返回
}
protected string $modelClass = Resource::class;
/**
* 获取用户所有有权限的按钮/功能权限 code
* 验证器
*/
public static function getButtonPermissionCodesForUser($userId)
{
$user = User::with('roles.resources')->find($userId);
if (!$user) return [];
$codes = [];
foreach ($user->roles as $role) {
foreach ($role->resources as $res) {
if ($res->type === 'button' && $res->code) {
$codes[$res->code] = true;
}
}
}
return array_keys($codes); // 去重后返回权限code数组
}
protected $validator = ResourceValidate::class;
/**
* 递归构建菜单树
* @param Resource[] $list
* @param int $parentId
* @return array
* 获取验证器
*/
protected static function buildTree($list, $parentId = 0)
public function getValidator()
{
$tree = [];
foreach ($list as $item) {
if ($item->parent_id == $parentId) {
$children = self::buildTree($list, $item->id);
$arr = [
'id' => $item->id,
'name' => $item->name,
'path' => $item->url,
'icon' => $item->icon ?? null,
'code' => $item->code,
'children' => $children,
];
$tree[] = $arr;
}
return new $this->validator;
}
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'default':
$config['where']['system_id'] = ['in', [0, $p['system_id']]];
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
break;
case 'menuList':
$config['where']['status'] = 1;
$config['where']['type'] = ['!=', 'permission'];
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
break;
case 'menuTreeList':
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
break;
case 'index':
$config['where']['status'] = 1;
$config['order']['sort'] = 'asc';
$config['order']['id'] = 'asc';
break;
case 'show':
$config['where']['id'] = $p['id'];
$config['where']['status'] = 1;
break;
case 'store':
break;
case 'update':
$config['where']['id'] = $p['id'];
break;
case 'destroy':
$config['where']['id'] = $p['id'];
break;
}
return $tree;
return $config;
}
public function serviceMenuList(Request $request)
{
$config = $this->buildQueryConfig($request, 'menuList');
$result = $this->logicListAll($config);
return $this->postProcess('MenuList', $result);
}
public function serviceMenuTreeList(Request $request)
{
$config = $this->buildQueryConfig($request, 'menuTreeList');
$result = (new ResourceLogic())->logicListAllResources($request, $config);
return $this->postProcess('MenuTreeList', $result);
}
protected function afterServiceListAll($data)
{
$data = Tree::buildMenuTreeWithReference($data->toArray());
return $data;
}
protected function afterServiceMenuList($data)
{
$data = Tree::buildMenuTree($data->toArray());
return $data;
}
protected function afterServiceMenuTreeList($data)
{
$data = Tree::buildMenuTree($data->toArray());
return $data;
}
public function beforeServiceStore($data, Request $request)
{
$data['parent_id'] = $data['parent_id'] != '' ? $data['parent_id'] : 0;
$data['system_id'] = $data['system_id'] != '' ? $data['system_id'] : 0;
return $data;
}
public function beforeServiceUpdate($data, Request $request)
{
$data['parent_id'] = $data['parent_id'] != '' ? $data['parent_id'] : 0;
$data['system_id'] = $data['system_id'] != '' ? $data['system_id'] : 0;
return $data;
}
}
+93 -6
View File
@@ -5,12 +5,22 @@ namespace app\common\service;
use app\common\logic\UserLogic;
use app\common\model\Corp;
use app\common\model\User;
use app\common\traits\crud\CrudLogicTrait;
use app\common\traits\crud\CrudServiceTrait;
use app\common\utils\CrudUtil;
use app\crm\model\Country;
use app\crm\model\Customer;
use support\exception\BusinessException;
use support\Request;
class UserService
{
use CrudServiceTrait;
/**
* 指定当前要操作的模型
*/
protected string $modelClass = User::class;
protected $logic;
@@ -25,9 +35,10 @@ 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);
$corp_name = Corp::where('abbr', $user->corp_id)->value('name');
return [
'user' => [
'id' => $user->id,
@@ -36,10 +47,14 @@ class UserService
'phone' => $user->phone,
'email' => $user->email,
'status' => $user->status,
'roles' => $user->roles->pluck('name')
'roles' => $user->roles->pluck('name'),
'sex' => $user->sex,
'sn' => $user->sn,
'corp_id' => $user->corp_id,
'corp_name' => $corp_name,
],
'menu' => $menuTree,
'permissions' => $permissionCodes
// 'menu' => $menuTree,
// 'permissions' => $permissionCodes
];
}
@@ -110,4 +125,76 @@ class UserService
{
return $this->logic->getList();
}
/** 分页列表 */
public function serviceTeam(Request $request)
{
if (method_exists($this, 'complexQuery')) {
$result = $this->complexQuery($request->get());
} else {
$config = $this->buildQueryConfig($request, 'team');
$limit = (int)$request->get('size', 15);
$page = (int)$request->get('current', 1);
$result = $this->logicList($config, $limit, $page);
}
foreach ($result as &$data){
$data['customer_count'] = Customer::where('user_id', $data['id'])->count();
unset($data['password']);
unset($data['open_id']);
unset($data['tenant_key']);
unset($data['union_id']);
}
return $this->postProcess('List', $result);
}
/** 分页列表 */
public function resignMember(Request $request)
{
$uid = $request->user->uid;
$userId = $request->get('user_id');
$user = User::find($userId);
if ($user->report_to != $uid) {
throw new BusinessException('禁止操作非组员');
}
$data = [
'status' => 2,
];
$user = $this->logic->edit($userId,$data);
$user = $user->toArray();
unset($user['password']);
unset($user['open_id']);
unset($user['tenant_key']);
unset($user['union_id']);
return $user;
}
/**
* 构造 CrudUtil 查询配置
*/
protected function buildQueryConfig(Request $request, string $scene = 'default'): array
{
$p = $request->all();
$config = [];
$config['model'] = $this->modelClass;
switch ($scene) {
case 'team':
// $config['where']['status'] = 1; // 关键字
$config['order']['id'] = 'desc';
if (isset($p['status']) && $p['status']) {
$config['where']['status'] = $p['status'];
}
$config['dataLimit']['type'] = 'relation';
$config['dataLimit']['userId'] = $request->user->uid;
$config['dataLimit']['userField'] = 'report_to';
break;
}
return $config;
}
}
+3 -2
View File
@@ -1,6 +1,7 @@
<?php
namespace app\common\traits\crud;
use support\exception\BusinessException;
use support\Request;
/**
@@ -48,7 +49,7 @@ trait CrudServiceTrait
$config = $this->buildQueryConfig($request, 'default');
$result = $this->logicListAll($config);
}
return $this->postProcess('List', $result);
return $this->postProcess('ListAll', $result);
}
/** 查看单条 */
@@ -188,6 +189,6 @@ trait CrudServiceTrait
*/
// protected function complexQuery(array $params): array
// {
// throw new \LogicException('请在子类实现 complexQuery');
// throw new BusinessException('请在子类实现 complexQuery');
// }
}
+505
View File
@@ -0,0 +1,505 @@
<?php
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 int $parentId 父级ID
* @return array
*/
public static function buildMenuTree(array $data, int $parentId = 0): array
{
$tree = [];
foreach ($data as $item) {
// 只处理非权限类型的节点
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']),
]
];
// 收集当前节点下的所有权限
$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;
}
// 将服务端额外字段放入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;
}
}
return $tree;
}
/**
* 使用引用方式构建前端需要的菜单树结构(性能更好)
*
* @param array $data 服务端菜单数据
* @return array
*/
public static function buildMenuTreeWithReference(array $data): array
{
$items = [];
$tree = [];
// 过滤出菜单类型的节点并转换数据结构
$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 ($menuData as $item) {
if ($item['parent_id'] == 0) {
$tree[] = &$items[$item['id']];
} else {
if (isset($items[$item['parent_id']])) {
if (!isset($items[$item['parent_id']]['children'])) {
$items[$item['parent_id']]['children'] = [];
}
$items[$item['parent_id']]['children'][] = &$items[$item['id']];
}
}
}
// 为每个节点添加权限列表
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'] ?? ''
];
}
$item['meta']['authList'] = $authList;
}
}
return $tree;
}
/**
* 收集指定节点下的直接权限项(只收集直接子节点中的权限)
*
* @param array $data 数据列表
* @param int $parentId 父级ID
* @return array
*/
public static function collectDirectPermissions(array $data, int $parentId): array
{
$permissions = [];
foreach ($data as $item) {
// 收集直接子节点中的权限类型
if ($item['parent_id'] == $parentId && ($item['type'] ?? '') == 'permission') {
$permissions[] = $item;
}
}
return $permissions;
}
/**
* 获取需要放入meta的额外字段
*
* @param array $item 菜单项
* @return array
*/
public static function getExtraFields(array $item): array
{
$extraFields = [];
$standardFields = [
'id', 'name', 'url', 'icon', 'is_hide', 'is_hide_tab', 'keep_alive', 'level'
];
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;
}
}
return $extraFields;
}
}
+5 -2
View File
@@ -156,8 +156,11 @@ class Generate
protected function deleteFileIfExists($path)
{
if (file_exists($path)) {
unlink($path);
echo "删除已存在文件: <comment>{$path}</comment> \n";
"文件: <comment>{$path}</comment> 已存在,核对后手动删除。\n";
return;
// unlink($path);
// echo "删除已存在文件: <comment>{$path}</comment> \n";
}
}
@@ -0,0 +1,102 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>邮箱提醒</title>
<style type="text/css">
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333333;
margin: 0;
padding: 0;
background-color: #f5f5f5;
}
.email-container {
max-width: 600px;
margin: 0 auto;
background-color: #ffffff;
}
.header {
padding: 20px;
background-color: #0056b3;
color: white;
text-align: center;
}
.content {
padding: 20px;
}
.button {
display: inline-block;
padding: 10px 20px;
background-color: #0056b3;
color: white !important;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
margin: 10px 0;
}
.footer {
padding: 15px;
text-align: center;
font-size: 12px;
color: #777777;
background-color: #f0f0f0;
}
@media only screen and (max-width: 480px) {
.email-container {
width: 100%;
}
.button {
display: block;
text-align: center;
}
}
</style>
</head>
<body>
<!--[if mso]>
<style type="text/css">
.button {
background-color: #0056b3 !important;
color: white !important;
}
</style>
<![endif]-->
<table class="email-container" width="100%" cellspacing="0" cellpadding="0" border="0" align="center">
<tr>
<td class="header">
<h1>工单提醒</h1>
</td>
</tr>
<tr>
<td class="content">
<p>你好,</p>
<p>你有新的工单待处理。请及时查看:</p>
<ul>
<li>工单编号:{{code}}</li>
<li>工单分类:{{category}}</li>
<li>问题描述:{{content}}</li>
</ul>
<p>请点击下方按钮立即查看详情:</p>
<div style="text-align: center;">
<a href="https://x.hnxinyouhe.com" class="button" style="color: white; text-decoration: none;">立即查看</a>
</div>
<!--<p>如果您未请求此操作,请忽略此邮件或联系我们的客服团队。</p>-->
</td>
</tr>
<tr>
<td class="footer">
<p>© 2025 河南新友合科技有限公司. 保留所有权利。</p>
<p>这是系统自动发送的邮件,请勿直接回复。</p>
</td>
</tr>
</table>
</body>
</html>
@@ -0,0 +1,29 @@
<?php
namespace app\common\validate;
use think\Validate;
class CorpFeishuAppValidate extends Validate
{
// 规则
protected $rule = [
];
// 提示
protected $message = [
];
// 场景
protected $scene = [
'store' => [
],
'update' => [
]
];
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace app\common\validate;
use think\Validate;
class CountryValidate extends Validate
{
// 规则
protected $rule = [
];
// 提示
protected $message = [
];
// 场景
protected $scene = [
'store' => [
],
'update' => [
]
];
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace app\common\validate;
use think\Validate;
class ResourceValidate extends Validate
{
// 规则
protected $rule = [
];
// 提示
protected $message = [
];
// 场景
protected $scene = [
'store' => [
],
'update' => [
]
];
}
+2 -2
View File
@@ -26,7 +26,7 @@ class RoleValidate extends Validate
];
protected $scene = [
'store' => ['system_id', 'name', 'desc', 'sort'],
'update' => ['id', 'system_id', 'name', 'desc', 'sort', 'status'],
'store' => ['name', 'desc', 'sort'],
'update' => ['id', 'name', 'desc', 'sort', 'status'],
];
}
+1 -1
View File
@@ -55,7 +55,7 @@ class UserValidate extends Validate
'feishu_register' => ['feishu_code'],
'upwd_login' => ['account' => 'require|checkAccount', 'password'],
'feishu_auth' => ['corp_abbr', 'system_code', 'login_type'],
'feishu_login' => ['feishu_code']
'feishu_login' => ['feishu_code'],
];
/**
+31 -23
View File
@@ -75,27 +75,27 @@ class IndexController
public function createResource()
{
$options = [
// 'controller' => [
// 'module' => 'inquiry',
// 'name' => 'country'
// ],
'controller' => [
'module' => 'common',
'name' => 'resources'
],
'model' => [
'module' => 'common',
'name' => 'email_parsed_result'
'name' => 'resources'
],
// 'service' => [
// 'module' => 'inquiry',
// 'name' => 'country'
// ],
// 'logic' => [
// 'module' => 'inquiry',
// 'name' => 'country'
// ],
// 'validate' => [
// 'module' => 'inquiry',
// 'name' => 'country'
// ],
'table' => 'email_parsed_result'
'service' => [
'module' => 'common',
'name' => 'resources'
],
'logic' => [
'module' => 'common',
'name' => 'resources'
],
'validate' => [
'module' => 'common',
'name' => 'resources'
],
'table' => 'resources'
];
$result = (new Generate($options))->doGenerate();
@@ -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);
$res = Cache::delete("user_resources_74_fdesk");
return $assignedUid;
return 22;
}
}
+37 -12
View File
@@ -325,6 +325,7 @@ class CustomerController extends BaseController
'lng' => Util::getParam($request, 'lng', 0),
'address' => Util::getParam($request, 'address', ''),
'sex' => Util::getParam($request, 'sex', 0),
'obtain' => time(),
];
if (Util::isEmpty($id)) {
@@ -393,13 +394,31 @@ class CustomerController extends BaseController
if(empty($customer)){
return $this->error( trans('fail'));
}
if($force == 'force'){
//强制删除
$res = $customer->forceDelete();
}else{
//软删除
$res = $customer->delete();
try {
Db::beginTransaction();
if ($force == 'force') {
//强制删除
$res = $customer->forceDelete();
} else {
$customer->user_id = 0;
$customer->save();
//记录客户关系变化
$his = CustomerHis::create([
'customer_id' => $id,
'user_his' => $userId,
'user_id' => 0,
]);
//软删除
$res = $customer->delete();
}
Db::commit();
} catch (\Throwable $e) {
Db::rollback();
return $this->error($e->getMessage());
}
if ($res) {
return $this->success($res, trans('success'));
} else {
@@ -485,10 +504,10 @@ class CustomerController extends BaseController
return $this->error(trans('customer_id').trans('not_null'));
}
$data = [
'user_id' => $request->user->uid
'user_id' => $request->user->uid,
'obtain' => time(),
];
try {
Db::beginTransaction();
$customer= Customer::withTrashed()->find($customerId);
@@ -526,8 +545,6 @@ class CustomerController extends BaseController
}
}
/**
* 批量转让
* @param Request $request
@@ -543,10 +560,11 @@ class CustomerController extends BaseController
return $this->error( trans('user_none'));
}
$query= Customer::whereIn('id',explode(',',$customerId));
$query= Customer::withTrashed()->whereIn('id',explode(',',$customerId));
try {
$customer = $query->get();
Db::beginTransaction();
foreach ($customer as $item) {
//记录客户关系变化
$his = CustomerHis::create([
@@ -561,8 +579,15 @@ class CustomerController extends BaseController
$inquiryLog->save();
}
}
$item->restore();
}
$res = $query->update(['user_id' => $user->id]);
$data = [
'user_id' => $user->id,
'obtain' => time(),
];
$res = $query->update($data);
Db::commit();
} catch (\Throwable $e) {
+27
View File
@@ -79,4 +79,31 @@ class Customer extends Model
{
return $this->belongsTo(User::class, 'user_id', 'id');
}
protected static function booted()
{
static::saved(function ($customer) {
if (empty($customer->code)) {
$customer->code = self::generateCustomerCode($customer);
// 使用saveQuietly避免递归调用
$customer->saveQuietly();
}
});
}
public static function generateCustomerCode($customer)
{
// 生成唯一16位code,CM+14位
$unique = strtoupper(substr(md5($customer->id . microtime(true) . mt_rand(1000, 9999)), 8, 6));
$code = 'CM' . date('Ymd') .$unique;
return $code;
}
// 静默保存方法,不触发事件
public function saveQuietly(array $options = [])
{
return static::withoutEvents(function () use ($options) {
return $this->save($options);
});
}
}
+21 -2
View File
@@ -36,6 +36,7 @@ class CustomerService
$follow = $params['follow'] ?? '';
$follow_last = $params['follow_last'] ?? '';
$tagId = $params['tag_id'] ?? [];
$countryId = $params['country_id'] ?? '';
$where = [];
$user_id =$params['user_id'];
@@ -49,6 +50,9 @@ class CustomerService
}
$where[] = ['status', 1];
if (!empty($countryId)){
$where[] = ['country_id', $countryId];
}
$grade = [];
if (!Util::isEmpty($follow) && $follow == 0) {
@@ -84,7 +88,10 @@ class CustomerService
if (!Util::isEmpty($keyword)) {
$query->where(function ($query) use ($keyword) {
$query->where('contacts', 'like', "%{$keyword}%")
->orWhere('phone', 'like', "%{$keyword}%");
->orWhere('phone', 'like', "%{$keyword}%")
->orWhere('email', 'like', "%{$keyword}%")
->orWhere('WhatsApp', 'like', "%{$keyword}%")
->orWhere('code', 'like', "%{$keyword}%");
});
}
if (!Util::isEmpty($grade)) {
@@ -148,9 +155,16 @@ class CustomerService
switch ($scene) {
case 'index':
$config['where']['status'] = 1; // 关键字
if (isset($p['user_id']) && $p['user_id']) {
$config['where']['user_id'] = $p['user_id'];
} // 关键字
if (isset($p['keyword']) && $p['keyword']) {
$k = '%' . $p['keyword'] . '%';
$config['where']['contacts'] = ['like', $k];
$config['orWhere'][] = ['contacts','like', $k];
$config['orWhere'][] = ['phone','like', $k];
$config['orWhere'][] = ['email','like', $k];
$config['orWhere'][] = ['WhatsApp','like', $k];
$config['orWhere'][] = ['code','like', $k];
}
$config['order']['id'] = 'desc';
$config['dataLimit']['type'] = 'relation';
@@ -162,6 +176,11 @@ class CustomerService
return $config;
}
/**
* 无用线索举报
* @param $params
* @return false
*/
public function report($params)
{
$id = $params['id'];
@@ -3,6 +3,7 @@
namespace app\crmAdmin\controller;
use app\common\model\Corp;
use app\crm\model\Customer;
use app\crmAdmin\service\CustomerService;
use support\Request;
@@ -133,6 +134,37 @@ class CustomerController extends BaseController
}
}
/**
* 移除
* @param Request $request
* @return \support\Response
*/
public function delete(Request $request)
{
try {
$param = $request->all();
if (empty($param['customer_id'])) {
return $this->error('customer_id'.trans('not_null'));
}
$where = [
'id' => $param['customer_id'],
];
$customer = Customer::where($where)->withTrashed()->first();
if(empty($customer)){
return $this->error( trans('fail'));
}
//软删除
$customer->status = 3;
$res = $customer->save();
$customer->delete();
return $this->success($res);
} catch (\Throwable $th) {
return $this->error($th->getMessage());
}
}
/**
* 客户详情
* @param Request $request
@@ -154,4 +186,25 @@ class CustomerController extends BaseController
}
}
/**
* 客户详情
* @param Request $request
* @return \support\Response
*/
public function follow(Request $request)
{
try {
$param = $request->all();
if (empty($param['id'])) {
return $this->error('id'.trans('not_null'));
}
$result = (new CustomerService())->follow($param);
return $this->success($result);
} catch (\Throwable $th) {
return $this->error($th->getMessage());
}
}
}
+7 -1
View File
@@ -13,6 +13,10 @@ class CustomerLogic
$customer->status = $status;
$res = $customer->save();
if($status == 1){
$customer->restore();
}
return $res;
}
@@ -20,7 +24,9 @@ class CustomerLogic
{
$customer = Customer::withTrashed()->find($id);
//强制删除
$res = $customer->forceDelete();
// $res = $customer->forceDelete();
$customer->status = 3;
$res = $customer->save();
return $res;
}
}
+62 -4
View File
@@ -4,6 +4,7 @@ namespace app\crmAdmin\service;
use app\common\model\InquiryLog;
use app\common\model\User;
use app\common\service\OssService;
use app\crm\model\CustomerHis;
use app\crmAdmin\logic\CustomerLogic;
use app\crm\model\CarType;
@@ -104,16 +105,22 @@ class CustomerService
if (!Util::isEmpty($keyword)) {
$query->where(function ($query) use ($keyword) {
$query->where('contacts', 'like', "%{$keyword}%")
->orWhere('phone', 'like', "%{$keyword}%");
->orWhere('phone', 'like', "%{$keyword}%")
->orWhere('email', 'like', "%{$keyword}%")
->orWhere('WhatsApp', 'like', "%{$keyword}%")
->orWhere('code', 'like', "%{$keyword}%");
});
}
if (!Util::isEmpty($grade)) {
$query->whereIn('grade', $grade);
}
if ($recycle == 'recycle' || $scene == 'recycle' || $seas == 'seas' || $scene == 'seas' || $scene == 'audit') {
if ($recycle == 'recycle' || $scene == 'recycle' || $scene == 'audit') {
$query->onlyTrashed();
}
if ($seas == 'seas' || $scene == 'seas') {
$query->withTrashed();
}
if (!$export) {
$customer = $query->orderBy('id', 'desc')->paginate($perPage, ['*'], 'page', $page);
@@ -124,9 +131,10 @@ class CustomerService
if (!$export) {
foreach ($customer as &$item) {
$item = $this->getCustomer($item);
$item['desc_collect'] = $item['desc_collect'] ?? '';
$item['follow_next'] = empty($item['follow_next'])?'':date('Y-m-d H:i:s', $item['follow_next']);
if ($seas == 'seas' || $scene == 'seas') {
$item['seas_time'] = time() - strtotime($item['deleted_at']);
$item['seas_time'] = time() - (empty($item['deleted_at'])?strtotime($item['updated_at']):strtotime($item['deleted_at']));
}
if ($scene == 'audit') { //审核时查询举报人姓名
$customerHis = CustomerHis::where('customer_id', $item->id)->orderBy('id', 'desc')->first(['user_his']);
@@ -315,7 +323,42 @@ class CustomerService
}
}
}
$res = $query->update(['user_id' => $userId]);
$data = [
'user_id' => $userId,
'obtain' => time(),
];
$res = $query->update($data);
Db::commit();
return $res;
} catch (\Throwable $e) {
Db::rollback();
throw $e;
}
}
/**
* @param $params
* @return bool
*/
public function delete($params)
{
try {
Db::beginTransaction();
$customerId = $params['customer_id'];
$query= Customer::whereIn('id',explode(',',$customerId));
$customer = $query->get();
foreach ($customer as $item) {
//记录客户关系变化
$his = CustomerHis::create([
'customer_id' => $item->id,
'user_his' => $item->user_id,
'user_id' => 0,
]);
}
$res = $query->update(['user_id' => 0]);
Db::commit();
return $res;
@@ -394,4 +437,19 @@ class CustomerService
return $customer;
}
public function follow($params)
{
$id = $params['id'];
$follow = Follow::
where('customer_id', $id)
->get();
$ossservice = new OssService();
foreach ($follow as $item) {
$item['picture_full'] = empty($item->picture)?'':$ossservice->generatePrivateUrl($item->picture);
}
return $follow;
}
}
+17 -2
View File
@@ -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];
}
}
/**
* 替换模板
*/
@@ -101,7 +101,7 @@ class StatisticsController extends BaseController
{
$list = InquiryLog::with(["recipientInfo"])
->where("recipient", ">", 0)
->whereNotIn('recipient', [1,5,10,11,13,15,16,17,19,74,75,78,141,147])
->whereNotIn('recipient', [1,5,10,11,13,155,16,17,19,74,75,78,141,147])
->select('recipient', Db::raw('COUNT(*) as totals'))
->groupBy('recipient')
->get();
+1
View File
@@ -60,6 +60,7 @@ class InquiryToCrm implements Consumer
$crmCustomer->contacts = $info->contacts;
$crmCustomer->desc_collect = $info->content;
$crmCustomer->enter_user_id = $info->enter_user_id;
$crmCustomer->obtain = time();
// 修改同步CRM状态
$info->sync_crm = 1;
// 保存
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace app\queue\redis;
use support\Db;
use support\exception\BusinessException;
use Webman\RedisQueue\Consumer;
class PermAgt implements Consumer
{
// 要消费的队列名
public $queue = 'perm-agt';
// 连接名,对应 plugin/webman/redis-queue/redis.php 里的连接`
public $connection = 'default';
// 消费
public function consume($data)
{
try {
$corpId = $data['corp_id'];
$userId = $data['user_id'];
if ($corpId == 2) {
$roles = [
[
'user_id' => $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);
}
}
+51 -9
View File
@@ -52,12 +52,17 @@ Route::group('/common', function () {
Route::group('/user', function () {
Route::any('/info', [app\common\controller\UserController::class, 'info']);
Route::any('/get', [app\common\controller\UserController::class, 'getUser']);
Route::any('/team', [app\common\controller\UserController::class, 'team']);
Route::any('/resign', [app\common\controller\UserController::class, 'resignMember']);
});
Route::group('/preset', 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']);
});
});
@@ -70,13 +75,7 @@ Route::group('/admin', function () {
Route::any('/store', [app\admin\controller\RoleController::class, 'store']);
Route::any('/update', [app\admin\controller\RoleController::class, 'update']);
Route::any('/destroy', [app\admin\controller\RoleController::class, 'destroy']);
});
// 用户
Route::group('/user', function () {
Route::any('/list', [app\admin\controller\UserController::class, 'index']);
Route::any('/show', [app\admin\controller\UserController::class, 'show']);
Route::any('/update', [app\admin\controller\UserController::class, 'update']);
Route::any('/menu/resource', [app\admin\controller\RoleController::class, 'menuResource']);
});
// 公司
@@ -88,6 +87,46 @@ Route::group('/admin', function () {
Route::any('/destroy', [app\admin\controller\CorpController::class, 'destroy']);
});
// 系统
Route::group('/systems', function () {
Route::any('/list', [app\admin\controller\SystemsController::class, 'index']);
Route::any('/show', [app\admin\controller\SystemsController::class, 'show']);
Route::any('/store', [app\admin\controller\SystemsController::class, 'store']);
Route::any('/update', [app\admin\controller\SystemsController::class, 'update']);
Route::any('/destroy', [app\admin\controller\SystemsController::class, 'destroy']);
});
// 飞书应用
Route::group('/corp/feishu', function () {
Route::any('/list', [app\admin\controller\CorpFeishuAppController::class, 'index']);
Route::any('/show', [app\admin\controller\CorpFeishuAppController::class, 'show']);
Route::any('/store', [app\admin\controller\CorpFeishuAppController::class, 'store']);
Route::any('/update', [app\admin\controller\CorpFeishuAppController::class, 'update']);
Route::any('/destroy', [app\admin\controller\CorpFeishuAppController::class, 'destroy']);
});
// 资源
Route::group('/resources', function () {
Route::any('/list/tree', [app\common\controller\ResourceController::class, 'listTree']);
Route::any('/show', [app\common\controller\ResourceController::class, 'show']);
Route::any('/store', [app\common\controller\ResourceController::class, 'store']);
Route::any('/update', [app\common\controller\ResourceController::class, 'update']);
Route::any('/destroy', [app\common\controller\ResourceController::class, 'destroy']);
Route::any('/list/menu', [app\common\controller\ResourceController::class, 'menuList']);
Route::any('/list/menu/tree', [app\common\controller\ResourceController::class, 'menuTreeList']);
});
// 用户
Route::group('/user', function () {
Route::any('/list', [app\admin\controller\UserController::class, 'index']);
Route::any('/show', [app\admin\controller\UserController::class, 'show']);
Route::any('/update', [app\admin\controller\UserController::class, 'update']);
Route::any('/edit', [app\admin\controller\UserController::class, 'edit']);
Route::any('/authorize', [app\admin\controller\UserController::class, 'authorize']);
Route::any('/set/resources', [app\admin\controller\UserController::class, 'updateResources']);
Route::any('/update/password', [app\admin\controller\UserController::class, 'savePassword']);
});
// 询单
Route::group('/inquiry', function () {
Route::any('/list', [app\inquiry\controller\InquiryLogController::class, 'index']);
@@ -117,6 +156,9 @@ Route::group('/admin', function () {
Route::any('/audit', [app\crmAdmin\controller\CustomerController::class, 'audit']);
Route::any('/transfer', [app\crmAdmin\controller\CustomerController::class, 'transfer']);
Route::any('/detail', [app\crmAdmin\controller\CustomerController::class, 'detail']);
Route::any('/delete', [app\crmAdmin\controller\CustomerController::class, 'delete']);
Route::any('/cede', [app\crm\controller\CustomerController::class, 'cede']);
Route::any('/follow', [app\crmAdmin\controller\CustomerController::class, 'follow']);
});
});
});
@@ -177,7 +219,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']);
});