2612 lines
107 KiB
Markdown
2612 lines
107 KiB
Markdown
# 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 与菜单。
|
||
|
||
```mermaid
|
||
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. 共用规则**
|
||
|
||
4. **系统自动操作**
|
||
- **`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](http://api.nanafa.com/admin/index.html?urls.primaryName=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
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
> 无请求参数,基于当前登录令牌解析用户身份。
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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 识别主体。
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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)
|
||
> 无请求参数。
|
||
**响应**
|
||
```json
|
||
[
|
||
"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)
|
||
> 无请求参数。
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
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
|
||
```
|
||
```json
|
||
[
|
||
"rbac:user:get-list",
|
||
"rbac:role:get-list"
|
||
]
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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)
|
||
> 无请求参数。
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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)
|
||
```json
|
||
{
|
||
"name": "运营",
|
||
"code": "Operator",
|
||
"allowedPrincipalTypes": "user",
|
||
"permissions": [
|
||
"rbac:user:get-list",
|
||
"rbac:role:get-list"
|
||
]
|
||
}
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
```json
|
||
{
|
||
"name": "运营A"
|
||
}
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
```json
|
||
[
|
||
"rbac:user:get-list",
|
||
"rbac:role:get-list"
|
||
]
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
[
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
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
|
||
```
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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)
|
||
> 无请求参数。
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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)
|
||
> 无请求参数。
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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)
|
||
```json
|
||
{
|
||
"parentId": 10,
|
||
"name": "运营部",
|
||
"code": "OP",
|
||
"type": "Department",
|
||
"leaderId": 1,
|
||
"order": 10,
|
||
"description": "运营部门",
|
||
"isEnabled": true
|
||
}
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
```json
|
||
{
|
||
"name": "运营中心",
|
||
"type": "Department",
|
||
"leaderId": 1,
|
||
"order": 20,
|
||
"description": "运营中心",
|
||
"isEnabled": true
|
||
}
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
```json
|
||
{
|
||
"newParentId": 10
|
||
}
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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)
|
||
```json
|
||
{
|
||
"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** 绑定。
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
```json
|
||
{
|
||
"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。
|
||
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
```json
|
||
[
|
||
20,
|
||
21
|
||
]
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
[
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
```json
|
||
{
|
||
"oldPassword": "123456",
|
||
"newPassword": "new123456"
|
||
}
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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)
|
||
> 无请求参数。
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
[
|
||
{
|
||
"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)
|
||
```json
|
||
{
|
||
"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": "用户管理菜单"
|
||
}
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
```json
|
||
{
|
||
"name": "用户中心",
|
||
"permissionCode": "rbac:user:get-list",
|
||
"icon": "User",
|
||
"route": "/system/user",
|
||
"component": "system/user/index",
|
||
"isHidden": false,
|
||
"isCached": true,
|
||
"description": "用户中心菜单"
|
||
}
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
**响应**
|
||
```json
|
||
{
|
||
"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](http://api.nanafa.com/admin/index.html?urls.primaryName=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](http://api.nanafa.com/admin/index.html?urls.primaryName=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=
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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`)
|
||
|
||
```json
|
||
{
|
||
"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`** 对齐便于前端刷新。
|
||
|
||
```json
|
||
{
|
||
"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`)
|
||
|
||
```json
|
||
{
|
||
"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"
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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`)
|
||
|
||
```json
|
||
{
|
||
"id": 1002
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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`)
|
||
|
||
```json
|
||
{
|
||
"id": 1001,
|
||
"newPassword": "Reset#2026"
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
```json
|
||
{
|
||
"customerId": 1001,
|
||
"roleIds": [200, 201]
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
[
|
||
{
|
||
"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=
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
```json
|
||
{
|
||
"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`**。
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
```json
|
||
{
|
||
"id": 5002,
|
||
"subStatus": 0,
|
||
"phone": "13900139022",
|
||
"email": "sub2-new@example.com",
|
||
"wechatOpenid": "oXXXX_后绑微信",
|
||
"wechatUnionid": null,
|
||
"wechatAvatar": "https://thirdwx.qlogo.cn/...",
|
||
"wechatNickname": "子账号微信昵称"
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
```json
|
||
{
|
||
"id": 5002
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
|
||
```json
|
||
[210, 211]
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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)
|
||
|
||
```json
|
||
{
|
||
"subAccountId": 5001,
|
||
"permissionCodes": [
|
||
"order:list",
|
||
"order:create"
|
||
]
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"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)。
|
||
|