This commit is contained in:
parent
5b43f99d9a
commit
db64e158ce
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
||||
// 微信公众号配置现在从数据库获取,不再从配置文件读取
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 分页结果
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ type ServiceGroup struct {
|
|||
MpConfigService MpConfigService // 微信配置管理服务
|
||||
MpDraftService MpDraftService // 公众号草稿服务
|
||||
MpTagService MpTagService // 公众号标签服务
|
||||
UserService UserService // 用户服务
|
||||
}
|
||||
|
||||
// ServiceGroupApp 服务组实例
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 ""
|
||||
}
|
||||
Loading…
Reference in New Issue