hugray-user-miniapp、hugray-partner-miniapp(两个小程序共用 /miniapp/** 前缀)/miniapp/**,**用户端与服务商端共用同一套前缀**;POST /miniapp/auth/account/register(规避后台注册与图形验证码冲突);/account/** → lb://ruoyi-user 路由;POST /miniapp/auth/phone/code-login。为 C 端用户小程序提供四个接口(均位于 /miniapp/auth/** 命名空间):
| 序号 | 功能 | 说明 |
|---|---|---|
| 1 | 短信验证码下发 | 注册 / 登录 / 重置密码场景下发 6 位验证码,含 30s 频控、有效期、错误次数限制 |
| 2 | 手机号一键登录 | 前端通过微信 <button open-type="getPhoneNumber"> 拿到 code,后端调微信接口换取手机号,命中会员则签发登录令牌 |
| 3 | C 端注册 | 手机号 + 短信验证码 + 推荐码(必填)注册会员,成功后返回注册成功页 |
| 4 | 手机号验证码登录 | 手机号 + 短信验证码换取登录令牌(登录页内嵌面板,不新增页面) |
C 端会员数据落在 ruoyi-user 服务的 account_user 表;认证令牌沿用 RuoYi 既有 TokenService(网关统一校验 Redis 中的 login_tokens:{token})。
/auth/**,与 hugray-admin 混在一起;且服务商端“忘记密码” POST /auth/sms/code 与用户端注册验证码**同 path**,无法区分。/miniapp/****,与后台(/auth、/system、/code、/schedule、/file)彻底隔离。StripPrefix=2,即剥掉 /miniapp 与二级段):| 外部路径(miniapp 前缀) | 目标服务 | 服务内真实路径 |
|---|---|---|
/miniapp/auth/** |
lb://ruoyi-auth |
/sms/code、/phone/login、/account/register、/login、/logout … |
/miniapp/account/** |
lb://ruoyi-user |
/user/info、/partner/** … |
/miniapp/product/** |
lb://ruoyi-product |
/home/content、/product/**、/order/** … |
/miniapp/system/** |
lb://ruoyi-system |
/user/getInfo … |
StripPrefix=2说明:/miniapp/auth/sms/code→ 剥掉miniapp、auth→ 服务收到/sms/code。
/auth/register 已被后台占用ruoyi-auth 现有 POST /auth/register 面向**后台 sys_user 注册**(hugray-admin 在用)。ValidateCodeFilter 的 VALIDATE_URL = { "/auth/login", "/auth/register" },会对该路径**强制校验图形验证码 uuid**。{ phone, code, referralCode }(无 uuid),复用该路径必然失败。结论:C 端注册走 POST /miniapp/auth/account/register(服务内 /account/register)。该路由**不挂 ValidateCodeFilter**,不受图形验证码影响;后台注册 POST /auth/register 完全不受影响。
ruoyi-gateway-dev.yml 的 security.ignore.whites 当前仅含 /auth/logout、/auth/login、/auth/register、/*/v2/api-docs、/*/v3/api-docs、/csrf。新增的 4 个免登录接口(/miniapp/auth/sms/code、/miniapp/auth/phone/login、/miniapp/auth/phone/code-login、/miniapp/auth/account/register)必须补入,否则 AuthFilter 直接返回 401「令牌不能为空」。
ServiceNameConstants 无 USER_SERVICE。ruoyi-user 的 Feign 客户端(现有 RemoteUserService 指向 ruoyi-system)。TokenService.createToken(LoginUser) 强依赖 loginUser.getSysUser().getUserId()/getUserName(),而 LoginUser 只承载 SysUser,C 端会员是 account_user,两者数据源/类型不同。
方案(最小改动,不改动 common-security):用 SysUser 作为**载体**装载会员身份(userId=account_user.user_id、userName=会员用户名、nickName/avatar/phonenumber/status/delFlag 一并带上),再走 createToken。C 端会员详情由 ruoyi-user 读取网关注入的 user_id 头(SecurityUtils.getUserId())返回,**不经过** /system/user/getInfo。
安全提示:C 端令牌与后台令牌共用同一套网关校验,
user_id存在与sys_user数值撞号的可能。后台接口均有@RequiresPermissions(@RequiresRoles/@RequiresPermissions对permissions/roles为空的 C 端令牌一律拒绝),风险可控。本次不做令牌分级,作为已知项记录。
SmsSender 接口 + 默认实现(验证码写 Redis + 日志输出),后续替换为真实短信服务商实现即可。HttpUtils → 调微信接口使用 JDK 自带 java.net.http.HttpClient + fastjson2(ruoyi-common-core 已含,无新增依赖)。account_user 无推荐码、无推荐人字段 → 新增 my_referral_code、referrer_user_id。
AccountUserServiceImpl.insertAccountUser 无条件执行 SecurityUtils.encryptPassword(getPassword()),C 端注册无密码会 NPE → 本次修复(密码为空跳过加密)。
统一响应体:R<T>(code=200 成功 / code=500 失败,msg 为提示文案),与工程既有约定一致;前端 utils/request.js 已按 successCode=200 / unauthorizedCode=401 解包。
规范说明:用户规则中的“
Result<T>、code=0成功、错误码分段”属通用模板,本工程实际框架为 RuoYiR/AjaxResult(200 成功)+@RestControllerAdvice全局异常。按“与现有工程保持一致”优先,采用框架既有约定。
POST /miniapp/auth/sms/code(服务内路径 /sms/code)SmsCodeBody:| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| phone | String | 是 | 手机号,^1[3-9]\d{9}$ |
| scene | String | 否 | register=注册(校验未注册);login=验证码登录(校验已注册);reset/为空=通用下发(不校验注册状态) |
R<?>(data 为空,成功即已发送)请输入手机号 / 请输入正确手机号 / 手机号已注册,请前往登录 / 该手机号未注册,请先注册 / 验证码发送过于频繁,请稍后再试 / 短信发送失败scene 为空或非 register/login 时按“通用下发”处理,兼容服务商端忘记密码场景(/miniapp/auth/sms/code 同一接口,传 scene=reset)。POST /miniapp/auth/phone/login(服务内路径 /phone/login)PhoneLoginBody:| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| code | String | 是 | 微信 getPhoneNumber 回调返回的动态令牌 e.detail.code |
R<Map>,data = { access_token, expires_in }(对齐现有 /auth/login)微信授权凭证不能为空 / 微信手机号获取失败,请重试 / 该手机号未注册,请先注册 / 账号已停用,请联系管理员 / 账号不存在POST /miniapp/auth/account/register(服务内路径 /account/register)AccountRegisterBody:| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| phone | String | 是 | 手机号 |
| code | String | 是 | 短信验证码 |
| referralCode | String | 是 | 推荐码(推荐人本人的 my_referral_code) |
R<?>(注册成功,前台跳转注册成功页)请输入手机号 / 请输入正确手机号 / 请输入验证码 / 请输入推荐人码 / 验证码错误 / 验证码已失效,请重新获取 / 验证码错误次数过多,请重新获取 / 手机号已注册,请前往登录 / 推荐人码无效 / 推荐人账号异常 / 注册失败,请稍后重试POST /miniapp/auth/phone/code-login(服务内路径 /phone/code-login)PhoneCodeLoginBody:| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| phone | String | 是 | 手机号,^1[3-9]\d{9}$ |
| code | String | 是 | 短信验证码(下发时 scene=login) |
R<Map>,data = { access_token, expires_in }(对齐现有 /auth/login 与一键登录)请输入手机号 / 请输入正确手机号 / 请输入验证码 / 该手机号未注册,请先注册 / 账号已停用,请联系管理员 / 账号不存在 / 验证码已失效,请重新获取 / 验证码错误 / 验证码错误次数过多,请重新获取依据:doc/琥歌瑞0928.pen 登录页稿 + hugray-user-miniapp/static/ 图片素材。
换算基准:设计稿 375pt 宽 → 750rpx(pt × 2 = rpx)。
| 元素 | 设计稿规格 | 实现值 |
|---|---|---|
| 页面底色 | 渐变 165°、opacity 0.8:#D3E4F8 0% → #BCD6F4 50% → #ABCCEC 100% |
同左(rgba .8 叠白底) |
| 微透十字 ×3 | 右上 80×80(条宽 22 R11 #FFFFFF35)/ 中左 68×68(条宽 18 R9 #FFFFFF30)/ 左下 100×100(条宽 28 R14 #FFFFFF35) |
160 / 136 / 200rpx,条宽 44 / 36 / 56rpx |
| 白色点阵 ×3 | 左上 x10/y200、右上 x310/y130、右下 x240/y752;微点 3×3 #FFFFFF55,gap 10 |
radial-gradient 平铺,步距 26rpx(设计稿为不规则形状,此处网格近似) |
| 欢迎来到 | 26pt 500 #0F172A,居中 |
52rpx / 500 / 居中 |
| 官方 Logo | 220×76pt(metabolism-300-logo.png) |
440×152rpx |
| 英译标语 | 11pt 600 #003E7E 字距 1.5,居中 |
22rpx / 600 / 3rpx |
| 医学双背书 | 加号 15×15(medical-plus-icon.png)+ 勾选 18.2×15(check-icon.png),项内 gap 6、两项 gap 24,文案 12pt 600 #1E293B |
30rpx / 36.4×30rpx;gap 12rpx / 48rpx,文案 24rpx |
| 核心标语卡 | industry-4-0-slogan-card.png 整图,卡片视觉宽 319pt |
图 759×304rpx,上下负边距 -48rpx / -72rpx 抵消透明留白 |
| 微信一键登录 | H48 R24,渐变 90° #07C160→#05A352,shadow #07C16040 y6 blur16,图标 18×18 + 文案 15pt 600,gap 8 |
96rpx / 48rpx;图标 36rpx,文案 30rpx,gap 16rpx;通栏(width:100%; margin:0 覆盖小程序 button 默认 margin:0 auto 导致的收缩) |
| 手机验证登录 | H48 R24,渐变 90° #1E5EBC→#144A94,shadow #1E5EBC30 |
同上(phone-icon.png);展开验证码面板后**隐藏本按钮**,面板内提供「收起」入口 |
| 协议勾选行 | 复选框 16×16 #003E7E R4 + 文案 11.5pt(正文 #64748B / 链接 #1E5EBC),与按钮组间距 19pt |
32rpx;间距 38rpx;字号按用户反馈由 11.5pt(23rpx) 上调至 26rpx(与原稿存在偏差,属可读性优化) |
| 底部 Home 条 | 135×5 R2.5 #0F172A25 |
270×10rpx / R5rpx |
约定:设计稿无「还没有账号?立即注册」入口,按业务需要保留在协议行下方(样式弱化)。
依据:设计稿 doc/琥歌瑞0928.pen 不含注册页,故注册页以登录页已落地的设计体系为实现规范(同一渐变底板、装饰层、控件规格与字号体系)。
| 元素 | 规格(实现值) |
|---|---|
| 页面底板 | 与登录页同款:渐变 165°、rgba .8 叠白底(#D3E4F8 → #BCD6F4 → #ABCCEC) |
| 背景装饰 | 复用登录页 3 处微透十字(tr/ml/bl)+ 3 处白色点阵(lt/rt/rb) |
| 顶部导航 | 透明底融合渐变,居中标题「注册」34rpx 600 #003E7E;左侧返回键 88rpx 热区 + CSS 双边框箭头 |
| 主标题 | 「创建账号」44rpx 600 #0F172A;副标题「完善以下信息,开启健康之旅」26rpx #64748B |
| 表单项 | 标签 26rpx 500 #334155,项间距 40rpx;输入框 R16 / H88,浅底 #F8FAFC + 描边 #CBD5E1,内嵌 44rpx 品牌图标 |
| 获取验证码 | 208×88rpx / R16 白底 + #1E5EBC 描边;倒计时期间置灰不可点 |
| 提交按钮 | H96 / R48,渐变 90° #1E5EBC → #003E7E,30rpx 600,shadow 0 12rpx 28rpx rgba(0,62,126,.2) |
| 返回登录入口 | 按钮下方居中弱化「已有账号?去登录」26rpx |
| 底部 Home 条 | 270×10rpx / R5 rgba(15,23,42,.145) |
| 边距体系 | 表单区与底部区统一左右 56rpx,底部叠加安全区 32rpx |
说明:注册页**无协议勾选行**(注册无需阅读协议),底部区为「注册按钮 + 已有账号?去登录 + Home 条」,与登录页视觉基调一致但裁剪了协议模块。
依据:设计稿 doc/琥歌瑞0928.pen 不含该页,故与注册页同源,沿用登录页已落地的设计体系(渐变底板 + 装饰层 + 控件规格)。
| 元素 | 规格(实现值) |
|---|---|
| 页面底板 | 与登录/注册页同款:渐变 165°、rgba .8 叠白底(#D3E4F8 → #BCD6F4 → #ABCCEC) |
| 背景装饰 | 复用登录页 3 处微透十字(tr/ml/bl)+ 3 处白色点阵(lt/rt/rb) |
| 结果区 | flex:1 占满剩余空间并整体居中;左右 56rpx |
| 成功图标 | 160rpx 圆形,渐变 135° #34D399 → #10B981,shadow 0 12rpx 32rpx rgba(16,185,129,.25);白色对勾由两条 8rpx 边框旋转 45° 拼成 |
| 结果主标题 | 「注册成功」40rpx 600 #0F172A,距图标 44rpx |
| 结果说明 | 「您的账号已成功注册,返回登录开启健康之旅吧」26rpx / 行高 40rpx / 居中 #64748B |
| 底部按钮 | 「返回登录」H96 / R48,渐变 90° #1E5EBC → #003E7E,30rpx 600,shadow 同注册页;点击 uni.reLaunch 回登录页 |
| 底部 Home 条 | 270×10rpx / R5 rgba(15,23,42,.145) |
| 边距体系 | 底部区左右 56rpx,叠加安全区 32rpx |
库:hugray-account,表:account_user(**ALTER,非重建**)。
ALTER TABLE `account_user`
ADD COLUMN `my_referral_code` varchar(20) DEFAULT NULL COMMENT '本人推荐码(注册时生成,全局唯一)' AFTER `remark`,
ADD COLUMN `referrer_user_id` bigint(20) DEFAULT NULL COMMENT '推荐人会员ID(关联 account_user.user_id)' AFTER `my_referral_code`;
-- 推荐码全局唯一:随机生成且不重复使用,逻辑删除行占用不影响业务
CREATE UNIQUE INDEX `uk_account_user_referral_code` ON `account_user` (`my_referral_code`);
回滚:
DROP INDEX `uk_account_user_referral_code` ON `account_user`;
ALTER TABLE `account_user` DROP COLUMN `referrer_user_id`, DROP COLUMN `my_referral_code`;
说明:
- 不新增 phone 唯一索引——逻辑删除(del_flag='2')残留行会占用手机号,与“仅校验 del_flag='0'”冲突(与表注释既定结论一致);并发同号注册由 Redis 分布式锁 兜底(见 5.3)。
- 归档脚本:sql/hugray_account_20261012.sql(与代码同一次提交,含变更 + 回滚)。
STEP 1: 非空 + 正则校验手机号
STEP 2: scene == register 时,Feign 查会员,命中即抛「手机号已注册,请前往登录」;
scene == login 时,Feign 查会员,未命中即抛「该手机号未注册,请先注册」
STEP 3: 频控:Redis key sms_code_limits:{scene}:{phone} 存在即抛「发送过于频繁」(TTL 30s,setIfAbsent)
STEP 4: 生成 6 位随机码;写 sms_codes:{scene}:{phone}(TTL 5 分钟),同时清零错误次数计数
STEP 5: 调用 SmsSender.send(phone, code) 下发
STEP 1: code 非空校验
STEP 2: WeChatMiniappService 获取 access_token(Redis 缓存,TTL 7000s,过期自动刷新)
STEP 3: 调微信 phonenumber.getPhoneNumber 换手机号,失败/异常抛「微信手机号获取失败,请重试」
STEP 4: Feign 按手机号查会员;不存在抛「该手机号未注册,请先注册」
STEP 5: 校验会员状态:status=1 停用 → 抛错;delFlag=2 → 抛「账号不存在」
STEP 6: 组装 SysUser 载体 + LoginUser,tokenService.createToken 返回 { access_token, expires_in }
STEP 1: 手机号非空 + 正则校验;推荐码非空校验
STEP 2: 校验并消费短信验证码(见 5.4),失败按错误提示抛出
STEP 3: 获取 Redis 分布式锁 account_register_locks:{phone}(setIfAbsent,TTL 10s),未获取到抛「操作过于频繁,请稍后重试」
STEP 4: 锁内二次校验手机号是否已注册(防并发 / 防重复提交),命中抛「手机号已注册,请前往登录」
STEP 5: Feign 调 ruoyi-user 注册:
5.1 按推荐码查推荐人,不存在抛「推荐人码无效」;推荐人 status=1 抛「推荐人账号异常」
5.2 生成唯一 my_referral_code(随机 8 位大写字母数字,库内查重,冲突重试 ≤5 次)
5.3 组装 AccountUser:userName=phone、phone、nickname=phone、myReferralCode、referrerUserId、status=0、delFlag=0
5.4 插入(无密码,password 为空)
STEP 6: 删除验证码 key;finally 释放锁
STEP 1: 从 sms_codes:{scene}:{phone} 读取;不存在抛「验证码已失效,请重新获取」
STEP 2: 比对失败 → 错误次数 +1(TTL 与验证码一致);达到上限(5 次)删除验证码并抛「验证码错误次数过多,请重新获取」
STEP 3: 比对成功 → 删除验证码 key(一次性使用)
STEP 1: Redis 取 wechat_access_token:{appid};无则请求 /cgi-bin/token 获取并缓存
STEP 2: POST /wxa/business/getuserphonenumber?access_token=xxx body={"code": code}
STEP 3: 解析:errcode!=0 或 phone_info.phoneNumber 为空 → 抛业务异常
STEP 1: 手机号非空 + 正则校验(兜底),非法抛「请输入正确手机号」
STEP 2: Feign 按手机号查会员;不存在抛「该手机号未注册,请先注册」;delFlag=2 抛「账号不存在」
STEP 3: 校验会员状态:status=1 停用 → 抛「账号已停用,请联系管理员」
STEP 4: 校验并消费短信验证码(scene=login,一次性),失败按错误提示抛出
STEP 5: 组装 SysUser 载体 + LoginUser,tokenService.createToken 返回 { access_token, expires_in }
顺序说明:**先校验会员存在与状态,再消费验证码**(STEP 2/3 在 STEP 4 之前),避免未注册/停用手机号白白消耗验证码;该顺序已由单测锁定(未注册/停用场景断言
verifyAndConsume从未被调用)。
仅用于联调测试,**通过 Nacos 配置开启,配置为空即完全关闭**(生产不配置即为关闭态)。
通用验证码(ruoyi-auth)
SmsCodeService.verifyAndConsume 入口先判定:
命中「配置的通用验证码」且场景为 login / register → 直接放行(不读缓存、不计数、不消费)
否则走原有「读缓存 → 比对 → 一次性消费」逻辑
通用推荐码(ruoyi-user)
AccountUserServiceImpl.registerMember 定位推荐人前先判定:
命中「配置的通用推荐码」→ 跳过推荐人查询与校验,referrerUserId 留空(不绑定推荐人)
否则走原有「按码查推荐人 → 不存在/停用则拒绝」逻辑
命中通用码时打印 WARN 日志留痕(手机号脱敏)。
| 服务 | 配置项 | 示例值 | 说明 |
|---|---|---|---|
| ruoyi-auth | hugray.auth.universal-sms-code |
999999 |
空 = 关闭;仅 login / register 场景生效 |
| ruoyi-user | hugray.user.universal-referral-code |
HUGRAY88 |
空 = 关闭;命中则不绑定推荐人 |
| 名称 | 取值 | 说明 |
|---|---|---|
| 验证码场景 | register / login / reset |
SmsSceneEnum:注册校验未注册、登录校验已注册、reset/为空为通用下发;含 getByCode |
| 会员状态 status | 0 正常 / 1 停用 |
复用现有语义 |
| 删除标志 delFlag | 0 存在 / 2 删除 |
复用现有语义 |
| 推荐码生成 | 8 位 [A-Z0-9] 随机 |
常量:长度、重试次数 |
新增常量(收口):
- ServiceNameConstants.USER_SERVICE = "ruoyi-user"
- CacheConstants:SMS_CODE_KEY、SMS_CODE_LIMIT_KEY、SMS_CODE_ERR_KEY、ACCOUNT_REGISTER_LOCK_KEY、WECHAT_ACCESS_TOKEN_KEY
- SmsConstants(ruoyi-auth):CODE_LENGTH=6、CODE_EXPIRE_MINUTES=5、SEND_INTERVAL_SECONDS=30、MAX_VERIFY_ATTEMPTS=5
- ReferralConstants(ruoyi-user):CODE_LENGTH=8、GEN_MAX_RETRY=5
| 模块 | 文件 | 说明 |
|---|---|---|
| ruoyi-common-core | (改)ServiceNameConstants、CacheConstants |
新增常量 |
| ruoyi-api-system | domain/AccountMember.java |
C 端会员跨服务契约 DTO |
| ruoyi-api-system | model/AccountRegisterRequest.java |
注册入参 DTO(phone、referrerReferralCode) |
| ruoyi-api-system | RemoteAccountUserService.java |
指向 ruoyi-user 的 Feign |
| ruoyi-api-system | factory/RemoteAccountUserFallbackFactory.java |
降级工厂 |
| ruoyi-user | controller/AccountMemberInnerController.java |
内部接口(@InnerAuth):按手机号查会员、注册会员 |
| ruoyi-user | service/impl/(改)AccountUserServiceImpl |
新增会员查询/注册业务 |
| ruoyi-auth | form/SmsCodeBody.java、form/PhoneLoginBody.java、form/PhoneCodeLoginBody.java、form/AccountRegisterBody.java |
入参对象(含 @NotBlank/@Pattern) |
| ruoyi-auth | controller/AccountAuthController.java |
/sms/code、/phone/login、/phone/code-login、/account/register |
| ruoyi-auth | service/AccountLoginService.java |
登录/注册编排 |
| ruoyi-auth | service/SmsCodeService.java + service/sms/{SmsSender, PlaceholderSmsSender} |
验证码 + 短信抽象 |
| ruoyi-auth | service/WeChatMiniappService.java + config/WeChatMiniappProperties.java |
微信手机号 |
| ruoyi-auth | enums/SmsSceneEnum.java、constant/SmsConstants.java |
枚举/常量 |
| 文件 | 改动 |
|---|---|
ruoyi-user domain/AccountUser.java |
新增 myReferralCode、referrerUserId 字段与注释、toString |
ruoyi-user mapper/AccountUserMapper.java + resources/mapper/user/AccountUserMapper.xml |
resultMap/列/insert/update 增列;新增 selectAccountUserByReferralCode |
ruoyi-user service/impl/AccountUserServiceImpl.insertAccountUser |
密码为空时跳过加密(当前无条件 encryptPassword,C 端无密码会 NPE) |
sql/hugray_account_20261012.sql |
新增 DDL 脚本 |
nacos ruoyi-gateway-dev.yml |
新增 /miniapp/** 路由 + /account/** 路由;白名单增 4 条 |
nacos ruoyi-auth-dev.yml |
新增 wechat.miniapp.* |
前端 hugray-user-miniapp/api/index.js |
全部业务路径加 /miniapp 前缀;register 改 /miniapp/auth/account/register |
前端 hugray-partner-miniapp/api/index.js |
同上(共用一套前缀) |
ruoyi-gateway-dev.yml路由新增(spring.cloud.gateway.server.webflux.routes):
# 小程序 - 认证(用户端 + 服务商端共用)
- id: ruoyi-miniapp-auth
uri: lb://ruoyi-auth
predicates:
- Path=/miniapp/auth/**
filters:
- StripPrefix=2
# 小程序 - 会员
- id: ruoyi-miniapp-user
uri: lb://ruoyi-user
predicates:
- Path=/miniapp/account/**
filters:
- StripPrefix=2
# 小程序 - 商品/订单
- id: ruoyi-miniapp-product
uri: lb://ruoyi-product
predicates:
- Path=/miniapp/product/**
filters:
- StripPrefix=2
# 小程序 - 系统信息
- id: ruoyi-miniapp-system
uri: lb://ruoyi-system
predicates:
- Path=/miniapp/system/**
filters:
- StripPrefix=2
# 会员服务(补齐,兼容前端 /account 前缀注释)
- id: ruoyi-user
uri: lb://ruoyi-user
predicates:
- Path=/account/**
filters:
- StripPrefix=1
白名单新增:
security:
ignore:
whites:
- /auth/logout
- /auth/login
- /auth/register
- /miniapp/auth/sms/code # 新增
- /miniapp/auth/phone/login # 新增
- /miniapp/auth/phone/code-login # 新增
- /miniapp/auth/account/register # 新增
- /*/v2/api-docs
- /*/v3/api-docs
- /csrf
/miniapp/auth/**路由**不挂ValidateCodeFilter**,因此phone/login、account/register不要求图形验证码 uuid。
ruoyi-auth-dev.yml# 微信小程序(appid 已知 wx895deca4258a4ecf,secret 需填入真实值)
wechat:
miniapp:
appid: wx895deca4258a4ecf
secret: <待填入,脱敏归档>
# 服务商端(hugray-partner-miniapp,appid wx9fff7ff7a44e155a):预留配置,代码暂不消费
partner-miniapp:
appid: wx9fff7ff7a44e155a
secret: <待填入,脱敏归档>
归档:按规范同步归档
doc/nacos/dev/(脱敏),真实值仅存于 nacos。
说明:服务商端目前不走微信能力(无手机号一键登录 /wx.login),wechat.partner-miniapp.*仅作为**预留配置**写入,待后续微信能力接入时启用(暂无对应@ConfigurationProperties与消费方)。
说明:当前仓库 seed 脚本sql/ry_config_20260918.sql的 nacos 租户为public,而服务bootstrap.yml使用命名空间c75c831e-0570-4a9f-b8de-aba9087a5f37;实际生效配置请在对应的 nacos 命名空间中修改。
ruoyi-user-dev.yml# 测试便利:通用推荐码(空 = 关闭;命中则不绑定推荐人)
hugray:
user:
universal-referral-code: HUGRAY88
归档:
doc/nacos/dev/ruoyi-user-dev.yml(脱敏);与 8.2 的hugray.auth.universal-sms-code配套,见 5.7 节。
| # | 场景 | 处理 |
|---|---|---|
| 1 | 手机号为空 / 格式错误 | 参数校验 + 服务兜底,提示「请输入手机号」/「请输入正确手机号」 |
| 2 | scene=register 且手机号已注册 |
「手机号已注册,请前往登录」 |
| 3 | 30s 内重复请求验证码 | 「验证码发送过于频繁,请稍后再试」 |
| 4 | 验证码错误 / 过期 / 超次数 | 分别提示,超次数后删除验证码 |
| 5 | 验证码一次性消费 | 校验成功后立即删除 |
| 6 | 推荐码为空 / 不存在 / 推荐人停用 | 「请输入推荐人码」/「推荐人码无效」/「推荐人账号异常」 |
| 7 | 推荐码为本人推荐码 | 注册前用户不存在,天然不可自荐 |
| 8 | 并发 / 重复提交同手机号注册 | Redis 分布式锁 + 锁内二次唯一性校验 |
| 9 | 逻辑删除用户以同手机号重新注册 | del_flag='0' 判定为未注册,允许注册 |
| 10 | 一键登录:手机号未注册 | 「该手机号未注册,请先注册」 |
| 11 | 一键登录:账号停用 / 删除 | 「账号已停用,请联系管理员」/「账号不存在」 |
| 12 | 微信接口报错 / 网络异常 | 「微信手机号获取失败,请重试」 |
| 13 | 微信 access_token 过期 | Redis 缓存 + 失效自动重新获取 |
| 14 | 无密码注册(C 端无密码) | insertAccountUser 密码为空跳过加密 |
| 15 | 推荐码生成冲突 | 查重重试 ≤5 次,仍失败抛「注册失败,请稍后重试」 |
| 16 | 参数校验失败 | @Validated → 全局 MethodArgumentNotValidException 处理,返回首条 message |
| 17 | 内部接口越权访问 | @InnerAuth 校验 from-source=inner |
| 18 | 敏感信息 | 日志不打印完整手机号(脱敏)、不打印微信 secret |
| 19 | 前缀迁移影响后台 | /auth/**、/system/**、/account/** 旧路由全部保留,后台 hugray-admin 不受影响 |
| 20 | 验证码登录:手机号未注册 | 下发(scene=login)即拦「该手机号未注册,请先注册」;登录接口再兜底,均**不消费验证码** |
| 21 | 验证码登录:账号停用 / 删除 | 「账号已停用,请联系管理员」/「账号不存在」,校验在消费验证码之前 |
| 22 | 通用验证码命中(login / register 且 hugray.auth.universal-sms-code 已配置) |
直接放行:不读缓存、不计数、不消费;reset / common 场景不生效;配置为空即关闭 |
| 23 | 通用推荐码命中(hugray.user.universal-referral-code 已配置且有值) |
跳过推荐人查询与校验,referrerUserId 留空(不绑定推荐人);命中通用码时不再校验「推荐人码无效 / 推荐人账号异常」 |
queryAccountMemberByPhone / registerAccountMember / sendSmsCode 等见名知意;实体 / DTO / 入参对象分离。// STEP x: 注释。ServiceException + 全局处理器,不散落 try-catch 拼返回值。@Slf4j 占位符 + [业务标记] 前缀,手机号脱敏,禁止 System.out.println。setnx 锁;创建类接口具备幂等守卫。${} 拼接;无 SELECT *。SmsCodeService、AccountLoginService、推荐码生成等核心逻辑补充单测(Mockito Mock Feign/Redis/HTTP)。/miniapp/**,**用户端与服务商端小程序共用**。✔POST /miniapp/auth/account/register。✔wechat.miniapp.secret,仓库不落明文。✔PlaceholderSmsSender 仅打印日志(形如 [SMS] phone=138****0000 code=123456),不真实下发。✔/account/** → lb://ruoyi-user。✔POST /miniapp/auth/phone/code-login 与 SmsSceneEnum.LOGIN 同步落地。✔.pen 设计稿 + static/ 素材做视觉 1:1 还原(背景装饰、Logo、背书图标、标语卡、按钮尺寸与渐变、协议行、Home 条),规格见 3.5 节。✔999999(hugray.auth.universal-sms-code,仅 login / register 生效)+ 通用推荐码 HUGRAY88(hugray.user.universal-referral-code,命中不绑定推荐人);均通过 nacos 配置开启,配置为空即关闭,命中打印 WARN 留痕。✔编码顺序:常量 → DDL → Feign/内部接口 → auth 服务与控制器 → 网关配置 → 前端前缀对齐 →
GetDiagnostics校验 → 单测。