front/docs/rbac系统设计-060801.md
zzh 5a26ae0048 feat: 小红书模块迁移、登录重构与 xhs-agent-desktop 桌面 Agent
- 新增小红书前端模块(启动服务、内容分析、爆款创作、发布记录、技能卡片、拟人设置、AI 助手)

- 新增 xhs-agent-desktop 独立桌面 Agent,对接 OMS 任务调度

- 登录页重构,整合微信登录与 OMS JWT 认证

- 订单、渠道、用户中心等页面与 API 增强

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 16:13:13 +08:00

40 KiB
Raw Permalink Blame History

RBAC 与统一认证 — 产品需求文档(PRD)

代码基线:C:\Users\wilso\Desktop\SimpleAdmin.Api-master(后端)+ E:\oms0414(前端 OpenAPI 客户端)
RBAC 内核:NuGet 包 Fake.Rbac.Application / Fake.Rbac.Infrastructure v8.0.5.11
API 前缀:/rbac/*(RBAC 管理)、/customer/*(OMS 客户资料,含 RBAC 联动)


导读

这份文档解决什么问题?

系统当前实现要搞清楚四件事:

  1. 谁能登录 — 内部用户(管理端)与客户(OMS 端)共用 RBAC user 表;客户登录凭证存 t_customer,通过 user.userid 与 RBAC 关联。
  2. 登录之后凭什么进业务 — JWT Access Token + Refresh Token;Claims 含 mapId(OMS 整型用户编号)与 loginType(0=内部用户,1=客户)。
  3. 能操作哪些功能 — 用户有效权限 = 其所有角色在 role_permission 中权限码的并集;子客户通过专属角色 CustomerSub_{userId} 收窄权限;菜单按权限码裁剪展示,不替代 API 鉴权。
  4. 客户与 RBAC 如何联动 — 创建客户时同步创建 RBAC 用户、分配 Customer 角色;创建子客户时创建专属角色并校验权限不超过主客户。

建议阅读路径

角色 建议路径
后端 / DBA 2 概念模型 → 3 表结构 → 8 接口详细设计
前端 / BFF 4 认证与令牌 → 7 接口速查 → 9 前端集成
产品 / 测试 2 → 5 权限模型 → 7 → 8.7 客户接口

章节一览

章 内容
1 系统架构
2 概念模型
3 数据表结构
4 认证与令牌
5 权限模型
6 枚举与常量
7 接口路径速查
8 接口详细设计
9 前端集成
10 种子数据与默认账号

1 系统架构

1.1 整体分层

SimpleAdmin.Api(宿主)
├── Fake.Rbac.Application      # User/Role/Menu/Permission/Organization 服务 + DTO + JWT
├── Fake.Rbac.Infrastructure   # FakeRbacDbContext、EF 配置、仓储、种子数据
├── Fake.Rbac.Domain           # 聚合根、AccountManager、权限定义 Provider
└── Oms.Application            # CustomerService:客户资料 + RBAC 用户/角色生命周期
  • API 生成方式:继承 ApplicationService 的服务由 Fake 框架自动映射为 Controller(ApplicationServices2Controller)。
  • RBAC 路由根路径:rbac(见 SimpleAdminApiModule.cs)。
  • 自定义认证:宿主项目 AuthService 替换包内默认 AuthService,补充 OMS 双端登录。
  • 数据库:RBAC 表由 EF Core 管理(MySQL);OMS 业务表由 SqlSugar 管理(db.sql)。

1.2 双 ID 体系(关键)

标识 类型 用途
user.Id GUID RBAC 主键;JWT sub;服务层用户标识
user.userid INT 自增 OMS 桥接 ID;t_customer.UserId 外键;JWT Claim mapId
  • 内部 API 操作用户时传 GUID(id 查询参数)。
  • OMS 业务侧(订单、客户上下文)用 CurrentUser.GetId<int>() 读取 mapId。

1.3 中间件管道

UseForwardedHeaders → UsePathBase → UseFakeSwagger → UseFakeExceptionHandling
→ UseCors → UseStaticFiles → UseRouting → UseAuthentication → UseAuthorization → MapControllers
  • 当前无细粒度权限拦截器;接口层主要校验 Bearer Token 是否有效([Authorize])。
  • 细粒度 [Authorize(Permission = "...")] 在包内标记为待实现。

2 概念模型

2.1 三层分工

层次 回答的问题 核心对象
身份(Authentication) 你是谁?凭证是否有效? user(内部用户)/ t_customer(客户登录凭证)
授权(Authorization) 你被允许执行哪些 PermissionCode? user_role → role → role_permission
展示(UI) 控制台显示哪些菜单? menu(每节点绑定 PermissionCode,按有效权限裁剪)

2.2 有效权限码集合 P 的计算

记用户绑定的所有角色,其 role_permission.PermissionCode 并集为 P:

  • 内部用户:P = ⋃ role_permission(该用户所有角色)。
  • 客户主账号:创建时分配 Customer 角色;P 由该角色(及后续分配的其他角色)决定。
  • 子客户:创建时新建角色 CustomerSub_{userId},创建入参 permissions[] 直接写入该角色的 role_permission;保存前校验 子客户权限 ⊆ 主客户当前权限。

注意:当前实现没有 customer_sub_permission 白名单表;子客户权限通过专属角色表达,而非与角色权限求交。

2.3 登录主体类型

OmsLoginType 值 说明
User 0 内部用户;校验 RBAC user.Account + 哈希密码
Customer 1 客户;校验 t_customer 手机/邮箱/客户编号 + MD5 密码,再映射到 RBAC GUID

2.4 关联关系图

flowchart LR
  subgraph RBAC
    U[user]
    UR[user_role]
    R[role]
    RP[role_permission]
    M[menu]
    O[organization]
  end
  subgraph OMS
    C[t_customer]
  end
  U -->|userid INT| C
  U --> UR --> R --> RP
  RP --> P["有效权限码 P"]
  P --> API["API 鉴权(当前主要为登录态)"]
  P --> M
  U -->|OrganizationId| O

3 数据表结构

RBAC 表名在 MySQL 中为小写;由 Fake.Rbac.Infrastructure EF Migration 创建。
OMS 客户表在 db.sql 中定义,表名保留 t_ 前缀。

3.1 user — RBAC 用户表

字段名 类型 约束 说明
Id CHAR(36) PK GUID 主键
userid INT 自增,唯一 OMS 桥接编号,对应 t_customer.UserId
Name VARCHAR NOT NULL 显示名称
Account VARCHAR NOT NULL,唯一索引 IX_user_Account 登录账号
Password VARCHAR NOT NULL 密码哈希(EncryptPassword 值对象)
Salt VARCHAR NOT NULL 密码盐
Email VARCHAR 可空,唯一索引 IX_user_Email 邮箱
Avatar VARCHAR 可空 头像 URL
OrganizationId CHAR(36) 可空,索引 IX_user_OrganizationId 所属组织
ConcurrencyStamp CHAR(36) NOT NULL 乐观并发
CreateUserId INT NOT NULL 创建人
CreateTime DATETIME NOT NULL 创建时间
UpdateUserId INT 可空 更新人
UpdateTime DATETIME 可空 更新时间
IsDeleted TINYINT NOT NULL,默认 0 软删除

说明:

  • 内部用户:账号、密码、姓名、邮箱均存此表。
  • 客户:RBAC 用户行由 CustomerService 创建(Account 默认用手机号),登录密码以 t_customer.Password(MD5)为准。

3.2 role — 角色表

字段名 类型 约束 说明
Id CHAR(36) PK GUID 主键
Name VARCHAR NOT NULL 角色名称,如「客户」「子客户-xxx」
Code VARCHAR NOT NULL,唯一索引 IX_role_Code 角色编码,如 Customer、CustomerSub_123
ConcurrencyStamp CHAR(36) NOT NULL 乐观并发
CreateUserId / CreateTime — 审计
UpdateUserId / UpdateTime — 可空
IsDeleted TINYINT 默认 0 软删除

内置角色:

Code Name 来源
Customer 客户 CustomerRoleSeedContributor 启动种子
CustomerSub_{userId} 子客户-{名称} CustomerService.CreateSubCustomerAsync 动态创建

3.3 user_role — 用户角色关联表

字段名 类型 约束 说明
Id CHAR(36) PK
UserId CHAR(36) FK → user.Id
RoleId CHAR(36) FK → role.Id
CreateUserId INT 审计
CreateTime DATETIME 审计

唯一索引:IX_user_role_UserId_RoleId

3.4 role_permission — 角色权限关联表

字段名 类型 约束 说明
Id CHAR(36) PK
RoleId CHAR(36) FK → role.Id
PermissionCode VARCHAR NOT NULL 权限码字符串
CreateUserId INT 审计
CreateTime DATETIME 审计

唯一索引:IX_role_permission_RoleId_PermissionCode

无 permission_definition 数据库表。权限码权威定义在代码 PermissionDefinitionProvider 中,分配时以字符串存入本表。

3.5 menu — 菜单表

字段名 类型 约束 说明
Id CHAR(36) PK
PId CHAR(36) NOT NULL 父菜单 ID;00000000-0000-0000-0000-000000000000 表示根
Name VARCHAR NOT NULL 菜单名称
PermissionCode VARCHAR 可空,索引 IX_menu_PermissionCode 关联权限码
Type INT NOT NULL MenuType:1=菜单,2=按钮
Icon VARCHAR 可空 图标
Route VARCHAR 可空 前端路由 path
Component VARCHAR 可空 前端组件路径
Order INT NOT NULL 排序
IsHidden TINYINT 默认 0 是否隐藏
IsCached TINYINT 默认 0 是否缓存(keep-alive)
Description VARCHAR 可空 描述
审计 + IsDeleted — 标准软删除

用户可见菜单算法(AuthService / MenuService):

  1. 取用户有效权限码集合 P。
  2. 选出 menu.PermissionCode ∈ P 的节点。
  3. 递归补齐所有祖先节点(保证树结构完整)。
  4. 按 Order 构建树返回。

3.6 organization — 组织表

字段名 类型 约束 说明
Id CHAR(36) PK
ParentId CHAR(36) 可空 父组织;空或零 GUID 为根
Name VARCHAR NOT NULL 组织名称
Code VARCHAR NOT NULL,唯一索引 IX_organization_Code 组织编码
Type INT NOT NULL OrganizationType:1/2/3
LeaderId CHAR(36) 可空 负责人用户 GUID
Order INT 排序
Description VARCHAR 可空 描述
IsEnabled TINYINT 默认 1 是否启用
审计 + IsDeleted —

说明:组织仅关联内部用户 user.OrganizationId;客户不挂组织树。组织不是权限码,用于人事与数据范围等业务查询。

3.7 t_customer — OMS 客户表(桥接 RBAC)

字段名 类型 约束 说明
Id INT PK,自增 客户主键
ParentCustomerId INT 可空 NULL=主客户;有值=子客户
UserId INT NOT NULL,唯一 uk_cus_user 关联 user.userid
CustomerCode VARCHAR(32) NOT NULL,唯一 客户编号
CustomerName VARCHAR(128) NOT NULL 客户名称
Phone VARCHAR(16) NOT NULL,唯一 登录账号(之一)
Email VARCHAR(64) 可空,唯一 登录账号(之一)
Password VARCHAR(64) NOT NULL MD5 加密,客户登录用
WechatAvatar / WechatNickname VARCHAR 可空 微信资料
WechatOpenid VARCHAR(64) 可空,唯一 微信 OpenID
Balance / TotalQuota / UsedQuota DECIMAL(18,2) 账户额度
Status TINYINT 默认 1 0=禁用,1=正常,2=冻结
CustomerGroup TINYINT 可空 0=普通,1~5=VIP1~VIP5
Salesman / CustomerService VARCHAR(32) 可空 销售/客服
LastLoginIp / LastLoginTime — 可空 最后登录
Remark VARCHAR(512) 可空 备注
审计 + IsDeleted + ConcurrencyStamp —

主/子客户判定:

  • ParentCustomerId IS NULL → 主客户,可调用 /customer/sub/create 创建子客户。
  • ParentCustomerId IS NOT NULL → 子客户;登录时同时校验子客户与主客户状态。

4 认证与令牌

4.1 JWT 配置项

配置键 说明 示例
Jwt:SecretKey 签名密钥 生产从 Consul 加载
Jwt:Issuer 签发者
Jwt:Audience 受众
Jwt:ExpiryInMinutes Access Token 有效期(分钟) 60
Jwt:RefreshExpiryInDays Refresh Token 有效期(天) 7
Jwt:ClockSkewMinutes 时钟偏移容忍(分钟) 5

4.2 JWT Claims

Claim 类型 说明
标准 RBAC Claims — 由 JwtService.GenerateClaimsByUserIdAsync 生成(用户 ID、角色等)
mapId int 字符串 user.userid,OMS 业务上下文用
loginType int 字符串 0=内部用户,1=客户

自定义实现:SimpleJwtService 在生成 Claims 时注入 mapId;AuthService 在客户登录时覆盖 loginType=1。

4.3 登录流程

4.3.1 内部用户(type=0,管理端 /admin)

不支持微信登录。管理端仅提供账号 + 密码方式,登录页不展示微信扫码入口。

  1. POST /rbac/auth/login,body:{ account, password, type: 0 }
  2. AccountManager.ValidateCredentialsAsync 校验 user.Account + 哈希密码。
  3. 生成 Access Token + Refresh Token。
  4. 前端调用 GET /rbac/auth/get-current-user 拉取用户信息、角色、权限、菜单。

4.3.2 客户(type=1,客户端 /login)

客户端支持 账号密码 与 微信扫码 两种方式(见 4.3.4)。

  1. POST /rbac/auth/login,body:{ account, password, type: 1 }
  2. 按手机 → 邮箱 → 客户编号顺序查找 t_customer。
  3. 校验客户状态(子客户还需校验主客户状态)。
  4. 比对 t_customer.Password(MD5)。
  5. 通过 user.userid 查 RBAC GUID,生成 Token(loginType=1)。
  6. 更新 LastLoginIp、LastLoginTime。

4.3.3 刷新令牌

  1. POST /rbac/auth/refresh-token,body:{ refreshToken }
  2. 校验 Refresh Token 有效性,解析 loginType。
  3. 客户刷新:通过 mapId 重新查 GUID 并签发新 Token。
  4. 内部用户刷新:通过 GUID 查用户并签发新 Token。

4.3.4 客户微信扫码登录(仅客户端)

适用范围
登录页 路由 账号密码 微信扫码
管理端 /admin(adminLogin) ✅ ❌ 不支持
客户端 /login(login) ✅ ✅

前端实现:login/index.vue 根据路由 loginPortal 判定主体;仅 OmsLoginType.Customer 时渲染 WechatLogin 组件及密码页底部的微信入口按钮。

现状与依赖
层级 状态
数据表 t_customer 已有 WechatOpenid(唯一)、WechatAvatar、WechatNickname
仓储 ICustomerRepository.GetByWechatOpenidAsync 已实现
后端认证 未实现 微信 OAuth / 扫码登录接口;AuthService 仅支持密码登录
前端 wechat-login.vue 为占位 UI(假二维码 + 120s 倒计时),待对接后端
微信开放平台配置(网站应用扫码登录)

采用 微信开放平台 · 网站应用 · 微信登录 方案:

配置项 说明
AppID / AppSecret 网站应用凭证,存 Consul / 环境变量,禁止写入前端
授权回调域 与 OMS 客户门户同域,如 oms.example.com
业务 scope snsapi_login(扫码确认后换取 OpenID)
端到端流程
sequenceDiagram
    participant U as 客户浏览器
    participant F as OMS 前端 /login
    participant B as SimpleAdmin.Api
    participant W as 微信开放平台

    U->>F: 打开客户端登录页,切换「微信扫码」
    F->>B: GET /rbac/auth/wechat/qrcode
    B->>B: 生成 scene/ticket,写入 Redis(TTL 120s)
    B->>W: 拼装 oauth2/authorize URL(state=ticket)
    B-->>F: { ticket, qrUrl, expiresIn }
    F->>F: 渲染二维码(qrUrl)
    loop 每 2s 轮询,最长 120s
        F->>B: GET /rbac/auth/wechat/poll?ticket=xxx
        alt 未扫码 / 已扫码未确认
            B-->>F: { status: pending }
        else 用户取消
            B-->>F: { status: cancelled }
        else 扫码成功
            B-->>F: { status: success, accessToken, refreshToken, expiresIn }
            F->>F: 写入 Token,跳转 /user/profile
        end
    end
    Note over W,B: 用户扫码确认后,微信回调 B 的 redirect_uri
    W->>B: GET /rbac/auth/wechat/callback?code=xxx&state=ticket
    B->>W: code 换 access_token + openid
    B->>B: GetByWechatOpenidAsync(openid)
    alt OpenID 已绑定客户
        B->>B: 校验客户/主客户状态,签发 JWT(loginType=1)
        B->>B: 更新 LastLoginIp/Time、WechatAvatar/Nickname
        B->>B: 将 ticket 标记为 success + Token
    else OpenID 未绑定
        B->>B: ticket 标记为 unbound
    end

步骤说明:

  1. 申请二维码:前端调用 GET /rbac/auth/wechat/qrcode,后端生成唯一 ticket(即 OAuth state),返回可嵌入 <img> 的 qrUrl(微信 showqrcode 或自建二维码图)。
  2. 用户扫码:微信 App 打开授权页,用户确认登录。
  3. 服务端回调:微信重定向 GET /rbac/auth/wechat/callback?code&state;后端用 code 换 openid(及昵称、头像)。
  4. 客户匹配:openid → t_customer.WechatOpenid;命中则走与密码登录相同的 Token 签发逻辑(mapId = user.userid,loginType = 1)。
  5. 前端轮询:poll 接口在 ticket 变为 success 时返回 Token 对;前端复用现有 rbac-user store 落盘与跳转逻辑。
  6. 刷新二维码:ticket 过期或 cancelled 时,前端重新调用 qrcode 接口。
待实现 API 契约

1. 获取扫码 ticket

GET /rbac/auth/wechat/qrcode

响应:

{
  "ticket": "a1b2c3d4-...",
  "qrUrl": "https://open.weixin.qq.com/connect/qrconnect?appid=...&redirect_uri=...&response_type=code&scope=snsapi_login&state=a1b2c3d4-...#wechat_redirect",
  "expiresIn": 120
}

2. 轮询扫码结果

GET /rbac/auth/wechat/poll?ticket={ticket}
status 含义 前端行为
pending 等待扫码或确认 继续轮询
cancelled 用户取消授权 提示后展示刷新按钮
unbound OpenID 未绑定任何客户 提示「请联系管理员绑定微信」
disabled 客户或主客户已禁用/冻结 提示账号不可用
expired ticket 超时 自动刷新二维码
success 登录成功 取 accessToken / refreshToken 完成登录

success 时响应体与 POST /rbac/auth/login 一致(含 accessToken、refreshToken、expiresIn)。

3. 微信 OAuth 回调(浏览器重定向,非前端直接调用)

GET /rbac/auth/wechat/callback?code={code}&state={ticket}
  • 成功:更新 Redis ticket 状态,返回简单 HTML「登录成功,请关闭此页」或 302 回 /login?wechat=ok。
  • 失败:ticket 标记 unbound / error,记录日志。
绑定与账号规则
规则 说明
绑定主体 仅客户(t_customer);内部 user 表不存 WechatOpenid
绑定时机 管理员在「客户管理」创建/编辑客户时写入 WechatOpenid;或客户首次密码登录后在个人中心「绑定微信」(二期)
唯一性 一个 WechatOpenid 只能绑定一个客户;解绑需管理员操作
子客户 子客户可有独立 WechatOpenid;登录时同样校验子客户与主客户 Status
资料同步 扫码成功后可选更新 WechatAvatar、WechatNickname(以微信侧为准)
无密码登录 微信登录不校验 t_customer.Password;禁用客户不得仅凭历史 OpenID 登录
安全要求
  • AppSecret 仅存服务端;code 换 openid 必须在后端完成。
  • ticket / state 一次性使用,Redis TTL ≤ 120s,防重放。
  • 回调域与 redirect_uri 严格匹配微信开放平台配置。
  • 轮询接口限流(建议每 IP 每 ticket ≤ 1 次/2s)。
  • 日志记录 openid 哈希或末四位,避免明文落盘。
前端集成要点(oms0414)
文件 职责
views/login/index.vue 仅 login 路由展示 WechatLogin;adminLogin 强制 activeKey=pwd
views/login/components/pwd-login/index.vue 仅 OmsLoginType.Customer 显示底部微信图标
views/login/components/wechat-login.vue 对接 qrcode + poll;二维码用 qrUrl 或 qrcode 库渲染
store/modules/rbac-user success 后复用 setToken + fetchCurrentUser + 跳转 /user/profile

建议新增 src/api/rbac/wechat-auth.ts 封装上述两个 GET 接口。

实施阶段建议
阶段 内容
P0(当前) 管理端隐藏微信入口;客户端保留占位 UI;本文档定稿
P1 后端实现 qrcode / callback / poll + Redis;管理端录入 WechatOpenid
P2 前端 wechat-login.vue 实装轮询与错误态;个人中心「绑定/解绑微信」

4.4 前端 Token 策略(rbac-user store)

  • Access Token 存 localStorage(rbac_access_token)。
  • 到期前 5 分钟触发刷新(expiresIn - 300s)。
  • 1 小时无用户交互则停止自动续期(KEEP_ALIVE_IDLE_MS)。
  • 401 / 刷新失败 → 清除本地状态并跳转登录页。

4.5 密码策略

主体 存储 修改接口
内部用户 RBAC 哈希 + 盐 POST /rbac/auth/change-password(本人)/ PUT /rbac/user/update-password(管理员重置)
客户 t_customer.Password MD5 POST /rbac/auth/change-password(本人)/ POST /customer/reset-password(管理员重置)

客户改密:AccountManager.ValidatePasswordStrength 校验新密码强度。


5 权限模型

5.1 权限定义方式

  • 权限码在 代码 中通过 RbacPermissionDefinitionProvider 静态注册。
  • 运行时通过 GET /rbac/permission/get-all-permissions 或 get-permission-tree 获取目录。
  • 角色分配时调用 POST /rbac/role/assign-permissions,权限码写入 role_permission。
  • 不存在权限定义数据库表。

5.2 权限码命名规范

格式:{Module}.{Resource}.{Action}(PascalCase,点分隔)

RBAC 管理模块(包内定义)

权限码 含义
Rbac.Users 用户模块
Rbac.Users.Query 查询用户
Rbac.Users.Create 创建用户
Rbac.Users.Update 更新用户
Rbac.Users.Delete 删除用户
Rbac.Users.AssignRoles 分配角色
Rbac.Users.ResetPassword 重置密码
Rbac.Roles / .Query / .Create / .Update / .Delete / .AssignPermissions 角色 CRUD + 分配权限
Rbac.Menus / .Query / .Create / .Update / .Delete / .Move 菜单 CRUD + 移动
Rbac.Organizations / .Query / .Create / .Update / .Delete / .Move 组织 CRUD + 移动

业务模块(前端路由已使用,须在 Provider 中注册后方能分配)

模块前缀 示例
Api.* Api.Interfaces.Query、Api.Applications.Create
Track.* Track.Orders.Query、Track.Kpi.Query
Home.* Home.Monitor
System.* System.BatchEmail.Use

完整列表以 GET /rbac/permission/get-permission-tree 返回为准。

5.3 鉴权现状

机制 状态
[Authorize](需登录) ✅ 已启用
[Authorize(Permission = "...")] 细粒度 ❌ 包内待实现
POST /rbac/permission/check-permission ✅ API 可用,供显式校验
IUserService.HasPermissionAsync ✅ 服务方法可用
前端 hasPermission() ⚠️ 当前临时返回 true(src/utils/rbac-permission.ts)

5.4 菜单类型

MenuType 值 说明
Menu 1 页面菜单
Button 2 按钮权限

6 枚举与常量

6.1 OMS 集成(代码内)

名称 值 文件
OmsLoginType.User 0 AuthRequestDtos.cs
OmsLoginType.Customer 1 AuthRequestDtos.cs
SimpleOmsClaimTypes.MapId "mapId" SimpleOmsClaimTypes.cs
SimpleOmsClaimTypes.LoginType "loginType" SimpleOmsClaimTypes.cs
CustomerRoleCode "Customer" CustomerRoleSeedContributor.cs
CustomerRoleName "客户" CustomerRoleSeedContributor.cs
子客户角色 Code CustomerSub_{userId} CustomerService.cs
CustomerStatus.Disabled 0
CustomerStatus.Normal 1
CustomerStatus.Frozen 2
CustomerGroup.Normal 0
CustomerGroup.Vip1~Vip5 1~5
OrganizationType 1 / 2 / 3 OpenAPI 生成
MenuType.Menu 1
MenuType.Button 2

7 接口路径速查

7.1 认证 /rbac/auth/*

方法 路径 鉴权 说明
POST /rbac/auth/login 匿名 用户/客户登录
POST /rbac/auth/refresh-token 匿名 刷新令牌
GET /rbac/auth/get-current-user Bearer 当前用户信息(含权限、菜单)
POST /rbac/auth/change-password Bearer 当前用户改密
PUT /rbac/auth/update-profile Bearer(仅内部用户) 更新姓名、邮箱
POST /rbac/auth/upload-avatar Bearer(仅内部用户) 上传头像

7.2 用户 /rbac/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-password 重置密码
PUT /rbac/user/update-avatar 更新头像

7.3 角色 /rbac/role/*

方法 路径 说明
GET /rbac/role/get-list 分页列表
GET /rbac/role/get-all-roles 全部角色(简表)
GET /rbac/role/get 详情
POST /rbac/role/create 创建(可带初始 permissions)
PUT /rbac/role/update 更新名称
DELETE /rbac/role/delete 删除
POST /rbac/role/assign-permissions 分配权限码
GET /rbac/role/get-role-permissions 角色权限码列表
GET /rbac/role/get-role-users 角色下用户
GET /rbac/role/get-role-user-count 角色用户数量

7.4 菜单 /rbac/menu/*

方法 路径 说明
GET /rbac/menu/get-menu-tree 完整菜单树
GET /rbac/menu/get-user-menus 指定用户可见菜单
GET /rbac/menu/get 菜单详情
POST /rbac/menu/create 创建
PUT /rbac/menu/update 更新
PUT /rbac/menu/update-order 更新排序
POST /rbac/menu/move-menu 移动节点
DELETE /rbac/menu/delete 删除

7.5 权限 /rbac/permission/*

方法 路径 说明
GET /rbac/permission/get-all-permissions 全部权限定义(树形)
GET /rbac/permission/get-permission-tree 按分组返回权限树
POST /rbac/permission/check-permission 单权限校验
POST /rbac/permission/check-permissions 批量权限校验

7.6 组织 /rbac/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 移动节点

7.7 客户(OMS)/customer/*

方法 路径 说明
GET /customer/list 客户分页列表
GET /customer/get 客户详情
POST /customer/create 创建主客户(联动 RBAC)
POST /customer/sub/create 主客户创建子客户(联动 RBAC + 专属角色)
POST /customer/update 更新客户资料
POST /customer/delete 删除客户
POST /customer/reset-password 重置客户密码

8 接口详细设计

请求/响应字段以 OpenAPI 生成客户端 E:\oms0414\src\api\rbac\generated 为准。
分页接口统一返回 { total, items };单条删除/变更成功通常无 body 或返回更新后 DTO。

8.1 认证接口

8.1.1 POST /rbac/auth/login

请求体 OmsLoginInput

{
  "account": "admin",
  "password": "123456",
  "type": 0
}
字段 类型 必填 说明
account string 是 内部用户=账号;客户=手机/邮箱/客户编号
password string 是 明文密码
type int 否,默认 0 0=内部用户,1=客户

响应 LoginResultDto

{
  "accessToken": "eyJ...",
  "refreshToken": "eyJ...",
  "tokenType": "Bearer",
  "expiresIn": 3600,
  "userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}

业务逻辑

  1. 按 type 分流到 LoginAdminAsync 或 LoginCustomerAsync。
  2. 客户登录失败统一提示「客户账号或密码错误」(不暴露具体原因)。
  3. 子客户登录额外校验主客户未禁用/冻结。

异常

场景 错误信息
不支持的 type 不支持的登录类型
客户凭证错误 客户账号或密码错误
客户已禁用/冻结 客户已禁用 / 客户已冻结
子客户已禁用/冻结 子客户已禁用 / 子客户已冻结
未关联 RBAC 用户 客户未关联用户

8.1.2 POST /rbac/auth/refresh-token

请求体

{ "refreshToken": "eyJ..." }

响应:同 LoginResultDto。

8.1.3 GET /rbac/auth/get-current-user

响应 UserInfoDto

{
  "id": "guid",
  "name": "管理员",
  "account": "admin",
  "email": "admin@example.com",
  "avatar": "/uploads/avatars/xxx.jpg",
  "organizationId": "guid",
  "organizationName": "总公司",
  "roles": [{ "id": "guid", "name": "超级管理员", "code": "Admin" }],
  "permissions": ["Rbac.Users.Query", "Rbac.Roles.Query"],
  "menus": [{ "id": "guid", "pId": "00000000-0000-0000-0000-000000000000", "name": "系统管理", "permissionCode": "Rbac.Users", "type": 1, "route": "/system", "children": [] }],
  "createdAt": "2026-01-01T00:00:00Z",
  "createdBy": "system",
  "updatedAt": null,
  "updatedBy": null
}

8.1.4 POST /rbac/auth/change-password

请求体 OmsChangePasswordInput

{ "oldPassword": "123456", "newPassword": "654321" }
  • 内部用户:走 AccountManager.UpdatePasswordAsync。
  • 客户:校验 t_customer MD5 旧密码后更新。

8.1.5 PUT /rbac/auth/update-profile

请求体 OmsUpdateProfileInput:{ name?, email? }

  • 仅内部用户可用;客户调用返回「当前登录主体暂不支持此操作」。

8.1.6 POST /rbac/auth/upload-avatar

  • Content-Type:multipart/form-data,字段 file。
  • 支持 jpg/jpeg/png/gif/webp,最大 10MB,自动缩放至 200px。
  • 返回头像 URL 字符串,如 /uploads/avatars/{userId}_{timestamp}.jpg。

8.2 用户接口

8.2.1 GET /rbac/user/get-list

Query 参数

参数 类型 说明
Keyword string 搜索名称或账号
RoleId guid 按角色筛选
Page int 页码,从 1 开始
PageSize int 每页条数
OrderBy string 排序字段
Descending bool 是否降序

响应 UserDtoPagedResultDto

{
  "total": 100,
  "items": [
    {
      "id": "guid",
      "name": "张三",
      "account": "zhangsan",
      "email": "zs@example.com",
      "avatar": null,
      "roles": [{ "id": "guid", "name": "运营", "code": "Operator" }],
      "createdAt": "...",
      "createdBy": "...",
      "updatedAt": null,
      "updatedBy": null
    }
  ]
}

8.2.2 POST /rbac/user/create

请求体 UserCreateDto

{
  "name": "张三",
  "account": "zhangsan",
  "password": "123456",
  "email": "zs@example.com",
  "avatar": null,
  "roleIds": ["role-guid-1"]
}

8.2.3 PUT /rbac/user/update

  • Query:id(用户 GUID)
  • Body UserUpdateDto:{ name?, email?, avatar? }

8.2.4 POST /rbac/user/assign-roles

  • Query:userId(GUID)
  • Body:string[](角色 GUID 列表)

8.2.5 PUT /rbac/user/update-password

  • Query:id
  • Body UpdatePasswordDto:{ oldPassword, newPassword }

8.2.6 DELETE /rbac/user/delete / delete-batch

  • 单删 Query:id
  • 批删 Query:ids(数组)

8.3 角色接口

8.3.1 POST /rbac/role/create

请求体 RoleCreateDto

{
  "name": "运营专员",
  "code": "Operator",
  "permissions": ["Api.Interfaces.Query", "Api.Logs.Query"]
}

8.3.2 PUT /rbac/role/update

  • Query:id
  • Body RoleUpdateDto:{ name? }(仅可改名称)

8.3.3 POST /rbac/role/assign-permissions

  • Query:roleId
  • Body:string[](权限码列表,全量覆盖式分配)

8.3.4 GET /rbac/role/get-role-permissions

  • Query:roleId
  • 响应:string[]

8.4 菜单接口

8.4.1 POST /rbac/menu/create

请求体 MenuCreateDto

{
  "pId": "00000000-0000-0000-0000-000000000000",
  "name": "用户管理",
  "permissionCode": "Rbac.Users.Query",
  "type": 1,
  "icon": "icon-user",
  "route": "/system/users",
  "component": "views/system/users/index",
  "order": 10,
  "isHidden": false,
  "isCached": true,
  "description": null
}

8.4.2 PUT /rbac/menu/update

  • Query:id
  • Body MenuUpdateDto:{ name?, permissionCode?, icon?, route?, component?, isHidden?, isCached?, description?, order? }

8.4.3 POST /rbac/menu/move-menu

  • Query:menuId、targetParentId

8.5 权限接口

8.5.1 GET /rbac/permission/get-permission-tree

响应 PermissionGroupDto[]

[
  {
    "code": "Rbac",
    "name": "权限管理",
    "permissions": [
      {
        "code": "Rbac.Users",
        "name": "用户管理",
        "parentCode": null,
        "description": null,
        "children": [
          { "code": "Rbac.Users.Query", "name": "查询", "parentCode": "Rbac.Users", "children": [] }
        ]
      }
    ]
  }
]

8.5.2 POST /rbac/permission/check-permissions

  • Query:userId
  • Body:string[](待检查的权限码)
  • 响应:Record<string, boolean>,如 { "Rbac.Users.Query": true, "Rbac.Users.Delete": false }

8.6 组织接口

8.6.1 POST /rbac/organization/create

请求体 OrganizationCreateDto

{
  "parentId": null,
  "name": "技术部",
  "code": "TECH",
  "type": 1,
  "leaderId": null,
  "order": 1,
  "description": null,
  "isEnabled": true
}

8.6.2 POST /rbac/organization/move

  • Query:id
  • Body OrganizationMoveDto:目标父节点信息

8.7 客户接口(OMS + RBAC 联动)

8.7.1 POST /customer/create — 创建主客户

请求体 CreateCustomerInput(主要字段)

字段 类型 必填 说明
customerCode string(32) 是 客户编号,全局唯一
customerName string(128) 是 客户名称
phone string(16) 是 手机号,全局唯一
email string(64) 否 邮箱,全局唯一
password string 是,≥6 位 登录密码(OMS 侧 MD5 存储)
status int 否,默认 1 0/1/2
balance / totalQuota / usedQuota decimal 否 额度
salesman / customerService string 否 销售/客服
customerGroup int 否 客户分组

核心流程

  1. 校验客户编号、手机、邮箱唯一性。
  2. UserService.CreateAsync:创建 RBAC 用户(Account=phone,Name=customerName)。
  3. UserService.AssignRolesAsync:分配 Customer 角色。
  4. 读取 user.userid 写入 t_customer.UserId。
  5. 插入 t_customer(ParentCustomerId=null)。
  6. 任一步失败则回滚已创建的 RBAC 用户。

8.7.2 POST /customer/sub/create — 创建子客户

请求体:CreateSubCustomerInput = CreateCustomerInput + permissions: string[]

核心流程

  1. 当前登录主体必须是主客户(ParentCustomerId IS NULL)。
  2. 校验主客户状态正常。
  3. 校验 permissions 中每个码 ⊆ 主客户当前权限;否则「子客户权限不能超出当前客户权限」。
  4. 创建 RBAC 用户。
  5. 创建角色 CustomerSub_{userId},名称 子客户-{customerName},写入 permissions。
  6. 分配该专属角色给用户。
  7. 插入 t_customer(ParentCustomerId=主客户Id)。
  8. 失败时回滚角色和用户。

8.7.3 GET /customer/list

Query CustomerPaginatedInput

参数 说明
page 页码,默认 1
pageSize 每页条数,默认 20
keyword 匹配编号/名称/手机/邮箱
status 状态筛选
customerGroup 分组筛选
salesman / customerService 销售/客服筛选

响应:{ items: CustomerPaginatedOutput[], total: number }

8.7.4 POST /customer/reset-password

  • Body:{ id, newPassword }
  • 直接更新 t_customer.Password(MD5),不影响 RBAC 密码。

9 前端集成

9.1 API 客户端

  • 路径:src/api/rbac/generated/(由 OpenAPI 自动生成)。
  • 统一前缀:/rbac/*;Bearer Token 由 OpenAPI.TOKEN 注入。

9.2 状态管理(src/store/modules/rbac-user.ts)

状态 说明
accessToken / refreshToken JWT
userInfo UserInfoDto,含 roles、permissions、menus
loginPortalType 0=管理端,1=客户端
permissions 计算属性,来自 userInfo.permissions
menus 计算属性,来自 userInfo.menus

登录调用:AuthService.postRbacAuthLogin({ account, password, type })
拉用户信息:AuthService.getRbacAuthGetCurrentUser()

9.3 路由权限(meta.permission)

前端路由在 src/router/routes/newModules/ 配置 meta.permission,示例:

路由模块 permission 示例
系统管理 Rbac.Users.Query、Rbac.Roles.Query
接口管理 Api.Interfaces.Query
轨迹中心 Track.Orders.Query
首页 Home.Monitor

路由守卫 src/router/guard/permission.ts 读取 meta.permission 并调用 hasPermission()。

9.4 权限校验函数现状

src/utils/rbac-permission.ts 中 hasPermission / hasAnyPermission / hasAllPermissions 当前固定返回 true(临时放开菜单权限)。
恢复时应改回:

return useRbacUserStore().permissions.includes(permission)

9.5 动态菜单

  • 登录后 get-current-user 返回的 menus 即为服务端按权限裁剪后的菜单树。
  • 亦可调用 MenuService.getRbacMenuGetUserMenus(userId) 单独获取。

10 种子数据与默认账号

10.1 RBAC 种子(Fake.Rbac.Infrastructure)

  • 自动创建默认管理员:账号 admin,密码 123456,角色「超级管理员」。
  • 初始化系统管理菜单(组织、用户、角色、菜单等)。

10.2 OMS 客户角色种子(CustomerRoleSeedContributor)

  • 启动时若不存在则创建角色:Code=Customer,Name=客户,Permissions=[]。
  • 客户主账号创建时分配此角色;具体业务权限需后续通过 assign-permissions 配置。

10.3 生产环境注意

  • 部署后立即修改默认管理员密码。
  • JWT SecretKey 通过 Consul(idwms/admin)或本地配置注入,禁止硬编码弱密钥。

附录 A 关键源码文件索引

用途 路径
宿主模块 / RBAC 注册 SimpleAdmin.Api/SimpleAdminApiModule.cs
自定义认证(双端登录) SimpleAdmin.Api/Auth/AuthService.cs
认证 DTO / 登录类型 SimpleAdmin.Api/Auth/AuthRequestDtos.cs
JWT Claims 扩展 SimpleAdmin.Api/SimpleJwtService.cs
OMS Claim 类型 SimpleAdmin.Api/SimpleOmsClaimTypes.cs
客户角色种子 SimpleAdmin.Api/Auth/CustomerRoleSeedContributor.cs
客户 ↔ RBAC 联动 Oms.Application/Services/Customers/CustomerService.cs
子客户权限入参 Oms.Application/Dtos/Customers/CreateSubCustomerInput.cs
OMS 客户表 SQL db.sql → t_customer
前端 OpenAPI 客户端 oms0414/src/api/rbac/generated/
前端用户 Store oms0414/src/store/modules/rbac-user.ts
前端权限工具 oms0414/src/utils/rbac-permission.ts

附录 B Swagger 分组

分组名 内容
RBAC 认证、用户、角色、菜单、权限、组织
OMS-Customer 客户资料 CRUD + 子客户创建

本地调试可访问 Fake Swagger UI 查看完整 OpenAPI 契约;前端 src/api/rbac/generated 应与 Swagger 保持同步重新生成。