Initial commit: 网络验证平台
This commit is contained in:
@@ -0,0 +1,805 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"verification-platform-backend/internal/database"
|
||||
"verification-platform-backend/internal/model"
|
||||
"verification-platform-backend/internal/service"
|
||||
"verification-platform-backend/internal/service/payment"
|
||||
"verification-platform-backend/pkg/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetupRoutes(r *gin.Engine) {
|
||||
auth := r.Group("/api/v1/auth")
|
||||
{
|
||||
auth.POST("/register", handleRegister)
|
||||
auth.POST("/login", handleLogin)
|
||||
auth.POST("/logout", handleLogout)
|
||||
auth.POST("/refresh", handleRefreshToken)
|
||||
}
|
||||
|
||||
api := r.Group("/api/v1")
|
||||
{
|
||||
api.POST("/login", handleLogin)
|
||||
api.POST("/register", handleRegister)
|
||||
api.POST("/logout", handleLogout)
|
||||
api.POST("/refresh", handleRefreshToken)
|
||||
api.POST("/forgot-password", handleForgotPassword)
|
||||
api.POST("/reset-password", handleResetPassword)
|
||||
api.POST("/send-sms", handleSendSms)
|
||||
api.POST("/verify", handleCardVerification)
|
||||
api.GET("/verify/status", handleGetVerificationStatus)
|
||||
}
|
||||
|
||||
docs := r.Group("/api/v1/docs")
|
||||
{
|
||||
docs.GET("", handleGetDocs)
|
||||
docs.GET("/:id", handleGetDoc)
|
||||
docs.GET("/slug/:slug", handleGetDocBySlug)
|
||||
}
|
||||
|
||||
docCategories := r.Group("/api/v1/doc-categories")
|
||||
{
|
||||
docCategories.GET("", handleGetDocCategories)
|
||||
}
|
||||
|
||||
pricing := r.Group("/api/v1/pricing")
|
||||
{
|
||||
pricing.GET("", handleGetPricing)
|
||||
}
|
||||
|
||||
packages := r.Group("/api/v1/packages")
|
||||
{
|
||||
packages.GET("", handleGetPricing)
|
||||
}
|
||||
|
||||
site := r.Group("/api/v1/site")
|
||||
{
|
||||
site.GET("/info", handleGetSiteInfo)
|
||||
site.GET("/contact", handleGetContact)
|
||||
}
|
||||
|
||||
captcha := r.Group("/api/v1/captcha")
|
||||
{
|
||||
captcha.GET("", handleGetCaptcha)
|
||||
}
|
||||
|
||||
settings := r.Group("/api/v1/settings")
|
||||
{
|
||||
settings.GET("", handleGetSettings)
|
||||
settings.GET("/payment-channels", handleGetActivePaymentChannels)
|
||||
}
|
||||
|
||||
public := r.Group("/api/v1/public")
|
||||
{
|
||||
public.GET("/stats", handleGetPublicStats)
|
||||
}
|
||||
}
|
||||
|
||||
func handleGetCaptcha(c *gin.Context) {
|
||||
authService := service.NewAuthService()
|
||||
result, err := authService.GetCaptcha()
|
||||
if err != nil {
|
||||
response.Error(c, 500, "获取验证码失败")
|
||||
return
|
||||
}
|
||||
response.Success(c, result)
|
||||
}
|
||||
|
||||
func handleGetSettings(c *gin.Context) {
|
||||
settingService := service.NewSettingService()
|
||||
settings, err := settingService.GetSettings()
|
||||
if err != nil {
|
||||
response.Error(c, 500, "获取设置失败")
|
||||
return
|
||||
}
|
||||
response.Success(c, settings)
|
||||
}
|
||||
|
||||
func handleGetActivePaymentChannels(c *gin.Context) {
|
||||
var channels []model.PaymentChannel
|
||||
if err := database.DB.Where("status = ?", "active").Order("sort asc, id asc").Find(&channels).Error; err != nil {
|
||||
response.Error(c, 500, "获取支付通道失败")
|
||||
return
|
||||
}
|
||||
response.Success(c, gin.H{"channels": channels})
|
||||
}
|
||||
|
||||
func handleRegister(c *gin.Context) {
|
||||
var req struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Role string `json:"role"`
|
||||
Type string `json:"type"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
fmt.Printf("注册参数错误: %v\n", err)
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("注册请求: username=%s, email=%s, phone=%s, role=%s, type=%s\n",
|
||||
req.Username, req.Email, req.Phone, req.Role, req.Type)
|
||||
|
||||
if req.Role == "" {
|
||||
req.Role = "developer"
|
||||
}
|
||||
|
||||
username := req.Username
|
||||
email := req.Email
|
||||
phone := req.Phone
|
||||
password := req.Password
|
||||
|
||||
if req.Type == "email" {
|
||||
username = email
|
||||
email = req.Email
|
||||
password = req.Password
|
||||
} else if req.Type == "sms" {
|
||||
username = phone
|
||||
phone = req.Phone
|
||||
password = req.Password
|
||||
}
|
||||
|
||||
fmt.Printf("处理后的注册数据: username=%s, email=%s, phone=%s, password_len=%d, role=%s\n",
|
||||
username, email, phone, len(password), req.Role)
|
||||
|
||||
authService := service.NewAuthService()
|
||||
err := authService.RegisterWithRole(username, email, phone, password, req.Role)
|
||||
if err != nil {
|
||||
fmt.Printf("注册失败: %v\n", err)
|
||||
response.Error(c, 400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "注册成功",
|
||||
})
|
||||
}
|
||||
|
||||
func handleLogin(c *gin.Context) {
|
||||
var req struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
AgentPath string `json:"agent_path"`
|
||||
CaptchaID string `json:"captcha_id"`
|
||||
Captcha string `json:"captcha"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
fmt.Printf("登录参数错误: %v\n", err)
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("登录请求: username=%s, password_len=%d, agent_path=%s\n",
|
||||
req.Username, len(req.Password), req.AgentPath)
|
||||
|
||||
// 检查是否启用了验证码
|
||||
var enableCaptchaSetting model.Setting
|
||||
err := database.DB.Where("category = ? AND key = ?", "basic", "enableCaptcha").First(&enableCaptchaSetting).Error
|
||||
enableCaptcha := false
|
||||
if err == nil && enableCaptchaSetting.Value == "true" {
|
||||
enableCaptcha = true
|
||||
}
|
||||
|
||||
// 如果启用了验证码,验证验证码
|
||||
if enableCaptcha {
|
||||
if req.CaptchaID == "" || req.Captcha == "" {
|
||||
response.Error(c, 400, "请输入验证码")
|
||||
return
|
||||
}
|
||||
|
||||
// 查询验证码
|
||||
var captcha model.Captcha
|
||||
if err := database.DB.Where("captcha_id = ?", req.CaptchaID).First(&captcha).Error; err != nil {
|
||||
response.Error(c, 400, "验证码错误或已过期")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查验证码是否过期
|
||||
if time.Now().After(captcha.ExpiresAt) {
|
||||
database.DB.Delete(&captcha)
|
||||
response.Error(c, 400, "验证码已过期")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证码比较(忽略大小写和空格)
|
||||
if strings.ToLower(strings.TrimSpace(req.Captcha)) != strings.ToLower(strings.TrimSpace(captcha.Code)) {
|
||||
database.DB.Delete(&captcha)
|
||||
response.Error(c, 400, "验证码错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 删除已使用的验证码
|
||||
database.DB.Delete(&captcha)
|
||||
}
|
||||
|
||||
authService := service.NewAuthService()
|
||||
result, err := authService.Login(req.Username, req.Password, req.AgentPath)
|
||||
if err != nil {
|
||||
fmt.Printf("登录失败: %v\n", err)
|
||||
response.Error(c, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("登录成功: username=%s\n", req.Username)
|
||||
response.Success(c, result)
|
||||
}
|
||||
|
||||
func handleLogout(c *gin.Context) {
|
||||
response.Success(c, gin.H{
|
||||
"message": "登出成功",
|
||||
})
|
||||
}
|
||||
|
||||
func handleRefreshToken(c *gin.Context) {
|
||||
response.Success(c, gin.H{
|
||||
"token": "",
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetDocs(c *gin.Context) {
|
||||
var docs []model.Doc
|
||||
if err := database.DB.Where("status = ?", "published").Preload("Category").Find(&docs).Error; err != nil {
|
||||
response.Error(c, 500, "获取文档列表失败")
|
||||
return
|
||||
}
|
||||
response.Success(c, docs)
|
||||
}
|
||||
|
||||
func handleGetDoc(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var doc model.Doc
|
||||
if err := database.DB.Where("id = ? AND status = ?", id, "published").Preload("Category").First(&doc).Error; err != nil {
|
||||
response.Error(c, 404, "文档不存在")
|
||||
return
|
||||
}
|
||||
response.Success(c, doc)
|
||||
}
|
||||
|
||||
func handleGetDocBySlug(c *gin.Context) {
|
||||
slug := c.Param("slug")
|
||||
var doc model.Doc
|
||||
if err := database.DB.Where("slug = ? AND status = ?", slug, "published").Preload("Category").First(&doc).Error; err != nil {
|
||||
response.Error(c, 404, "文档不存在")
|
||||
return
|
||||
}
|
||||
response.Success(c, doc)
|
||||
}
|
||||
|
||||
func handleGetDocCategories(c *gin.Context) {
|
||||
var categories []model.DocCategory
|
||||
if err := database.DB.Find(&categories).Error; err != nil {
|
||||
response.Error(c, 500, "获取文档分类失败")
|
||||
return
|
||||
}
|
||||
response.Success(c, categories)
|
||||
}
|
||||
|
||||
func handleGetPricing(c *gin.Context) {
|
||||
var packages []model.Package
|
||||
if err := database.DB.Where("status = ?", "active").Find(&packages).Error; err != nil {
|
||||
response.Error(c, 500, "获取价格信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
var result []gin.H
|
||||
for _, pkg := range packages {
|
||||
var permission model.PackagePermission
|
||||
database.DB.Where("package_id = ?", pkg.ID).First(&permission)
|
||||
|
||||
result = append(result, gin.H{
|
||||
"id": pkg.ID,
|
||||
"name": pkg.Name,
|
||||
"name_en": pkg.NameEn,
|
||||
"price": pkg.Price,
|
||||
"currency": pkg.Currency,
|
||||
"period": pkg.Period,
|
||||
"description": pkg.Description,
|
||||
"description_en": pkg.DescriptionEn,
|
||||
"status": pkg.Status,
|
||||
"sort": pkg.Sort,
|
||||
"is_recommended": pkg.IsRecommended,
|
||||
"allow_upgrade": pkg.AllowUpgrade,
|
||||
"created_at": pkg.CreatedAt,
|
||||
"updated_at": pkg.UpdatedAt,
|
||||
"permissions": permission,
|
||||
})
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"packages": result,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetSiteInfo(c *gin.Context) {
|
||||
response.Success(c, gin.H{
|
||||
"site_name": "验证平台",
|
||||
"site_url": "http://localhost:3000",
|
||||
"description": "专业的应用验证平台",
|
||||
"contact_email": "admin@example.com",
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetContact(c *gin.Context) {
|
||||
response.Success(c, gin.H{
|
||||
"email": "admin@example.com",
|
||||
"qq": "123456789",
|
||||
"wechat": "example_wechat",
|
||||
"phone": "400-123-4567",
|
||||
})
|
||||
}
|
||||
|
||||
func handleForgotPassword(c *gin.Context) {
|
||||
var req struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
authService := service.NewAuthService()
|
||||
err := authService.ForgotPassword(req.Email)
|
||||
if err != nil {
|
||||
response.Error(c, 400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "重置链接已发送到您的邮箱",
|
||||
})
|
||||
}
|
||||
|
||||
func handleResetPassword(c *gin.Context) {
|
||||
var req struct {
|
||||
Token string `json:"token"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
authService := service.NewAuthService()
|
||||
err := authService.ResetPassword(req.Token, req.Password)
|
||||
if err != nil {
|
||||
response.Error(c, 400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "密码重置成功",
|
||||
})
|
||||
}
|
||||
|
||||
func handleSendSms(c *gin.Context) {
|
||||
var req struct {
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "验证码已发送",
|
||||
})
|
||||
}
|
||||
|
||||
func HandleGetProfile(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Error(c, 401, "未授权")
|
||||
return
|
||||
}
|
||||
|
||||
authService := service.NewAuthService()
|
||||
user, err := authService.GetProfile(userID.(uint))
|
||||
if err != nil {
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, user)
|
||||
}
|
||||
|
||||
func HandleUpdateProfile(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Error(c, 401, "未授权")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
authService := service.NewAuthService()
|
||||
err := authService.UpdateProfile(userID.(uint), req.Email, req.Phone)
|
||||
if err != nil {
|
||||
response.Error(c, 400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "资料更新成功",
|
||||
})
|
||||
}
|
||||
|
||||
func HandleChangePassword(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Error(c, 401, "未授权")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
OldPassword string `json:"old_password"`
|
||||
NewPassword string `json:"new_password"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
authService := service.NewAuthService()
|
||||
err := authService.ChangePassword(userID.(uint), req.OldPassword, req.NewPassword)
|
||||
if err != nil {
|
||||
response.Error(c, 400, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "密码修改成功",
|
||||
})
|
||||
}
|
||||
|
||||
func HandleUploadAvatar(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Error(c, 401, "未授权")
|
||||
return
|
||||
}
|
||||
|
||||
file, err := c.FormFile("avatar")
|
||||
if err != nil {
|
||||
response.Error(c, 400, "请上传文件")
|
||||
return
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("avatar_%d_%s", userID, file.Filename)
|
||||
if err := c.SaveUploadedFile(file, "uploads/"+filename); err != nil {
|
||||
response.Error(c, 500, "文件保存失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"avatar": "/uploads/" + filename,
|
||||
})
|
||||
}
|
||||
|
||||
func handleCardVerification(c *gin.Context) {
|
||||
var req struct {
|
||||
CardKey string `json:"card_key"`
|
||||
Username string `json:"username"`
|
||||
DeviceID string `json:"device_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var card model.Card
|
||||
if err := database.DB.Where("card_key = ?", req.CardKey).First(&card).Error; err != nil {
|
||||
response.Error(c, 404, "卡密不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if card.Status != "unused" {
|
||||
response.Error(c, 400, "卡密已被使用或已禁用")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "验证成功",
|
||||
"card_id": card.ID,
|
||||
"card_type": card.CardType,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetVerificationStatus(c *gin.Context) {
|
||||
cardKey := c.Query("card_key")
|
||||
if cardKey == "" {
|
||||
response.Error(c, 400, "请提供卡密")
|
||||
return
|
||||
}
|
||||
|
||||
var card model.Card
|
||||
if err := database.DB.Where("card_key = ?", cardKey).First(&card).Error; err != nil {
|
||||
response.Error(c, 404, "卡密不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"status": card.Status,
|
||||
"card_id": card.ID,
|
||||
"used_at": card.UsedAt,
|
||||
})
|
||||
}
|
||||
|
||||
func HandleCreateOrder(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Error(c, 401, "请先登录")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
PackageID uint `json:"package_id"`
|
||||
PaymentChannelID uint `json:"payment_channel_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
if req.PackageID == 0 {
|
||||
response.Error(c, 400, "请选择套餐")
|
||||
return
|
||||
}
|
||||
|
||||
var pkg model.Package
|
||||
if err := database.DB.Where("id = ? AND status = ?", req.PackageID, "active").First(&pkg).Error; err != nil {
|
||||
response.Error(c, 404, "套餐不存在或已下架")
|
||||
return
|
||||
}
|
||||
|
||||
orderNo := fmt.Sprintf("ORD%d%d", time.Now().Unix(), userID.(uint))
|
||||
|
||||
if pkg.Price == 0 {
|
||||
now := time.Now()
|
||||
pkgID := pkg.ID
|
||||
order := model.Order{
|
||||
OrderNo: orderNo,
|
||||
UserID: userID.(uint),
|
||||
PackageID: &pkgID,
|
||||
OrderType: "package",
|
||||
Title: fmt.Sprintf("领取套餐 - %s", pkg.Name),
|
||||
Amount: 0,
|
||||
PaymentType: "free",
|
||||
Status: "paid",
|
||||
PaymentAt: &now,
|
||||
Description: fmt.Sprintf("免费套餐: %s, 周期: %s", pkg.Name, pkg.Period),
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&order).Error; err != nil {
|
||||
response.Error(c, 500, "创建订单失败")
|
||||
return
|
||||
}
|
||||
|
||||
var expiredAt *time.Time
|
||||
if pkg.Period != "" && pkg.Period != "permanent" {
|
||||
duration := parsePeriod(pkg.Period)
|
||||
if duration > 0 {
|
||||
exp := now.Add(duration)
|
||||
expiredAt = &exp
|
||||
}
|
||||
}
|
||||
|
||||
userPackage := model.UserPackage{
|
||||
UserID: userID.(uint),
|
||||
PackageID: pkg.ID,
|
||||
ExpiredAt: expiredAt,
|
||||
Status: "active",
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&userPackage).Error; err != nil {
|
||||
response.Error(c, 500, "创建套餐授权失败")
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Model(&model.User{}).Where("id = ?", userID).Update("current_package_id", pkg.ID).Error; err != nil {
|
||||
response.Error(c, 500, "更新用户套餐失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"order_no": order.OrderNo,
|
||||
"order_id": order.ID,
|
||||
"amount": 0,
|
||||
"status": "paid",
|
||||
"message": "免费套餐领取成功",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.PaymentChannelID == 0 {
|
||||
response.Error(c, 400, "请选择支付方式")
|
||||
return
|
||||
}
|
||||
|
||||
var paymentChannel model.PaymentChannel
|
||||
if err := database.DB.Where("id = ? AND status = ?", req.PaymentChannelID, "active").First(&paymentChannel).Error; err != nil {
|
||||
response.Error(c, 404, "支付通道不存在或已禁用")
|
||||
return
|
||||
}
|
||||
|
||||
pkgID := pkg.ID
|
||||
order := model.Order{
|
||||
OrderNo: orderNo,
|
||||
UserID: userID.(uint),
|
||||
PackageID: &pkgID,
|
||||
OrderType: "package",
|
||||
Title: fmt.Sprintf("购买套餐 - %s", pkg.Name),
|
||||
Amount: pkg.Price,
|
||||
PaymentType: paymentChannel.Type,
|
||||
PaymentMethod: paymentChannel.Type,
|
||||
Status: "pending",
|
||||
Description: fmt.Sprintf("套餐ID: %d, 套餐名称: %s, 周期: %s, 支付通道: %s", pkg.ID, pkg.Name, pkg.Period, paymentChannel.Name),
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&order).Error; err != nil {
|
||||
response.Error(c, 500, "创建订单失败")
|
||||
return
|
||||
}
|
||||
|
||||
scheme := "http"
|
||||
if c.Request.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
host := c.Request.Host
|
||||
callbackBaseURL := fmt.Sprintf("%s://%s", scheme, host)
|
||||
|
||||
switch paymentChannel.Type {
|
||||
case "bepusdt":
|
||||
paymentService, err := payment.GetPaymentService(paymentChannel, callbackBaseURL)
|
||||
if err != nil {
|
||||
response.Error(c, 500, "支付服务初始化失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
notifyURL := fmt.Sprintf("%s/api/v1/payment/callback/bepusdt", callbackBaseURL)
|
||||
redirectURL := fmt.Sprintf("%s/developer/finance?order=%s", callbackBaseURL, orderNo)
|
||||
|
||||
result, err := paymentService.CreateOrder(orderNo, pkg.Price, notifyURL, redirectURL, order.Title)
|
||||
if err != nil {
|
||||
response.Error(c, 500, "创建支付订单失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
extraData := map[string]interface{}{
|
||||
"trade_id": result.TradeID,
|
||||
"payment_url": result.PaymentURL,
|
||||
"actual_amount": result.ActualAmount,
|
||||
"token": result.Token,
|
||||
"expiration_time": result.ExpirationTime,
|
||||
"channel_id": paymentChannel.ID,
|
||||
}
|
||||
extraJSON, _ := json.Marshal(extraData)
|
||||
order.ExtraData = string(extraJSON)
|
||||
database.DB.Save(&order)
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"order_no": order.OrderNo,
|
||||
"order_id": order.ID,
|
||||
"amount": order.Amount,
|
||||
"actual_amount": result.ActualAmount,
|
||||
"payment_url": result.PaymentURL,
|
||||
"trade_id": result.TradeID,
|
||||
"token": result.Token,
|
||||
"expiration_time": result.ExpirationTime,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"order_no": order.OrderNo,
|
||||
"order_id": order.ID,
|
||||
"amount": order.Amount,
|
||||
"pay_url": "",
|
||||
})
|
||||
}
|
||||
|
||||
func HandleGetOrder(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Error(c, 401, "请先登录")
|
||||
return
|
||||
}
|
||||
|
||||
orderNo := c.Param("order_no")
|
||||
if orderNo == "" {
|
||||
response.Error(c, 400, "订单号不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var order model.Order
|
||||
if err := database.DB.Where("order_no = ? AND user_id = ?", orderNo, userID).First(&order).Error; err != nil {
|
||||
response.Error(c, 404, "订单不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"order": gin.H{
|
||||
"id": order.ID,
|
||||
"order_no": order.OrderNo,
|
||||
"amount": order.Amount,
|
||||
"status": order.Status,
|
||||
"payment_type": order.PaymentType,
|
||||
"title": order.Title,
|
||||
"extra_data": order.ExtraData,
|
||||
"created_at": order.CreatedAt,
|
||||
"payment_at": order.PaymentAt,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func HandleGetOrderStatus(c *gin.Context) {
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
response.Error(c, 401, "请先登录")
|
||||
return
|
||||
}
|
||||
|
||||
orderNo := c.Param("order_no")
|
||||
if orderNo == "" {
|
||||
response.Error(c, 400, "订单号不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
var order model.Order
|
||||
if err := database.DB.Where("order_no = ? AND user_id = ?", orderNo, userID).First(&order).Error; err != nil {
|
||||
response.Error(c, 404, "订单不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"status": order.Status,
|
||||
"payment_at": order.PaymentAt,
|
||||
})
|
||||
}
|
||||
|
||||
func parsePeriod(period string) time.Duration {
|
||||
switch period {
|
||||
case "daily":
|
||||
return 24 * time.Hour
|
||||
case "weekly":
|
||||
return 7 * 24 * time.Hour
|
||||
case "monthly":
|
||||
return 30 * 24 * time.Hour
|
||||
case "quarterly":
|
||||
return 90 * 24 * time.Hour
|
||||
case "yearly":
|
||||
return 365 * 24 * time.Hour
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
func handleGetPublicStats(c *gin.Context) {
|
||||
var totalApps, totalUsers, totalVerifications int64
|
||||
|
||||
database.DB.Model(&model.Application{}).Count(&totalApps)
|
||||
database.DB.Model(&model.User{}).Where("role = ?", "developer").Count(&totalUsers)
|
||||
database.DB.Model(&model.Card{}).Where("status = ?", "used").Count(&totalVerifications)
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"totalApps": totalApps,
|
||||
"totalUsers": totalUsers,
|
||||
"totalVerifications": totalVerifications,
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package frontend
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
"verification-platform-backend/internal/database"
|
||||
"verification-platform-backend/internal/model"
|
||||
"verification-platform-backend/internal/service/payment"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetupPaymentCallbackRoutes(r *gin.Engine) {
|
||||
r.POST("/api/v1/payment/callback/bepusdt", handleBEpusdtCallback)
|
||||
}
|
||||
|
||||
func handleBEpusdtCallback(c *gin.Context) {
|
||||
body, err := io.ReadAll(c.Request.Body)
|
||||
if err != nil {
|
||||
c.String(200, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
var callbackData payment.CallbackData
|
||||
if err := json.Unmarshal(body, &callbackData); err != nil {
|
||||
c.String(200, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
var order model.Order
|
||||
if err := database.DB.Where("order_no = ?", callbackData.OrderID).First(&order).Error; err != nil {
|
||||
c.String(200, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
if order.Status != "pending" {
|
||||
c.String(200, "success")
|
||||
return
|
||||
}
|
||||
|
||||
var extraData struct {
|
||||
TradeID string `json:"trade_id"`
|
||||
ChannelID uint `json:"channel_id"`
|
||||
ExpirationTime string `json:"expiration_time"`
|
||||
}
|
||||
if order.ExtraData != "" {
|
||||
json.Unmarshal([]byte(order.ExtraData), &extraData)
|
||||
}
|
||||
|
||||
if extraData.ExpirationTime != "" {
|
||||
expirationTime, err := time.Parse(time.RFC3339, extraData.ExpirationTime)
|
||||
if err == nil && time.Now().After(expirationTime) {
|
||||
c.String(200, "fail")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var paymentChannel model.PaymentChannel
|
||||
if extraData.ChannelID > 0 {
|
||||
database.DB.First(&paymentChannel, extraData.ChannelID)
|
||||
}
|
||||
|
||||
if paymentChannel.ID > 0 {
|
||||
paymentService, err := payment.GetPaymentService(paymentChannel, "")
|
||||
if err == nil {
|
||||
if !paymentService.VerifyCallback(callbackData) {
|
||||
c.String(200, "fail")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if callbackData.Status == 2 {
|
||||
now := time.Now()
|
||||
order.Status = "paid"
|
||||
order.PaymentAt = &now
|
||||
order.PaymentMethod = "bepusdt"
|
||||
|
||||
if err := database.DB.Save(&order).Error; err != nil {
|
||||
c.String(200, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
if order.OrderType == "package" {
|
||||
var pkg model.Package
|
||||
if err := database.DB.First(&pkg, order.PackageID).Error; err == nil {
|
||||
var expiredAt *time.Time
|
||||
if pkg.Period != "" && pkg.Period != "permanent" {
|
||||
duration := parsePeriod(pkg.Period)
|
||||
if duration > 0 {
|
||||
exp := now.Add(duration)
|
||||
expiredAt = &exp
|
||||
}
|
||||
}
|
||||
|
||||
userPackage := model.UserPackage{
|
||||
UserID: order.UserID,
|
||||
PackageID: pkg.ID,
|
||||
ExpiredAt: expiredAt,
|
||||
Status: "active",
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&userPackage).Error; err != nil {
|
||||
fmt.Printf("创建套餐授权失败: %v\n", err)
|
||||
} else {
|
||||
database.DB.Model(&model.User{}).Where("id = ?", order.UserID).Update("current_package_id", pkg.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.String(200, "success")
|
||||
}
|
||||
Reference in New Issue
Block a user