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
|
|
}
|