# API Contract Specification ## Purpose 定义所有接口的统一契约:响应封装、错误码、分页、鉴权层级与认证接口,作为前后端联调的唯一锚点。 ## Requirements ### Requirement: 统一响应封装 所有接口 SHALL 返回 `{ code, message, data }` 结构;code=0 表示成功。 #### Scenario: 成功响应 - **WHEN** 接口处理成功 - **THEN** 返回 code=0、message=ok、data 为业务数据 #### Scenario: 失败响应 - **WHEN** 接口处理失败 - **THEN** 返回非 0 code 与可读 message,data 为空 ### Requirement: 错误码分段 错误码 SHALL 采用「业务域 + 错误序」分段:5 位数字 `[域码 2 位][序 3 位]`,域码集中登记于 `error-codes` 注册表(00 通用、10 认证、11 用户、12 角色、13 资源、14 组织、15 租户、16 字典、17 配置、20 会话、21 通知…);HTTP 状态仍按语义返回(400/401/403/404/409/500),`code` 为业务码。 #### Scenario: 未登录 - **WHEN** 未携带有效 token 访问受保护接口 - **THEN** HTTP 401,code 为认证域业务码(如 10002),无业务码时回退 40100 #### Scenario: 业务错误码 - **WHEN** 业务处理失败(如用户名已存在) - **THEN** 返回对应 HTTP 状态 + 注册表内的业务码与可读 message #### Scenario: 系统错误兜底 - **WHEN** 非业务异常(框架/未知错误) - **THEN** code 回退为 HTTP 状态 ×100 ### Requirement: 分页约定 列表接口 SHALL 使用 page/pageSize 分页,返回 `{ list, total, page, pageSize }`。 #### Scenario: 分页查询 - **WHEN** 客户端传 page=1&pageSize=20 - **THEN** 返回 data 含 list、total、page、pageSize ### Requirement: 三级鉴权 接口 SHALL 分为三级:公开接口无需 token;未登记接口仅需登录;登记为「接口」资源的接口需 token 且当前用户具备对应 code(权限标识)。 #### Scenario: 未登记接口 - **WHEN** 已登录用户访问未登记为资源的接口 - **THEN** 校验通过(仅需登录态) #### Scenario: 登记接口越权 - **WHEN** 已登录用户访问登记为接口资源、但自身角色无该 permission 的接口 - **THEN** 系统返回 403 ### Requirement: 认证接口 系统 SHALL 提供登录、刷新令牌、登出接口;登录需 tenantCode+username+password,返回 access token、refresh token 与 mustChangePassword(待改密标志)。 #### Scenario: 登录成功 - **WHEN** 用户提供正确的租户、账号与密码 - **THEN** 返回 access token(2h 过期)、refresh token(7d 过期)与 mustChangePassword(待改密为 true,否则 false) #### Scenario: 刷新令牌 - **WHEN** access 过期后客户端携带 refresh token 请求刷新 - **THEN** 系统签发新 access 与 refresh,并作废旧 refresh ### Requirement: 核心域接口目录 系统 SHALL 为每个核心域提供 REST 资源接口(复数、kebab-case):/users、/roles、/resources、/orgs、/dict-types、/configs、/audit-logs、/login-logs、/sessions。 #### Scenario: 资源 CRUD - **WHEN** 管理端操作某核心域 - **THEN** 通过对应复数资源路径进行增删改查 ### Requirement: 字典聚合查询接口 系统 SHALL 提供 `GET /dicts` 接口,需登录态,按当前租户一次返回全部启用状态的字典类型及其启用状态的字典项(字典项按 sort 升序),供前端登录后缓存渲染。 #### Scenario: 一次性拉取 - **WHEN** 前端调用 GET /dicts - **THEN** 返回全部启用字典类型,每个类型含其启用字典项(label/value/color_type/sort) #### Scenario: 停用过滤 - **WHEN** 某字典类型或字典项被停用 - **THEN** GET /dicts 不返回该类型或该项 ### Requirement: 会话与账号安全接口 系统 SHALL 提供以下接口:`GET /sessions`(分页查询活跃会话)、`POST /sessions/:sid/kick`(单会话强退)、`POST /users/:id/kick-sessions`(踢用户全部会话)、`POST /users/:id/lock`(锁定用户,body 可含 reason)、`POST /users/:id/unlock`(解锁用户)。 #### Scenario: 查询活跃会话 - **WHEN** 客户端请求 GET /sessions - **THEN** 返回活跃会话分页数据(list/total/page/pageSize) #### Scenario: 单会话强退 - **WHEN** 客户端请求 POST /sessions/:sid/kick - **THEN** 该会话被强退 #### Scenario: 锁定与解锁 - **WHEN** 客户端请求 POST /users/:id/lock 或 /users/:id/unlock - **THEN** 用户被锁定或解锁 ### Requirement: 通知接口 系统 SHALL 提供:`GET /notices`(分页,管理)、`POST /notices`、`PATCH /notices/:id`、`DELETE /notices/:id`(管理,需 `notice:*` 权限);`GET /notices/mine`(当前用户可见通知 + 已读状态)、`GET /notices/unread-count`(未读数)、`POST /notices/:id/read`(标记已读)、`POST /notices/read-all`(全部已读),后四者仅需登录态。 #### Scenario: 拉取未读数 - **WHEN** 用户请求 GET /notices/unread-count - **THEN** 返回其未读通知数 #### Scenario: 标记已读 - **WHEN** 用户请求 POST /notices/:id/read - **THEN** 该通知对当前用户标记为已读 ### Requirement: 租户品牌接口 系统 SHALL 提供 `GET /auth/brand`(仅登录态),返回当前租户品牌(name、brandName、logoUrl、themeColor、copyright);tenant 的新增/编辑/列表接口 SHALL 支持品牌字段。 #### Scenario: 拉取当前租户品牌 - **WHEN** 已登录用户请求 GET /auth/brand - **THEN** 返回其所在租户的品牌字段(缺省为空) #### Scenario: 维护租户品牌 - **WHEN** 管理员新增或编辑租户并填写品牌字段 - **THEN** 品牌字段随 tenant 一并保存与返回