feat(permission): 完成权限模块修改接口全链路实现(含分布式锁、同级重名、父子module一致、非法移动、状态可变更等校验)
This commit is contained in:
parent
24155039b6
commit
74fa306d7a
@ -8,12 +8,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Elysia } from 'elysia';
|
import { Elysia } from 'elysia';
|
||||||
import { CreatePermissionSchema } from './permission.schema';
|
import { CreatePermissionSchema, GetPermissionTreeByPidParamsSchema } from './permission.schema';
|
||||||
import { CreatePermissionResponsesSchema } from './permission.response';
|
import { CreatePermissionResponsesSchema } from './permission.response';
|
||||||
import { DeletePermissionParamsSchema } from './permission.schema';
|
import { DeletePermissionParamsSchema } from './permission.schema';
|
||||||
import { DeletePermissionResponsesSchema } from './permission.response';
|
import { DeletePermissionResponsesSchema } from './permission.response';
|
||||||
|
import { UpdatePermissionParamsSchema, UpdatePermissionBodySchema } from './permission.schema';
|
||||||
|
import { UpdatePermissionResponsesSchema } from './permission.response';
|
||||||
import { permissionService } from './permission.service';
|
import { permissionService } from './permission.service';
|
||||||
import { tags } from '@/constants/swaggerTags';
|
import { tags } from '@/constants/swaggerTags';
|
||||||
|
import { GetPermissionTreeQuerySchema } from './permission.schema';
|
||||||
|
import { GetPermissionTreeResponsesSchema } from './permission.response';
|
||||||
|
|
||||||
export const permissionController = new Elysia()
|
export const permissionController = new Elysia()
|
||||||
/**
|
/**
|
||||||
@ -22,7 +26,7 @@ export const permissionController = new Elysia()
|
|||||||
* @description 创建新的权限项,支持树形结构
|
* @description 创建新的权限项,支持树形结构
|
||||||
*/
|
*/
|
||||||
.post(
|
.post(
|
||||||
'/api/permission',
|
'/',
|
||||||
async ({ body }) => permissionService.createPermission(body),
|
async ({ body }) => permissionService.createPermission(body),
|
||||||
{
|
{
|
||||||
body: CreatePermissionSchema,
|
body: CreatePermissionSchema,
|
||||||
@ -41,7 +45,7 @@ export const permissionController = new Elysia()
|
|||||||
* @description 删除指定权限,需检查子权限和引用关系
|
* @description 删除指定权限,需检查子权限和引用关系
|
||||||
*/
|
*/
|
||||||
.delete(
|
.delete(
|
||||||
'/api/permission/:id',
|
'/:id',
|
||||||
async ({ params }) => permissionService.deletePermission(params.id),
|
async ({ params }) => permissionService.deletePermission(params.id),
|
||||||
{
|
{
|
||||||
params: DeletePermissionParamsSchema,
|
params: DeletePermissionParamsSchema,
|
||||||
@ -53,4 +57,44 @@ export const permissionController = new Elysia()
|
|||||||
},
|
},
|
||||||
response: DeletePermissionResponsesSchema,
|
response: DeletePermissionResponsesSchema,
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
/**
|
||||||
|
* 修改权限接口
|
||||||
|
* @route PUT /api/permission/:id
|
||||||
|
* @description 修改指定权限,支持字段变更、同级重名、父子module一致、非法移动、状态可变更等校验
|
||||||
|
*/
|
||||||
|
.put(
|
||||||
|
'/:id',
|
||||||
|
async ({ params, body }) => permissionService.updatePermission(params.id, body),
|
||||||
|
{
|
||||||
|
params: UpdatePermissionParamsSchema,
|
||||||
|
body: UpdatePermissionBodySchema,
|
||||||
|
detail: {
|
||||||
|
summary: '修改权限',
|
||||||
|
description: '修改指定权限,支持字段变更、同级重名、父子module一致、非法移动、状态可变更等校验',
|
||||||
|
tags: [tags.permission],
|
||||||
|
operationId: 'updatePermission',
|
||||||
|
},
|
||||||
|
response: UpdatePermissionResponsesSchema,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
/**
|
||||||
|
* 查看权限完整树接口
|
||||||
|
* @route GET /api/permission/tree
|
||||||
|
* @description 查询权限完整树,支持多条件筛选
|
||||||
|
*/
|
||||||
|
.get(
|
||||||
|
'/tree/:pid',
|
||||||
|
async ({ query, params }) => permissionService.getPermissionTree(query, params.pid),
|
||||||
|
{
|
||||||
|
query: GetPermissionTreeQuerySchema,
|
||||||
|
params: GetPermissionTreeByPidParamsSchema,
|
||||||
|
detail: {
|
||||||
|
summary: '查看权限完整树',
|
||||||
|
description: '查询权限完整树,支持module、status、type多条件筛选',
|
||||||
|
tags: [tags.permission],
|
||||||
|
operationId: 'getPermissionTree',
|
||||||
|
},
|
||||||
|
response: GetPermissionTreeResponsesSchema,
|
||||||
|
}
|
||||||
);
|
);
|
@ -403,3 +403,93 @@ CREATE TABLE sys_permission (
|
|||||||
6. **非法移动**
|
6. **非法移动**
|
||||||
- 输入:pid为自身或子节点
|
- 输入:pid为自身或子节点
|
||||||
- 预期:返回400错误
|
- 预期:返回400错误
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. 查看权限完整树接口 (GET /api/permission/tree)
|
||||||
|
|
||||||
|
#### 业务逻辑
|
||||||
|
|
||||||
|
1. **参数校验**
|
||||||
|
- 支持可选参数:module(模块)、status(状态)、type(权限类型)
|
||||||
|
- module:字符串,筛选所属模块
|
||||||
|
- status:数字或字符串,1=启用,0=禁用,默认全部
|
||||||
|
- type:数字或字符串,1=菜单,2=按钮,3=接口,4=数据,默认全部
|
||||||
|
|
||||||
|
2. **业务规则**
|
||||||
|
- 查询所有符合条件的权限数据
|
||||||
|
- 按level和sort排序
|
||||||
|
- 构建树形结构(递归或循环)
|
||||||
|
- 只返回未被禁用(或根据status参数)权限
|
||||||
|
- 支持多模块、多类型、多状态筛选
|
||||||
|
|
||||||
|
3. **错误处理**
|
||||||
|
- 参数格式错误:400 Bad Request
|
||||||
|
- 查询异常:500 Internal Server Error
|
||||||
|
|
||||||
|
#### 性能与安全考虑
|
||||||
|
|
||||||
|
- 查询前加索引优化(module、status、type、pid、sort)
|
||||||
|
- 构建树结构时避免N+1查询,建议一次性查全量后内存组装
|
||||||
|
- 支持缓存(如Redis),提升高频查询性能
|
||||||
|
- 需管理员或有权限用户访问
|
||||||
|
|
||||||
|
#### 缓存策略
|
||||||
|
|
||||||
|
- 支持按module、status、type等条件缓存完整树结构
|
||||||
|
- 查询后写入缓存,更新/删除/新增权限时清理相关缓存
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
**成功响应 (200)**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"message": "查询成功",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": "1",
|
||||||
|
"permission_key": "system",
|
||||||
|
"name": "系统管理",
|
||||||
|
"type": 1,
|
||||||
|
"module": "system",
|
||||||
|
"pid": "0",
|
||||||
|
"level": 1,
|
||||||
|
"sort": 1,
|
||||||
|
"status": 1,
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"id": "2",
|
||||||
|
"permission_key": "user",
|
||||||
|
"name": "用户管理",
|
||||||
|
"type": 1,
|
||||||
|
"module": "system",
|
||||||
|
"pid": "1",
|
||||||
|
"level": 2,
|
||||||
|
"sort": 1,
|
||||||
|
"status": 1,
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 测试用例
|
||||||
|
|
||||||
|
1. **正常查询**
|
||||||
|
- 输入:无参数
|
||||||
|
- 预期:返回完整权限树
|
||||||
|
2. **按模块筛选**
|
||||||
|
- 输入:module=system
|
||||||
|
- 预期:只返回system模块的权限树
|
||||||
|
3. **按状态筛选**
|
||||||
|
- 输入:status=1
|
||||||
|
- 预期:只返回启用权限
|
||||||
|
4. **按类型筛选**
|
||||||
|
- 输入:type=1
|
||||||
|
- 预期:只返回菜单类型权限
|
||||||
|
5. **参数错误**
|
||||||
|
- 输入:非法status/type/module
|
||||||
|
- 预期:返回400错误
|
@ -196,3 +196,41 @@ export const UpdatePermissionResponsesSchema = {
|
|||||||
|
|
||||||
/** 修改权限成功响应类型 */
|
/** 修改权限成功响应类型 */
|
||||||
export type UpdatePermissionSuccessType = Static<(typeof UpdatePermissionResponsesSchema)[200]>;
|
export type UpdatePermissionSuccessType = Static<(typeof UpdatePermissionResponsesSchema)[200]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 权限树节点Schema
|
||||||
|
* @description 递归结构,children为子权限数组
|
||||||
|
*/
|
||||||
|
export const PermissionTreeItemSchema = t.Recursive((self) =>
|
||||||
|
t.Object({
|
||||||
|
id: t.String({ description: '权限ID(bigint字符串)', examples: ['1', '2', '100'] }),
|
||||||
|
permissionKey: t.String({ description: '权限标识', examples: ['user:create'] }),
|
||||||
|
name: t.String({ description: '权限名称', examples: ['创建用户'] }),
|
||||||
|
description: t.Optional(t.String({ description: '权限描述', examples: ['创建新用户的权限'] })),
|
||||||
|
type: t.Number({ description: '权限类型:1=菜单,2=按钮,3=接口,4=数据', examples: [1, 2, 3, 4] }),
|
||||||
|
apiPathKey: t.Optional(t.String({ description: '接口路径标识', examples: ['/api/user'] })),
|
||||||
|
pagePathKey: t.Optional(t.String({ description: '前端路由标识', examples: ['/user'] })),
|
||||||
|
module: t.String({ description: '所属模块', examples: ['user'] }),
|
||||||
|
pid: t.String({ description: '父权限ID(bigint字符串)', examples: ['0', '1'] }),
|
||||||
|
level: t.Number({ description: '权限层级', examples: [1, 2, 3] }),
|
||||||
|
sort: t.String({ description: '排序值', examples: ['0', '1', '10'] }),
|
||||||
|
icon: t.Optional(t.String({ description: '图标标识', examples: ['icon-user'] })),
|
||||||
|
status: t.Number({ description: '权限状态:1=启用,0=禁用', examples: [1, 0] }),
|
||||||
|
createdAt: t.String({ description: '创建时间', examples: ['2024-12-19T10:30:00Z'] }),
|
||||||
|
updatedAt: t.String({ description: '更新时间', examples: ['2024-12-19T10:30:00Z'] }),
|
||||||
|
children: t.Optional(t.Array(self)),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看权限完整树接口响应组合
|
||||||
|
* @description 用于Controller中定义所有可能的响应格式
|
||||||
|
*/
|
||||||
|
export const GetPermissionTreeResponsesSchema = {
|
||||||
|
200: responseWrapperSchema(t.Array(PermissionTreeItemSchema)),
|
||||||
|
400: responseWrapperSchema(t.Object({ error: t.String({ description: '请求错误', examples: ['参数错误'] }) })),
|
||||||
|
500: responseWrapperSchema(t.Object({ error: t.String({ description: '服务器错误', examples: ['内部服务器错误'] }) })),
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 查看权限完整树成功响应类型 */
|
||||||
|
export type GetPermissionTreeSuccessType = Static<typeof GetPermissionTreeResponsesSchema[200]>;
|
@ -288,3 +288,56 @@ export const PermissionStatus = {
|
|||||||
ENABLED: 1, // 启用
|
ENABLED: 1, // 启用
|
||||||
DISABLED: 0, // 禁用
|
DISABLED: 0, // 禁用
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看权限完整树接口查询参数Schema
|
||||||
|
*/
|
||||||
|
export const GetPermissionTreeQuerySchema = t.Object({
|
||||||
|
module: t.Optional(
|
||||||
|
t.String({
|
||||||
|
minLength: 1,
|
||||||
|
maxLength: 30,
|
||||||
|
description: '所属模块,筛选权限所属模块',
|
||||||
|
examples: ['system', 'user'],
|
||||||
|
})
|
||||||
|
),
|
||||||
|
status: t.Optional(
|
||||||
|
t.Union([
|
||||||
|
t.Literal(1), t.Literal(0), t.Literal('1'), t.Literal('0'), t.Literal('all')
|
||||||
|
], {
|
||||||
|
description: '权限状态,1=启用,0=禁用,all=全部',
|
||||||
|
examples: [1, 0, 'all'],
|
||||||
|
})
|
||||||
|
),
|
||||||
|
type: t.Optional(
|
||||||
|
t.Union([
|
||||||
|
t.Literal(1), t.Literal(2), t.Literal(3), t.Literal(4),
|
||||||
|
t.Literal('1'), t.Literal('2'), t.Literal('3'), t.Literal('4'), t.Literal('all')
|
||||||
|
], {
|
||||||
|
description: '权限类型,1=菜单,2=按钮,3=接口,4=数据,all=全部',
|
||||||
|
examples: [1, 2, 3, 4, 'all'],
|
||||||
|
})
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export type GetPermissionTreeQuery = Static<typeof GetPermissionTreeQuerySchema>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看指定父权限下的子权限树接口路径参数Schema
|
||||||
|
* @description 校验GET /api/permission/tree/:pid 路径参数
|
||||||
|
*/
|
||||||
|
export const GetPermissionTreeByPidParamsSchema = t.Object({
|
||||||
|
pid: t.Union([
|
||||||
|
t.Literal('0'),
|
||||||
|
t.String({
|
||||||
|
pattern: '^[1-9]\\d*$',
|
||||||
|
description: '父权限ID,bigint字符串,必须为正整数',
|
||||||
|
}),
|
||||||
|
], {
|
||||||
|
description: '父权限ID,0表示获取根权限,其他值表示获取指定父权限下的子权限树',
|
||||||
|
examples: ['0', '1', '100', '123456789'],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 查看指定父权限下的子权限树参数类型 */
|
||||||
|
export type GetPermissionTreeByPidParams = Static<typeof GetPermissionTreeByPidParamsSchema>;
|
@ -10,7 +10,8 @@
|
|||||||
import { Logger } from '@/plugins/logger/logger.service';
|
import { Logger } from '@/plugins/logger/logger.service';
|
||||||
import { db } from '@/plugins/drizzle/drizzle.service';
|
import { db } from '@/plugins/drizzle/drizzle.service';
|
||||||
import { sysPermission, sysRolePermissions } from '@/eneities';
|
import { sysPermission, sysRolePermissions } from '@/eneities';
|
||||||
import { eq, and, max, ne } from 'drizzle-orm';
|
import { eq, and, max, ne, sql, asc } from 'drizzle-orm';
|
||||||
|
import { alias, bigint, int, mysqlTable } from 'drizzle-orm/mysql-core';
|
||||||
import { successResponse, BusinessError } from '@/utils/responseFormate';
|
import { successResponse, BusinessError } from '@/utils/responseFormate';
|
||||||
import { nextId } from '@/utils/snowflake';
|
import { nextId } from '@/utils/snowflake';
|
||||||
import { DistributedLockService } from '@/utils/distributedLock';
|
import { DistributedLockService } from '@/utils/distributedLock';
|
||||||
@ -280,6 +281,141 @@ export class PermissionService {
|
|||||||
await lock.release();
|
await lock.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取权限完整树
|
||||||
|
* @param query 查询参数(module、status、type)
|
||||||
|
* @returns Promise<GetPermissionTreeSuccessType>
|
||||||
|
*/
|
||||||
|
public async getPermissionTree(query: import('./permission.schema').GetPermissionTreeQuery, pid: string): Promise<import('./permission.response').GetPermissionTreeSuccessType> {
|
||||||
|
// 1. 定义表别名
|
||||||
|
const ctetTableName = 'ctet'
|
||||||
|
// 锚点表
|
||||||
|
const pointTable = alias(sysPermission, 'pt');
|
||||||
|
// CTE表
|
||||||
|
const cteTable = alias(sysPermission, ctetTableName);
|
||||||
|
const cteAlias = mysqlTable(ctetTableName, { id: int() })
|
||||||
|
// 递归表
|
||||||
|
const recursiveTable = alias(sysPermission, 'rt');
|
||||||
|
|
||||||
|
// 2. 构建基础查询条件
|
||||||
|
const baseConditions: any[] = [];
|
||||||
|
if (query.module) baseConditions.push(eq(sysPermission.module, query.module));
|
||||||
|
if (query.status && query.status !== 'all') baseConditions.push(eq(sysPermission.status, Number(query.status)));
|
||||||
|
if (query.type && query.type !== 'all') baseConditions.push(eq(sysPermission.type, Number(query.type)));
|
||||||
|
// 找根节点
|
||||||
|
if(pid !== '0'){
|
||||||
|
baseConditions.push(eq(pointTable.id, pid));
|
||||||
|
}else{
|
||||||
|
baseConditions.push(eq(pointTable.pid, pid));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 3. 定义主查询
|
||||||
|
const mainQuery = db().select({
|
||||||
|
id: pointTable.id,
|
||||||
|
permissionKey: pointTable.permissionKey,
|
||||||
|
name: pointTable.name,
|
||||||
|
description: pointTable.description,
|
||||||
|
type: pointTable.type,
|
||||||
|
apiPathKey: pointTable.apiPathKey,
|
||||||
|
pagePathKey: pointTable.pagePathKey,
|
||||||
|
module: pointTable.module,
|
||||||
|
pid: pointTable.pid,
|
||||||
|
level: pointTable.level,
|
||||||
|
sort: pointTable.sort,
|
||||||
|
icon: pointTable.icon,
|
||||||
|
status: pointTable.status,
|
||||||
|
createdAt: pointTable.createdAt,
|
||||||
|
updatedAt: pointTable.updatedAt,
|
||||||
|
})
|
||||||
|
.from(pointTable)
|
||||||
|
.where(and(...baseConditions))
|
||||||
|
.union(
|
||||||
|
db().select({
|
||||||
|
id: recursiveTable.id,
|
||||||
|
permissionKey: recursiveTable.permissionKey,
|
||||||
|
name: recursiveTable.name,
|
||||||
|
description: recursiveTable.description,
|
||||||
|
type: recursiveTable.type,
|
||||||
|
apiPathKey: recursiveTable.apiPathKey,
|
||||||
|
pagePathKey: recursiveTable.pagePathKey,
|
||||||
|
module: recursiveTable.module,
|
||||||
|
pid: recursiveTable.pid,
|
||||||
|
level: recursiveTable.level,
|
||||||
|
sort: recursiveTable.sort,
|
||||||
|
icon: recursiveTable.icon,
|
||||||
|
status: recursiveTable.status,
|
||||||
|
createdAt: recursiveTable.createdAt,
|
||||||
|
updatedAt: recursiveTable.updatedAt,
|
||||||
|
})
|
||||||
|
.from(recursiveTable)
|
||||||
|
.innerJoin(cteAlias, eq(recursiveTable.pid, cteTable.id))
|
||||||
|
)
|
||||||
|
// 4. 构建最终查询
|
||||||
|
const finalQuery = db()
|
||||||
|
.select({
|
||||||
|
id: cteTable.id,
|
||||||
|
permissionKey: cteTable.permissionKey,
|
||||||
|
name: cteTable.name,
|
||||||
|
description: cteTable.description,
|
||||||
|
type: cteTable.type,
|
||||||
|
apiPathKey: cteTable.apiPathKey,
|
||||||
|
pagePathKey: cteTable.pagePathKey,
|
||||||
|
module: cteTable.module,
|
||||||
|
pid: cteTable.pid,
|
||||||
|
level: cteTable.level,
|
||||||
|
sort: cteTable.sort,
|
||||||
|
icon: cteTable.icon,
|
||||||
|
status: cteTable.status,
|
||||||
|
createdAt: cteTable.createdAt,
|
||||||
|
updatedAt: cteTable.updatedAt,
|
||||||
|
})
|
||||||
|
.from(cteAlias)
|
||||||
|
.orderBy(asc(cteTable.level), asc(cteTable.sort));
|
||||||
|
|
||||||
|
// 5. 将union查询放入WITH RECURSIVE中,并构建最终查询
|
||||||
|
const recursiveSql = sql` WITH RECURSIVE ${cteTable} AS ( ${mainQuery} ) ${finalQuery} `;
|
||||||
|
// 6. 执行递归查询
|
||||||
|
const [rows] = await db().execute(recursiveSql);
|
||||||
|
const list = Array.isArray(rows) ? rows : (rows as any)[0];
|
||||||
|
|
||||||
|
// 7. 构建树结构
|
||||||
|
const nodeMap: Record<string, any> = {};
|
||||||
|
const roots: any[] = [];
|
||||||
|
|
||||||
|
for (const item of list as any[]) {
|
||||||
|
const node = {
|
||||||
|
id: String(item.id),
|
||||||
|
permissionKey: item.permissionKey || item.permission_key,
|
||||||
|
name: item.name,
|
||||||
|
description: item.description,
|
||||||
|
type: item.type,
|
||||||
|
apiPathKey: item.apiPathKey || item.api_path_key,
|
||||||
|
pagePathKey: item.pagePathKey || item.page_path_key,
|
||||||
|
module: item.module,
|
||||||
|
pid: String(item.pid),
|
||||||
|
level: item.level,
|
||||||
|
sort: String(item.sort || '0'),
|
||||||
|
icon: item.icon,
|
||||||
|
status: item.status,
|
||||||
|
createdAt: item.createdAt || item.created_at,
|
||||||
|
updatedAt: item.updatedAt || item.updated_at,
|
||||||
|
children: [],
|
||||||
|
};
|
||||||
|
nodeMap[node.id] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 组装父子关系
|
||||||
|
for (const node of Object.values(nodeMap)) {
|
||||||
|
if (node.pid && node.pid !== '0' && nodeMap[node.pid]) {
|
||||||
|
nodeMap[node.pid].children.push(node);
|
||||||
|
} else {
|
||||||
|
roots.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return successResponse(roots, '查询权限树成功');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const permissionService = new PermissionService();
|
export const permissionService = new PermissionService();
|
@ -80,21 +80,21 @@ CREATE TABLE sys_permission (
|
|||||||
|
|
||||||
### 3.4 查看权限完整树接口
|
### 3.4 查看权限完整树接口
|
||||||
|
|
||||||
- [ ] 4.0 创建查看权限完整树接口 (GET /api/permission/tree)
|
- [x] 4.0 创建查看权限完整树接口 (GET /api/permission/tree)
|
||||||
- [ ] 4.1 更新 `permission.docs.md` - 添加查看权限完整树业务逻辑文档
|
- [x] 4.1 更新 `permission.docs.md` - 添加查看权限完整树业务逻辑文档
|
||||||
- [ ] 4.2 更新 `permission.schema.ts` - 定义查看权限完整树Schema
|
- [x] 4.2 更新 `permission.schema.ts` - 定义查看权限完整树Schema
|
||||||
- [ ] 4.3 更新 `permission.response.ts` - 定义查看权限完整树响应格式
|
- [x] 4.3 更新 `permission.response.ts` - 定义查看权限完整树响应格式
|
||||||
- [ ] 4.4 更新 `permission.service.ts` - 实现查看权限完整树业务逻辑
|
- [x] 4.4 更新 `permission.service.ts` - 实现查看权限完整树业务逻辑
|
||||||
- [ ] 4.5 更新 `permission.controller.ts` - 实现查看权限完整树路由
|
- [x] 4.5 更新 `permission.controller.ts` - 实现查看权限完整树路由
|
||||||
|
|
||||||
### 3.5 查看指定权限树接口
|
### 3.5 查看指定权限树接口
|
||||||
|
|
||||||
- [ ] 5.0 创建查看指定权限树接口 (GET /api/permission/:id/tree)
|
- ~~[ ] 5.0 创建查看指定权限树接口 (GET /api/permission/:id/tree)~~
|
||||||
- [ ] 5.1 更新 `permission.docs.md` - 添加查看指定权限树业务逻辑文档
|
- ~~[ ] 5.1 更新 `permission.docs.md` - 添加查看指定权限树业务逻辑文档~~
|
||||||
- [ ] 5.2 更新 `permission.schema.ts` - 定义查看指定权限树Schema
|
- ~~[ ] 5.2 更新 `permission.schema.ts` - 定义查看指定权限树Schema~~
|
||||||
- [ ] 5.3 更新 `permission.response.ts` - 定义查看指定权限树响应格式
|
- ~~[ ] 5.3 更新 `permission.response.ts` - 定义查看指定权限树响应格式~~
|
||||||
- [ ] 5.4 更新 `permission.service.ts` - 实现查看指定权限树业务逻辑
|
- ~~[ ] 5.4 更新 `permission.service.ts` - 实现查看指定权限树业务逻辑~~
|
||||||
- [ ] 5.5 更新 `permission.controller.ts` - 实现查看指定权限树路由
|
- ~~[ ] 5.5 更新 `permission.controller.ts` - 实现查看指定权限树路由~~
|
||||||
|
|
||||||
### 3.6 权限排序接口
|
### 3.6 权限排序接口
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user