fix: 全面修复项目问题

后端修复:
- 验证码存储与校验机制
- 订单创建事务+库存扣减
- GetStats字段名错误
- VerifyEmail改为POST
- 供应商更新字段白名单
- 文件删除安全检查
- 抽奖安全随机数
- 用户管理CRUD
- 订单取消/确认收货
- 工单回复
- Toggle返回新数据
- 移除死代码

前端修复:
- 404兜底路由
- 401软跳转
- API层统一
- 面包屑补充banners
- 国际化完善
- 购物车并行删除
- 退出清理购物车
- 供应商Dashboard数据
- 工单详情页
- 订单取消/确认收货
This commit is contained in:
2026-05-06 09:16:34 +08:00
parent 739481b59f
commit 3a898e8aa0
52 changed files with 2807 additions and 920 deletions
+111
View File
@@ -2,6 +2,8 @@ package handlers
import (
"net/http"
"strconv"
"sync"
"time"
"sale/internal/models"
@@ -17,6 +19,43 @@ func NewAuthHandler() *AuthHandler {
return &AuthHandler{}
}
var (
verifyCodes = make(map[string]verifyCodeEntry)
verifyCodesMux sync.Mutex
)
type verifyCodeEntry struct {
Code string
ExpiresAt time.Time
}
func storeVerifyCode(email, code string) {
verifyCodesMux.Lock()
defer verifyCodesMux.Unlock()
verifyCodes[email] = verifyCodeEntry{
Code: code,
ExpiresAt: time.Now().Add(30 * time.Minute),
}
}
func checkVerifyCode(email, code string) bool {
verifyCodesMux.Lock()
defer verifyCodesMux.Unlock()
entry, ok := verifyCodes[email]
if !ok {
return false
}
if time.Now().After(entry.ExpiresAt) {
delete(verifyCodes, email)
return false
}
if entry.Code != code {
return false
}
delete(verifyCodes, email)
return true
}
func (h *AuthHandler) Register(c *gin.Context) {
var req schemas.RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
@@ -68,6 +107,7 @@ func (h *AuthHandler) Register(c *gin.Context) {
}
verifyCode := utils.GenerateVerifyCode()
storeVerifyCode(req.Email, verifyCode)
utils.SendVerifyEmail(req.Email, verifyCode)
token, _ := utils.GenerateToken(user.ID, user.Role)
@@ -137,6 +177,7 @@ func (h *AuthHandler) ForgotPassword(c *gin.Context) {
}
code := utils.GenerateVerifyCode()
storeVerifyCode(req.Email, code)
utils.SendResetPasswordEmail(req.Email, code)
c.JSON(http.StatusOK, gin.H{"message": "If the email exists, a verification code has been sent"})
@@ -149,6 +190,11 @@ func (h *AuthHandler) ResetPassword(c *gin.Context) {
return
}
if !checkVerifyCode(req.Email, req.Code) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or expired verification code"})
return
}
var user models.User
if err := utils.DB.Where("email = ?", req.Email).First(&user).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid email"})
@@ -172,6 +218,11 @@ func (h *AuthHandler) VerifyEmail(c *gin.Context) {
return
}
if !checkVerifyCode(req.Email, req.Code) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid or expired verification code"})
return
}
var user models.User
if err := utils.DB.Where("email = ?", req.Email).First(&user).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "User not found"})
@@ -261,6 +312,7 @@ func (h *AuthHandler) SendVerifyCode(c *gin.Context) {
}
code := utils.GenerateVerifyCode()
storeVerifyCode(user.Email, code)
utils.SendVerifyEmail(user.Email, code)
c.JSON(http.StatusOK, gin.H{"message": "Verification code sent", "expires_at": time.Now().Add(30 * time.Minute)})
@@ -320,3 +372,62 @@ func (h *AuthHandler) Install(c *gin.Context) {
},
})
}
func (h *AuthHandler) AdminGetUsers(c *gin.Context) {
var users []models.User
utils.DB.Select("id, username, email, role, purchase_credits, is_active, email_verified, created_at").Find(&users)
c.JSON(http.StatusOK, gin.H{"data": users})
}
func (h *AuthHandler) AdminUpdateUser(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var user models.User
if err := utils.DB.First(&user, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
var req struct {
Username *string `json:"username"`
Email *string `json:"email"`
Role *string `json:"role"`
IsActive *bool `json:"is_active"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
updates := make(map[string]interface{})
if req.Username != nil {
updates["username"] = *req.Username
}
if req.Email != nil {
updates["email"] = *req.Email
}
if req.Role != nil {
updates["role"] = *req.Role
}
if req.IsActive != nil {
updates["is_active"] = *req.IsActive
}
utils.DB.Model(&user).Updates(updates)
utils.DB.First(&user, id)
c.JSON(http.StatusOK, gin.H{"data": user})
}
func (h *AuthHandler) AdminDeleteUser(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var user models.User
if err := utils.DB.First(&user, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
return
}
if user.Role == "admin" {
c.JSON(http.StatusForbidden, gin.H{"error": "Cannot delete admin user"})
return
}
utils.DB.Delete(&user)
c.JSON(http.StatusOK, gin.H{"message": "User deleted successfully"})
}