diff --git a/src/modules/tags.ts b/src/constants/swaggerTags.ts similarity index 90% rename from src/modules/tags.ts rename to src/constants/swaggerTags.ts index 9f49431..bea4ae4 100644 --- a/src/modules/tags.ts +++ b/src/constants/swaggerTags.ts @@ -1,36 +1,36 @@ -/** - * @file API文档标签定义 - * @author hotok - * @date 2025-06-29 - * @lastEditor hotok - * @lastEditTime 2025-06-29 - * @description 统一管理Swagger文档中的标签,便于接口分类和文档组织 - */ - -/** - * API文档标签枚举 - * @description 用于Swagger文档的接口分类 - */ -export const tags = { - /** 用户相关接口 */ - user: 'User', - /** 认证相关接口 */ - auth: 'Auth', - /** 健康检查接口 */ - health: 'Health', - /** 测试接口 */ - test: 'Test', - /** 文件上传接口 */ - upload: 'Upload', - /** 系统管理接口 */ - system: 'System', - /** 权限管理接口 */ - permission: 'Permission', - /** 验证码相关接口 */ - captcha: 'Captcha', -} as const; - -/** - * 标签类型定义 - */ +/** + * @file API文档标签定义 + * @author hotok + * @date 2025-06-29 + * @lastEditor hotok + * @lastEditTime 2025-06-29 + * @description 统一管理Swagger文档中的标签,便于接口分类和文档组织 + */ + +/** + * API文档标签枚举 + * @description 用于Swagger文档的接口分类 + */ +export const tags = { + /** 用户相关接口 */ + user: 'User', + /** 认证相关接口 */ + auth: 'Auth', + /** 健康检查接口 */ + health: 'Health', + /** 测试接口 */ + test: 'Test', + /** 文件上传接口 */ + upload: 'Upload', + /** 系统管理接口 */ + system: 'System', + /** 权限管理接口 */ + permission: 'Permission', + /** 验证码相关接口 */ + captcha: 'Captcha', +} as const; + +/** + * 标签类型定义 + */ export type ApiTag = typeof tags[keyof typeof tags]; \ No newline at end of file diff --git a/src/modules/auth/auth.controller.ts b/src/modules/auth/auth.controller.ts index 37946b8..0ba99bb 100644 --- a/src/modules/auth/auth.controller.ts +++ b/src/modules/auth/auth.controller.ts @@ -11,7 +11,7 @@ import { Elysia } from 'elysia'; import { RegisterSchema, ActivateSchema, LoginSchema, RefreshSchema, ResetPasswordRequestSchema, ResetPasswordConfirmSchema } from './auth.schema'; import { RegisterResponsesSchema, ActivateResponsesSchema, LoginResponsesSchema, RefreshResponsesSchema, ResetPasswordRequestResponsesSchema, ResetPasswordConfirmResponsesSchema } from './auth.response'; import { authService } from './auth.service'; -import { tags } from '@/modules/tags'; +import { tags } from '@/constants/swaggerTags'; /** * 认证控制器 diff --git a/src/modules/captcha/captcha.controller.ts b/src/modules/captcha/captcha.controller.ts index 1a54c72..38797ad 100644 --- a/src/modules/captcha/captcha.controller.ts +++ b/src/modules/captcha/captcha.controller.ts @@ -9,7 +9,7 @@ import { Elysia, t } from 'elysia'; import { GenerateCaptchaSchema, VerifyCaptchaSchema } from './captcha.schema'; import { responseWrapperSchema } from '@/utils/responseFormate'; import { captchaService } from './captcha.service'; -import { tags } from '@/modules/tags'; +import { tags } from '@/constants/swaggerTags'; export const captchaController = new Elysia() /** diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index f3b2abe..f119a62 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -11,7 +11,7 @@ import { Elysia } from 'elysia'; import { userService } from './user.service'; import { GetCurrentUserResponsesSchema, GetUserListResponsesSchema } from './user.response'; import { UserListQuerySchema } from './user.schema'; -import { tags } from '@/modules/tags'; +import { tags } from '@/constants/swaggerTags'; import { jwtAuthPlugin } from '@/plugins/jwt/jwt.plugins'; import type { JwtUserType } from '@/type/jwt.type'; @@ -26,37 +26,29 @@ export const userController = new Elysia() * @description 获取当前登录用户的详细信息,需要JWT认证 */ .use(jwtAuthPlugin) - .get( - '/me', - ({ user }: { user: JwtUserType }) => userService.getCurrentUser(user.userId), - { - detail: { - summary: '获取当前用户信息', - description: '获取当前登录用户的详细信息,包括基本信息、状态、时间等', - tags: [tags.user], - operationId: 'getCurrentUser', - security: [{ bearerAuth: [] }] - }, - response: GetCurrentUserResponsesSchema, - } - ) + .get('/me', ({ user }: { user: JwtUserType }) => userService.getCurrentUser(user.userId), { + detail: { + summary: '获取当前用户信息', + description: '获取当前登录用户的详细信息,包括基本信息、状态、时间等', + tags: [tags.user], + operationId: 'getCurrentUser', + security: [{ bearerAuth: [] }], + }, + response: GetCurrentUserResponsesSchema, + }) /** * 用户列表查询接口 * @route GET /api/users * @description 获取用户列表,支持分页、搜索、筛选等功能,需要JWT认证 */ - .get( - '/list', - ({ query }) => userService.getUserList(query), - { - query: UserListQuerySchema, - detail: { - summary: '获取用户列表', - description: '获取用户列表,支持分页查询、关键词搜索、状态筛选、排序等功能', - tags: [tags.user], - operationId: 'getUserList', - security: [{ bearerAuth: [] }] - }, - response: GetUserListResponsesSchema, - } - ); \ No newline at end of file + .get('/list', ({ query }) => userService.getUserList(query), { + query: UserListQuerySchema, + detail: { + summary: '获取用户列表', + description: '获取用户列表,支持分页查询、关键词搜索、状态筛选、排序等功能', + tags: [tags.user], + operationId: 'getUserList', + security: [{ bearerAuth: [] }], + }, + response: GetUserListResponsesSchema, + }); diff --git a/src/modules/user/user.response.ts b/src/modules/user/user.response.ts index 98ff804..185963b 100644 --- a/src/modules/user/user.response.ts +++ b/src/modules/user/user.response.ts @@ -9,7 +9,64 @@ import { t, type Static } from 'elysia'; import { responseWrapperSchema } from '@/utils/responseFormate'; -import { CurrentUserSchema, UserListResponseSchema } from './user.schema'; +import { createPaginationResponseSchema } from '@/utils/pagination'; + +/** + * 当前用户信息响应Schema + * @description 获取当前用户信息的响应数据结构 + */ +export const CurrentUserSchema = t.Object({ + /** 用户ID */ + id: t.String({ + description: '用户ID(bigint类型以字符串形式返回防止精度丢失)', + examples: ['1', '2', '3'], + }), + /** 用户名 */ + username: t.String({ + description: '用户名', + examples: ['admin', 'testuser'], + }), + /** 邮箱地址 */ + email: t.String({ + description: '邮箱地址', + examples: ['user@example.com'], + }), + /** 昵称 */ + nickname: t.Union([t.String(), t.Null()], { + description: '用户昵称', + examples: ['管理员', '测试用户', null], + }), + /** 头像URL */ + avatar: t.Union([t.String(), t.Null()], { + description: '用户头像URL', + examples: ['https://example.com/avatar.jpg', null], + }), + /** 手机号 */ + phone: t.Union([t.String(), t.Null()], { + description: '手机号码', + examples: ['13800138000', null], + }), + /** 账号状态 */ + status: t.String({ + description: '账号状态', + examples: ['active', 'inactive', 'pending'], + }), + /** 最后登录时间 */ + lastLoginAt: t.Union([t.String(), t.Null()], { + description: '最后登录时间', + examples: ['2024-12-19T10:30:00Z', null], + }), + /** 创建时间 */ + createdAt: t.String({ + description: '创建时间', + examples: ['2024-12-19T10:30:00Z'], + }), + /** 更新时间 */ + updatedAt: t.String({ + description: '更新时间', + examples: ['2024-12-19T10:30:00Z'], + }), +}); /** * 获取当前用户信息接口响应组合 @@ -17,60 +74,172 @@ import { CurrentUserSchema, UserListResponseSchema } from './user.schema'; */ export const GetCurrentUserResponsesSchema = { 200: responseWrapperSchema(CurrentUserSchema), - 401: responseWrapperSchema(t.Object({ - error: t.String({ - description: '认证失败', - examples: ['未提供有效的认证令牌', '令牌已过期'] - }) - })), - 404: responseWrapperSchema(t.Object({ - error: t.String({ - description: '用户不存在', - examples: ['用户不存在或已被删除'] - }) - })), - 500: responseWrapperSchema(t.Object({ - error: t.String({ - description: '服务器错误', - examples: ['内部服务器错误'] - }) - })) + 401: responseWrapperSchema( + t.Object({ + error: t.String({ + description: '认证失败', + examples: ['未提供有效的认证令牌', '令牌已过期'], + }), + }), + ), + 404: responseWrapperSchema( + t.Object({ + error: t.String({ + description: '用户不存在', + examples: ['用户不存在或已被删除'], + }), + }), + ), + 500: responseWrapperSchema( + t.Object({ + error: t.String({ + description: '服务器错误', + examples: ['内部服务器错误'], + }), + }), + ), }; +/** + * 用户列表项Schema + * @description 用户列表中单个用户的数据结构 + */ +export const UserListItemSchema = t.Object({ + /** 用户ID */ + id: t.String({ + description: '用户ID(bigint类型以字符串形式返回防止精度丢失)', + examples: ['1', '2', '3'], + }), + /** 用户名 */ + username: t.String({ + description: '用户名', + examples: ['admin', 'testuser'], + }), + /** 邮箱地址 */ + email: t.String({ + description: '邮箱地址', + examples: ['user@example.com'], + }), + /** 手机号 */ + mobile: t.Union([t.String(), t.Null()], { + description: '手机号码', + examples: ['13800138000', null], + }), + /** 昵称 */ + nickname: t.Union([t.String(), t.Null()], { + description: '用户昵称', + examples: ['管理员', '测试用户', null], + }), + /** 头像URL */ + avatar: t.Union([t.String(), t.Null()], { + description: '用户头像URL', + examples: ['https://example.com/avatar.jpg', null], + }), + /** 账号状态 */ + status: t.String({ + description: '账号状态', + examples: ['active', 'inactive', 'pending'], + }), + /** 性别 */ + gender: t.Union([t.Number(), t.Null()], { + description: '性别:0-未知,1-男,2-女', + examples: [0, 1, 2, null], + }), + /** 生日 */ + birthday: t.Union([t.String(), t.Null()], { + description: '生日', + examples: ['1990-01-01', null], + }), + /** 个人简介 */ + bio: t.Union([t.String(), t.Null()], { + description: '个人简介', + examples: ['这是一段个人简介', null], + }), + /** 登录次数 */ + loginCount: t.Number({ + description: '登录次数', + examples: [0, 10, 100], + }), + /** 最后登录时间 */ + lastLoginAt: t.Union([t.String(), t.Null()], { + description: '最后登录时间', + examples: ['2024-12-19T10:30:00Z', null], + }), + /** 最后登录IP */ + lastLoginIp: t.Union([t.String(), t.Null()], { + description: '最后登录IP', + examples: ['192.168.1.1', null], + }), + /** 失败尝试次数 */ + failedAttempts: t.Number({ + description: '失败尝试次数', + examples: [0, 1, 5], + }), + /** 是否超级管理员 */ + isRoot: t.Boolean({ + description: '是否超级管理员', + examples: [true, false], + }), + /** 创建时间 */ + createdAt: t.String({ + description: '创建时间', + examples: ['2024-12-19T10:30:00Z'], + }), + /** 更新时间 */ + updatedAt: t.String({ + description: '更新时间', + examples: ['2024-12-19T10:30:00Z'], + }), +}); + /** * 获取用户列表接口响应组合 * @description 用于Controller中定义所有可能的响应格式 */ export const GetUserListResponsesSchema = { - 200: responseWrapperSchema(UserListResponseSchema), - 401: responseWrapperSchema(t.Object({ - error: t.String({ - description: '认证失败', - examples: ['未提供有效的认证令牌', '令牌已过期'] - }) - })), - 403: responseWrapperSchema(t.Object({ - error: t.String({ - description: '权限不足', - examples: ['权限不足,无法访问用户列表'] - }) - })), - 400: responseWrapperSchema(t.Object({ - error: t.String({ - description: '参数错误', - examples: ['分页参数无效', '搜索关键词格式错误'] - }) - })), - 500: responseWrapperSchema(t.Object({ - error: t.String({ - description: '服务器错误', - examples: ['内部服务器错误'] - }) - })) + 200: responseWrapperSchema(createPaginationResponseSchema(UserListItemSchema)), + 401: responseWrapperSchema( + t.Object({ + error: t.String({ + description: '认证失败', + examples: ['未提供有效的认证令牌', '令牌已过期'], + }), + }), + ), + 403: responseWrapperSchema( + t.Object({ + error: t.String({ + description: '权限不足', + examples: ['权限不足,无法访问用户列表'], + }), + }), + ), + 400: responseWrapperSchema( + t.Object({ + error: t.String({ + description: '参数错误', + examples: ['分页参数无效', '搜索关键词格式错误'], + }), + }), + ), + 500: responseWrapperSchema( + t.Object({ + error: t.String({ + description: '服务器错误', + examples: ['内部服务器错误'], + }), + }), + ), }; +/** 当前用户信息响应类型 */ +export type CurrentUserResponse = Static; + +/** 用户列表项类型 */ +export type UserListItem = Static; + /** 获取当前用户信息成功响应数据类型 */ -export type GetCurrentUserSuccessType = Static; +export type GetCurrentUserSuccessType = Static<(typeof GetCurrentUserResponsesSchema)[200]>; /** 获取用户列表成功响应数据类型 */ -export type GetUserListSuccessType = Static; \ No newline at end of file +export type GetUserListSuccessType = Static<(typeof GetUserListResponsesSchema)[200]>; diff --git a/src/modules/user/user.schema.ts b/src/modules/user/user.schema.ts index a1c3711..cdaa20f 100644 --- a/src/modules/user/user.schema.ts +++ b/src/modules/user/user.schema.ts @@ -10,62 +10,6 @@ import { t, type Static } from 'elysia'; import { createPaginationResponseSchema, createQuerySchema } from '@/utils/pagination'; -/** - * 当前用户信息响应Schema - * @description 获取当前用户信息的响应数据结构 - */ -export const CurrentUserSchema = t.Object({ - /** 用户ID */ - id: t.String({ - description: '用户ID(bigint类型以字符串形式返回防止精度丢失)', - examples: ['1', '2', '3'] - }), - /** 用户名 */ - username: t.String({ - description: '用户名', - examples: ['admin', 'testuser'] - }), - /** 邮箱地址 */ - email: t.String({ - description: '邮箱地址', - examples: ['user@example.com'] - }), - /** 昵称 */ - nickname: t.Union([t.String(), t.Null()], { - description: '用户昵称', - examples: ['管理员', '测试用户', null] - }), - /** 头像URL */ - avatar: t.Union([t.String(), t.Null()], { - description: '用户头像URL', - examples: ['https://example.com/avatar.jpg', null] - }), - /** 手机号 */ - phone: t.Union([t.String(), t.Null()], { - description: '手机号码', - examples: ['13800138000', null] - }), - /** 账号状态 */ - status: t.String({ - description: '账号状态', - examples: ['active', 'inactive', 'pending'] - }), - /** 最后登录时间 */ - lastLoginAt: t.Union([t.String(), t.Null()], { - description: '最后登录时间', - examples: ['2024-12-19T10:30:00Z', null] - }), - /** 创建时间 */ - createdAt: t.String({ - description: '创建时间', - examples: ['2024-12-19T10:30:00Z'] - }), - /** 更新时间 */ - updatedAt: t.String({ - description: '更新时间', - examples: ['2024-12-19T10:30:00Z'] - }) -}); /** * 用户列表查询参数Schema @@ -104,112 +48,9 @@ export const UserListQuerySchema = createQuerySchema(t.Object({ })) })); -/** - * 用户列表项Schema - * @description 用户列表中单个用户的数据结构 - */ -export const UserListItemSchema = t.Object({ - /** 用户ID */ - id: t.String({ - description: '用户ID(bigint类型以字符串形式返回防止精度丢失)', - examples: ['1', '2', '3'] - }), - /** 用户名 */ - username: t.String({ - description: '用户名', - examples: ['admin', 'testuser'] - }), - /** 邮箱地址 */ - email: t.String({ - description: '邮箱地址', - examples: ['user@example.com'] - }), - /** 手机号 */ - mobile: t.Union([t.String(), t.Null()], { - description: '手机号码', - examples: ['13800138000', null] - }), - /** 昵称 */ - nickname: t.Union([t.String(), t.Null()], { - description: '用户昵称', - examples: ['管理员', '测试用户', null] - }), - /** 头像URL */ - avatar: t.Union([t.String(), t.Null()], { - description: '用户头像URL', - examples: ['https://example.com/avatar.jpg', null] - }), - /** 账号状态 */ - status: t.String({ - description: '账号状态', - examples: ['active', 'inactive', 'pending'] - }), - /** 性别 */ - gender: t.Union([t.Number(), t.Null()], { - description: '性别:0-未知,1-男,2-女', - examples: [0, 1, 2, null] - }), - /** 生日 */ - birthday: t.Union([t.String(), t.Null()], { - description: '生日', - examples: ['1990-01-01', null] - }), - /** 个人简介 */ - bio: t.Union([t.String(), t.Null()], { - description: '个人简介', - examples: ['这是一段个人简介', null] - }), - /** 登录次数 */ - loginCount: t.Number({ - description: '登录次数', - examples: [0, 10, 100] - }), - /** 最后登录时间 */ - lastLoginAt: t.Union([t.String(), t.Null()], { - description: '最后登录时间', - examples: ['2024-12-19T10:30:00Z', null] - }), - /** 最后登录IP */ - lastLoginIp: t.Union([t.String(), t.Null()], { - description: '最后登录IP', - examples: ['192.168.1.1', null] - }), - /** 失败尝试次数 */ - failedAttempts: t.Number({ - description: '失败尝试次数', - examples: [0, 1, 5] - }), - /** 是否超级管理员 */ - isRoot: t.Boolean({ - description: '是否超级管理员', - examples: [true, false] - }), - /** 创建时间 */ - createdAt: t.String({ - description: '创建时间', - examples: ['2024-12-19T10:30:00Z'] - }), - /** 更新时间 */ - updatedAt: t.String({ - description: '更新时间', - examples: ['2024-12-19T10:30:00Z'] - }) -}); -/** - * 用户列表响应Schema - * @description 用户列表查询的响应数据结构 - */ -export const UserListResponseSchema = createPaginationResponseSchema(UserListItemSchema); -/** 当前用户信息响应类型 */ -export type CurrentUserResponse = Static; /** 用户列表查询参数类型 */ export type UserListQueryRequest = Static; -/** 用户列表项类型 */ -export type UserListItem = Static; - -/** 用户列表响应类型 */ -export type UserListResponse = Static; \ No newline at end of file diff --git a/src/modules/user/user.service.ts b/src/modules/user/user.service.ts index d5c0d3b..eec93dc 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/user/user.service.ts @@ -15,7 +15,8 @@ import { successResponse, errorResponse, BusinessError } from '@/utils/responseF import { calculatePagination, normalizePaginationParams } from '@/utils/pagination'; import type { GetCurrentUserSuccessType, GetUserListSuccessType } from './user.response'; -import type { UserListQueryRequest, UserListItem } from './user.schema'; +import type { UserListQueryRequest } from './user.schema'; +import type { UserListItem } from './user.response'; /** * 用户服务类