- 切换数据库为PostgreSQL,更新依赖与配置 - 重构权限体系,使用sys_user/sys_role/sys_menu等新实体替代旧模型 - 移除旧的用户角色/权限/系统设置相关代码与实体 - 新增字典、菜单管理模块与基础CRUD接口 - 优化鉴权逻辑,基于菜单权限标识进行接口校验 - 前端同步更新API、路由与权限控制逻辑 - 删除冗余的v-permission指令与相关代码
308 lines
6.5 KiB
TypeScript
308 lines
6.5 KiB
TypeScript
import { Provide, Inject } from "@midwayjs/core";
|
|
import { InjectRepository } from "@midwayjs/sequelize";
|
|
import SysRole from "../entity/sysRole.entity";
|
|
import SysUserRole from "../entity/sysUserRole.entity";
|
|
import SysRoleMenu from "../entity/sysRoleMenu.entity";
|
|
import SysMenu from "../entity/sysMenu.entity";
|
|
import { Op } from "sequelize";
|
|
|
|
@Provide()
|
|
export class RoleService {
|
|
@InjectRepository(SysRole)
|
|
roleModel!: typeof SysRole;
|
|
|
|
@InjectRepository(SysUserRole)
|
|
userRoleModel!: typeof SysUserRole;
|
|
|
|
@InjectRepository(SysRoleMenu)
|
|
roleMenuModel!: typeof SysRoleMenu;
|
|
|
|
@InjectRepository(SysMenu)
|
|
menuModel!: typeof SysMenu;
|
|
|
|
async list(query: {
|
|
page?: number;
|
|
pageSize?: number;
|
|
keyword?: string;
|
|
}): Promise<any> {
|
|
const page = query.page || 1;
|
|
const pageSize = query.pageSize || 10;
|
|
const offset = (page - 1) * pageSize;
|
|
|
|
const whereClause: any = {};
|
|
if (query.keyword) {
|
|
whereClause[Op.or] = [
|
|
{ roleCode: { [Op.like]: `%${query.keyword}%` } },
|
|
{ roleName: { [Op.like]: `%${query.keyword}%` } },
|
|
];
|
|
}
|
|
|
|
const { count, rows } = await this.roleModel.findAndCountAll({
|
|
where: whereClause,
|
|
attributes: ["id", "roleCode", "roleName", "description", "sortOrder"],
|
|
offset,
|
|
limit: pageSize,
|
|
order: [["sort_order", "ASC"], ["created_at", "DESC"]],
|
|
});
|
|
|
|
for (const role of rows) {
|
|
const userCount = await this.userRoleModel.count({
|
|
where: { roleId: role.id },
|
|
});
|
|
(role as any).setDataValue("userCount", userCount);
|
|
}
|
|
|
|
return {
|
|
code: 200,
|
|
message: "获取角色列表成功",
|
|
data: {
|
|
list: rows,
|
|
total: count,
|
|
page,
|
|
pageSize,
|
|
},
|
|
};
|
|
}
|
|
|
|
async getAllRoles(): Promise<any> {
|
|
const roles = await this.roleModel.findAll({
|
|
order: [["sort_order", "ASC"], ["created_at", "DESC"]],
|
|
});
|
|
|
|
return {
|
|
code: 200,
|
|
message: "获取所有角色成功",
|
|
data: roles,
|
|
};
|
|
}
|
|
|
|
async detail(id: string): Promise<any> {
|
|
const role = await this.roleModel.findByPk(Number(id));
|
|
|
|
if (!role) {
|
|
return {
|
|
code: 404,
|
|
message: "角色不存在",
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
const userCount = await this.userRoleModel.count({
|
|
where: { roleId: role.id },
|
|
});
|
|
(role as any).setDataValue("userCount", userCount);
|
|
|
|
return {
|
|
code: 200,
|
|
message: "获取角色详情成功",
|
|
data: role,
|
|
};
|
|
}
|
|
|
|
async create(data: any): Promise<any> {
|
|
const { roleCode, roleName, description, sortOrder = 0 } = data;
|
|
|
|
const existingRole = await this.roleModel.findOne({
|
|
where: { roleCode },
|
|
});
|
|
|
|
if (existingRole) {
|
|
return {
|
|
code: 400,
|
|
message: "角色编码已存在",
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
const role = await this.roleModel.create({
|
|
roleCode,
|
|
roleName,
|
|
description,
|
|
sort_order: sortOrder,
|
|
});
|
|
|
|
return {
|
|
code: 200,
|
|
message: "创建角色成功",
|
|
data: role,
|
|
};
|
|
}
|
|
|
|
async update(id: string, data: any): Promise<any> {
|
|
const role = await this.roleModel.findByPk(Number(id));
|
|
|
|
if (!role) {
|
|
return {
|
|
code: 404,
|
|
message: "角色不存在",
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
if (data.roleCode && data.roleCode !== role.roleCode) {
|
|
const existingRole = await this.roleModel.findOne({
|
|
where: { roleCode: data.roleCode },
|
|
});
|
|
|
|
if (existingRole) {
|
|
return {
|
|
code: 400,
|
|
message: "角色编码已存在",
|
|
data: null,
|
|
};
|
|
}
|
|
}
|
|
|
|
const updateData: any = {};
|
|
if (data.roleCode !== undefined) updateData.roleCode = data.roleCode;
|
|
if (data.roleName !== undefined) updateData.roleName = data.roleName;
|
|
if (data.description !== undefined) updateData.description = data.description;
|
|
if (data.sortOrder !== undefined) updateData.sort_order = data.sortOrder;
|
|
|
|
await role.update(updateData);
|
|
|
|
return {
|
|
code: 200,
|
|
message: "更新角色成功",
|
|
data: role,
|
|
};
|
|
}
|
|
|
|
async delete(id: string): Promise<any> {
|
|
const role = await this.roleModel.findByPk(Number(id));
|
|
|
|
if (!role) {
|
|
return {
|
|
code: 404,
|
|
message: "角色不存在",
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
const userCount = await this.userRoleModel.count({
|
|
where: { roleId: Number(id) },
|
|
});
|
|
|
|
if (userCount > 0) {
|
|
return {
|
|
code: 400,
|
|
message: `该角色下有${userCount}个用户,无法删除`,
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
await this.roleMenuModel.destroy({
|
|
where: { roleId: Number(id) },
|
|
});
|
|
|
|
await role.destroy();
|
|
|
|
return {
|
|
code: 200,
|
|
message: "删除角色成功",
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
async getMenus(roleId: number): Promise<any> {
|
|
const role = await this.roleModel.findByPk(roleId);
|
|
|
|
if (!role) {
|
|
return {
|
|
code: 404,
|
|
message: "角色不存在",
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
const roleMenus = await this.roleMenuModel.findAll({
|
|
where: { roleId },
|
|
});
|
|
const assignedMenuIds = roleMenus.map(rm => rm.menuId);
|
|
|
|
const menus = await this.menuModel.findAll({
|
|
where: { menuType: { [Op.in]: [1, 2] } },
|
|
order: [["sort_order", "ASC"], ["id", "ASC"]],
|
|
});
|
|
|
|
const menuTree = this.buildMenuTree(
|
|
menus.map(m => m.toJSON()),
|
|
assignedMenuIds
|
|
);
|
|
|
|
return {
|
|
code: 200,
|
|
message: "获取角色菜单成功",
|
|
data: {
|
|
menuTree,
|
|
menuIds: assignedMenuIds,
|
|
},
|
|
};
|
|
}
|
|
|
|
async updateMenus(roleId: number, menuIds: number[]): Promise<any> {
|
|
const role = await this.roleModel.findByPk(roleId);
|
|
|
|
if (!role) {
|
|
return {
|
|
code: 404,
|
|
message: "角色不存在",
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
await this.roleMenuModel.destroy({
|
|
where: { roleId },
|
|
});
|
|
|
|
if (menuIds && menuIds.length > 0) {
|
|
const roleMenus = menuIds.map(menuId => ({
|
|
roleId,
|
|
menuId,
|
|
}));
|
|
|
|
await this.roleMenuModel.bulkCreate(roleMenus);
|
|
}
|
|
|
|
return {
|
|
code: 200,
|
|
message: "菜单更新成功",
|
|
data: null,
|
|
};
|
|
}
|
|
|
|
private buildMenuTree(menus: any[], checkedIds: number[]): any[] {
|
|
const nodeMap = new Map<number, any>();
|
|
const menuIdSet = new Set(menus.map(m => m.id));
|
|
const tree: any[] = [];
|
|
|
|
for (const menu of menus) {
|
|
nodeMap.set(menu.id, {
|
|
...menu,
|
|
children: [],
|
|
checked: checkedIds.includes(menu.id),
|
|
});
|
|
}
|
|
|
|
for (const menu of menus) {
|
|
const node = nodeMap.get(menu.id)!;
|
|
const parent = menu.parentId ? nodeMap.get(menu.parentId) : undefined;
|
|
|
|
// 仅当父节点为一级节点时挂为其子节点,保证输出两级树;
|
|
// 父节点不在列表或父节点本身是二级节点时,提升为一级节点
|
|
if (parent && !menuIdSet.has(parent.parentId)) {
|
|
parent.children.push(node);
|
|
} else {
|
|
tree.push(node);
|
|
}
|
|
}
|
|
|
|
// 一级节点已在查询时按 sort_order 排序,此处对子节点排序
|
|
for (const node of tree) {
|
|
node.children.sort((a: any, b: any) => a.sortOrder - b.sortOrder);
|
|
}
|
|
|
|
return tree;
|
|
}
|
|
}
|