Files
dafuweng-saiadmin6.x/server/plugin/saiadmin/app/service/SystemRoleChannelService.php

280 lines
8.9 KiB
PHP
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
declare(strict_types=1);
namespace plugin\saiadmin\app\service;
use plugin\saiadmin\app\model\system\SystemDept;
use plugin\saiadmin\app\model\system\SystemRole;
use plugin\saiadmin\exception\ApiException;
use support\think\Db;
/**
* 渠道角色:从默认模板复制指定角色、同步与删除
*/
class SystemRoleChannelService
{
/** 全局超级管理员角色,不参与渠道复制 */
public const SUPER_ADMIN_ROLE_ID = 1;
/**
* 为渠道从默认模板复制三个代理角色(缺失则补齐,不整包跳过)
*/
public function copyDefaultRolesToDept(int $deptId, bool $pruneExtra = false): array
{
if ($deptId <= 0) {
throw new ApiException('Invalid channel id');
}
if (!$this->tableHasColumn('sa_system_role', 'dept_id')) {
return ['dept_id' => $deptId, 'copied' => 0, 'skipped' => 0, 'pruned' => 0, 'message' => 'dept_id column missing'];
}
$templates = $this->defaultTemplateRoles();
if (empty($templates)) {
return ['dept_id' => $deptId, 'copied' => 0, 'skipped' => 0, 'pruned' => 0, 'message' => 'no template roles'];
}
$copied = 0;
$skipped = 0;
foreach ($templates as $template) {
$template = (array) $template;
$templateId = (int) ($template['id'] ?? 0);
$code = (string) ($template['code'] ?? '');
if ($templateId <= 0 || $code === '') {
continue;
}
if ($this->roleExists($deptId, $code)) {
$skipped++;
continue;
}
$newId = $this->insertRoleFromTemplate($template, $deptId);
if ($newId > 0) {
$this->copyRoleMenus($templateId, $newId);
$copied++;
}
}
$pruned = 0;
if ($pruneExtra) {
$pruned = $this->pruneExtraChannelRoles($deptId);
}
return ['dept_id' => $deptId, 'copied' => $copied, 'skipped' => $skipped, 'pruned' => $pruned];
}
/**
* 为所有已启用渠道补齐三个默认角色,并移除多余历史角色
*/
public function syncAllChannelsFromDefault(): array
{
$deptIds = SystemDept::where('status', 1)->where('id', '>', 0)->column('id');
$result = [];
foreach ($deptIds as $deptId) {
$result[(int) $deptId] = $this->copyDefaultRolesToDept((int) $deptId, true);
}
return $result;
}
/**
* 删除渠道下全部角色及菜单关联
*/
public function deleteRolesByDept(int $deptId): int
{
if ($deptId <= 0) {
return 0;
}
$roleIds = SystemRole::where('dept_id', $deptId)->column('id');
if (empty($roleIds)) {
return 0;
}
Db::name('sa_system_user_role')->whereIn('role_id', $roleIds)->delete();
Db::name('sa_system_role_menu')->whereIn('role_id', $roleIds)->delete();
Db::name('sa_system_role_dept')->whereIn('role_id', $roleIds)->delete();
return SystemRole::destroy($roleIds);
}
/**
* 将用户已绑定的模板角色映射到其渠道对应角色(仅三个默认 code
*/
public function remapUserRolesToChannelRoles(): int
{
if (!$this->tableHasColumn('sa_system_role', 'dept_id')) {
return 0;
}
$codes = $this->getDefaultChannelRoleCodes();
if (empty($codes)) {
return 0;
}
$codeList = "'" . implode("','", array_map('addslashes', $codes)) . "'";
return Db::execute(
'UPDATE `sa_system_user_role` ur
INNER JOIN `sa_system_user` u ON ur.user_id = u.id
INNER JOIN `sa_system_role` r_old ON ur.role_id = r_old.id
INNER JOIN `sa_system_role` r_new ON r_new.dept_id = u.dept_id AND r_new.code = r_old.code
SET ur.role_id = r_new.id
WHERE u.dept_id > 0
AND r_old.dept_id = 0
AND r_old.code IN (' . $codeList . ')
AND r_old.id <> ' . self::SUPER_ADMIN_ROLE_ID
);
}
/**
* @return string[]
*/
public function getDefaultChannelRoleCodes(): array
{
$codes = config('plugin.saiadmin.saithink.channel_default_role_codes', []);
if (!is_array($codes) || $codes === []) {
return ['yijidaili', 'erjidaili', 'sanjidaili'];
}
$out = [];
foreach ($codes as $code) {
$code = trim((string) $code);
if ($code !== '') {
$out[] = $code;
}
}
return $out;
}
/**
* 删除渠道下不在默认三个 code 内、且未被用户绑定的角色
*/
public function pruneExtraChannelRoles(int $deptId): int
{
if ($deptId <= 0) {
return 0;
}
$allowed = $this->getDefaultChannelRoleCodes();
if (empty($allowed)) {
return 0;
}
$query = SystemRole::where('dept_id', $deptId)->whereNotIn('code', $allowed);
$roleIds = $query->column('id');
if (empty($roleIds)) {
return 0;
}
$usedIds = Db::name('sa_system_user_role')->whereIn('role_id', $roleIds)->column('role_id');
$usedMap = array_flip($usedIds ?: []);
$pruned = 0;
foreach ($roleIds as $roleId) {
if (isset($usedMap[$roleId])) {
continue;
}
Db::name('sa_system_role_menu')->where('role_id', $roleId)->delete();
Db::name('sa_system_role_dept')->where('role_id', $roleId)->delete();
SystemRole::destroy($roleId);
$pruned++;
}
return $pruned;
}
/**
* @return array<int, array<string, mixed>>
*/
private function defaultTemplateRoles(): array
{
$codes = $this->getDefaultChannelRoleCodes();
if (empty($codes)) {
return [];
}
$query = Db::table('sa_system_role')
->where('id', '<>', self::SUPER_ADMIN_ROLE_ID)
->whereIn('code', $codes);
if ($this->tableHasColumn('sa_system_role', 'dept_id')) {
$query->where('dept_id', 0);
}
$rows = $query->select()->toArray();
if (empty($rows)) {
return [];
}
if (count($rows) === count($codes)) {
return $this->sortRolesByLevelDesc($rows);
}
// 按配置 code 补齐,缺失的 code 跳过,最终按角色级别从大到小排序
$byCode = [];
foreach ($rows as $row) {
$byCode[(string) ($row['code'] ?? '')] = $row;
}
$ordered = [];
foreach ($codes as $code) {
if (isset($byCode[$code])) {
$ordered[] = $byCode[$code];
}
}
return $this->sortRolesByLevelDesc($ordered);
}
/**
* 按角色级别从大到小排序(同级别按 sort 降序)
*
* @param array<int, array<string, mixed>> $rows
* @return array<int, array<string, mixed>>
*/
private function sortRolesByLevelDesc(array $rows): array
{
usort($rows, static function (array $a, array $b): int {
$levelCompare = (int) ($b['level'] ?? 0) <=> (int) ($a['level'] ?? 0);
if ($levelCompare !== 0) {
return $levelCompare;
}
return (int) ($b['sort'] ?? 0) <=> (int) ($a['sort'] ?? 0);
});
return $rows;
}
private function insertRoleFromTemplate(array $template, int $deptId): int
{
unset(
$template['id'],
$template['create_time'],
$template['update_time'],
$template['delete_time']
);
$template['dept_id'] = $deptId;
$now = date('Y-m-d H:i:s');
if (!isset($template['create_time'])) {
$template['create_time'] = $now;
}
if (!isset($template['update_time'])) {
$template['update_time'] = $now;
}
return (int) Db::table('sa_system_role')->insertGetId($template);
}
private function copyRoleMenus(int $fromRoleId, int $toRoleId): void
{
$menuIds = Db::name('sa_system_role_menu')->where('role_id', $fromRoleId)->column('menu_id');
if (empty($menuIds)) {
return;
}
$rows = [];
foreach ($menuIds as $menuId) {
$rows[] = ['role_id' => $toRoleId, 'menu_id' => $menuId];
}
Db::name('sa_system_role_menu')->limit(100)->insertAll($rows);
}
private function roleExists(int $deptId, string $code): bool
{
return SystemRole::where('dept_id', $deptId)->where('code', $code)->count() > 0;
}
private function tableHasColumn(string $table, string $column): bool
{
try {
$fields = Db::getFields($table);
return isset($fields[$column]);
} catch (\Throwable $e) {
return false;
}
}
}