# 用户端小程序 我的邀请 开发设计文档 - 模块: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`(成功 `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,...`),前端直接作为 `` | - **缓存**:以邀请码为 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 结构 - **邀请码**:居中白色卡片,内为小程序码 ``(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`)。