hl-api-changelog/changelogs/2026-05/22_feat_customizer_share_and_remaining_attempts.md

6.9 KiB

feat(user/order): 定制师小程序分享 + 登录锁定剩余次数 + AdminLoginResponse 加 mobile

仓库: HL (后端 hl-user-service + hl-mp-service + hl-order-service-v2 + hl-common-*) 关联 PR/Issue: PR #2913, Closes #2907 日期: 2026-05-22 影响范围: 管理后台登录页错误提示、admin 自身信息接口、小程序定制师身份识别、客人下单链路 接收方: mmg (前端) 前端: 需要 3 处改动 — 登录失败响应解析 + admin info 显示 mobile + 小程序分享按钮


🚨 三大变化

1. AdminLoginResponse 加 mobile 字段

/admin/auth/info / /admin/auth/login / /admin/auth/sms/login / /admin/auth/wechat/verify-2fa / /admin/auth/refresh 等所有 admin 鉴权出口共 9 处 builder 全部填充 admin.mobile。

前端可以直接 data.mobile 拿到当前登录管理员的手机号。

2. 5 次错误锁定 + 剩余次数 / 锁定分钟数

引入 LoginFailException (BusinessException 子类) + GlobalExceptionHandler.handleLoginFail 拦截器 + Result.error(code, msg, data) 三参重载,统一在 Result.data 携带结构化字段:

{
  "code": 200203,
  "message": "密码错误,剩余 3 次尝试",
  "data": {
    "remainingAttempts": 3,
    "lockedMinutes": null
  }
}

前端直接读 data.remainingAttempts / data.lockedMinutes,不用解析 message 中文

3. 定制师小程序分享 + 客人下单 openid 反查

完整链路:

1. 定制师后台补 mobile (#2886 已上线)
2. 定制师 SMS 登录小程序,user 表存 phone + openid
3. 小程序前端调 GET /mp/customizer/me 识别身份 → 显示「分享给客人」按钮
4. 客人通过分享链接下单,提交 sharerOpenid
5. 后端 sharerOpenid → user.phone → admin_user.mobile → adminId → order.customizer_id

一、错误响应 data 字段规范

密码登录失败 POST /admin/auth/login

场景 code message data
错 1-4 次 200203 密码错误,剩余 N 次尝试 {remainingAttempts: N}
错 5 次直接锁定 200212 账号已锁定,请 30 分钟后重试 {lockedMinutes: 30}
已锁定中重新登录 200212 同上 {lockedMinutes: <剩余分钟>}

⚠️ 改密码路径(PUT /admin/auth/password)旧密码错使用 200213 ADMIN_CHANGE_PASSWORD_OLD_WRONG(避免 200203 模板冲突)

短信验证码登录失败 POST /admin/auth/sms/login

场景 code message data
码错 1-4 次 200102 验证码错误,剩余 N 次尝试 {remainingAttempts: N}
码错 5 次直接锁定 200104 短信登录失败次数过多,请 15 分钟后重试 {lockedMinutes: 15}
验证码过期 / 未发送 200103 验证码已过期,请重新获取 {} (不增 fail count,防攻击者锁住真用户)

🚨 前端需要做的事

1. 登录失败响应解析(账密 + 短信)

文件:hl-ui/src/views/login/...hl-ui/src/api/admin.js

// 拦截器或登录页 onError
if (resp.code === 200203 || resp.code === 200102) {
  // 密码 / 验证码错
  const remaining = resp.data?.remainingAttempts
  showError(`${resp.message}` + (remaining != null ? `(还剩 ${remaining} 次)` : ''))
}
if (resp.code === 200212 || resp.code === 200104) {
  // 账号 / 短信登录已锁定
  const minutes = resp.data?.lockedMinutes
  showError(`${resp.message}` + (minutes != null ? `(剩余 ${minutes} 分钟)` : ''))
  lockLoginButton(minutes * 60 * 1000)
}

2. /admin/auth/info 显示当前管理员手机号

文件:hl-ui/src/views/profile/... 或顶部用户信息组件

const me = await getAdminInfo()  // GET /admin/auth/info
this.mobile = me.data.mobile  // 直接读 (null 时显示「未补录」)

3. 小程序定制师身份识别 + 分享按钮(mp 前端)

文件:mp-cli/src/pages/...mp-cli/src/api/customizer.js

// 登录后调一次
const me = await getCustomizerMe()  // GET /mp/customizer/me
if (me.data.isCustomizer) {
  this.showShareButton = true
  this.customizerInfo = me.data  // adminId, customizerName, customizerAvatar
}

分享链接 URL 必须带 ?sharerOpenid={当前用户 openid}(从当前小程序登录态拿)。

客人点开分享链接下单时,把 sharerOpenid 透传到下单请求 body (字段名沿用现有 MpCreateOrderRequest.sharerOpenid,已具备)。

4. 自查

grep -rE "remainingAttempts|lockedMinutes|/mp/customizer/me|sharerOpenid|admin.*mobile" hl-ui/src mp-cli/src

二、新增接口清单

GET /mp/customizer/me — 当前用户的定制师身份

JWT 鉴权,无 token → 401。

响应:

{
  "code": 200,
  "data": {
    "isCustomizer": true,
    "adminId": 9999999998,
    "customizerName": "张三",
    "customizerAvatar": "https://..."
  }
}

isCustomizer: false 时其他字段为 null。

内部 Feign(后端服务间用,前端不调)

  • GET /internal/user/admin/by-phone?phone= 返 admin 信息或 null
  • GET /internal/mp/user/by-openid?openid= 返 user.phone 或 null

三、客人下单链路改造(对前端透明)

POST /mp/order/create 流程改造,前端无 API 字段变化:

优先级:
1. request.customizerId 显式传 (现有 ?adminId=xxx URL 透传路径)
2. customizerId 为空 + sharerOpenid 非空 → 后端 openid → phone → adminId 反查
3. 都没命中 → 沿用现有 assignRandomCustomizer 兜底

反查失败降级不抛异常,客人下单不被后台问题阻塞。


四、新错误码(本期新增 4 个)

code 含义 data
200212 账号已锁定(密码登录) {lockedMinutes: 30}
200213 旧密码错(改密码场景,与登录密码错码分离) {}
200102 message 更新 验证码错误,剩余 N 次尝试 {remainingAttempts: N}
200203 message 更新 密码错误,剩余 N 次尝试 {remainingAttempts: N}

五、测试服验证记录

部署到测试服 (api.test.1814.love:9443) 后 round-trip 验证 (2026-05-22 18:07):

# 场景 实测
T1 admin 登录 + /admin/auth/info 返回 mobile mobile=13800138000
T2 密码错 1-3 次 remainingAttempts 累加 4/3/2
T3 密码错 5 次直接锁定 200212 + lockedMinutes=30
T4 SMS 错码 remainingAttempts 累加 4/3/2
T5 /mp/customizer/me 端点路由 (401 鉴权要求, 路由正常)

⚠️ test_admin_lock 测试用户因没绑企微 login 时 token=null(requireWechatBind=true),改用内置 admin(已绑企微)测 info 端点通过。


六、不在本期范围

  • dev-v3 二期同步 (另起工单)
  • 客人维度长期绑定定制师 (另起工单)
  • admin_user 表 schema 改动 (字段都已具备)
  • 跨 appId openid 隔离 (当前单小程序)

七、联系人

后端: wx(呼籁旅行) 前端: mmg

如有疑问可在 #2907 评论区留言。