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())。
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),无任何「收益」相关表/字段 → 收益部分本次新增;等级复用既有列,不再新增重复列。
ruoyi-user 现有控制器:
AccountUserController(/user,后台 CRUD,全部挂 @RequiresPermissions)——面向后端管理,不适合 C 端调用。AccountMemberInnerController(/member,@InnerAuth)——仅服务间调用,按手机号查会员 / 注册。无按当前登录会员返回个人中心数据的接口 → 本次新增。
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)。hugray-user-miniapp/api/index.js 现有两个未接线函数:
| 函数 | 路径 | 处理 |
|---|---|---|
getMemberInfo() |
/miniapp/account/user/info |
本期采用(指向 ruoyi-user) |
getUserInfo() |
/miniapp/system/user/getInfo |
误导:指向后台 sys_user,与 C 端会员不同源;全仓无调用点,建议删除 |
结论:个人中心统一走 getMemberInfo();getUserInfo() 予以移除(避免后续误用)。
角标数量应来源于「仓库订单」,但 ruoyi-product 模块**当前仅有一个启动类**(无订单/仓库任何表与接口),无数据来源。本期接口不返回该字段,前端保持现状(withBadge 已登录展示,值仍为本地 mock 1),待订单模块就绪后由订单服务提供独立接口,不在本次范围。
用户规范要求“金额用分(long)”。故接口 earnings 三值均以 分 返回(如 1000 = 10.00 元),前端新增 分→元 格式化后再展示为两位小数。
统一响应体:R<T>(code=200 成功),与 C 端 /miniapp/** 既有约定一致;前端 utils/request.js 按 successCode=200 / unauthorizedCode=401 解包,调用方取 res.data。
规范说明:用户规则中的“
Result<T>、code=0 成功、错误码分段”为通用模板;本工程实际框架为 RuoYiR/AjaxResult(200 成功)+@RestControllerAdvice全局异常。按“与现有工程保持一致”优先,采用框架既有约定。
GET /miniapp/account/user/info(网关 StripPrefix=2 → 服务内 /user/info)AuthFilter 直接返回 401,前端 request.js 自动跳登录页;**后端不做未登录分支**。user_id 头)R<AccountUserInfoVo>{
"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(与代码同一次提交,含变更 + 回滚)。
account_user.system_level(无 DDL)权威库 account_user 已存在 system_level varchar(8) DEFAULT 'C'(取值 C<L1<L2<L3<L4,从低到高),故**不再新增 member_level 重复列**。代码统一读写既有 system_level,对外出参仍为 memberLevel。
account_earning_recordCREATE TABLE `account_earning_record` (
`record_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '收益记录ID',
`user_id` bigint(20) NOT NULL COMMENT '会员ID(关联 account_user.user_id)',
`amount` bigint(20) NOT NULL DEFAULT 0 COMMENT '收益金额(单位:分,可正可负)',
`source` char(1) NOT NULL COMMENT '收益来源(1订单收益 2邀请奖励 3等级差价 9其他)',
`biz_no` varchar(64) DEFAULT NULL COMMENT '业务单号(同来源幂等去重)',
`remark` varchar(255) DEFAULT NULL COMMENT '备注',
`create_time` datetime DEFAULT NULL COMMENT '发生时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`record_id`),
KEY `idx_earning_user_time` (`user_id`, `create_time`),
UNIQUE KEY `uk_earning_source_biz` (`source`, `biz_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员收益流水表';
idx_earning_user_time 覆盖 WHERE user_id=? AND create_time>=? 聚合,避免全表扫描。uk_earning_source_biz 由 (source, biz_no) 保证业务幂等;biz_no 为 NULL 时 MySQL 唯一索引不冲突(手工补录场景允许)。回滚:
DROP TABLE IF EXISTS `account_earning_record`;
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(一次查询出三值):
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编辑),接口只负责读取下发;后续等级规则确定后新增独立升级流程。
| 文件 | 说明 |
|---|---|
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 为空,前端头部回退展示脱敏手机号,不报错 |
getAccountUserInfo / selectEarningSummary 等见名知意;Entity / VO 分离。// STEP x: 注释。ServiceException + 全局处理器,不散落 try-catch 拼返回值。@Slf4j 占位符 + [业务标记] 前缀,敏感字段脱敏。SELECT *;无 ${} 拼接;无循环查库。getAccountUserInfo(正常 / 会员不存在 / 无流水 / 等级空兜底 / 等级非法兜底)补充单测,Mockito Mock Mapper。account_earning_record 主键沿用自增(与同库 account_user 及全工程 RuoYi 风格一致),未采用雪花 ID,作为已知项记录。account_user.system_level(C/L1~L4,默认 C),不新增 member_level 重复列。account_earning_record + 实时聚合。long),前端格式化为两位小数。ruoyi-product 尚无订单实现)。ruoyi-user-dev.yml master 已由 localhost 改为权威库 192.168.110.111:3306/hugray-account(与 ruoyi-system/ruoyi-product 一致)。