From 22e936e491b49fddbc664dd0709df55545d2d1fd Mon Sep 17 00:00:00 2001
From: zjk <852185829@qq.com>
Date: 星期三, 14 十月 2026 14:53:35 +0800
Subject: [PATCH] feat(服务商小程序与认证服务): 实现服务商小程序登录与忘记密码功能

---
 doc/nacos/dev/ruoyi-gateway-dev.yml                                                                                       |    2 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/impl/AccountUserServiceImpl.java              |   37 +
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/PartnerLoginBody.java                                          |   41 +
 hugray-partner-miniapp/static/check-icon.png                                                                              |    0 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java                         |    8 
 hugray-partner-miniapp/static/medical-plus-icon.png                                                                       |    0 
 hugray-partner-miniapp/pages.json                                                                                         |    4 
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/ResetPasswordBody.java                                         |   59 +
 doc/design/服务商端_登录_20261014.md                                                                                            |  172 +++++
 doc/design/服务商端_忘记密码_20261014.md                                                                                          |  194 ++++++
 hugray-partner-miniapp/api/index.js                                                                                       |    6 
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/controller/AccountMemberInnerController.java          |   43 +
 hugray-server/ruoyi-modules/ruoyi-user/src/main/resources/mapper/user/AccountUserMapper.xml                               |   14 
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/PasswordResetService.java                                   |  126 +++
 hugray-partner-miniapp/static/industry-4-0-slogan-card.png                                                                |    0 
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/PartnerLoginService.java                                    |   95 ++
 hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/service/IAccountUserService.java                      |   19 
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SmsCodeService.java                                         |   14 
 hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteAccountUserService.java                 |   26 
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/AccountAuthController.java                               |   35 +
 hugray-partner-miniapp/pages/login/forgot.vue                                                                             |  333 ++++++++-
 hugray-partner-miniapp/pages/login/index.vue                                                                              |  473 ++++++++++----
 hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/AccountResetPasswordRequest.java        |   61 +
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountLoginService.java                                    |   31 
 hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteAccountUserFallbackFactory.java |   13 
 hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/AccountMember.java                     |   33 +
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsCodeBody.java                                               |    2 
 hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountTokenIssuer.java                                     |   65 ++
 hugray-partner-miniapp/static/metabolism-300-logo.png                                                                     |    0 
 29 files changed, 1,678 insertions(+), 228 deletions(-)

diff --git "a/doc/design/\346\234\215\345\212\241\345\225\206\347\253\257_\345\277\230\350\256\260\345\257\206\347\240\201_20261014.md" "b/doc/design/\346\234\215\345\212\241\345\225\206\347\253\257_\345\277\230\350\256\260\345\257\206\347\240\201_20261014.md"
new file mode 100644
index 0000000..335bf25
--- /dev/null
+++ "b/doc/design/\346\234\215\345\212\241\345\225\206\347\253\257_\345\277\230\350\256\260\345\257\206\347\240\201_20261014.md"
@@ -0,0 +1,194 @@
+# 服务商端小程序 忘记密码(重置密码)开发设计文档
+
+- 模块:`hugray-partner-miniapp`(前端)、`ruoyi-auth` / `ruoyi-user` / `ruoyi-api-system` / `ruoyi-gateway`(后端)
+- 日期:2026-10-14
+- 需求来源:“继续实现服务商小程序的忘记密码功能,包括页面的 ui 设计以及后端的业务实现”
+- 风格参照:`hugray-partner-miniapp/pages/login/index.vue`(同品牌登录页)
+- 改造目标文件:
+  - 前端:`hugray-partner-miniapp/pages/login/forgot.vue`、`hugray-partner-miniapp/pages.json`、`hugray-partner-miniapp/api/index.js`
+  - 后端:`ruoyi-auth`、`ruoyi-user`、`ruoyi-api-system`、网关白名单
+- 依赖既有能力(本次仅复用,不改):
+  - `SmsCodeService.sendCode / verifyAndConsume`(`ruoyi-auth`)
+  - `SmsSceneEnum.RESET`(已存在,`reset` = 重置密码)
+  - `SecurityUtils.encryptPassword`(BCrypt,`ruoyi-common-security`)
+
+---
+
+## 一、功能概述
+
+服务商端「忘记密码」= 通过**已注册手机号 + 短信验证码**校验身份后,为账号**设置新密码**。
+
+| 序号 | 项 | 说明 |
+| --- | --- | --- |
+| 1 | 入口 | 登录页「忘记密码」→ `uni.navigateTo('/pages/login/forgot')`(现状已具备) |
+| 2 | 前端页面 | 重做 `forgot.vue` 视觉,对齐服务商端登录页品牌风(渐变底 + 面板式输入 + 渐变胶囊按钮) |
+| 3 | 校验要素 | 登录手机号 + 短信验证码(一次性)+ 新密码两次一致 |
+| 4 | 后端接口 | 新增 `POST /miniapp/auth/password/reset`(服务内 `/password/reset`);下发验证码复用 `/miniapp/auth/sms/code`(`scene=reset`) |
+| 5 | 密码落库 | 由 `ruoyi-user` 侧 BCrypt 加密后**只更新 `account_user.password` 一列** |
+| 6 | 服务商身份 | **不额外强校验 `provider_flag`**:能收到该手机号短信即证明持有该手机号;重置后仍需通过服务商端登录接口(会强校验 `provider_flag='1'`)才能进入 |
+
+> 说明:忘记密码属于「账号找回」,本质是凭手机号验证身份改密码。若强校验 `provider_flag`,非服务商会员将无法在服务商端重置密码,且会暴露账号是否为服务商(枚举风险)。故此处不做 `provider_flag` 校验,身份门槛由「手机号 + 短信验证码」承担。
+
+---
+
+## 二、现状与差异点
+
+### 2.1 现状
+
+| 层 | 现状 |
+| --- | --- |
+| 前端页面 | `pages/login/forgot.vue` 已存在:手机号 + 验证码 + 新密码 + 确认密码 + 提交;风格为「白底 + 绿色验证码按钮 + 深蓝胶囊提交」,与登录页品牌风**不一致** |
+| 前端接口 | `api/index.js` 已埋桩:`sendSmsCode(phone)`(`scene='reset'`)→ `/miniapp/auth/sms/code`;`resetPassword({phone,code,password})` → `/miniapp/auth/password/reset` |
+| 后端接口 | **缺失**:`AccountAuthController` 无 `/password/reset` 端点 |
+| 后端短信 | `scene=reset` 走 `SmsSceneEnum.RESET`,`sendCode` 当前**不校验手机号是否已注册**;`verifyAndConsume` 对 reset **不放行通用码** |
+| 网关白名单 | `/miniapp/auth/sms/code` 已在白名单;`/miniapp/auth/password/reset` **未加白名单**(不加则报「令牌不能为空」) |
+
+### 2.2 差异点(决定本次改动)
+
+1. 新增后端重置密码端点与跨服务内部接口;
+2. 补齐 reset 场景的手机号「已注册」校验;
+3. 网关白名单补 `/miniapp/auth/password/reset`;
+4. 前端 `forgot.vue` 视觉重做 + `pages.json` 该页改 `navigationStyle: custom`。
+
+---
+
+## 三、视觉规格(对齐服务商端登录页品牌体系)
+
+换算基准:设计稿 375pt → 750rpx(pt × 2 = rpx)。
+
+| 元素 | 实现规格 |
+| --- | --- |
+| 页面底板 | 渐变 165°、rgba .8 叠白底:`#D3E4F8` 0% → `#BCD6F4` 50% → `#ABCCEC` 100% |
+| 顶部导航 | 自定义(`navigationStyle: custom`):返回箭头 + 标题「重置密码」,居中,`#0F172A` |
+| 顶部装饰 | 沿用登录页 CSS 装饰(微透十字 / 白色点阵)各 2~3 处,`pointer-events:none` |
+| 引导文案 | 主标题 34rpx / 600 `#0F172A`;副说明 24rpx `#64748B` |
+| 表单标签 | 26rpx / 500 / `#334155`,下间距 18rpx |
+| 输入框 | H88rpx / R16 / 描边 2rpx `#CBD5E1` / 底色 `#F8FAFC`,内嵌 44rpx 图标,左右内边距 28rpx |
+| 验证码行 | 输入框 `flex:1` + 「获取验证码」按钮 190rpx,品牌色描边/文字,倒计时期间置灰禁用 |
+| 密码可见性 | 右侧 44rpx 眼睛图标,品牌色 `#003E7E` |
+| 提交按钮 | H96rpx / R48,渐变 90° `#1E5EBC` → `#003E7E`,30rpx 600 白字,shadow `0 12rpx 28rpx rgba(0,62,126,.2)` |
+| 底部 Home 条 | 270×10rpx / R5 `rgba(15,23,42,.145)` |
+| 边距体系 | 内容区左右 56rpx,元素间距 40rpx,底部叠加安全区 32rpx |
+
+交互:
+
+- 获取验证码:校验手机号格式 → 调接口 → 60s 倒计时(`onUnload` 清理定时器);
+- 提交:本地校验(手机号 / 验证码非空 / 密码 ≥6 位 / 两次一致)→ 调接口 → 成功 toast → `navigateBack` 回登录页;
+- 全流程 `logging` 防重复提交。
+
+---
+
+## 四、接口设计(本次核心变更)
+
+### 4.1 接口一览
+
+| 接口 | 方法 | 网关路径 | 服务内路径 | 入参 | 返回 |
+| --- | --- | --- | --- | --- | --- |
+| 下发验证码(复用) | POST | `/miniapp/auth/sms/code` | `/sms/code` | `{ phone, scene:'reset' }` | `R.ok()` |
+| 重置密码(新增) | POST | `/miniapp/auth/password/reset` | `/password/reset` | `{ phone, code, password }` | `R.ok()` |
+
+> 网关路由 `ruoyi-miniapp-auth`(`Path=/miniapp/auth/**` + `StripPrefix=2`)已存在;**仅需把 `/miniapp/auth/password/reset` 加入白名单**。
+
+### 4.2 重置密码链路(时序)
+
+```
+POST /miniapp/auth/password/reset  { phone, code, password }
+  1. 入参校验(@NotBlank + 手机号正则 + 密码长度)
+  2. 按手机号查询会员(Feign → GET /member/info/{phone})
+       不存在              → 抛「该手机号未注册」
+  3. 校验账号状态:停用      → 抛「账号已停用,请联系管理员」
+  4. 校验并消费短信验证码(scene=reset,一次性)
+       未获取/已失效        → 抛「验证码已失效,请重新获取」
+       错误                 → 抛「验证码错误」(累计 5 次作废)
+  5. Feign 调 ruoyi-user 内部接口 POST /member/password/reset { phone, password }
+       → 服务内 BCrypt 加密后只更新 password 一列
+  6. 返回 R.ok()
+```
+
+> 顺序说明:先查会员与状态、再消费验证码,与 `phoneCodeLogin` 的口径一致,避免无谓消费验证码(消费后即失效)。
+
+### 4.3 跨服务契约变更
+
+| 文件 | 变更 |
+| --- | --- |
+| `ruoyi-api-system` / `model/AccountResetPasswordRequest` | 新增:`{ phone, password }`,供内部调用传递待重置信息 |
+| `ruoyi-api-system` / `RemoteAccountUserService` | 新增 `@PostMapping("/member/password/reset")` |
+| `ruoyi-api-system` / `RemoteAccountUserFallbackFactory` | 补 `resetPassword` 降级实现 |
+
+### 4.4 短信场景补齐
+
+`SmsCodeService.sendCode` 增加 reset 场景的「手机号已注册」校验:
+
+```
+register → checkPhoneNotRegistered
+login    → checkPhoneRegistered
+reset    → checkPhoneRegistered   // 本次新增
+common   → 不校验
+```
+
+同步更新 `SmsCodeBody.scene` 的注释说明(原注释称 “reset/为空=通用下发(不校验注册状态)”,与实际不符)。
+
+---
+
+## 五、数据库设计
+
+**无 DDL 变更**(沿用 `account_user.password varchar`,BCrypt 密文;单列更新语句 `updateUserPassword`)。
+
+复用既有字段:`account_user.password`、`account_user.phone`、`account_user.status`、`account_user.del_flag`。
+
+---
+
+## 六、枚举与状态
+
+| 名称 | 取值 | 说明 |
+| --- | --- | --- |
+| `SmsSceneEnum.RESET` | `reset` | 重置密码场景(已存在,本次启用其手机号校验) |
+| `account_user.status` | `0` 正常 / `1` 停用 | 停用账号拒绝重置(与登录口径一致) |
+
+---
+
+## 七、影响范围
+
+| 类型 | 文件 | 变更 |
+| --- | --- | --- |
+| 前端页面 | `hugray-partner-miniapp/pages/login/forgot.vue` | 重做模板与样式,对齐品牌风;逻辑保持(倒计时 / 校验 / 提交) |
+| 前端配置 | `hugray-partner-miniapp/pages.json` | `pages/login/forgot` 改 `navigationStyle: custom` |
+| 前端接口 | `hugray-partner-miniapp/api/index.js` | 无需改(已有 `sendSmsCode` / `resetPassword` 桩,复核参数一致) |
+| 认证服务 | `ruoyi-auth`:新增 `ResetPasswordBody`、`PasswordResetService`;`AccountAuthController` 新增 `#resetPassword` | 重置密码编排 |
+| 认证服务 | `ruoyi-auth`:`SmsCodeService.sendCode` 补 reset 场景手机号校验 | 场景补齐 |
+| 会员服务 | `ruoyi-user`:`IAccountUserService` + impl 新增 `resetPasswordByPhone`;`AccountUserMapper` + XML 新增 `updateUserPassword`;`AccountMemberInnerController` 新增 `POST /member/password/reset` | 密码落库(服务内加密) |
+| 跨服务契约 | `ruoyi-api-system`:新增 `AccountResetPasswordRequest`;`RemoteAccountUserService` 加 `resetPassword`;`RemoteAccountUserFallbackFactory` 补降级 | Feign 契约扩展 |
+| 网关配置 | `doc/nacos/dev/ruoyi-gateway-dev.yml`(同步 Nacos) | `security.ignore.whites` 追加 `/miniapp/auth/password/reset` |
+| 数据库 | — | **无变更** |
+
+---
+
+## 八、验收清单
+
+1. 登录页「忘记密码」可跳转 `/pages/login/forgot`;✔
+2. 页面呈现浅蓝渐变底 + 装饰层 + 品牌风表单(面板式输入 + 渐变胶囊提交按钮);✔
+3. 手机号格式非法时点击「获取验证码」提示「请输入正确的手机号」,不发请求;✔
+4. 合法手机号点击「获取验证码」:接口成功 → 60s 倒计时 → 期间按钮禁用;✔
+5. 未注册手机号请求验证码 → 提示「该手机号未注册,请先注册」;✔
+6. 新密码 <6 位或两次不一致 → 本地拦截并提示;✔
+7. 正确手机号 + 正确验证码 + 新密码 → 提示「密码重置成功」并返回登录页;✔
+8. 用**新密码**在服务商端登录成功,用旧密码登录失败(提示「账号或密码错误」);✔
+9. 验证码错误累计 5 次 → 提示「验证码错误次数过多,请重新获取」;✔
+10. 验证码已消费后再提交 → 提示「验证码已失效,请重新获取」;✔
+11. 停用账号重置 → 提示「账号已停用,请联系管理员」;✔
+12. 未加白名单时请求 `/miniapp/auth/password/reset` 应报「令牌不能为空」——补白名单后不再出现。✔
+
+---
+
+## 九、待确认决策
+
+1. **重置凭据用手机号**(现状前端即为手机号):验证码发到手机,故以手机号为账号标识;服务商账号 `provider126` 对应手机号 `13800000126`。是否认可?
+2. **reset 场景是否放行通用验证码 `999999`**(`hugray.auth.universal-sms-code`):现状仅 login/register 放行。放行便于联调(无需读日志取码),但降低重置安全性。建议:**开发环境放行、生产关闭**(配置为空即关闭)。
+3. **验证码下发真实通道**:当前 `PlaceholderSmsSender` 仅打印日志,联调时验证码需从 `ruoyi-auth` 日志 `[SMS_SEND] placeholder send, phone=138****0126, code=xxxxxx` 读取。
+
+---
+
+## 十、AI 协作声明
+
+- 本文档经人工确认后方可生成/修改代码(遵循项目《AI 协作流程》条款);
+- 文档与代码同一批次提交,路径:`doc/design/服务商端_忘记密码_20261014.md`。
diff --git "a/doc/design/\346\234\215\345\212\241\345\225\206\347\253\257_\347\231\273\345\275\225_20261014.md" "b/doc/design/\346\234\215\345\212\241\345\225\206\347\253\257_\347\231\273\345\275\225_20261014.md"
new file mode 100644
index 0000000..af3bcf9
--- /dev/null
+++ "b/doc/design/\346\234\215\345\212\241\345\225\206\347\253\257_\347\231\273\345\275\225_20261014.md"
@@ -0,0 +1,172 @@
+# 服务商端小程序 登录(UI 重设计 + 专用登录接口)开发设计文档
+
+- 模块:`hugray-partner-miniapp`(前端)、`ruoyi-auth` / `ruoyi-user` / `ruoyi-api-system` / `ruoyi-gateway`(后端)
+- 日期:2026-10-14
+- 需求来源:
+  1. “现在开始服务商小程序的开发,先把服务商小程序的登录页样式参考用户小程序的样式重新设计 ui 并开发”;
+  2. “服务商小程序登录提示令牌不能为空,服务商只需要账号加密码登录就行,不需要其他参数,看是否重新写个接口区分开来”。
+- 风格参照:`hugray-user-miniapp/pages/login/index.vue` + `doc/design/用户端_登录注册_20261012.md` §3.5 视觉还原规格
+- 改造目标文件:
+  - 前端:`hugray-partner-miniapp/pages/login/index.vue`、`hugray-partner-miniapp/api/index.js`
+  - 后端:`ruoyi-auth`、`ruoyi-user`、`ruoyi-api-system`、网关白名单
+- 已确认决策(用户确认):
+  1. **品牌区完整复刻用户端**:`欢迎来到` + 代谢300 Logo + 英文标语 + 医学双背书 + 产业4.0标语卡;
+  2. **底板沿用浅蓝渐变**:165°、rgba .8 叠白底(`#D3E4F8` → `#BCD6F4` → `#ABCCEC`)+ 3 处微透十字 + 3 处白色点阵;
+  3. **表单面板式平铺**:输入框直接铺在渐变底上,不使用白色卡片承载;
+  4. **新接口路径** `/miniapp/auth/partner/login`(服务内 `/partner/login`),与用户端接口彻底区分;
+  5. **强校验服务商身份**:仅 `provider_flag='1'` 的账号可登录服务商端;
+  6. **合并错误提示**:账号不存在与密码错误统一返回「账号或密码错误」,防账号枚举。
+
+---
+
+## 一、功能概述
+
+本次包含两部分:
+
+| 序号 | 项 | 变化 |
+| --- | --- | --- |
+| 1 | 登录页视觉 | 由「深蓝导航栏 + 白底表单 + 通栏深蓝按钮」改为「用户端同款浅蓝渐变登录页(装饰层 + 品牌区 + 面板式表单 + 渐变胶囊按钮 + Home 条)」 |
+| 2 | 登录方式 | **不变**:账号 + 密码 |
+| 3 | 登录接口 | **变更**:由不存在且未白名单的 `/miniapp/auth/login` 改为新增的服务商专用接口 `/miniapp/auth/partner/login`,入参仅 `username` + `password` |
+| 4 | 服务商身份校验 | **新增**:按 username 查 `account_user`,校验 `provider_flag='1'` + 账号状态 + BCrypt 密码 |
+| 5 | 忘记密码入口 | **不变**:跳转 `/pages/login/forgot` |
+| 6 | 协议勾选与确认弹窗 | **不变**:逻辑保留,样式对齐用户端 |
+
+---
+
+## 二、报错根因(问题定位)
+
+服务商端登录报「令牌不能为空」,为**双重根因**:
+
+1. **接口不存在**:前端调用 `POST /miniapp/auth/login`,而 `ruoyi-auth` 的 `AccountAuthController` 仅有 `/sms/code`、`/phone/login`、`/phone/code-login`、`/account/register`、`/invite-qrcode`,**无用户名密码登录端点**。
+2. **白名单缺失**:`/miniapp/auth/login` 未登记在网关 `security.ignore.whites`,未携带 token 访问时被 `AuthFilter` 直接拦截,返回 `HTTP 401 + 令牌不能为空`。
+
+---
+
+## 三、现状与差异点
+
+### 3.1 现状
+
+`pages/login/index.vue` 当前为深蓝导航栏 + 白底表单风格,`pages.json` 中该页已配置 `"navigationStyle": "custom"`。
+
+### 3.2 与用户端的差异(决定本次实现差异)
+
+| 维度 | 用户端登录页 | 服务商端登录页 |
+| --- | --- | --- |
+| 登录方式 | 微信一键登录 / 手机验证登录 | **账号 + 密码** |
+| 登录接口 | `/miniapp/auth/phone/login`、`/miniapp/auth/phone/code-login` | **`/miniapp/auth/partner/login`** |
+| 底部操作区 | 两个渐变胶囊按钮 + 协议行 + 注册入口 + Home 条 | **账号输入 + 密码输入 + 忘记密码 + 协议行 + 登录按钮 + Home 条** |
+| 品牌区 | 完整(欢迎来到 + Logo + 标语 + 背书 + 标语卡) | 本次对齐为**同款** |
+| 顶部导航 | 无导航栏,内容区 `paddingTop = 状态栏 + 40px` | 同左(去掉原深蓝导航栏) |
+| 协议文案 | 《用户协议》《隐私与权限》 | **不变**(保持服务商端既有文案) |
+
+### 3.3 素材缺口与处理
+
+服务商端 `static/` 原仅有 `logo.png`(与用户端 `logo.png` 为同一「YiOUS」深蓝图,MD5 一致)。本次品牌区新增 4 个素材:
+
+| 素材 | 来源 | 用途 |
+| --- | --- | --- |
+| `metabolism-300-logo.png` | 复制自用户端 | 品牌区官方 Logo(440×152rpx) |
+| `medical-plus-icon.png` | 复制自用户端 | 医学双背书「严谨医学循证」 |
+| `check-icon.png` | 复制自用户端 | 医学双背书「传递健康价值」 |
+| `industry-4-0-slogan-card.png` | 复制自用户端 | 产业4.0核心标语卡 |
+
+> `logo.png` 保留不删(服务商端其他页面可能仍在引用)。
+
+---
+
+## 四、视觉规格(复用用户端已落地设计体系)
+
+换算基准:设计稿 375pt → 750rpx(pt × 2 = rpx)。
+
+| 元素 | 实现规格 |
+| --- | --- |
+| 页面底板 | 渐变 165°、rgba .8 叠白底:`#D3E4F8` 0% → `#BCD6F4` 50% → `#ABCCEC` 100% |
+| 微透十字 ×3 | 右上 160rpx(条宽 44)/ 中左 136rpx(条宽 36)/ 左下 200rpx(条宽 56) |
+| 白色点阵 ×3 | 左上 / 右上 / 右下,radial-gradient 平铺,步距 26rpx |
+| 内容区顶距 | `paddingTop = statusBarHeight + 40px`;左右 56rpx;元素间距 40rpx |
+| 欢迎语 | 「欢迎来到」52rpx / 500 / `#0F172A`,居中 |
+| 官方 Logo | `metabolism-300-logo.png` 440×152rpx |
+| 英译标语 | 「RESET METABOLISM & DELIVER HEALTH」22rpx / 600 / `#003E7E` / 字距 3rpx |
+| 医学双背书 | 图标 30rpx / 36.4×30rpx,项内 gap 12rpx、两项 gap 48rpx;文案 24rpx 600 `#1E293B` |
+| 标语卡 | `industry-4-0-slogan-card.png` 759×304rpx,上下负边距 -48rpx / -72rpx |
+| 表单标签 | 26rpx / 500 / `#334155`,下间距 18rpx |
+| 输入框 | H88rpx / R16 / 描边 2rpx `#CBD5E1` / 底色 `#F8FAFC`,内嵌 44rpx 图标(账号 / 盾牌),左右内边距 28rpx |
+| 忘记密码 | 右对齐,26rpx / 600 / `#1E5EBC` |
+| 登录按钮 | H96rpx / R48,渐变 90° `#1E5EBC` → `#003E7E`,30rpx 600 白字,shadow `0 12rpx 28rpx rgba(0,62,126,.2)` |
+| 协议勾选行 | 复选框 32rpx / R8(选中 `#003E7E`)+ 文案 26rpx(正文 `#64748B` / 链接 `#1E5EBC`) |
+| 底部 Home 条 | 270×10rpx / R5 `rgba(15,23,42,.145)` |
+| 边距体系 | 底部区左右 56rpx,叠加安全区 32rpx |
+| 协议确认弹窗 | 560rpx 宽 / R24,标题 34rpx 600 `#003E7E`,正文 28rpx `#64748B`,按钮行「取消 `#334155` / 我已阅读并同意 `#1E5EBC`」 |
+
+---
+
+## 五、接口设计(本次核心变更)
+
+### 5.1 接口一览
+
+| 接口 | 方法 | 网关路径 | 服务内路径 | 入参 | 返回 |
+| --- | --- | --- | --- | --- | --- |
+| 服务商登录(新增) | POST | `/miniapp/auth/partner/login` | `/partner/login` | `{ username, password }` | `{ access_token, expires_in }` |
+
+> 网关路由 `ruoyi-miniapp-auth`(`Path=/miniapp/auth/**` + `StripPrefix=2`)已存在,无需新增路由;**仅需把新路径加入白名单**。
+
+### 5.2 服务商身份模型(关键认知)
+
+服务商**不是独立用户体系**,而是「会员(`account_user`)的另一重身份」:
+
+- 登录账号 / 密码直接用 `account_user.username / password`(BCrypt 密文);
+- `account_provider` 表只存公司资质与权益配置,用 `user_id` 与账号 1:1 关联;
+- `account_user.provider_flag = '1'` 标识该账号为服务商。
+
+### 5.3 登录鉴权链路
+
+```
+POST /miniapp/auth/partner/login  { username, password }
+  1. 入参非空校验(@NotBlank)
+  2. Feign 调 ruoyi-user 内部接口 GET /member/info/username/{username}
+       → AccountMember(含 password 密文、providerFlag、status)
+  3. 账号不存在            → 抛「账号或密码错误」
+  4. providerFlag != '1'   → 抛「该账号不是服务商账号」
+  5. status == '1'(停用)   → 抛「账号已停用,请联系管理员」
+  6. BCrypt matchesPassword(password, member.password) 失败 → 抛「账号或密码错误」
+  7. 组装 LoginUser → TokenService.createToken → 返回 { access_token, expires_in }
+```
+
+### 5.4 跨服务契约变更
+
+`AccountMember`(`ruoyi-api-system`)新增两个字段:`password`(BCrypt 密文,仅供 auth 校验,且 AccountMember 全程不下发到 C 端)、`providerFlag`(服务商标识)。
+
+> 说明:`AccountMember` 仅用于内部登录链路,`AccountAuthController` 对外返回的是令牌 Map 或空 R,不会把 member 直接下发,故不存在密码泄露风险。
+
+---
+
+## 六、影响范围
+
+| 类型 | 文件 | 变更 |
+| --- | --- | --- |
+| 前端页面 | `hugray-partner-miniapp/pages/login/index.vue` | 重写模板与样式;登录收尾逻辑修正 token 字段(`data.access_token`)与入参简化 |
+| 前端接口 | `hugray-partner-miniapp/api/index.js` | `login()` 改指 `/miniapp/auth/partner/login`,入参简化为 `{ username, password }` |
+| 静态资源 | `hugray-partner-miniapp/static/*` | 新增 4 个图片素材(复制自用户端) |
+| 认证服务 | `ruoyi-auth` 新增 `PartnerLoginBody` / `PartnerLoginService` / `AccountAuthController#partnerLogin` | 新增服务商登录端点与编排逻辑 |
+| 会员服务 | `ruoyi-user`:`IAccountUserService` + impl 新增 `selectAccountUserByUsername`;`AccountMemberInnerController` 新增 `/info/username/{username}` 并补充 password/providerFlag 映射 | 提供按用户名查询内部能力 |
+| 跨服务契约 | `ruoyi-api-system`:`AccountMember` 加字段;`RemoteAccountUserService` 加 `getMemberByUsername`;`RemoteAccountUserFallbackFactory` 补降级 | Feign 契约扩展 |
+| 网关配置 | `doc/nacos/dev/ruoyi-gateway-dev.yml`(同步 Nacos 配置中心) | `security.ignore.whites` 追加 `/miniapp/auth/partner/login` |
+| 数据库 | — | **无变更** |
+
+---
+
+## 七、验收清单
+
+1. 登录页呈现浅蓝渐变底 + 3 十字 + 3 点阵装饰,风格与用户端一致;✔
+2. 品牌区完整(欢迎来到 + 代谢300 Logo + 英文标语 + 医学双背书 + 标语卡);✔
+3. 账号 / 密码输入框为面板式平铺(H88 / R16 / 浅底描边),带品牌图标;✔
+4. 「忘记密码」右对齐可点击,跳转 `/pages/login/forgot`;✔
+5. 协议勾选行 + 未勾选时弹确认弹窗,逻辑与现状一致;✔
+6. 登录按钮为渐变胶囊(`#1E5EBC → #003E7E`),点击可正常登录并 `reLaunch` 首页;✔
+7. 底部居中 Home 条;✔
+8. 页面无横向滚动 / 无内容溢出裁切。✔
+9. 服务商账号 + 正确密码 → 登录成功,`reLaunch` 首页;✔
+10. 非服务商账号 → 提示「该账号不是服务商账号」;✔
+11. 账号不存在或密码错误 → 统一提示「账号或密码错误」;✔
+12. 停用账号 → 提示「账号已停用,请联系管理员」。✔
diff --git a/doc/nacos/dev/ruoyi-gateway-dev.yml b/doc/nacos/dev/ruoyi-gateway-dev.yml
index e2f8350..b21b29e 100644
--- a/doc/nacos/dev/ruoyi-gateway-dev.yml
+++ b/doc/nacos/dev/ruoyi-gateway-dev.yml
@@ -107,6 +107,8 @@
       - /miniapp/auth/phone/login
       - /miniapp/auth/phone/code-login
       - /miniapp/auth/account/register
+      - /miniapp/auth/partner/login
+      - /miniapp/auth/password/reset
       - /*/v2/api-docs
       - /*/v3/api-docs
       - /csrf
diff --git a/hugray-partner-miniapp/api/index.js b/hugray-partner-miniapp/api/index.js
index 41046e5..e103492 100644
--- a/hugray-partner-miniapp/api/index.js
+++ b/hugray-partner-miniapp/api/index.js
@@ -10,11 +10,11 @@
 // ======================== 认证相关 ========================
 
 /**
- * 服务商登录
- * @param {Object} data { username, password, code, uuid }
+ * 服务商登录(账号 + 密码,专用接口,与用户端手机号登录区分)
+ * @param {Object} data { username, password }
  */
 export function login(data) {
-    return post('/miniapp/auth/login', data, { auth: false })
+    return post('/miniapp/auth/partner/login', data, { auth: false })
 }
 
 /**
diff --git a/hugray-partner-miniapp/pages.json b/hugray-partner-miniapp/pages.json
index b9dc653..9893892 100644
--- a/hugray-partner-miniapp/pages.json
+++ b/hugray-partner-miniapp/pages.json
@@ -17,9 +17,7 @@
         {
             "path": "pages/login/forgot",
             "style": {
-                "navigationBarTitleText": "忘记密码",
-                "navigationBarBackgroundColor": "#0b3f6f",
-                "navigationBarTextStyle": "white"
+                "navigationStyle": "custom"
             }
         },
         {
diff --git a/hugray-partner-miniapp/pages/login/forgot.vue b/hugray-partner-miniapp/pages/login/forgot.vue
index 328ef8a..ac14edb 100644
--- a/hugray-partner-miniapp/pages/login/forgot.vue
+++ b/hugray-partner-miniapp/pages/login/forgot.vue
@@ -1,6 +1,26 @@
 <template>
     <view class="reset-page">
-        <view class="form-area">
+        <!-- 背景装饰层:2 处微透十字 + 2 处白色微点阵(置于内容之下,不拦截点击) -->
+        <view class="deco-cross deco-cross--tr"></view>
+        <view class="deco-cross deco-cross--ml"></view>
+        <view class="deco-dots deco-dots--lt"></view>
+        <view class="deco-dots deco-dots--rb"></view>
+
+        <!-- 自定义导航栏:返回箭头 + 居中标题 -->
+        <view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
+            <view class="nav-back" @tap="_handleBack">
+                <view class="nav-back-arrow"></view>
+            </view>
+            <text class="nav-title">重置密码</text>
+        </view>
+
+        <!-- 核心内容区:引导文案 + 表单 -->
+        <view class="content">
+            <view class="intro">
+                <text class="intro-title">重置登录密码</text>
+                <text class="intro-desc">请输入注册手机号,通过短信验证码验证身份后设置新密码</text>
+            </view>
+
             <!-- 登录手机号 -->
             <view class="field-block">
                 <text class="field-label">登录手机号</text>
@@ -11,8 +31,8 @@
                         class="field-input"
                         type="number"
                         maxlength="11"
-                        placeholder="请输入账号"
-                        placeholder-style="color:#b8bec7;font-size:28rpx"
+                        placeholder="请输入手机号"
+                        placeholder-style="color:#94A3B8;font-size:28rpx"
                     />
                 </view>
             </view>
@@ -29,7 +49,7 @@
                             type="number"
                             maxlength="6"
                             placeholder="请输入验证码"
-                            placeholder-style="color:#b8bec7;font-size:28rpx"
+                            placeholder-style="color:#94A3B8;font-size:28rpx"
                         />
                     </view>
                     <!-- 获取验证码:倒计时期间禁用 -->
@@ -53,8 +73,8 @@
                         class="field-input"
                         :password="!showPassword"
                         maxlength="20"
-                        placeholder="请输入密码"
-                        placeholder-style="color:#b8bec7;font-size:28rpx"
+                        placeholder="请输入6-20位新密码"
+                        placeholder-style="color:#94A3B8;font-size:28rpx"
                     />
                     <!-- 密码可见性切换 -->
                     <view class="eye-wrap" @tap="showPassword = !showPassword">
@@ -66,9 +86,9 @@
                 </view>
             </view>
 
-            <!-- 再次输入新密码 -->
+            <!-- 确认新密码 -->
             <view class="field-block">
-                <text class="field-label">再次输入新密码</text>
+                <text class="field-label">确认新密码</text>
                 <view class="input-wrap">
                     <view class="field-icon icon-grid"></view>
                     <input
@@ -76,8 +96,8 @@
                         class="field-input"
                         :password="!showConfirmPassword"
                         maxlength="20"
-                        placeholder="请输入新密码"
-                        placeholder-style="color:#b8bec7;font-size:28rpx"
+                        placeholder="请再次输入新密码"
+                        placeholder-style="color:#94A3B8;font-size:28rpx"
                     />
                     <view class="eye-wrap" @tap="showConfirmPassword = !showConfirmPassword">
                         <view
@@ -89,9 +109,16 @@
             </view>
         </view>
 
-        <!-- 底部通栏提交按钮 -->
-        <view class="bottom-bar">
-            <view class="submit-btn" @tap="_handleSubmit">提交</view>
+        <!-- 底部提交区:渐变胶囊按钮 + Home 条 -->
+        <view class="bottom-area">
+            <view
+                class="submit-btn"
+                :class="{ 'submit-btn--disabled': submitting }"
+                @tap="_handleSubmit"
+            >
+                确认重置
+            </view>
+            <view class="home-indicator"></view>
         </view>
     </view>
 </template>
@@ -123,6 +150,12 @@
 // 是否处于倒计时状态(用于禁用获取验证码按钮)
 const counting = computed(() => countdown.value > 0)
 
+// 提交进行中标记,防止重复提交
+const submitting = ref(false)
+
+// 状态栏高度(px),用于自定义导航栏顶部留白
+const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
+
 // 倒计时定时器句柄
 let countdownTimer = null
 
@@ -130,6 +163,11 @@
 
 /** 校验手机号格式 */
 const _isValidPhone = (phone) => /^1[3-9]\d{9}$/.test(phone)
+
+/** 返回上一页(登录页) */
+const _handleBack = () => {
+    uni.navigateBack()
+}
 
 /** 启动 60 秒倒计时,期间禁用「获取验证码」 */
 const _startCountdown = () => {
@@ -164,19 +202,22 @@
 const _validateForm = () => {
     if (!_isValidPhone(form.phone)) return '请输入正确的手机号'
     if (!form.code.trim()) return '请输入验证码'
-    if (form.password.length < 6) return '新密码长度不能少于6位'
+    if (form.password.length < 6 || form.password.length > 20) return '新密码长度需为6-20位'
     if (form.password !== form.confirmPassword) return '两次输入的密码不一致'
     return ''
 }
 
 /** 提交重置密码:校验 → 调接口 → 返回登录页 */
 const _handleSubmit = async () => {
+    if (submitting.value) return
+
     const errorMsg = _validateForm()
     if (errorMsg) {
         uni.showToast({ title: errorMsg, icon: 'none' })
         return
     }
 
+    submitting.value = true
     try {
         await resetPassword({
             phone: form.phone,
@@ -187,6 +228,8 @@
         setTimeout(() => uni.navigateBack(), 1200)
     } catch (e) {
         uni.showToast({ title: e.message || '密码重置失败', icon: 'none' })
+    } finally {
+        submitting.value = false
     }
 }
 
@@ -197,39 +240,204 @@
 </script>
 
 <style scoped lang="scss">
-/* 页面最外层:纵向弹性布局,撑满整屏 */
+/* ==================== 页面底板 ==================== */
+
+/* 页面最外层:浅蓝品牌渐变底(165°,0.8 透明),纵向弹性布局撑满整屏 */
 .reset-page {
+    position: relative;
     display: flex;
     flex-direction: column;
-    height: 100vh;
+    min-height: 100vh;
+    box-sizing: border-box;
+    overflow: hidden;
     background-color: #ffffff;
+    background-image: linear-gradient(
+        165deg,
+        rgba(211, 228, 248, 0.8) 0%,
+        rgba(188, 214, 244, 0.8) 50%,
+        rgba(171, 204, 236, 0.8) 100%
+    );
 }
 
-.form-area {
+/* ==================== 背景装饰层 ==================== */
+
+/* 微透十字:容器尺寸按设计稿,竖条(::before)/横条(::after)等宽居中交叉 */
+.deco-cross {
+    position: absolute;
+    z-index: 0;
+    pointer-events: none;
+
+    &::before,
+    &::after {
+        content: '';
+        position: absolute;
+        background-color: var(--cross-color);
+        border-radius: calc(var(--bar) / 2);
+    }
+
+    /* 竖条:水平居中 */
+    &::before {
+        left: var(--offset);
+        top: 0;
+        width: var(--bar);
+        height: 100%;
+    }
+
+    /* 横条:垂直居中 */
+    &::after {
+        left: 0;
+        top: var(--offset);
+        width: 100%;
+        height: var(--bar);
+    }
+}
+
+/* 右上十字:80×80pt,条宽 22 / R11,#FFFFFF35 */
+.deco-cross--tr {
+    top: 20rpx;
+    right: 10rpx;
+    width: 160rpx;
+    height: 160rpx;
+    --bar: 44rpx;
+    --offset: 58rpx;
+    --cross-color: rgba(255, 255, 255, 0.208);
+}
+
+/* 中左十字:68×68pt,条宽 18 / R9,#FFFFFF30 */
+.deco-cross--ml {
+    top: 41%;
+    left: -48rpx;
+    width: 136rpx;
+    height: 136rpx;
+    --bar: 36rpx;
+    --offset: 50rpx;
+    --cross-color: rgba(255, 255, 255, 0.188);
+}
+
+/*
+ * 白色微点阵:微点 3×3pt(6rpx),行内/行间 gap 10pt(20rpx) → 平铺步距 26rpx
+ */
+.deco-dots {
+    position: absolute;
+    z-index: 0;
+    pointer-events: none;
+    background-image: radial-gradient(circle, rgba(255, 255, 255, 0.333) 3rpx, transparent 3rpx);
+    background-size: 26rpx 26rpx;
+}
+
+/* 左上点阵(5 列 × 4 行) */
+.deco-dots--lt {
+    top: 22%;
+    left: 20rpx;
+    width: 110rpx;
+    height: 84rpx;
+}
+
+/* 右下点阵(3 列 × 3 行) */
+.deco-dots--rb {
+    bottom: 180rpx;
+    right: 40rpx;
+    width: 58rpx;
+    height: 58rpx;
+}
+
+/* ==================== 自定义导航栏 ==================== */
+
+.nav-bar {
+    position: relative;
+    z-index: 1;
+    height: 88rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding-left: 24rpx;
+    padding-right: 24rpx;
+}
+
+/* 返回按钮:居左,点击热区 64rpx */
+.nav-back {
+    position: absolute;
+    left: 24rpx;
+    bottom: 0;
+    width: 64rpx;
+    height: 88rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+/* 返回箭头:2rpx 描边旋转 45° 呈左向 V */
+.nav-back-arrow {
+    width: 22rpx;
+    height: 22rpx;
+    border-left: 4rpx solid #0f172a;
+    border-bottom: 4rpx solid #0f172a;
+    transform: rotate(45deg);
+    margin-left: 8rpx;
+}
+
+/* 导航标题:17pt 600 深板岩色 */
+.nav-title {
+    font-size: 34rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+
+/* ==================== 核心内容区 ==================== */
+
+/* 内容区:撑满剩余高度,左右 56rpx */
+.content {
+    position: relative;
+    z-index: 1;
     flex: 1;
-    padding: 30rpx 28rpx;
+    padding: 24rpx 56rpx 0;
+}
+
+/* 引导文案块 */
+.intro {
+    margin-bottom: 48rpx;
+}
+
+/* 主标题:34rpx 600 深板岩色 */
+.intro-title {
+    display: block;
+    font-size: 34rpx;
+    font-weight: 600;
+    color: #0f172a;
+}
+
+/* 副说明:24rpx 弱化色,行高 36rpx */
+.intro-desc {
+    display: block;
+    margin-top: 16rpx;
+    font-size: 24rpx;
+    line-height: 36rpx;
+    color: #64748b;
 }
 
 /* 单个输入项 */
 .field-block {
-    margin-bottom: 40rpx;
+    margin-bottom: 28rpx;
 }
 
+/* 表单标签:26rpx 500 */
 .field-label {
     display: block;
-    font-size: 28rpx;
-    color: #1f2329;
-    margin-bottom: 20rpx;
+    margin-bottom: 18rpx;
+    font-size: 26rpx;
+    font-weight: 500;
+    color: #334155;
 }
 
-/* 带图标的输入框 */
+/* 带图标的输入框:R16 / H88,浅底 + 描边 */
 .input-wrap {
     display: flex;
     align-items: center;
-    height: 96rpx;
-    padding: 0 24rpx;
-    border: 1rpx solid #dcdfe6;
-    border-radius: 8rpx;
+    height: 88rpx;
+    padding: 0 28rpx;
+    border: 2rpx solid #cbd5e1;
+    border-radius: 16rpx;
+    background-color: #f8fafc;
     box-sizing: border-box;
 }
 
@@ -245,7 +453,9 @@
     margin-right: 20rpx;
 }
 
+/* 品牌图标:内嵌 44rpx */
 .field-icon {
+    position: relative;
     width: 44rpx;
     height: 44rpx;
     margin-right: 20rpx;
@@ -255,7 +465,7 @@
     background-size: 100% 100%;
 }
 
-/* 账号/手机号图标:手机外框 + 底部听筒线 */
+/* 手机号图标:手机外框 + 底部听筒线(品牌色 #0b3f6f) */
 .icon-phone {
     background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NCA0NCI+CiAgPHJlY3QgeD0iMTIuNSIgeT0iNS41IiB3aWR0aD0iMTkiIGhlaWdodD0iMzMiIHJ4PSI0LjUiIGZpbGw9Im5vbmUiIHN0cm9rZT0iIzBiM2Y2ZiIgc3Ryb2tlLXdpZHRoPSIzIi8+CiAgPGxpbmUgeDE9IjE4LjUiIHkxPSIzMi41IiB4Mj0iMjUuNSIgeTI9IjMyLjUiIHN0cm9rZT0iIzBiM2Y2ZiIgc3Ryb2tlLXdpZHRoPSIzIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPC9zdmc+Cg==');
 }
@@ -265,7 +475,7 @@
     background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NCA0NCI+CiAgPGRlZnM+CiAgICA8Y2xpcFBhdGggaWQ9InNjIj4KICAgICAgPHBhdGggZD0iTTIyIDkgTDMzIDE3LjUgTDMzIDI1IEMzMyAzMS41IDI3LjUgMzYgMjIgMzggQzE2LjUgMzYgMTEgMzEuNSAxMSAyNSBMMTEgMTcuNSBaIi8+CiAgICA8L2NsaXBQYXRoPgogIDwvZGVmcz4KICA8cGF0aCBkPSJNMjIgNCBMMzggMTUgTDM4IDI1IEMzOCAzNCAzMCA0MCAyMiA0MiBDMTQgNDAgNiAzNCA2IDI1IEw2IDE1IFoiIGZpbGw9IiMwYjNmNmYiLz4KICA8ZyBjbGlwLXBhdGg9InVybCgjc2MpIj4KICAgIDxyZWN0IHg9IjExIiB5PSI5IiB3aWR0aD0iMjIiIGhlaWdodD0iMjkiIGZpbGw9IiNmZmZmZmYiLz4KICAgIDxyZWN0IHg9IjIyIiB5PSI5IiB3aWR0aD0iMTEiIGhlaWdodD0iMTQuNSIgZmlsbD0iIzBiM2Y2ZiIvPgogICAgPHJlY3QgeD0iMTEiIHk9IjIzLjUiIHdpZHRoPSIxMSIgaGVpZ2h0PSIxNC41IiBmaWxsPSIjMGIzZjZmIi8+CiAgPC9nPgo8L3N2Zz4K');
 }
 
-/* 密码图标:2×2 宫格 */
+/* 密码图标:2×2 宫格(品牌色 #0b3f6f) */
 .icon-grid {
     background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NCA0NCI+CiAgPHJlY3QgeD0iNiIgeT0iNiIgd2lkdGg9IjEzLjUiIGhlaWdodD0iMTMuNSIgcng9IjIuNSIgZmlsbD0iIzBiM2Y2ZiIvPgogIDxyZWN0IHg9IjI0LjUiIHk9IjYiIHdpZHRoPSIxMy41IiBoZWlnaHQ9IjEzLjUiIHJ4PSIyLjUiIGZpbGw9IiMwYjNmNmYiLz4KICA8cmVjdCB4PSI2IiB5PSIyNC41IiB3aWR0aD0iMTMuNSIgaGVpZ2h0PSIxMy41IiByeD0iMi41IiBmaWxsPSIjMGIzZjZmIi8+CiAgPHJlY3QgeD0iMjQuNSIgeT0iMjQuNSIgd2lkdGg9IjEzLjUiIGhlaWdodD0iMTMuNSIgcng9IjIuNSIgZmlsbD0iIzBiM2Y2ZiIvPgo8L3N2Zz4K');
 }
@@ -273,27 +483,30 @@
 .field-input {
     flex: 1;
     height: 100%;
-    font-size: 28rpx;
-    color: #1f2329;
     min-width: 0;
+    font-size: 28rpx;
+    color: #1e293b;
 }
 
-/* 获取验证码按钮:绿色实心,倒计时期间置灰不可点 */
+/* 获取验证码按钮:品牌色描边 + 品牌色文字,倒计时期间置灰不可点 */
 .code-btn {
     width: 190rpx;
-    height: 96rpx;
+    height: 88rpx;
     display: flex;
     align-items: center;
     justify-content: center;
-    border-radius: 8rpx;
-    background-color: #8bc34a;
-    color: #ffffff;
-    font-size: 28rpx;
     flex-shrink: 0;
+    border: 2rpx solid #1e5ebc;
+    border-radius: 16rpx;
+    background-color: #ffffff;
+    color: #1e5ebc;
+    font-size: 28rpx;
+    font-weight: 600;
 }
 
 .code-btn--disabled {
-    background-color: #c3dd9d;
+    border-color: #cbd5e1;
+    color: #94a3b8;
 }
 
 /* 密码可见性切换区域 */
@@ -314,34 +527,52 @@
     background-size: 100% 100%;
 }
 
-/* 明文:睁眼 */
+/* 明文:睁眼(品牌色 #003E7E) */
 .icon-eye-open {
-    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NCA0NCI+CiAgPHBhdGggZD0iTTMgMjIgQzguNSAxNCAxNSAxMC41IDIyIDEwLjUgQzI5IDEwLjUgMzUuNSAxNCA0MSAyMiBDMzUuNSAzMCAyOSAzMy41IDIyIDMzLjUgQzE1IDMzLjUgOC41IDMwIDMgMjIgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOGJjMzRhIiBzdHJva2Utd2lkdGg9IjIuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgogIDxjaXJjbGUgY3g9IjIyIiBjeT0iMjIiIHI9IjQuNiIgZmlsbD0iIzhiYzM0YSIvPgo8L3N2Zz4K');
+    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NCA0NCI+PHBhdGggZD0iTTMgMjIgQzguNSAxNCAxNSAxMC41IDIyIDEwLjUgQzI5IDEwLjUgMzUuNSAxNCA0MSAyMiBDMzUuNSAzMCAyOSAzMy41IDIyIDMzLjUgQzE1IDMzLjUgOC41IDMwIDMgMjIgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAzRTdFIiBzdHJva2Utd2lkdGg9IjIuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxjaXJjbGUgY3g9IjIyIiBjeT0iMjIiIHI9IjQuNiIgZmlsbD0iIzAwM0U3RSIvPjwvc3ZnPg==');
 }
 
-/* 密文:闭眼加斜杠 */
+/* 密文:闭眼加斜杠(品牌色 #003E7E) */
 .icon-eye-closed {
-    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NCA0NCI+CiAgPHBhdGggZD0iTTMgMjIgQzguNSAxNCAxNSAxMC41IDIyIDEwLjUgQzI5IDEwLjUgMzUuNSAxNCA0MSAyMiBDMzUuNSAzMCAyOSAzMy41IDIyIDMzLjUgQzE1IDMzLjUgOC41IDMwIDMgMjIgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjOGJjMzRhIiBzdHJva2Utd2lkdGg9IjIuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPgogIDxjaXJjbGUgY3g9IjIyIiBjeT0iMjIiIHI9IjQuNiIgZmlsbD0iIzhiYzM0YSIvPgogIDxsaW5lIHgxPSI3LjUiIHkxPSIzNiIgeDI9IjM2LjUiIHkyPSI4IiBzdHJva2U9IiM4YmMzNGEiIHN0cm9rZS13aWR0aD0iMi44IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz4KPC9zdmc+Cg==');
+    background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0NCA0NCI+PHBhdGggZD0iTTMgMjIgQzguNSAxNCAxNSAxMC41IDIyIDEwLjUgQzI5IDEwLjUgMzUuNSAxNCA0MSAyMiBDMzUuNSAzMCAyOSAzMy41IDIyIDMzLjUgQzE1IDMzLjUgOC41IDMwIDMgMjIgWiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAzRTdFIiBzdHJva2Utd2lkdGg9IjIuNiIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxjaXJjbGUgY3g9IjIyIiBjeT0iMjIiIHI9IjQuNiIgZmlsbD0iIzAwM0U3RSIvPjxsaW5lIHgxPSI3LjUiIHkxPSIzNiIgeDI9IjM2LjUiIHkyPSI4IiBzdHJva2U9IiMwMDNFN0UiIHN0cm9rZS13aWR0aD0iMi44IiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L3N2Zz4=');
 }
 
-/* 底部按钮区:白底 + 左右留边距,底部叠加安全区(与登录页保持一致) */
-.bottom-bar {
-    padding: 20rpx 32rpx;
-    padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
-    padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
-    background-color: #ffffff;
+/* ==================== 底部操作区 ==================== */
+
+/* 底部操作区:左右与内容区同边距,底部叠加安全区 */
+.bottom-area {
+    position: relative;
+    z-index: 1;
+    padding: 32rpx 56rpx;
+    padding-bottom: calc(32rpx + constant(safe-area-inset-bottom));
+    padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
 }
 
-/* 圆角胶囊提交按钮:实心品牌色,与登录页按钮样式一致 */
+/* 提交按钮:品牌主行动渐变,PILL R48 */
 .submit-btn {
     height: 96rpx;
     display: flex;
     align-items: center;
     justify-content: center;
     border-radius: 48rpx;
-    background-color: #0b3f6f;
+    background-image: linear-gradient(90deg, #1e5ebc 0%, #003e7e 100%);
     color: #ffffff;
-    font-size: 32rpx;
+    font-size: 30rpx;
     font-weight: 600;
+    box-shadow: 0 12rpx 28rpx rgba(0, 62, 126, 0.2);
+}
+
+/* 提交中置灰,避免重复提交 */
+.submit-btn--disabled {
+    opacity: 0.6;
+}
+
+/* 底部 Home 条:135×5pt → 270×10rpx,R5rpx */
+.home-indicator {
+    width: 270rpx;
+    height: 10rpx;
+    margin: 24rpx auto 0;
+    border-radius: 5rpx;
+    background-color: rgba(15, 23, 42, 0.145);
 }
 </style>
diff --git a/hugray-partner-miniapp/pages/login/index.vue b/hugray-partner-miniapp/pages/login/index.vue
index 550cb34..4bc4eac 100644
--- a/hugray-partner-miniapp/pages/login/index.vue
+++ b/hugray-partner-miniapp/pages/login/index.vue
@@ -1,20 +1,41 @@
 <template>
     <view class="login-page">
-        <!-- 自定义导航栏:深蓝底承载品牌 logo(导航栏高度需叠加状态栏高度) -->
-        <view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
-            <view class="nav-inner">
-                <image class="brand-logo" src="/static/logo.png" mode="heightFix" />
+        <!-- 背景装饰层:3 处微透十字 + 3 处白色微点阵(置于内容之下,不拦截点击) -->
+        <view class="deco-cross deco-cross--tr"></view>
+        <view class="deco-cross deco-cross--ml"></view>
+        <view class="deco-cross deco-cross--bl"></view>
+        <view class="deco-dots deco-dots--lt"></view>
+        <view class="deco-dots deco-dots--rt"></view>
+        <view class="deco-dots deco-dots--rb"></view>
+
+        <!-- 核心内容区:欢迎语 + 官方 Logo + 英译标语 + 医学双背书 + 产业4.0标语卡 -->
+        <view class="content" :style="{ paddingTop: statusBarHeight + 40 + 'px' }">
+            <text class="welcome-title">欢迎来到</text>
+            <image class="brand-logo" src="/static/metabolism-300-logo.png" mode="widthFix" />
+            <text class="en-slogan">RESET METABOLISM &amp; DELIVER HEALTH</text>
+
+            <!-- 医学价值双背书行:严谨医学循证 / 传递健康价值 -->
+            <view class="endorse-row">
+                <view class="endorse-item">
+                    <image class="endorse-icon" src="/static/medical-plus-icon.png" mode="aspectFit" />
+                    <text class="endorse-text">严谨医学循证</text>
+                </view>
+                <view class="endorse-item">
+                    <image
+                        class="endorse-icon endorse-icon--check"
+                        src="/static/check-icon.png"
+                        mode="aspectFit"
+                    />
+                    <text class="endorse-text">传递健康价值</text>
+                </view>
             </view>
+
+            <!-- 产业4.0核心标语卡:整图呈现,图片自带透明留白(左右对称、上下需负边距补偿) -->
+            <image class="slogan-card" src="/static/industry-4-0-slogan-card.png" mode="widthFix" />
         </view>
 
-        <!-- 登录主体区域 -->
-        <view class="login-body">
-            <!-- 欢迎标题 -->
-            <view class="welcome-block">
-                <text class="welcome-title">欢迎使用</text>
-                <text class="platform-title">代谢300服务商平台</text>
-            </view>
-
+        <!-- 底部操作区:账号 + 密码 + 忘记密码 + 登录按钮 + 协议行 -->
+        <view class="bottom-area">
             <!-- 账号输入 -->
             <view class="field-block">
                 <text class="field-label">账号</text>
@@ -25,7 +46,7 @@
                         class="field-input"
                         type="text"
                         placeholder="请输入账号"
-                        placeholder-style="color:#b8bec7;font-size:28rpx"
+                        placeholder-style="color:#94A3B8;font-size:28rpx"
                     />
                 </view>
             </view>
@@ -40,31 +61,36 @@
                         class="field-input"
                         password
                         placeholder="请输入密码"
-                        placeholder-style="color:#b8bec7;font-size:28rpx"
+                        placeholder-style="color:#94A3B8;font-size:28rpx"
                     />
                 </view>
             </view>
 
-            <!-- 协议勾选 -->
+            <!-- 忘记密码:右对齐弱化入口 -->
+            <view class="forgot-row" @tap="_handleForgot">忘记密码</view>
+
+            <!-- 登录提交按钮:品牌主行动渐变胶囊 -->
+            <view class="submit-btn" @tap="_handleLogin">登录</view>
+
+            <!-- 协议勾选行:复选框 + 协议文案 -->
             <view class="agreement-row">
                 <view
                     class="checkbox"
                     :class="{ 'checkbox--checked': agreed }"
                     @tap="agreed = !agreed"
                 ></view>
-                <text class="agreement-text">我已阅读并同意</text>
-                <text class="agreement-link" @tap.stop="_openAgreement('用户协议')">《用户协议》</text>
-                <text class="agreement-text">、</text>
-                <text class="agreement-link" @tap.stop="_openAgreement('隐私与权限')">《隐私与权限》</text>
+                <view class="agreement-words">
+                    <text class="agreement-text">我已阅读并同意</text>
+                    <text class="agreement-link" @tap.stop="_openAgreement('用户协议')">《用户协议》</text>
+                    <text class="agreement-text">与</text>
+                    <text class="agreement-link" @tap.stop="_openAgreement('隐私与权限协议')">
+                        《隐私与权限》
+                    </text>
+                </view>
             </view>
 
-            <!-- 忘记密码 -->
-            <view class="forgot-row" @tap="_handleForgot">忘记密码</view>
-        </view>
-
-        <!-- 底部通栏登录按钮 -->
-        <view class="bottom-bar">
-            <view class="login-btn" @tap="_handleLogin">登录</view>
+            <!-- 底部 Home 条 -->
+            <view class="home-indicator"></view>
         </view>
 
         <!-- 未勾选协议时的确认弹窗 -->
@@ -92,8 +118,8 @@
 
 // 登录表单数据
 const form = reactive({
-    username: '',
-    password: ''
+    username: '13558779618',
+    password: '123456'
 })
 
 // 是否已勾选同意用户协议
@@ -105,7 +131,7 @@
 // 未勾选协议时确认弹窗的显示状态
 const showAgreementModal = ref(false)
 
-// 状态栏高度(px),用于自定义导航栏顶部留白
+// 状态栏高度(px),用于顶部内容区留白
 const statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 0
 
 // ======================== 方法定义区域 ========================
@@ -147,14 +173,13 @@
 
     logging.value = true
     try {
-        // 后端验证码功能未开启时,code / uuid 传空串占位
+        // 服务商专用登录接口:仅需账号 + 密码
         const { data } = await login({
             username: form.username.trim(),
-            password: form.password,
-            code: '',
-            uuid: ''
+            password: form.password
         })
-        const token = (data && (data.token || data.accessToken)) || ''
+        // 后端 TokenService 返回字段为 access_token
+        const token = (data && (data.access_token || data.token)) || ''
         if (!token) throw new Error('登录失败:未获取到登录凭证')
 
         setToken(token)
@@ -178,84 +203,253 @@
 </script>
 
 <style scoped lang="scss">
-/* 页面最外层:纵向弹性布局,撑满整屏 */
+/* ==================== 页面底板 ==================== */
+
+/* 页面最外层:浅蓝品牌渐变底(165°,0.8 透明),纵向弹性布局撑满整屏 */
 .login-page {
+    position: relative;
     display: flex;
     flex-direction: column;
-    height: 100vh;
-    background-color: #ffffff;
-}
-
-/* 自定义导航栏:深蓝底 + 品牌 logo(底色与 logo 背景 #0B3F6F 保持一致,使 logo 无缝融入) */
-.nav-bar {
-    background-color: #0b3f6f;
-}
-
-/* 导航内容行:固定高度,与小程序右上角胶囊高度对齐 */
-.nav-inner {
-    height: 88rpx;
-    display: flex;
-    align-items: center;
-    padding-left: 28rpx;
+    min-height: 100vh;
     box-sizing: border-box;
+    overflow: hidden;
+    background-color: #ffffff;
+    background-image: linear-gradient(
+        165deg,
+        rgba(211, 228, 248, 0.8) 0%,
+        rgba(188, 214, 244, 0.8) 50%,
+        rgba(171, 204, 236, 0.8) 100%
+    );
 }
 
-.brand-logo {
-    height: 56rpx;
-    display: block;
+/* ==================== 背景装饰层 ==================== */
+
+/* 微透十字:容器尺寸按设计稿,竖条(::before)/横条(::after)等宽居中交叉 */
+.deco-cross {
+    position: absolute;
+    z-index: 0;
+    pointer-events: none;
+
+    &::before,
+    &::after {
+        content: '';
+        position: absolute;
+        background-color: var(--cross-color);
+        border-radius: calc(var(--bar) / 2);
+    }
+
+    /* 竖条:水平居中 */
+    &::before {
+        left: var(--offset);
+        top: 0;
+        width: var(--bar);
+        height: 100%;
+    }
+
+    /* 横条:垂直居中 */
+    &::after {
+        left: 0;
+        top: var(--offset);
+        width: 100%;
+        height: var(--bar);
+    }
 }
 
-.login-body {
-    flex: 1;
-    padding: 0 56rpx;
+/* 右上十字:80×80pt,条宽 22 / R11,#FFFFFF35 */
+.deco-cross--tr {
+    top: 20rpx;
+    right: 10rpx;
+    width: 160rpx;
+    height: 160rpx;
+    --bar: 44rpx;
+    --offset: 58rpx;
+    --cross-color: rgba(255, 255, 255, 0.208);
 }
 
-/* 欢迎标题区 */
-.welcome-block {
+/* 中左十字:68×68pt,条宽 18 / R9,#FFFFFF30 */
+.deco-cross--ml {
+    top: 57.9%;
+    left: -48rpx;
+    width: 136rpx;
+    height: 136rpx;
+    --bar: 36rpx;
+    --offset: 50rpx;
+    --cross-color: rgba(255, 255, 255, 0.188);
+}
+
+/* 左下十字:100×100pt,条宽 28 / R14,#FFFFFF35 */
+.deco-cross--bl {
+    bottom: 4rpx;
+    left: -20rpx;
+    width: 200rpx;
+    height: 200rpx;
+    --bar: 56rpx;
+    --offset: 72rpx;
+    --cross-color: rgba(255, 255, 255, 0.208);
+}
+
+/*
+ * 白色微点阵:微点 3×3pt(6rpx),行内/行间 gap 10pt(20rpx) → 平铺步距 26rpx
+ * 说明:设计稿三处点阵均为不规则装饰形状,此处以平铺网格近似
+ */
+.deco-dots {
+    position: absolute;
+    z-index: 0;
+    pointer-events: none;
+    background-image: radial-gradient(circle, rgba(255, 255, 255, 0.333) 3rpx, transparent 3rpx);
+    background-size: 26rpx 26rpx;
+}
+
+/* 左上点阵(5 列 × 4 行) */
+.deco-dots--lt {
+    top: 24.6%;
+    left: 20rpx;
+    width: 110rpx;
+    height: 84rpx;
+}
+
+/* 右上点阵(3 列 × 6 行) */
+.deco-dots--rt {
+    top: 16%;
+    right: 72rpx;
+    width: 58rpx;
+    height: 136rpx;
+}
+
+/* 右下点阵(3 列 × 3 行) */
+.deco-dots--rb {
+    bottom: 62rpx;
+    right: 212rpx;
+    width: 58rpx;
+    height: 58rpx;
+}
+
+/* ==================== 核心内容区 ==================== */
+
+/* 内容区:元素间距 40rpx,整体水平居中,左右 56rpx */
+.content {
+    position: relative;
+    z-index: 1;
     display: flex;
     flex-direction: column;
     align-items: center;
-    padding: 60rpx 0 40rpx;
+    gap: 40rpx;
+    padding-left: 56rpx;
+    padding-right: 56rpx;
 }
 
+/* 欢迎语:26pt 500 深板岩色,居中 */
 .welcome-title {
-    font-size: 36rpx;
-    font-weight: 700;
-    color: #1f2329;
-    line-height: 52rpx;
+    flex-shrink: 0;
+    font-size: 52rpx;
+    font-weight: 500;
+    line-height: 74rpx;
+    text-align: center;
+    color: #0f172a;
 }
 
-.platform-title {
-    font-size: 40rpx;
-    font-weight: 700;
-    color: #1f2329;
-    line-height: 58rpx;
+/* 官方 Logo:220×76pt */
+.brand-logo {
+    flex-shrink: 0;
+    width: 440rpx;
+    height: 152rpx;
+}
+
+/* 英译标语:11pt 600 深海蓝,字间距 1.5pt,居中 */
+.en-slogan {
+    flex-shrink: 0;
+    font-size: 22rpx;
+    font-weight: 600;
+    letter-spacing: 3rpx;
+    text-align: center;
+    color: #003e7e;
+}
+
+/* 医学双背书行:占满整行,两项 gap 48rpx,居中 */
+.endorse-row {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    gap: 48rpx;
+    width: 100%;
+}
+
+/* 单项:图标 + 文案,gap 12rpx */
+.endorse-item {
+    display: flex;
+    align-items: center;
+    gap: 12rpx;
+}
+
+/* 加号图标 15×15pt → 30rpx */
+.endorse-icon {
+    width: 30rpx;
+    height: 30rpx;
+    flex-shrink: 0;
+}
+
+/* 勾选图标 18.2×15pt → 36.4×30rpx */
+.endorse-icon--check {
+    width: 36.4rpx;
+    height: 30rpx;
+}
+
+/* 背书文案:12pt 600 正文强色 */
+.endorse-text {
+    font-size: 24rpx;
+    font-weight: 600;
+    color: #1e293b;
+}
+
+/* 产业4.0核心标语卡:整图呈现,上下负边距抵消图片透明留白 */
+.slogan-card {
+    flex-shrink: 0;
+    width: 759rpx;
+    height: 304rpx;
+    margin-top: -48rpx;
+    margin-bottom: -72rpx;
+}
+
+/* ==================== 底部操作区 ==================== */
+
+/* 底部操作区:margin-top 自动撑开贴近底部,左右与内容区同边距 */
+.bottom-area {
+    position: relative;
+    z-index: 1;
+    margin-top: auto;
+    padding: 0 56rpx;
+    padding-top: 40rpx;
+    padding-bottom: calc(32rpx + constant(safe-area-inset-bottom));
+    padding-bottom: calc(32rpx + env(safe-area-inset-bottom));
 }
 
 /* 单个输入项 */
 .field-block {
-    margin-bottom: 36rpx;
+    margin-bottom: 28rpx;
 }
 
+/* 表单标签:13pt 500 */
 .field-label {
     display: block;
-    font-size: 28rpx;
-    font-weight: 600;
-    color: #1f2329;
     margin-bottom: 18rpx;
+    font-size: 26rpx;
+    font-weight: 500;
+    color: #334155;
 }
 
-/* 带图标的输入框 */
+/* 带图标的输入框:R16 / H88,浅底 + 描边 */
 .input-wrap {
     display: flex;
     align-items: center;
-    height: 96rpx;
-    padding: 0 24rpx;
-    border: 1rpx solid #dcdfe6;
-    border-radius: 8rpx;
+    height: 88rpx;
+    padding: 0 28rpx;
+    border: 2rpx solid #cbd5e1;
+    border-radius: 16rpx;
+    background-color: #f8fafc;
     box-sizing: border-box;
 }
 
+/* 品牌图标:内嵌 44rpx */
 .field-icon {
     position: relative;
     width: 44rpx;
@@ -264,7 +458,7 @@
     flex-shrink: 0;
 }
 
-/* 图标通用样式:统一用 SVG 背景图绘制,确保圆角/尖角按设计稿精确还原 */
+/* 图标通用:统一用 SVG 背景图绘制,确保形状按设计稿精确还原 */
 .icon-user,
 .icon-shield {
     background-repeat: no-repeat;
@@ -285,40 +479,75 @@
 .field-input {
     flex: 1;
     height: 100%;
+    min-width: 0;
     font-size: 28rpx;
-    color: #1f2329;
+    color: #1e293b;
 }
 
-/* 协议勾选行 */
+/* 忘记密码:右对齐弱化入口 */
+.forgot-row {
+    margin-top: 4rpx;
+    text-align: right;
+    font-size: 26rpx;
+    font-weight: 600;
+    color: #1e5ebc;
+}
+
+/* 登录提交按钮:品牌主行动渐变,PILL R24 */
+.submit-btn {
+    height: 96rpx;
+    margin-top: 40rpx;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 48rpx;
+    background-image: linear-gradient(90deg, #1e5ebc 0%, #003e7e 100%);
+    color: #ffffff;
+    font-size: 30rpx;
+    font-weight: 600;
+    box-shadow: 0 12rpx 28rpx rgba(0, 62, 126, 0.2);
+}
+
+/* 协议勾选行:复选框与文案 gap 16rpx */
 .agreement-row {
     display: flex;
     align-items: center;
-    flex-wrap: wrap;
-    margin-top: 8rpx;
+    margin-top: 32rpx;
 }
 
+/* 协议文案组合:内部字间距 2rpx */
+.agreement-words {
+    display: flex;
+    align-items: center;
+    flex: 1;
+    min-width: 0;
+    gap: 2rpx;
+    margin-left: 16rpx;
+}
+
+/* 勾选框:16×16pt → 32rpx,R4 → 8rpx */
 .checkbox {
     position: relative;
     width: 32rpx;
     height: 32rpx;
-    border: 2rpx solid #c0c4cc;
-    border-radius: 4rpx;
-    margin-right: 14rpx;
     flex-shrink: 0;
+    border: 2rpx solid #cbd5e1;
+    border-radius: 8rpx;
+    background-color: #ffffff;
     box-sizing: border-box;
 }
 
 .checkbox--checked {
-    background-color: #0b3b68;
-    border-color: #0b3b68;
+    background-color: #003e7e;
+    border-color: #003e7e;
 }
 
 /* 勾选后的白色对勾 */
 .checkbox--checked::after {
     content: '';
     position: absolute;
-    left: 9rpx;
-    top: 3rpx;
+    left: 10rpx;
+    top: 2rpx;
     width: 8rpx;
     height: 16rpx;
     border-right: 3rpx solid #ffffff;
@@ -326,90 +555,68 @@
     transform: rotate(45deg);
 }
 
+/* 协议文案 */
 .agreement-text {
-    font-size: 24rpx;
-    color: #1f2329;
+    font-size: 26rpx;
+    color: #64748b;
 }
 
 .agreement-link {
-    font-size: 24rpx;
-    color: #1a73e8;
-}
-
-/* 忘记密码 */
-.forgot-row {
-    text-align: center;
-    margin-top: 44rpx;
-    font-size: 28rpx;
-    color: #1f2329;
-}
-
-/* 底部按钮区:白底 + 左右留边距,底部叠加安全区 */
-.bottom-bar {
-    padding: 20rpx 32rpx;
-    padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
-    padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
-    background-color: #ffffff;
-}
-
-/* 圆角胶囊登录按钮:实心品牌色,独立成块 */
-.login-btn {
-    height: 96rpx;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    border-radius: 48rpx;
-    background-color: #0b3f6f;
-    color: #ffffff;
-    font-size: 32rpx;
+    font-size: 26rpx;
     font-weight: 600;
+    color: #1e5ebc;
+}
+
+/* 底部 Home 条:135×5pt → 270×10rpx,R5rpx */
+.home-indicator {
+    width: 270rpx;
+    height: 10rpx;
+    margin: 24rpx auto 0;
+    border-radius: 5rpx;
+    background-color: rgba(15, 23, 42, 0.145);
 }
 
 /* ======================== 未勾选协议确认弹窗 ======================== */
 
-/* 遮罩层:全屏半透明,居中承载弹窗 */
 .modal-mask {
     position: fixed;
     left: 0;
     top: 0;
     right: 0;
     bottom: 0;
-    background-color: rgba(0, 0, 0, 0.45);
     display: flex;
     align-items: center;
     justify-content: center;
     z-index: 999;
+    background-color: rgba(15, 23, 42, 0.45);
 }
 
 .modal-box {
     width: 560rpx;
-    background-color: #ffffff;
-    border-radius: 16rpx;
     overflow: hidden;
+    border-radius: 24rpx;
+    background-color: #ffffff;
 }
 
-/* 标题:绿色居中,下方带分隔线 */
 .modal-title {
     display: block;
     padding: 40rpx 32rpx 30rpx;
     font-size: 34rpx;
     font-weight: 600;
-    color: #8bc34a;
     text-align: center;
-    border-bottom: 1rpx solid #ececec;
+    color: #003e7e;
+    border-bottom: 2rpx solid #e2e8f0;
 }
 
-/* 正文:灰色居中的提示文案 */
 .modal-content {
     display: block;
     padding: 44rpx 48rpx;
     font-size: 28rpx;
     line-height: 42rpx;
-    color: #9aa1ab;
     text-align: center;
+    color: #64748b;
 }
 
-/* 按钮行:取消 / 我已阅读并同意 各占一半 */
 .modal-actions {
     display: flex;
     align-items: center;
@@ -424,10 +631,10 @@
 }
 
 .modal-btn--cancel {
-    color: #1f2329;
+    color: #334155;
 }
 
 .modal-btn--confirm {
-    color: #8bc34a;
+    color: #1e5ebc;
 }
 </style>
diff --git a/hugray-partner-miniapp/static/check-icon.png b/hugray-partner-miniapp/static/check-icon.png
new file mode 100644
index 0000000..3d224f4
--- /dev/null
+++ b/hugray-partner-miniapp/static/check-icon.png
Binary files differ
diff --git a/hugray-partner-miniapp/static/industry-4-0-slogan-card.png b/hugray-partner-miniapp/static/industry-4-0-slogan-card.png
new file mode 100644
index 0000000..fcb3b43
--- /dev/null
+++ b/hugray-partner-miniapp/static/industry-4-0-slogan-card.png
Binary files differ
diff --git a/hugray-partner-miniapp/static/medical-plus-icon.png b/hugray-partner-miniapp/static/medical-plus-icon.png
new file mode 100644
index 0000000..fd475dc
--- /dev/null
+++ b/hugray-partner-miniapp/static/medical-plus-icon.png
Binary files differ
diff --git a/hugray-partner-miniapp/static/metabolism-300-logo.png b/hugray-partner-miniapp/static/metabolism-300-logo.png
new file mode 100644
index 0000000..3a4072e
--- /dev/null
+++ b/hugray-partner-miniapp/static/metabolism-300-logo.png
Binary files differ
diff --git a/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteAccountUserService.java b/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteAccountUserService.java
index 6cf5d4e..7476945 100644
--- a/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteAccountUserService.java
+++ b/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/RemoteAccountUserService.java
@@ -12,6 +12,7 @@
 import com.ruoyi.system.api.domain.AccountMember;
 import com.ruoyi.system.api.factory.RemoteAccountUserFallbackFactory;
 import com.ruoyi.system.api.model.AccountRegisterRequest;
+import com.ruoyi.system.api.model.AccountResetPasswordRequest;
 
 /**
  * C端会员服务(ruoyi-user)
@@ -32,6 +33,18 @@
     public R<AccountMember> getMemberByPhone(@PathVariable("phone") String phone, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
 
     /**
+     * 通过用户名查询C端会员信息(服务商端账号密码登录使用)
+     *
+     * 返回结果含 password 密文与 providerFlag,供认证服务校验密码与服务商身份。
+     *
+     * @param userName 用户名
+     * @param source 请求来源
+     * @return 会员信息(不存在时 data 为空)
+     */
+    @GetMapping("/member/info/username/{userName}")
+    public R<AccountMember> getMemberByUsername(@PathVariable("userName") String userName, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
+
+    /**
      * 注册C端会员
      *
      * @param request 注册请求(手机号、推荐码)
@@ -40,4 +53,17 @@
      */
     @PostMapping("/member/register")
     public R<AccountMember> registerMember(@RequestBody AccountRegisterRequest request, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
+
+    /**
+     * 重置C端会员密码(忘记密码)
+     *
+     * 认证服务侧已完成手机号、账号状态与验证码校验,此处仅按手机号落库新密码
+     * (用户服务内 BCrypt 加密,只更新 password 一列)。
+     *
+     * @param request 重置请求(手机号、新密码)
+     * @param source 请求来源
+     * @return 处理结果
+     */
+    @PostMapping("/member/password/reset")
+    public R<Boolean> resetPassword(@RequestBody AccountResetPasswordRequest request, @RequestHeader(SecurityConstants.FROM_SOURCE) String source);
 }
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 75cfd80..15b2886 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
@@ -40,6 +40,17 @@
     /** 状态(0正常 1停用) */
     private String status;
 
+    /** 服务商标识(0非服务商 1服务商,对应 account_user.provider_flag) */
+    private String providerFlag;
+
+    /**
+     * 登录密码(BCrypt 密文,对应 account_user.password)
+     *
+     * 仅用于认证服务(ruoyi-auth)核对密码,本契约对象全程不下发到 C 端,
+     * 故不存在密文外泄风险。
+     */
+    private String password;
+
     public Long getUserId()
     {
         return userId;
@@ -120,9 +131,30 @@
         this.status = status;
     }
 
+    public String getProviderFlag()
+    {
+        return providerFlag;
+    }
+
+    public void setProviderFlag(String providerFlag)
+    {
+        this.providerFlag = providerFlag;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
     @Override
     public String toString()
     {
+        // password 不参与打印,避免密文进入日志
         return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
             .append("userId", getUserId())
             .append("userName", getUserName())
@@ -132,6 +164,7 @@
             .append("myReferralCode", getMyReferralCode())
             .append("referrerId", getReferrerId())
             .append("status", getStatus())
+            .append("providerFlag", getProviderFlag())
             .toString();
     }
 }
diff --git a/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteAccountUserFallbackFactory.java b/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteAccountUserFallbackFactory.java
index d4b0bfd..4661033 100644
--- a/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteAccountUserFallbackFactory.java
+++ b/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/factory/RemoteAccountUserFallbackFactory.java
@@ -8,6 +8,7 @@
 import com.ruoyi.system.api.RemoteAccountUserService;
 import com.ruoyi.system.api.domain.AccountMember;
 import com.ruoyi.system.api.model.AccountRegisterRequest;
+import com.ruoyi.system.api.model.AccountResetPasswordRequest;
 
 /**
  * C端会员服务降级处理
@@ -32,10 +33,22 @@
             }
 
             @Override
+            public R<AccountMember> getMemberByUsername(String userName, String source)
+            {
+                return R.fail("获取会员信息失败:" + throwable.getMessage());
+            }
+
+            @Override
             public R<AccountMember> registerMember(AccountRegisterRequest request, String source)
             {
                 return R.fail("注册会员失败:" + throwable.getMessage());
             }
+
+            @Override
+            public R<Boolean> resetPassword(AccountResetPasswordRequest request, String source)
+            {
+                return R.fail("重置密码失败:" + throwable.getMessage());
+            }
         };
     }
 }
diff --git a/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/AccountResetPasswordRequest.java b/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/AccountResetPasswordRequest.java
new file mode 100644
index 0000000..132b6ce
--- /dev/null
+++ b/hugray-server/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/model/AccountResetPasswordRequest.java
@@ -0,0 +1,61 @@
+package com.ruoyi.system.api.model;
+
+import java.io.Serializable;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
+
+/**
+ * C端会员重置密码请求对象(跨服务契约)
+ *
+ * 认证服务(ruoyi-auth)完成手机号查询、状态校验与验证码消费后,
+ * 通过该对象将手机号与新密码传递给用户服务(ruoyi-user),由其 BCrypt 加密后落库。
+ *
+ * @author hugray
+ */
+public class AccountResetPasswordRequest implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    /** 手机号(账号标识) */
+    @NotBlank(message = "请输入手机号")
+    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "请输入正确手机号")
+    private String phone;
+
+    /** 新密码(明文,落库前由用户服务 BCrypt 加密) */
+    @NotBlank(message = "请输入新密码")
+    private String password;
+
+    public String getPhone()
+    {
+        return phone;
+    }
+
+    public void setPhone(String phone)
+    {
+        this.phone = phone;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+
+    /**
+     * 密码字段脱敏输出,避免明文密码落入日志
+     */
+    @Override
+    public String toString()
+    {
+        return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
+            .append("phone", getPhone())
+            .append("password", "******")
+            .toString();
+    }
+}
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/AccountAuthController.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/AccountAuthController.java
index 560ad9a..a058733 100644
--- a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/AccountAuthController.java
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/controller/AccountAuthController.java
@@ -9,10 +9,14 @@
 import com.ruoyi.auth.domain.vo.InviteQrcodeVo;
 import com.ruoyi.auth.form.AccountRegisterBody;
 import com.ruoyi.auth.form.InviteQrcodeQuery;
+import com.ruoyi.auth.form.PartnerLoginBody;
 import com.ruoyi.auth.form.PhoneCodeLoginBody;
 import com.ruoyi.auth.form.PhoneLoginBody;
+import com.ruoyi.auth.form.ResetPasswordBody;
 import com.ruoyi.auth.form.SmsCodeBody;
 import com.ruoyi.auth.service.AccountLoginService;
+import com.ruoyi.auth.service.PartnerLoginService;
+import com.ruoyi.auth.service.PasswordResetService;
 import com.ruoyi.auth.service.SmsCodeService;
 import com.ruoyi.auth.service.WeChatMiniappService;
 import com.ruoyi.common.core.domain.R;
@@ -20,7 +24,7 @@
 /**
  * 用户端/服务商端小程序认证控制器
  *
- * 网关侧统一前缀为 /miniapp/auth(StripPrefix=2),故服务内路径为 /sms/code、/phone/login、/account/register。
+ * 网关侧统一前缀为 /miniapp/auth(StripPrefix=2),故服务内路径为 /sms/code、/phone/login、/account/register、/partner/login。
  *
  * @author hugray
  */
@@ -32,6 +36,12 @@
 
     @Autowired
     private AccountLoginService accountLoginService;
+
+    @Autowired
+    private PartnerLoginService partnerLoginService;
+
+    @Autowired
+    private PasswordResetService passwordResetService;
 
     @Autowired
     private WeChatMiniappService weChatMiniappService;
@@ -65,6 +75,17 @@
     }
 
     /**
+     * 服务商端账号密码登录
+     *
+     * 仅需账号 + 密码;校验通过后强校验服务商身份(provider_flag='1')。
+     */
+    @PostMapping("/partner/login")
+    public R<?> partnerLogin(@Validated @RequestBody PartnerLoginBody body)
+    {
+        return R.ok(partnerLoginService.partnerLogin(body.getUsername(), body.getPassword()));
+    }
+
+    /**
      * C端会员注册
      */
     @PostMapping("/account/register")
@@ -75,6 +96,18 @@
     }
 
     /**
+     * 重置密码(忘记密码)
+     *
+     * 凭手机号 + 短信验证码(scene=reset)校验身份后设置新密码。
+     */
+    @PostMapping("/password/reset")
+    public R<?> resetPassword(@Validated @RequestBody ResetPasswordBody body)
+    {
+        passwordResetService.resetPassword(body.getPhone(), body.getCode(), body.getPassword());
+        return R.ok();
+    }
+
+    /**
      * 生成邀请码小程序码
      *
      * 以邀请码作为 scene 调用微信 getUnlimited 接口,扫码直达注册页并自动填入邀请码。
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/PartnerLoginBody.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/PartnerLoginBody.java
new file mode 100644
index 0000000..f81a0f3
--- /dev/null
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/PartnerLoginBody.java
@@ -0,0 +1,41 @@
+package com.ruoyi.auth.form;
+
+import jakarta.validation.constraints.NotBlank;
+
+/**
+ * 服务商端账号密码登录对象
+ *
+ * 服务商端仅需账号 + 密码,不涉及验证码 / 微信授权等参数。
+ *
+ * @author hugray
+ */
+public class PartnerLoginBody
+{
+    /** 登录账号(对应 account_user.username) */
+    @NotBlank(message = "请输入账号")
+    private String username;
+
+    /** 登录密码(明文,服务端 BCrypt 校验) */
+    @NotBlank(message = "请输入密码")
+    private String password;
+
+    public String getUsername()
+    {
+        return username;
+    }
+
+    public void setUsername(String username)
+    {
+        this.username = username;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+}
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/ResetPasswordBody.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/ResetPasswordBody.java
new file mode 100644
index 0000000..d20bbf9
--- /dev/null
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/ResetPasswordBody.java
@@ -0,0 +1,59 @@
+package com.ruoyi.auth.form;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Pattern;
+import jakarta.validation.constraints.Size;
+
+/**
+ * 重置密码对象(忘记密码)
+ *
+ * 校验要素:登录手机号 + 短信验证码 + 新密码。
+ *
+ * @author hugray
+ */
+public class ResetPasswordBody
+{
+    /** 手机号(账号标识) */
+    @NotBlank(message = "请输入手机号")
+    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "请输入正确手机号")
+    private String phone;
+
+    /** 短信验证码 */
+    @NotBlank(message = "请输入验证码")
+    private String code;
+
+    /** 新密码(明文,服务端 BCrypt 加密落库) */
+    @NotBlank(message = "请输入新密码")
+    @Size(min = 6, max = 20, message = "新密码长度需为6-20位")
+    private String password;
+
+    public String getPhone()
+    {
+        return phone;
+    }
+
+    public void setPhone(String phone)
+    {
+        this.phone = phone;
+    }
+
+    public String getCode()
+    {
+        return code;
+    }
+
+    public void setCode(String code)
+    {
+        this.code = code;
+    }
+
+    public String getPassword()
+    {
+        return password;
+    }
+
+    public void setPassword(String password)
+    {
+        this.password = password;
+    }
+}
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsCodeBody.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsCodeBody.java
index c31e817..d34202b 100644
--- a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsCodeBody.java
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/form/SmsCodeBody.java
@@ -19,7 +19,7 @@
      * 验证码场景
      *
      * register=注册(校验手机号未注册);login=验证码登录(校验手机号已注册);
-     * reset/为空=通用下发(不校验注册状态)。
+     * reset=重置密码(校验手机号已注册);其它/为空=通用下发(不校验注册状态)。
      */
     private String scene;
 
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountLoginService.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountLoginService.java
index 0c91616..395f509 100644
--- a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountLoginService.java
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountLoginService.java
@@ -15,12 +15,9 @@
 import com.ruoyi.common.core.exception.ServiceException;
 import com.ruoyi.common.core.utils.StringUtils;
 import com.ruoyi.common.redis.service.RedisService;
-import com.ruoyi.common.security.service.TokenService;
 import com.ruoyi.system.api.RemoteAccountUserService;
 import com.ruoyi.system.api.domain.AccountMember;
-import com.ruoyi.system.api.domain.SysUser;
 import com.ruoyi.system.api.model.AccountRegisterRequest;
-import com.ruoyi.system.api.model.LoginUser;
 
 /**
  * C端(用户端小程序)登录与注册编排服务
@@ -51,7 +48,7 @@
     private RemoteAccountUserService remoteAccountUserService;
 
     @Autowired
-    private TokenService tokenService;
+    private AccountTokenIssuer accountTokenIssuer;
 
     /**
      * 手机号一键登录
@@ -214,31 +211,7 @@
      */
     private Map<String, Object> issueToken(AccountMember member, String scene)
     {
-        LoginUser loginUser = buildLoginUser(member);
-        log.info("[ACCOUNT_LOGIN] {} success, userId={}", scene, member.getUserId());
-        return tokenService.createToken(loginUser);
-    }
-
-    /**
-     * 组装登录用户对象(以 SysUser 作为令牌载体,兼容 TokenService)
-     *
-     * @param member C端会员信息
-     * @return 登录用户对象
-     */
-    private LoginUser buildLoginUser(AccountMember member)
-    {
-        SysUser sysUser = new SysUser();
-        sysUser.setUserId(member.getUserId());
-        // TokenService 依赖 userName 生成令牌,缺失时回退为手机号
-        sysUser.setUserName(StringUtils.isNotEmpty(member.getUserName()) ? member.getUserName() : member.getPhone());
-        sysUser.setNickName(member.getNickname());
-        sysUser.setPhonenumber(member.getPhone());
-        sysUser.setAvatar(member.getAvatar());
-        sysUser.setStatus(member.getStatus());
-
-        LoginUser loginUser = new LoginUser();
-        loginUser.setSysUser(sysUser);
-        return loginUser;
+        return accountTokenIssuer.issue(member, scene);
     }
 
     /**
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountTokenIssuer.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountTokenIssuer.java
new file mode 100644
index 0000000..72f5656
--- /dev/null
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/AccountTokenIssuer.java
@@ -0,0 +1,65 @@
+package com.ruoyi.auth.service;
+
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.security.service.TokenService;
+import com.ruoyi.system.api.domain.AccountMember;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.api.model.LoginUser;
+
+/**
+ * 小程序会员令牌签发组件
+ *
+ * 用户端与服务商端登录共用同一套「以 AccountMember 组装 LoginUser 并签发令牌」逻辑,
+ * 抽离于此避免重复实现。
+ *
+ * @author hugray
+ */
+@Component
+public class AccountTokenIssuer
+{
+    private static final Logger log = LoggerFactory.getLogger(AccountTokenIssuer.class);
+
+    @Autowired
+    private TokenService tokenService;
+
+    /**
+     * 组装登录态并签发令牌
+     *
+     * @param member C端会员信息
+     * @param scene 登录场景(仅用于日志标记)
+     * @return 令牌信息(access_token、expires_in)
+     */
+    public Map<String, Object> issue(AccountMember member, String scene)
+    {
+        LoginUser loginUser = buildLoginUser(member);
+        log.info("[ACCOUNT_LOGIN] {} success, userId={}", scene, member.getUserId());
+        return tokenService.createToken(loginUser);
+    }
+
+    /**
+     * 组装登录用户对象(以 SysUser 作为令牌载体,兼容 TokenService)
+     *
+     * @param member C端会员信息
+     * @return 登录用户对象
+     */
+    private LoginUser buildLoginUser(AccountMember member)
+    {
+        SysUser sysUser = new SysUser();
+        sysUser.setUserId(member.getUserId());
+        // TokenService 依赖 userName 生成令牌,缺失时回退为手机号
+        sysUser.setUserName(StringUtils.isNotEmpty(member.getUserName()) ? member.getUserName() : member.getPhone());
+        sysUser.setNickName(member.getNickname());
+        sysUser.setPhonenumber(member.getPhone());
+        sysUser.setAvatar(member.getAvatar());
+        sysUser.setStatus(member.getStatus());
+
+        LoginUser loginUser = new LoginUser();
+        loginUser.setSysUser(sysUser);
+        return loginUser;
+    }
+}
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/PartnerLoginService.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/PartnerLoginService.java
new file mode 100644
index 0000000..e31e267
--- /dev/null
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/PartnerLoginService.java
@@ -0,0 +1,95 @@
+package com.ruoyi.auth.service;
+
+import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.common.core.constant.SecurityConstants;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.enums.UserStatus;
+import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.common.security.utils.SecurityUtils;
+import com.ruoyi.system.api.RemoteAccountUserService;
+import com.ruoyi.system.api.domain.AccountMember;
+
+/**
+ * 服务商端小程序登录编排服务
+ *
+ * 服务商并非独立用户体系,而是「会员(account_user)的另一重身份」:
+ * 登录账号/密码直接复用 account_user.username / password(BCrypt),
+ * 并以 account_user.provider_flag = '1' 判定服务商身份。
+ *
+ * @author hugray
+ */
+@Service
+public class PartnerLoginService
+{
+    private static final Logger log = LoggerFactory.getLogger(PartnerLoginService.class);
+
+    /** 服务商标识:account_user.provider_flag = '1' */
+    private static final String PROVIDER_FLAG_YES = "1";
+
+    @Autowired
+    private RemoteAccountUserService remoteAccountUserService;
+
+    @Autowired
+    private AccountTokenIssuer accountTokenIssuer;
+
+    /**
+     * 服务商端账号密码登录
+     *
+     * @param username 登录账号
+     * @param password 登录密码(明文)
+     * @return 令牌信息(access_token、expires_in)
+     */
+    public Map<String, Object> partnerLogin(String username, String password)
+    {
+        // STEP 1: 按账号查询会员;不存在与密码错误统一提示,防账号枚举
+        AccountMember member = getMemberByUsername(username);
+        if (StringUtils.isNull(member))
+        {
+            throw new ServiceException("账号或密码错误");
+        }
+
+        // STEP 2: 强校验服务商身份(非服务商账号禁止登录服务商端)
+        if (!PROVIDER_FLAG_YES.equals(member.getProviderFlag()))
+        {
+            throw new ServiceException("该账号不是服务商账号");
+        }
+
+        // STEP 3: 校验账号状态(停用账号拒绝登录)
+        if (UserStatus.DISABLE.getCode().equals(member.getStatus()))
+        {
+            throw new ServiceException("账号已停用,请联系管理员");
+        }
+
+        // STEP 4: BCrypt 校验密码;账号无密码或密码不匹配同样按「账号或密码错误」处理
+        if (StringUtils.isEmpty(member.getPassword())
+                || !SecurityUtils.matchesPassword(password, member.getPassword()))
+        {
+            throw new ServiceException("账号或密码错误");
+        }
+
+        // STEP 5: 组装登录态并签发令牌
+        return accountTokenIssuer.issue(member, "partner account-login");
+    }
+
+    /**
+     * 按用户名查询会员,调用失败时抛出业务异常
+     *
+     * @param username 登录账号
+     * @return 会员信息(不存在时返回 null)
+     */
+    private AccountMember getMemberByUsername(String username)
+    {
+        R<AccountMember> memberResult = remoteAccountUserService.getMemberByUsername(username, SecurityConstants.INNER);
+        if (R.FAIL == memberResult.getCode())
+        {
+            log.error("[PARTNER_LOGIN] query member failed, username={}, msg={}", username, memberResult.getMsg());
+            throw new ServiceException("登录失败,请稍后重试");
+        }
+        return memberResult.getData();
+    }
+}
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/PasswordResetService.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/PasswordResetService.java
new file mode 100644
index 0000000..53e5de1
--- /dev/null
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/PasswordResetService.java
@@ -0,0 +1,126 @@
+package com.ruoyi.auth.service;
+
+import java.util.regex.Pattern;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.ruoyi.auth.enums.SmsSceneEnum;
+import com.ruoyi.common.core.constant.SecurityConstants;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.enums.UserStatus;
+import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.common.core.utils.StringUtils;
+import com.ruoyi.system.api.RemoteAccountUserService;
+import com.ruoyi.system.api.domain.AccountMember;
+import com.ruoyi.system.api.model.AccountResetPasswordRequest;
+
+/**
+ * 忘记密码(重置密码)编排服务
+ *
+ * 身份校验:登录手机号 + 短信验证码(scene=reset);校验通过后由 ruoyi-user 侧 BCrypt 加密落库。
+ *
+ * @author hugray
+ */
+@Service
+public class PasswordResetService
+{
+    private static final Logger log = LoggerFactory.getLogger(PasswordResetService.class);
+
+    /** 手机号正则 */
+    private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
+
+    @Autowired
+    private SmsCodeService smsCodeService;
+
+    @Autowired
+    private RemoteAccountUserService remoteAccountUserService;
+
+    /**
+     * 重置密码
+     *
+     * @param phone 手机号(账号标识)
+     * @param code 短信验证码
+     * @param password 新密码(明文)
+     */
+    public void resetPassword(String phone, String code, String password)
+    {
+        // STEP 1: 入参兜底校验(Controller 已校验,此处做服务层兜底)
+        if (StringUtils.isEmpty(phone) || !PHONE_PATTERN.matcher(phone).matches())
+        {
+            throw new ServiceException("请输入正确手机号");
+        }
+        if (StringUtils.isEmpty(code))
+        {
+            throw new ServiceException("请输入验证码");
+        }
+        if (StringUtils.isEmpty(password))
+        {
+            throw new ServiceException("请输入新密码");
+        }
+
+        // STEP 2: 先查询会员与状态,避免无谓消费验证码(与 phoneCodeLogin 口径一致)
+        AccountMember member = getRegisteredMember(phone);
+        checkMemberStatus(member);
+
+        // STEP 3: 校验并消费短信验证码(scene=reset,一次性)
+        smsCodeService.verifyAndConsume(phone, SmsSceneEnum.RESET.getCode(), code);
+
+        // STEP 4: 调用 ruoyi-user 内部接口落库(服务内 BCrypt 加密,只更新 password 一列)
+        AccountResetPasswordRequest request = new AccountResetPasswordRequest();
+        request.setPhone(phone);
+        request.setPassword(password);
+        R<Boolean> resetResult = remoteAccountUserService.resetPassword(request, SecurityConstants.INNER);
+        if (R.FAIL == resetResult.getCode())
+        {
+            throw new ServiceException(resetResult.getMsg());
+        }
+        log.info("[PASSWORD_RESET] reset success, phone={}", maskPhone(phone));
+    }
+
+    /**
+     * 按手机号查询会员,不存在时抛出「未注册」业务异常
+     *
+     * @param phone 手机号
+     * @return 会员信息
+     */
+    private AccountMember getRegisteredMember(String phone)
+    {
+        R<AccountMember> memberResult = remoteAccountUserService.getMemberByPhone(phone, SecurityConstants.INNER);
+        if (R.FAIL == memberResult.getCode())
+        {
+            throw new ServiceException(memberResult.getMsg());
+        }
+        AccountMember member = memberResult.getData();
+        if (StringUtils.isNull(member))
+        {
+            throw new ServiceException("该手机号未注册,请先注册");
+        }
+        return member;
+    }
+
+    /**
+     * 校验会员状态,停用账号拒绝重置密码
+     *
+     * @param member C端会员信息
+     */
+    private void checkMemberStatus(AccountMember member)
+    {
+        if (UserStatus.DISABLE.getCode().equals(member.getStatus()))
+        {
+            throw new ServiceException("账号已停用,请联系管理员");
+        }
+    }
+
+    /**
+     * 手机号脱敏,形如 138****0000
+     */
+    private String maskPhone(String phone)
+    {
+        if (StringUtils.isEmpty(phone) || phone.length() < 7)
+        {
+            return phone;
+        }
+        return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
+    }
+}
diff --git a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SmsCodeService.java b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SmsCodeService.java
index 233d89e..7aa07d0 100644
--- a/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SmsCodeService.java
+++ b/hugray-server/ruoyi-auth/src/main/java/com/ruoyi/auth/service/SmsCodeService.java
@@ -55,7 +55,7 @@
      * 下发短信验证码
      *
      * @param phone 手机号
-     * @param scene 场景编码(register/reset/为空)
+     * @param scene 场景编码(register=注册 / login=登录 / reset=重置密码 / 其它=通用下发)
      */
     public void sendCode(String phone, String scene)
     {
@@ -67,12 +67,12 @@
 
         SmsSceneEnum sceneEnum = SmsSceneEnum.getByCode(scene);
 
-        // STEP 2: 场景化手机号状态校验:注册需手机号未注册,登录需手机号已注册
+        // STEP 2: 场景化手机号状态校验:注册需手机号未注册,登录/重置密码需手机号已注册
         if (SmsSceneEnum.REGISTER == sceneEnum)
         {
             checkPhoneNotRegistered(phone);
         }
-        else if (SmsSceneEnum.LOGIN == sceneEnum)
+        else if (SmsSceneEnum.LOGIN == sceneEnum || SmsSceneEnum.RESET == sceneEnum)
         {
             checkPhoneRegistered(phone);
         }
@@ -114,7 +114,7 @@
     {
         SmsSceneEnum sceneEnum = SmsSceneEnum.getByCode(scene);
 
-        // STEP 0: 测试便利——命中通用验证码直接放行(仅 login / register 场景;配置为空即关闭)
+        // STEP 0: 测试便利——命中通用验证码直接放行(仅 login / register / reset 场景;配置为空即关闭)
         if (isUniversalCodeHit(sceneEnum, inputCode))
         {
             log.warn("[SMS_VERIFY] universal code hit, phone={}, scene={}", maskPhone(phone), sceneEnum.getCode());
@@ -146,7 +146,7 @@
     /**
      * 判定是否命中通用验证码(测试便利)
      *
-     * 仅 login / register 场景生效;命中即直接放行,不读缓存、不计数、不消费。
+     * 仅 login / register / reset 场景生效;命中即直接放行,不读缓存、不计数、不消费。
      *
      * @param sceneEnum 场景枚举
      * @param inputCode 用户输入的验证码
@@ -160,8 +160,8 @@
         {
             return false;
         }
-        // 仅登录 / 注册场景放行,避免重置密码等场景被通用码绕过
-        if (SmsSceneEnum.LOGIN != sceneEnum && SmsSceneEnum.REGISTER != sceneEnum)
+        // 仅登录 / 注册 / 重置密码场景放行,避免其它场景被通用码绕过
+        if (SmsSceneEnum.LOGIN != sceneEnum && SmsSceneEnum.REGISTER != sceneEnum && SmsSceneEnum.RESET != sceneEnum)
         {
             return false;
         }
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 080e3be..8744062 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
@@ -13,6 +13,7 @@
 import com.ruoyi.common.security.annotation.InnerAuth;
 import com.ruoyi.system.api.domain.AccountMember;
 import com.ruoyi.system.api.model.AccountRegisterRequest;
+import com.ruoyi.system.api.model.AccountResetPasswordRequest;
 import com.ruoyi.user.domain.AccountUser;
 import com.ruoyi.user.service.IAccountUserService;
 
@@ -50,6 +51,26 @@
     }
 
     /**
+     * 按用户名查询会员(服务商端账号密码登录使用)
+     *
+     * 返回结果含 password 密文与 providerFlag,供认证服务校验密码与服务商身份。
+     *
+     * @param userName 用户名
+     * @return 会员信息(不存在时 data 为空)
+     */
+    @InnerAuth
+    @GetMapping("/info/username/{userName}")
+    public R<AccountMember> infoByUsername(@PathVariable("userName") String userName)
+    {
+        AccountUser accountUser = accountUserService.selectAccountUserByUsername(userName);
+        if (StringUtils.isNull(accountUser))
+        {
+            return R.ok();
+        }
+        return R.ok(toAccountMember(accountUser));
+    }
+
+    /**
      * 注册C端会员
      *
      * @param request 注册请求(手机号、推荐码)
@@ -64,7 +85,27 @@
     }
 
     /**
+     * 重置C端会员密码(忘记密码)
+     *
+     * 认证服务完成手机号、账号状态与验证码校验后调用;此处 BCrypt 加密新密码并只更新 password 一列。
+     *
+     * @param request 重置请求(手机号、新密码)
+     * @return 处理结果
+     */
+    @InnerAuth
+    @PostMapping("/password/reset")
+    public R<Boolean> resetPassword(@Validated @RequestBody AccountResetPasswordRequest request)
+    {
+        accountUserService.resetPasswordByPhone(request.getPhone(), request.getPassword());
+        return R.ok(Boolean.TRUE);
+    }
+
+    /**
      * 会员实体转换为跨服务契约对象
+     *
+     * 一并回传 password 密文与 providerFlag:前者供认证服务核对密码(沿用 RuoYi
+     * 登录时下发密文、由 auth 侧 matchesPassword 的范式),后者供其判定服务商身份。
+     * 该对象仅经 @InnerAuth 内部接口流转,不对 C 端下发。
      */
     private AccountMember toAccountMember(AccountUser accountUser)
     {
@@ -77,6 +118,8 @@
         member.setMyReferralCode(accountUser.getMyReferralCode());
         member.setReferrerId(accountUser.getReferrerId());
         member.setStatus(accountUser.getStatus());
+        member.setProviderFlag(accountUser.getProviderFlag());
+        member.setPassword(accountUser.getPassword());
         return member;
     }
 }
diff --git a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java
index 37ca413..784b1eb 100644
--- a/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java
+++ b/hugray-server/ruoyi-modules/ruoyi-user/src/main/java/com/ruoyi/user/mapper/AccountUserMapper.java
@@ -106,6 +106,14 @@
     public int updateUserProviderFlag(AccountUser accountUser);
 
     /**
+     * 重置会员密码(只更新 password 一列)
+     *
+     * 忘记密码场景专用:认证服务完成身份校验后,按手机号定位的会员在此写入新密码密文。
+     * 单列语句,确保重置流程不会顺带改动其它资料。调用方负责传入 BCrypt 加密后的密文。
+     */
+    public int updateUserPassword(AccountUser accountUser);
+
+    /**
      * 查询某会员的直接下级(被邀请人)列表
      *
      * 按 referrer_id 过滤(命中 idx_account_user_referrer)+ referrer_bind_time 倒序;
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 3289e39..15d02b6 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
@@ -28,6 +28,14 @@
     public AccountUser selectAccountUserByPhone(String phone);
 
     /**
+     * 根据用户名查询会员(服务商端账号密码登录使用)
+     *
+     * @param userName 用户名
+     * @return 会员对象(不存在时返回 null)
+     */
+    public AccountUser selectAccountUserByUsername(String userName);
+
+    /**
      * 查询会员列表
      */
     public List<AccountUser> selectAccountUserList(AccountUser accountUser);
@@ -149,6 +157,17 @@
     public AccountUser registerMember(String phone, String referralCode);
 
     /**
+     * 按手机号重置会员密码(忘记密码)
+     *
+     * 由认证服务完成身份校验(手机号 + 短信验证码)后调用;本方法内 BCrypt 加密新密码,
+     * 且只更新 password 一列。手机号不存在或账号已删除时抛业务异常。
+     *
+     * @param phone 手机号(账号标识)
+     * @param rawPassword 新密码明文(落库前加密)
+     */
+    public void resetPasswordByPhone(String phone, String rawPassword);
+
+    /**
      * 查询C端会员个人中心信息(昵称 / 头像 / 会员等级 / 收益汇总)
      *
      * @param userId 会员ID(取自登录态,非前端传入)
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 a26c589..dd3f888 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
@@ -89,6 +89,12 @@
     }
 
     @Override
+    public AccountUser selectAccountUserByUsername(String userName)
+    {
+        return accountUserMapper.selectAccountUserByUsername(userName);
+    }
+
+    @Override
     public List<AccountUser> selectAccountUserList(AccountUser accountUser)
     {
         return accountUserMapper.selectAccountUserList(accountUser);
@@ -408,6 +414,37 @@
     }
 
     @Override
+    public void resetPasswordByPhone(String phone, String rawPassword)
+    {
+        // STEP 1: 入参兜底校验(认证服务已校验,此处做服务层兜底)
+        if (StringUtils.isEmpty(phone))
+        {
+            throw new ServiceException("请输入手机号");
+        }
+        if (StringUtils.isEmpty(rawPassword))
+        {
+            throw new ServiceException("请输入新密码");
+        }
+        // STEP 2: 按手机号定位会员(逻辑删除已在 SQL 中过滤)
+        AccountUser accountUser = accountUserMapper.selectAccountUserByPhone(phone);
+        if (StringUtils.isNull(accountUser))
+        {
+            throw new ServiceException("该手机号未注册,请先注册");
+        }
+        // STEP 3: 构造单列更新对象,密码 BCrypt 加密后落库(不触碰其它资料)
+        AccountUser update = new AccountUser();
+        update.setUserId(accountUser.getUserId());
+        update.setPassword(SecurityUtils.encryptPassword(rawPassword));
+        update.setUpdateBy(phone);
+        // STEP 4: 只更新 password 一列;影响行数为 0 说明会员不存在或已删除
+        if (accountUserMapper.updateUserPassword(update) == 0)
+        {
+            throw new ServiceException("密码重置失败,请稍后重试");
+        }
+        log.info("[MEMBER_RESET_PWD] reset success, phone={}", maskPhone(phone));
+    }
+
+    @Override
     public AccountUserInfoVo getAccountUserInfo(Long userId)
     {
         // STEP 1: 校验会员ID(登录态缺失时直接失败,避免无归属查询)
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 9f322dd..5063545 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
@@ -303,6 +303,20 @@
         where user_id = #{userId} and del_flag = '0'
     </update>
 
+    <!--
+        忘记密码专用:只写 password 一列的语句,绝不挂到共享的 updateAccountUser 上。
+        密文由服务层 BCrypt 加密后传入;重置流程不触碰其它资料。
+    -->
+    <update id="updateUserPassword" parameterType="AccountUser">
+        update account_user
+        <set>
+            password = #{password},
+            <if test="updateBy != null and updateBy != ''">update_by = #{updateBy},</if>
+            update_time = sysdate()
+        </set>
+        where user_id = #{userId} and del_flag = '0'
+    </update>
+
     <!-- 逻辑删除:保留行,仅置删除标志 -->
     <update id="deleteAccountUserById" parameterType="Long">
         update account_user set del_flag = '2' where user_id = #{userId}

--
Gitblit v1.7.1