# 权限矩阵与接口联调契约 ## 一、管理端权限计算模型 管理页面和操作的最终可用性不是只看 `user_type`,而是四个条件的交集: ```text 终端适用范围 web/h5/all ∩ 账号所绑定角色的菜单和按钮权限 ∩ 账号身份的硬性业务限制 ∩ 当前组织的数据范围 ``` ### 1.1 终端 权限记录包含 `platform`: - `web`:只在管理 Web 登录时返回。 - `h5`:只在管理 H5 登录时返回。 - `all`:两端都返回。 ### 1.2 角色 - 超级管理员不分配角色。 - 平台账号使用平台角色,可绑定多个。 - 代理和企业账号使用客户角色,最多绑定一个。 - 登录返回 `menus`、`buttons` 和兼容字段 `permissions`。 因此,“代理可用什么”应由给该代理账号绑定的客户角色决定,而不是维护一套固定代理菜单。 ### 1.3 身份硬限制 角色权限不能突破组织身份的业务限制。例如: - 企业账号即使错误分配了店铺菜单,也不应管理店铺。 - 企业账号不应访问代理资金、佣金、提现和代理充值。 - 代理账号不能越过本店及下级店铺的数据范围。 - 代理账号不能管理平台直属企业。 - 只有超管或平台管理身份可以维护支付、企微和全局系统配置。 ### 1.4 数据范围 | 用户身份 | 组织字段 | 正常数据范围 | | --- | --- | --- | | 超级管理员 | 无 | 全平台 | | 平台账号 | 无 | 全平台,但功能仍受角色权限控制 | | 代理账号 | `shop_id` | 本店及全部下级店铺 | | 企业账号 | `enterprise_id` | 本企业有效授权资产和本企业业务记录 | | 个人客户 | `customer_id` | 本人绑定资产及本人订单、充值、通知 | ## 二、目标权限矩阵 下表是产品和安全目标,不代表当前每条后端接口都已完整实现。 图例: - `权限控制`:角色有相应菜单/按钮且数据范围满足时可用。 - `范围受限`:可用,但只能处理当前组织范围。 - `只读建议`:首期只开放查询。 - `禁止`:角色配置也不得突破。 | 业务模块 | 超管 | 平台账号 | 代理账号 | 企业账号 | 个人客户 | | --- | --- | --- | --- | --- | --- | | 账号管理 | 全部 | 权限控制 | 范围受限 | 禁止 | 不适用 | | 角色/权限配置 | 全部 | 权限控制 | 建议禁止 | 禁止 | 不适用 | | 店铺管理 | 全部 | 权限控制 | 本店及下级、权限控制 | 禁止 | 不适用 | | 企业管理 | 全部 | 权限控制 | 本店及下级企业、权限控制 | 只读本企业资料 | 不适用 | | 企业资产授权 | 全部 | 权限控制 | 本店及下级企业、权限控制 | 只读本企业授权 | 不适用 | | 卡/设备库存 | 全部 | 权限控制 | 本店及下级、权限控制 | 仅本企业授权资产 | 仅本人绑定资产 | | 资产分配 | 全部 | 权限控制 | 通常仅直属下级、权限控制 | 禁止 | 禁止 | | 资产状态与设备控制 | 全部 | 权限控制 | 范围受限、权限控制 | 本企业授权范围、权限控制 | 本人绑定范围 | | 运营商配置 | 全部 | 权限控制 | 禁止 | 禁止 | 不适用 | | 套餐系列/套餐设计 | 全部 | 权限控制 | 建议只读 | 禁止 | 只看可购投影 | | 套餐授权/分配/定价 | 全部 | 权限控制 | 直属下级、权限控制 | 禁止 | 禁止 | | 管理端订单 | 全部 | 权限控制 | 范围受限、权限控制 | 视业务角色决定 | 仅本人 C 端订单 | | 退款/换货 | 全部 | 权限控制 | 范围受限、权限控制 | 只读建议 | C 端只提交换货收货信息 | | 代理主钱包/佣金/提现 | 全部 | 权限控制 | 本店及下级、权限控制 | 禁止 | 不适用 | | 代理充值 | 全部 | 权限控制 | 本店在线充值、权限控制 | 禁止 | 不适用 | | 资产钱包 | 全部 | 权限控制 | 范围受限、权限控制 | 授权资产只读建议 | 本人绑定资产 | | 导入/批量处理 | 全部 | 权限控制 | 范围受限、权限控制 | 禁止 | 禁止 | | 导出 | 全部 | 权限控制 | 按数据范围导出 | 只导本企业且需明确开放 | 禁止 | | 微信/支付宝/企微配置 | 全部 | 权限控制 | 禁止 | 禁止 | 禁止 | | 轮询、告警、清理 | 全部 | 权限控制 | 只读建议 | 禁止 | 禁止 | | 通知 | 全部 | 当前账号 | 当前账号 | 当前账号 | 当前客户 | ## 三、统一认证契约 ### 3.1 管理 Web/H5 登录 ```http POST /api/auth/login Content-Type: application/json ``` ```json { "username": "账号或手机号", "password": "密码", "device": "web" } ``` 管理 H5 将 `device` 改为 `h5`。 成功后保存: ```json { "access_token": "...", "refresh_token": "...", "expires_in": 86400, "user": { "id": 1, "username": "admin", "user_type": 2, "user_type_name": "平台用户", "shop_id": 0, "enterprise_id": 0 }, "menus": [], "buttons": [], "permissions": [] } ``` 所有受保护请求携带: ```http Authorization: Bearer ``` ### 3.2 Token 刷新 ```http POST /api/auth/refresh-token ``` ```json { "refresh_token": "..." } ``` 前端请求模块需要实现单次刷新队列:多个并发请求同时收到 401 时,只允许一个请求刷新 Token,其余等待结果;刷新失败统一清除会话并跳转登录。 ### 3.3 C 端认证 C 端使用 `/api/c/v1/auth/*`,不使用管理端登录或刷新 Token: 1. `verify-asset`。 2. `wechat-login` 或 `miniapp-login`。 3. 必要时 `bind-phone`。 4. 后续请求携带 C 端 Bearer Token。 5. 认证失效重新走资产验证和微信登录。 ## 四、响应与错误契约 ### 4.1 统一响应 ```json { "code": 0, "msg": "success", "data": {}, "timestamp": "2026-07-29T12:00:00+08:00" } ``` 前端成功判断必须同时尊重 HTTP 状态和业务 `code`,不能只判断 HTTP 200。 ### 4.2 分页 常见请求参数为 `page`、`page_size`,常见返回为: ```json { "items": [], "total": 0, "page": 1, "size": 20 } ``` 不同历史模块的列表 DTO 可能有差异,类型生成和页面开发必须以对应 OpenAPI Operation 为准,不能强制假设所有列表结构完全相同。 ### 4.3 HTTP 错误处理 | HTTP 状态 | 前端行为 | | --- | --- | | 400 | 定位请求字段,展示后端安全中文提示 | | 401 | 管理端尝试刷新;C 端重新登录 | | 403 | 显示无权限,不重试,不猜测资源是否存在 | | 404 | 显示资源不存在或已失效 | | 409 | 提示状态已变化,重新拉取详情 | | 429 | 提示操作频繁,按 `Retry-After` 或短时退避 | | 5xx | 展示通用错误和 Request ID,允许用户手动重试 | ## 五、数据展示契约 - 金额:接口整数单位通常为分,展示时格式化为元,提交时转换回分;禁止浮点数直接参与计算。 - 状态:优先使用响应中的 `status_name`、`*_name`,不要在多个前端重复维护中文枚举。 - 时间:按 ISO 8601 解析,统一展示为当前业务时区;提交日期范围按接口约定处理起止边界。 - ID:前端将 ID 当作不透明标识,不参与金额或业务计算。 - 空值:区分 `null`、空字符串、0 和空数组,不能用统一假值替换。 - 资产标识:按字符串保存和上传,避免 ICCID 被转为科学计数法或丢失前导零。 - 支付方式:使用 `allowed_payment_methods` 或支付方式查询接口,不写死微信/支付宝/钱包组合。 - 状态流转:按钮是否出现先看权限和当前状态;提交后仍以后端状态机结果为准。 ## 六、关键接口调用顺序 ### 6.1 管理端创建订单 1. 解析或选择资产。 2. 查询资产可用套餐。 3. `POST /api/admin/orders/purchase-check`。 4. 展示后端计算的购买条件、金额和支付方式。 5. `POST /api/admin/orders`。 6. 查询订单详情确认最终状态。 ### 6.2 代理在线充值 1. `GET /api/admin/agent-recharges/payment-methods`。 2. `POST /api/admin/agent-recharges`。 3. 按响应拉起支付。 4. 轮询 `GET /api/admin/agent-recharges/:id/payment-status`。 5. 查询详情和主钱包流水确认到账。 ### 6.3 企业资产授权 1. 查询企业详情。 2. 查询候选卡或设备。 3. 提交 `allocate-cards` 或 `allocate-devices`。 4. 重新查询企业卡/设备列表。 5. 查询授权记录确认结果。 设备授权由后端同步处理绑定卡,前端不得自行拆分请求。 ### 6.4 文件上传和异步任务 1. `POST /api/admin/storage/upload-url`。 2. 直接向响应的对象存储 URL 上传。 3. 将 `file_key` 提交业务任务接口。 4. 轮询任务详情。 5. 完成后展示结果或下载文件。 ### 6.5 第三方支付 1. 创建业务订单或充值单。 2. 使用后端返回的支付参数/链接拉起支付。 3. 支付平台回调只由服务端接收,前端不得调用 `/api/callback/*`。 4. 用户返回页面后重新查询业务单状态。 5. 结果未知时保持“处理中”,不在前端直接改为成功。 ## 七、管理 Web/H5 的路由和权限约定 1. 登录后以 `menus[].url` 建立可访问路由白名单。 2. 路由守卫同时检查登录状态和菜单权限。 3. 按钮组件检查 `buttons`,但按钮隐藏只负责体验,不是安全门禁。 4. Web/H5 使用同一权限码,不创建 `web_xxx`、`h5_xxx` 两套业务权限码;终端差异使用权限记录的 `platform` 字段。 5. 不给当前终端返回的菜单不得通过手输 URL 进入。 6. 页面加载收到 403 后应移除当前不可用操作并提示重新登录获取最新权限。 ## 八、联调验收矩阵 每个管理接口至少使用以下账号验证: | 用例 | 超管 | 有权限平台 | 无权限平台 | 有权限代理 | 越权代理 | 企业账号 | | --- | --- | --- | --- | --- | --- | --- | | 菜单是否返回 | 验证 | 验证 | 不返回 | 验证 | 验证 | 验证 | | 页面能否进入 | 验证 | 验证 | 禁止 | 验证 | 视权限 | 视权限 | | API 能否调用 | 验证 | 验证 | 403 | 验证 | 403/空范围 | 仅目标能力 | | 列表数据范围 | 全部 | 全部 | 无调用 | 本店及下级 | 不含平级/上级 | 仅本企业 | | 详情越权 | 可见 | 可见 | 403 | 非范围资源 403 | 403 | 非本企业 403 | | 写操作越权 | 可操作 | 按权限 | 403 | 仅允许范围 | 403 | 仅明确开放能力 | C 端接口至少覆盖:本人绑定资产、本人未绑定资产、其他客户订单、Token 被新登录替换、资产解绑、店铺禁止新 C 端登录。 ## 九、当前后端安全缺口 以下问题是上线门禁,不应交给前端规避: 1. `/api/admin` 当前统一挂载认证中间件,但没有发现逐路由普遍挂载 `RequirePermission`。 2. 当前 `menus/buttons` 主要用于前端显示,不能证明每个接口已做相同权限校验。 3. 部分接口明确说明按钮权限不在后端校验。 4. 企业账号没有店铺下级范围,部分使用宽松店铺过滤的查询可能需要专项验证。 5. 企业、企业授权、卡和设备列表需要使用真实企业 Token 做越权测试。 前端可以按权限正确隐藏界面,但生产上线前必须由后端补齐或验证: - 功能权限门禁。 - 资源级权限校验。 - 企业数据范围。 - 写操作状态机和幂等。 - 敏感配置和高风险动作的身份限制。 ## 十、联调权威来源 1. 路由和 DTO:`internal/routes`、`internal/model/dto`。 2. 机器可读契约:`docs/admin-openapi.yaml`。 3. 业务和页面组合:`docs/前端建设`。 4. 七月迭代新增字段:`docs/7月迭代/七月迭代实现与接口对接说明.md`。 发现接口与 OpenAPI 不一致时,先修正文档生成器和 DTO,不在前端编写长期兼容分支掩盖契约问题。