用户端小程序 我的邀请 开发设计文档

  • 模块:ruoyi-user(会员/邀请业务)+ ruoyi-auth(微信小程序码)+ hugray-user-miniapp(前端)
  • 日期:2026-10-13
  • 需求来源:"点击个人中心-我的邀请,进入我的邀请页面,需要根据pen源文件的设计风格设计ui然后实现页面和后端的业务功能"
  • 原型:3 张 UI 稿(邀请码 / 我邀请的 / 邀请我的)
  • 关联文档:doc/design/用户端_个人中心_20261013.md(入口所在页)、doc/design/用户端_登录注册_20261012.md(注册链路、扫码回填)
  • 数据权威源:192.168.110.111:3306/hugray-account(实际表结构优先于本文档)

一、功能概述

个人中心「我的邀请码」行当前点击仅弹占位 toast,本次改为**跳转到「我的邀请」页**(pages/profile/invite.vue):

  1. 新增 C 端页面「我的邀请」,顶部为**三段式 Tab**(邀请码 / 我邀请的 / 邀请我的),自定义导航栏(返回箭头 + 居中标题「我的邀请」);
  2. 邀请码 Tab:展示本人邀请码(account_user.my_referral_code)与**真实微信小程序码**,底部绿色「分享邀请码」按钮;
  3. 我邀请的 Tab:按**绑定时间倒序**列出直接下级(头像、昵称、等级徽标、脱敏手机号、绑定日、合计消费);
  4. 邀请我的 Tab:展示本人**上级**(推荐人)信息(大头像、昵称、等级徽标、脱敏手机号、绑定日);无上级时展示空态;
  5. 扫码链路:小程序码 scene = 邀请码,扫码直达注册页并**自动填入邀请码**(补 register.vue 的 onLoad scene 解析);「分享邀请码」分享注册页链接(?referralCode=xxx),同样自动填入。

无订单模块,故「合计消费」直接用预留列 buy_amount_total(决策见 2.2);
等级徽标直接展示 system_level 原值,不扩展 MemberLevelEnum(决策见 2.3)。


二、现状与关键约束

2.1 数据模型已预留,无需改表

account_user 已具备本期所需列,**无需新增 DDL**:

列 类型 用途
referrer_id bigint 上级(推荐人)会员ID,自连接 account_user.user_id;已有索引 idx_account_user_referrer
referrer_bind_time datetime 绑定时间,列表/详情「绑定日」来源
my_referral_code varchar(32) 本人邀请码,唯一索引 uk_account_user_referral_code
system_level char(1) 等级原值(C / L1~L4),等级徽标来源
buy_amount_total decimal(16,2) 购买金额合计(单位:元,现恒为 0,待订单模块回填)

2.2 「合计消费」复用预留列 buy_amount_total(已确认)

无订单表,暂用 account_user.buy_amount_total(单位:元,与列定义一致,前端加 ¥ 前缀展示)。当前恒为 0,待订单模块上线后回填,**前端无需改动**。

2.3 等级徽标直接展示 system_level 原值(已确认)

MemberLevelEnum 仅到 L4,原型出现 L6(设计稿示意)。本期**不做 normalize**,将 system_level 原样下发(C / L1~L4 / 其它历史值),避免把非法值改写。前端空值兜底为 C。

2.4 小程序码由 ruoyi-auth 生成(已确认「真实小程序码」)

wechat.miniapp 配置与 WeChatMiniappService(access_token 获取、缓存)**均在 ruoyi-auth**;ruoyi-user 无 wechat 配置、无 ruoyi-api-system 依赖。为避免重复实现 access_token 逻辑(DRY),**小程序码生成放在 ruoyi-auth**,新增 C 端接口,入参邀请码、返回 base64 图片。

2.5 扫码进入注册页需补 scene 解析(关键缺口)

pages/login/register.vue 当前无 onLoad(仅 onUnload),form.referralCode 仅支持手动输入。小程序码扫码后微信会把 scene 放入页面 options,需补 onLoad(options) 解码回填,否则「扫码自动填邀请码」不成立。

2.6 邀请码恒定存在,上级可能为空

my_referral_code 在注册时生成(generateUniqueReferralCode),C 端会员必有,故「邀请码」Tab 无须空态;而 referrer_id 可为空(命中通用推荐码注册、或历史数据),故「邀请我的」Tab 必须有空态。

2.7 路径规划

  • 网关注入 user_id;SecurityUtils.getUserId() 取会员身份,**C 端接口不接受前端传 userId**(防水平越权)。
  • 会员侧三条接口挂在既有 AccountProfileController(@RequestMapping("/user"),外部 /miniapp/account/user/**),网关路由已存在,**无需改网关**。
  • 小程序码接口挂 ruoyi-auth 的 AccountAuthController(外部 /miniapp/auth/invite-qrcode),**不在网关白名单**,需登录态。

2.8 手机号脱敏

列表/详情中的手机号一律脱敏下发(复用 AccountUserServiceImpl.maskPhone,形如 136****6666),**不下发明文**。


三、接口设计

统一响应体:C 端 R<T>(成功 code=200)。

3.1 查询本人邀请码(本次新增 · ruoyi-user)

  • 外部路径:GET /miniapp/account/user/invite-code(网关 StripPrefix=2 → 服务内 GET /user/invite-code)
  • 鉴权:需登录;会员身份取自登录态,不接受前端传 userId。
  • 入参:无。
  • 出参 InviteCodeVo:
字段 类型 说明
referralCode String 本人邀请码(account_user.my_referral_code)

3.2 生成微信小程序码(本次新增 · ruoyi-auth)

  • 外部路径:GET /miniapp/auth/invite-qrcode?code={邀请码}(服务内 GET /invite-qrcode)
  • 鉴权:需登录(不在网关白名单)。
  • 入参:
参数 类型 必填 校验 说明
code String 是 @NotBlank + @Pattern(^[A-Z0-9]{8}$) 邀请码,作为小程序码 scene
  • 出参 InviteQrcodeVo:
字段 类型 说明
qrcode String 小程序码 PNG 的 base64 DataURI(data:image/png;base64,...),前端直接作为 <image src>
  • 缓存:以邀请码为 key 缓存生成结果(Redis,TTL 30 天),避免重复消耗微信 getUnlimited 日配额。
  • 错误提示:「邀请码格式不正确」/「邀请码二维码生成失败,请重试」。

3.3 查询我邀请的(本次新增 · ruoyi-user)

  • 外部路径:GET /miniapp/account/user/invitees(→ 服务内 GET /user/invitees)
  • 鉴权:需登录;会员身份取自登录态。
  • 入参:pageNum(默认 1)、pageSize(默认 10,与 TableSupport 默认一致)— 标准分页。
  • 查询口径:account_user 中 referrer_id = 当前用户ID 且 del_flag='0',**按 referrer_bind_time 倒序**(最新在最前;referrer_bind_time 为空的历史行排最后)。
  • 出参:走统一分页 TableDataInfo,即 { code, msg, rows, total }(**数据在顶层 rows,非 data 包裹**;total 为总数)。rows 元素 AccountInviteeVo:
字段 类型 说明
userId Long 下级会员ID
nickname String 昵称(为空前端回退脱敏手机号)
avatar String 头像地址(为空前端回退默认图标)
phone String 脱敏手机号(如 136****6666)
systemLevel String 等级原值(C / L1~L4)
bindDate String 绑定日(yyyy-MM-dd,源自 referrer_bind_time;为空不展示)
buyAmountTotal BigDecimal 合计消费(元,源自 buy_amount_total)

3.4 查询邀请我的(上级信息)(本次新增 · ruoyi-user)

  • 外部路径:GET /miniapp/account/user/referrer(→ 服务内 GET /user/referrer)
  • 鉴权:需登录;会员身份取自登录态。
  • 入参:无。
  • 查询口径:取本人 referrer_id → 查该会员(del_flag='0')。
  • 出参 AccountReferrerVo(无上级时各字段为 null,前端渲染空态):
字段 类型 说明
userId Long 上级会员ID
nickname String 昵称
avatar String 头像地址
phone String 脱敏手机号
systemLevel String 等级原值(C / L1~L4)
bindDate String 绑定日(yyyy-MM-dd)

四、数据库设计

本期无 DDL 变更,仅复用 account_user 既有列(见 2.1)。查询 SQL 遵循规范:

  • 「我邀请的」按 referrer_id 过滤 + referrer_bind_time 排序,命中 idx_account_user_referrer;**不挂 join**(避免干扰 PageHelper 的 count SQL)。
  • 「邀请我的」按 user_id 主键查(复用 selectAccountUserById),不额外建方法。
  • 禁止 SELECT *:新增查询显式列出所需列。

五、微信小程序码设计(ruoyi-auth)

5.1 复用与新增

  • 复用 WeChatMiniappService.getAccessToken(access_token 获取 + Redis 缓存 7000s)。当前为 private,在**同类内新增小程序码方法**即可调用,无需改可见性。
  • 当前 doPost 用 BodyHandlers.ofString() 仅适配 JSON;小程序码返回**二进制图片**,新增一个 doPostForBytes(BodyHandlers.ofByteArray())。

5.2 调用参数(wxacode.getUnlimited)

POST https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=ACCESS_TOKEN
Body: {
  "scene": "{邀请码}",              // 8 位 A-Z0-9,符合 scene 字符集与长度限制
  "page": "pages/login/register",   // 扫码直达注册页
  "check_path": false,              // 跳过页面存在性校验(体验版/开发版可扫码)
  "env_version": "release",         // 正式版;体验/开发调试可改 trial/develop
  "width": 430
}

5.3 响应处理与缓存

  • 微信成功返回 PNG 字节流,失败返回 JSON(含 errcode/errmsg)。先按 Content-Type 判断:非图片 → 解析 JSON 记日志并抛业务异常。
  • 成功后将 PNG 转 base64 DataURI,按 scene 缓存到 Redis(TTL 30 天),命中缓存直接返回。
  • 运维注意:page 需为已发布页面;正式环境扫码前,小程序需先发布含 pages/login/register 的版本。

六、前端页面设计(pages/profile/invite.vue)

6.1 视觉令牌(对齐 琥歌瑞0928.pen)

用途 取值
页面底色 #F8FAFC(pen 根底色)
主色(草绿) #10B981(pen 强调色);按钮渐变 #34D399 → #10B981
等级/状态徽标 实心绿胶囊:底 #10B981、字 #FFFFFF,圆角 6rpx(原型 L1/L6 徽标)
卡片 白底 #FFFFFF,圆角 16rpx,描边 #E2E8F0,阴影 0 6rpx 16rpx rgba(15,23,42,0.06)
主文本 / 次文本 / 弱文本 #0F172A / #64748B / #94A3B8
导航 自定义,返回箭头黑色 #0F172A,标题居中 #0F172A 34rpx 加粗
Tab 高亮 #10B981 + 底部 4rpx 绿色指示条;未选中 #64748B

说明:个人中心页等级胶囊为金色渐变,本页**按原型用绿色胶囊**(更贴合 pen 的 #10B981 / #ECFDF5 状态徽标体系)。

6.2 三 Tab 结构

  • 邀请码:居中白色卡片,内为小程序码 <image>(base64);下方文案「我的邀请码」+ 邀请码原值(可点击复制);底部整宽绿色胶囊按钮「分享邀请码」(open-type="share")。
  • 我邀请的:列表,每行左为头像 + (昵称 + 等级徽标)+ 脱敏手机号;右侧为「合计消费:¥xxx」与「绑定日:xxxx-xx-xx」;按绑定时间倒序;空态「还没有邀请好友」。
  • 邀请我的:居中大号头像 + 昵称 +(等级徽标)+ 脱敏手机号 +「绑定日:xxxx-xx-xx」;无上级时空态「暂无上级信息」。

6.3 分享与扫码

  • 「分享邀请码」:onShareAppMessage 返回 { title, path: '/pages/login/register?referralCode={邀请码}', imageUrl: {小程序码} },分享小程序注册页。
  • 扫码进入:wxacode.getUnlimited 的 scene 落到 pages/login/register 的 options.scene,由 register.vue 的 onLoad 解码回填。

七、前端改动清单

文件 改动
pages/profile/invite.vue 新增:我的邀请页(三 Tab + 分享 + 空态)
pages.json 新增页面注册 pages/profile/invite(navigationStyle: "custom")
pages/profile/index.vue handleMenuTap 中 invite 分支由「敬请期待」改为 uni.navigateTo({ url: '/pages/profile/invite' })
api/index.js 新增 getInviteCode / getInviteQrcode(code) / getInvitees(params) / getReferrer
pages/login/register.vue 新增 onLoad(options):读 options.referralCode(分享链接)与 options.scene(扫码,需 decodeURIComponent)回填 form.referralCode

八、影响范围与测试要点

  • 影响面:新增页面与接口,不改动既有表结构与既有接口出参;个人中心仅改一处菜单跳转。
  • 测试要点:
  1. 邀请码 Tab:邀请码正确展示;小程序码可正常显示;分享卡片可拉起且 path 带推荐码;
  2. 我邀请的:按绑定时间倒序;脱敏手机号;合计消费展示(当前为 0);空态;
  3. 邀请我的:有上级/无上级两种状态;
  4. 扫码链路:扫描本人小程序码 → 进入注册页且推荐码已自动填入;手动输入仍可用;
  5. 边界:referrer_bind_time 为空的历史行排序不报错;未登录访问由网关 401 拦截。

九、待确认项

  1. 小程序码生成落在 ruoyi-auth(复用现成 WeChat 能力),会员侧接口落在 ruoyi-user;如希望统一前缀在 /miniapp/account/**,需在 ruoyi-user 补 wechat 配置与 WeChat 客户端(会引入重复实现),请确认取舍。
  2. 「我邀请的」是否**需要分页**(原型仅 4 条示意);本文默认分页(pageNum/pageSize)。
  3. 小程序码 env_version / page 取值(默认 release + pages/login/register)。