# 孩子邀请功能设计方案 **日期:** 2026-05-26 **状态:** 待审批 ## 问题 家长手动创建的孩子不能登录小程序,只能通过家长端切换角色来操作。但有些场景下孩子需要自主登录(如独立完成任务、查看积分)。需要: 1. 家长可以发起邀请卡片分享,被邀请者通过微信卡片链接加入,直接以孩子身份登录 2. 手动创建的孩子不能登录(因为没有关联 User 账号),只能家长切换使用 3. 如果手动创建的孩子填了手机号,被邀请者用同一手机号注册时,应匹配到该孩子记录,不重复创建 ## 现有系统分析 ### 现有 InviteCard 表结构 ```sql invite_card ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type VARCHAR(20) -- 'family' | 'guide' | 'child' ref_id BIGINT -- childId / familyId code VARCHAR(10) -- 6位随机码 creator_id BIGINT -- 创建人 expires_at DATETIME -- 24h过期 used TINYINT(1) -- 是否已使用 ... ) ``` ### 现有相关接口 | 接口 | 说明 | |------|------| | `POST /api/invite-card/generate` | 生成邀请码,已有 type/ref_id 参数 | | `POST /api/invite-card/verify` | 验证邀请码,返回 type/refId/creatorPhone/childInfo | | `POST /api/invite-card/accept` | 接受邀请,已有 child 类型处理逻辑 | | `POST /api/auth/phone-login` | 手机验证码登录,返回 token + 用户信息 | ### 数据关系 - `Child.userId` — 如果为 null,表示该孩子是手动创建,无关联 User 账号 - `Child.userId` — 如果非 null,表示关联到 User 表,孩子可以自主登录 - `Child.phone` — 可选,手动创建时可填写,用于后续邀请匹配 - `Child.familyId` — 所属家庭 ## 方案 ### 流程概览 ``` 家长端孩子管理页 ├── 「添加孩子」按钮 → 手动创建 Child(无 User 关联) └── 「邀请孩子」卡片入口 → 生成邀请卡 → 微信分享 ↓ 被邀请者打开小程序卡片 ↓ login.vue 检测 invite_code ↓ 手机号 + 验证码 登录 ↓ 登录成功后,检测 invite_type=child ↓ 调 /api/invite-card/accept 手机号匹配 Child.phone ↓ ┌─ 匹配到 → 绑定 Child 到当前 User └─ 未匹配 → 新建 Child + 绑定 User ↓ 设置 currentRole = child 跳转到 child-index ``` ### 核心规则 1. **手动创建的孩子不能登录** — `Child.userId = null`,无 User 关联,JWT 登录拦截器会拦截 2. **匹配逻辑** — `accept` 时用手机号查该家庭下的 Child:`SELECT * FROM child WHERE family_id = ? AND phone = ?` 3. **已绑定 User 的孩子不允许重复邀请** — 生成邀请码时校验 `child.userId != null` 则提示「该孩子已绑定账号」 4. **被邀请者注册** — 通过手机验证码登录后,后端自动完成孩子绑定,返回 `role = child` 的 JWT ### 后端改动 #### 1. InviteCardService.acceptChild() — 增强匹配逻辑 当前逻辑: ```java // 现有:仅通过 refId 查找 Child,然后匹配手机号 Child child = childService.getById(refId); if (!child.getPhone().equals(phone)) throw error; ``` 改为: ```java // 增强:先尝试通过 phone + familyId 匹配已存在的 Child Child existingChild = childService.lambdaQuery() .eq(Child::getPhone, phone) .eq(Child::getFamilyId, familyId) .one(); if (existingChild != null) { // 匹配到手动创建的孩子 → 绑定到当前 User existingChild.setUserId(currentUserId); childService.updateById(existingChild); return existingChild; } // 未匹配 → 新建 Child(用 User 信息创建)→ 绑定 ``` #### 2. InviteCardService.generate() — 生成前校验 生成 child 类型邀请卡时: - 校验该孩子属于当前用户所属家庭 - 校验 `child.userId == null`(未绑定账号的孩子才可邀请) - 失效旧的未使用邀请码 #### 3. AuthController / login flow — 登录后自动接受邀请 在 `phoneLogin()` 成功后,检测 session 中是否有 `invite_code` + `type=child`: - 有 → 自动调用 accept → 返回 JWT 的 role=child - 无 → 正常返回(role = parent) ### 前端改动 #### 1. children.vue — 邀请孩子入口 重构孩子管理页顶部按钮区域: ``` ┌─────────────────────────────┐ │ ┌──────────┐ ┌──────────┐ │ │ │ + 添加孩子 │ │ 📤 邀请孩子 │ │ │ └──────────┘ └──────────┘ │ └─────────────────────────────┘ ``` 「邀请孩子」按钮: ``` 1. 点击 → 调 /api/invite-card/generate { type: "child", refId: childId } 2. 成功 → 存储 inviteCode 到本地 3. 调用 uni.shareAppMessage() 弹出微信分享菜单 4. 分享卡片 path = /pages/login/login?invite_code=xxx&invite_type=child ``` #### 2. login.vue — 处理孩子邀请参数 ```javascript onLoad(options) { if (options.invite_code) { uni.setStorageSync('inviteCode', options.invite_code); uni.setStorageSync('inviteType', options.invite_type || 'family'); } } ``` 登录成功后: ```javascript const inviteType = uni.getStorageSync('inviteType'); if (inviteType === 'child') { const inviteCode = uni.getStorageSync('inviteCode'); // 调 accept 接口完成孩子绑定 await acceptInvite({ code: inviteCode, phone: this.phone }); // 返回的 role 应为 child uni.setStorageSync('currentRole', 'child'); uni.setStorageSync('isSwitchedChild', false); uni.reLaunch({ url: '/pages/index/index' }); } ``` #### 3. api.js — 增加邀请相关 API ```javascript export const generateInviteCard = (data) => request('/api/invite-card/generate', data); export const acceptInvite = (data) => request('/api/invite-card/accept', data); export const verifyInviteCode = (data) => request('/api/invite-card/verify', data); ``` ### 数据库变更 无。复用现有 `invite_card` 表 + `child` 表。仅需确保 `child.phone` 字段可用于手机号匹配。 ## 约束 - 每个孩子同时只能有一个未使用的邀请码 - 已绑定 User 的孩子不能再次生成邀请码 - 手动创建的孩子填了手机号 → 被邀请者用同手机号注册 → 匹配并绑定,不重复创建 - 手动创建的孩子没有填手机号 → 被邀请者用任意手机号注册 → 创建新 Child 记录 - 被邀请者登录后直接以孩子身份使用小程序