# 用户端小程序 个人中心 接口 开发设计文档 - 模块:ruoyi-user(会员业务)+ ruoyi-gateway(路由/鉴权头注入) - 日期:2026-10-13 - 需求来源:“个人中心页的接口功能实现,根据现在展示的字段实现对应的 java 后端业务” - 关联前端:`hugray-user-miniapp`(`pages/profile/index.vue`) - 关联文档:`doc/design/用户端_登录注册_20261012.md`(C 端会员落库与令牌载体方案)、`doc/design/用户端_首页与导航_20261012.md`(9.3 个人中心页规格) - 数据权威源:`192.168.110.111:3306/hugray-account`(实际表结构优先于本文档) --- ## 一、功能概述 个人中心页(`pages/profile/index.vue`)当前为本地 mock,需由后端下发以下字段,页面据此渲染: | 展示字段 | 页面位置 | 现状 | 本次是否提供 | | --- | --- | --- | --- | | 姓名 `nickname` | 头部文本 | mock `辉煌G891` | 是(`account_user.nickname`;注册未采集姓名字段,落库为空,前端回退展示脱敏手机号) | | 手机号 `phone`(脱敏) | 头部名称回退 | 无 | 是(`account_user.phone`,脱敏为 `133****5555`,不下发明文) | | 会员等级 `memberLevel` | 等级胶囊 `C`/`L1~L4` | mock `L4` | 是(复用既有 `account_user.system_level`) | | 头像 `avatar` | 头部头像 | 相机图标占位(「我的资料」页已支持点击上传更换) | 是(已存在字段,有值前端渲染图片,无值回退图标) | | 今日 / 本月 / 累计收益 | 收益玻璃卡三栏 | mock `10.00 / 400.00 / 128000.00` | 是(新增收益流水表,实时聚合) | | 仓库订单角标数量 | 菜单角标 | mock `1` | 否(见 2.5,无数据来源,本期不入接口) | 展示规则(沿用已确认业务约定):`isRestrictedView = !isLoggedIn || memberLevel === 'C'`;**未登录或 C 普通用户**仅展示「收货地址」「联系我们」两行菜单并隐藏收益卡;**L1~L4** 展示全部内容。该判定由前端完成,后端只负责下发 `memberLevel` 真实值。 本期只新增 **一个 C 端查询接口**:`GET /miniapp/account/user/info`(对应前端已预留的 `getMemberInfo()`)。 --- ## 二、现状与关键约束 ### 2.1 表结构缺口 `account_user`(`hugray-account` 库)现有字段:`user_id, username, phone, password, nickname, avatar, gender, birthday, status, del_flag, my_referral_code, system_level, referrer_id, referrer_bind_time, create_*, update_*, remark`。 **已有「会员等级」字段 `system_level`(`varchar(8)`,默认 `C`),无任何「收益」相关表/字段** → 收益部分本次新增;等级复用既有列,不再新增重复列。 ### 2.2 已有接口不足以支撑 `ruoyi-user` 现有控制器: - `AccountUserController`(`/user`,后台 CRUD,全部挂 `@RequiresPermissions`)——面向后端管理,不适合 C 端调用。 - `AccountMemberInnerController`(`/member`,`@InnerAuth`)——仅服务间调用,按手机号查会员 / 注册。 **无按当前登录会员返回个人中心数据的接口** → 本次新增。 ### 2.3 登录态与取数来源(已定方案) - C 端令牌沿用 RuoYi `TokenService`,网关 `AuthFilter` 校验通过后向目标服务注入请求头 `user_id`、`username`(`SecurityConstants.DETAILS_USER_ID/DETAILS_USERNAME`)。 - `ruoyi-user` 侧通过 `SecurityUtils.getUserId()` 读取 `user_id`,即当前登录会员 ID。 - 该路径**不经过** `/miniapp/system/user/getInfo`(后者面向后台 `sys_user`,见 2.4)。 ### 2.4 前端预留入口的取舍 `hugray-user-miniapp/api/index.js` 现有两个未接线函数: | 函数 | 路径 | 处理 | | --- | --- | --- | | `getMemberInfo()` | `/miniapp/account/user/info` | **本期采用**(指向 ruoyi-user) | | `getUserInfo()` | `/miniapp/system/user/getInfo` | **误导**:指向后台 `sys_user`,与 C 端会员不同源;全仓无调用点,建议删除 | 结论:个人中心统一走 `getMemberInfo()`;`getUserInfo()` 予以移除(避免后续误用)。 ### 2.5 仓库订单角标数量:本期不提供 角标数量应来源于「仓库订单」,但 `ruoyi-product` 模块**当前仅有一个启动类**(无订单/仓库任何表与接口),无数据来源。本期接口不返回该字段,前端保持现状(`withBadge` 已登录展示,值仍为本地 mock `1`),待订单模块就绪后由订单服务提供独立接口,不在本次范围。 ### 2.6 金额单位约定 用户规范要求“金额用分(long)”。故接口 `earnings` 三值均以 **分** 返回(如 `1000` = 10.00 元),前端新增 `分→元` 格式化后再展示为两位小数。 --- ## 三、接口设计 统一响应体:`R`(`code=200` 成功),与 C 端 `/miniapp/**` 既有约定一致;前端 `utils/request.js` 按 `successCode=200` / `unauthorizedCode=401` 解包,调用方取 `res.data`。 > 规范说明:用户规则中的“`Result`、`code=0 成功`、错误码分段”为通用模板;本工程实际框架为 RuoYi `R`/`AjaxResult`(200 成功)+ `@RestControllerAdvice` 全局异常。按“与现有工程保持一致”优先,采用框架既有约定。 ### 3.1 查询当前会员个人中心信息 - **外部路径**:`GET /miniapp/account/user/info`(网关 `StripPrefix=2` → 服务内 `/user/info`) - **鉴权**:需登录。路径不在网关白名单内,未登录由 `AuthFilter` 直接返回 401,前端 `request.js` 自动跳登录页;**后端不做未登录分支**。 - **入参**:无(会员身份取自网关注入的 `user_id` 头) - **出参**:`R` ```json { "code": 200, "msg": "操作成功", "data": { "userId": 100, "nickname": null, "phone": "133****5555", "avatar": null, "memberLevel": "L4", "earnings": { "today": 1000, "month": 40000, "total": 12800000 } } } ``` | 字段 | 类型 | 说明 | | --- | --- | --- | | userId | Long | 会员ID | | nickname | String | 姓名/昵称(注册未采集,可为空;为空时前端回退展示脱敏手机号) | | phone | String | 手机号(**脱敏**,形如 `133****5555`,不下发明文) | | avatar | String | 头像地址,可为空 | | memberLevel | String | 会员等级 `C`/`L1~L4`(空或非法兜底为 `C`;对外字段名沿用 `memberLevel`,数据源为 `account_user.system_level`) | | earnings.today | Long | 今日收益(单位:分) | | earnings.month | Long | 本月收益(单位:分) | | earnings.total | Long | 累计收益(单位:分) | - **错误提示**:`获取会员信息失败`(会员不存在或查询异常) - **说明**:手机号仅下发**脱敏**值(`133****5555`),不下发明文;`userName`(C 端即手机号)不下发,避免敏感信息外泄。 --- ## 四、数据库设计 库:`hugray-account`(权威库 `192.168.110.111:3306`)。脚本:`sql/hugray_account_20261013.sql`(与代码同一次提交,含变更 + 回滚)。 ### 4.1 会员等级:复用既有 `account_user.system_level`(无 DDL) 权威库 `account_user` 已存在 `system_level varchar(8) DEFAULT 'C'`(取值 `C=?` 聚合,避免全表扫描。 - `uk_earning_source_biz` 由 `(source, biz_no)` 保证业务幂等;`biz_no` 为 `NULL` 时 MySQL 唯一索引不冲突(手工补录场景允许)。 **回滚**: ```sql DROP TABLE IF EXISTS `account_earning_record`; ``` --- ## 五、流程设计 ### 5.1 查询个人中心信息(`AccountUserServiceImpl.getAccountUserInfo`) ``` STEP 1: 校验 userId 非空(未登录由网关拦截,此处兜底),为空抛「获取会员信息失败」 STEP 2: 按 userId 查 account_user(del_flag='0');为空抛「获取会员信息失败」 STEP 3: 查收益汇总(单条 SQL,单位分),无流水返回 0 STEP 4: 组装 AccountUserInfoVo:nickname/avatar 直取;phone 脱敏(`133****5555`);memberLevel 取 system_level,空或非法兜底 MemberLevelEnum.C STEP 5: 返回 VO ``` 收益汇总 SQL(一次查询出三值): ```sql SELECT IFNULL(SUM(CASE WHEN create_time >= CURDATE() THEN amount END), 0) AS today, IFNULL(SUM(CASE WHEN create_time >= DATE_FORMAT(CURDATE(), '%Y-%m-01') THEN amount END), 0) AS month, IFNULL(SUM(amount), 0) AS total FROM account_earning_record WHERE user_id = #{userId} ``` --- ## 六、枚举与状态 | 名称 | 取值 | 说明 | | --- | --- | --- | | 会员等级 `memberLevel`(源 `system_level`) | `C` / `L1` / `L2` / `L3` / `L4` | `MemberLevelEnum`:`C`=普通用户(受限展示);`L1~L4`=会员;含 `code` + `desc` + `getByCode` + `normalize` | | 会员状态 `status` | `0` 正常 / `1` 停用 | 复用现有语义 | | 删除标志 `delFlag` | `0` 存在 / `2` 删除 | 复用现有语义 | | 收益来源 `source` | `1` 订单收益 / `2` 邀请奖励 / `3` 等级差价 / `9` 其他 | `EarningSourceEnum`(本期仅建枚举,写入方待订单/邀请模块接入) | > 等级升降级规则(何种行为升到 L1~L4)属于会员体系策略,尚未定义。本期 `system_level` 由后台维护(`AccountUserController` 编辑),接口只负责读取下发;后续等级规则确定后新增独立升级流程。 --- ## 七、影响范围与文件清单 ### 新增(ruoyi-user) | 文件 | 说明 | | --- | --- | | `enums/MemberLevelEnum.java` | 会员等级枚举(`C`/`L1~L4`,含 `getByCode` + `normalize`) | | `enums/EarningSourceEnum.java` | 收益来源枚举(`1/2/3/9`) | | `domain/AccountEarningRecord.java` | 收益流水实体(含字段注释) | | `domain/vo/AccountUserInfoVo.java` | 个人中心信息出参 VO(含脱敏手机号 `phone`) | | `domain/vo/EarningSummaryVo.java` | 收益汇总出参(today/month/total,单位分) | | `mapper/AccountEarningRecordMapper.java` + `resources/mapper/user/AccountEarningRecordMapper.xml` | 收益汇总查询 | | `controller/AccountProfileController.java` | C 端接口 `GET /user/info` | ### 修改 | 文件 | 改动 | | --- | --- | | ruoyi-user `domain/AccountUser.java` | 复用既有 `systemLevel`(源 `system_level`)字段;推荐关系字段为 `referrerId`(源 `referrer_id`)+ getter/setter + toString | | ruoyi-user `mapper/AccountUserMapper.xml` | `system_level` / `referrer_id` 纳入 resultMap / 列 / insert / update | | ruoyi-user `service/IAccountUserService.java` + `service/impl/AccountUserServiceImpl.java` | 新增 `getAccountUserInfo(Long userId)`;注册不再以手机号填充姓名(`nickname` 留空);出参手机号脱敏 | | `sql/hugray_account_20261013.sql` | 新增 DDL(变更 + 回滚) | | 前端 `hugray-user-miniapp/api/index.js` | 删除误导且无调用的 `getUserInfo()` | | 前端 `hugray-user-miniapp/utils/`(新增格式化方法) | `分→元` 两位小数格式化 | | 前端 `hugray-user-miniapp/pages/profile/index.vue` | `onShow` 调 `getMemberInfo()` 替换 mock;头部名称优先姓名、为空回退脱敏手机号 | | `doc/design/用户端_首页与导航_20261012.md` | 9.3 / 9.5 标注 mock 已由接口替换 | > 网关无需改动:`/miniapp/account/**` → `ruoyi-user` 路由已存在。 --- ## 八、边界与异常清单 | # | 场景 | 处理 | | --- | --- | --- | | 1 | 未登录访问 | 网关 `AuthFilter` 返回 401,前端跳登录页;后端不做分支 | | 2 | `user_id` 头缺失/为空 | 服务内兜底判空,抛「获取会员信息失败」(不 NPE) | | 3 | 会员不存在 / 已逻辑删除 | 抛「获取会员信息失败」 | | 4 | `system_level` 为空或非法(历史脏数据) | 兜底为 `C`,不影响页面渲染 | | 5 | 无任何收益流水 | 三值均返回 `0`(分),前端展示 `0.00` | | 6 | 收益为负(退款冲正) | `amount` 支持负数,聚合自然抵扣 | | 7 | 金额单位一致性 | 接口统一返回分;前端统一格式化,禁止后端直接返回字符串金额 | | 8 | 敏感信息 | 接口下发**脱敏**手机号(`133****5555`),不下发明文与 `userName`;日志手机号脱敏 | | 9 | 越权 | 会员身份取自令牌注入头,不接受前端传入 `userId`,天然防水平越权 | | 10 | 姓名未采集(注册未填) | `nickname` 为空,前端头部回退展示脱敏手机号,不报错 | --- ## 九、规范符合性说明 - 分层:Controller 只做取身份 + 转发,业务在 Service,SQL 在 Mapper;Controller 不直接调 Mapper。 - 命名:`getAccountUserInfo` / `selectEarningSummary` 等见名知意;Entity / VO 分离。 - 注释:类注释、字段注释(含取值与单位说明)、Javadoc、核心流程 `// STEP x:` 注释。 - 魔法值:等级、收益来源收口枚举;CUCRATE 等 SQL 表达式内聚在 Mapper。 - 异常:统一 `ServiceException` + 全局处理器,不散落 try-catch 拼返回值。 - 日志:`@Slf4j` 占位符 + `[业务标记]` 前缀,敏感字段脱敏。 - 数据库:字段显式长度 + COMMENT;聚合列有索引;无 `SELECT *`;无 `${}` 拼接;无循环查库。 - 单测:`getAccountUserInfo`(正常 / 会员不存在 / 无流水 / 等级空兜底 / 等级非法兜底)补充单测,Mockito Mock Mapper。 - 说明(与现有工程一致优先):`account_earning_record` 主键沿用自增(与同库 `account_user` 及全工程 RuoYi 风格一致),未采用雪花 ID,作为已知项记录。 --- ## 十、已确认决策 1. **等级存储方式**:**已定** —— 复用既有 `account_user.system_level`(`C`/`L1~L4`,默认 `C`),不新增 `member_level` 重复列。 2. **收益数据模型**:**已定** —— 新建流水表 `account_earning_record` + 实时聚合。 3. **金额单位**:**已定** —— 接口返回分(`long`),前端格式化为两位小数。 4. **仓库订单角标**:**已定** —— 本期不提供(`ruoyi-product` 尚无订单实现)。 5. **数据库连接**:**已定** —— `ruoyi-user-dev.yml` master 已由 `localhost` 改为权威库 `192.168.110.111:3306/hugray-account`(与 `ruoyi-system`/`ruoyi-product` 一致)。