315 lines
8.5 KiB
Go
315 lines
8.5 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 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) {
|
||
// 创建新用户
|
||
// 生成默认昵称:萌宠爱 + OpenID的前6位
|
||
defaultNickname := "萌宠爱" + session.OpenID[:6]
|
||
|
||
user = model.MiniUser{
|
||
OpenID: session.OpenID,
|
||
SessionKey: &session.SessionKey,
|
||
Nickname: &defaultNickname,
|
||
}
|
||
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
|
||
}
|