package service import ( "errors" "fmt" "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/officialaccount" "github.com/silenceper/wechat/v2/officialaccount/config" "gorm.io/gorm" ) type MpUserService struct{} // GetOfficialAccount 获取微信公众号实例 func (m *MpUserService) GetOfficialAccount() (*officialaccount.OfficialAccount, error) { // 从数据库获取微信公众号配置 var mpConfig model.MpConfig err := global.GVA_DB.Where("config_type = ?", model.ConfigTypeMP).First(&mpConfig).Error if err != nil { return nil, fmt.Errorf("获取微信公众号配置失败: %v", err) } if mpConfig.AppID == "" || mpConfig.AppSecret == "" { return nil, errors.New("微信公众号配置不完整") } wc := wechat.NewWechat() memory := cache.NewMemory() cfg := &config.Config{ AppID: mpConfig.AppID, AppSecret: mpConfig.AppSecret, Cache: memory, } // 如果有Token,设置服务器配置 if mpConfig.Token != nil && *mpConfig.Token != "" { cfg.Token = *mpConfig.Token } return wc.GetOfficialAccount(cfg), nil } // SyncUser 同步微信用户信息 func (m *MpUserService) SyncUser(openid string) (*model.MpUser, error) { oa, err := m.GetOfficialAccount() if err != nil { return nil, err } // 从微信获取用户信息 userInfo, err := oa.GetUser().GetUserInfo(openid) if err != nil { global.GVA_LOG.Error("获取微信用户信息失败: " + err.Error()) return nil, err } // 查找或创建用户 var user model.MpUser err = global.GVA_DB.Where("openid = ?", openid).First(&user).Error if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { // 创建新用户 user = model.MpUser{ OpenID: userInfo.OpenID, SubscribeStatus: int(userInfo.Subscribe), } if userInfo.Nickname != "" { user.Nickname = &userInfo.Nickname } if userInfo.Headimgurl != "" { user.HeadImageURL = &userInfo.Headimgurl } if userInfo.Language != "" { user.Language = &userInfo.Language } if userInfo.Country != "" { user.Country = &userInfo.Country } if userInfo.Province != "" { user.Province = &userInfo.Province } if userInfo.City != "" { user.City = &userInfo.City } // 设置关注时间 if userInfo.Subscribe == 1 && userInfo.SubscribeTime > 0 { subscribeTime := time.Unix(int64(userInfo.SubscribeTime), 0) user.SubscribeTime = &subscribeTime } // 设置 UnionID (仅用于记录,公众号不关联系统用户) if userInfo.UnionID != "" { user.UnionID = &userInfo.UnionID global.GVA_LOG.Info("公众号用户 UnionID: " + userInfo.UnionID) } 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 { // 更新用户信息 user.SubscribeStatus = int(userInfo.Subscribe) if userInfo.Nickname != "" { user.Nickname = &userInfo.Nickname } if userInfo.Headimgurl != "" { user.HeadImageURL = &userInfo.Headimgurl } if userInfo.Language != "" { user.Language = &userInfo.Language } if userInfo.Country != "" { user.Country = &userInfo.Country } if userInfo.Province != "" { user.Province = &userInfo.Province } if userInfo.City != "" { user.City = &userInfo.City } // 更新关注状态和时间 if userInfo.Subscribe == 1 && userInfo.SubscribeTime > 0 { subscribeTime := time.Unix(int64(userInfo.SubscribeTime), 0) user.SubscribeTime = &subscribeTime user.UnsubscribeTime = nil } else if userInfo.Subscribe == 0 { now := time.Now() user.UnsubscribeTime = &now } // 如果还没有关联系统用户且有 unionid,尝试关联 // 注意:这里需要根据实际业务需求决定是否需要重新关联 err = global.GVA_DB.Save(&user).Error if err != nil { global.GVA_LOG.Error("更新微信公众号用户失败: " + err.Error()) return nil, err } } return &user, nil } // GetUserByOpenID 根据 OpenID 获取用户 func (m *MpUserService) GetUserByOpenID(openid string) (*model.MpUser, error) { var user model.MpUser 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 } // GetUserList 获取用户列表 func (m *MpUserService) GetUserList(page, pageSize int, subscribeStatus *int) ([]model.MpUser, int64, error) { var users []model.MpUser var total int64 db := global.GVA_DB.Model(&model.MpUser{}) // 根据关注状态筛选 if subscribeStatus != nil { db = db.Where("subscribe_status = ?", *subscribeStatus) } // 获取总数 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 } // UpdateUserRemark 更新用户备注 func (m *MpUserService) UpdateUserRemark(openid, remark string) error { // 先更新微信服务器 oa, err := m.GetOfficialAccount() if err != nil { return err } err = oa.GetUser().UpdateRemark(openid, remark) if err != nil { global.GVA_LOG.Error("更新微信用户备注失败: " + err.Error()) return err } // 更新本地数据库 err = global.GVA_DB.Model(&model.MpUser{}).Where("openid = ?", openid).Update("remark", remark).Error if err != nil { global.GVA_LOG.Error("更新本地用户备注失败: " + err.Error()) return err } return nil } // SubscribeUser 用户关注事件处理 func (m *MpUserService) SubscribeUser(openid string) error { // 同步用户信息 _, err := m.SyncUser(openid) if err != nil { return err } global.GVA_LOG.Info("用户关注: " + openid) return nil } // UnsubscribeUser 用户取消关注事件处理 func (m *MpUserService) UnsubscribeUser(openid string) error { now := time.Now() err := global.GVA_DB.Model(&model.MpUser{}).Where("openid = ?", openid).Updates(map[string]interface{}{ "subscribe_status": 0, "unsubscribe_time": now, }).Error if err != nil { global.GVA_LOG.Error("处理用户取消关注失败: " + err.Error()) return err } global.GVA_LOG.Info("用户取消关注: " + openid) return nil } // GetUserByUnionID 根据 UnionID 获取公众号用户信息 (仅用于查询,不关联系统用户) func (m *MpUserService) GetUserByUnionID(unionid string) (*model.MpUser, error) { var user model.MpUser 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 }