feat(server): 商业化支付与订单系统 + Harness 改进
Build and Publish Server / build (push) Successful in 4m30s

- 新增商业化模块:Stripe/Binance Pay 支付、邮箱登录、订单管理
- 新增数据模型:ReportProduct, ReportEntitlement, ReportSnapshot, PaymentEvent, EmailLoginCode
- 新增 API:/api/commerce/checkout, /api/commerce/webhooks/*, /api/commerce/orders/*
- 新增管理后台:订单列表、统计图表、统一 AdminHeader 组件
- 新增中间件测试:RateLimit, Auth, CORS 共 7 个测试用例
- 新增 AGENTS.md 项目代理指令文件
- 新增 REMINDER.md 商业化功能开发规范
- 修复 JWT 解析:添加 WithValidMethods 限制 HS256
- 修复 GetPayStatus:增加用户权限校验
- 更新 .gitignore 和 README.md
This commit is contained in:
gouki
2026-09-19 18:57:23 +00:00
parent 241a04496b
commit deb32f8fab
52 changed files with 5419 additions and 339 deletions
+50
View File
@@ -0,0 +1,50 @@
package handler
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
"github.com/gouki/lunar-server/internal/service"
)
func RequestEmailLoginCode(c *gin.Context) {
var request struct {
Email string `json:"email" binding:"required"`
}
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "邮箱格式错误"})
return
}
err := service.NewEmailAuthService().RequestCode(request.Email)
if errors.Is(err, service.ErrInvalidEmail) {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "邮箱格式错误"})
return
}
if errors.Is(err, service.ErrLoginCodeRateLimited) {
c.JSON(http.StatusTooManyRequests, gin.H{"code": 429, "msg": "请稍后再获取验证码"})
return
}
if err != nil {
c.JSON(http.StatusServiceUnavailable, gin.H{"code": 503, "msg": "验证码暂时无法发送"})
return
}
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "验证码已发送"})
}
func VerifyEmailLoginCode(c *gin.Context) {
var request struct {
Email string `json:"email" binding:"required"`
Code string `json:"code" binding:"required"`
}
if err := c.ShouldBindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "msg": "参数错误"})
return
}
user, token, err := service.NewEmailAuthService().VerifyCode(request.Email, request.Code)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "msg": "验证码无效或已过期"})
return
}
c.JSON(http.StatusOK, gin.H{"code": 0, "msg": "success", "data": gin.H{"token": token, "user": user}})
}