cursor-init/.cursor/rules/api-development-standard.md
expressgy a23d336ebd refactor: 重构项目架构并标准化开发规范
- 重构项目结构:controllers/services -> modules模块化组织

- 新增Drizzle ORM集成和数据库schema定义

- 添加完整的开发规范文档(.cursor/rules/)

- 重新组织插件结构为子目录方式

- 新增用户模块和示例代码

- 更新类型定义并移除试验性代码

- 添加API文档和JWT使用示例

关联任务计划文档
2025-06-30 01:25:17 +08:00

5.0 KiB
Raw Blame History

API 开发规范

文件结构

每个API模块必须包含以下文件

  • *.schema.ts - 请求参数和数据结构定义 + TypeScript类型导出
  • *.response.ts - 响应格式定义 + 响应类型导出
  • *.service.ts - 业务逻辑实现(使用类型注解)
  • *.controller.ts - 路由和控制器使用Schema验证
  • *.test.ts - 测试用例(类型安全的测试数据)

开发流程

  1. Schema 定义 - 使用 TypeBox 定义请求参数和数据结构导出TypeScript类型
  2. Response 定义 - 基于全局响应格式定义各种场景的响应,导出响应类型
  3. Service 实现 - 编写业务逻辑,使用类型注解确保类型安全
  4. Controller 实现 - 集成JWT认证、Schema验证、错误处理
  5. 测试编写 - 使用类型安全的测试数据,覆盖正常、异常、边界场景

必须遵循

1. 认证与授权

// 需要认证的接口必须使用 jwtAuthPlugin
export const controller = new Elysia().use(jwtAuthPlugin).get('/protected-route', handler, options);

2. 参数验证与类型使用

// example.schema.ts - 定义Schema和导出类型
import { t, type Static } from 'elysia';

export const GetUserByUsernameSchema = t.Object({
    username: t.String({ minLength: 2, maxLength: 50 }),
});

// 导出TypeScript类型
export type GetUserByUsernameParams = Static<typeof GetUserByUsernameSchema>;

// example.service.ts - Service中使用类型
import type { GetUserByUsernameParams } from './example.schema';

export class ExampleService {
    async getUserByUsername(params: GetUserByUsernameParams): Promise<UserInfo> {
        const { username } = params; // 类型安全
        // 业务逻辑...
    }
}

// example.controller.ts - Controller中使用Schema验证
export const controller = new Elysia().use(jwtAuthPlugin).get('/user/:username', handler, {
    params: GetUserByUsernameSchema, // 运行时验证
});

要求:

  • 每个Schema必须导出对应的TypeScript类型
  • Service方法必须使用类型注解
  • 禁止行内定义任何参数Schema

3. 统一响应格式

// 成功响应
return {
    code: ERROR_CODES.SUCCESS,
    message: '操作成功',
    data: result,
};

// 错误响应
return {
    code: ERROR_CODES.BUSINESS_ERROR,
    message: '具体错误信息',
    data: null,
};
  • 响应内容的类型需要在.response.ts中定义

4. 错误处理

try {
    const result = await service.method();
    return successResponse(result);
} catch (error) {
    Logger.error(new Error(`操作失败: ${error}`));

    const errorMessage = error instanceof Error ? error.message : '未知错误';
    if (errorMessage.includes('特定错误')) {
        set.status = 400;
        return errorResponse(ERROR_CODES.BUSINESS_ERROR, '业务错误消息');
    }

    set.status = 500;
    return errorResponse(ERROR_CODES.INTERNAL_ERROR, '服务器内部错误');
}

5. 文档配置

{
    detail: {
        summary: '接口简要描述',
        description: '接口详细描述',
        tags: [tags.moduleName],
        security: [{ bearerAuth: [] }], // 需要认证时添加
    },
    response: PredefinedResponses,
}

6. 日志记录

// 接口调用日志
Logger.info(`接口被调用,参数: ${param}, 用户: ${JSON.stringify(user)}`);

// 成功日志
Logger.info(`操作成功,结果: ${result.id}`);

// 错误日志
Logger.error(new Error(`操作失败,错误: ${error}`));

7. 必要的注释

  1. 接口名称注释
export const controller = new Elysia()
    .use(jwtAuthPlugin)
    /**
     * 根据用户名查询用户信息
     * @route GET /api/sample/user/:username
     * @description 通过用户名查询用户的详细信息需要JWT认证
     * @param username 用户名路径参数长度2-50字符
     * @returns 用户信息对象或错误响应
     * @modification hotok 2025-06-29 初始实现
     */
    .get('/protected-route', handler, options);

禁止事项

  • 直接在 Controller 中写业务逻辑
  • 不进行参数验证
  • 返回非标准格式的响应
  • 暴露敏感信息(如密码哈希)
  • 缺少错误处理和日志记录
  • 不编写测试用例

命名规范

  • 文件名:module.type.ts(如:user.controller.ts
  • SchemaGetUserByIdSchemaCreateUserSchema
  • ResponseGetUserSuccessResponseUserErrorResponse
  • Service 类:UserService、导出实例:userService
  • ControlleruserController

测试要求

每个接口必须包含:

  • 正常流程测试
  • 参数验证边界测试(最短、最长、无效格式)
  • 业务逻辑异常测试(不存在、权限不足等)
  • 认证相关测试无Token、无效Token、过期Token
  • 响应格式验证状态码、code、message、data结构