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

311 lines
8.3 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 MiniService struct{}
// GetWechatMiniProgram 获取微信小程序实例
func (w *MiniService) 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 *MiniService) Code2Session(code string) (*model.MiniUser, 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.MiniUser
err = global.GVA_DB.Where("open_id = ?", session.OpenID).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 创建新用户
user = model.MiniUser{
OpenID: session.OpenID,
SessionKey: &session.SessionKey,
}
if session.UnionID != "" {
user.UnionID = &session.UnionID
}
now := time.Now()
user.LastLoginTime = &now
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
}
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 *MiniService) GetUserInfo(openid string) (*model.MiniUser, error) {
var user model.MiniUser
err := global.GVA_DB.Where("open_id = ?", openid).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, errors.New("用户不存在")
}
return nil, err
}
return &user, nil
}
// GetUserByID 根据用户ID获取用户信息
func (w *MiniService) GetUserByID(userID uint) (*model.MiniUser, error) {
var user model.MiniUser
err := global.GVA_DB.Where("id = ?", userID).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 *MiniService) 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.MiniUser{}).Where("open_id = ?", openid).Updates(updates).Error
if err != nil {
global.GVA_LOG.Error("更新微信小程序用户信息失败: " + err.Error())
return err
}
return nil
}
// GetUserList 获取用户列表
func (w *MiniService) GetUserList(page, pageSize int) ([]model.MiniUser, int64, error) {
var users []model.MiniUser
var total int64
db := global.GVA_DB.Model(&model.MiniUser{})
// 获取总数
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
}
// GetUserByUnionID 根据 UnionID 获取用户信息
func (w *MiniService) GetUserByUnionID(unionid string) (*model.MiniUser, error) {
var user model.MiniUser
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
}
// BindPhone 绑定手机号
func (w *MiniService) BindPhone(openid, phone string) (*model.MiniUser, error) {
// 更新小程序用户的手机号
err := global.GVA_DB.Model(&model.MiniUser{}).Where("open_id = ?", openid).Update("phone", phone).Error
if err != nil {
global.GVA_LOG.Error("更新手机号失败: " + err.Error())
return nil, err
}
// 获取更新后的用户信息
var user model.MiniUser
err = global.GVA_DB.Where("open_id = ?", openid).First(&user).Error
if err != nil {
return nil, err
}
global.GVA_LOG.Info("用户绑定手机号成功: " + phone + ", openid: " + openid)
return &user, nil
}
// GetUserByPhone 根据手机号获取用户
func (w *MiniService) GetUserByPhone(phone string) (*model.MiniUser, error) {
var user model.MiniUser
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 *MiniService) CheckUnionIDExists(unionid string) (*model.MiniUser, error) {
if unionid == "" {
return nil, errors.New("unionid 不能为空")
}
var user model.MiniUser
err := global.GVA_DB.Where("union_id = ?", unionid).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil // 不存在,返回 nil 但不报错
}
return nil, err
}
return &user, nil
}
// UpdatePhoneByEncryption 通过加密数据更新用户手机号
func (w *MiniService) UpdatePhoneByEncryption(openid, encryptedData, iv string) (string, error) {
// 1. 获取用户信息
var user model.MiniUser
err := global.GVA_DB.Where("open_id = ?", openid).First(&user).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return "", errors.New("用户不存在")
}
return "", err
}
if user.SessionKey == nil || *user.SessionKey == "" {
return "", errors.New("用户 SessionKey 不存在,请重新登录")
}
// 2. 获取微信小程序实例
mini, err := w.GetWechatMiniProgram()
if err != nil {
return "", err
}
// 3. 使用 session_key 解密手机号
encryptor := mini.GetEncryptor()
plainData, err := encryptor.Decrypt(*user.SessionKey, encryptedData, iv)
if err != nil {
global.GVA_LOG.Error("解密手机号失败: " + err.Error())
return "", errors.New("解密手机号失败")
}
// 4. 从解密数据中获取手机号
phoneNumber := ""
if plainData.PhoneNumber != "" {
phoneNumber = plainData.PhoneNumber
} else {
return "", errors.New("解密数据中未找到手机号")
}
// 5. 更新用户手机号
err = global.GVA_DB.Model(&user).Update("phone", phoneNumber).Error
if err != nil {
global.GVA_LOG.Error("更新手机号失败: " + err.Error())
return "", err
}
global.GVA_LOG.Info("用户手机号更新成功: " + phoneNumber + ", openid: " + openid)
return phoneNumber, nil
}