front/docs/rbac系统设计-050901.md
2026-06-04 19:15:28 +08:00

107 KiB
Raw Blame History

RBAC 与统一认证 — 规范说明

导读

这份文档解决什么问题?

用直白话说,系统要搞清楚四件事:

  1. 谁能登录(内部员工、客户主账号、客户子账号)——账号与凭证放在哪张表、怎么区分类型。
  2. 登录之后凭什么进业务——访问令牌、刷新令牌,以及改权限、改密码后旧令牌如何作废。
  3. 能操作哪些功能——有效权限码来自 t_principal_role 的角色并集;客户子账号还可由主账号在 t_customer_sub_permission 配可选白名单,与角色并集求交再收窄(无任何白名单行时等于仅用角色)。用意是:同一角色模板下仍可把某个子账号缩到更少能力,而不必为每人微调就新建一堆细角色;菜单由同一套有效权限码推导,并非与白名单并列的另一套「来源」。菜单只负责界面展示,不能代替接口鉴权。
  4. 出了问题怎么追溯——RBAC 关键动作与普通业务增删改,统一落在 t_operation_log(支持字段级、JSON 快照或 RBAC 动作行)。

更细的表字段、接口示例见后文各节。

正文层级怎么读?

编号用 点分十进制,层级越深小数位越多(与 Markdown 标题 ## → ### → #### → ##### 对应):

层级 示例 说明
章 1、2、… 5 五大块(表结构、令牌、接口清单、详细设计、客户域)
节 1.1、2.1、4.2 章内分节(###)
条 1.1.1、3.1.3、4.3.2 更细条目或接口分组(####)
款 4.2.1.1、4.2.1.2 单个接口下的「请求示例」「业务逻辑」(#####)
目 4.2.1.2.1、4.2.1.2.2 「核心流程」「校验与异常」等(######)

五章一览:1 表结构 · 2 令牌与鉴权 · 3 接口路径速查 · 4 接口详细设计 · 5 客户域接口详细设计(5.1 OMS-Customer 资料 6 接口 · 5.2 主/子账号 RBAC 目标 8 接口,结构与 4 相同)。

建议怎么读?

你是谁 建议路径
后端 / DBA 1.1 概念与 1.1.6 落地自检 → 1.2 起各表;需要时看 4
前端 / BFF 2 → 3 → 4(管理端 RBAC);客户资料与 5.1、客户 RBAC 目标 5.2;参数与异常看对应章节
产品 / 测试 1.1 前几段 + 2.2 客户子账号表 + 5(5.1 客户资料 OpenAPI + 5.2 RBAC 设计项)

第一次通读:1(概念 + 流程图 + 1.1.6)→ 2 → 再按需打开 3~5。

1 表结构(按链路自上而下)

本章导读:先读 1.1「概念模型」四件事和流程图,建立「登录账号 → 主体 → 角色 → 权限码」;再按需翻到 1.2 起各数据表。三张枢纽表:t_auth_account、t_principal_role、role_permission。

1.1 RBAC 概念模型与设计原则(主体·组织·角色·权限·菜单)

本段回答四件事:谁在登录、凭什么进系统、能做什么(权限从哪来)、菜单与 API 的分工。以下与 2、2.2 一致。

1.1.1 设计目标

  • 统一身份:所有可登录主体(内部用户、客户主账号、客户子账号)各对应 t_auth_account 一行(PrincipalType + PrincipalId 唯一),登录凭证(手机、邮箱、口令、微信等)只落在认证表,不散落在业务主体表。
  • 统一授权:主体与角色的绑定只通过 t_principal_role;角色拥有的权限码只通过 role_permission;权限码的合法集合由 t_permission_definition 定义。API 与后台任务不得以未注册的字符串当作权限码。
  • 可吊销、可版本化:授权变更、改密、全员登出等通过 PermissionRevision / SessionVersion 与令牌中的 permission_rev / session_ver 对齐(详见 2),避免「改权限后旧令牌长期有效」。
  • 可审计:RBAC 关键动作与任意业务对数据的增删改均落 t_operation_log(见 1.13):业务侧可选 字段级、快照(OldSnapshotJson/NewSnapshotJson,前端 diff);RBAC 动作用 Action 为动作码 + DetailJson / TargetPrincipal* / RoleId 等,与订单/客户等共用同一追溯模型。

1.1.2 三层分工(身份 / 授权 / 展示)

层次 回答的问题 核心对象
身份(Authentication) 你是谁?凭证是否有效? t_auth_account → 映射到业务主体
授权(Authorization) 你被允许执行哪些 PermissionCode? t_principal_role → role → role_permission → ⊆ t_permission_definition
展示(UI,非鉴权替代) 控制台显示哪些菜单项? menu(每节点绑定一个 PermissionCode,用于裁剪,不能替代 API 侧校验)

1.1.3 有效权限码集合 P 怎么算

记某主体通过 t_principal_role 绑定的所有角色,其 role_permission 中权限码的并集为 (P_{\text{role}})(均在 t_permission_definition 中有定义且未删除)。

  • PrincipalType = user 或 customer(主账号)
    (P = P_{\text{role}})。

  • PrincipalType = customer_sub(子账号)
    先算 (P_{\text{role}})。若 t_customer_sub_permission 对该子账号存在未删除白名单行,则 (P = P_{\text{role}} \cap \text{白名单码集});若无白名单行,则 (P = P_{\text{role}})。
    风控约束:子账号权限必须不超过其主客户主账号的权限集合((P \subseteq P_{\text{主}})),保存时事务内强校验;细节与接口见 2.2、5。

1.1.4 组织、字典、客户域在 RBAC 中的位置

  • 组织 organization:仅与内部用户 user.OrganizationId 关联,用于人事与行级数据范围等业务查询;不是权限码,也不替代 PermissionCode。客户主账号、子账号不挂组织树。
  • 字典 t_dict:存放岗位、学历、客户类型、结算方式等可配置枚举(亦可供其他模块扩展 DictType,如 notice_type、warehouse_type);业务表存 DictCode,按 DictType + DictCode 联查 DictName,须 IsDeleted=0;列表可按 DictSort 排序(见 1.14)。
  • 令牌 org_id:对客户类主体通常为空;内部用户可按实现携带,用于 BFF 做数据范围过滤(与权限码校验正交)。

1.1.5 分层职责一览表

层级 对象 职责
认证 t_auth_account 手机 + 邮箱注册必填;口令;微信(可选);PermissionRevision/SessionVersion;PrincipalType + PrincipalId → 业务主体
主体 user / t_customer / t_customer_sub_account 业务专有资料;不存口令/手机/邮箱、微信头像/微信昵称(见 t_auth_account);user 另存员工档案(岗位/学历等为 t_dict 编码等);t_customer 另存工商+执照影像、客户类型/结算方式(字典)、应收财务等;user.Avatar 仅为本地上传等非微信展示;不直接存权限集合
组织 organization 仅 user.OrganizationId;数据范围/人事,不是权限码
权限目录 t_permission_definition 权限码权威库;API / menu / role_permission 的 Code 须存在且未删
授权 t_principal_role → role → role_permission PermissionCode 鉴权,码集 ⊆ 权限目录
菜单 menu 按 PermissionCode 裁剪前端路由/菜单,不替代 API 校验
审计与操作日志 t_operation_log 全系统统一表:RBAC 动作(一行一动作,Action 为动作码)与 业务数据 ADD/UPDATE/DELETE(字段级或 快照);业务与定时任务、登录主体均可写入

关联小结:主体 → 角色 只经 t_principal_role;客户子账号可再经 t_customer_sub_permission 做交集收紧。下图与上表一致,从登录身份走到「权限码集合 (P)」再分流到 API 与菜单。

flowchart LR
  A[t_auth_account] --> B["主体 user / customer / customer_sub"]
  B --> C[t_principal_role]
  C --> D[role]
  D --> E[role_permission]
  E --> F["有效权限码集合 P"]
  E -.-> I[t_permission_definition]
  W[t_customer_sub_permission] -.->|"子账号:与白名单求交(可选)"| F
  F --> G["API:校验 PermissionCode"]
  F --> H["menu:按 PermissionCode 过滤"]
  H -.-> I

1.1.6 落地自检(实现/联调)

  • 任意受保护 API 的 PermissionCode 必须 ∈ t_permission_definition 且未删除;menu.PermissionCode 列长度与定义表一致(1.11)。
  • 写入 t_principal_role 或子账号 t_customer_sub_permission 成功后,对应主体 PermissionRevision+1(与 2 令牌策略一致)。
  • 分配角色前校验 role.AllowedPrincipalTypes 与主体类型匹配。
  • 子账号保存角色/白名单时 (P_{\text{子}}\subseteq P_{\text{主}}) 强校验;违反则整单 400(见 2.2)。
  • 关键写操作(含 RBAC 与业务数据)写 t_operation_log;改密、吊销会话等 SessionVersion 规则见 2。
  • 业务数据 ADD/UPDATE/DELETE 按 t_operation_log 规则落库(字段级或快照二选一为主、批量不合并、系统操作 OpPrincipalType=system 等,见 1.13)。

1.2 统一认证账号表 t_auth_account

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
Phone VARCHAR 16 - 是 注册必填;全局唯一
Email VARCHAR 64 - 是 注册必填;全局唯一
PasswordHash VARCHAR 256 - 否 密码哈希;可与微信并存;纯微信快捷登录且无本地口令时可空(须已完成手机+邮箱注册并绑定微信)
PasswordSalt VARCHAR 128 - 否 密码盐值;无本地口令时可为空
WechatOpenid VARCHAR 64 - 否 可选;便捷登录,非开户凭证;绑定前须已有手机+邮箱注册行
WechatUnionid VARCHAR 64 - 否 微信 UnionID(跨应用识别,可选)
WechatAvatar VARCHAR 255 - 否 微信头像 URL(OAuth/授权回调同步;各主体认证行均可有值)
WechatNickname VARCHAR 64 - 否 微信昵称(展示;与 OpenID 同源更新)
PrincipalType VARCHAR 32 - 是 主体类型:user(内部用户)/ customer(客户主账号)/ customer_sub(客户子账号)
PrincipalId BIGINT - - 是 主体业务主键:分别关联 user.Id、t_customer.Id、t_customer_sub_account.Id(均为 BIGINT,非 GUID)
Status TINYINT - - 是 状态:0-禁用,1-启用
LastLoginTime DATETIME - - 否 最后登录时间
LastLoginIp VARCHAR 64 - 否 最后登录 IP
PermissionRevision INT - - 是 该主体 t_principal_role 或(customer_sub)t_customer_sub_permission 白名单变更时 +1;access token permission_rev 与库一致(见 2)
SessionVersion INT - - 是 改密、全部登出、冻结等 +1;refresh 内 session_ver 对齐
IsDeleted TINYINT - - 是 是否删除:0-否,1-是,默认0
CreateUserId INT - - 是 创建人
CreateTime DATETIME - - 是 创建时间
UpdateUserId INT - - 否 更新人
UpdateTime DATETIME - - 否 更新时间

注册:Phone、Email 均必填、全局唯一。微信 OpenID 可选,仅快捷登录/展示,不可替代手机+邮箱开户。WechatAvatar/WechatNickname 同源;业务表不重复存手机/邮箱。IsDeleted=0 时 PrincipalType+PrincipalId 唯一(每主体一行认证账号)。先扫码绑微信再开户 / 先开户再补绑 见 2.3。
Id 与 PrincipalId:本表 Id 为认证行主键;对外主体标识(登录响应 principalId、令牌 claims)使用 PrincipalId(业务主体主键),二者勿混用;请求参数约定见 4.1.1。

1.3 用户表 user

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
Name VARCHAR 32 - 是 用户姓名(展示)
Avatar VARCHAR 128 - 否 本地上传等非微信头像 URL;微信头像见 t_auth_account.WechatAvatar
PositionCode VARCHAR 64 - 否 岗位:存 t_dict.DictCode,DictType=user_position;联查 DictName(IsDeleted=0);下拉可按 DictSort
Age TINYINT - - 否 年龄(周岁);可与 Birthday 冗余或仅维护其一
IdCardNo VARCHAR 18 - 否 身份证号;敏感须脱敏;建议 IsDeleted=0 时全局唯一
Birthday DATE - - 否 生日
EducationCode VARCHAR 64 - 否 学历:存 t_dict.DictCode,DictType=user_education;联查 DictName(IsDeleted=0);下拉可按 DictSort
Address VARCHAR 255 - 否 住址
GraduateSchool VARCHAR 128 - 否 毕业学校
EmergencyContact VARCHAR 64 - 否 紧急联系人姓名
EmergencyContactPhone VARCHAR 32 - 否 紧急联系电话
HireDate DATE - - 否 入职时间
LeaveDate DATE - - 否 离职时间;在职为空
WarehouseId BIGINT - 外键(t_warehouse_address.Id) 否 归属/作业仓库
ConcurrencyStamp INT - - 是 乐观并发版本号(从 0 递增)
CreateUserId INT - - 是 创建人
CreateTime DATETIME(6) - - 是 创建时间
UpdateUserId INT - - 是 更新人
UpdateTime DATETIME(6) - - 是 更新时间
IsDeleted TINYINT(1) - - 是 是否删除:0-否,1-是,默认0
OrganizationId BIGINT - - 否 所属组织ID

手机、邮箱、微信头像/昵称见 t_auth_account(PrincipalType=user,PrincipalId=本表.Id)。员工人事类字段(证件、入离职、仓库等)仅存本表;岗位/学历存 t_dict.DictCode(user_position、user_education),联查与校验见 1.14。档案字段的读写:管理端 POST /rbac/user/create、PUT /rbac/user/update、GET /rbac/user/get、列表见 4.6(与下表字段一一对应;注册场景不必带档案,见导读与 4.6 说明)。

1.4 客户表 t_customer

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增唯一标识
CustomerCode VARCHAR 32 - 是 客户编号,规则:前缀w+年份+序号(如W2501)
CustomerName VARCHAR 128 - 是 客户名称(平台主展示;可与 CompanyName 并存)
CompanyName VARCHAR 256 - 否 公司名称(工商全称)
BusinessLicenseNo VARCHAR 32 - 否 营业执照号码;敏感须脱敏
BusinessLicenseImageUrl VARCHAR 512 - 否 执照扫描件/照片 URL
LegalRepresentative VARCHAR 64 - 否 法定代表人
Website VARCHAR 255 - 否 官网 URL
SettlementMethodCode VARCHAR 64 - 否 结算方式:存 t_dict.DictCode,DictType=customer_settlement_method;联查 DictName(IsDeleted=0);下拉可按 DictSort
CustomerTypeCode VARCHAR 64 - 否 客户类型:存 t_dict.DictCode,DictType=customer_type;联查 DictName(IsDeleted=0);下拉可按 DictSort
AppId VARCHAR 64 - 否 应用 Id(开放平台/子应用/对接侧约定的字符串标识)
ReceivableFinanceUserId BIGINT - 外键(user.Id) 否 所属应收财务(内部用户)
Balance DECIMAL 18,2 - 是 账户余额,默认0
TotalQuota DECIMAL 18,2 - 是 总额度,默认0
UsedQuota DECIMAL 18,2 - 是 已用额度,默认0
Status TINYINT - - 是 状态:0-禁用,1-正常,2-冻结
Remark VARCHAR 512 - 否 备注信息
CreateUserId INT - - 是 创建人(系统/管理员账号)
CreateTime DATETIME - - 是 创建时间,默认当前时间
UpdateUserId INT - - 否 更新人
UpdateTime DATETIME - - 否 更新时间
Salesman VARCHAR 32 - 否 所属销售员账号
CustomerService VARCHAR 32 - 否 所属客服账号
CustomerGroup TINYINT - - 否 客户分组:0-普通客户,1-VIP1,2-VIP2,3-VIP3,4-VIP4,5-VIP5
IsDeleted TINYINT - - 是 是否删除:0-否,1-是,默认0

CustomerGroup 枚举:0-普通客户、1-VIP1、2-VIP2、3-VIP3、4-VIP4、5-VIP5

权限 t_principal_role;手机/邮箱与微信/最后登录见 t_auth_account(微信非注册必须)。客户类型、结算方式存 t_dict.DictCode(customer_type、customer_settlement_method),联查 DictName 且 IsDeleted=0;应收财务指向 user。管理端客户资料与重置密码:OpenAPI OMS-Customer 见 5.1(请求/响应示例);主/子账号 RBAC 目标接口见 5.2。

1.5 子账号表 t_customer_sub_account

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增唯一标识
MainCustomerId BIGINT - 外键(t_customer.Id) 是 主客户ID
SubAccountCode VARCHAR 32 - 是 子账号编号,规则:主客户编号+"_"+序号
SubStatus TINYINT - - 是 子账号状态:0-禁用,1-正常
CreateUserId INT - - 是 创建人(主客户/管理员)
CreateTime DATETIME - - 是 创建时间
UpdateUserId INT - - 否 更新人
UpdateTime DATETIME - - 否 更新时间
IsDeleted TINYINT - - 是 是否删除:0-否,1-是

独立登录:t_auth_account(customer_sub + 本表 Id);Phone/Email 注册必填;微信/口令约定同 2。角色见 t_principal_role。

1.6 组织表 organization

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
ParentId BIGINT - - 否 父级组织ID
Name VARCHAR 128 - 是 组织名称
Code VARCHAR 64 - 是 组织编码(唯一)
Type VARCHAR 32 - 是 组织类型
LeaderId BIGINT - - 否 负责人用户ID
Order INT - - 是 排序
Description VARCHAR 512 - 否 描述
IsEnabled TINYINT(1) - - 是 是否启用:0-否,1-是
ConcurrencyStamp INT - - 是 乐观并发版本号(从 0 递增)
CreateUserId INT - - 是 创建人
CreateTime DATETIME(6) - - 是 创建时间
UpdateUserId INT - - 是 更新人
UpdateTime DATETIME(6) - - 是 更新时间
IsDeleted TINYINT(1) - - 是 是否删除:0-否,1-是,默认0

行级数据范围由业务查询叠加 OrganizationId / org_id,非 RBAC 权限码。

1.7 角色表 role

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
Name VARCHAR 32 - 是 角色名称
Code VARCHAR 64 - 是 角色编码(唯一);与客户 /customer/* 入参 role 字符串同一套编码,长度须一致
AllowedPrincipalTypes VARCHAR 128 - 是 逗号分隔 user/customer/customer_sub;写 t_principal_role 前校验
ConcurrencyStamp INT - - 是 乐观并发版本号(从 0 递增)
CreateUserId INT - - 是 创建人
CreateTime DATETIME(6) - - 是 创建时间
UpdateUserId INT - - 是 更新人
UpdateTime DATETIME(6) - - 是 更新时间
IsDeleted TINYINT(1) - - 是 是否删除:0-否,1-是,默认0

1.8 角色权限关联表 role_permission

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
RoleId BIGINT - 外键(关联 role.Id) 是 角色ID
PermissionCode VARCHAR 255 - 是 须 ∈ t_permission_definition.Code(未删)
CreateUserId INT - - 是 创建人
CreateTime DATETIME(6) - - 是 创建时间

变更 role_permission 不逐行刷各主体 PermissionRevision 时,须 短 access TTL(建议 ≤15 分钟) 或缓存失效(见 2)。

1.9 权限定义表 t_permission_definition

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
Code VARCHAR 255 - 是 权限编码(全局唯一)
DisplayName VARCHAR 128 - 是 展示名
Module VARCHAR 64 - 否 分组,供权限树
SortOrder INT - - 是 排序,默认 0
IsDeleted TINYINT - - 是 是否删除:0-否,1-是,默认0
CreateUserId INT - - 是 创建人
CreateTime DATETIME - - 是 创建时间
UpdateUserId INT - - 否 更新人
UpdateTime DATETIME - - 否 更新时间

权威库;get-all-permissions / get-permission-tree 基于本表。

1.9.1 权限树与表字段(实现约定)

t_permission_definition 无 ParentId 列 时,GET /rbac/permission/get-permission-tree(4.3.2)的 children 由实现生成:常见做法为按 Module + SortOrder 聚类,或维护 独立树配置/物化路径,须在 OpenAPI 与实现注释中写清来源;不得以未在 t_permission_definition 注册的节点 作为鉴权依据。

1.10 主体角色关联表 t_principal_role

与 t_auth_account 主体维度一致;主体与角色的绑定只维护本表。扩展新主体类型时仅增加 PrincipalType 取值,并在 role.AllowedPrincipalTypes、相关接口与鉴权中一并约定。

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
PrincipalType VARCHAR 32 - 是 主体类型,与 t_auth_account 一致:user(内部用户)/ customer(客户主账号)/ customer_sub(客户子账号);预留扩展
PrincipalId BIGINT - - 是 主体业务主键:对应 user.Id、t_customer.Id、t_customer_sub_account.Id 等(由 PrincipalType 解释,数据库层一般不建跨表 FK)
RoleId BIGINT - 外键(关联 role.Id) 是 角色 ID
IsDeleted TINYINT - - 是 是否删除:0-否,1-是,默认0
CreateUserId INT - - 是 创建人
CreateTime DATETIME - - 是 创建时间
UpdateUserId INT - - 否 更新人
UpdateTime DATETIME - - 否 更新时间

IsDeleted=0 时 PrincipalType+PrincipalId+RoleId 唯一;/rbac/user/assign-roles 等即维护本表对应行。

1.11 菜单表 menu

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
PId BIGINT - - 是 父级菜单ID(根节点为 0)
Name VARCHAR 64 - 是 菜单名称
PermissionCode VARCHAR 255 - 是 须与 t_permission_definition.Code 等长语义一致(建议 255);须 ∈ 权限定义且未删;IsDeleted=0 时全局唯一
Type INT - - 是 菜单类型
Icon VARCHAR 64 - 否 图标
Route VARCHAR 64 - 否 路由地址
Component VARCHAR 64 - 否 组件路径
Order INT - - 是 排序
IsHidden TINYINT(1) - - 是 是否隐藏:0-否,1-是
IsCached TINYINT(1) - - 是 是否缓存:0-否,1-是
Description VARCHAR 256 - 否 描述
ConcurrencyStamp INT - - 是 乐观并发版本号(从 0 递增)
CreateUserId INT - - 是 创建人
CreateTime DATETIME(6) - - 是 创建时间
UpdateUserId INT - - 是 更新人
UpdateTime DATETIME(6) - - 是 更新时间
IsDeleted TINYINT(1) - - 是 是否删除:0-否,1-是,默认0

menu.PermissionCode 须在权限定义表中注册;长度与 t_permission_definition.Code 对齐,避免出现「权限码已注册但菜单列截断」。

1.12 子账号权限白名单表 t_customer_sub_permission

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增唯一标识
SubAccountId BIGINT - 外键(t_customer_sub_account.Id) 是 子账号 ID
PermissionCode VARCHAR 255 - 是 须 ∈ t_permission_definition.Code(与 role_permission 同源)
IsDeleted TINYINT - - 是 是否删除:0-否,1-是,默认0
CreateUserId INT - - 是 创建人
CreateTime DATETIME - - 是 创建时间

有未删行:最终权限 = 子账号 t_principal_role 角色并集 ∩ 白名单;无白名单行:= 角色并集。恒满足 ⊆ 主账号 (P_{\text{主}})(见 2.2)。变更后子账号认证行 PermissionRevision+1。

1.13 统一审计与操作日志表 t_operation_log(RBAC 动作 + 业务:字段级 / 快照)

定位:RBAC 动作与业务数据变更统一写入本表;不另建与 t_operation_log 职责重叠的 RBAC 专用审计表。按行类型使用不同列(勿在同一业务动作里混用字段级与快照以免重复):

  • RBAC 动作行:一次权限/会话类操作 1 行,Action 为 动作码(非 ADD/UPDATE/DELETE),DetailJson / TargetPrincipal* / RoleId 等承载语义;FieldName、逐字段列与快照列通常为 NULL。
  • 业务·字段级:FieldName + OldValue / NewValue,便于 SQL 按字段过滤、合规导出「一行一字段」。
  • 业务·快照:OldSnapshotJson / NewSnapshotJson 存整条(或约定范围内的)对象前后态,后端不写 diff;前端深度对比展示差异。
    若既要「前端好展示」又要「库内可按字段检索」,可采用 快照为主 + 异步任务根据快照生成字段级行(物化),或仅保留快照、检索走搜索引擎/数仓。
字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增主键
OpPrincipalType VARCHAR 32 - 是 操作者主体类型:user / customer / customer_sub / system(定时任务等非登录主体)
OpPrincipalId BIGINT - - 否 操作者业务主键;system 时 NULL
OpDisplayName VARCHAR 64 - 是 操作者展示名快照(防删除后无法展示);system 时固定 系统自动
OpTime DATETIME(6) - - 是 记录时间(服务端写入时刻)
ModuleCode VARCHAR 64 - 否 模块编码(如 rbac、order、customer),便于筛选
TargetType VARCHAR 64 - 否 业务行:被操作对象类型(全库统一编码)。RBAC 动作行可 NULL 或填 Principal / Role 等(实现约定)
TargetId VARCHAR 64 - 否 业务行:对象主键字符串化。RBAC 动作行可 NULL(若已用 TargetPrincipal* 表达)
TargetIdSecondary VARCHAR 64 - 否 可选第二键(子行、明细序号等)
TargetPrincipalType VARCHAR 32 - 否 RBAC:受影响主体类型(如 user、customer_sub)
TargetPrincipalId BIGINT - - 否 RBAC:受影响主体 Id
RoleId BIGINT - - 否 RBAC:相关角色 Id
Action VARCHAR 64 - 是 业务:ADD / UPDATE / DELETE(大写;批量仍不合并行)。RBAC:动作码,如 principal_role_assign、role_permission_assign、sub_whitelist_assign、auth_session_revoke、auth_password_change
DetailJson JSON - - 否 RBAC:请求/变更摘要。业务行可 NULL 或补充元数据
FieldName VARCHAR 128 - 否 业务·字段级必填(列名或路径);快照 / RBAC 行 NULL
OldValue TEXT - - 否 业务·字段级按 Action 规则;其余可 NULL
NewValue TEXT - - 否 业务·字段级按 Action 规则;其余可 NULL
OldSnapshotJson JSON - - 否 业务·快照按 Action 规则;RBAC 通常 NULL
NewSnapshotJson JSON - - 否 业务·快照按 Action 规则;RBAC 通常 NULL
BatchId VARCHAR 64 - 否 同一批次关联号;仅关联,不合并日志行
RequestId VARCHAR 64 - 否 请求链路 ID
ClientIp VARCHAR 64 - 否 客户端 IP
Remark VARCHAR 512 - 否 补充说明;system 操作时建议必填触发原因(如「定时任务更新价格」)

1.13.1 记录形态约定

形态 每行含义 必填字段 典型场景
RBAC 动作 一次管理动作 1 行 Action(动作码)、OpPrincipalType/OpDisplayName/OpTime;ModuleCode 建议 rbac;TargetPrincipal* / RoleId / DetailJson 按动作填充 分配角色、改角色权限、子账号白名单、吊销会话、改密等
业务·字段级 同一 Target + Action + OpTime(及 BatchId)下,每个变化字段 1 行 TargetType、TargetId、Action∈{ADD,UPDATE,DELETE}、FieldName、OldValue/NewValue(按规则);快照与 RBAC 专用列 NULL 报表/审计 SQL 直接查「谁改了金额」
业务·快照 同一 Target + Action + OpTime(及 BatchId)下,每次对象变更 1 行 TargetType、TargetId、Action、OldSnapshotJson/NewSnapshotJson(按规则) 前端 diff、嵌套结构

快照模式注意:UPDATE 建议在事务内基于库中完整旧行与提交后完整新行各序列化一份 JSON,避免「只提交部分字段」导致快照缺键、前端 diff 误判为大量删除。JSON 键顺序不影响语义,但 diff 库可能对顺序敏感——前端对比前可做 key 排序归一化(实现约定)。

1.13.2 写入规则(必须遵守)

A. 业务·字段级模式

  1. 新增(ADD)

    • 对每个需要留痕的字段写 1 条记录(若配置为「仅关键字段」,则只对配置集内字段写)。
    • OldValue = NULL,NewValue = 录入值(字符串化存储;JSON 可整段序列化)。
  2. 修改(UPDATE)

    • 仅当字段值相对库中旧值发生变化才写日志;无变化不写。
    • 每个变化字段 1 条记录:OldValue = 修改前,NewValue = 修改后。
  3. 删除(DELETE)

    • OldValue = 删除前的值,NewValue = NULL。
    • 可按策略记录全部字段或关键字段(与 ADD 一致,由模块配置)。
  4. 批量操作

    • 逐条、逐字段写入,禁止把多条数据合并成一条摘要;保证 每条数据、每个字段变更 均有 独立日志行(可用相同 BatchId 关联同一事务/任务)。

B. 业务·快照模式

  1. 新增(ADD)

    • OldSnapshotJson = NULL,NewSnapshotJson = 录入后对象(建议含主键与业务键,便于前端展示)。
    • 每个新增对象 1 行(批量则多行,同一 BatchId)。
  2. 修改(UPDATE)

    • 若整体与库中相比无任何字段变化,可不写日志。
    • 每次更新 1 行:OldSnapshotJson = 变更前完整对象,NewSnapshotJson = 变更后完整对象。
  3. 删除(DELETE)

    • 每个删除对象 1 行:OldSnapshotJson = 删除前完整对象,NewSnapshotJson = NULL。
  4. 批量操作

    • 逐对象写入,不合并;每条变更独立一行,可用相同 BatchId。

C. RBAC 动作行

  1. 一次 API 或后台动作 1 行(勿与业务字段级/快照混在同一语义行内重复描述同一事件)。
  2. Action 使用已注册动作码;ModuleCode 建议 rbac。
  3. TargetPrincipalType/TargetPrincipalId、RoleId、DetailJson 按具体接口填充;无对应实体时列可 NULL。

D. 共用规则

  1. 系统自动操作
    • OpPrincipalType = system,OpPrincipalId = NULL,OpDisplayName = 系统自动。
    • Remark 必填:写明触发原因(如「定时任务更新价格」「规则引擎自动调账」「MQ 消费重试」)。

操作者字段:操作者为内部用户时,使用 OpPrincipalType=user,OpPrincipalId 填其 user.Id。本规范所列 RBAC 动作与业务留痕均写入 t_operation_log,不拆分为其它 RBAC 专用审计表。

1.14 字典表 t_dict

字段名 字段类型 长度 主键/外键 非空 说明
Id BIGINT - 主键 是 自增唯一标识
DictType VARCHAR 64 - 是 字典类型(如 notice_type、warehouse_type、user_position(岗位)、user_education(学历)、customer_type(客户类型)、customer_settlement_method(客户结算方式))
DictSort INT - - 是 字典排序,值越小越靠前
DictCode VARCHAR 64 - 是 字典编码(同类型下唯一)
DictName VARCHAR 128 - 是 字典名称
IsDeleted TINYINT - - 是 是否删除:0-否,1-是,默认0
CreateUserId INT - - 是 创建人
CreateTime DATETIME - - 是 创建时间
UpdateUserId INT - - 否 更新人
UpdateTime DATETIME - - 否 更新时间

约束:业务存储、联查与接口校验均针对 DictType + DictCode;须 IsDeleted=0 方为有效字典项;同一 DictType 下有效行 DictCode 唯一。下拉/列表可按 DictSort 升序(值越小越靠前)。

本规范已使用的 DictType:user_position(内部用户岗位)、user_education(内部用户学历)、customer_type(客户类型)、customer_settlement_method(客户结算方式)。


2 令牌与鉴权约定

本章导读:抓住三条线——access / refresh 与 PermissionRevision/SessionVersion;改角色、改白名单、改密、吊销;客户子账号 ⊆ 主账号。2.1 全局约定;2.2 客户域风控表;2.3 三类主体注册时 微信绑定 的两种产品路径(与创建/更新接口对应)。

2.1 统一认证与鉴权约定

  • 模型分层与链路见 1.1;客户主账号/子账号细则见 2.2。
  • 命名:/rbac/user/* 仅 PrincipalType=user;客户主/子账号角色与白名单走 /customer/role/*、/customer/sub-account/*(目标接口见 5.2);客户资料 CRUD 见 5.1。
  • 登录:校验 t_auth_account → PrincipalType + PrincipalId。
  • 注册:Phone、Email 必填且全局唯一;微信字段可选,仅便捷登录。
  • 登录:请求参数 account = 手机号或邮箱(与 Phone 或 Email 匹配)+ 口令和/或已绑微信(实现可约定 Query/Body)。登录 / 刷新 响应体与访问令牌中标识主体:principalType + principalId(与 t_auth_account 一致)。
  • 口令、手机/邮箱、微信与展示字段 仅以 t_auth_account 为准。user.Avatar 仅为非微信头像。
  • 权限码:t_permission_definition 为权威;role_permission、menu、API 声明须 ∈ 该表。
  • 令牌 claims:含 permission_rev、session_ver(同库字段);与库不一致则 401;或 access ≤15 分钟 且变更主体绑定后 PermissionRevision+1。
    • 与示例的关系:expiresIn/exp(如登录响应里 7200 秒)以实现与网关配置为准;上文「≤15 分钟」指 role_permission 批量变更 且不刷各主体 PermissionRevision 时,靠 短 TTL 收敛权限漂移的策略建议(1.8),不表示响应里的秒数必须等于 900。
  • refresh:内嵌 session_ver 与库一致;改密、revoke-all-sessions、冻结则 SessionVersion+1。
  • **PermissionRevision+1时机**:覆盖写该主体t_principal_role、或该 customer_sub` 的白名单成功后。
  • role_permission 批量变更:不逐行刷 revision 时依赖短 TTL 或全局缓存失效。
  • 数据隔离:账单用 main_customer_id / principal_id;行级范围用 org_id / OrganizationId 叠加查询。
  • 接口域:user 走管理端;customer / customer_sub 走客户中心。
  • 微信字段落库:一律 t_auth_account(1.2);与注册/绑定的接口分工见 2.3。

2.2 客户域 RBAC 与子账号权限(模型与风控)

项目 说明
角色与扩展 可绑 RoleId 受 role.AllowedPrincipalTypes 约束。
主账号 P_{\text{主}} PrincipalType=customer 的角色 → role_permission 并集。
子账号 P_{\text{子}} 角色并集 ∩ 白名单(若有);必须 (P_{\text{子}}\subseteq P_{\text{主}})。
保存强校验 assign-roles / assign-permissions 事务内计算,⊄ 则 400;主账号 收紧 致子超限:默认 拒绝 直至先调整子账号。
登录与操作 写 t_operation_log;删子账号吊销 token。
改密 SessionVersion+1。

2.3 主体注册与微信绑定(两种路径)

适用于 PrincipalType = user / customer / customer_sub:微信仅作便捷登录与展示,不能替代 Phone + Email 开户(1.2)。前端一般先走微信 OAuth / 扫码拿到 openid(及可选 unionid)、头像、昵称,再与业务创建/更新配合。

路径 流程 接口
A. 先微信,再开户 用户完成微信授权 → 再填写手机、邮箱、密码(及主体业务字段)→ 一次性创建主体与认证行 POST /rbac/user/create、POST /customer/create、POST /customer/sub-account/create(4.6.3、5.1.5、5.2.5):Body 携带 wechatOpenid / wechatUnionid(可选)/ wechatAvatar / wechatNickname,写入 t_auth_account
B. 先开户,后绑微信 创建时 微信字段为空 → 后续用户在微信侧完成授权 → 调用更新接口补全绑定 PUT /rbac/user/update、POST /customer/update、POST /customer/sub-account/update(4.6.4、5.1.6、5.2.6):Body 携带上述微信字段;服务端更新 t_auth_account,WechatOpenid/WechatUnionid 全局唯一(IsDeleted=0 下不得与其它主体重复)

实现注意:路径 B 若提供独立 bind-wechat 接口,仍须写 t_auth_account 并做 OpenID 占用校验;与「更新接口带微信字段」二选一或并存时,在 OpenAPI 标明即可。


3 接口清单

本章导读:只关心「有哪些 URL、什么方法」时看本节。下面 3.1 为说明与链接;3.1.1~3.1.6 按 认证 → 权限 → 角色 → 组织 → 用户 → 菜单 排,与 OpenAPI 标签顺序可能不一致。3.1.7 为 OMS-Customer 已发布路径;3.1.8 为客户域 RBAC 目标路径(详见 5.2)。请求体、错误码、流程见 4(RBAC)、5(客户域)。

下表按业务链路重排;3.1.1~3.1.6 仅作分组标题,与实现侧 OpenAPI 中的出现顺序可能不一致。

3.1 RBAC 接口路径一览

来源:https://api.nanafa.com/admin/index.html?urls.primaryName=RBAC

实际文档地址:/admin/swagger/RBAC/swagger.json(接口数以运行时 OpenAPI 为准,含 revoke-all-sessions)

3.1.1 认证(Auth)

接口名称 请求方式 请求路径 说明
登录 POST /rbac/auth/login 用户登录
刷新令牌 POST /rbac/auth/refresh-token 刷新 token
修改密码 POST /rbac/auth/change-password 当前用户改密(SessionVersion+1)
当前用户信息 GET /rbac/auth/get-current-user 获取当前登录用户
吊销全部会话 POST /rbac/auth/revoke-all-sessions 当前主体所有 refresh 失效(SessionVersion+1)

3.1.2 权限(Permission)

接口名称 请求方式 请求路径 说明
全量权限 GET /rbac/permission/get-all-permissions 查询全部权限编码
权限树 GET /rbac/permission/get-permission-tree 查询权限树
单权限校验 POST /rbac/permission/check-permission 校验单个权限(唯一单码判权入口;不再提供 /rbac/user/has-permission)
多权限校验 POST /rbac/permission/check-permissions 批量校验权限

3.1.3 角色(Role)

接口名称 请求方式 请求路径 说明
角色列表 GET /rbac/role/get-list 分页查询角色
全部角色 GET /rbac/role/get-all-roles 查询全部角色
角色详情 GET /rbac/role/get 查询角色详情
创建角色 POST /rbac/role/create 新增角色
更新角色 PUT /rbac/role/update 编辑角色
删除角色 DELETE /rbac/role/delete 删除角色
分配权限 POST /rbac/role/assign-permissions 给角色分配权限
角色权限 GET /rbac/role/get-role-permissions 查询角色权限
角色用户数 GET /rbac/role/get-role-user-count 查询角色下用户数
角色用户列表 GET /rbac/role/get-role-users 查询角色下用户列表

3.1.4 组织(Organization)

接口名称 请求方式 请求路径 说明
组织列表 GET /rbac/organization/get-list 分页查询组织
组织树 GET /rbac/organization/get-tree 查询组织树
组织详情 GET /rbac/organization/get 查询组织详情
创建组织 POST /rbac/organization/create 新增组织
更新组织 PUT /rbac/organization/update 编辑组织
删除组织 DELETE /rbac/organization/delete 删除组织
移动组织 POST /rbac/organization/move 调整组织节点位置

3.1.5 用户(User)

接口名称 请求方式 请求路径 说明
用户列表 GET /rbac/user/get-list 分页查询用户
用户详情 GET /rbac/user/get 查询用户详情
创建用户 POST /rbac/user/create 新增用户
更新用户 PUT /rbac/user/update 编辑用户
删除用户 DELETE /rbac/user/delete 删除单个用户
批量删除用户 DELETE /rbac/user/delete-batch 批量删除用户
分配角色 POST /rbac/user/assign-roles 给用户分配角色
用户角色 GET /rbac/user/get-user-roles 查询用户角色
用户权限 GET /rbac/user/get-user-permissions 查询用户权限
角色下用户 GET /rbac/user/get-users-by-role 查询某角色下用户
更新用户头像 PUT /rbac/user/update-avatar 更新用户头像
更新用户密码 PUT /rbac/user/update-password 重置/修改用户密码

3.1.6 菜单(Menu)

接口名称 请求方式 请求路径 说明
菜单详情 GET /rbac/menu/get 查询菜单详情
菜单树 GET /rbac/menu/get-menu-tree 查询菜单树
用户菜单 GET /rbac/menu/get-user-menus 查询当前用户可见菜单
创建菜单 POST /rbac/menu/create 新增菜单
更新菜单 PUT /rbac/menu/update 编辑菜单
删除菜单 DELETE /rbac/menu/delete 删除菜单
移动菜单 POST /rbac/menu/move-menu 移动菜单节点
更新菜单排序 PUT /rbac/menu/update-order 更新菜单排序

3.1.7 客户资料(OMS-Customer)

来源:Swagger UI — OMS-Customer · /admin/swagger/OMS-Customer/swagger.json — 详细设计见 5.1。

接口名称 请求方式 请求路径 说明
客户列表 GET /customer/list 分页与筛选
客户详情 GET /customer/get 按 id
新增客户 POST /customer/create CreateCustomerInput
更新客户 POST /customer/update UpdateCustomerInput
删除客户 POST /customer/delete IdInput
重置客户密码 POST /customer/reset-password SessionVersion+1,见 5.1.8

3.1.8 客户域 RBAC(目标)

截至 OMS-Customer v1 可能未出现在同一 Swagger;路径与 5.2 一致。

接口名称 请求方式 请求路径 说明
主账号分配角色 POST /customer/role/assign 5.2.2
主账号角色列表 GET /customer/role/list 5.2.3
子账号列表 GET /customer/sub-account/list 5.2.4
创建子账号 POST /customer/sub-account/create 5.2.5
更新子账号 POST /customer/sub-account/update 5.2.6
删除子账号 POST /customer/sub-account/delete 5.2.7
子账号分配角色 POST /customer/sub-account/assign-roles 5.2.8
子账号配置白名单 POST /customer/sub-account/assign-permissions 5.2.9
子账号有效权限预览 GET /customer/sub-account/effective-permissions 5.2.10

4 接口详细设计

本章导读:写代码 / 写用例以本节为主。结构为 4.2~4.7 六大块(与 3.1.1~3.1.6 接口分组同序);块内 4.x.y.z 为接口与子步骤编号(便于搜索),每个接口含 示例 → 业务逻辑 → 校验与异常。篇幅长,请 按路径搜索(如 /rbac/role/create)。

4.1 接口详细设计总述

下文顺序:认证 → 权限 → 角色 → 组织 → 用户 → 菜单,与 4.2~4.7 分块一致;接口条目为 4.2.1、4.3.1 形式;路径以运行时 API 为准。令牌 permission_rev/session_ver、PermissionRevision/SessionVersion 及审计要求见 2。客户域(客户资料 + 客户侧 RBAC 目标接口)详细设计见 5。

4.1.1 标识符与请求参数约定(principalId / userId / id)

为避免 t_auth_account.Id(认证行主键)、PrincipalId(业务主体主键)、user.Id 与各类 Query 同名混用,统一约定如下(OpenAPI 须与之一致):

符号 / 参数 含义 典型出现位置
principalType + principalId(camelCase) 与 t_auth_account 一致:principalId 永远是业务主体主键(user.Id / t_customer.Id / t_customer_sub_account.Id),不是 t_auth_account.Id。 登录/刷新响应(4.2.1~4.2.2)、令牌 claims;POST /rbac/permission/check-permission 等
userId 仅表示 user.Id(内部员工)。在 **PrincipalType=user 时 userId = 该主体的 PrincipalId。 /rbac/user/assign-roles、get-user-roles、get-user-permissions、get-user-menus 等 4.6 / 4.7 示例
id(裸 Query) 随资源而变,须读接口路径判断目标表:例如 /rbac/user/* 下多为 user.Id;/rbac/role/* 下为 role.Id;/rbac/menu/* 下为 menu.Id;/customer/get 下为 t_customer.Id(即客户主账号的 PrincipalId,但参数名沿用 OpenAPI id)。 各节请求示例
customerId / subAccountId 分别对应 t_customer.Id、t_customer_sub_account.Id;写入 t_principal_role 时即同类型的 PrincipalId。 5.2 客户域 RBAC

须写进 OpenAPI 的实现决策(当前规范留歧义处):

  1. GET /rbac/menu/get-user-menus?userId=:userId 是否仅允许查询本人(忽略 Query,以 token 为准)、还是允许管理员查任意用户——二者选一并实现,禁止默认可查他人而不鉴权。

库表字段名提示:各业务表 CreateUserId/UpdateUserId 为 INT 与 user.Id BIGINT 并存时,以实现与迁移为准;新库建议审计列与 user.Id 类型对齐,避免大 Id 溢出。

4.2 认证(Auth)

4.2.1 登录(POST /rbac/auth/login)

4.2.1.1 接口请求示例

请求 /rbac/auth/login(POST,Query 参数)

?account=13800138000&password=123456&type=0

响应

{
  "accessToken": "eyJhbGciOi...",
  "refreshToken": "rt_refresh_token_01",
  "tokenType": "Bearer",
  "expiresIn": 7200,
  "principalType": "user",
  "principalId": 1
}
4.2.1.2 接口业务逻辑
4.2.1.2.1 核心流程
  • account 为 手机号或邮箱(与 t_auth_account.Phone 或 Email 匹配);password 与/或已绑微信鉴权;成功后签发 accessToken 与 refreshToken。
  • 返回 LoginResultDto:principalType、principalId 与 t_auth_account 一致;令牌 claims 与客户端缓存均以二者为唯一主体标识。
4.2.1.2.2 校验与异常
  • 校验 account 必填;password 与「已绑微信」对应的鉴权材料至少满足其一(与 4.2.1.2.1 一致;纯微信登录时 Body/Query 形态由 OpenAPI 约定),不得再写「密码必填」以免与微信鉴权矛盾。type 默认 0(内部管理端);其它取值由部署约定(如客户门户 type=1),语义见 2.2。
  • 鉴权失败或主体状态异常时返回 ApplicationExceptionResult(400)。
  • 客户主账号 / 子账号登录成功:按主体从 t_principal_role(及子账号 t_customer_sub_permission 白名单)聚合 roles/permissions/menus(2.2)。零角色绑定 时 permissions 可为空或仅登录/个人中心最小集(须在 t_permission_definition 注册)。

4.2.2 刷新令牌(POST /rbac/auth/refresh-token)

4.2.2.1 接口请求示例

请求 /rbac/auth/refresh-token(POST,Query 参数)

?refreshToken=rt_refresh_token_01

响应

{
  "accessToken": "eyJhbGciOi...",
  "refreshToken": "rt_refresh_token_02",
  "tokenType": "Bearer",
  "expiresIn": 7200,
  "principalType": "user",
  "principalId": 1
}
4.2.2.2 接口业务逻辑
4.2.2.2.1 核心流程
  • 校验 refreshToken 有效且内嵌 session_ver 与 t_auth_account.SessionVersion 一致,生成并返回新令牌对(可旋转 refresh);principalType/principalId 同 4.2.1。
4.2.2.2.2 校验与异常
  • refreshToken 无效、过期、session_ver 不匹配或被吊销时返回 ApplicationExceptionResult(400)。

4.2.3 修改密码(POST /rbac/auth/change-password)

4.2.3.1 接口请求示例

请求 /rbac/auth/change-password(POST,Query 参数)

?oldPassword=old123&newPassword=new123456

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.2.3.2 接口业务逻辑
4.2.3.2.1 核心流程
  • 使用当前登录主体校验旧密码,成功后仅更新当前主体对应 t_auth_account 的 PasswordHash/PasswordSalt,不写入 user 表;SessionVersion+1;写 t_operation_log(auth_password_change)。
4.2.3.2.2 校验与异常
  • 旧密码错误、密码策略不满足或主体不存在时返回 ApplicationExceptionResult(400)。

4.2.4 当前用户信息(GET /rbac/auth/get-current-user)

4.2.4.1 接口请求示例

请求 /rbac/auth/get-current-user(GET)

无请求参数,基于当前登录令牌解析用户身份。

响应

{
  "id": 1,
  "name": "管理员A",
  "phone": "13800138000",
  "email": "admin@example.com",
  "avatar": "https://cdn.example.com/avatar/admin.png",
  "organizationId": 10,
  "organizationName": "总部",
  "roles": [
    "Admin"
  ],
  "permissions": [
    "rbac:user:get-list",
    "rbac:role:get-list"
  ],
  "menus": []
}
4.2.4.2 接口业务逻辑
4.2.4.2.1 核心流程
  • 根据 token 解析主体,聚合返回基础信息、角色、权限与菜单。
4.2.4.2.2 校验与异常
  • token 无效、主体不存在或已禁用时返回 ApplicationExceptionResult(400)。
  • principal_type 为 customer / customer_sub:按 2.2 聚合权限与菜单;子账号响应可带 mainCustomerId(与令牌 claim main_customer_id 一致)。

4.2.5 吊销全部会话(POST /rbac/auth/revoke-all-sessions)

4.2.5.1 接口请求示例

请求 /rbac/auth/revoke-all-sessions(POST)

无 Body;基于当前 access token 识别主体。

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.2.5.2 接口业务逻辑
4.2.5.2.1 核心流程
  • 当前主体 SessionVersion+1,吊销全部 refresh;写 t_operation_log(auth_session_revoke)。
4.2.5.2.2 校验与异常
  • token 无效时返回 ApplicationExceptionResult(400)。

4.3 权限(Permission)

4.3.1 全量权限(GET /rbac/permission/get-all-permissions)

4.3.1.1 接口请求示例

请求 /rbac/permission/get-all-permissions(GET)

无请求参数。 响应

[
  "rbac:user:get-list",
  "rbac:role:get-list"
]
4.3.1.2 接口业务逻辑
4.3.1.2.1 核心流程
  • 返回系统全部权限编码列表。
4.3.1.2.2 校验与异常
  • 查询异常返回 ApplicationExceptionResult(400)。

4.3.2 权限树(GET /rbac/permission/get-permission-tree)

4.3.2.1 接口请求示例

请求 /rbac/permission/get-permission-tree(GET)

无请求参数。 响应

[
  {
    "permissionCode": "rbac:user",
    "children": [
      {
        "permissionCode": "rbac:user:get-list"
      }
    ]
  }
]
4.3.2.2 接口业务逻辑
4.3.2.2.1 核心流程
  • 返回权限树结构,供角色授权界面使用;树形与 t_permission_definition 库表字段的对应关系见 1.9.1。
4.3.2.2.2 校验与异常
  • 数据异常时返回 ApplicationExceptionResult(400)。

4.3.3 单权限校验(POST /rbac/permission/check-permission)

唯一单码判权接口:不提供 POST /rbac/user/has-permission。对内部用户判权使用 principalType=user 且 principalId=user.Id(见 4.1.1)。

4.3.3.1 接口请求示例

请求 /rbac/permission/check-permission(POST,Query 参数)

?principalType=user&principalId=1&permissionCode=rbac:user:get-list

响应

true
4.3.3.2 接口业务逻辑
4.3.3.2.1 核心流程
  • 按 principalType + principalId 解析 t_auth_account 与 t_principal_role(及子账号白名单),判断是否具备目标 permissionCode。
4.3.3.2.2 校验与异常
  • 主体不存在、permissionCode 未注册或权限码为空时返回 ApplicationExceptionResult(400)。

4.3.4 多权限校验(POST /rbac/permission/check-permissions)

4.3.4.1 接口请求示例

请求 /rbac/permission/check-permissions(POST,Query + Body)

?principalType=user&principalId=1
[
  "rbac:user:get-list",
  "rbac:role:get-list"
]

响应

{
  "rbac:user:get-list": true,
  "rbac:role:get-list": false
}
4.3.4.2 接口业务逻辑
4.3.4.2.1 核心流程
  • 批量校验权限并返回映射结果;主体解析同 4.3.3。
4.3.4.2.2 校验与异常
  • 请求体为空或主体不存在时返回 ApplicationExceptionResult(400)。

4.4 角色(Role)

4.4.1 角色列表(GET /rbac/role/get-list)

4.4.1.1 接口请求示例

请求 /rbac/role/get-list(GET,Query 参数)

?Keyword=admin&Page=1&PageSize=20&OrderBy=CreateTime&Descending=true

响应

{
  "total": 1,
  "items": [
    {
      "id": 20,
      "name": "管理员",
      "code": "Admin",
      "permissions": [
        "rbac:user:get-list"
      ]
    }
  ]
}
4.4.1.2 接口业务逻辑
4.4.1.2.1 核心流程
  • 分页查询角色并返回 RoleDtoPagedResultDto。
4.4.1.2.2 校验与异常
  • 分页参数非法时返回 ApplicationExceptionResult(400)。

4.4.2 全部角色(GET /rbac/role/get-all-roles)

4.4.2.1 接口请求示例

请求 /rbac/role/get-all-roles(GET)

无请求参数。 响应

[
  {
    "id": 20,
    "name": "管理员",
    "code": "Admin"
  }
]
4.4.2.2 接口业务逻辑
4.4.2.2.1 核心流程
  • 返回全部可用角色列表。
4.4.2.2.2 校验与异常
  • 查询异常返回 ApplicationExceptionResult(400)。

4.4.3 角色详情(GET /rbac/role/get)

4.4.3.1 接口请求示例

请求 /rbac/role/get(GET,Query 参数)

?id=20

响应

{
  "id": 20,
  "name": "管理员",
  "code": "Admin",
  "permissions": [
    "rbac:user:get-list"
  ]
}
4.4.3.2 接口业务逻辑
4.4.3.2.1 核心流程
  • 根据角色 ID 返回 RoleDto。
4.4.3.2.2 校验与异常
  • 角色不存在时返回 ApplicationExceptionResult(400)。

4.4.4 创建角色(POST /rbac/role/create)

4.4.4.1 接口请求示例

请求 /rbac/role/create(POST,Body)

{
  "name": "运营",
  "code": "Operator",
  "allowedPrincipalTypes": "user",
  "permissions": [
    "rbac:user:get-list",
    "rbac:role:get-list"
  ]
}

响应

{
  "id": 21,
  "name": "运营",
  "code": "Operator",
  "allowedPrincipalTypes": "user",
  "permissions": [
    "rbac:user:get-list",
    "rbac:role:get-list"
  ]
}
4.4.4.2 接口业务逻辑
4.4.4.2.1 核心流程
  • 写入 role(含 AllowedPrincipalTypes) 与 role_permission;权限码须注册;写 t_operation_log(RBAC 动作行)。
4.4.4.2.2 校验与异常
  • code 重复、allowedPrincipalTypes 非法或权限编码未注册时返回 ApplicationExceptionResult(400)。

4.4.5 更新角色(PUT /rbac/role/update)

4.4.5.1 接口请求示例

请求 /rbac/role/update(PUT,Query + Body)

?id=21
{
  "name": "运营A"
}

响应

{
  "id": 21,
  "name": "运营A",
  "code": "Operator"
}
4.4.5.2 接口业务逻辑
4.4.5.2.1 核心流程
  • 更新角色名称并返回 RoleDto。
4.4.5.2.2 校验与异常
  • 角色不存在时返回 ApplicationExceptionResult(400)。

4.4.6 删除角色(DELETE /rbac/role/delete)

4.4.6.1 接口请求示例

请求 /rbac/role/delete(DELETE,Query 参数)

?id=21

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.4.6.2 接口业务逻辑
4.4.6.2.1 核心流程
  • 删除角色及其关联权限关系。
4.4.6.2.2 校验与异常
  • 角色被使用且不允许删除时返回 ApplicationExceptionResult(400)。

4.4.7 分配权限(POST /rbac/role/assign-permissions)

4.4.7.1 接口请求示例

请求 /rbac/role/assign-permissions(POST,Query + Body)

?roleId=21
[
  "rbac:user:get-list",
  "rbac:role:get-list"
]

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.4.7.2 接口业务逻辑
4.4.7.2.1 核心流程
  • 校验 PermissionCode ∈ t_permission_definition;覆盖更新 role_permission;写 t_operation_log(role_permission_assign)。(不逐行刷各主体 PermissionRevision 时依赖 2所述短 TTL 或缓存失效。)
4.4.7.2.2 校验与异常
  • 角色不存在或权限码未注册时返回 ApplicationExceptionResult(400)。

4.4.8 角色权限(GET /rbac/role/get-role-permissions)

4.4.8.1 接口请求示例

请求 /rbac/role/get-role-permissions(GET,Query 参数)

?roleId=21

响应

[
  "rbac:user:get-list",
  "rbac:role:get-list"
]
4.4.8.2 接口业务逻辑
4.4.8.2.1 核心流程
  • 查询角色已授权权限编码列表。
4.4.8.2.2 校验与异常
  • 角色不存在时返回 ApplicationExceptionResult(400)。

4.4.9 角色用户数(GET /rbac/role/get-role-user-count)

4.4.9.1 接口请求示例

请求 /rbac/role/get-role-user-count(GET,Query 参数)

?roleId=21

响应

12
4.4.9.2 接口业务逻辑
4.4.9.2.1 核心流程
  • 统计角色下用户数。
4.4.9.2.2 校验与异常
  • 角色不存在时返回 ApplicationExceptionResult(400)。

4.4.10 角色用户列表(GET /rbac/role/get-role-users)

4.4.10.1 接口请求示例

请求 /rbac/role/get-role-users(GET,Query 参数)

?roleId=21

响应

[
  {
    "id": 1,
    "name": "管理员A",
    "phone": "13800138000"
  }
]
4.4.10.2 接口业务逻辑
4.4.10.2.1 核心流程
  • 返回角色下用户集合。
4.4.10.2.2 校验与异常
  • 角色不存在时返回 ApplicationExceptionResult(400)。

4.5 组织(Organization)

4.5.1 组织列表(GET /rbac/organization/get-list)

4.5.1.1 接口请求示例

请求 /rbac/organization/get-list(GET)

无请求参数。 响应

[
  {
    "id": 10,
    "parentId": null,
    "name": "总部",
    "code": "HQ",
    "type": "Company",
    "order": 1,
    "isEnabled": true
  }
]
4.5.1.2 接口业务逻辑
4.5.1.2.1 核心流程
  • 查询组织平铺列表。
4.5.1.2.2 校验与异常
  • 查询异常返回 ApplicationExceptionResult(400)。

4.5.2 组织树(GET /rbac/organization/get-tree)

4.5.2.1 接口请求示例

请求 /rbac/organization/get-tree(GET)

无请求参数。 响应

[
  {
    "id": 10,
    "name": "总部",
    "children": []
  }
]
4.5.2.2 接口业务逻辑
4.5.2.2.1 核心流程
  • 按父子关系构建并返回组织树。
4.5.2.2.2 校验与异常
  • 数据异常(循环父子关系)时返回 ApplicationExceptionResult(400)。

4.5.3 组织详情(GET /rbac/organization/get)

4.5.3.1 接口请求示例

请求 /rbac/organization/get(GET,Query 参数)

?id=10

响应

{
  "id": 10,
  "parentId": null,
  "name": "总部",
  "code": "HQ",
  "type": "Company",
  "leaderId": 1,
  "leaderName": "管理员A",
  "order": 1,
  "description": "总部组织",
  "isEnabled": true
}
4.5.3.2 接口业务逻辑
4.5.3.2.1 核心流程
  • 根据组织 ID 查询 OrganizationDto。
4.5.3.2.2 校验与异常
  • 组织不存在时返回 ApplicationExceptionResult(400)。

4.5.4 创建组织(POST /rbac/organization/create)

4.5.4.1 接口请求示例

请求 /rbac/organization/create(POST,Body)

{
  "parentId": 10,
  "name": "运营部",
  "code": "OP",
  "type": "Department",
  "leaderId": 1,
  "order": 10,
  "description": "运营部门",
  "isEnabled": true
}

响应

{
  "id": 11,
  "name": "运营部",
  "code": "OP",
  "type": "Department"
}
4.5.4.2 接口业务逻辑
4.5.4.2.1 核心流程
  • 新增组织节点并返回 OrganizationDto。
4.5.4.2.2 校验与异常
  • 编码重复、父节点不存在时返回 ApplicationExceptionResult(400)。

4.5.5 更新组织(PUT /rbac/organization/update)

4.5.5.1 接口请求示例

请求 /rbac/organization/update(PUT,Query + Body)

?id=11
{
  "name": "运营中心",
  "type": "Department",
  "leaderId": 1,
  "order": 20,
  "description": "运营中心",
  "isEnabled": true
}

响应

{
  "id": 11,
  "name": "运营中心",
  "code": "OP"
}
4.5.5.2 接口业务逻辑
4.5.5.2.1 核心流程
  • 按 ID 更新组织信息。
4.5.5.2.2 校验与异常
  • 组织不存在或状态非法时返回 ApplicationExceptionResult(400)。

4.5.6 删除组织(DELETE /rbac/organization/delete)

4.5.6.1 接口请求示例

请求 /rbac/organization/delete(DELETE,Query 参数)

?id=11

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.5.6.2 接口业务逻辑
4.5.6.2.1 核心流程
  • 删除组织节点。
4.5.6.2.2 校验与异常
  • 有子节点或被用户引用时返回 ApplicationExceptionResult(400)。

4.5.7 移动组织(POST /rbac/organization/move)

4.5.7.1 接口请求示例

请求 /rbac/organization/move(POST,Query + Body)

?id=11
{
  "newParentId": 10
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.5.7.2 接口业务逻辑
4.5.7.2.1 核心流程
  • 调整组织父节点,重建树关系。
4.5.7.2.2 校验与异常
  • 新父节点非法、移动后形成循环层级时返回 ApplicationExceptionResult(400)。

4.6 用户(User)

命名:本节 Query 中的 userId = user.Id(即 PrincipalType=user 时的 PrincipalId),见 4.1.1。
员工档案与接口:user 表人事字段见 1.3。POST /rbac/user/create、PUT /rbac/user/update 在姓名、手机、邮箱、组织、角色等基础字段之外,均可选携带与 1.3 一致的档案属性(positionCode、age、idCardNo、birthday、educationCode、address、graduateSchool、emergencyContact、emergencyContactPhone、hireDate、leaveDate、warehouseId;命名建议 camelCase 与 OpenAPI 一致)。微信绑定与 t_auth_account 的两种路径见 2.3:创建时可带 wechatOpenid 等;仅后续绑定时用 update 写入。GET /rbac/user/get 返回完整 UserDto;列表可为 UserSummaryDto。positionCode / educationCode 响应可附带 positionName / educationName(t_dict,IsDeleted=0)。idCardNo 按权限脱敏;warehouseId 须校验仓库存在。

4.6.1 用户列表(GET /rbac/user/get-list)

4.6.1.1 接口请求示例

请求 /rbac/user/get-list(GET,Query 参数)

?Keyword=admin&RoleId=&OrganizationId=&Page=1&PageSize=20&OrderBy=CreateTime&Descending=true

响应

{
  "total": 1,
  "items": [
    {
      "id": 1,
      "name": "管理员A",
      "phone": "13800138000",
      "email": "admin@example.com",
      "organizationId": 10,
      "organizationName": "总部",
      "positionCode": "pos_dev",
      "positionName": "开发",
      "warehouseId": 100,
      "hireDate": "2023-01-15",
      "roles": [
        "Admin"
      ]
    }
  ]
}
4.6.1.2 接口业务逻辑
4.6.1.2.1 核心流程
  • 按关键字、角色、组织与分页参数查询用户,返回 UserDtoPagedResultDto;列表项可含 1.3 中常用档案列(如岗位、仓库、入职日),敏感列默认不出或按权限裁剪。
4.6.1.2.2 校验与异常
  • 分页参数非法或排序字段不支持时返回 ApplicationExceptionResult(400)。

4.6.2 用户详情(GET /rbac/user/get)

4.6.2.1 接口请求示例

请求 /rbac/user/get(GET,Query 参数)

?id=1

响应

{
  "id": 1,
  "name": "管理员A",
  "phone": "13800138000",
  "email": "admin@example.com",
  "avatar": "https://cdn.example.com/avatar/admin.png",
  "organizationId": 10,
  "organizationName": "总部",
  "positionCode": "pos_dev",
  "positionName": "开发",
  "age": 32,
  "idCardNo": "110101********1234",
  "birthday": "1993-06-01",
  "educationCode": "edu_bachelor",
  "educationName": "本科",
  "address": "北京市朝阳区…",
  "graduateSchool": "某某大学",
  "emergencyContact": "李四",
  "emergencyContactPhone": "13900000000",
  "hireDate": "2020-03-01",
  "leaveDate": null,
  "warehouseId": 100,
  "concurrencyStamp": 3,
  "roles": [
    "Admin"
  ]
}
4.6.2.2 接口业务逻辑
4.6.2.2.1 核心流程
  • 根据用户 ID 查询并返回 UserDto(含 1.3 全部员工档案字段;idCardNo 等敏感字段按数据权限脱敏或掩码;concurrencyStamp 供更新时乐观并发可选使用)。
4.6.2.2.2 校验与异常
  • id 不存在或已删除返回 ApplicationExceptionResult(400)。

4.6.3 创建用户(POST /rbac/user/create)

4.6.3.1 接口请求示例

请求 /rbac/user/create(POST,Body)

{
  "name": "测试用户",
  "phone": "13800138001",
  "password": "123456",
  "email": "test001@example.com",
  "avatar": "",
  "organizationId": 10,
  "roleIds": [20],
  "positionCode": "pos_operator",
  "age": 28,
  "idCardNo": "110101199001011234",
  "birthday": "1990-01-01",
  "educationCode": "edu_bachelor",
  "address": "上海市浦东新区…",
  "graduateSchool": "某某学院",
  "emergencyContact": "王五",
  "emergencyContactPhone": "13800000000",
  "hireDate": "2025-05-01",
  "leaveDate": null,
  "warehouseId": 100,
  "wechatOpenid": "oXXXX_先微信授权再开户时可带",
  "wechatUnionid": null,
  "wechatAvatar": "https://thirdwx.qlogo.cn/...",
  "wechatNickname": "微信昵称"
}

上列 positionCode~warehouseId 均为可选;wechatOpenid~wechatNickname 可选(路径 A:与手机邮箱密码同单创建,见 2.3)。不传微信字段则 路径 B 留待 update 绑定。

响应

{
  "id": 2,
  "name": "测试用户",
  "phone": "13800138001",
  "email": "test001@example.com",
  "organizationId": 10,
  "organizationName": "总部",
  "positionCode": "pos_operator",
  "positionName": "运营",
  "hireDate": "2025-05-01",
  "warehouseId": 100,
  "roles": [
    "Operator"
  ]
}
4.6.3.2 接口业务逻辑
4.6.3.2.1 核心流程
  • 建 user + t_auth_account(Phone、Email 必填;口令仅存认证表;请求体 avatar 落 user.Avatar);wechatOpenid/wechatUnionid/wechatAvatar/wechatNickname 若有则写入 t_auth_account(路径 A,2.3)。员工档案字段(1.3)与请求体 camelCase 一一写入 user;PositionCode/EducationCode 非空须 t_dict 有效;WarehouseId/IdCardNo 校验同前;roleIds → t_principal_role(user + user.Id);返回 UserDto。
4.6.3.2.2 校验与异常
  • phone/email 重复、微信 OpenID/UnionID 已被占用、组织不存在、角色无效、字典码无效、仓库不存在、身份证号冲突时返回 ApplicationExceptionResult(400)。

4.6.4 更新用户(PUT /rbac/user/update)

4.6.4.1 接口请求示例

请求 /rbac/user/update(PUT,Query + Body)

?id=2
{
  "name": "测试用户A",
  "phone": "13800138001",
  "email": "test001a@example.com",
  "avatar": "https://cdn.example.com/avatar/test001.png",
  "organizationId": 10,
  "positionCode": "pos_operator",
  "educationCode": "edu_master",
  "emergencyContactPhone": "13800000001",
  "leaveDate": null,
  "warehouseId": 101,
  "concurrencyStamp": 3,
  "wechatOpenid": "oXXXX_后绑微信时由更新写入",
  "wechatUnionid": null,
  "wechatAvatar": "https://thirdwx.qlogo.cn/...",
  "wechatNickname": "微信昵称"
}

部分更新:仅传需要变更的字段即可;未出现的档案字段保持原值。concurrencyStamp 若传入须与库中一致,否则 409。路径 B(2.3):首次绑定或换绑微信时提交 wechatOpenid 等;仅改展示可不重复传 OpenID。

响应

{
  "id": 2,
  "name": "测试用户A",
  "phone": "13800138001",
  "email": "test001a@example.com",
  "avatar": "https://cdn.example.com/avatar/test001.png",
  "organizationId": 10,
  "positionCode": "pos_operator",
  "positionName": "运营",
  "educationCode": "edu_master",
  "educationName": "硕士",
  "warehouseId": 101,
  "concurrencyStamp": 4
}
4.6.4.2 接口业务逻辑
4.6.4.2.1 核心流程
  • 按 id 更新用户基础信息与员工档案字段(1.3 全部,可部分更新),字典字段校验同创建;phone/email 变更同步 t_auth_account;微信字段变更同步 t_auth_account(路径 B,2.3),OpenID/UnionID 不得与其它主体冲突。可选 concurrencyStamp 与 user.ConcurrencyStamp 对齐后 +1;返回完整 UserDto。
4.6.4.2.2 校验与异常
  • 用户不存在、邮箱/手机冲突、微信 OpenID 已被占用、组织非法、字典码无效、仓库不存在、并发戳冲突时返回 ApplicationExceptionResult(400/409)。

4.6.5 删除用户(DELETE /rbac/user/delete)

4.6.5.1 接口请求示例

请求 /rbac/user/delete(DELETE,Query 参数)

?id=2

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.6.5.2 接口业务逻辑
4.6.5.2.1 核心流程
  • 逻辑删除指定用户及其关联关系。
4.6.5.2.2 校验与异常
  • 用户不存在、不可删除主体(如内置管理员)返回 ApplicationExceptionResult(400)。

4.6.6 批量删除用户(DELETE /rbac/user/delete-batch)

4.6.6.1 接口请求示例

请求 /rbac/user/delete-batch(DELETE,Query 参数)

?ids=2,3

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.6.6.2 接口业务逻辑
4.6.6.2.1 核心流程
  • 批量逻辑删除用户。
4.6.6.2.2 校验与异常
  • 任一 ID 无效或存在不可删除用户时返回 ApplicationExceptionResult(400)。

4.6.7 分配角色(POST /rbac/user/assign-roles)

4.6.7.1 接口请求示例

请求 /rbac/user/assign-roles(POST,Query + Body)

?userId=2
[
  20,
  21
]

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.6.7.2 接口业务逻辑
4.6.7.2.1 核心流程
  • 校验角色 AllowedPrincipalTypes 含 user;覆盖写 t_principal_role;该用户认证行 PermissionRevision+1;写 t_operation_log(principal_role_assign)。
4.6.7.2.2 校验与异常
  • 用户不存在、角色不适用 user 或 Id 非法时返回 ApplicationExceptionResult(400)。

4.6.8 用户角色(GET /rbac/user/get-user-roles)

4.6.8.1 接口请求示例

请求 /rbac/user/get-user-roles(GET,Query 参数)

?userId=2

响应

[
  {
    "id": 20,
    "name": "管理员",
    "code": "Admin"
  }
]
4.6.8.2 接口业务逻辑
4.6.8.2.1 核心流程
  • 查询指定用户角色列表。
4.6.8.2.2 校验与异常
  • 用户不存在时返回 ApplicationExceptionResult(400)。

4.6.9 用户权限(GET /rbac/user/get-user-permissions)

4.6.9.1 接口请求示例

请求 /rbac/user/get-user-permissions(GET,Query 参数)

?userId=2

响应

[
  "rbac:user:get-list",
  "rbac:role:get-list"
]
4.6.9.2 接口业务逻辑
4.6.9.2.1 核心流程
  • 聚合用户全部权限编码并返回。
4.6.9.2.2 校验与异常
  • 用户不存在或状态异常时返回 ApplicationExceptionResult(400)。

4.6.10 角色下用户(GET /rbac/user/get-users-by-role)

4.6.10.1 接口请求示例

请求 /rbac/user/get-users-by-role(GET,Query 参数)

?roleId=20

响应

[
  {
    "id": 1,
    "name": "管理员A",
    "phone": "13800138000"
  }
]
4.6.10.2 接口业务逻辑
4.6.10.2.1 核心流程
  • 查询某角色下的用户列表。
4.6.10.2.2 校验与异常
  • 角色不存在时返回 ApplicationExceptionResult(400)。

4.6.11 更新用户头像(PUT /rbac/user/update-avatar)

4.6.11.1 接口请求示例

请求 /rbac/user/update-avatar(PUT,Query 参数)

?id=2&avatarUrl=https://cdn.example.com/avatar/test001.png

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.6.11.2 接口业务逻辑
4.6.11.2.1 核心流程
  • 更新指定用户头像地址。
4.6.11.2.2 校验与异常
  • 用户不存在或头像地址非法时返回 ApplicationExceptionResult(400)。

4.6.12 更新用户密码(PUT /rbac/user/update-password)

4.6.12.1 接口请求示例

请求 /rbac/user/update-password(PUT,Query + Body)

?id=2
{
  "oldPassword": "123456",
  "newPassword": "new123456"
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.6.12.2 接口业务逻辑
4.6.12.2.1 核心流程
  • 按用户 ID 定位 t_auth_account(PrincipalType=user),校验旧密码后更新 PasswordHash/PasswordSalt,不写入 user 表;SessionVersion+1;写 t_operation_log(auth_password_change)。
4.6.12.2.2 校验与异常
  • 原密码错误、密码策略不满足时返回 ApplicationExceptionResult(400)。

4.7 菜单(Menu)

4.7.1 菜单详情(GET /rbac/menu/get)

4.7.1.1 接口请求示例

请求 /rbac/menu/get(GET,Query 参数)

?id=30

响应

{
  "id": 30,
  "pId": 0,
  "name": "系统管理",
  "permissionCode": "rbac:system",
  "type": 1,
  "route": "/system",
  "component": "system/index",
  "order": 1,
  "isHidden": false,
  "isCached": true,
  "description": null
}
4.7.1.2 接口业务逻辑
4.7.1.2.1 核心流程
  • 根据菜单 ID 查询 MenuDto。
4.7.1.2.2 校验与异常
  • 菜单不存在时返回 ApplicationExceptionResult(400)。

4.7.2 菜单树(GET /rbac/menu/get-menu-tree)

4.7.2.1 接口请求示例

请求 /rbac/menu/get-menu-tree(GET)

无请求参数。 响应

[
  {
    "id": 30,
    "name": "系统管理",
    "children": []
  }
]
4.7.2.2 接口业务逻辑
4.7.2.2.1 核心流程
  • 返回完整菜单树。
4.7.2.2.2 校验与异常
  • 查询异常返回 ApplicationExceptionResult(400)。

4.7.3 用户菜单(GET /rbac/menu/get-user-menus)

4.7.3.1 接口请求示例

请求 /rbac/menu/get-user-menus(GET,Query 参数)

?userId=1

响应

[
  {
    "id": 30,
    "name": "系统管理",
    "route": "/system"
  }
]
4.7.3.2 接口业务逻辑
4.7.3.2.1 核心流程
  • 根据 userId(即 user.Id,见 4.1.1) 解析 PrincipalType=user + PrincipalId,再按有效权限码过滤可见菜单。
4.7.3.2.2 校验与异常
  • 用户不存在时返回 ApplicationExceptionResult(400)。
  • 授权:调用者能否指定任意 userId 须按 4.1.1 在 OpenAPI 中写死(仅本人 / 或具备用户管理权限),避免越权枚举他人菜单。

4.7.4 创建菜单(POST /rbac/menu/create)

4.7.4.1 接口请求示例

请求 /rbac/menu/create(POST,Body)

{
  "pId": 0,
  "name": "用户管理",
  "permissionCode": "rbac:user:get-list",
  "type": 2,
  "icon": "User",
  "route": "/system/user",
  "component": "system/user/index",
  "order": 10,
  "isHidden": false,
  "isCached": true,
  "description": "用户管理菜单"
}

响应

{
  "id": 31,
  "name": "用户管理",
  "permissionCode": "rbac:user:get-list"
}
4.7.4.2 接口业务逻辑
4.7.4.2.1 核心流程
  • 新增菜单并返回 MenuDto;permissionCode 须已注册于 t_permission_definition 且未删(1.9、1.11)。
4.7.4.2.2 校验与异常
  • permissionCode 未注册、与同表 IsDeleted=0 下行重复、父节点不存在时返回 ApplicationExceptionResult(400)。

4.7.5 更新菜单(PUT /rbac/menu/update)

4.7.5.1 接口请求示例

请求 /rbac/menu/update(PUT,Query + Body)

?id=31
{
  "name": "用户中心",
  "permissionCode": "rbac:user:get-list",
  "icon": "User",
  "route": "/system/user",
  "component": "system/user/index",
  "isHidden": false,
  "isCached": true,
  "description": "用户中心菜单"
}

响应

{
  "id": 31,
  "name": "用户中心",
  "permissionCode": "rbac:user:get-list"
}
4.7.5.2 接口业务逻辑
4.7.5.2.1 核心流程
  • 更新菜单信息并返回 MenuDto。
4.7.5.2.2 校验与异常
  • 菜单不存在、编码冲突时返回 ApplicationExceptionResult(400)。

4.7.6 删除菜单(DELETE /rbac/menu/delete)

4.7.6.1 接口请求示例

请求 /rbac/menu/delete(DELETE,Query 参数)

?id=31

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.7.6.2 接口业务逻辑
4.7.6.2.1 核心流程
  • 删除菜单节点。
4.7.6.2.2 校验与异常
  • 菜单存在子节点或被引用时返回 ApplicationExceptionResult(400)。

4.7.7 移动菜单(POST /rbac/menu/move-menu)

4.7.7.1 接口请求示例

请求 /rbac/menu/move-menu(POST,Query 参数)

?menuId=31&targetParentId=30

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.7.7.2 接口业务逻辑
4.7.7.2.1 核心流程
  • 调整菜单父子层级。
4.7.7.2.2 校验与异常
  • 节点非法或形成循环层级时返回 ApplicationExceptionResult(400)。

4.7.8 更新菜单排序(PUT /rbac/menu/update-order)

4.7.8.1 接口请求示例

请求 /rbac/menu/update-order(PUT,Query 参数)

?id=31&order=20

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
4.7.8.2 接口业务逻辑
4.7.8.2.1 核心流程
  • 更新菜单排序值。
4.7.8.2.2 校验与异常
  • order 非法或菜单不存在时返回 ApplicationExceptionResult(400)。

5 客户域接口详细设计

本章导读:客户主账号、子账号 不走 /rbac/user/*。写法与 4 一致:每接口 请求示例 → 业务逻辑(核心流程 / 校验与异常)。5.1 为管理端 OMS-Customer OpenAPI(Swagger UI — OMS-Customer);5.2 为落实 1.1 / 2.2 / 1.10 / 1.12 的 目标接口(截至 OMS-Customer v1 可能尚未出现在同一 Swagger,落地后以 OpenAPI 为准)。须满足 2.2(子账号权限 ⊆ 主账号)。

5.1 客户资料(OMS-Customer)

在线文档:Swagger UI — OMS-Customer
机器可读:/admin/swagger/OMS-Customer/swagger.json(servers[0].url 一般为 https://api.nanafa.com/admin,下列路径相对该 base;以部署环境为准)。
鉴权:Authorization: Bearer {token}。

5.1.1 接口路径速查

接口名称 请求方式 路径(相对 admin base) 说明
客户列表 GET /customer/list Query:Page、PageSize、Keyword、Status、CustomerGroup、Salesman、CustomerService
客户详情 GET /customer/get Query:id(long)
新增客户 POST /customer/create Body:CreateCustomerInput
更新客户 POST /customer/update Body:UpdateCustomerInput
删除客户 POST /customer/delete Body:IdInput
重置客户密码 POST /customer/reset-password Body:ResetCustomerPasswordInput

5.1.2 与 1.4 t_customer / 1.2 t_auth_account 及 DTO 约定

  • 主键参数名:/customer/get 等 OMS-Customer 路径使用 Query id 表示 t_customer.Id;5.2 客户 RBAC 使用 customerId。二者同值(即客户主账号的 PrincipalId),异名为兼容既有 OpenAPI 与 RBAC 草案,勿与 user.Id 混淆(见 4.1.1)。
  • t_customer:创建/更新 Body 与列表、详情响应以 5.1.3~5.1.6 JSON 为准;settlementMethodCode / customerTypeCode 非空须 t_dict 有效(1.14);receivableFinanceUserId 非空须指向未删 user。
  • t_auth_account:手机、邮箱、口令(仅创建)、微信。管理端代客重置口令 走 /customer/reset-password(5.1.8)。若产品对客户主账号 / 子账号开放 /rbac/auth/change-password(4.2.3),则 自助改密 与之相同机制(SessionVersion+1,见 2),不要把「改密」理解成仅有 5.1.8 一条路。微信:路径 A 在 /customer/create 一次写入;路径 B 在 /customer/update 补写(2.3)。
  • CustomerStatus:0 禁用、1 正常、2 冻结。
  • role(字符串):写入 t_principal_role 的映射由实现约定,或与 roleIds(5.2.2)统一。

5.1.3 客户列表(GET /customer/list)

5.1.3.1 接口请求示例

请求 /customer/list(GET,Query 参数)

?Page=1&PageSize=30&Keyword=&Status=1&CustomerGroup=0&Salesman=&CustomerService=

响应

{
  "items": [
    {
      "id": 1001,
      "customerCode": "W2601",
      "customerName": "示例客户A",
      "companyName": "示例科技有限公司",
      "businessLicenseNo": "91110000MA01234567",
      "businessLicenseImageUrl": "https://cdn.example.com/license/w2601.jpg",
      "legalRepresentative": "张三",
      "website": "https://example.com",
      "settlementMethodCode": "settle_monthly",
      "settlementMethodName": "月结",
      "customerTypeCode": "type_enterprise",
      "customerTypeName": "企业客户",
      "appId": "app_oms_001",
      "receivableFinanceUserId": 88,
      "wechatAvatar": null,
      "wechatNickname": null,
      "email": "a@example.com",
      "phone": "13800138000",
      "balance": 0,
      "totalQuota": 100000,
      "usedQuota": 0,
      "status": 1,
      "remark": null,
      "salesman": "zhangsan",
      "customerService": "lisi",
      "customerGroup": 0,
      "role": "CustomerDefault",
      "lastLoginIp": "10.0.0.1",
      "lastLoginTime": "2026-05-08T10:00:00Z",
      "createTime": "2026-01-01T08:00:00Z",
      "updateTime": "2026-05-08T10:00:00Z"
    }
  ],
  "total": 1
}
5.1.3.2 接口业务逻辑
5.1.3.2.1 核心流程
  • 按 Keyword(客户编号/名称/手机等,实现约定)、Status、CustomerGroup、Salesman、CustomerService 过滤 t_customer(IsDeleted=0)。
  • 分页:Page 从 1 开始,PageSize 与 3 中 TablePro 约定对齐时可默认 30。
  • 列表项返回 1.4 业务列;settlementMethodCode/customerTypeCode 联 t_dict 填充 settlementMethodName/customerTypeName。
  • 联查 t_auth_account 展示 phone/email/微信/最后登录;role 可由 t_principal_role + role.Code 聚合为首角色或主角色(与实现一致即可,须在 OpenAPI 说明)。
5.1.3.2.2 校验与异常
  • 分页参数非法、排序字段不支持时返回 ApplicationExceptionResult(400)。
  • 未授权或 token 无效时 401(与网关一致)。

5.1.4 客户详情(GET /customer/get)

5.1.4.1 接口请求示例

请求 /customer/get(GET,Query 参数)

?id=1001

响应

{
  "id": 1001,
  "customerCode": "W2601",
  "customerName": "示例客户A",
  "companyName": "示例科技有限公司",
  "businessLicenseNo": "91110000MA01234567",
  "businessLicenseImageUrl": "https://cdn.example.com/license/w2601.jpg",
  "legalRepresentative": "张三",
  "website": "https://example.com",
  "settlementMethodCode": "settle_monthly",
  "settlementMethodName": "月结",
  "customerTypeCode": "type_enterprise",
  "customerTypeName": "企业客户",
  "appId": "app_oms_001",
  "receivableFinanceUserId": 88,
  "wechatAvatar": null,
  "wechatNickname": null,
  "email": "a@example.com",
  "phone": "13800138000",
  "balance": 0,
  "totalQuota": 100000,
  "usedQuota": 0,
  "status": 1,
  "remark": null,
  "salesman": "zhangsan",
  "customerService": "lisi",
  "customerGroup": 0,
  "role": "CustomerDefault",
  "lastLoginIp": "10.0.0.1",
  "lastLoginTime": "2026-05-08T10:00:00Z",
  "createTime": "2026-01-01T08:00:00Z",
  "createBy": "admin",
  "updateTime": "2026-05-08T10:00:00Z",
  "updateBy": "admin"
}
5.1.4.2 接口业务逻辑
5.1.4.2.1 核心流程
  • 按 id 加载 t_customer;不存在或已删则异常。
  • 拼接 t_auth_account(customer + PrincipalId=id)展示联系方式与登录信息。
  • 返回 1.4 业务列;settlementMethodCode/customerTypeCode 联 t_dict 填充 settlementMethodName/customerTypeName(见 5.1.4.1 响应示例)。
5.1.4.2.2 校验与异常
  • id 缺失、客户不存在或已删除时返回 ApplicationExceptionResult(400)。

5.1.5 新增客户(POST /customer/create)

5.1.5.1 接口请求示例

请求 /customer/create(POST,Body:CreateCustomerInput)

{
  "customerCode": "W2602",
  "customerName": "新客户B",
  "companyName": "新客户B有限公司",
  "businessLicenseNo": "91310115MA0ABCDEF0",
  "businessLicenseImageUrl": "https://cdn.example.com/license/w2602.jpg",
  "legalRepresentative": "李四",
  "website": "https://newb.example.com",
  "settlementMethodCode": "settle_prepaid",
  "customerTypeCode": "type_enterprise",
  "appId": "app_oms_002",
  "receivableFinanceUserId": 88,
  "phone": "13800138002",
  "email": "b@example.com",
  "password": "Initial#123",
  "wechatOpenid": "oXXXX_先微信再开户",
  "wechatUnionid": null,
  "wechatAvatar": "https://thirdwx.qlogo.cn/...",
  "wechatNickname": "客户微信昵称",
  "balance": 0,
  "totalQuota": 50000,
  "usedQuota": 0,
  "status": 1,
  "remark": "线下签约",
  "salesman": "zhangsan",
  "customerService": "lisi",
  "customerGroup": 0,
  "role": "CustomerDefault"
}

响应

OpenAPI 200 无 schema;实现可返回新建 id 或 CustomerOutput,建议与 /customer/get 对齐便于前端刷新。

{
  "id": 1002
}
5.1.5.2 接口业务逻辑
5.1.5.2.1 核心流程
  • 校验 customerCode 唯一(IsDeleted=0)、phone/email 全局唯一(1.2);settlementMethodCode/customerTypeCode、receivableFinanceUserId 校验见 5.1.2。
  • 插入 t_customer(1.4 业务列与 Body 一致,含工商、字典码、AppId、ReceivableFinanceUserId 等)。
  • 创建 t_auth_account:PrincipalType=customer,PrincipalId=新客户Id,写入 PasswordHash/PasswordSalt、Phone/Email、微信列(路径 A 见 2.3,Body 可含 wechatOpenid 等);Status 与客户状态策略一致(禁用客户则认证行禁用)。
  • role:按 5.1.2 约定写入 t_principal_role(仅 customer 主体),角色 AllowedPrincipalTypes 须含 customer。
  • 写 t_operation_log(业务 ADD 快照或字段级,模块 customer)。
  • PermissionRevision:若创建即绑定角色,主账号认证行 PermissionRevision+1(1.1.6)。
5.1.5.2.2 校验与异常
  • 必填缺失、编号/手机/邮箱冲突、微信 OpenID/UnionID 冲突、密码策略不满足、字典码无效、应收财务用户不存在、角色编码不存在或不允许 customer 主体时返回 ApplicationExceptionResult(400)。

5.1.6 更新客户(POST /customer/update)

5.1.6.1 接口请求示例

请求 /customer/update(POST,Body:UpdateCustomerInput)

{
  "id": 1002,
  "customerCode": "W2602",
  "customerName": "新客户B(更名)",
  "companyName": "新客户B有限公司",
  "businessLicenseNo": "91310115MA0ABCDEF0",
  "businessLicenseImageUrl": "https://cdn.example.com/license/w2602.jpg",
  "legalRepresentative": "李四",
  "website": "https://newb.example.com",
  "settlementMethodCode": "settle_monthly",
  "customerTypeCode": "type_enterprise",
  "appId": "app_oms_002",
  "receivableFinanceUserId": 90,
  "phone": "13800138002",
  "email": "b-new@example.com",
  "wechatOpenid": "oXXXX_后绑微信",
  "wechatUnionid": null,
  "wechatAvatar": "https://thirdwx.qlogo.cn/...",
  "wechatNickname": "客户微信昵称",
  "balance": 1000,
  "totalQuota": 80000,
  "usedQuota": 0,
  "status": 1,
  "remark": "已续约",
  "salesman": "zhangsan",
  "customerService": "wangwu",
  "customerGroup": 1,
  "role": "CustomerVIP"
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
5.1.6.2 接口业务逻辑
5.1.6.2.1 核心流程
  • 加载客户与认证行;部分字段未在 Body 出现时可保持原值(实现约定 PATCH 语义或全量替换,须在 OpenAPI 写清;上例为常见「可编辑字段全量提交」)。
  • 更新 t_customer;phone/email/微信 同步 t_auth_account(路径 B 补绑微信见 2.3),OpenID/UnionID 唯一性校验。
  • role 变更:覆盖更新 t_principal_role(customer 主体),成功后 PermissionRevision+1。
  • 若 status 变为禁用/冻结:可同步认证行 Status,是否立即 SessionVersion+1 由产品决定(建议冻结时 +1 踢下线)。
  • 写 t_operation_log(UPDATE)。
5.1.6.2.2 校验与异常
  • 客户不存在、编号/手机/邮箱冲突、微信 OpenID 已被占用、额度逻辑非法(如 usedQuota > totalQuota)、字典码无效、应收财务用户不存在、角色非法时返回 ApplicationExceptionResult(400)。

5.1.7 删除客户(POST /customer/delete)

5.1.7.1 接口请求示例

请求 /customer/delete(POST,Body:IdInput)

{
  "id": 1002
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
5.1.7.2 接口业务逻辑
5.1.7.2.1 核心流程
  • 逻辑删除 t_customer(IsDeleted=1);逻辑删除或禁用 t_auth_account;清理/失效 refresh(SessionVersion+1 或等价吊销)。
  • 软删 t_principal_role 相关行(或保留历史由 IsDeleted 表达,与实现统一即可)。
  • 若存在 t_customer_sub_account:须先业务校验(禁止删主客户或级联策略由产品定)。
  • 写 t_operation_log(DELETE 或 RBAC 相关动作码)。
5.1.7.2.2 校验与异常
  • 客户不存在、存在未处理子账号或业务禁止删除时返回 ApplicationExceptionResult(400)。

5.1.8 重置客户密码(POST /customer/reset-password)

5.1.8.1 接口请求示例

请求 /customer/reset-password(POST,Body:ResetCustomerPasswordInput)

{
  "id": 1001,
  "newPassword": "Reset#2026"
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
5.1.8.2 接口业务逻辑
5.1.8.2.1 核心流程
  • 定位 t_auth_account(customer + PrincipalId=id)。
  • 更新 PasswordHash/PasswordSalt;SessionVersion+1,使已有 refresh 失效(见 2)。
  • 写 t_operation_log(auth_password_change 或管理端重置专用动作码,TargetPrincipalType=customer)。
5.1.8.2.2 校验与异常
  • 客户不存在、认证行不存在、新密码不满足策略时返回 ApplicationExceptionResult(400)。
  • 操作者须具备客户管理权限(PermissionCode 由实现注册在 t_permission_definition)。

5.2 客户域 RBAC(主账号 / 子账号 / 白名单)— 目标接口

说明:下列路径在 OMS-Customer v1 OpenAPI 中可能尚未出现;请求/响应字段以未来 OpenAPI 为准,本节为与 1.1、2.2 对齐的 规范草案。子账号 MainCustomerId、白名单 t_customer_sub_permission 见 1.5、1.12。

5.2.1 接口路径速查

接口名称 请求方式 请求路径 说明
主账号分配角色 POST /customer/role/assign 覆盖写主账号 t_principal_role;PermissionRevision+1;子账号权限 ⊆ 主校验
主账号角色列表 GET /customer/role/list 主账号已绑角色
子账号列表 GET /customer/sub-account/list 某主客户下子账号分页
创建子账号 POST /customer/sub-account/create t_customer_sub_account + t_auth_account
更新子账号 POST /customer/sub-account/update 状态/认证信息等
删除子账号 POST /customer/sub-account/delete 软删 + 吊销登录态
子账号分配角色 POST /customer/sub-account/assign-roles (P_{\text{子}}\subseteq P_{\text{主}})
子账号配置白名单 POST /customer/sub-account/assign-permissions 覆盖 t_customer_sub_permission
子账号有效权限预览 GET /customer/sub-account/effective-permissions 聚合权限码

5.2.2 主账号分配角色(POST /customer/role/assign)

5.2.2.1 接口请求示例

请求 /customer/role/assign(POST,Body)

{
  "customerId": 1001,
  "roleIds": [200, 201]
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
5.2.2.2 接口业务逻辑
5.2.2.2.1 核心流程
  • 校验客户存在且 PrincipalType=customer 对应认证行有效。
  • 校验每个 roleId:role.AllowedPrincipalTypes 含 customer,且 role 未删除。
  • 事务内:计算主账号新权限并集 (P_{\text{主}}^{\text{new}});对所有 t_customer_sub_account(MainCustomerId=customerId,未删)计算当前 (P_{\text{子}}),须满足 (P_{\text{子}}\subseteq P_{\text{主}}^{\text{new}})(2.2);否则整单回滚 400。
  • 覆盖写 t_principal_role(PrincipalType=customer,PrincipalId=customerId)。
  • 主账号 t_auth_account.PermissionRevision+1。
  • t_operation_log:Action=principal_role_assign,TargetPrincipalType=customer,TargetPrincipalId=customerId,DetailJson 含 roleIds。
5.2.2.2.2 校验与异常
  • 客户不存在、角色非法、子账号超限、空 roleIds 是否允许(产品定)时返回 ApplicationExceptionResult(400)。

5.2.3 主账号角色列表(GET /customer/role/list)

5.2.3.1 接口请求示例

请求 /customer/role/list(GET,Query)

?customerId=1001

响应

[
  {
    "id": 200,
    "name": "客户默认角色",
    "code": "CustomerDefault"
  }
]
5.2.3.2 接口业务逻辑
5.2.3.2.1 核心流程
  • 查 t_principal_role(customer + PrincipalId=customerId,IsDeleted=0),联 role 表返回 id/name/code。
5.2.3.2.2 校验与异常
  • customerId 无效时返回 ApplicationExceptionResult(400)。

5.2.4 子账号列表(GET /customer/sub-account/list)

5.2.4.1 接口请求示例

请求 /customer/sub-account/list(GET,Query)

?mainCustomerId=1001&Page=1&PageSize=30&Keyword=

响应

{
  "total": 2,
  "items": [
    {
      "id": 5001,
      "mainCustomerId": 1001,
      "subAccountCode": "W2601_01",
      "subStatus": 1,
      "phone": "13900139001",
      "email": "sub1@example.com",
      "createTime": "2026-03-01T08:00:00Z"
    }
  ]
}
5.2.4.2 接口业务逻辑
5.2.4.2.1 核心流程
  • 过滤 t_customer_sub_account:MainCustomerId=mainCustomerId,IsDeleted=0;Keyword 可匹配编号/手机(联认证表)。
  • 返回分页 total + items(与 TablePro 约定一致)。
5.2.4.2.2 校验与异常
  • 主客户不存在、分页参数非法时返回 ApplicationExceptionResult(400)。

5.2.5 创建子账号(POST /customer/sub-account/create)

5.2.5.1 接口请求示例

请求 /customer/sub-account/create(POST,Body)

{
  "mainCustomerId": 1001,
  "subAccountCode": "W2601_02",
  "phone": "13900139002",
  "email": "sub2@example.com",
  "password": "Sub#123456",
  "wechatOpenid": null,
  "wechatUnionid": null,
  "wechatAvatar": null,
  "wechatNickname": null,
  "roleIds": [210]
}

subAccountCode 可省略,由规则「主客户编号 + _ + 序号」生成(1.5)。wechatOpenid/wechatUnionid/wechatAvatar/wechatNickname:路径 A 可与本单创建一并提交(2.3);路径 B 留 /customer/sub-account/update。

响应

{
  "id": 5002,
  "subAccountCode": "W2601_02"
}
5.2.5.2 接口业务逻辑
5.2.5.2.1 核心流程
  • 校验主客户存在;phone/email 在 t_auth_account 全局唯一。
  • 插入 t_customer_sub_account(SubStatus 默认正常)。
  • 创建 t_auth_account:PrincipalType=customer_sub,PrincipalId=子账号Id,口令、联系方式与可选 微信字段(1.2)写入。
  • roleIds:写入 t_principal_role(customer_sub),角色须 AllowedPrincipalTypes 含 customer_sub;事务内校验 (P_{\text{子}}\subseteq P_{\text{主}})(1.1.3、2.2)。
  • 子账号认证行 PermissionRevision 初值与后续 +1 规则同 1.1.6。
  • t_operation_log:子账号创建(业务 ADD 或 RBAC 组合动作)。
5.2.5.2.2 校验与异常
  • 主客户不存在、编号重复、手机邮箱冲突、微信 OpenID/UnionID 冲突、角色非法、子权限超出主账号时返回 ApplicationExceptionResult(400)。

5.2.6 更新子账号(POST /customer/sub-account/update)

5.2.6.1 接口请求示例

请求 /customer/sub-account/update(POST,Body)

{
  "id": 5002,
  "subStatus": 0,
  "phone": "13900139022",
  "email": "sub2-new@example.com",
  "wechatOpenid": "oXXXX_后绑微信",
  "wechatUnionid": null,
  "wechatAvatar": "https://thirdwx.qlogo.cn/...",
  "wechatNickname": "子账号微信昵称"
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
5.2.6.2 接口业务逻辑
5.2.6.2.1 核心流程
  • 更新 t_customer_sub_account(如 SubStatus)。
  • phone/email/微信 变更同步 t_auth_account(路径 B 见 2.3),OpenID/UnionID 唯一;若禁用子账号可 SessionVersion+1。
  • 若变更影响权限边界(如后续版本增加字段),须重新校验 (P_{\text{子}}\subseteq P_{\text{主}})。
  • t_operation_log(UPDATE)。
5.2.6.2.2 校验与异常
  • 子账号不存在、越权(非该主客户下)、联系方式冲突、微信 OpenID 已被占用时返回 ApplicationExceptionResult(400)。

5.2.7 删除子账号(POST /customer/sub-account/delete)

5.2.7.1 接口请求示例

请求 /customer/sub-account/delete(POST,Body)

{
  "id": 5002
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
5.2.7.2 接口业务逻辑
5.2.7.2.1 核心流程
  • 软删 t_customer_sub_account;软删/禁用认证行;SessionVersion+1 吊销 token。
  • 软删 t_principal_role、t_customer_sub_permission 相关行(约定与 1.12 一致)。
  • t_operation_log。
5.2.7.2.2 校验与异常
  • 子账号不存在或已删除时返回 ApplicationExceptionResult(400)。

5.2.8 子账号分配角色(POST /customer/sub-account/assign-roles)

5.2.8.1 接口请求示例

请求 /customer/sub-account/assign-roles(POST,Query + Body)

?subAccountId=5001
[210, 211]

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
5.2.8.2 接口业务逻辑
5.2.8.2.1 核心流程
  • 解析 subAccountId 归属 MainCustomerId,加载主账号有效权限 (P_{\text{主}})。
  • 事务内:由 roleIds 得 (P_{\text{role}});若有白名单 t_customer_sub_permission 未删行,则 (P = P_{\text{role}} \cap P_{\text{whitelist}}),否则 (P = P_{\text{role}})(1.1.3)。校验 (P \subseteq P_{\text{主}})。
  • 覆盖写 t_principal_role(customer_sub + PrincipalId=subAccountId)。
  • 子账号 PermissionRevision+1。
  • t_operation_log:principal_role_assign,TargetPrincipalType=customer_sub,TargetPrincipalId=subAccountId。
5.2.8.2.2 校验与异常
  • 子账号不存在、角色不适用 customer_sub、权限超出主账号时返回 ApplicationExceptionResult(400)。

5.2.9 子账号配置白名单(POST /customer/sub-account/assign-permissions)

5.2.9.1 接口请求示例

请求 /customer/sub-account/assign-permissions(POST,Body)

{
  "subAccountId": 5001,
  "permissionCodes": [
    "order:list",
    "order:create"
  ]
}

响应

{
  "code": 0,
  "msg": "success",
  "data": null
}
5.2.9.2 接口业务逻辑
5.2.9.2.1 核心流程
  • 每个 permissionCodes 元素须 ∈ t_permission_definition.Code 且未删除。
  • 事务内:令白名单集合为 (W);角色并集 (P_{\text{role}});最终 (P = P_{\text{role}} \cap W)(若 permissionCodes 为空 是否表示「无白名单」= 仅用角色并集,产品须二选一并写清 OpenAPI;推荐:空数组 = 清除白名单行,回退角色并集)。
  • 校验 (P \subseteq P_{\text{主}})。
  • 覆盖写 t_customer_sub_permission(软删旧行 + 插入新行,或等价单表维护)。
  • 子账号 PermissionRevision+1。
  • t_operation_log:sub_whitelist_assign,DetailJson 含码表。
5.2.9.2.2 校验与异常
  • 未注册权限码、超出主账号权限、子账号不存在时返回 ApplicationExceptionResult(400)。

5.2.10 子账号有效权限预览(GET /customer/sub-account/effective-permissions)

5.2.10.1 接口请求示例

请求 /customer/sub-account/effective-permissions(GET,Query)

?subAccountId=5001

响应

{
  "rolePermissionCodes": ["order:list", "order:create", "order:export"],
  "afterWhitelist": ["order:list", "order:create"],
  "mainCustomerMax": ["order:list", "order:create", "order:export", "finance:view"]
}

afterWhitelist 为最终用于 API 鉴权的集合 (P);字段命名实现可改为 effectivePermissionCodes。

5.2.10.2 接口业务逻辑
5.2.10.2.1 核心流程
  • 计算 (P_{\text{role}})、白名单交、(P_{\text{主}}),返回便于联调与排错的三元组(或最小返回 effectivePermissionCodes)。
5.2.10.2.2 校验与异常
  • 子账号不存在、调用者非主客户管理员时返回 ApplicationExceptionResult(400)。