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 }