Files
verify/backend/internal/router/frontend/frontend.go
T

806 lines
20 KiB
Go

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 = "admin"
}
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 = ?", "admin").Count(&totalUsers)
database.DB.Model(&model.Card{}).Where("status = ?", "used").Count(&totalVerifications)
response.Success(c, gin.H{
"totalApps": totalApps,
"totalUsers": totalUsers,
"totalVerifications": totalVerifications,
})
}