feat: rename qiniu to oss, add admin upload proxy with thumbnail, add dev-login
- Rename all QINIU_* config/code/docs to OSS_* to match actual Alibaba Cloud OSS - Refactor upload module from internal/common/qiniu to internal/common/upload - Add backend proxy upload endpoint (POST /api/admin/marketing/upload) to avoid CORS - Auto-generate compressed thumbnail (800px, JPEG 80%) on admin image upload - Add dev-login endpoint (POST /api/v1/auth/dev-login) for H5 debugging - Add imageutil package for server-side image resizing Made-with: Cursor
This commit is contained in:
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -107,3 +108,43 @@ func (h *AuthHandler) LoginWithWeChat(c *gin.Context) {
|
||||
"mini_program": miniProgramPayload,
|
||||
}))
|
||||
}
|
||||
|
||||
type devLoginRequest struct {
|
||||
MiniProgramID uint `json:"mini_program_id"`
|
||||
}
|
||||
|
||||
// DevLogin 仅在非 release 模式下可用,用于 H5 开发调试。
|
||||
func (h *AuthHandler) DevLogin(c *gin.Context) {
|
||||
if gin.Mode() == gin.ReleaseMode {
|
||||
c.JSON(http.StatusNotFound, model.Error(http.StatusNotFound, "not found"))
|
||||
return
|
||||
}
|
||||
|
||||
var req devLoginRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
if req.MiniProgramID == 0 {
|
||||
req.MiniProgramID = 3
|
||||
}
|
||||
|
||||
result, err := h.authService.DevLogin(c.Request.Context(), req.MiniProgramID)
|
||||
if err != nil {
|
||||
log.Printf("[dev_login] error: %v", err)
|
||||
c.JSON(http.StatusInternalServerError, model.Error(http.StatusInternalServerError, "dev login failed: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, model.Success(gin.H{
|
||||
"user": gin.H{
|
||||
"id": result.User.ID,
|
||||
"mini_program_id": result.User.MiniProgramID,
|
||||
"open_id": result.User.OpenID,
|
||||
"nickname": result.User.NickName,
|
||||
"avatar_url": result.User.AvatarURL,
|
||||
},
|
||||
"session_key": result.SessionKey,
|
||||
"mini_program": gin.H{
|
||||
"id": result.MiniProgram.ID,
|
||||
"name": result.MiniProgram.Name,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -147,6 +147,51 @@ func (s *AuthService) LoginWithCode(ctx context.Context, req LoginRequest) (*Log
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// DevLogin 开发模式专用:创建或查找测试用户,无需微信授权。
|
||||
func (s *AuthService) DevLogin(ctx context.Context, miniProgramID uint) (*LoginResult, error) {
|
||||
if miniProgramID == 0 {
|
||||
return nil, ErrMiniProgramRequired
|
||||
}
|
||||
|
||||
miniProgram, err := s.miniProgramSvc.GetByID(ctx, miniProgramID)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, ErrMiniProgramNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("load mini program: %w", err)
|
||||
}
|
||||
|
||||
const devOpenID = "dev_test_user"
|
||||
sessionKey := fmt.Sprintf("dev_session_%d", miniProgramID)
|
||||
|
||||
tx := s.db.WithContext(ctx)
|
||||
var user model.User
|
||||
err = tx.Where("mini_program_id = ? AND open_id = ?", miniProgramID, devOpenID).First(&user).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
user = model.User{
|
||||
MiniProgramID: miniProgramID,
|
||||
OpenID: devOpenID,
|
||||
NickName: "开发测试用户",
|
||||
AvatarURL: defaultAvatarURL,
|
||||
SessionKey: sessionKey,
|
||||
}
|
||||
if err := tx.Create(&user).Error; err != nil {
|
||||
return nil, fmt.Errorf("create dev user: %w", err)
|
||||
}
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("query dev user: %w", err)
|
||||
} else {
|
||||
tx.Model(&user).Update("session_key", sessionKey)
|
||||
user.SessionKey = sessionKey
|
||||
}
|
||||
|
||||
return &LoginResult{
|
||||
User: &user,
|
||||
SessionKey: sessionKey,
|
||||
MiniProgram: miniProgram,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *AuthService) getSmokeMode(ctx context.Context, uid int) (string, error) {
|
||||
var profile smokemodel.SmokeUserProfile
|
||||
err := s.db.WithContext(ctx).
|
||||
|
||||
Reference in New Issue
Block a user