Jelajahi Sumber

✨ feat(shared-crud): 完善数据权限错误处理机制

- 在路由层统一处理权限错误,返回403 Forbidden状态码而非500错误
- 修复只读模式路由的权限错误处理逻辑
- 更新Swagger文档,添加403错误响应定义
- 调整集成测试用例,验证403状态码返回
- 确保所有CRUD操作都正确实现权限错误处理
yourname 1 bulan lalu
induk
melakukan
ae336af911

+ 7 - 2
docs/stories/006.001.shared-crud-data-permission.story.md

@@ -1,7 +1,7 @@
 # Story 006.001: Shared-CRUD 数据权限控制完整实现
 
 ## Status
-Draft
+Ready for Review
 
 ## Story
 **As a** 业务模块开发者,
@@ -43,6 +43,7 @@ Draft
   - [x] 更新 `createCrudRoutes` 函数参数解构,包含 `dataPermission` 配置
   - [x] 更新路由处理函数,将 `dataPermission` 配置传递给 CRUD 服务
   - [x] 确保所有 CRUD 操作都正确传递数据权限配置
+  - [x] 在路由层统一处理权限错误,返回适当的HTTP状态码(403 Forbidden)而不是500错误
 - [x] 实现管理员权限覆盖机制 (AC: 7) 跳过:如果实际不指定dataPermission 配置,默认就是全权限(管理员权限),不需要额外实现管理员权限覆盖机制
   - [x] 添加管理员角色检查逻辑
   - [x] 实现管理员权限覆盖功能
@@ -155,8 +156,11 @@ Draft
 - 已更新 createCrudRoutes 函数参数解构,包含 dataPermission 配置
 - 已更新所有路由处理函数,将 dataPermission 配置传递给 CRUD 服务
 - 已确保所有 CRUD 操作都正确传递数据权限配置
+- 已在路由层统一处理权限错误,返回403状态码而不是500错误
+- 已修复只读模式路由的权限错误处理
+- 已更新集成测试验证403状态码返回
 - 构建验证通过,无类型错误
-- 所有单元测试通过
+- 所有单元测试和集成测试通过
 
 ### File List
 - [packages/shared-crud/src/types/data-permission.types.ts](packages/shared-crud/src/types/data-permission.types.ts) - 新增
@@ -164,6 +168,7 @@ Draft
 - [packages/shared-crud/src/services/index.ts](packages/shared-crud/src/services/index.ts) - 修改
 - [packages/shared-crud/src/services/concrete-crud.service.ts](packages/shared-crud/src/services/concrete-crud.service.ts) - 修改
 - [packages/shared-crud/src/routes/generic-crud.routes.ts](packages/shared-crud/src/routes/generic-crud.routes.ts) - 修改
+- [packages/shared-crud/tests/integration/data-permission.integration.test.ts](packages/shared-crud/tests/integration/data-permission.integration.test.ts) - 修改
 
 ## QA Results
 *此部分由QA代理在质量检查后填写*

+ 52 - 0
packages/shared-crud/src/routes/generic-crud.routes.ts

@@ -81,6 +81,10 @@ export function createCrudRoutes<
         description: '认证失败',
         content: { 'application/json': { schema: ErrorSchema } }
       },
+      403: {
+        description: '权限不足',
+        content: { 'application/json': { schema: ErrorSchema } }
+      },
       500: {
         description: '服务器错误',
         content: { 'application/json': { schema: ErrorSchema } }
@@ -113,6 +117,10 @@ export function createCrudRoutes<
         description: '认证失败',
         content: { 'application/json': { schema: ErrorSchema } }
       },
+      403: {
+        description: '权限不足',
+        content: { 'application/json': { schema: ErrorSchema } }
+      },
       500: {
         description: '服务器错误',
         content: { 'application/json': { schema: ErrorSchema } }
@@ -317,6 +325,14 @@ export function createCrudRoutes<
             return c.json({ code: 400, message: '数据已存在,请检查唯一性约束' }, 400);
           }
 
+          // 处理权限错误,返回403状态码
+          if (error instanceof Error && error.message.includes('无权')) {
+            return c.json({
+              code: 403,
+              message: error.message
+            }, 403);
+          }
+
           return c.json({
             code: 500,
             message: error instanceof Error ? error.message : '创建资源失败'
@@ -375,6 +391,15 @@ export function createCrudRoutes<
           if (error instanceof z.ZodError) {
             return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
           }
+
+          // 处理权限错误,返回403状态码
+          if (error instanceof Error && error.message.includes('无权')) {
+            return c.json({
+              code: 403,
+              message: error.message
+            }, 403);
+          }
+
           return c.json({
             code: 500,
             message: error instanceof Error ? error.message : '更新资源失败'
@@ -402,6 +427,15 @@ export function createCrudRoutes<
           if (error instanceof z.ZodError) {
             return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
           }
+
+          // 处理权限错误,返回403状态码
+          if (error instanceof Error && error.message.includes('无权')) {
+            return c.json({
+              code: 403,
+              message: error.message
+            }, 403);
+          }
+
           return c.json({
             code: 500,
             message: error instanceof Error ? error.message : '删除资源失败'
@@ -462,6 +496,15 @@ export function createCrudRoutes<
           if (error instanceof z.ZodError) {
             return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
           }
+
+          // 处理权限错误,返回403状态码
+          if (error instanceof Error && error.message.includes('无权')) {
+            return c.json({
+              code: 403,
+              message: error.message
+            }, 403);
+          }
+
           return c.json({
             code: 500,
             message: error instanceof Error ? error.message : '获取列表失败'
@@ -490,6 +533,15 @@ export function createCrudRoutes<
           if (error instanceof z.ZodError) {
             return c.json({ code: 400, message: '参数验证失败', errors: JSON.parse(error.message) }, 400);
           }
+
+          // 处理权限错误,返回403状态码
+          if (error instanceof Error && error.message.includes('无权')) {
+            return c.json({
+              code: 403,
+              message: error.message
+            }, 403);
+          }
+
           return c.json({
             code: 500,
             message: error instanceof Error ? error.message : '获取资源失败'

+ 6 - 6
packages/shared-crud/tests/integration/data-permission.integration.test.ts

@@ -268,9 +268,9 @@ describe('共享CRUD数据权限控制集成测试', () => {
       });
 
       console.debug('创建无权数据响应状态:', response.status);
-      expect(response.status).toBe(500); // 权限验证失败会抛出错误
+      expect(response.status).toBe(403); // 权限验证失败返回403 Forbidden
 
-      if (response.status === 500) {
+      if (response.status === 403) {
         const data = await response.json();
         expect(data.message).toContain('无权');
       }
@@ -408,9 +408,9 @@ describe('共享CRUD数据权限控制集成测试', () => {
       });
 
       console.debug('更新无权数据响应状态:', response.status);
-      expect(response.status).toBe(500); // 权限验证失败会抛出错误
+      expect(response.status).toBe(403); // 权限验证失败返回403 Forbidden
 
-      if (response.status === 500) {
+      if (response.status === 403) {
         const data = await response.json();
         expect(data.message).toContain('无权');
       }
@@ -466,9 +466,9 @@ describe('共享CRUD数据权限控制集成测试', () => {
       });
 
       console.debug('删除无权数据响应状态:', response.status);
-      expect(response.status).toBe(500); // 权限验证失败会抛出错误
+      expect(response.status).toBe(403); // 权限验证失败返回403 Forbidden
 
-      if (response.status === 500) {
+      if (response.status === 403) {
         const data = await response.json();
         expect(data.message).toContain('无权');
       }