Files
zyt/server/app/adminapi/logic/auth/AdminLogic.php
T
2026-08-17 09:06:49 +08:00

489 lines
17 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
// +----------------------------------------------------------------------
// | likeadmin快速开发前后端分离管理后台(PHP版)
// +----------------------------------------------------------------------
// | 欢迎阅读学习系统程序代码,建议反馈是我们前进的动力
// | 开源版本可自由商用,可去除界面版权logo
// | gitee下载:https://gitee.com/likeshop_gitee/likeadmin
// | github下载:https://github.com/likeshop-github/likeadmin
// | 访问官网:https://www.likeadmin.cn
// | likeadmin团队 版权所有 拥有最终解释权
// +----------------------------------------------------------------------
// | author: likeadminTeam
// +----------------------------------------------------------------------
namespace app\adminapi\logic\auth;
use app\adminapi\logic\LoginLogic;
use app\common\cache\AdminAuthCache;
use app\common\enum\YesNoEnum;
use app\common\logic\BaseLogic;
use app\common\model\auth\Admin;
use app\common\model\auth\AdminDept;
use app\common\model\auth\AdminJobs;
use app\common\model\auth\AdminRole;
use app\common\model\auth\AdminSession;
use app\common\cache\AdminTokenCache;
use app\common\service\FileService;
use app\common\service\TencentImService;
use think\facade\Config;
use think\facade\Db;
use think\facade\Log;
/**
* 管理员逻辑
* Class AdminLogic
* @package app\adminapi\logic\auth
*/
class AdminLogic extends BaseLogic
{
/**
* @notes 添加管理员
* @param array $params
* @author 段誉
* @date 2021/12/29 10:23
*/
public static function add(array $params)
{
Db::startTrans();
try {
$passwordSalt = Config::get('project.unique_identification');
$password = create_password($params['password'], $passwordSalt);
$defaultAvatar = config('project.default_image.admin_avatar');
$avatar = !empty($params['avatar']) ? FileService::setFileUrl($params['avatar']) : $defaultAvatar;
// 处理资质图片:如果是数组则转为JSON字符串
$qualificationImages = '';
if (isset($params['qualification_images'])) {
if (is_array($params['qualification_images'])) {
$qualificationImages = json_encode($params['qualification_images'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} else {
$qualificationImages = $params['qualification_images'];
}
}
$admin = Admin::create([
'name' => $params['name'],
'account' => $params['account'],
'avatar' => $avatar,
'password' => $password,
'create_time' => time(),
'disable' => $params['disable'],
'multipoint_login' => $params['multipoint_login'],
// 新增字段
'gender' => $params['gender'] ?? 1,
'age' => $params['age'] ?? null,
'phone' => $params['phone'] ?? null,
'title' => $params['title'] ?? null,
'department' => $params['department'] ?? null,
'specialty' => $params['specialty'] ?? null,
'education' => $params['education'] ?? null,
'experience' => $params['experience'] ?? null,
'honors' => $params['honors'] ?? null,
'license_no' => $params['license_no'] ?? '',
'qualification_images' => $qualificationImages,
'enable_image_consult' => $params['enable_image_consult'] ?? 1,
'enable_video_consult' => $params['enable_video_consult'] ?? 1,
'enable_charge' => $params['enable_charge'] ?? 0,
]);
// 角色
self::insertRole($admin['id'], $params['role_id'] ?? []);
// 部门
self::insertDept($admin['id'], $params['dept_id'] ?? []);
// 岗位
self::insertJobs($admin['id'], $params['jobs_id'] ?? []);
// 导入医生账号到腾讯云IM
self::importDoctorAccountToIm($admin['id'], $params['name']);
Db::commit();
return true;
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 编辑管理员
* @param array $params
* @return bool
* @author 段誉
* @date 2021/12/29 10:43
*/
public static function edit(array $params): bool
{
Db::startTrans();
try {
// 处理资质图片:如果是数组则转为JSON字符串
$qualificationImages = '';
if (isset($params['qualification_images'])) {
if (is_array($params['qualification_images'])) {
$qualificationImages = json_encode($params['qualification_images'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
} else {
$qualificationImages = $params['qualification_images'];
}
}
// 基础信息
$data = [
'id' => $params['id'],
'name' => $params['name'],
'account' => $params['account'],
'disable' => $params['disable'],
'multipoint_login' => $params['multipoint_login'],
// 新增字段
'gender' => $params['gender'] ?? 1,
'age' => $params['age'] ?? null,
'phone' => $params['phone'] ?? null,
'title' => $params['title'] ?? null,
'department' => $params['department'] ?? null,
'specialty' => $params['specialty'] ?? null,
'education' => $params['education'] ?? null,
'experience' => $params['experience'] ?? null,
'honors' => $params['honors'] ?? null,
'license_no' => $params['license_no'] ?? '',
'qualification_images' => $qualificationImages,
'enable_image_consult' => $params['enable_image_consult'] ?? 1,
'enable_video_consult' => $params['enable_video_consult'] ?? 1,
'enable_charge' => $params['enable_charge'] ?? 0,
];
// 头像
$data['avatar'] = !empty($params['avatar']) ? FileService::setFileUrl($params['avatar']) : '';
// 密码
if (!empty($params['password'])) {
$passwordSalt = Config::get('project.unique_identification');
$data['password'] = create_password($params['password'], $passwordSalt);
}
// 禁用或更换角色后.设置token过期
$roleId = AdminRole::where('admin_id', $params['id'])->column('role_id');
$submittedRoleIds = self::normalizeRoleIds($params['role_id'] ?? []);
$editRole = self::roleIdsChanged($roleId, $submittedRoleIds);
if ($params['disable'] == 1 || $editRole) {
$tokenArr = AdminSession::where('admin_id', $params['id'])->select()->toArray();
foreach ($tokenArr as $token) {
self::expireToken($token['token']);
}
}
Admin::update($data);
// 删除旧的关联信息
AdminRole::delByUserId($params['id']);
AdminDept::delByUserId($params['id']);
AdminJobs::delByUserId($params['id']);
// 角色
self::insertRole($params['id'], $submittedRoleIds);
// 部门
self::insertDept($params['id'], $params['dept_id'] ?? []);
// 岗位
self::insertJobs($params['id'], $params['jobs_id'] ?? []);
// 导入医生账号到腾讯云IM
self::importDoctorAccountToIm($params['id'], $params['name']);
Db::commit();
// 必须在角色关联提交后删除该账号的 URI 权限缓存,避免并发请求重新写入旧权限。
try {
(new AdminAuthCache($params['id']))->clearAuthCache();
} catch (\Throwable $cacheError) {
// 数据已经提交,缓存清理失败不能把成功的编辑伪装成失败;记录后等待缓存自然过期。
Log::warning('管理员角色权限缓存清理失败:admin_id=' . $params['id'] . 'error=' . $cacheError->getMessage());
}
return true;
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 删除管理员
* @param array $params
* @return bool
* @author 段誉
* @date 2021/12/29 10:45
*/
public static function delete(array $params): bool
{
Db::startTrans();
try {
$admin = Admin::findOrEmpty($params['id']);
if ($admin->root == YesNoEnum::YES) {
throw new \Exception("超级管理员不允许被删除");
}
Admin::destroy($params['id']);
//设置token过期
$tokenArr = AdminSession::where('admin_id', $params['id'])->select()->toArray();
foreach ($tokenArr as $token) {
self::expireToken($token['token']);
}
(new AdminAuthCache($params['id']))->clearAuthCache();
// 删除旧的关联信息
AdminRole::delByUserId($params['id']);
AdminDept::delByUserId($params['id']);
AdminJobs::delByUserId($params['id']);
Db::commit();
return true;
} catch (\Exception $e) {
Db::rollback();
self::setError($e->getMessage());
return false;
}
}
/**
* @notes 过期token
* @param $token
* @return bool
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
* @author 段誉
* @date 2021/12/29 10:46
*/
public static function expireToken($token): bool
{
$adminSession = AdminSession::where('token', '=', $token)
->with('admin')
->find();
if (empty($adminSession)) {
return false;
}
$time = time();
$adminSession->expire_time = $time;
$adminSession->update_time = $time;
$adminSession->save();
return (new AdminTokenCache())->deleteAdminInfo($token);
}
/**
* @notes 查看管理员详情
* @param $params
* @return array
* @author 段誉
* @date 2021/12/29 11:07
*/
public static function detail($params, $action = 'detail'): array
{
$admin = Admin::field([
'id', 'account', 'name', 'disable', 'root',
'multipoint_login', 'avatar', 'is_paw',
'gender', 'age', 'phone', 'title', 'department',
'specialty', 'education', 'experience', 'honors',
'license_no', 'qualification_images', 'enable_image_consult', 'enable_video_consult', 'enable_charge',
'work_wechat_userid'
])->findOrEmpty($params['id'])->toArray();
// 将资质图片JSON字符串转换为数组,供前端组件使用
if (!empty($admin['qualification_images'])) {
try {
$images = json_decode($admin['qualification_images'], true);
if (is_array($images)) {
$admin['qualification_images'] = $images;
}
} catch (\Exception $e) {
// 解析失败时保持原值
Log::error('解析资质图片失败: ' . $e->getMessage());
}
}
if ($action == 'detail') {
$roleIds = AdminRole::where('admin_id', $params['id'])->column('role_id');
if (in_array(2, $roleIds)) {
$admin['diagnosis_count'] = \app\common\model\tcm\Diagnosis::where('assistant_id', $params['id'])
->whereNull('delete_time')
->count();
}
return $admin;
}
$authRoleIds = AdminRole::where('admin_id', $params['id'])->column('role_id');
$admin['role_ids'] = array_values(array_map('intval', $authRoleIds));
$admin['need_bind_work_wechat'] = LoginLogic::adminMustBindWorkWechat([
'root' => (int) ($admin['root'] ?? 0),
'work_wechat_userid' => (string) ($admin['work_wechat_userid'] ?? ''),
]);
$result['user'] = $admin;
// 当前管理员角色拥有的菜单
$result['menu'] = MenuLogic::getMenuByAdminId($params['id']);
// 当前管理员橘色拥有的按钮权限
$result['permissions'] = AuthLogic::getBtnAuthByRoleId($admin);
return $result;
}
/**
* @notes 编辑超级管理员
* @param $params
* @return Admin
* @author 段誉
* @date 2022/4/8 17:54
*/
public static function editSelf($params)
{
$data = [
'id' => $params['admin_id'],
'name' => $params['name'],
'avatar' => FileService::setFileUrl($params['avatar']),
];
if (!empty($params['password'])) {
$passwordSalt = Config::get('project.unique_identification');
$data['password'] = create_password($params['password'], $passwordSalt);
}
return Admin::update($data);
}
/**
* @notes 新增角色
* @param $adminId
* @param $roleIds
* @throws \Exception
* @author 段誉
* @date 2022/11/25 14:23
*/
public static function insertRole($adminId, $roleIds)
{
$roleIds = self::normalizeRoleIds($roleIds);
if ($roleIds !== []) {
// 角色
$roleData = [];
foreach ($roleIds as $roleId) {
$roleData[] = [
'admin_id' => $adminId,
'role_id' => $roleId,
];
}
(new AdminRole())->saveAll($roleData);
}
}
/** @return int[] */
private static function normalizeRoleIds(mixed $roleIds): array
{
if (!is_array($roleIds)) {
return [];
}
$normalized = array_values(array_unique(array_filter(
array_map('intval', $roleIds),
static fn (int $roleId): bool => $roleId > 0
)));
sort($normalized, SORT_NUMERIC);
return $normalized;
}
/**
* 角色是无序集合;新增、移除或替换都必须使现有 Token 失效,仅顺序变化不算修改。
*
* @param array<int|string,mixed> $currentRoleIds
* @param array<int|string,mixed> $submittedRoleIds
*/
private static function roleIdsChanged(array $currentRoleIds, array $submittedRoleIds): bool
{
return self::normalizeRoleIds($currentRoleIds) !== self::normalizeRoleIds($submittedRoleIds);
}
/**
* @notes 新增部门
* @param $adminId
* @param $deptIds
* @throws \Exception
* @author 段誉
* @date 2022/11/25 14:22
*/
public static function insertDept($adminId, $deptIds)
{
// 部门
if (!empty($deptIds)) {
$deptData = [];
foreach ($deptIds as $deptId) {
$deptData[] = [
'admin_id' => $adminId,
'dept_id' => $deptId
];
}
(new AdminDept())->saveAll($deptData);
}
}
/**
* @notes 新增岗位
* @param $adminId
* @param $jobsIds
* @throws \Exception
* @author 段誉
* @date 2022/11/25 14:22
*/
public static function insertJobs($adminId, $jobsIds)
{
// 岗位
if (!empty($jobsIds)) {
$jobsData = [];
foreach ($jobsIds as $jobsId) {
$jobsData[] = [
'admin_id' => $adminId,
'jobs_id' => $jobsId
];
}
(new AdminJobs())->saveAll($jobsData);
}
}
/**
* @notes 导入医生账号到腾讯云IM
* @param int $adminId 管理员ID
* @param string $name 管理员名称
* @return void
* @author AI Assistant
* @date 2026/03/02
*/
public static function importDoctorAccountToIm($adminId, $name)
{
try {
$userId = 'doctor_' . $adminId;
Log::info('开始导入医生IM账号 - admin_id: ' . $adminId . ', user_id: ' . $userId . ', name: ' . $name);
$imService = new TencentImService();
$result = $imService->importAccount($userId, $name);
if ($result) {
Log::info('医生IM账号导入成功 - admin_id: ' . $adminId . ', user_id: ' . $userId);
} else {
Log::warning('医生IM账号导入失败 - admin_id: ' . $adminId . ', user_id: ' . $userId);
}
} catch (\Exception $e) {
Log::error('导入医生IM账号异常 - admin_id: ' . $adminId . ', error: ' . $e->getMessage());
}
}
}