From f8c73a38709f7120d11b04bd70c6af3ccc53014d Mon Sep 17 00:00:00 2001
From: zjk <852185829@qq.com>
Date: 星期二, 13 十月 2026 14:51:37 +0800
Subject: [PATCH] feat: 新增用户小程序会员个人中心及资料编辑页面

---
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountEarningRecord.java             |  121 +++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java     |   75 +
 hugray-server/ruoyi-modules/ruoyi-user/src/test/java/com/ruoyi/user/service/impl/AccountUserServiceImplTest.java |  120 +++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountProfileController.java     |   42 +
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/enums/EarningSourceEnum.java                 |   61 +
 hugray-server/sql/hugray_account_20261013.sql                                                                    |   37 +
 doc/design/用户端_我的资料_20261013.html                                                                                |  377 ++++++++++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountEarningRecordMapper.java       |   22 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/enums/MemberLevelEnum.java                   |   78 ++
 doc/nacos/dev/ruoyi-user-dev.yml                                                                                 |    2 
 hugray-user-miniapp/pages.json                                                                                   |    7 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/vo/AccountUserInfoVo.java             |   90 ++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountMemberInnerController.java |    2 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml                      |   14 
 hugray-server/sql/hugray_account_20261012.sql                                                                    |   14 
 doc/design/用户端_首页与导航_20261012.md                                                                                 |   14 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java             |    9 
 hugray-user-miniapp/pages/profile/edit.vue                                                                       |  447 ++++++++++++
 doc/design/用户端_登录注册_20261012.md                                                                                  |   23 
 hugray-user-miniapp/pages/profile/index.vue                                                                      |   87 +
 doc/design/用户端_个人中心_20261013.md                                                                                  |  263 +++++++
 hugray-user-miniapp/api/index.js                                                                                 |    7 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/vo/EarningSummaryVo.java              |   72 ++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java                      |   27 
 hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/AccountMember.java            |   12 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountEarningRecordMapper.xml             |   23 
 hugray-user-miniapp/utils/format.js                                                                              |   20 
 27 files changed, 1,986 insertions(+), 80 deletions(-)

diff --git "a/doc/design/\347\224\250\346\210\267\347\253\257_\344\270\252\344\272\272\344\270\255\345\277\203_20261013.md" "b/doc/design/\347\224\250\346\210\267\347\253\257_\344\270\252\344\272\272\344\270\255\345\277\203_20261013.md"
new file mode 100644
index 0000000..cf76667
--- /dev/null
+++ "b/doc/design/\347\224\250\346\210\267\347\253\257_\344\270\252\344\272\272\344\270\255\345\277\203_20261013.md"
@@ -0,0 +1,263 @@
+# 用户端小程序 个人中心 接口 开发设计文档
+
+- 模块: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<T>`(`code=200` 成功),与 C 端 `/miniapp/**` 既有约定一致;前端 `utils/request.js` 按 `successCode=200` / `unauthorizedCode=401` 解包,调用方取 `res.data`。
+
+> 规范说明:用户规则中的“`Result<T>`、`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<AccountUserInfoVo>`
+
+```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<L1<L2<L3<L4`,从低到高),故**不再新增 `member_level` 重复列**。代码统一读写既有 `system_level`,对外出参仍为 `memberLevel`。
+
+### 4.2 变更:新增收益流水表 `account_earning_record`
+
+```sql
+CREATE 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 唯一索引不冲突(手工补录场景允许)。
+
+**回滚**:
+
+```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` 一致)。
diff --git "a/doc/design/\347\224\250\346\210\267\347\253\257_\346\210\221\347\232\204\350\265\204\346\226\231_20261013.html" "b/doc/design/\347\224\250\346\210\267\347\253\257_\346\210\221\347\232\204\350\265\204\346\226\231_20261013.html"
new file mode 100644
index 0000000..fee42d4
--- /dev/null
+++ "b/doc/design/\347\224\250\346\210\267\347\253\257_\346\210\221\347\232\204\350\265\204\346\226\231_20261013.html"
@@ -0,0 +1,377 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+<meta charset="UTF-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+<title>我的资料 · UI 高保真原型</title>
+<style>
+    :root {
+        --brand-blue: #003e7e;
+        --brand-blue-2: #1e5ebc;
+        --green: #639c1f;
+        --green-2: #82c236;
+        --ink: #0f172a;
+        --label: #1f2937;
+        --sub: #64748b;
+        --muted: #94a3b8;
+        --line: #f1f5f9;
+        --line-2: #e2e8f0;
+    }
+
+    * { margin: 0; padding: 0; box-sizing: border-box; }
+
+    body {
+        font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", sans-serif;
+        background: #eef2f7;
+        color: var(--ink);
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        padding: 40px 16px 64px;
+        min-height: 100vh;
+    }
+
+    /* ==================== 文档头 ==================== */
+    .doc-head { text-align: center; margin-bottom: 28px; }
+    .doc-title { font-size: 20px; font-weight: 700; }
+    .doc-sub { margin-top: 6px; font-size: 13px; color: var(--sub); }
+
+    /* ==================== 手机框 ==================== */
+    .phone {
+        width: 375px;
+        height: 812px;
+        background: #fff;
+        border-radius: 44px;
+        box-shadow: 0 24px 60px rgba(15, 23, 42, .18), 0 2px 8px rgba(15, 23, 42, .06);
+        overflow: hidden;
+        position: relative;
+        display: flex;
+        flex-direction: column;
+    }
+
+    /* 状态栏(模拟) */
+    .status-bar {
+        height: 44px;
+        flex-shrink: 0;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 0 22px 0 28px;
+        font-size: 14px;
+        font-weight: 600;
+        letter-spacing: .2px;
+    }
+    .status-icons { display: flex; align-items: center; gap: 5px; }
+
+    /* 自定义导航栏 */
+    .nav {
+        height: 44px;
+        flex-shrink: 0;
+        position: relative;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+    .nav-back {
+        position: absolute;
+        left: 10px;
+        width: 40px;
+        height: 40px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+    }
+    .nav-title { font-size: 17px; font-weight: 700; letter-spacing: .5px; }
+    .nav-actions {
+        position: absolute;
+        right: 14px;
+        display: flex;
+        align-items: center;
+        gap: 16px;
+    }
+
+    /* ==================== 列表 ==================== */
+    .body { flex: 1; display: flex; flex-direction: column; }
+    .list { padding: 4px 16px 0; }
+
+    .row {
+        min-height: 58px;
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        border-bottom: 1px solid var(--line);
+    }
+    .row:last-child { border-bottom: none; }
+    .row-label { font-size: 15px; color: var(--label); }
+    .row-right { display: flex; align-items: center; gap: 8px; }
+    .row-value { font-size: 15px; color: var(--muted); letter-spacing: .3px; }
+
+    /* 头像 */
+    .avatar {
+        width: 46px;
+        height: 46px;
+        border-radius: 50%;
+        background: #f1f5f9;
+        border: 1px solid var(--line-2);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        overflow: hidden;
+    }
+    .avatar img { width: 100%; height: 100%; object-fit: cover; }
+
+    /* 认证状态徽标 */
+    .badge {
+        font-size: 13px;
+        font-weight: 500;
+        line-height: 1;
+        padding: 5px 10px;
+        border-radius: 6px;
+        border: 1px solid;
+    }
+    .badge--none { color: #ea580c; background: #fff7ed; border-color: #fdba74; }
+    .badge--ing  { color: #1e5ebc; background: #eff6ff; border-color: #93c5fd; }
+    .badge--fail { color: #dc2626; background: #fef2f2; border-color: #fca5a5; }
+    .badge--done { color: #4d7c0f; background: #f7fee7; border-color: #bef264; }
+
+    /* ==================== 底部操作区 ==================== */
+    .footer {
+        margin-top: auto;
+        padding: 16px 24px 22px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        gap: 18px;
+    }
+    .btn-logout {
+        width: 100%;
+        height: 48px;
+        border-radius: 24px;
+        background: linear-gradient(90deg, #82c236 0%, #639c1f 100%);
+        color: #fff;
+        font-size: 15px;
+        font-weight: 600;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        box-shadow: 0 8px 20px rgba(99, 156, 31, .28);
+    }
+    .link-cancel {
+        font-size: 14px;
+        color: var(--brand-blue-2);
+        text-decoration: underline;
+        text-underline-offset: 3px;
+    }
+
+    /* Home 指示条 */
+    .home-bar {
+        height: 26px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        flex-shrink: 0;
+    }
+    .home-bar span {
+        width: 134px;
+        height: 5px;
+        border-radius: 3px;
+        background: #0f172a;
+        opacity: .85;
+    }
+
+    /* ==================== 图例 / 设计说明 ==================== */
+    .legend {
+        width: 375px;
+        margin-top: 32px;
+        display: flex;
+        flex-direction: column;
+        gap: 18px;
+    }
+    .card {
+        background: #fff;
+        border-radius: 16px;
+        padding: 18px 20px;
+        box-shadow: 0 6px 18px rgba(15, 23, 42, .05);
+    }
+    .card h3 {
+        font-size: 14px;
+        font-weight: 700;
+        margin-bottom: 14px;
+        color: var(--ink);
+    }
+    .badge-row { display: flex; flex-wrap: wrap; gap: 10px; }
+    .legend-note {
+        font-size: 13px;
+        line-height: 1.7;
+        color: var(--sub);
+    }
+    .legend-note b { color: var(--label); font-weight: 600; }
+    .legend-note .no { color: var(--brand-blue-2); font-weight: 700; margin-right: 4px; }
+    .legend-note li { list-style: none; margin-bottom: 6px; }
+    .avatar-row { display: flex; gap: 28px; align-items: center; }
+    .avatar-item { display: flex; flex-direction: column; align-items: center; gap: 8px; }
+    .avatar-item small { font-size: 12px; color: var(--muted); }
+</style>
+</head>
+<body>
+    <div class="doc-head">
+        <div class="doc-title">「我的资料」页面 · UI 高保真原型</div>
+        <div class="doc-sub">用户端小程序 · 375×812 · 本期交互为占位</div>
+    </div>
+
+    <!-- ==================== 手机原型 ==================== -->
+    <div class="phone">
+        <!-- 状态栏 -->
+        <div class="status-bar">
+            <span>12:00</span>
+            <span class="status-icons">
+                <!-- 信号 -->
+                <svg width="17" height="11" viewBox="0 0 17 11" fill="#0f172a">
+                    <rect x="0" y="7" width="3" height="4" rx="1"></rect>
+                    <rect x="4.5" y="5" width="3" height="6" rx="1"></rect>
+                    <rect x="9" y="2.5" width="3" height="8.5" rx="1"></rect>
+                    <rect x="13.5" y="0" width="3" height="11" rx="1"></rect>
+                </svg>
+                <!-- WiFi -->
+                <svg width="16" height="11" viewBox="0 0 16 11" fill="#0f172a">
+                    <path d="M8 10.5 5.4 7.6a4 4 0 0 1 5.2 0L8 10.5Z"></path>
+                    <path d="M8 5.6c-1.9 0-3.6.7-4.9 1.9L1.4 5.6A9.6 9.6 0 0 1 8 3.1c2.6 0 4.9 1 6.6 2.5l-1.7 1.9A7.1 7.1 0 0 0 8 5.6Z" opacity=".95"></path>
+                </svg>
+                <!-- 电池 -->
+                <svg width="25" height="12" viewBox="0 0 25 12" fill="none">
+                    <rect x=".5" y=".5" width="21" height="11" rx="3" stroke="#0f172a" opacity=".4"></rect>
+                    <rect x="2" y="2" width="18" height="8" rx="1.6" fill="#0f172a"></rect>
+                    <rect x="22.5" y="4" width="2" height="4" rx="1" fill="#0f172a" opacity=".5"></rect>
+                </svg>
+            </span>
+        </div>
+
+        <!-- 导航栏 -->
+        <div class="nav">
+            <div class="nav-back">
+                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#1f2937" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
+                    <path d="m15 18-6-6 6-6" />
+                </svg>
+            </div>
+            <div class="nav-title">我的资料</div>
+            <div class="nav-actions">
+                <!-- 更多 -->
+                <svg width="22" height="22" viewBox="0 0 24 24" fill="#334155">
+                    <circle cx="5" cy="12" r="1.7"></circle>
+                    <circle cx="12" cy="12" r="1.7"></circle>
+                    <circle cx="19" cy="12" r="1.7"></circle>
+                </svg>
+                <!-- 帮助 -->
+                <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#334155" stroke-width="2">
+                    <circle cx="12" cy="12" r="9"></circle>
+                    <path d="M9.4 9.2a2.6 2.6 0 1 1 3.3 2.5c-.8.3-1.3 1-1.3 1.9v.3" stroke-linecap="round"></path>
+                    <circle cx="11.4" cy="17" r="1" fill="#334155" stroke="none"></circle>
+                </svg>
+            </div>
+        </div>
+
+        <!-- 内容 -->
+        <div class="body">
+            <div class="list">
+                <!-- 头像 -->
+                <div class="row">
+                    <span class="row-label">头像</span>
+                    <div class="row-right">
+                        <div class="avatar">
+                            <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                                <path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"></path>
+                                <circle cx="12" cy="13" r="3"></circle>
+                            </svg>
+                        </div>
+                        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#cbd5e1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                            <path d="m9 18 6-6-6-6" />
+                        </svg>
+                    </div>
+                </div>
+
+                <!-- 手机号(不可更换) -->
+                <div class="row">
+                    <span class="row-label">手机号</span>
+                    <div class="row-right">
+                        <span class="row-value">136****6542</span>
+                    </div>
+                </div>
+
+                <!-- 实名认证 -->
+                <div class="row">
+                    <span class="row-label">实名认证</span>
+                    <div class="row-right">
+                        <span class="badge badge--none">未认证</span>
+                        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#cbd5e1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                            <path d="m9 18 6-6-6-6" />
+                        </svg>
+                    </div>
+                </div>
+
+                <!-- 企业 / 个体户认证 -->
+                <div class="row">
+                    <span class="row-label">企业/个体户认证</span>
+                    <div class="row-right">
+                        <span class="badge badge--none">未认证</span>
+                        <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#cbd5e1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                            <path d="m9 18 6-6-6-6" />
+                        </svg>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 底部操作 -->
+            <div class="footer">
+                <div class="btn-logout">退出登录</div>
+                <span class="link-cancel">注销账号</span>
+            </div>
+        </div>
+
+        <!-- Home 指示条 -->
+        <div class="home-bar"><span></span></div>
+    </div>
+
+    <!-- ==================== 图例与设计说明 ==================== -->
+    <div class="legend">
+        <div class="card">
+            <h3>认证状态徽标(4 态)</h3>
+            <div class="badge-row">
+                <span class="badge badge--none">未认证</span>
+                <span class="badge badge--ing">认证中</span>
+                <span class="badge badge--fail">认证失败</span>
+                <span class="badge badge--done">已认证</span>
+            </div>
+        </div>
+
+        <div class="card">
+            <h3>头像两种状态</h3>
+            <div class="avatar-row">
+                <div class="avatar-item">
+                    <div class="avatar">
+                        <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="#94a3b8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
+                            <path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"></path>
+                            <circle cx="12" cy="13" r="3"></circle>
+                        </svg>
+                    </div>
+                    <small>未设置(相机占位)</small>
+                </div>
+                <div class="avatar-item">
+                    <div class="avatar">
+                        <img src="https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=portrait%20photo%20of%20a%20smiling%20chinese%20young%20man%2C%20studio%20headshot%2C%20clean%20light%20background%2C%20high%20quality&image_size=square" alt="avatar" />
+                    </div>
+                    <small>已设置(圆形裁切)</small>
+                </div>
+            </div>
+        </div>
+
+        <div class="card">
+            <h3>原型标注</h3>
+            <ul class="legend-note">
+                <li><span class="no">1</span>认证状态共 <b>4</b> 种:未认证 / 认证中 / 认证失败 / 已认证,由后端返回状态枚举驱动徽标样式。</li>
+                <li><span class="no">2</span>后台按会员等级设置所需认证方式;完成<b>企业/个体户认证</b>后,页面将有限展示企业/个体户信息。</li>
+                <li><span class="no">3</span>手机号在小程序<b>暂不支持更换</b>,仅只读展示(已脱敏),故该行无箭头。</li>
+            </ul>
+        </div>
+    </div>
+</body>
+</html>
diff --git "a/doc/design/\347\224\250\346\210\267\347\253\257_\347\231\273\345\275\225\346\263\250\345\206\214_20261012.md" "b/doc/design/\347\224\250\346\210\267\347\253\257_\347\231\273\345\275\225\346\263\250\345\206\214_20261012.md"
index 872635e..0f68308 100644
--- "a/doc/design/\347\224\250\346\210\267\347\253\257_\347\231\273\345\275\225\346\263\250\345\206\214_20261012.md"
+++ "b/doc/design/\347\224\250\346\210\267\347\253\257_\347\231\273\345\275\225\346\263\250\345\206\214_20261012.md"
@@ -78,7 +78,7 @@
 
 ### 2.7 表结构缺口
 
-`account_user` 无推荐码、无推荐人字段 → 新增 `my_referral_code`、`referrer_user_id`。
+`account_user` 无推荐码字段 → 新增 `my_referral_code`;推荐关系复用既有 `referrer_id`(权威库已存在),不新增重复列。
 
 ### 2.8 既有代码缺陷
 
@@ -212,18 +212,18 @@
 
 ```sql
 ALTER TABLE `account_user`
-  ADD COLUMN `my_referral_code` varchar(20) DEFAULT NULL COMMENT '本人推荐码(注册时生成,全局唯一)' AFTER `remark`,
-  ADD COLUMN `referrer_user_id` bigint(20) DEFAULT NULL COMMENT '推荐人会员ID(关联 account_user.user_id)' AFTER `my_referral_code`;
+  ADD COLUMN `my_referral_code` varchar(20) DEFAULT NULL COMMENT '本人推荐码(注册时生成,全局唯一)' AFTER `remark`;
 
 -- 推荐码全局唯一:随机生成且不重复使用,逻辑删除行占用不影响业务
 CREATE UNIQUE INDEX `uk_account_user_referral_code` ON `account_user` (`my_referral_code`);
+-- 推荐关系复用既有 referrer_id(权威库已存在),不新增 referrer_user_id
 ```
 
 回滚:
 
 ```sql
 DROP INDEX `uk_account_user_referral_code` ON `account_user`;
-ALTER TABLE `account_user` DROP COLUMN `referrer_user_id`, DROP COLUMN `my_referral_code`;
+ALTER TABLE `account_user` DROP COLUMN `my_referral_code`;
 ```
 
 说明:
@@ -266,7 +266,7 @@
 STEP 5: Feign 调 ruoyi-user 注册:
        5.1 按推荐码查推荐人,不存在抛「推荐人码无效」;推荐人 status=1 抛「推荐人账号异常」
        5.2 生成唯一 my_referral_code(随机 8 位大写字母数字,库内查重,冲突重试 ≤5 次)
-       5.3 组装 AccountUser:userName=phone、phone、nickname=phone、myReferralCode、referrerUserId、status=0、delFlag=0
+       5.3 组装 AccountUser:userName=phone、phone、nickname=phone、myReferralCode、referrerId、status=0、delFlag=0
        5.4 插入(无密码,password 为空)
 STEP 6: 删除验证码 key;finally 释放锁
 ```
@@ -311,8 +311,9 @@
 
 通用推荐码(ruoyi-user)
   AccountUserServiceImpl.registerMember 定位推荐人前先判定:
-    命中「配置的通用推荐码」→ 跳过推荐人查询与校验,referrerUserId 留空(不绑定推荐人)
-    否则走原有「按码查推荐人 → 不存在/停用则拒绝」逻辑
+    命中「配置的通用推荐码」→ 跳过推荐人查询与校验,referrerId 留空(不绑定推荐人);
+                              同时直接发放 L4 会员等级(system_level = L4)
+    否则走原有「按码查推荐人 → 不存在/停用则拒绝」逻辑,且不设等级(走库默认值 C)
 ```
 
 命中通用码时打印 WARN 日志留痕(手机号脱敏)。
@@ -320,7 +321,7 @@
 | 服务 | 配置项 | 示例值 | 说明 |
 | --- | --- | --- | --- |
 | ruoyi-auth | `hugray.auth.universal-sms-code` | `999999` | 空 = 关闭;仅 `login` / `register` 场景生效 |
-| ruoyi-user | `hugray.user.universal-referral-code` | `HUGRAY88` | 空 = 关闭;命中则不绑定推荐人 |
+| ruoyi-user | `hugray.user.universal-referral-code` | `HUGRAY88` | 空 = 关闭;命中则不绑定推荐人,且注册用户直接为 L4 等级 |
 
 ---
 
@@ -365,7 +366,7 @@
 
 | 文件 | 改动 |
 | --- | --- |
-| ruoyi-user `domain/AccountUser.java` | 新增 `myReferralCode`、`referrerUserId` 字段与注释、toString |
+| ruoyi-user `domain/AccountUser.java` | 新增 `myReferralCode` 字段与注释、toString;推荐关系复用既有 `referrerId`(源 `referrer_id`) |
 | ruoyi-user `mapper/AccountUserMapper.java` + `resources/mapper/user/AccountUserMapper.xml` | resultMap/列/insert/update 增列;新增 `selectAccountUserByReferralCode` |
 | ruoyi-user `service/impl/AccountUserServiceImpl.insertAccountUser` | 密码为空时跳过加密(当前无条件 `encryptPassword`,C 端无密码会 NPE) |
 | `sql/hugray_account_20261012.sql` | 新增 DDL 脚本 |
@@ -497,7 +498,7 @@
 | 20 | 验证码登录:手机号未注册 | 下发(scene=login)即拦「该手机号未注册,请先注册」;登录接口再兜底,均**不消费验证码** |
 | 21 | 验证码登录:账号停用 / 删除 | 「账号已停用,请联系管理员」/「账号不存在」,校验在消费验证码之前 |
 | 22 | 通用验证码命中(`login` / `register` 且 `hugray.auth.universal-sms-code` 已配置) | 直接放行:不读缓存、不计数、不消费;`reset` / `common` 场景不生效;配置为空即关闭 |
-| 23 | 通用推荐码命中(`hugray.user.universal-referral-code` 已配置且有值) | 跳过推荐人查询与校验,`referrerUserId` 留空(不绑定推荐人);命中通用码时不再校验「推荐人码无效 / 推荐人账号异常」 |
+| 23 | 通用推荐码命中(`hugray.user.universal-referral-code` 已配置且有值) | 跳过推荐人查询与校验,`referrerId` 留空(不绑定推荐人),且注册用户等级直接为 `L4`;命中通用码时不再校验「推荐人码无效 / 推荐人账号异常」 |
 
 ---
 
@@ -526,7 +527,7 @@
 6. 登录页双按钮(微信一键登录 / 手机验证登录),「手机验证登录」为页内嵌面板;后端 `POST /miniapp/auth/phone/code-login` 与 `SmsSceneEnum.LOGIN` 同步落地。✔
 7. 登录页按 `.pen` 设计稿 + `static/` 素材做视觉 1:1 还原(背景装饰、Logo、背书图标、标语卡、按钮尺寸与渐变、协议行、Home 条),规格见 3.5 节。✔
 8. 登录页 UI 缺陷修复:微信一键登录恢复通栏;协议行字号 23rpx → 26rpx;展开验证码面板后隐藏「手机验证登录」按钮并在面板内提供「收起」入口。✔
-9. 测试便利:通用验证码 `999999`(`hugray.auth.universal-sms-code`,仅 `login` / `register` 生效)+ 通用推荐码 `HUGRAY88`(`hugray.user.universal-referral-code`,命中不绑定推荐人);均通过 nacos 配置开启,配置为空即关闭,命中打印 WARN 留痕。✔
+9. 测试便利:通用验证码 `999999`(`hugray.auth.universal-sms-code`,仅 `login` / `register` 生效)+ 通用推荐码 `HUGRAY88`(`hugray.user.universal-referral-code`,命中不绑定推荐人且注册用户直接发放 `L4` 等级);均通过 nacos 配置开启,配置为空即关闭,命中打印 WARN 留痕。✔
 10. 注册流程:注册页**无需阅读/勾选协议**(提交仅校验手机号 / 验证码 / 推荐码);注册成功页与登录/注册页视觉统一(渐变底 + 装饰层 + 居中结果区 + 渐变胶囊「返回登录」按钮 + Home 条),规格见 3.7 节。✔
 
 > 编码顺序:常量 → DDL → Feign/内部接口 → auth 服务与控制器 → 网关配置 → 前端前缀对齐 → `GetDiagnostics` 校验 → 单测。
diff --git "a/doc/design/\347\224\250\346\210\267\347\253\257_\351\246\226\351\241\265\344\270\216\345\257\274\350\210\252_20261012.md" "b/doc/design/\347\224\250\346\210\267\347\253\257_\351\246\226\351\241\265\344\270\216\345\257\274\350\210\252_20261012.md"
index 5e095d0..9827380 100644
--- "a/doc/design/\347\224\250\346\210\267\347\253\257_\351\246\226\351\241\265\344\270\216\345\257\274\350\210\252_20261012.md"
+++ "b/doc/design/\347\224\250\346\210\267\347\253\257_\351\246\226\351\241\265\344\270\216\345\257\274\350\210\252_20261012.md"
@@ -231,7 +231,7 @@
 | 3 | 个人中心角标数量(未读)由后端下发 | 待接口(当前默认 1) |
 | 4 | 缺货订单列表页真实内容 | 占位 |
 | 5 | 「探索更多」产品详情页 | 占位(toast 提示) |
-| 6 | 三页真实数据接口(订单列表 / 收益数值 / 会员等级与昵称) | 待接口(当前 mock) |
+| 6 | 真实数据接口:个人中心「昵称 / 头像 / 等级 / 收益」已由 `getMemberInfo` 下发;订单列表仍待 `getOrderList` | 部分完成 |
 | 7 | 三页图片素材(方案卡、订单缩略图、用户头像)入仓后替换渐变占位 | 待素材 |
 
 
@@ -273,16 +273,16 @@
 | 页面底色 | 175° 渐变 `#D6E6F8` 0 → `#C4DCF6` .18 → `#E8F1FC` .38 → `#F8FAFC` .58 → `#F8FAFC` 1 | `.page` |
 | 头部信息区 | x20 335×72,居中,头像与文本间隙 14 | `.header` |
 | 头像 | 70×70 R35 fill `#F1F5F9` + 2.5 白描边 | 已登录内嵌 `camera` 32×32 `#94A3B8`;未登录内嵌 `user` 34×34 `#CBD5E1` |
-| 文本信息 | 昵称 18pt 700 `#0F172A` | 已登录「辉煌G891」+ 等级胶囊(`userLevel` 动态展示 L0~L4);未登录「点击登录 / 注册」+ 副标 12pt `#64748B` |
+| 文本信息 | 昵称 18pt 700 `#0F172A` | 已登录「辉煌G891」+ 等级胶囊(`userLevel` 动态展示 C/L1~L4);未登录「点击登录 / 注册」+ 副标 12pt `#64748B` |
 | 会员等级胶囊 | h22 渐变 90° `#FDE68A→#FCD34D→#F59E0B`,V 勋章 18×18 渐变 145° | `.level-pill` / `.level-medal`;等级文字 12.5pt 800 `#78350F` |
 | 主按钮 | h32 渐变 90° `#1E5EBC→#003E7E` R16 | 已登录「编辑资料」/ 未登录「立即登录」 |
-| 收益玻璃卡 | x16 343×74,180° 半透明白渐变 + blur20,R16 | 三栏等分:**今日 10.00 / 本月 400.00 / 累计 128000.00**;未登录统一 `0.00`;**未登录或 L0 普通用户不展示该卡** |
-| 功能菜单卡 | x16 343×408,同款玻璃态,6 行 × 68(未登录 / L0 仅 2 行) | 行图标 20×20 渐变描边 `#3B82F6→#1D4ED8`,右箭头 16×16 `#94A3B8` |
-| 菜单项与等级 | — | L1~L4 且已登录时展示 6 项:`share-2` 我的邀请码 / `crown` 会员中心 / `file-text` 仓库订单 / `badge-cent` 收入明细 / `map-pin` 收货地址 / `contact` 联系我们;**未登录或 L0 普通用户仅展示「收货地址」「联系我们」** |
+| 收益玻璃卡 | x16 343×74,180° 半透明白渐变 + blur20,R16 | 三栏等分:**今日 10.00 / 本月 400.00 / 累计 128000.00**;未登录统一 `0.00`;**未登录或 C 普通用户不展示该卡** |
+| 功能菜单卡 | x16 343×408,同款玻璃态,6 行 × 68(未登录 / C 仅 2 行) | 行图标 20×20 渐变描边 `#3B82F6→#1D4ED8`,右箭头 16×16 `#94A3B8` |
+| 菜单项与等级 | — | L1~L4 且已登录时展示 6 项:`share-2` 我的邀请码 / `crown` 会员中心 / `file-text` 仓库订单 / `badge-cent` 收入明细 / `map-pin` 收货地址 / `contact` 联系我们;**未登录或 C 普通用户仅展示「收货地址」「联系我们」** |
 | 仓库订单角标 | 18×18 `#EF4444` R9 文字 1 | **仅已登录态**展示 |
 | ICP 区 | x16 343×40 居中 | `京ICP备2026092800号-1X` + 版权行;**内容不足一屏时贴页面底部**(`.page` 为纵向 flex,`.icp` 用 `margin-top: auto` 吸收剩余空间) |
 
-> **用户等级与展示规则(L0~L4)**:由 `userLevel` 控制,`isRestrictedView = !isLoggedIn || userLevel === 'L0'`。**未登录或 L0 普通用户**在个人中心仅保留「收货地址」「联系我们」两行菜单,并隐藏收益数据卡;**已登录且 L1~L4** 展示全部内容。等级接口未联调(当前 mock `L4`),联调后由 `getUserInfo` 下发等级替换。
+> **用户等级与展示规则(C/L1~L4)**:由 `userLevel` 控制,`isRestrictedView = !isLoggedIn || userLevel === 'C'`。**未登录或 C 普通用户**在个人中心仅保留「收货地址」「联系我们」两行菜单,并隐藏收益数据卡;**已登录且 L1~L4** 展示全部内容。昵称、头像、等级、收益均已由 `getMemberInfo`(`GET /miniapp/account/user/info`)下发;会员等级空或非法时后端兜底 `C`。
 
 ### 9.4 新增内联 base64 图标
 
@@ -300,7 +300,7 @@
 - 三页模板均引入 `<stock-order-toast />`,保证缺货提示全局可见。
 - 首页 / 健康方案 / 我的订单 / 个人中心:**均不校验登录态**,未登录可正常浏览(原 `ensureLogin()` 守卫已移除,仅缺货订单列表页保留)。
 - 个人中心:`onShow` 读 `getToken()` 仅用于切换「已登录 / 未登录」展示态,不做跳转;未登录时点击头像 / 名称 / 主按钮直接 `uni.reLaunch` 至登录页,**点击菜单项先 `uni.showModal` 提示登录、确认后跳转**。
-- 订单列表、收益数值、用户昵称、用户等级均为本地 mock,待 `getOrderList` / `getMemberInfo` / `getUserInfo` 就绪后替换。
+- 订单列表、仓库订单角标仍为本地 mock,待 `getOrderList` 就绪后替换;收益数值、用户昵称、头像、用户等级已由 `getMemberInfo`(`GET /miniapp/account/user/info`,金额单位分,前端 `formatYuan` 格式化)替换。
 - 「探索更多 / 二级筛选 / 去支付 / 查看订单 / 编辑资料」本轮统一为 toast 占位(菜单项仅在已登录态为占位)。
 
 ### 9.6 联系我们页(`pages/contact/index.vue`)
diff --git a/doc/nacos/dev/ruoyi-user-dev.yml b/doc/nacos/dev/ruoyi-user-dev.yml
index 5bc5a9a..9771117 100644
--- a/doc/nacos/dev/ruoyi-user-dev.yml
+++ b/doc/nacos/dev/ruoyi-user-dev.yml
@@ -33,7 +33,7 @@
           # 主库数据源
           master:
             driver-class-name: com.mysql.cj.jdbc.Driver
-            url: jdbc:mysql://localhost:3306/hugray-account?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+            url: jdbc:mysql://192.168.110.111:3306/hugray-account?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
             username: root
             password: <脱敏归档,真实值见配置中心>
           # 从库数据源
diff --git a/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/AccountMember.java b/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/AccountMember.java
index 5b94d84..75cfd80 100644
--- a/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/AccountMember.java
+++ b/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/AccountMember.java
@@ -35,7 +35,7 @@
     private String myReferralCode;
 
     /** 推荐人会员ID(关联 account_user.user_id) */
-    private Long referrerUserId;
+    private Long referrerId;
 
     /** 状态(0正常 1停用) */
     private String status;
@@ -100,14 +100,14 @@
         this.myReferralCode = myReferralCode;
     }
 
-    public Long getReferrerUserId()
+    public Long getReferrerId()
     {
-        return referrerUserId;
+        return referrerId;
     }
 
-    public void setReferrerUserId(Long referrerUserId)
+    public void setReferrerId(Long referrerId)
     {
-        this.referrerUserId = referrerUserId;
+        this.referrerId = referrerId;
     }
 
     public String getStatus()
@@ -130,7 +130,7 @@
             .append("nickname", getNickname())
             .append("avatar", getAvatar())
             .append("myReferralCode", getMyReferralCode())
-            .append("referrerUserId", getReferrerUserId())
+            .append("referrerId", getReferrerId())
             .append("status", getStatus())
             .toString();
     }
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountMemberInnerController.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountMemberInnerController.java
index 5917d58..080e3be 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountMemberInnerController.java
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountMemberInnerController.java
@@ -75,7 +75,7 @@
         member.setNickname(accountUser.getNickname());
         member.setAvatar(accountUser.getAvatar());
         member.setMyReferralCode(accountUser.getMyReferralCode());
-        member.setReferrerUserId(accountUser.getReferrerUserId());
+        member.setReferrerId(accountUser.getReferrerId());
         member.setStatus(accountUser.getStatus());
         return member;
     }
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountProfileController.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountProfileController.java
new file mode 100644
index 0000000..c2f4ea8
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountProfileController.java
@@ -0,0 +1,42 @@
+package com.ruoyi.user.controller;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.user.domain.vo.AccountUserInfoVo;
+import com.ruoyi.user.service.IAccountUserService;
+
+/**
+ * C端会员 个人中心接口
+ *
+ * 网关前缀 /miniapp/account 由 StripPrefix=2 剥掉,故此处映射为 /user,
+ * 外部实际路径为 /miniapp/account/user/**。
+ *
+ * 会员身份取自网关注入的登录态(SecurityUtils.getUserId),不接受前端传 userId,
+ * 天然防止水平越权;未登录请求由网关直接以 401 拦截,故此处不处理未登录分支。
+ *
+ * @author hugray
+ */
+@RestController
+@RequestMapping("/user")
+public class AccountProfileController
+{
+    @Autowired
+    private IAccountUserService accountUserService;
+
+    /**
+     * 查询当前登录会员的个人中心信息
+     *
+     * 返回昵称、头像、会员等级与收益汇总(今日 / 本月 / 累计,单位分)。
+     *
+     * @return 会员信息展示对象
+     */
+    @GetMapping("/info")
+    public R<AccountUserInfoVo> info()
+    {
+        return R.ok(accountUserService.getAccountUserInfo(SecurityUtils.getUserId()));
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountEarningRecord.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountEarningRecord.java
new file mode 100644
index 0000000..6dc1584
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountEarningRecord.java
@@ -0,0 +1,121 @@
+package com.ruoyi.user.domain;
+
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+/**
+ * 会员收益流水 account_earning_record
+ *
+ * 一条记录代表一次收益事件;个人中心「今日 / 本月 / 累计收益」由本表按时间实时聚合得出,
+ * 无需在 account_user 上维护需要定时归零的汇总字段。
+ *
+ * @author hugray
+ */
+public class AccountEarningRecord
+{
+    /** 收益记录ID */
+    private Long recordId;
+
+    /** 会员ID(关联 account_user.user_id) */
+    private Long userId;
+
+    /** 收益金额(单位:分,可正可负,负数表示扣减) */
+    private Long amount;
+
+    /** 收益来源(1订单收益 2邀请奖励 3等级差价 9其他,见 EarningSourceEnum) */
+    private String source;
+
+    /** 业务单号(同来源幂等去重,可为空) */
+    private String bizNo;
+
+    /** 备注 */
+    private String remark;
+
+    /** 发生时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date createTime;
+
+    /** 更新时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date updateTime;
+
+    public Long getRecordId()
+    {
+        return recordId;
+    }
+
+    public void setRecordId(Long recordId)
+    {
+        this.recordId = recordId;
+    }
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public Long getAmount()
+    {
+        return amount;
+    }
+
+    public void setAmount(Long amount)
+    {
+        this.amount = amount;
+    }
+
+    public String getSource()
+    {
+        return source;
+    }
+
+    public void setSource(String source)
+    {
+        this.source = source;
+    }
+
+    public String getBizNo()
+    {
+        return bizNo;
+    }
+
+    public void setBizNo(String bizNo)
+    {
+        this.bizNo = bizNo;
+    }
+
+    public String getRemark()
+    {
+        return remark;
+    }
+
+    public void setRemark(String remark)
+    {
+        this.remark = remark;
+    }
+
+    public Date getCreateTime()
+    {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime)
+    {
+        this.createTime = createTime;
+    }
+
+    public Date getUpdateTime()
+    {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime)
+    {
+        this.updateTime = updateTime;
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java
index 4bff50b..f1d8edd 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/AccountUser.java
@@ -70,7 +70,11 @@
     private String myReferralCode;
 
     /** 推荐人会员ID(关联 account_user.user_id) */
-    private Long referrerUserId;
+    private Long referrerId;
+
+    /** 系统等级(C普通用户 L1~L4会员,对应既有列 account_user.system_level,见 MemberLevelEnum) */
+    @Excel(name = "系统等级")
+    private String systemLevel;
 
     public Long getUserId()
     {
@@ -187,14 +191,24 @@
         this.myReferralCode = myReferralCode;
     }
 
-    public Long getReferrerUserId()
+    public Long getReferrerId()
     {
-        return referrerUserId;
+        return referrerId;
     }
 
-    public void setReferrerUserId(Long referrerUserId)
+    public void setReferrerId(Long referrerId)
     {
-        this.referrerUserId = referrerUserId;
+        this.referrerId = referrerId;
+    }
+
+    public String getSystemLevel()
+    {
+        return systemLevel;
+    }
+
+    public void setSystemLevel(String systemLevel)
+    {
+        this.systemLevel = systemLevel;
     }
 
     @Override
@@ -210,7 +224,8 @@
             .append("status", getStatus())
             .append("delFlag", getDelFlag())
             .append("myReferralCode", getMyReferralCode())
-            .append("referrerUserId", getReferrerUserId())
+            .append("referrerId", getReferrerId())
+            .append("systemLevel", getSystemLevel())
             .append("createBy", getCreateBy())
             .append("createTime", getCreateTime())
             .append("updateBy", getUpdateBy())
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/vo/AccountUserInfoVo.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/vo/AccountUserInfoVo.java
new file mode 100644
index 0000000..25bd635
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/vo/AccountUserInfoVo.java
@@ -0,0 +1,90 @@
+package com.ruoyi.user.domain.vo;
+
+/**
+ * C端会员信息 VO(个人中心页展示用)
+ *
+ * 仅暴露页面所需字段;手机号仅下发脱敏值(如 133****5555),不下发明文;
+ * 会员身份由服务端从登录态解析,不接受前端传入。
+ *
+ * @author hugray
+ */
+public class AccountUserInfoVo
+{
+    /** 会员ID */
+    private Long userId;
+
+    /** 姓名/昵称(注册未采集,可为空;为空时前端回退展示脱敏手机号) */
+    private String nickname;
+
+    /** 手机号(脱敏,形如 133****5555;仅供展示,不下发明文) */
+    private String phone;
+
+    /** 头像地址(可为空,为空时前端回退默认图标) */
+    private String avatar;
+
+    /** 会员等级(对外展示字段,取值 C普通用户 / L1~L4会员,源自 account_user.system_level) */
+    private String memberLevel;
+
+    /** 收益汇总(今日 / 本月 / 累计,单位:分) */
+    private EarningSummaryVo earnings;
+
+    public Long getUserId()
+    {
+        return userId;
+    }
+
+    public void setUserId(Long userId)
+    {
+        this.userId = userId;
+    }
+
+    public String getNickname()
+    {
+        return nickname;
+    }
+
+    public void setNickname(String nickname)
+    {
+        this.nickname = nickname;
+    }
+
+    public String getPhone()
+    {
+        return phone;
+    }
+
+    public void setPhone(String phone)
+    {
+        this.phone = phone;
+    }
+
+    public String getAvatar()
+    {
+        return avatar;
+    }
+
+    public void setAvatar(String avatar)
+    {
+        this.avatar = avatar;
+    }
+
+    public String getMemberLevel()
+    {
+        return memberLevel;
+    }
+
+    public void setMemberLevel(String memberLevel)
+    {
+        this.memberLevel = memberLevel;
+    }
+
+    public EarningSummaryVo getEarnings()
+    {
+        return earnings;
+    }
+
+    public void setEarnings(EarningSummaryVo earnings)
+    {
+        this.earnings = earnings;
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/vo/EarningSummaryVo.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/vo/EarningSummaryVo.java
new file mode 100644
index 0000000..82b463d
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/domain/vo/EarningSummaryVo.java
@@ -0,0 +1,72 @@
+package com.ruoyi.user.domain.vo;
+
+/**
+ * 会员收益汇总 VO
+ *
+ * 金额单位统一为「分」,由前端负责格式化为元展示;
+ * 无收益流水时三栏均为 0,避免前端出现 null 判断分支。
+ *
+ * @author hugray
+ */
+public class EarningSummaryVo
+{
+    /** 今日收益(单位:分) */
+    private Long today;
+
+    /** 本月收益(单位:分) */
+    private Long month;
+
+    /** 累计收益(单位:分) */
+    private Long total;
+
+    public EarningSummaryVo()
+    {
+    }
+
+    public EarningSummaryVo(Long today, Long month, Long total)
+    {
+        this.today = today;
+        this.month = month;
+        this.total = total;
+    }
+
+    /**
+     * 全零汇总:用于会员无任何收益流水的场景
+     *
+     * @return 三栏均为 0 的汇总对象
+     */
+    public static EarningSummaryVo zero()
+    {
+        return new EarningSummaryVo(0L, 0L, 0L);
+    }
+
+    public Long getToday()
+    {
+        return today;
+    }
+
+    public void setToday(Long today)
+    {
+        this.today = today;
+    }
+
+    public Long getMonth()
+    {
+        return month;
+    }
+
+    public void setMonth(Long month)
+    {
+        this.month = month;
+    }
+
+    public Long getTotal()
+    {
+        return total;
+    }
+
+    public void setTotal(Long total)
+    {
+        this.total = total;
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/enums/EarningSourceEnum.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/enums/EarningSourceEnum.java
new file mode 100644
index 0000000..0f425ec
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/enums/EarningSourceEnum.java
@@ -0,0 +1,61 @@
+package com.ruoyi.user.enums;
+
+/**
+ * 收益来源枚举
+ *
+ * 与 account_earning_record.source 字段一一对应;本期仅定义取值,
+ * 实际写入方(订单收益 / 邀请奖励 / 等级差价)待对应业务模块接入后补齐。
+ *
+ * @author hugray
+ */
+public enum EarningSourceEnum
+{
+    /** 订单收益 */
+    ORDER("1", "订单收益"),
+    /** 邀请奖励 */
+    INVITE("2", "邀请奖励"),
+    /** 等级差价 */
+    LEVEL_DIFF("3", "等级差价"),
+    /** 其他 */
+    OTHER("9", "其他");
+
+    /** 来源编码(落库值) */
+    private final String code;
+
+    /** 来源描述 */
+    private final String desc;
+
+    EarningSourceEnum(String code, String desc)
+    {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public String getCode()
+    {
+        return code;
+    }
+
+    public String getDesc()
+    {
+        return desc;
+    }
+
+    /**
+     * 按编码获取枚举
+     *
+     * @param code 来源编码
+     * @return 命中返回枚举;未命中返回 null
+     */
+    public static EarningSourceEnum getByCode(String code)
+    {
+        for (EarningSourceEnum source : values())
+        {
+            if (source.code.equals(code))
+            {
+                return source;
+            }
+        }
+        return null;
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/enums/MemberLevelEnum.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/enums/MemberLevelEnum.java
new file mode 100644
index 0000000..092f3cd
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/enums/MemberLevelEnum.java
@@ -0,0 +1,78 @@
+package com.ruoyi.user.enums;
+
+/**
+ * 会员等级枚举
+ *
+ * 与 account_user.system_level 字段一一对应(复用既有列,不再另建等级列):
+ *   C    —— 普通用户(个人中心受限展示:仅「收货地址」「联系我们」)
+ *   L1~L4 —— 会员(个人中心展示全部内容,含收益卡与全部菜单)
+ *
+ * @author hugray
+ */
+public enum MemberLevelEnum
+{
+    /** 普通用户(受限展示) */
+    C("C", "普通用户"),
+    /** 会员 L1 */
+    L1("L1", "会员"),
+    /** 会员 L2 */
+    L2("L2", "会员"),
+    /** 会员 L3 */
+    L3("L3", "会员"),
+    /** 会员 L4 */
+    L4("L4", "会员");
+
+    /** 等级编码(落库值) */
+    private final String code;
+
+    /** 等级描述 */
+    private final String desc;
+
+    /** 默认等级:普通用户,用于等级为空或非法的兜底 */
+    public static final MemberLevelEnum DEFAULT = C;
+
+    MemberLevelEnum(String code, String desc)
+    {
+        this.code = code;
+        this.desc = desc;
+    }
+
+    public String getCode()
+    {
+        return code;
+    }
+
+    public String getDesc()
+    {
+        return desc;
+    }
+
+    /**
+     * 按编码获取枚举
+     *
+     * @param code 等级编码
+     * @return 命中返回枚举;未命中返回 null
+     */
+    public static MemberLevelEnum getByCode(String code)
+    {
+        for (MemberLevelEnum level : values())
+        {
+            if (level.code.equals(code))
+            {
+                return level;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 归一化等级编码:非法或空值统一兜底为默认等级,避免脏数据影响前端展示
+     *
+     * @param code 原始等级编码
+     * @return 合法的等级编码
+     */
+    public static String normalize(String code)
+    {
+        return getByCode(code) == null ? DEFAULT.getCode() : code;
+    }
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountEarningRecordMapper.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountEarningRecordMapper.java
new file mode 100644
index 0000000..096793f
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountEarningRecordMapper.java
@@ -0,0 +1,22 @@
+package com.ruoyi.user.mapper;
+
+import org.apache.ibatis.annotations.Param;
+import com.ruoyi.user.domain.vo.EarningSummaryVo;
+
+/**
+ * 会员收益流水 数据层
+ *
+ * @author hugray
+ */
+public interface AccountEarningRecordMapper
+{
+    /**
+     * 汇总指定会员的收益(今日 / 本月 / 累计)
+     *
+     * 单条 SQL 聚合三栏,命中 idx_earning_user_time 索引;无流水时三栏均为 0。
+     *
+     * @param userId 会员ID
+     * @return 收益汇总(单位:分)
+     */
+    public EarningSummaryVo selectEarningSummary(@Param("userId") Long userId);
+}
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java
index faf33e1..a27cd26 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java
@@ -2,6 +2,7 @@
 
 import java.util.List;
 import com.ruoyi.user.domain.AccountUser;
+import com.ruoyi.user.domain.vo.AccountUserInfoVo;
 
 /**
  * 会员用户 业务层
@@ -62,4 +63,12 @@
      * @return 注册成功后的会员信息(含生成的 userId 与 myReferralCode)
      */
     public AccountUser registerMember(String phone, String referralCode);
+
+    /**
+     * 查询C端会员个人中心信息(昵称 / 头像 / 会员等级 / 收益汇总)
+     *
+     * @param userId 会员ID(取自登录态,非前端传入)
+     * @return 会员信息展示对象;会员不存在时抛业务异常
+     */
+    public AccountUserInfoVo getAccountUserInfo(Long userId);
 }
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java
index ebd2a43..98a8af8 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java
@@ -13,6 +13,10 @@
 import com.ruoyi.user.config.UniversalCodeProperties;
 import com.ruoyi.user.constant.ReferralConstants;
 import com.ruoyi.user.domain.AccountUser;
+import com.ruoyi.user.domain.vo.AccountUserInfoVo;
+import com.ruoyi.user.domain.vo.EarningSummaryVo;
+import com.ruoyi.user.enums.MemberLevelEnum;
+import com.ruoyi.user.mapper.AccountEarningRecordMapper;
 import com.ruoyi.user.mapper.AccountUserMapper;
 import com.ruoyi.user.service.IAccountUserService;
 
@@ -28,6 +32,9 @@
 
     @Autowired
     private AccountUserMapper accountUserMapper;
+
+    @Autowired
+    private AccountEarningRecordMapper accountEarningRecordMapper;
 
     @Autowired
     private UniversalCodeProperties universalCodeProperties;
@@ -118,24 +125,74 @@
         {
             throw new ServiceException("手机号已注册,请前往登录");
         }
-        // STEP 2: 解析推荐人——命中通用推荐码则不绑定推荐人,否则按码定位并校验
-        Long referrerUserId = resolveReferrerUserId(phone, referralCode);
-        // STEP 3: 生成全局唯一的本人推荐码(库内查重,冲突重试)
+        // STEP 2: 判定是否命中通用推荐码(命中则不绑定推荐人,且直接发放 L4 会员等级)
+        boolean universalReferral = isUniversalReferralCode(referralCode);
+        // STEP 3: 解析推荐人——命中通用推荐码则不绑定推荐人,否则按码定位并校验
+        Long referrerUserId = resolveReferrerUserId(phone, referralCode, universalReferral);
+        // STEP 4: 生成全局唯一的本人推荐码(库内查重,冲突重试)
         String myReferralCode = generateUniqueReferralCode();
-        // STEP 4: 组装会员信息(C端无密码,账号=手机号),落库(userId 回填)
+        // STEP 5: 组装会员信息(C端无密码,账号=手机号;姓名注册未采集,落库留空,由用户后续在资料页完善)
         AccountUser accountUser = new AccountUser();
         accountUser.setUserName(phone);
         accountUser.setPhone(phone);
-        accountUser.setNickname(phone);
         accountUser.setStatus(UserConstants.NORMAL);
         accountUser.setMyReferralCode(myReferralCode);
-        accountUser.setReferrerUserId(referrerUserId);
+        accountUser.setReferrerId(referrerUserId);
+        // 通用推荐码注册的会员直接发放 L4 等级;其余不设等级,走库默认值 C(普通用户)
+        if (universalReferral)
+        {
+            accountUser.setSystemLevel(MemberLevelEnum.L4.getCode());
+        }
         accountUser.setCreateBy(phone);
         if (insertAccountUser(accountUser) <= 0)
         {
             throw new ServiceException("注册失败,请稍后重试");
         }
         return accountUser;
+    }
+
+    @Override
+    public AccountUserInfoVo getAccountUserInfo(Long userId)
+    {
+        // STEP 1: 校验会员ID(登录态缺失时直接失败,避免无归属查询)
+        if (StringUtils.isNull(userId))
+        {
+            throw new ServiceException("获取会员信息失败");
+        }
+        // STEP 2: 按会员ID查询会员(逻辑删除已在 SQL 中过滤)
+        AccountUser accountUser = accountUserMapper.selectAccountUserById(userId);
+        if (StringUtils.isNull(accountUser))
+        {
+            throw new ServiceException("获取会员信息失败");
+        }
+        // STEP 3: 查询收益汇总(单位分),无任何流水时直接返回全零,避免前端 null 分支
+        EarningSummaryVo earnings = accountEarningRecordMapper.selectEarningSummary(userId);
+        if (StringUtils.isNull(earnings))
+        {
+            earnings = EarningSummaryVo.zero();
+        }
+        // STEP 4: 组装展示对象;等级为空或非法值统一兜底为 C(普通用户);手机号脱敏后下发
+        AccountUserInfoVo vo = new AccountUserInfoVo();
+        vo.setUserId(accountUser.getUserId());
+        vo.setNickname(accountUser.getNickname());
+        vo.setPhone(maskPhone(accountUser.getPhone()));
+        vo.setAvatar(accountUser.getAvatar());
+        vo.setMemberLevel(MemberLevelEnum.normalize(accountUser.getSystemLevel()));
+        vo.setEarnings(earnings);
+        // STEP 5: 返回个人中心展示数据
+        return vo;
+    }
+
+    /**
+     * 判定是否为通用推荐码
+     *
+     * @param referralCode 注册提交的推荐码
+     * @return true 表示命中通用推荐码
+     */
+    private boolean isUniversalReferralCode(String referralCode)
+    {
+        String universalCode = universalCodeProperties.getUniversalReferralCode();
+        return StringUtils.isNotEmpty(universalCode) && universalCode.equals(referralCode);
     }
 
     /**
@@ -146,13 +203,13 @@
      *
      * @param phone 注册手机号(仅用于日志留痕,脱敏输出)
      * @param referralCode 推荐码
+     * @param universalReferral 是否已命中通用推荐码
      * @return 推荐人用户ID;命中通用码时为 null
      */
-    private Long resolveReferrerUserId(String phone, String referralCode)
+    private Long resolveReferrerUserId(String phone, String referralCode, boolean universalReferral)
     {
-        String universalCode = universalCodeProperties.getUniversalReferralCode();
         // 命中通用推荐码:不绑定推荐人
-        if (StringUtils.isNotEmpty(universalCode) && universalCode.equals(referralCode))
+        if (universalReferral)
         {
             log.warn("[REFERRAL] universal code hit, not binding referrer, phone={}", maskPhone(phone));
             return null;
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountEarningRecordMapper.xml b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountEarningRecordMapper.xml
new file mode 100644
index 0000000..bdeafc9
--- /dev/null
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountEarningRecordMapper.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ruoyi.user.mapper.AccountEarningRecordMapper">
+
+    <resultMap type="com.ruoyi.user.domain.vo.EarningSummaryVo" id="EarningSummaryResult">
+        <result property="today" column="today" />
+        <result property="month" column="month" />
+        <result property="total" column="total" />
+    </resultMap>
+
+    <!-- 单条 SQL 聚合今日 / 本月 / 累计,避免多次数据库往返;命中 (user_id, create_time) 联合索引 -->
+    <select id="selectEarningSummary" parameterType="Long" resultMap="EarningSummaryResult">
+        select
+            ifnull(sum(case when create_time &gt;= curdate() then amount end), 0)                         as today,
+            ifnull(sum(case when create_time &gt;= 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}
+    </select>
+
+</mapper>
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml
index f2594b3..ce17e9d 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml
@@ -16,7 +16,8 @@
         <result property="status"     column="status"      />
         <result property="delFlag"    column="del_flag"    />
         <result property="myReferralCode"   column="my_referral_code"   />
-        <result property="referrerUserId"   column="referrer_user_id"   />
+        <result property="referrerId"       column="referrer_id"        />
+        <result property="systemLevel"      column="system_level"       />
         <result property="createBy"   column="create_by"   />
         <result property="createTime" column="create_time" />
         <result property="updateBy"   column="update_by"   />
@@ -25,7 +26,7 @@
     </resultMap>
 
     <sql id="selectAccountUserVo">
-        select user_id, username, phone, password, nickname, avatar, gender, birthday, status, del_flag, my_referral_code, referrer_user_id, create_by, create_time, update_by, update_time, remark
+        select user_id, username, phone, password, nickname, avatar, gender, birthday, status, del_flag, my_referral_code, referrer_id, system_level, create_by, create_time, update_by, update_time, remark
         from account_user
     </sql>
 
@@ -79,7 +80,8 @@
             <if test="birthday != null">birthday,</if>
             <if test="status != null and status != ''">status,</if>
             <if test="myReferralCode != null and myReferralCode != ''">my_referral_code,</if>
-            <if test="referrerUserId != null">referrer_user_id,</if>
+            <if test="referrerId != null">referrer_id,</if>
+            <if test="systemLevel != null and systemLevel != ''">system_level,</if>
             <if test="remark != null">remark,</if>
             <if test="createBy != null and createBy != ''">create_by,</if>
             create_time
@@ -93,7 +95,8 @@
             <if test="birthday != null">#{birthday},</if>
             <if test="status != null and status != ''">#{status},</if>
             <if test="myReferralCode != null and myReferralCode != ''">#{myReferralCode},</if>
-            <if test="referrerUserId != null">#{referrerUserId},</if>
+            <if test="referrerId != null">#{referrerId},</if>
+            <if test="systemLevel != null and systemLevel != ''">#{systemLevel},</if>
             <if test="remark != null">#{remark},</if>
             <if test="createBy != null and createBy != ''">#{createBy},</if>
             sysdate()
@@ -112,7 +115,8 @@
             <if test="birthday != null">birthday = #{birthday},</if>
             <if test="status != null and status != ''">status = #{status},</if>
             <if test="myReferralCode != null and myReferralCode != ''">my_referral_code = #{myReferralCode},</if>
-            <if test="referrerUserId != null">referrer_user_id = #{referrerUserId},</if>
+            <if test="referrerId != null">referrer_id = #{referrerId},</if>
+            <if test="systemLevel != null and systemLevel != ''">system_level = #{systemLevel},</if>
             <if test="remark != null">remark = #{remark},</if>
             <if test="updateBy != null">update_by = #{updateBy},</if>
             update_time = sysdate()
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/test/java/com/ruoyi/user/service/impl/AccountUserServiceImplTest.java b/hugray-server/ruoyi-modules/ruoyi-user/src/test/java/com/ruoyi/user/service/impl/AccountUserServiceImplTest.java
index c5e7df2..e638e85 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/test/java/com/ruoyi/user/service/impl/AccountUserServiceImplTest.java
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/test/java/com/ruoyi/user/service/impl/AccountUserServiceImplTest.java
@@ -21,12 +21,17 @@
 import com.ruoyi.common.core.exception.ServiceException;
 import com.ruoyi.user.config.UniversalCodeProperties;
 import com.ruoyi.user.domain.AccountUser;
+import com.ruoyi.user.domain.vo.AccountUserInfoVo;
+import com.ruoyi.user.domain.vo.EarningSummaryVo;
+import com.ruoyi.user.enums.MemberLevelEnum;
+import com.ruoyi.user.mapper.AccountEarningRecordMapper;
 import com.ruoyi.user.mapper.AccountUserMapper;
 
 /**
- * 会员注册服务单元测试
+ * 会员服务单元测试
  *
- * 覆盖通用推荐码放行、无效/停用推荐码拒绝、正常绑定推荐人三分支。
+ * 覆盖会员注册(通用推荐码放行、无效/停用推荐码拒绝、正常绑定推荐人)
+ * 与个人中心会员信息查询(正常返回、入参/会员缺失校验、收益与等级兜底)。
  *
  * @author hugray
  */
@@ -39,6 +44,9 @@
 
     @Mock
     private AccountUserMapper accountUserMapper;
+
+    @Mock
+    private AccountEarningRecordMapper accountEarningRecordMapper;
 
     @Mock
     private UniversalCodeProperties universalCodeProperties;
@@ -60,8 +68,12 @@
         ArgumentCaptor<AccountUser> captor = ArgumentCaptor.forClass(AccountUser.class);
         verify(accountUserMapper).insertAccountUser(captor.capture());
         AccountUser saved = captor.getValue();
-        assertNull(saved.getReferrerUserId());
+        assertNull(saved.getReferrerId());
         assertNotNull(saved.getMyReferralCode());
+        // 姓名注册未采集,落库留空(不再以手机号填充)
+        assertNull(saved.getNickname());
+        // 通用推荐码注册直接发放 L4 会员等级
+        assertEquals(MemberLevelEnum.L4.getCode(), saved.getSystemLevel());
         // 通用码不参与推荐人定位
         verify(accountUserMapper, never()).selectAccountUserByReferralCode(UNIVERSAL_CODE);
     }
@@ -113,6 +125,106 @@
 
         ArgumentCaptor<AccountUser> captor = ArgumentCaptor.forClass(AccountUser.class);
         verify(accountUserMapper).insertAccountUser(captor.capture());
-        assertEquals(100L, captor.getValue().getReferrerUserId());
+        assertEquals(100L, captor.getValue().getReferrerId());
+        // 非通用码注册不设等级,走库默认值 C(普通用户)
+        assertNull(captor.getValue().getSystemLevel());
+    }
+
+    @Test
+    @DisplayName("会员存在时应返回昵称/头像/等级与收益汇总")
+    void getAccountUserInfo_shouldReturnProfile_whenMemberExists()
+    {
+        AccountUser member = new AccountUser();
+        member.setUserId(66L);
+        member.setNickname("辉煌G891");
+        member.setPhone("13355555555");
+        member.setAvatar("https://cdn.example.com/a.png");
+        member.setSystemLevel(MemberLevelEnum.L3.getCode());
+        EarningSummaryVo summary = new EarningSummaryVo(1000L, 40000L, 12800000L);
+        when(accountUserMapper.selectAccountUserById(66L)).thenReturn(member);
+        when(accountEarningRecordMapper.selectEarningSummary(66L)).thenReturn(summary);
+
+        AccountUserInfoVo vo = accountUserService.getAccountUserInfo(66L);
+
+        assertEquals(66L, vo.getUserId());
+        assertEquals("辉煌G891", vo.getNickname());
+        assertEquals("133****5555", vo.getPhone());
+        assertEquals("https://cdn.example.com/a.png", vo.getAvatar());
+        assertEquals(MemberLevelEnum.L3.getCode(), vo.getMemberLevel());
+        assertEquals(1000L, vo.getEarnings().getToday());
+        assertEquals(40000L, vo.getEarnings().getMonth());
+        assertEquals(12800000L, vo.getEarnings().getTotal());
+    }
+
+    @Test
+    @DisplayName("会员ID为空时应抛出获取会员信息失败")
+    void getAccountUserInfo_shouldThrow_whenUserIdNull()
+    {
+        ServiceException ex = assertThrows(ServiceException.class,
+            () -> accountUserService.getAccountUserInfo(null));
+        assertEquals("获取会员信息失败", ex.getMessage());
+    }
+
+    @Test
+    @DisplayName("会员不存在时应抛出获取会员信息失败")
+    void getAccountUserInfo_shouldThrow_whenMemberNotFound()
+    {
+        when(accountUserMapper.selectAccountUserById(66L)).thenReturn(null);
+
+        ServiceException ex = assertThrows(ServiceException.class,
+            () -> accountUserService.getAccountUserInfo(66L));
+        assertEquals("获取会员信息失败", ex.getMessage());
+    }
+
+    @Test
+    @DisplayName("无收益流水时应兜底返回全零汇总")
+    void getAccountUserInfo_shouldFallbackZeroEarnings_whenNoRecord()
+    {
+        AccountUser member = new AccountUser();
+        member.setUserId(66L);
+        member.setSystemLevel(MemberLevelEnum.L1.getCode());
+        when(accountUserMapper.selectAccountUserById(66L)).thenReturn(member);
+        when(accountEarningRecordMapper.selectEarningSummary(66L)).thenReturn(null);
+
+        AccountUserInfoVo vo = accountUserService.getAccountUserInfo(66L);
+
+        assertNotNull(vo.getEarnings());
+        assertEquals(0L, vo.getEarnings().getToday());
+        assertEquals(0L, vo.getEarnings().getMonth());
+        assertEquals(0L, vo.getEarnings().getTotal());
+    }
+
+    @Test
+    @DisplayName("等级为空时应兜底为 C")
+    void getAccountUserInfo_shouldFallbackC_whenSystemLevelBlank()
+    {
+        assertFallbackLevel(null, MemberLevelEnum.C.getCode());
+        assertFallbackLevel("", MemberLevelEnum.C.getCode());
+    }
+
+    @Test
+    @DisplayName("等级为非法值时应兜底为 C")
+    void getAccountUserInfo_shouldFallbackC_whenSystemLevelIllegal()
+    {
+        assertFallbackLevel("L9", MemberLevelEnum.C.getCode());
+    }
+
+    /**
+     * 校验不同等级取值下的等级兜底结果
+     *
+     * @param rawLevel 库中原始等级值
+     * @param expectedLevel 期望下发的等级
+     */
+    private void assertFallbackLevel(String rawLevel, String expectedLevel)
+    {
+        AccountUser member = new AccountUser();
+        member.setUserId(66L);
+        member.setSystemLevel(rawLevel);
+        when(accountUserMapper.selectAccountUserById(66L)).thenReturn(member);
+        when(accountEarningRecordMapper.selectEarningSummary(66L)).thenReturn(EarningSummaryVo.zero());
+
+        AccountUserInfoVo vo = accountUserService.getAccountUserInfo(66L);
+
+        assertEquals(expectedLevel, vo.getMemberLevel());
     }
 }
diff --git a/hugray-server/sql/hugray_account_20261012.sql b/hugray-server/sql/hugray_account_20261012.sql
index ac23db4..2942c5f 100644
--- a/hugray-server/sql/hugray_account_20261012.sql
+++ b/hugray-server/sql/hugray_account_20261012.sql
@@ -2,7 +2,12 @@
 -- C 端会员用户表变更(ruoyi-user 用户服务)
 --
 -- 需求:用户小程序注册需支持「推荐码(邀请码)」。
--- 变更内容:account_user 增加「本人推荐码」与「推荐人会员ID」两个字段,并为本人推荐码建唯一索引。
+-- 变更内容:account_user 增加「本人推荐码」列,并为其建唯一索引。
+--
+-- 说明(按权威库 192.168.110.111 的既有字段调整):
+--   权威库已存在「推荐人」列 referrer_id(自连接 account_user.user_id)与
+--   system_level,故本脚本不再新增 referrer_user_id / member_level 等重复列,
+--   推荐关系直接复用既有 referrer_id。
 --
 -- 目标库 hugray-account 由使用方预先创建(utf8mb4)。
 -- 注意:库名含连字符,任何引用都必须用反引号。
@@ -10,10 +15,9 @@
 
 USE `hugray-account`;
 
--- 变更语句:新增两个字段
+-- 变更语句:新增「本人推荐码」列(权威库无此列,推荐码机制所需)
 ALTER TABLE `account_user`
-  ADD COLUMN `my_referral_code` varchar(20) DEFAULT NULL COMMENT '本人推荐码(注册时生成,全局唯一)' AFTER `remark`,
-  ADD COLUMN `referrer_user_id` bigint(20) DEFAULT NULL COMMENT '推荐人会员ID(关联 account_user.user_id)' AFTER `my_referral_code`;
+  ADD COLUMN `my_referral_code` varchar(20) DEFAULT NULL COMMENT '本人推荐码(注册时生成,全局唯一)' AFTER `remark`;
 
 -- 本人推荐码全局唯一:随机生成且不重复使用,逻辑删除行占用不影响业务
 CREATE UNIQUE INDEX `uk_account_user_referral_code` ON `account_user` (`my_referral_code`);
@@ -22,4 +26,4 @@
 -- 回滚语句(如需回滚,执行以下语句)
 -- ----------------------------
 -- DROP INDEX `uk_account_user_referral_code` ON `account_user`;
--- ALTER TABLE `account_user` DROP COLUMN `referrer_user_id`, DROP COLUMN `my_referral_code`;
+-- ALTER TABLE `account_user` DROP COLUMN `my_referral_code`;
diff --git a/hugray-server/sql/hugray_account_20261013.sql b/hugray-server/sql/hugray_account_20261013.sql
new file mode 100644
index 0000000..1bfd89b
--- /dev/null
+++ b/hugray-server/sql/hugray_account_20261013.sql
@@ -0,0 +1,37 @@
+-- ----------------------------
+-- C 端会员 个人中心 相关变更(ruoyi-user 用户服务)
+--
+-- 需求:个人中心页需展示「会员等级」与「今日/本月/累计收益」。
+-- 变更内容:新增「会员收益流水表」account_earning_record,收益三栏由其按时间实时聚合。
+--
+-- 说明(按权威库 192.168.110.111 的既有字段调整):
+--   会员等级复用权威库既有列 account_user.system_level(C<L1<L2<L3<L4,从低到高),
+--   故本脚本不再新增 member_level 重复列。
+--
+-- 目标库 hugray-account 由使用方预先创建(utf8mb4)。
+-- 注意:库名含连字符,任何引用都必须用反引号。
+-- ----------------------------
+
+USE `hugray-account`;
+
+-- 变更:新增会员收益流水表(一条流水=一次收益事件,三栏由 SQL 按时间聚合)
+CREATE 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`),
+  -- 业务幂等:同来源同业务单号唯一(biz_no 为空时 MySQL 唯一索引不冲突,允许手工补录)
+  UNIQUE KEY `uk_earning_source_biz` (`source`, `biz_no`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员收益流水表';
+
+-- ----------------------------
+-- 回滚语句(如需回滚,执行以下语句)
+-- ----------------------------
+-- DROP TABLE IF EXISTS `account_earning_record`;
diff --git a/hugray-user-miniapp/api/index.js b/hugray-user-miniapp/api/index.js
index 1ed7643..cc7e5ec 100644
--- a/hugray-user-miniapp/api/index.js
+++ b/hugray-user-miniapp/api/index.js
@@ -20,13 +20,6 @@
 }
 
 /**
- * 获取当前登录人信息(含会员等级 L1~L4 / 普通用户)
- */
-export function getUserInfo() {
-    return get('/miniapp/system/user/getInfo')
-}
-
-/**
  * 手机号一键登录(微信手机号快速验证,凭证换取登录态)
  * @param {Object} data { code } 手机号快速验证凭证
  */
diff --git a/hugray-user-miniapp/pages.json b/hugray-user-miniapp/pages.json
index 2cb82bc..fdb3bde 100644
--- a/hugray-user-miniapp/pages.json
+++ b/hugray-user-miniapp/pages.json
@@ -30,6 +30,13 @@
 			}
 		},
 		{
+			"path": "pages/profile/edit",
+			// 导航栏含更多 / 帮助图标,需自定义导航栏承载
+			"style": {
+				"navigationStyle": "custom"
+			}
+		},
+		{
 			"path": "pages/contact/index",
 			// 顶部为深蓝底品牌区,与白底原生导航冲突,故使用自定义导航栏
 			"style": {
diff --git a/hugray-user-miniapp/pages/profile/edit.vue b/hugray-user-miniapp/pages/profile/edit.vue
new file mode 100644
index 0000000..9cf013f
--- /dev/null
+++ b/hugray-user-miniapp/pages/profile/edit.vue
@@ -0,0 +1,447 @@
+<template>
+    <view class="page">
+        <!-- 自定义导航栏:状态栏留白由行内 paddingTop 撑开 -->
+        <view class="nav" :style="{ paddingTop: statusBarHeight + 'px' }">
+            <view class="nav-bar">
+                <!-- 返回 -->
+                <view class="nav-back" @tap="handleBack">
+                    <view class="icon-back"></view>
+                </view>
+                <text class="nav-title">我的资料</text>
+                <!-- 更多 / 帮助(本期占位) -->
+                <view class="nav-actions">
+                    <view class="nav-action" @tap="handlePlaceholder('更多')">
+                        <view class="icon-more">
+                            <view class="dot"></view>
+                            <view class="dot"></view>
+                            <view class="dot"></view>
+                        </view>
+                    </view>
+                    <view class="nav-action" @tap="handlePlaceholder('帮助')">
+                        <view class="icon-help"><text class="icon-help-text">?</text></view>
+                    </view>
+                </view>
+            </view>
+        </view>
+
+        <view class="body">
+            <view class="list">
+                <!-- 头像:点击更换(本期占位) -->
+                <view class="row" @tap="handlePlaceholder('头像更换')">
+                    <text class="row-label">头像</text>
+                    <view class="row-right">
+                        <view class="avatar">
+                            <image v-if="avatar" class="avatar-img" :src="avatar" mode="aspectFill" />
+                            <view v-else class="icon icon-camera"></view>
+                        </view>
+                        <view class="icon icon-chevron-right"></view>
+                    </view>
+                </view>
+
+                <!-- 手机号:小程序内暂不支持更换,仅只读展示(已脱敏),故无箭头 -->
+                <view class="row">
+                    <text class="row-label">手机号</text>
+                    <text class="row-value">{{ phone || '—' }}</text>
+                </view>
+
+                <!-- 实名认证 -->
+                <view class="row" @tap="handlePlaceholder('实名认证')">
+                    <text class="row-label">实名认证</text>
+                    <view class="row-right">
+                        <view class="badge" :class="realNameBadge.cls">
+                            <text class="badge-text">{{ realNameBadge.text }}</text>
+                        </view>
+                        <view class="icon icon-chevron-right"></view>
+                    </view>
+                </view>
+
+                <!-- 企业 / 个体户认证 -->
+                <view class="row" @tap="handlePlaceholder('企业/个体户认证')">
+                    <text class="row-label">企业/个体户认证</text>
+                    <view class="row-right">
+                        <view class="badge" :class="companyBadge.cls">
+                            <text class="badge-text">{{ companyBadge.text }}</text>
+                        </view>
+                        <view class="icon icon-chevron-right"></view>
+                    </view>
+                </view>
+            </view>
+
+            <!-- 底部操作区 -->
+            <view class="footer">
+                <view class="btn-logout" @tap="handleLogout">
+                    <text class="btn-logout-text">退出登录</text>
+                </view>
+                <text class="link-cancel" @tap="handlePlaceholder('注销账号')">注销账号</text>
+            </view>
+        </view>
+    </view>
+</template>
+
+<script setup>
+import { computed, ref } from 'vue'
+import { onShow } from '@dcloudio/uni-app'
+import { clearToken, ensureLogin } from '@/utils/request.js'
+import { getMemberInfo, logout } from '@/api/index.js'
+
+/** 状态栏高度:自定义导航栏下用于顶部留白 */
+const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
+
+/** 用户头像:登录后由 getMemberInfo 下发,为空时回退相机占位 */
+const avatar = ref('')
+
+/** 用户手机号:登录后由 getMemberInfo 下发(已脱敏,形如 133****5555) */
+const phone = ref('')
+
+/**
+ * 认证状态字典:key 为后端下发的状态枚举,值含徽标文案与样式类
+ * 本期为原型,两处认证均取 none(未认证)占位,后端接入后直接替换 ref 初值即可
+ */
+const AUTH_STATUS_MAP = {
+    none: { text: '未认证', cls: 'badge--none' },
+    ing: { text: '认证中', cls: 'badge--ing' },
+    fail: { text: '认证失败', cls: 'badge--fail' },
+    done: { text: '已认证', cls: 'badge--done' }
+}
+
+/** 实名认证状态(本期占位) */
+const realNameStatus = ref('none')
+
+/** 企业 / 个体户认证状态(本期占位) */
+const companyStatus = ref('none')
+
+/** 实名认证徽标:状态缺省兜底「未认证」 */
+const realNameBadge = computed(() => AUTH_STATUS_MAP[realNameStatus.value] || AUTH_STATUS_MAP.none)
+
+/** 企业 / 个体户认证徽标:状态缺省兜底「未认证」 */
+const companyBadge = computed(() => AUTH_STATUS_MAP[companyStatus.value] || AUTH_STATUS_MAP.none)
+
+/** 返回上一页:无上级页面(如直接进入)时回到个人中心 */
+const handleBack = () => {
+    const pages = getCurrentPages()
+    if (pages.length > 1) {
+        uni.navigateBack()
+        return
+    }
+    uni.switchTab({ url: '/pages/profile/index' })
+}
+
+/** 占位交互:头像更换 / 实名认证 / 企业认证 / 注销 / 更多 / 帮助本期均不接后端 */
+const handlePlaceholder = (name) => {
+    uni.showToast({ title: `${name}功能开发中`, icon: 'none' })
+}
+
+/** 退出登录:二次确认后调用退出接口并清理本地登录态,回到个人中心 */
+const handleLogout = () => {
+    uni.showModal({
+        title: '提示',
+        content: '确定要退出登录吗?',
+        confirmText: '退出',
+        cancelText: '取消',
+        success: async (res) => {
+            if (!res.confirm) return
+            try {
+                await logout()
+            } catch (error) {
+                // 退出接口失败不阻塞本地退出,仅记录上下文日志
+                console.error('[PROFILE_EDIT] 退出登录接口失败', error)
+            }
+            clearToken()
+            uni.reLaunch({ url: '/pages/profile/index' })
+        }
+    })
+}
+
+/** 拉取当前会员信息(头像 / 脱敏手机号),失败时静默降级为空 */
+const loadMemberInfo = async () => {
+    try {
+        const res = await getMemberInfo()
+        const data = res.data || {}
+        avatar.value = data.avatar || ''
+        phone.value = data.phone || ''
+    } catch (error) {
+        console.error('[PROFILE_EDIT] 拉取会员信息失败', error)
+        avatar.value = ''
+        phone.value = ''
+    }
+}
+
+// 进入页面校验登录态,未登录跳登录页;已登录则拉取会员信息
+onShow(() => {
+    if (!ensureLogin()) return
+    loadMemberInfo()
+})
+</script>
+
+<style scoped lang="scss">
+.page {
+    min-height: 100vh;
+    display: flex;
+    flex-direction: column;
+    box-sizing: border-box;
+    background-color: #ffffff;
+}
+
+/* ==================== 自定义导航栏 ==================== */
+
+.nav {
+    flex-shrink: 0;
+    background-color: #ffffff;
+}
+
+.nav-bar {
+    position: relative;
+    height: 88rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.nav-back {
+    position: absolute;
+    left: 16rpx;
+    top: 50%;
+    transform: translateY(-50%);
+    width: 64rpx;
+    height: 64rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+/* 纯 CSS 左向箭头:左 + 下边框旋转 45° */
+.icon-back {
+    width: 22rpx;
+    height: 22rpx;
+    border-left: 4rpx solid #1f2937;
+    border-bottom: 4rpx solid #1f2937;
+    transform: rotate(45deg);
+}
+
+.nav-title {
+    font-size: 34rpx;
+    font-weight: 700;
+    line-height: 1;
+    letter-spacing: 1rpx;
+    color: #0f172a;
+}
+
+.nav-actions {
+    position: absolute;
+    right: 28rpx;
+    top: 50%;
+    transform: translateY(-50%);
+    display: flex;
+    align-items: center;
+    gap: 32rpx;
+}
+
+.nav-action {
+    width: 56rpx;
+    height: 56rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+/* 更多:三点 */
+.icon-more {
+    display: flex;
+    align-items: center;
+    gap: 6rpx;
+}
+
+.icon-more .dot {
+    width: 8rpx;
+    height: 8rpx;
+    border-radius: 50%;
+    background: #334155;
+}
+
+/* 帮助:圆圈问号 */
+.icon-help {
+    width: 36rpx;
+    height: 36rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    box-sizing: border-box;
+    border: 3rpx solid #334155;
+    border-radius: 50%;
+}
+
+.icon-help-text {
+    font-size: 22rpx;
+    font-weight: 700;
+    line-height: 1;
+    color: #334155;
+}
+
+/* ==================== 资料列表 ==================== */
+
+.body {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+}
+
+.list {
+    padding: 8rpx 32rpx 0;
+}
+
+.row {
+    min-height: 116rpx;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    border-bottom: 2rpx solid #f1f5f9;
+}
+
+/* 末行无分隔线 */
+.row:last-child {
+    border-bottom: none;
+}
+
+.row-label {
+    font-size: 30rpx;
+    line-height: 1;
+    color: #1f2937;
+}
+
+.row-right {
+    display: flex;
+    align-items: center;
+    gap: 16rpx;
+}
+
+.row-value {
+    font-size: 30rpx;
+    line-height: 1;
+    letter-spacing: 1rpx;
+    color: #94a3b8;
+}
+
+/* 头像容器:46pt 圆形 */
+.avatar {
+    width: 92rpx;
+    height: 92rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    box-sizing: border-box;
+    border-radius: 50%;
+    background: #f1f5f9;
+    border: 2rpx solid #e2e8f0;
+    overflow: hidden;
+}
+
+.avatar-img {
+    width: 100%;
+    height: 100%;
+    border-radius: 50%;
+}
+
+/* ==================== 认证状态徽标(4 态) ==================== */
+
+.badge {
+    display: flex;
+    align-items: center;
+    padding: 10rpx 20rpx;
+    border-radius: 12rpx;
+    border: 2rpx solid;
+}
+
+.badge-text {
+    font-size: 26rpx;
+    font-weight: 500;
+    line-height: 1;
+}
+
+/* 未认证 */
+.badge--none {
+    color: #ea580c;
+    background: #fff7ed;
+    border-color: #fdba74;
+}
+
+/* 认证中 */
+.badge--ing {
+    color: #1e5ebc;
+    background: #eff6ff;
+    border-color: #93c5fd;
+}
+
+/* 认证失败 */
+.badge--fail {
+    color: #dc2626;
+    background: #fef2f2;
+    border-color: #fca5a5;
+}
+
+/* 已认证 */
+.badge--done {
+    color: #4d7c0f;
+    background: #f7fee7;
+    border-color: #bef264;
+}
+
+/* ==================== 底部操作区 ==================== */
+
+.footer {
+    /* 内容不足一屏时按钮自动贴底 */
+    margin-top: auto;
+    padding: 32rpx 48rpx 44rpx;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 36rpx;
+}
+
+.btn-logout {
+    width: 100%;
+    height: 96rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 48rpx;
+    background: linear-gradient(90deg, #82c236 0%, #639c1f 100%);
+    box-shadow: 0 16rpx 40rpx rgba(99, 156, 31, 0.28);
+}
+
+.btn-logout-text {
+    font-size: 30rpx;
+    font-weight: 600;
+    line-height: 1;
+    color: #ffffff;
+}
+
+.link-cancel {
+    font-size: 28rpx;
+    line-height: 1;
+    color: #1e5ebc;
+    text-decoration: underline;
+}
+
+/* ==================== 图标(内联 base64 SVG) ==================== */
+
+.icon {
+    flex-shrink: 0;
+    background-repeat: no-repeat;
+    background-position: center center;
+    background-size: 100% 100%;
+}
+
+/* 相机占位(#94A3B8) */
+.icon-camera {
+    width: 40rpx;
+    height: 40rpx;
+    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTRBM0I4IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0iTTE0LjUgNGgtNUw3IDdINGEyIDIgMCAwIDAtMiAydjlhMiAyIDAgMCAwIDIgMmgxNmEyIDIgMCAwIDAgMi0yVjlhMiAyIDAgMCAwLTItMmgtM2wtMi41LTN6Ii8+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMyIgcj0iMyIvPjwvc3ZnPg==');
+}
+
+/* 右箭头(#94A3B8) */
+.icon-chevron-right {
+    width: 32rpx;
+    height: 32rpx;
+    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOTRBM0I4IiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCI+PHBhdGggZD0ibTkgMTggNi02LTYtNiIvPjwvc3ZnPg==');
+}
+</style>
diff --git a/hugray-user-miniapp/pages/profile/index.vue b/hugray-user-miniapp/pages/profile/index.vue
index 1da43be..38ce0af 100644
--- a/hugray-user-miniapp/pages/profile/index.vue
+++ b/hugray-user-miniapp/pages/profile/index.vue
@@ -7,11 +7,13 @@
         <view class="header">
             <view class="user-main" @tap="handleAccountTap">
                 <view class="avatar">
-                    <view class="icon" :class="isLoggedIn ? 'icon-camera' : 'icon-user'"></view>
+                    <!-- 有头像则渲染图片,否则回退图标(已登录相机 / 未登录用户) -->
+                    <image v-if="avatar" class="avatar-img" :src="avatar" mode="aspectFill" />
+                    <view v-else class="icon" :class="isLoggedIn ? 'icon-camera' : 'icon-user'"></view>
                 </view>
                 <view class="user-text">
                     <!-- 已登录:昵称 + 会员等级;未登录:登录入口 + 引导文案 -->
-                    <text class="user-name">{{ isLoggedIn ? nickname : '点击登录 / 注册' }}</text>
+                    <text class="user-name">{{ isLoggedIn ? displayName : '点击登录 / 注册' }}</text>
                     <view v-if="isLoggedIn" class="level-pill">
                         <view class="level-medal">
                             <text class="level-medal-text">V</text>
@@ -26,7 +28,7 @@
             </view>
         </view>
 
-        <!-- 收益数据展示区(343×74pt):未登录或 L0 普通用户不展示 -->
+        <!-- 收益数据展示区(343×74pt):未登录或 C 普通用户不展示 -->
         <view v-if="!isRestrictedView" class="earnings-card">
             <view v-for="item in earnings" :key="item.key" class="earning-item">
                 <text class="earning-label">{{ item.label }}</text>
@@ -71,6 +73,8 @@
 import { computed, ref } from 'vue'
 import { onShow } from '@dcloudio/uni-app'
 import { getToken } from '@/utils/request.js'
+import { getMemberInfo } from '@/api/index.js'
+import { formatYuan } from '@/utils/format.js'
 import config from '@/config/index.js'
 import StockOrderToast from '@/components/stock-order-toast/stock-order-toast.vue'
 
@@ -80,22 +84,34 @@
 /** 登录态:本页需同时兼容「已登录 / 未登录」两种展示,故以本地 token 判定 */
 const isLoggedIn = ref(false)
 
-/** 用户昵称:用户信息接口未就绪,先本地 mock(联调后替换为 getUserInfo) */
-const nickname = ref('辉煌G891')
+/** 用户姓名/昵称:登录后由 getMemberInfo 下发;注册未采集,通常为空 */
+const nickname = ref('')
 
-/** 用户等级:取值 L0~L4,其中 L0 为普通用户;等级接口未联调,先本地 mock(联调后取 getUserInfo 下发等级) */
-const userLevel = ref('L4')
+/** 用户手机号:登录后由 getMemberInfo 下发(已脱敏,形如 133****5555) */
+const phone = ref('')
 
-/** 是否受限展示:未登录或 L0 普通用户,个人中心仅展示「收货地址」「联系我们」 */
-const isRestrictedView = computed(() => !isLoggedIn.value || userLevel.value === 'L0')
+/** 头部展示名:姓名优先,为空时回退展示脱敏手机号 */
+const displayName = computed(() => nickname.value || phone.value)
 
-/** 收益三栏:未登录态统一展示 0.00 */
+/** 用户头像:登录后由 getMemberInfo 下发,为空时回退默认图标 */
+const avatar = ref('')
+
+/** 用户等级:取值 C/L1~L4,登录后由 getMemberInfo 下发;C 为普通用户 */
+const userLevel = ref('C')
+
+/** 是否受限展示:未登录或 C 普通用户,个人中心仅展示「收货地址」「联系我们」 */
+const isRestrictedView = computed(() => !isLoggedIn.value || userLevel.value === 'C')
+
+/** 收益汇总原始值(单位:分),登录后由 getMemberInfo 下发,未登录保持全零 */
+const earningsRaw = ref({ today: 0, month: 0, total: 0 })
+
+/** 收益三栏:后端下发单位为分,统一格式化为元展示;未登录统一展示 0.00 */
 const earnings = computed(() => {
-    const zeroValue = '0.00'
+    const source = isLoggedIn.value ? earningsRaw.value : { today: 0, month: 0, total: 0 }
     return [
-        { key: 'today', label: '今日收益', value: isLoggedIn.value ? '10.00' : zeroValue },
-        { key: 'month', label: '本月收益', value: isLoggedIn.value ? '400.00' : zeroValue },
-        { key: 'total', label: '累计收益', value: isLoggedIn.value ? '128000.00' : zeroValue }
+        { key: 'today', label: '今日收益', value: formatYuan(source.today) },
+        { key: 'month', label: '本月收益', value: formatYuan(source.month) },
+        { key: 'total', label: '累计收益', value: formatYuan(source.total) }
     ]
 })
 
@@ -109,18 +125,18 @@
     { key: 'contact', icon: 'contact', title: '联系我们' }
 ]
 
-/** 当前可见菜单:受限展示(未登录 / L0)时仅保留「收货地址」「联系我们」 */
+/** 当前可见菜单:受限展示(未登录 / C)时仅保留「收货地址」「联系我们」 */
 const menuList = computed(() =>
     isRestrictedView.value ? MENU_LIST.filter((menu) => !menu.restricted) : MENU_LIST
 )
 
-/** 头像 / 名称 / 按钮点击:未登录跳登录页,已登录进入资料编辑 */
+/** 头像 / 名称 / 编辑资料按钮点击:未登录跳登录页,已登录进入「我的资料」页 */
 const handleAccountTap = () => {
     if (!isLoggedIn.value) {
         uni.reLaunch({ url: config.loginPage })
         return
     }
-    uni.showToast({ title: '敬请期待', icon: 'none' })
+    uni.navigateTo({ url: '/pages/profile/edit' })
 }
 
 /** 菜单项点击:未登录先提示登录;已登录进入二级功能页(未接入的暂为占位反馈) */
@@ -151,9 +167,37 @@
     uni.showToast({ title: '敬请期待', icon: 'none' })
 }
 
-// 每次页面显示均刷新登录态,保证登录 / 退出后展示态同步
+/**
+ * 拉取当前会员信息(昵称 / 头像 / 等级 / 收益汇总)
+ * 失败时静默降级为普通用户态,不阻塞页面展示
+ */
+const loadMemberInfo = async () => {
+    try {
+        const res = await getMemberInfo()
+        const data = res.data || {}
+        nickname.value = data.nickname || ''
+        // 手机号后端已脱敏(133****5555),可直接用于展示兜底
+        phone.value = data.phone || ''
+        avatar.value = data.avatar || ''
+        // 等级缺省兜底 C,与后端 MemberLevelEnum.normalize 保持一致
+        userLevel.value = data.memberLevel || 'C'
+        earningsRaw.value = data.earnings || { today: 0, month: 0, total: 0 }
+    } catch (error) {
+        console.error('[PROFILE] 拉取会员信息失败', error)
+        nickname.value = ''
+        phone.value = ''
+        avatar.value = ''
+        userLevel.value = 'C'
+        earningsRaw.value = { today: 0, month: 0, total: 0 }
+    }
+}
+
+// 每次页面显示均刷新登录态,保证登录 / 退出后展示态同步;已登录则拉取会员信息
 onShow(() => {
     isLoggedIn.value = Boolean(getToken())
+    if (isLoggedIn.value) {
+        loadMemberInfo()
+    }
 })
 </script>
 
@@ -208,6 +252,13 @@
     border: 5rpx solid #ffffff;
 }
 
+/* 用户头像图片:铺满头像容器并裁为圆形 */
+.avatar-img {
+    width: 100%;
+    height: 100%;
+    border-radius: 50%;
+}
+
 /* 已登录:相机图标;未登录:用户图标 */
 .icon-camera {
     width: 64rpx;
diff --git a/hugray-user-miniapp/utils/format.js b/hugray-user-miniapp/utils/format.js
new file mode 100644
index 0000000..6fef783
--- /dev/null
+++ b/hugray-user-miniapp/utils/format.js
@@ -0,0 +1,20 @@
+/**
+ * 通用格式化工具(用户端)
+ * 约定:后端金额统一以「分」下发,前端展示统一转为「元」
+ */
+
+/**
+ * 金额分 → 元
+ * @param {Number|String} fen          金额(单位:分)
+ * @param {Boolean}       [withThousands=false] 是否添加千分位分隔(如 128,000.00)
+ * @returns {String} 保留两位小数的金额字符串,如 12800000 → '128000.00'
+ */
+export function formatYuan(fen, withThousands = false) {
+    const num = Number(fen)
+    // 非法、空值统一按 0 处理,避免页面出现 NaN
+    const yuan = Number.isFinite(num) ? num / 100 : 0
+    const fixed = yuan.toFixed(2)
+    if (!withThousands) return fixed
+    const [intPart, decPart] = fixed.split('.')
+    return intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',') + '.' + decPart
+}

--
Gitblit v1.7.1