All checks were successful
构建并部署到测试环境(无 SSH) / build-and-deploy (push) Successful in 1m8s
12 KiB
12 KiB
权限矩阵与接口联调契约
一、管理端权限计算模型
管理页面和操作的最终可用性不是只看 user_type,而是四个条件的交集:
终端适用范围 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 登录
POST /api/auth/login
Content-Type: application/json
{
"username": "账号或手机号",
"password": "密码",
"device": "web"
}
管理 H5 将 device 改为 h5。
成功后保存:
{
"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": []
}
所有受保护请求携带:
Authorization: Bearer <access_token>
3.2 Token 刷新
POST /api/auth/refresh-token
{
"refresh_token": "..."
}
前端请求模块需要实现单次刷新队列:多个并发请求同时收到 401 时,只允许一个请求刷新 Token,其余等待结果;刷新失败统一清除会话并跳转登录。
3.3 C 端认证
C 端使用 /api/c/v1/auth/*,不使用管理端登录或刷新 Token:
verify-asset。wechat-login或miniapp-login。- 必要时
bind-phone。 - 后续请求携带 C 端 Bearer Token。
- 认证失效重新走资产验证和微信登录。
四、响应与错误契约
4.1 统一响应
{
"code": 0,
"msg": "success",
"data": {},
"timestamp": "2026-07-29T12:00:00+08:00"
}
前端成功判断必须同时尊重 HTTP 状态和业务 code,不能只判断 HTTP 200。
4.2 分页
常见请求参数为 page、page_size,常见返回为:
{
"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 管理端创建订单
- 解析或选择资产。
- 查询资产可用套餐。
POST /api/admin/orders/purchase-check。- 展示后端计算的购买条件、金额和支付方式。
POST /api/admin/orders。- 查询订单详情确认最终状态。
6.2 代理在线充值
GET /api/admin/agent-recharges/payment-methods。POST /api/admin/agent-recharges。- 按响应拉起支付。
- 轮询
GET /api/admin/agent-recharges/:id/payment-status。 - 查询详情和主钱包流水确认到账。
6.3 企业资产授权
- 查询企业详情。
- 查询候选卡或设备。
- 提交
allocate-cards或allocate-devices。 - 重新查询企业卡/设备列表。
- 查询授权记录确认结果。
设备授权由后端同步处理绑定卡,前端不得自行拆分请求。
6.4 文件上传和异步任务
POST /api/admin/storage/upload-url。- 直接向响应的对象存储 URL 上传。
- 将
file_key提交业务任务接口。 - 轮询任务详情。
- 完成后展示结果或下载文件。
6.5 第三方支付
- 创建业务订单或充值单。
- 使用后端返回的支付参数/链接拉起支付。
- 支付平台回调只由服务端接收,前端不得调用
/api/callback/*。 - 用户返回页面后重新查询业务单状态。
- 结果未知时保持“处理中”,不在前端直接改为成功。
七、管理 Web/H5 的路由和权限约定
- 登录后以
menus[].url建立可访问路由白名单。 - 路由守卫同时检查登录状态和菜单权限。
- 按钮组件检查
buttons,但按钮隐藏只负责体验,不是安全门禁。 - Web/H5 使用同一权限码,不创建
web_xxx、h5_xxx两套业务权限码;终端差异使用权限记录的platform字段。 - 不给当前终端返回的菜单不得通过手输 URL 进入。
- 页面加载收到 403 后应移除当前不可用操作并提示重新登录获取最新权限。
八、联调验收矩阵
每个管理接口至少使用以下账号验证:
| 用例 | 超管 | 有权限平台 | 无权限平台 | 有权限代理 | 越权代理 | 企业账号 |
|---|---|---|---|---|---|---|
| 菜单是否返回 | 验证 | 验证 | 不返回 | 验证 | 验证 | 验证 |
| 页面能否进入 | 验证 | 验证 | 禁止 | 验证 | 视权限 | 视权限 |
| API 能否调用 | 验证 | 验证 | 403 | 验证 | 403/空范围 | 仅目标能力 |
| 列表数据范围 | 全部 | 全部 | 无调用 | 本店及下级 | 不含平级/上级 | 仅本企业 |
| 详情越权 | 可见 | 可见 | 403 | 非范围资源 403 | 403 | 非本企业 403 |
| 写操作越权 | 可操作 | 按权限 | 403 | 仅允许范围 | 403 | 仅明确开放能力 |
C 端接口至少覆盖:本人绑定资产、本人未绑定资产、其他客户订单、Token 被新登录替换、资产解绑、店铺禁止新 C 端登录。
九、当前后端安全缺口
以下问题是上线门禁,不应交给前端规避:
/api/admin当前统一挂载认证中间件,但没有发现逐路由普遍挂载RequirePermission。- 当前
menus/buttons主要用于前端显示,不能证明每个接口已做相同权限校验。 - 部分接口明确说明按钮权限不在后端校验。
- 企业账号没有店铺下级范围,部分使用宽松店铺过滤的查询可能需要专项验证。
- 企业、企业授权、卡和设备列表需要使用真实企业 Token 做越权测试。
前端可以按权限正确隐藏界面,但生产上线前必须由后端补齐或验证:
- 功能权限门禁。
- 资源级权限校验。
- 企业数据范围。
- 写操作状态机和幂等。
- 敏感配置和高风险动作的身份限制。
十、联调权威来源
- 路由和 DTO:
internal/routes、internal/model/dto。 - 机器可读契约:
docs/admin-openapi.yaml。 - 业务和页面组合:
docs/前端建设。 - 七月迭代新增字段:
docs/7月迭代/七月迭代实现与接口对接说明.md。
发现接口与 OpenAPI 不一致时,先修正文档生成器和 DTO,不在前端编写长期兼容分支掩盖契约问题。