Files
huang 9c6d4a3bd4 实现个人客户微信认证和短信验证功能
- 添加个人客户微信登录和手机验证码登录接口
- 实现个人客户设备、ICCID、手机号关联管理
- 添加短信发送服务(HTTP 客户端)
- 添加微信认证服务(含 mock 实现)
- 添加 JWT Token 生成和验证工具
- 创建数据库迁移脚本(personal_customer 关联表)
- 修复测试文件中的路由注册参数错误
- 重构 scripts 目录结构(分离独立脚本到子目录)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-10 11:42:38 +08:00

8.4 KiB
Raw Permalink Blame History

Change: 添加个人客户和微信登录

Why

个人客户是系统的重要用户群体,他们通过 H5/小程序访问系统,使用 ICCID/设备号登录并绑定微信。个人客户不参与 RBAC 权限体系,但需要独立的认证流程和数据存储。

What Changes

新增功能

  • 个人客户登录流程: 通过 ICCID/设备号 + 微信授权登录,首次需绑定手机号
  • 微信绑定: 存储 OpenID/UnionID 用于微信支付和通知(用户唯一标识)
  • 个人客户认证中间件: 独立于 B 端账号的认证体系
  • 短信验证码: 对接武汉聚惠富通行业短信平台发送验证码
  • ICCID/设备号绑定记录: 记录微信用户使用过哪些 ICCID/设备号

核心业务模型

用户身份识别

  • 个人客户 (PersonalCustomer) = 微信用户(通过 wx_open_id 唯一标识)
  • ICCID/设备号 是独立的资源(可以被充值、使用),不是用户身份
  • 任何人拿到 ICCID/设备号 都可以使用,没有所有权概念

数据模型关系

  1. PersonalCustomer: 微信用户主表不存储手机号、ICCID
  2. PersonalCustomerPhone: 微信用户绑定的手机号(一对多)
  3. PersonalCustomerICCID: 微信用户使用过的 ICCID 记录(多对多)
  4. PersonalCustomerDevice: 微信用户使用过的设备号记录(多对多,可选)

业务规则

  1. 用户身份:个人客户由微信 OpenID/UnionID 唯一标识
  2. 手机号绑定:一个微信用户可以绑定多个手机号(用于接收验证码)
  3. ICCID/设备号绑定:记录微信用户使用过哪些 ICCID/设备号(用于业务追踪)
  4. 充值业务:充值是充到 ICCID/设备号上,不是充到用户账户

登录流程

用户扫码/进入H5
     ↓
输入 ICCID/设备号(业务标识,不存储到用户表)
     ↓
微信授权登录
     ↓
获取 wx_open_id, wx_union_id
     ↓
检查微信用户是否存在
     ├─ 是 → 记录 ICCID 绑定关系 → 登录成功
     └─ 否 → 创建新用户 → 提示绑定手机号
             ↓
         输入手机号 → 发送验证码 → 验证
             ↓
         创建手机号绑定记录 → 创建 ICCID 绑定记录 → 登录成功

数据模型设计

1. PersonalCustomer个人客户 = 微信用户)

type PersonalCustomer struct {
    ID           uint      // 主键
    WxOpenID     string    // 微信OpenID唯一标识必填
    WxUnionID    string    // 微信UnionID必填
    Nickname     string    // 微信昵称
    AvatarURL    string    // 微信头像URL
    Status       int       // 状态 0=禁用 1=启用
    CreatedAt    time.Time
    UpdatedAt    time.Time
    DeletedAt    *time.Time
}

索引:

  • 唯一索引: wx_open_id (where deleted_at IS NULL)
  • 普通索引: wx_union_id

说明:

  • 移除 phoneiccidimei 字段
  • 微信信息是唯一标识用户的字段

2. PersonalCustomerPhone微信用户的手机号

type PersonalCustomerPhone struct {
    ID         uint      // 主键
    CustomerID uint      // 关联个人客户 ID微信用户
    Phone      string    // 手机号
    IsPrimary  bool      // 是否主手机号(用于通知等)
    VerifiedAt time.Time // 验证通过时间
    Status     int       // 状态 0=禁用 1=启用
    CreatedAt  time.Time
    UpdatedAt  time.Time
    DeletedAt  *time.Time
}

索引:

  • 唯一索引: (customer_id, phone) (where deleted_at IS NULL)
  • 普通索引: phone

说明:

  • 一个微信用户可以绑定多个手机号
  • 手机号用于接收验证码、通知等

3. PersonalCustomerICCIDICCID 与微信用户的绑定关系)

type PersonalCustomerICCID struct {
    ID         uint      // 主键
    CustomerID uint      // 关联个人客户 ID微信用户
    ICCID      string    // ICCID20位数字
    BindAt     time.Time // 绑定时间
    LastUsedAt time.Time // 最后使用时间
    Status     int       // 状态 0=禁用 1=启用
    CreatedAt  time.Time
    UpdatedAt  time.Time
    DeletedAt  *time.Time
}

索引:

  • 唯一索引: (customer_id, iccid) (where deleted_at IS NULL)
  • 普通索引: iccid - 查询某个 ICCID 被哪些用户使用过

说明:

  • 记录微信用户使用过哪些 ICCID
  • 一个 ICCID 可以被多个微信用户使用过
  • 一个微信用户可以使用多个 ICCID

4. PersonalCustomerDevice设备号与微信用户的绑定关系可选

type PersonalCustomerDevice struct {
    ID         uint      // 主键
    CustomerID uint      // 关联个人客户 ID微信用户
    DeviceNo   string    // 设备号/IMEI
    BindAt     time.Time // 绑定时间
    LastUsedAt time.Time // 最后使用时间
    Status     int       // 状态 0=禁用 1=启用
    CreatedAt  time.Time
    UpdatedAt  time.Time
    DeletedAt  *time.Time
}

索引:

  • 唯一索引: (customer_id, device_no) (where deleted_at IS NULL)
  • 普通索引: device_no

说明:

  • 记录微信用户使用过哪些设备号
  • 与 ICCID 类似的多对多关系

Impact

  • Affected specs: personal-customer (新建)
  • Affected code:
    • internal/model/personal_customer.go - 需要修改(移除 phone 字段)
    • internal/model/personal_customer_phone.go - 新增
    • internal/model/personal_customer_iccid.go - 新增
    • internal/model/personal_customer_device.go - 新增(可选)
    • internal/store/postgres/personal_customer_store.go - 需要扩展
    • internal/store/postgres/personal_customer_phone_store.go - 新增
    • internal/store/postgres/personal_customer_iccid_store.go - 新增
    • internal/service/personal_customer_service.go - 扩展登录逻辑
    • internal/handler/personal_customer_handler.go - 新增
    • internal/middleware/personal_auth.go - 个人客户认证中间件
    • pkg/sms/ - 短信验证码服务(对接武汉聚惠富通行业短信)
    • config/config.yaml - 新增短信服务配置项
    • migrations/ - 新增数据库迁移脚本

依赖关系

本提案依赖 add-user-organization-model 提案中的 PersonalCustomer 模型定义。

短信服务对接方案

第三方服务信息

  • 服务商: 武汉聚惠富通(行业短信)
  • 接口网关: https://gateway.sms.whjhft.com:8443/sms
  • 协议: HTTP JSON API v1.6
  • 接口文档: docs/第三方文档/SMS_HTTP_1.6.md

使用接口

短信批量发送接口: POST /api/sendMessageMass

发送方式: 直接发送内容(不使用短信模板)

短信内容格式: 【签名】自定义内容

  • 签名部分需提前向服务商报备并审核通过
  • 示例: 【签名】您的验证码是1234565分钟内有效
  • 不使用 templateIdparams 参数,只使用 content 字段

实现方案

  1. 包结构: pkg/sms/

    • client.go - 短信客户端封装
    • types.go - 请求/响应类型定义
    • error.go - 错误码映射
  2. 配置管理:

    sms:
      gateway_url: "https://gateway.sms.whjhft.com:8443/sms"
      username: "账号用户名"
      password: "账号密码"
      signature: "【签名】"
      timeout: 10s
    
  3. 核心功能:

    • 生成 Sign 签名MD5 计算)
    • 发送验证码短信
    • 错误处理和日志记录
    • 超时和重试机制
  4. 安全要求:

    • 短信密码不得硬编码,必须从配置文件读取
    • Sign 计算遵循官方规范:MD5(userName + timestamp + MD5(password))
    • 时间戳与服务器时间误差不得超过5分钟
  5. 错误处理:

    • 余额不足code=5记录错误日志返回用户友好提示
    • 时间戳错误code=16检查服务器时间同步
    • 账号异常code=3, 4记录错误日志通知管理员
    • 其他错误:参考文档响应状态码列表

注意事项

  • 数据模型变更PersonalCustomer 模型需要移除 phone 字段,新增 PersonalCustomerPhone、PersonalCustomerICCID 关联表
  • 微信 SDK 集成:可以先预留接口或使用 Mock 实现,后续对接具体的微信 OAuth API
  • 短信签名:需要提前向服务商报备,使用报备通过的签名
  • 业务逻辑实现:本提案重点在数据模型建立,具体业务逻辑(登录流程、绑定流程)后续实现
  • ICCID/设备号充值:充值是充到 ICCID/设备号资源上,不是充到用户账户,需与后续的资产模块协同设计