pet-ai/server/plugin/wechat-integration/service/wechat_mini_service.go

310 lines
9.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package service
import (
"errors"
"time"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model"
"github.com/silenceper/wechat/v2"
"github.com/silenceper/wechat/v2/cache"
"github.com/silenceper/wechat/v2/miniprogram"
miniConfig "github.com/silenceper/wechat/v2/miniprogram/config"
"gorm.io/gorm"
)
type WechatMiniService struct{}
// GetWechatMiniProgram 获取微信小程序实例
func (w *WechatMiniService) GetWechatMiniProgram() (*miniprogram.MiniProgram, error) {
if global.GVA_CONFIG.Wechat.MiniAppID == "" || global.GVA_CONFIG.Wechat.MiniAppSecret == "" {
return nil, errors.New("微信小程序配置不完整")
}
wc := wechat.NewWechat()
memory := cache.NewMemory()
cfg := &miniConfig.Config{
AppID: global.GVA_CONFIG.Wechat.MiniAppID,
AppSecret: global.GVA_CONFIG.Wechat.MiniAppSecret,
Cache: memory,
}
return wc.GetMiniProgram(cfg), nil
}
// Code2Session 小程序登录凭证校验
func (w *WechatMiniService) Code2Session(code string) (*model.WechatMiniUser, error) {
mini, err := w.GetWechatMiniProgram()
if err != nil {
return nil, err
}
// 调用微信接口获取 session
session, err := mini.GetAuth().Code2Session(code)
if err != nil {
global.GVA_LOG.Error("微信小程序 Code2Session 失败: " + err.Error())
return nil, err
}
if session.OpenID == "" {
return nil, errors.New("获取用户 OpenID 失败")
}
// 查找或创建用户
var user model.WechatMiniUser
err = global.GVA_DB.Where("openid = ?", session.OpenID).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 创建新用户
user = model.WechatMiniUser{
OpenID: session.OpenID,
SessionKey: &session.SessionKey,
}
if session.UnionID != "" {
user.UnionID = &session.UnionID
}
now := time.Now()
user.LastLoginTime = &now
// 尝试关联系统用户
err = w.linkSystemUser(&user)
if err != nil {
global.GVA_LOG.Error("关联系统用户失败: " + err.Error())
}
err = global.GVA_DB.Create(&user).Error
if err != nil {
global.GVA_LOG.Error("创建微信小程序用户失败: " + err.Error())
return nil, err
}
} else {
global.GVA_LOG.Error("查询微信小程序用户失败: " + err.Error())
return nil, err
}
} else {
// 更新用户信息
now := time.Now()
user.SessionKey = &session.SessionKey
user.LastLoginTime = &now
if session.UnionID != "" {
user.UnionID = &session.UnionID
}
// 如果还没有关联系统用户,尝试关联
if user.UserID == nil {
err = w.linkSystemUser(&user)
if err != nil {
global.GVA_LOG.Error("关联系统用户失败: " + err.Error())
}
}
err = global.GVA_DB.Save(&user).Error
if err != nil {
global.GVA_LOG.Error("更新微信小程序用户失败: " + err.Error())
return nil, err
}
}
return &user, nil
}
// GetUserInfo 获取用户信息
func (w *WechatMiniService) GetUserInfo(openid string) (*model.WechatMiniUser, error) {
var user model.WechatMiniUser
err := global.GVA_DB.Where("openid = ?", openid).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("用户不存在")
}
return nil, err
}
return &user, nil
}
// UpdateUserInfo 更新用户信息
func (w *WechatMiniService) UpdateUserInfo(openid string, userInfo map[string]interface{}) error {
updates := make(map[string]interface{})
if nickname, ok := userInfo["nickname"]; ok {
if str, ok := nickname.(string); ok && str != "" {
updates["nickname"] = str
}
}
if avatarUrl, ok := userInfo["avatarUrl"]; ok {
if str, ok := avatarUrl.(string); ok && str != "" {
updates["avatar_url"] = str
}
}
if gender, ok := userInfo["gender"]; ok {
if num, ok := gender.(float64); ok {
updates["gender"] = int(num)
}
}
if country, ok := userInfo["country"]; ok {
if str, ok := country.(string); ok && str != "" {
updates["country"] = str
}
}
if province, ok := userInfo["province"]; ok {
if str, ok := province.(string); ok && str != "" {
updates["province"] = str
}
}
if city, ok := userInfo["city"]; ok {
if str, ok := city.(string); ok && str != "" {
updates["city"] = str
}
}
if language, ok := userInfo["language"]; ok {
if str, ok := language.(string); ok && str != "" {
updates["language"] = str
}
}
if len(updates) == 0 {
return errors.New("没有需要更新的信息")
}
err := global.GVA_DB.Model(&model.WechatMiniUser{}).Where("openid = ?", openid).Updates(updates).Error
if err != nil {
global.GVA_LOG.Error("更新微信小程序用户信息失败: " + err.Error())
return err
}
return nil
}
// GetUserList 获取用户列表
func (w *WechatMiniService) GetUserList(page, pageSize int) ([]model.WechatMiniUser, int64, error) {
var users []model.WechatMiniUser
var total int64
db := global.GVA_DB.Model(&model.WechatMiniUser{})
// 获取总数
err := db.Count(&total).Error
if err != nil {
return nil, 0, err
}
// 分页查询
offset := (page - 1) * pageSize
err = db.Offset(offset).Limit(pageSize).Order("created_at desc").Find(&users).Error
if err != nil {
return nil, 0, err
}
return users, total, nil
}
// linkSystemUser 关联系统用户 (通过 unionid 关联为未来APP扩展预留)
func (w *WechatMiniService) linkSystemUser(user *model.WechatMiniUser) error {
if user.UnionID == nil || *user.UnionID == "" {
// 没有 unionid无法跨平台关联但小程序仍可正常使用
global.GVA_LOG.Warn("小程序用户没有 UnionID无法跨平台关联")
return nil
}
// 查找是否有其他小程序用户或未来的APP用户已经关联了系统用户
var existingMiniUser model.WechatMiniUser
err := global.GVA_DB.Where("unionid = ? AND user_id IS NOT NULL AND openid != ?", *user.UnionID, user.OpenID).First(&existingMiniUser).Error
if err == nil {
// 找到了已关联系统用户的用户,使用相同的 user_id
user.UserID = existingMiniUser.UserID
global.GVA_LOG.Info("通过 UnionID 关联到已存在的系统用户: " + *user.UnionID)
return nil
}
// 如果没有找到,暂时不自动创建系统用户
// 等用户输入手机号后,通过业务逻辑关联或创建系统用户
global.GVA_LOG.Info("新用户等待手机号验证后关联系统用户unionid: " + *user.UnionID)
return nil
}
// GetUserByUnionID 根据 UnionID 获取用户信息
func (w *WechatMiniService) GetUserByUnionID(unionid string) (*model.WechatMiniUser, error) {
var user model.WechatMiniUser
err := global.GVA_DB.Where("unionid = ?", unionid).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("用户不存在")
}
return nil, err
}
return &user, nil
}
// LinkToSystemUser 手动关联系统用户
func (w *WechatMiniService) LinkToSystemUser(openid string, userID uint) error {
err := global.GVA_DB.Model(&model.WechatMiniUser{}).Where("openid = ?", openid).Update("user_id", userID).Error
if err != nil {
global.GVA_LOG.Error("关联系统用户失败: " + err.Error())
return err
}
global.GVA_LOG.Info("成功关联系统用户")
return nil
}
// BindPhoneAndLinkUser 绑定手机号并关联系统用户
func (w *WechatMiniService) BindPhoneAndLinkUser(openid, phone string) (*model.WechatMiniUser, error) {
// 1. 更新小程序用户的手机号
err := global.GVA_DB.Model(&model.WechatMiniUser{}).Where("openid = ?", openid).Update("phone", phone).Error
if err != nil {
global.GVA_LOG.Error("更新手机号失败: " + err.Error())
return nil, err
}
// 2. 获取更新后的用户信息
var user model.WechatMiniUser
err = global.GVA_DB.Where("openid = ?", openid).First(&user).Error
if err != nil {
return nil, err
}
// 3. 根据手机号查找或创建系统用户
// 这里需要根据实际的系统用户表结构来实现
// 暂时先记录日志,实际项目中需要调用系统用户服务
global.GVA_LOG.Info("用户绑定手机号: " + phone + ", openid: " + openid)
// 4. 如果有 UnionID同时更新所有相关的微信账号
if user.UnionID != nil && *user.UnionID != "" {
// 未来如果有APP用户也会通过相同的 UnionID 关联到同一个系统用户
global.GVA_LOG.Info("用户有 UnionID支持跨平台关联: " + *user.UnionID)
}
return &user, nil
}
// GetUserByPhone 根据手机号获取用户
func (w *WechatMiniService) GetUserByPhone(phone string) (*model.WechatMiniUser, error) {
var user model.WechatMiniUser
err := global.GVA_DB.Where("phone = ?", phone).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("用户不存在")
}
return nil, err
}
return &user, nil
}
// CheckUnionIDExists 检查 UnionID 是否已存在为APP登录预留
func (w *WechatMiniService) CheckUnionIDExists(unionid string) (*model.WechatMiniUser, error) {
if unionid == "" {
return nil, errors.New("unionid 不能为空")
}
var user model.WechatMiniUser
err := global.GVA_DB.Where("unionid = ?", unionid).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil // 不存在,返回 nil 但不报错
}
return nil, err
}
return &user, nil
}