Refactor WeChat integration to support multiple mini programs, removing hardcoded WeChat credentials and updating user model and authentication flow accordingly.
This commit is contained in:
@@ -21,11 +21,12 @@ func NewAuthHandler(authService *service.AuthService) *AuthHandler {
|
||||
}
|
||||
|
||||
type weChatLoginRequest struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
NickName string `json:"nickname"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Gender *int `json:"gender"`
|
||||
Phone string `json:"phone"`
|
||||
MiniProgramID uint `json:"mini_program_id" binding:"required"`
|
||||
Code string `json:"code" binding:"required"`
|
||||
NickName string `json:"nickname"`
|
||||
AvatarURL string `json:"avatar_url"`
|
||||
Gender *int `json:"gender"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
func (h *AuthHandler) LoginWithWeChat(c *gin.Context) {
|
||||
@@ -36,16 +37,21 @@ func (h *AuthHandler) LoginWithWeChat(c *gin.Context) {
|
||||
}
|
||||
|
||||
result, err := h.authService.LoginWithCode(c.Request.Context(), service.LoginRequest{
|
||||
Code: req.Code,
|
||||
NickName: req.NickName,
|
||||
AvatarURL: req.AvatarURL,
|
||||
Gender: req.Gender,
|
||||
Phone: req.Phone,
|
||||
MiniProgramID: req.MiniProgramID,
|
||||
Code: req.Code,
|
||||
NickName: req.NickName,
|
||||
AvatarURL: req.AvatarURL,
|
||||
Gender: req.Gender,
|
||||
Phone: req.Phone,
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrCodeRequired):
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "code is required"))
|
||||
case errors.Is(err, service.ErrMiniProgramRequired):
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "mini_program_id is required"))
|
||||
case errors.Is(err, service.ErrMiniProgramNotFound):
|
||||
c.JSON(http.StatusBadRequest, model.Error(http.StatusBadRequest, "mini program not found"))
|
||||
default:
|
||||
var apiErr *service.WeChatError
|
||||
if errors.As(err, &apiErr) {
|
||||
@@ -58,19 +64,30 @@ func (h *AuthHandler) LoginWithWeChat(c *gin.Context) {
|
||||
}
|
||||
|
||||
userPayload := gin.H{
|
||||
"id": result.User.ID,
|
||||
"open_id": result.User.OpenID,
|
||||
"nickname": result.User.NickName,
|
||||
"avatar_url": result.User.AvatarURL,
|
||||
"gender": result.User.Gender,
|
||||
"phone": result.User.Phone,
|
||||
"id": result.User.ID,
|
||||
"mini_program_id": result.User.MiniProgramID,
|
||||
"open_id": result.User.OpenID,
|
||||
"nickname": result.User.NickName,
|
||||
"avatar_url": result.User.AvatarURL,
|
||||
"gender": result.User.Gender,
|
||||
"phone": result.User.Phone,
|
||||
}
|
||||
if result.User.UnionID != "" {
|
||||
userPayload["union_id"] = result.User.UnionID
|
||||
}
|
||||
|
||||
miniProgramPayload := gin.H{
|
||||
"id": result.MiniProgram.ID,
|
||||
"name": result.MiniProgram.Name,
|
||||
"app_id": result.MiniProgram.AppID,
|
||||
}
|
||||
if result.MiniProgram.Description != "" {
|
||||
miniProgramPayload["description"] = result.MiniProgram.Description
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.Success(gin.H{
|
||||
"user": userPayload,
|
||||
"session_key": result.SessionKey,
|
||||
"user": userPayload,
|
||||
"session_key": result.SessionKey,
|
||||
"mini_program": miniProgramPayload,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MiniProgram struct {
|
||||
ID uint `gorm:"primarykey" json:"id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
Name string `gorm:"size:100;not null" json:"name"`
|
||||
AppID string `gorm:"size:100;uniqueIndex" json:"app_id"`
|
||||
AppSecret string `gorm:"size:200;not null" json:"-"`
|
||||
Description string `gorm:"size:255" json:"description"`
|
||||
}
|
||||
|
||||
func (MiniProgram) TableName() string {
|
||||
return "mini_programs"
|
||||
}
|
||||
@@ -12,12 +12,14 @@ type User struct {
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
OpenID string `gorm:"uniqueIndex;size:100" json:"open_id"`
|
||||
UnionID string `gorm:"size:100" json:"union_id,omitempty"`
|
||||
NickName string `gorm:"size:100" json:"nickname"`
|
||||
AvatarURL string `gorm:"size:500" json:"avatar_url"`
|
||||
Gender int `gorm:"default:0" json:"gender"`
|
||||
Phone string `gorm:"size:20" json:"phone,omitempty"`
|
||||
MiniProgramID uint `gorm:"index:idx_mini_open,priority:1" json:"mini_program_id"`
|
||||
MiniProgram MiniProgram `gorm:"foreignKey:MiniProgramID" json:"mini_program,omitempty"`
|
||||
OpenID string `gorm:"size:100;index:idx_mini_open,priority:2" json:"open_id"`
|
||||
UnionID string `gorm:"size:100" json:"union_id,omitempty"`
|
||||
NickName string `gorm:"size:100" json:"nickname"`
|
||||
AvatarURL string `gorm:"size:500" json:"avatar_url"`
|
||||
Gender int `gorm:"default:0" json:"gender"`
|
||||
Phone string `gorm:"size:20" json:"phone,omitempty"`
|
||||
|
||||
SessionKey string `gorm:"size:100" json:"-"`
|
||||
}
|
||||
|
||||
@@ -4,46 +4,67 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"wx_service/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var ErrCodeRequired = errors.New("code is required")
|
||||
var (
|
||||
ErrCodeRequired = errors.New("code is required")
|
||||
ErrMiniProgramRequired = errors.New("mini program id is required")
|
||||
ErrMiniProgramNotFound = errors.New("mini program not found")
|
||||
)
|
||||
|
||||
type AuthService struct {
|
||||
db *gorm.DB
|
||||
wechat *WeChatClient
|
||||
db *gorm.DB
|
||||
miniProgramSvc *MiniProgramService
|
||||
wechatClientCache map[uint]*WeChatClient
|
||||
cacheMu sync.RWMutex
|
||||
}
|
||||
|
||||
type LoginRequest struct {
|
||||
Code string
|
||||
NickName string
|
||||
AvatarURL string
|
||||
Gender *int
|
||||
Phone string
|
||||
MiniProgramID uint
|
||||
Code string
|
||||
NickName string
|
||||
AvatarURL string
|
||||
Gender *int
|
||||
Phone string
|
||||
}
|
||||
|
||||
type LoginResult struct {
|
||||
User *model.User
|
||||
SessionKey string
|
||||
User *model.User
|
||||
SessionKey string
|
||||
MiniProgram *model.MiniProgram
|
||||
}
|
||||
|
||||
func NewAuthService(db *gorm.DB, wechat *WeChatClient) *AuthService {
|
||||
func NewAuthService(db *gorm.DB, miniProgramSvc *MiniProgramService) *AuthService {
|
||||
return &AuthService{
|
||||
db: db,
|
||||
wechat: wechat,
|
||||
db: db,
|
||||
miniProgramSvc: miniProgramSvc,
|
||||
wechatClientCache: make(map[uint]*WeChatClient),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *AuthService) LoginWithCode(ctx context.Context, req LoginRequest) (*LoginResult, error) {
|
||||
if strings.TrimSpace(req.Code) == "" {
|
||||
if req.MiniProgramID == 0 {
|
||||
return nil, ErrMiniProgramRequired
|
||||
}
|
||||
if req.Code == "" {
|
||||
return nil, ErrCodeRequired
|
||||
}
|
||||
|
||||
session, err := s.wechat.Code2Session(ctx, req.Code)
|
||||
miniProgram, err := s.miniProgramSvc.GetByID(ctx, req.MiniProgramID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrMiniProgramNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("load mini program: %w", err)
|
||||
}
|
||||
|
||||
client := s.getWeChatClient(miniProgram)
|
||||
session, err := client.Code2Session(ctx, req.Code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -53,15 +74,16 @@ func (s *AuthService) LoginWithCode(ctx context.Context, req LoginRequest) (*Log
|
||||
|
||||
tx := s.db.WithContext(ctx)
|
||||
var user model.User
|
||||
err = tx.Where("open_id = ?", session.OpenID).First(&user).Error
|
||||
err = tx.Where("mini_program_id = ? AND open_id = ?", miniProgram.ID, session.OpenID).First(&user).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
user = model.User{
|
||||
OpenID: session.OpenID,
|
||||
UnionID: session.UnionID,
|
||||
NickName: req.NickName,
|
||||
AvatarURL: req.AvatarURL,
|
||||
Phone: req.Phone,
|
||||
SessionKey: session.SessionKey,
|
||||
MiniProgramID: miniProgram.ID,
|
||||
OpenID: session.OpenID,
|
||||
UnionID: session.UnionID,
|
||||
NickName: req.NickName,
|
||||
AvatarURL: req.AvatarURL,
|
||||
Phone: req.Phone,
|
||||
SessionKey: session.SessionKey,
|
||||
}
|
||||
if req.Gender != nil {
|
||||
user.Gender = *req.Gender
|
||||
@@ -104,8 +126,24 @@ func (s *AuthService) LoginWithCode(ctx context.Context, req LoginRequest) (*Log
|
||||
}
|
||||
|
||||
result := &LoginResult{
|
||||
User: &user,
|
||||
SessionKey: session.SessionKey,
|
||||
User: &user,
|
||||
SessionKey: session.SessionKey,
|
||||
MiniProgram: miniProgram,
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *AuthService) getWeChatClient(mp *model.MiniProgram) *WeChatClient {
|
||||
s.cacheMu.RLock()
|
||||
client, ok := s.wechatClientCache[mp.ID]
|
||||
s.cacheMu.RUnlock()
|
||||
if ok {
|
||||
return client
|
||||
}
|
||||
|
||||
newClient := NewWeChatClient(mp.AppID, mp.AppSecret, nil)
|
||||
s.cacheMu.Lock()
|
||||
s.wechatClientCache[mp.ID] = newClient
|
||||
s.cacheMu.Unlock()
|
||||
return newClient
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"wx_service/internal/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MiniProgramService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewMiniProgramService(db *gorm.DB) *MiniProgramService {
|
||||
return &MiniProgramService{db: db}
|
||||
}
|
||||
|
||||
func (s *MiniProgramService) GetByID(ctx context.Context, id uint) (*model.MiniProgram, error) {
|
||||
var mp model.MiniProgram
|
||||
if err := s.db.WithContext(ctx).Where("id = ?", id).First(&mp).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mp, nil
|
||||
}
|
||||
Reference in New Issue
Block a user