Initial commit: 网络验证平台
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
"verification-platform-backend/internal/database"
|
||||
"verification-platform-backend/internal/model"
|
||||
"verification-platform-backend/internal/service"
|
||||
"verification-platform-backend/pkg/response"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func SetupAccountRoutes(r *gin.RouterGroup) {
|
||||
r.GET("/account", handleAppGetAccount)
|
||||
r.POST("/heartbeat", handleAppHeartbeat)
|
||||
}
|
||||
|
||||
func handleAppGetAccount(c *gin.Context) {
|
||||
appKey := c.Param("appKey")
|
||||
var app model.Application
|
||||
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
||||
response.Error(c, 404, "应用不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if service.GetApplicationDisabledStatus(app.ID) {
|
||||
response.Error(c, 403, "该应用已被禁用")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
UserID uint `json:"user_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.First(&user, req.UserID).Error; err != nil {
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"user_id": user.ID,
|
||||
"username": user.Username,
|
||||
"balance": user.Balance,
|
||||
"status": user.Status,
|
||||
})
|
||||
}
|
||||
|
||||
func handleAppHeartbeat(c *gin.Context) {
|
||||
appKey := c.Param("appKey")
|
||||
var app model.Application
|
||||
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
||||
response.Error(c, 404, "应用不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if service.GetApplicationDisabledStatus(app.ID) {
|
||||
response.Error(c, 403, "该应用已被禁用")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
UserID uint `json:"user_id"`
|
||||
DeviceID string `json:"device_id"`
|
||||
InstanceID string `json:"instance_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.First(&user, req.UserID).Error; err != nil {
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if blocked, reason := checkRiskControl(c, app.ID, req.DeviceID, user.Username); blocked {
|
||||
log.Printf("[DEBUG] Heartbeat blocked by risk control: %s", reason)
|
||||
response.Error(c, 403, reason)
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
user.LastHeartbeatAt = &now
|
||||
|
||||
if app.BillingType != "free" && app.DeductionMode == "auto" && app.DeductionAmount > 0 {
|
||||
shouldDeduct := false
|
||||
|
||||
switch app.DeductionType {
|
||||
case "timer":
|
||||
if user.LastHeartbeatAt != nil {
|
||||
elapsed := now.Sub(*user.LastHeartbeatAt)
|
||||
var interval time.Duration
|
||||
switch app.DeductionUnit {
|
||||
case "minute":
|
||||
interval = time.Duration(app.DeductionInterval) * time.Minute
|
||||
case "hour":
|
||||
interval = time.Duration(app.DeductionInterval) * time.Hour
|
||||
case "day":
|
||||
interval = time.Duration(app.DeductionInterval) * 24 * time.Hour
|
||||
default:
|
||||
interval = time.Duration(app.DeductionInterval) * time.Minute
|
||||
}
|
||||
if elapsed >= interval {
|
||||
shouldDeduct = true
|
||||
}
|
||||
}
|
||||
case "login":
|
||||
shouldDeduct = true
|
||||
}
|
||||
|
||||
if shouldDeduct {
|
||||
if user.Balance >= app.DeductionAmount {
|
||||
user.Balance -= app.DeductionAmount
|
||||
log.Printf("[DEBUG] Deducted %.2f from user %d, new balance: %.2f", app.DeductionAmount, user.ID, user.Balance)
|
||||
} else {
|
||||
log.Printf("[DEBUG] User %d has insufficient balance: %.2f < %.2f", user.ID, user.Balance, app.DeductionAmount)
|
||||
response.Error(c, 403, "余额不足")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := database.DB.Save(&user).Error; err != nil {
|
||||
response.Error(c, 500, "更新心跳失败")
|
||||
return
|
||||
}
|
||||
|
||||
if req.DeviceID != "" {
|
||||
var device model.UserDevice
|
||||
err := database.DB.Where("user_id = ? AND application_id = ? AND device_id = ?", user.ID, app.ID, req.DeviceID).First(&device).Error
|
||||
|
||||
if err != nil {
|
||||
device = model.UserDevice{
|
||||
UserID: user.ID,
|
||||
ApplicationID: app.ID,
|
||||
DeviceID: req.DeviceID,
|
||||
DeviceName: req.DeviceID,
|
||||
Status: "active",
|
||||
}
|
||||
if err := database.DB.Create(&device).Error; err != nil {
|
||||
log.Printf("[DEBUG] Failed to create device: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
instanceID := req.InstanceID
|
||||
if instanceID == "" {
|
||||
instanceID = req.DeviceID
|
||||
}
|
||||
|
||||
if device.ID > 0 {
|
||||
var session model.DeviceSession
|
||||
sessionErr := database.DB.Where("device_id = ? AND instance_id = ?", device.ID, instanceID).First(&session).Error
|
||||
|
||||
if sessionErr != nil {
|
||||
session = model.DeviceSession{
|
||||
DeviceID: device.ID,
|
||||
UserID: user.ID,
|
||||
ApplicationID: app.ID,
|
||||
InstanceID: instanceID,
|
||||
LastHeartbeat: &now,
|
||||
}
|
||||
if err := database.DB.Create(&session).Error; err != nil {
|
||||
log.Printf("[DEBUG] Failed to create session: %v", err)
|
||||
}
|
||||
} else {
|
||||
session.LastHeartbeat = &now
|
||||
if err := database.DB.Save(&session).Error; err != nil {
|
||||
log.Printf("[DEBUG] Failed to update session: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.SuccessWithMessage(c, "心跳成功", gin.H{
|
||||
"message": "心跳成功",
|
||||
"balance": user.Balance,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user