2
0
Эх сурвалжийг харах

✨ feat(tenant-auth): 添加简单的登录接口生成JWT token

- 新增认证路由 authRoutes,提供 /login 接口
- 使用固定账号密码:superadmin/admin123
- 成功登录后返回包含超级管理员ID(1)的JWT token
- 添加完整的认证API集成测试,覆盖成功和失败场景
- 所有测试通过,确保现有功能不受影响

🤖 Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 1 сар өмнө
parent
commit
402a21230f

+ 85 - 0
packages/tenant-module-mt/src/routes/auth.routes.ts

@@ -0,0 +1,85 @@
+import { OpenAPIHono } from '@hono/zod-openapi';
+import { z } from '@hono/zod-openapi';
+import { createRoute } from '@hono/zod-openapi';
+import { JWTUtil } from '@d8d/shared-utils';
+
+// 固定的超级管理员账号信息
+const SUPER_ADMIN_USERNAME = 'superadmin';
+const SUPER_ADMIN_PASSWORD = 'admin123';
+const SUPER_ADMIN_ID = 1;
+
+// 登录请求Schema
+const LoginSchema = z.object({
+  username: z.string().min(1, '用户名不能为空'),
+  password: z.string().min(1, '密码不能为空')
+}).openapi('LoginRequest');
+
+// 登录响应Schema
+const LoginResponseSchema = z.object({
+  token: z.string(),
+  userId: z.number(),
+  username: z.string(),
+  message: z.string()
+}).openapi('LoginResponse');
+
+// 创建认证路由
+const authRoutes = new OpenAPIHono();
+
+// 登录路由
+const loginRoute = createRoute({
+  method: 'post',
+  path: '/login',
+  request: {
+    body: {
+      content: {
+        'application/json': {
+          schema: LoginSchema
+        }
+      }
+    }
+  },
+  responses: {
+    200: {
+      description: '登录成功',
+      content: {
+        'application/json': {
+          schema: LoginResponseSchema
+        }
+      }
+    },
+    401: {
+      description: '用户名或密码错误',
+      content: {
+        'application/json': {
+          schema: z.object({
+            message: z.string()
+          })
+        }
+      }
+    }
+  }
+});
+
+authRoutes.openapi(loginRoute, async (c) => {
+  const { username, password } = c.req.valid('json');
+
+  // 验证用户名和密码
+  if (username !== SUPER_ADMIN_USERNAME || password !== SUPER_ADMIN_PASSWORD) {
+    return c.json({ message: '用户名或密码错误' }, 401);
+  }
+
+  // 生成JWT token
+  const token = JWTUtil.generateToken({
+    id: SUPER_ADMIN_ID,
+    username: SUPER_ADMIN_USERNAME
+  });
+
+  return c.json({
+    token,
+    userId: SUPER_ADMIN_ID,
+    username: SUPER_ADMIN_USERNAME,
+    message: '登录成功'
+  });
+});
+
+export { authRoutes };

+ 4 - 1
packages/tenant-module-mt/src/routes/index.ts

@@ -19,4 +19,7 @@ export const tenantRoutes = createCrudRoutes({
   dataPermission: {
     enabled: false // 租户管理不使用数据权限控制,由固定的超级管理员账号管理
   }
-});
+});
+
+// 导出认证路由
+export { authRoutes } from './auth.routes';

+ 82 - 0
packages/tenant-module-mt/tests/integration/auth-routes.integration.test.ts

@@ -0,0 +1,82 @@
+import { describe, it, expect, beforeEach } from 'vitest';
+import { testClient } from 'hono/testing';
+import { IntegrationTestDatabase, setupIntegrationDatabaseHooksWithEntities } from '@d8d/shared-test-util';
+import { authRoutes } from '../../src/routes';
+
+// 设置集成测试钩子
+setupIntegrationDatabaseHooksWithEntities([])
+
+describe('租户认证API集成测试', () => {
+  let client: ReturnType<typeof testClient<typeof authRoutes>>;
+
+  beforeEach(async () => {
+    // 创建测试客户端
+    client = testClient(authRoutes);
+  });
+
+  describe('POST /login - 登录接口', () => {
+    it('应该使用正确的账号密码成功登录', async () => {
+      const response = await client.login.$post({
+        json: {
+          username: 'superadmin',
+          password: 'admin123'
+        }
+      });
+
+      expect(response.status).toBe(200);
+      const data = await response.json();
+      expect(data.token).toBeDefined();
+      expect(data.userId).toBe(1);
+      expect(data.username).toBe('superadmin');
+      expect(data.message).toBe('登录成功');
+    });
+
+    it('应该拒绝错误的用户名', async () => {
+      const response = await client.login.$post({
+        json: {
+          username: 'wronguser',
+          password: 'admin123'
+        }
+      });
+
+      expect(response.status).toBe(401);
+      const data = await response.json();
+      expect(data.message).toBe('用户名或密码错误');
+    });
+
+    it('应该拒绝错误的密码', async () => {
+      const response = await client.login.$post({
+        json: {
+          username: 'superadmin',
+          password: 'wrongpassword'
+        }
+      });
+
+      expect(response.status).toBe(401);
+      const data = await response.json();
+      expect(data.message).toBe('用户名或密码错误');
+    });
+
+    it('应该拒绝空的用户名', async () => {
+      const response = await client.login.$post({
+        json: {
+          username: '',
+          password: 'admin123'
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+
+    it('应该拒绝空的密码', async () => {
+      const response = await client.login.$post({
+        json: {
+          username: 'superadmin',
+          password: ''
+        }
+      });
+
+      expect(response.status).toBe(400);
+    });
+  });
+});