简介
本文件面向开发者,系统化梳理本项目中“微信集成”的API能力与实现要点,覆盖以下范围:
- 微信小程序登录(code换取session_key、unionid/openid)、手机号获取与解密、支付发起
- 微信公众号基础能力:签名校验、被动消息处理、access_token管理、菜单与素材推送
- 用户与微信账号绑定关系:首次登录自动注册、多账号关联(unionid优先)、按手机号关联、解绑策略说明
- 安全考量:签名验证、数据加密、防重放、频率限制、审计日志
- 高级功能:模板消息等能力在本仓库未直接实现,给出接入建议与扩展点
项目结构
微信相关能力分布在三个层次:
- API端(小程序):提供登录、手机号获取、支付等接口
- 公众号服务:提供签名校验、被动消息、access_token、菜单与素材推送
- 插件层(PC端):提供第三方OAuth登录能力(非小程序路径)
graph TB
subgraph "API端(小程序)"
A["api/controller/user/WeixinController"]
end
subgraph "公众号服务"
B["core/service/weixin/WeixinService"]
end
subgraph "插件(PC端)"
C["plugin/wxlogin/WxloginProvider"]
D["plugin/wxlogin/WxloginService"]
end
subgraph "后台入口"
E["admin/controller/weixin/WeixinController"]
end
A --> |"调用微信API"| B
C --> |"PC端OAuth流程"| D
E --> |"菜单/素材管理"| B
图表来源
- api/controller/user/WeixinController.php:108-379
- core/service/weixin/WeixinService.php:45-135
- admin/controller/weixin/WeixinController.php:27-41
章节来源
- api/controller/user/WeixinController.php:108-379
- core/service/weixin/WeixinService.php:45-135
- admin/controller/weixin/WeixinController.php:27-41
核心组件
- 小程序登录与支付控制器:负责小程序侧登录、手机号获取、支付发起
- 公众号服务:封装微信服务器交互(签名、消息、token、菜单、素材)
- PC端微信登录插件:提供独立于API端的OAuth登录能力
- 后台微信入口:统一跳转到菜单管理页面
章节来源
- api/controller/user/WeixinController.php:75-414
- core/service/weixin/WeixinService.php:45-199
- plugin/wxlogin/WxloginProvider.php
- plugin/wxlogin/WxloginService.php
- admin/controller/weixin/WeixinController.php:27-41
架构总览
下图展示小程序登录、手机号获取、支付的核心调用链,以及公众号被动消息与菜单管理的调用关系。
sequenceDiagram
participant Client as "小程序客户端"
participant API as "api/controller/user/WeixinController"
participant WX as "微信开放平台API"
participant DB as "本地数据库(user_sns/user)"
participant Token as "ApiTokenService"
participant Audit as "审计日志"
Client->>API : POST /user/weixin/login (code, phone?)
API->>WX : jscode2session(code)
WX-->>API : {openid, unionid?, session_key}
API->>DB : 查询 user_sns(openid/unionid)
alt 已存在用户
API->>Token : 签发API Token
API->>Audit : 记录登录成功
API-->>Client : {user_id, token, is_work}
else 未绑定/新用户
API->>DB : 创建用户并写入 user_sns
API->>Token : 签发API Token
API->>Audit : 记录注册/绑定
API-->>Client : {user_id, token, is_work}
end
Client->>API : POST /user/weixin/pay (order_sn)
API->>WX : 发起微信支付统一下单
WX-->>API : 支付参数
API-->>Client : 返回支付参数
图表来源
- api/controller/user/WeixinController.php:108-379
- api/controller/user/WeixinController.php:389-414
详细组件分析
小程序登录接口(POST /user/weixin/login)
- 功能:通过小程序 code 换取 session_key,解析 openid/unionid,完成用户绑定或自动注册,签发系统API Token
- 关键流程:
- 频率限制:基于IP进行登录频率限制
- 获取会话:调用微信 jscode2session
- 绑定策略:优先按 openid 查找;若配置为 unionid 模式则尝试 unionid 匹配;支持按手机号关联到已有用户
- 自动注册:未命中时创建新用户并写入 user_sns
- 手机号回填:若用户无手机号且请求携带有效手机号,更新并同步默认联系方式
- 审计:记录登录成功/失败及原因
- 错误场景:
- code无效或微信接口异常
- unionid缺失(当配置为unionid模式)
- 账户锁定
- 绑定/注册异常
flowchart TD
Start(["进入 login"]) --> Rate["IP频率限制检查"]
Rate --> |超限| ErrRate["抛出频繁操作异常"]
Rate --> CallWX["调用 jscode2session"]
CallWX --> CheckWX{"微信返回有效?"}
CheckWX --> |否| ErrWX["抛出微信接口异常"]
CheckWX --> FindUser["按openid/unionid/phone查找用户"]
FindUser --> HasUser{"是否找到用户?"}
HasUser --> |是| LockCheck["账户锁定检查"]
LockCheck --> |锁定| ErrLock["抛出账户锁定异常"]
LockCheck --> IssueToken["签发API Token并返回"]
HasUser --> |否| AutoReg["自动注册用户并写入user_sns"]
AutoReg --> IssueToken
ErrRate --> End(["结束"])
ErrWX --> End
ErrLock --> End
IssueToken --> End
图表来源
- api/controller/user/WeixinController.php:108-379
章节来源
- api/controller/user/WeixinController.php:108-379
小程序手机号获取(POST /user/weixin/getPhone)
- 功能:使用小程序 code 换取手机号信息(服务端解密)
- 关键点:
- 先获取小程序 access_token
- 调用微信 getuserphonenumber 接口
- 返回明文手机号字段
- 适用场景:小程序端在用户授权后获取手机号
章节来源
- api/controller/user/WeixinController.php:75-99
小程序支付发起(POST /user/weixin/pay)
- 功能:根据订单号发起微信支付(小程序端)
- 关键点:
- 校验订单状态
- 从 user_sns 获取当前用户的 openid
- 构造支付参数并返回给前端
- 注意:实际统一下单细节由内部 WxPayService 封装
章节来源
- api/controller/user/WeixinController.php:389-414
公众号基础能力(签名、消息、token、菜单、素材)
- 签名校验:对GET请求进行signature/timestamp/nonce校验,用于URL验证
- 被动消息:解析XML,处理订阅、点击事件、文本消息,并回复图文消息
- access_token:按配置获取公众号access_token
- 菜单:创建、获取、删除公众号菜单
- 素材推送:根据类型/关键字组装待推送图文条目
sequenceDiagram
participant WX as "微信服务器"
participant Svc as "WeixinService"
participant DB as "weixin_media/article"
WX->>Svc : GET /verify?signature=...
Svc->>Svc : checkSignature()
Svc-->>WX : echostr
WX->>Svc : POST XML(事件/文本)
Svc->>Svc : responseMsg()
Svc->>DB : 读取素材/文章
Svc-->>WX : 回复图文XML
图表来源
- core/service/weixin/WeixinService.php:45-109
- core/service/weixin/WeixinService.php:118-199
- core/service/weixin/WeixinService.php:488-541
章节来源
- core/service/weixin/WeixinService.php:45-199
- core/service/weixin/WeixinService.php:488-541
PC端微信登录插件(OAuth)
- 作用:提供PC端通过微信OAuth授权的登录能力,与小程序API路径分离
- 组成:
- Provider:负责OAuth流程编排
- Service:业务逻辑封装
- 注意:不要将PC端OAuth与小程序登录混用
章节来源
- plugin/wxlogin/WxloginProvider.php
- plugin/wxlogin/WxloginService.php
后台微信入口
- 行为:访问后台微信模块时重定向至菜单管理页
- 用途:统一管理公众号菜单与素材
章节来源
- admin/controller/weixin/WeixinController.php:27-41
依赖关系分析
- 小程序登录依赖:
- 微信开放平台API(jscode2session、getuserphonenumber)
- 本地数据库(user、user_sns)
- 令牌服务(ApiTokenService)
- 审计日志(UserLogAction/UserLogDetail)
- 公众号服务依赖:
- 微信公共平台API(token、menu、消息)
- 本地数据库(weixin_menu、weixin_media、article)
- HTTP客户端(Client)
graph LR
Login["api/controller/user/WeixinController"] --> WXAPI["微信开放平台API"]
Login --> DB["user / user_sns"]
Login --> Token["ApiTokenService"]
Login --> Audit["审计日志"]
WXCore["core/service/weixin/WeixinService"] --> WXPub["微信公共平台API"]
WXCore --> DB2["weixin_menu / weixin_media / article"]
图表来源
- api/controller/user/WeixinController.php:108-379
- core/service/weixin/WeixinService.php:118-199
章节来源
- api/controller/user/WeixinController.php:108-379
- core/service/weixin/WeixinService.php:118-199
性能考虑
- 登录频率限制:通过IP维度限制,防止暴力攻击与资源滥用
- 数据库事务:绑定/注册/更新手机号等操作使用事务保证一致性
- 外部API调用:尽量减少重复请求,合理缓存access_token(可在上层扩展)
- 响应体精简:仅返回必要字段,降低网络开销
故障排查指南
- 登录失败常见原因:
- code无效或过期:检查小程序端是否正确获取并传递code
- appid/secret不匹配:核对配置项
- unionid缺失:当配置为unionid模式时需确保能获取unionid
- 账户锁定:检查账户锁定策略与解锁时间
- 手机号获取失败:
- 检查小程序是否已授权手机号
- 确认access_token有效
- 支付失败:
- 订单状态是否为待支付
- openid是否正确获取
- 商户配置是否完整
章节来源
- api/controller/user/WeixinController.php:116-142
- api/controller/user/WeixinController.php:152-162
- api/controller/user/WeixinController.php:389-400
结论
本项目实现了小程序侧的微信登录、手机号获取与支付发起,以及公众号的基础能力(签名、消息、token、菜单、素材)。PC端通过插件提供独立的OAuth登录流程。整体设计清晰、职责分离,便于扩展模板消息等高级能力。
附录:接口定义与示例
小程序登录
- 接口:POST /user/weixin/login
- 请求参数:
- code: string,必填,小程序登录凭证
- phone: string,可选,用户手机号(用于自动关联或回填)
- act: string,可选,预检标识(如preload)
- 响应字段:
- user.user_id: int
- user.token: string
- dou.auth.is_work: bool
- 典型成功响应示例:
- { "user": { "user_id": 123, "token": "xxxx" }, "dou": { "auth": { "is_work": false } } }
- 典型失败场景:
- Code无效:提示“请求微信接口失败,appid或私钥不匹配!”
- 账户锁定:提示“账户已被锁定,请X分钟后重试”
- 绑定失败:提示“绑定微信失败,请重试”
章节来源
- api/controller/user/WeixinController.php:108-379
小程序手机号获取
- 接口:POST /user/weixin/getPhone
- 请求参数:
- code: string,必填,手机号授权码
- 响应字段:
- phone: string,明文手机号
- 典型成功响应示例:
- { "phone": "13800138000" }
- 典型失败场景:
- 未授权或code无效:返回空手机号或错误
章节来源
- api/controller/user/WeixinController.php:75-99
小程序支付发起
- 接口:POST /user/weixin/pay
- 请求参数:
- order_sn: string,必填,订单号
- 响应字段:
- 支付所需参数(由内部WxPayService生成)
- 典型成功响应示例:
- { ...支付参数对象... }
- 典型失败场景:
- 订单状态不符:提示“当前订单状态无法发起支付,请联系客服”,状态码422
章节来源
- api/controller/user/WeixinController.php:389-414
公众号签名校验与被动消息
- URL验证:GET /your_weixin_verify?signature=×tamp=&nonce=&echostr=
- 校验通过后返回echostr
- 被动消息:POST /your_weixin_callback
- 支持事件(subscribe、CLICK)与文本消息
- 可回复图文消息(基于素材与文章)
章节来源
- core/service/weixin/WeixinService.php:45-109
- core/service/weixin/WeixinService.php:488-541
用户与微信账号绑定关系管理
- 首次登录绑定:
- 通过openid/unionid查找用户,不存在则自动注册并写入user_sns
- 多账号关联:
- 当配置为unionid模式时,优先按unionid关联,实现多端账号打通
- 按手机号关联:
- 若请求携带有效手机号且命中已有用户,可将该微信与该用户绑定
- 解绑操作:
- 当前代码未提供解绑接口;如需解绑,建议在user_sns表层面移除对应记录,并在业务层增加权限校验与审计
章节来源
- api/controller/user/WeixinController.php:148-379
安全考虑
- 签名验证:公众号URL验证使用signature/timestamp/nonce校验
- 数据加密:小程序手机号通过微信官方接口解密,避免明文传输敏感信息
- 防重放攻击:结合时间戳与随机数校验,配合后端频率限制
- 频率限制:登录接口按IP限制,防止暴力破解
- 审计日志:登录成功/失败、绑定/注册均记录审计日志,便于追溯
章节来源
- core/service/weixin/WeixinService.php:45-65
- api/controller/user/WeixinController.php:116-142
高级功能:模板消息
- 现状:本仓库未直接实现模板消息接口
- 建议:
- 在WeixinService中新增模板消息发送方法,复用accessToken机制
- 调用微信模板消息API,传入模板ID、用户openid与数据变量
- 增加幂等与重试机制,记录发送结果与错误码