diff --git a/server/plugin/wechat-integration/api/mini_api.go b/server/plugin/wechat-integration/api/mini_api.go index b34c4bb8..9d37514f 100644 --- a/server/plugin/wechat-integration/api/mini_api.go +++ b/server/plugin/wechat-integration/api/mini_api.go @@ -8,6 +8,7 @@ import ( "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/request" wechatResponse "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model/response" "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/service" + "github.com/flipped-aurora/gin-vue-admin/server/utils" "github.com/gin-gonic/gin" "go.uber.org/zap" ) @@ -45,11 +46,18 @@ func (w *MiniApi) Login(c *gin.Context) { return } - // 这里可以生成JWT token或其他认证信息 + // 生成JWT token + token, _, err := utils.AppUserLoginToken(user) + if err != nil { + global.GVA_LOG.Error("生成token失败!", zap.Error(err)) + response.FailWithMessage("登录失败: "+err.Error(), c) + return + } + resp := wechatResponse.MiniLoginResponse{ OpenID: user.OpenID, UnionID: user.UnionID, - UserID: user.UserID, + Token: token, } response.OkWithDetailed(resp, "登录成功", c) @@ -135,7 +143,7 @@ func (w *MiniApi) BindPhone(c *gin.Context) { return } - user, err := miniService.BindPhoneAndLinkUser(req.OpenID, req.Phone) + user, err := miniService.BindPhone(req.OpenID, req.Phone) if err != nil { global.GVA_LOG.Error("绑定手机号失败!", zap.Error(err)) response.FailWithMessage("绑定失败: "+err.Error(), c) diff --git a/server/plugin/wechat-integration/config/config.go b/server/plugin/wechat-integration/config/config.go index ac08efa5..694f3e0a 100644 --- a/server/plugin/wechat-integration/config/config.go +++ b/server/plugin/wechat-integration/config/config.go @@ -5,6 +5,4 @@ type WechatConfig struct { // 微信小程序配置 MiniAppID string `mapstructure:"mini-app-id" json:"miniAppId" yaml:"mini-app-id"` MiniAppSecret string `mapstructure:"mini-app-secret" json:"miniAppSecret" yaml:"mini-app-secret"` - - // 微信公众号配置现在从数据库获取,不再从配置文件读取 } diff --git a/server/plugin/wechat-integration/initialize/router.go b/server/plugin/wechat-integration/initialize/router.go index 8157341b..02f7294e 100644 --- a/server/plugin/wechat-integration/initialize/router.go +++ b/server/plugin/wechat-integration/initialize/router.go @@ -11,9 +11,11 @@ func Router(engine *gin.Engine) { // 获取路由组,微信插件使用/api前缀与前端匹配 publicGroup := engine.Group("") privateGroup := engine.Group("") + userGroup := engine.Group("") // 添加JWT认证和Casbin权限中间件 privateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) + userGroup.Use(middleware.UserJWTAuth()) // 初始化微信路由 wechatRouter := router.Router{} diff --git a/server/plugin/wechat-integration/model/mini_user.go b/server/plugin/wechat-integration/model/mini_user.go index 40283b39..9f015e36 100644 --- a/server/plugin/wechat-integration/model/mini_user.go +++ b/server/plugin/wechat-integration/model/mini_user.go @@ -4,6 +4,7 @@ import ( "time" "github.com/flipped-aurora/gin-vue-admin/server/global" + "github.com/google/uuid" ) // MiniUser 微信小程序用户表 @@ -11,7 +12,6 @@ type MiniUser struct { global.GVA_MODEL OpenID string `json:"openid" gorm:"type:varchar(100);not null;uniqueIndex;comment:用户openid"` UnionID *string `json:"unionid" gorm:"type:varchar(64);index;comment:微信开放平台统一标识"` - UserID *uint `json:"userId" gorm:"index;comment:关联系统用户ID"` SessionKey *string `json:"sessionKey" gorm:"type:varchar(255);comment:会话密钥"` Nickname *string `json:"nickname" gorm:"type:varchar(50);comment:用户昵称"` AvatarURL *string `json:"avatarUrl" gorm:"type:varchar(1024);comment:用户头像"` @@ -26,12 +26,78 @@ type MiniUser struct { // TableName 指定表名 func (MiniUser) TableName() string { - return "wechat_mini_users" + return "mini_users" } -// IsLinkedToSystemUser 检查是否已关联系统用户 -func (w *MiniUser) IsLinkedToSystemUser() bool { - return w.UserID != nil && *w.UserID > 0 +// 实现AppUserLogin接口 +func (u *MiniUser) GetUUID() uuid.UUID { + // 将uint ID转换为UUID,这里使用一个简单的方法 + // 在实际项目中,你可能需要为用户添加一个专门的UUID字段 + return uuid.New() +} + +func (u *MiniUser) GetUserId() uint { + return u.ID +} + +func (u *MiniUser) GetOpenID() string { + return u.OpenID +} + +func (u *MiniUser) GetUnionID() string { + if u.UnionID != nil { + return *u.UnionID + } + return "" +} + +func (u *MiniUser) GetNickname() string { + if u.Nickname != nil { + return *u.Nickname + } + return "" +} + +func (u *MiniUser) GetAvatar() string { + if u.AvatarURL != nil { + return *u.AvatarURL + } + return "" +} + +func (u *MiniUser) GetPhone() string { + if u.Phone != nil { + return *u.Phone + } + return "" +} + +func (u *MiniUser) GetGender() int { + if u.Gender != nil { + return *u.Gender + } + return 0 +} + +func (u *MiniUser) GetCity() string { + if u.City != nil { + return *u.City + } + return "" +} + +func (u *MiniUser) GetProvince() string { + if u.Province != nil { + return *u.Province + } + return "" +} + +func (u *MiniUser) GetCountry() string { + if u.Country != nil { + return *u.Country + } + return "" } // HasUnionID 检查是否有 UnionID diff --git a/server/plugin/wechat-integration/model/response/response.go b/server/plugin/wechat-integration/model/response/response.go index 71f163ee..eea8308c 100644 --- a/server/plugin/wechat-integration/model/response/response.go +++ b/server/plugin/wechat-integration/model/response/response.go @@ -2,9 +2,9 @@ package response // MiniLoginResponse 小程序登录响应 type MiniLoginResponse struct { - OpenID string `json:"openid"` // 用户openid - UnionID *string `json:"unionid"` // 用户unionid - UserID *uint `json:"userId,omitempty"` // 关联的系统用户ID + OpenID string `json:"openid"` // 用户openid + UnionID *string `json:"unionid"` // 用户unionid + Token string `json:"token"` // JWT token } // PageResult 分页结果 diff --git a/server/plugin/wechat-integration/service/enter.go b/server/plugin/wechat-integration/service/enter.go index dcee41a2..628fbe5e 100644 --- a/server/plugin/wechat-integration/service/enter.go +++ b/server/plugin/wechat-integration/service/enter.go @@ -15,7 +15,6 @@ type ServiceGroup struct { MpConfigService MpConfigService // 微信配置管理服务 MpDraftService MpDraftService // 公众号草稿服务 MpTagService MpTagService // 公众号标签服务 - UserService UserService // 用户服务 } // ServiceGroupApp 服务组实例 diff --git a/server/plugin/wechat-integration/service/mini_service.go b/server/plugin/wechat-integration/service/mini_service.go index 0eb61c47..975c67af 100644 --- a/server/plugin/wechat-integration/service/mini_service.go +++ b/server/plugin/wechat-integration/service/mini_service.go @@ -67,12 +67,6 @@ func (w *MiniService) Code2Session(code string) (*model.MiniUser, error) { 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()) @@ -91,14 +85,6 @@ func (w *MiniService) Code2Session(code string) (*model.MiniUser, error) { 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()) @@ -198,31 +184,6 @@ func (w *MiniService) GetUserList(page, pageSize int) ([]model.MiniUser, int64, return users, total, nil } -// linkSystemUser 关联系统用户 (通过 unionid 关联,为未来APP扩展预留) -func (w *MiniService) linkSystemUser(user *model.MiniUser) error { - if user.UnionID == nil || *user.UnionID == "" { - // 没有 unionid,无法跨平台关联,但小程序仍可正常使用 - global.GVA_LOG.Warn("小程序用户没有 UnionID,无法跨平台关联") - return nil - } - - // 查找是否有其他小程序用户(或未来的APP用户)已经关联了系统用户 - var existingMiniUser model.MiniUser - 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 *MiniService) GetUserByUnionID(unionid string) (*model.MiniUser, error) { var user model.MiniUser @@ -236,45 +197,23 @@ func (w *MiniService) GetUserByUnionID(unionid string) (*model.MiniUser, error) return &user, nil } -// LinkToSystemUser 手动关联系统用户 -func (w *MiniService) LinkToSystemUser(openid string, userID uint) error { - err := global.GVA_DB.Model(&model.MiniUser{}).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 *MiniService) BindPhoneAndLinkUser(openid, phone string) (*model.MiniUser, error) { - // 1. 更新小程序用户的手机号 +// BindPhone 绑定手机号 +func (w *MiniService) BindPhone(openid, phone string) (*model.MiniUser, error) { + // 更新小程序用户的手机号 err := global.GVA_DB.Model(&model.MiniUser{}).Where("openid = ?", openid).Update("phone", phone).Error if err != nil { global.GVA_LOG.Error("更新手机号失败: " + err.Error()) return nil, err } - // 2. 获取更新后的用户信息 + // 获取更新后的用户信息 var user model.MiniUser 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) - } - + global.GVA_LOG.Info("用户绑定手机号成功: " + phone + ", openid: " + openid) return &user, nil } diff --git a/server/plugin/wechat-integration/service/user_service.go b/server/plugin/wechat-integration/service/user_service.go deleted file mode 100644 index b7801bd0..00000000 --- a/server/plugin/wechat-integration/service/user_service.go +++ /dev/null @@ -1,234 +0,0 @@ -package service - -import ( - "errors" - - "github.com/flipped-aurora/gin-vue-admin/server/global" - "github.com/flipped-aurora/gin-vue-admin/server/plugin/wechat-integration/model" -) - -type UserService struct{} - -// GetUnifiedUserInfo 获取统一的微信用户信息 (通过 unionid 关联) -func (w *UserService) GetUnifiedUserInfo(unionid string) (*UnifiedWechatUser, error) { - if unionid == "" { - return nil, errors.New("unionid 不能为空") - } - - var unifiedUser UnifiedWechatUser - - // 查找小程序用户 - var miniUser model.MiniUser - err := global.GVA_DB.Where("unionid = ?", unionid).First(&miniUser).Error - if err == nil { - unifiedUser.MiniUser = &miniUser - unifiedUser.UserID = miniUser.UserID - } - - // 查找公众号用户 - var mpUser model.MpUser - err = global.GVA_DB.Where("unionid = ?", unionid).First(&mpUser).Error - if err == nil { - unifiedUser.MpUser = &mpUser - if unifiedUser.UserID == nil { - unifiedUser.UserID = mpUser.UserID - } - } - - // 如果都没找到 - if unifiedUser.MiniUser == nil && unifiedUser.MpUser == nil { - return nil, errors.New("未找到相关用户信息") - } - - unifiedUser.UnionID = unionid - return &unifiedUser, nil -} - -// GetUserBySystemUserID 根据系统用户ID获取微信用户信息 -func (w *UserService) GetUserBySystemUserID(userID uint) (*UnifiedWechatUser, error) { - var unifiedUser UnifiedWechatUser - unifiedUser.UserID = &userID - - // 查找小程序用户 - var miniUser model.MiniUser - err := global.GVA_DB.Where("user_id = ?", userID).First(&miniUser).Error - if err == nil { - unifiedUser.MiniUser = &miniUser - if miniUser.UnionID != nil { - unifiedUser.UnionID = *miniUser.UnionID - } - } - - // 查找公众号用户 - var mpUser model.MpUser - err = global.GVA_DB.Where("user_id = ?", userID).First(&mpUser).Error - if err == nil { - unifiedUser.MpUser = &mpUser - if unifiedUser.UnionID == "" && mpUser.UnionID != nil { - unifiedUser.UnionID = *mpUser.UnionID - } - } - - // 如果都没找到 - if unifiedUser.MiniUser == nil && unifiedUser.MpUser == nil { - return nil, errors.New("未找到相关用户信息") - } - - return &unifiedUser, nil -} - -// LinkAllAccountsToSystemUser 将所有微信账号关联到系统用户 -func (w *UserService) LinkAllAccountsToSystemUser(unionid string, userID uint) error { - if unionid == "" { - return errors.New("unionid 不能为空") - } - - // 开启事务 - tx := global.GVA_DB.Begin() - defer func() { - if r := recover(); r != nil { - tx.Rollback() - } - }() - - // 关联小程序用户 - err := tx.Model(&model.MiniUser{}).Where("unionid = ?", unionid).Update("user_id", userID).Error - if err != nil { - tx.Rollback() - global.GVA_LOG.Error("关联小程序用户失败: " + err.Error()) - return err - } - - // 关联公众号用户 - err = tx.Model(&model.MpUser{}).Where("unionid = ?", unionid).Update("user_id", userID).Error - if err != nil { - tx.Rollback() - global.GVA_LOG.Error("关联公众号用户失败: " + err.Error()) - return err - } - - // 提交事务 - err = tx.Commit().Error - if err != nil { - global.GVA_LOG.Error("提交事务失败: " + err.Error()) - return err - } - - global.GVA_LOG.Info("成功关联所有微信账号到系统用户") - return nil -} - -// GetAllWechatUsers 获取所有微信用户列表 (分页) -func (w *UserService) GetAllWechatUsers(page, pageSize int) ([]UnifiedWechatUser, int64, error) { - var users []UnifiedWechatUser - var total int64 - - // 这里需要复杂的查询逻辑,暂时简化实现 - // 实际项目中可能需要使用 SQL 联合查询或者分别查询后合并 - - // 先获取所有有 unionid 的用户 - var unionIDs []string - - // 从小程序用户中获取 unionid - err := global.GVA_DB.Model(&model.MiniUser{}). - Where("unionid IS NOT NULL AND unionid != ''"). - Distinct("unionid"). - Pluck("unionid", &unionIDs).Error - if err != nil { - return nil, 0, err - } - - // 从公众号用户中获取 unionid - var mpUnionIDs []string - err = global.GVA_DB.Model(&model.MpUser{}). - Where("unionid IS NOT NULL AND unionid != ''"). - Distinct("unionid"). - Pluck("unionid", &mpUnionIDs).Error - if err != nil { - return nil, 0, err - } - - // 合并去重 - unionIDMap := make(map[string]bool) - for _, id := range unionIDs { - unionIDMap[id] = true - } - for _, id := range mpUnionIDs { - unionIDMap[id] = true - } - - // 转换为切片 - allUnionIDs := make([]string, 0, len(unionIDMap)) - for id := range unionIDMap { - allUnionIDs = append(allUnionIDs, id) - } - - total = int64(len(allUnionIDs)) - - // 分页处理 - start := (page - 1) * pageSize - end := start + pageSize - if end > len(allUnionIDs) { - end = len(allUnionIDs) - } - if start >= len(allUnionIDs) { - return users, total, nil - } - - pageUnionIDs := allUnionIDs[start:end] - - // 获取每个 unionid 对应的用户信息 - for _, unionID := range pageUnionIDs { - unifiedUser, err := w.GetUnifiedUserInfo(unionID) - if err == nil { - users = append(users, *unifiedUser) - } - } - - return users, total, nil -} - -// UnifiedWechatUser 统一的微信用户信息结构 -type UnifiedWechatUser struct { - UnionID string `json:"unionid"` - UserID *uint `json:"userId"` - MiniUser *model.MiniUser `json:"miniUser,omitempty"` - MpUser *model.MpUser `json:"mpUser,omitempty"` -} - -// HasMiniProgram 是否有小程序账号 -func (u *UnifiedWechatUser) HasMiniProgram() bool { - return u.MiniUser != nil -} - -// HasOfficialAccount 是否有公众号账号 -func (u *UnifiedWechatUser) HasOfficialAccount() bool { - return u.MpUser != nil -} - -// IsLinkedToSystemUser 是否已关联系统用户 -func (u *UnifiedWechatUser) IsLinkedToSystemUser() bool { - return u.UserID != nil && *u.UserID > 0 -} - -// GetDisplayName 获取显示名称 -func (u *UnifiedWechatUser) GetDisplayName() string { - if u.MiniUser != nil && u.MiniUser.Nickname != nil { - return *u.MiniUser.Nickname - } - if u.MpUser != nil && u.MpUser.Nickname != nil { - return *u.MpUser.Nickname - } - return "未知用户" -} - -// GetAvatarURL 获取头像URL -func (u *UnifiedWechatUser) GetAvatarURL() string { - if u.MiniUser != nil && u.MiniUser.AvatarURL != nil { - return *u.MiniUser.AvatarURL - } - if u.MpUser != nil && u.MpUser.HeadImageURL != nil { - return *u.MpUser.HeadImageURL - } - return "" -}