310 lines
9.1 KiB
Go
310 lines
9.1 KiB
Go
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
|
||
}
|