fix: 订阅模式登录验证、永久会员类型区分、动态代码HTTP返回值修复、侧边栏滚动位置保持
- 修复订阅模式登录时错误检查余额的问题 - 区分无限余额和永久订阅两种永久会员类型 - 修复动态代码HTTP请求返回值在JS中无法正确访问的问题 - 添加侧边栏滚动位置保持功能 - 移除developer角色相关代码,统一使用admin - 添加缺失的i18n翻译key
This commit is contained in:
@@ -331,13 +331,26 @@ func initData() {
|
||||
}
|
||||
|
||||
// 迁移:将 developer 角色统一为 admin
|
||||
var developerCount int64
|
||||
DB.Model(&model.User{}).Where("role = ?", "developer").Count(&developerCount)
|
||||
if developerCount > 0 {
|
||||
log.Printf("Migrating %d developer users to admin role...", developerCount)
|
||||
var devRoleCount int64
|
||||
DB.Model(&model.User{}).Where("role = ?", "developer").Count(&devRoleCount)
|
||||
if devRoleCount > 0 {
|
||||
log.Printf("Migrating %d developer users to admin role...", devRoleCount)
|
||||
DB.Model(&model.User{}).Where("role = ?", "developer").Update("role", "admin")
|
||||
}
|
||||
|
||||
// 迁移:将 developer_id 列名改为 admin_id
|
||||
if DB.Migrator().HasColumn(&model.AgentApplication{}, "developer_id") {
|
||||
log.Println("Migrating agent_applications.developer_id to admin_id...")
|
||||
DB.Exec("ALTER TABLE agent_applications RENAME COLUMN developer_id TO admin_id")
|
||||
}
|
||||
if DB.Migrator().HasColumn(&model.AgentApplicationRequest{}, "developer_id") {
|
||||
log.Println("Migrating agent_application_requests.developer_id to admin_id...")
|
||||
DB.Exec("ALTER TABLE agent_application_requests RENAME COLUMN developer_id TO admin_id")
|
||||
}
|
||||
|
||||
// 迁移:将 write_permission 默认值从 developer 改为 admin
|
||||
DB.Exec("UPDATE cloud_variables SET write_permission = 'admin' WHERE write_permission = 'developer'")
|
||||
|
||||
// 清理没有关联应用的云端常量和变量
|
||||
var orphanConstants int64
|
||||
DB.Model(&model.CloudConstant{}).Where("app_id IS NULL").Count(&orphanConstants)
|
||||
|
||||
@@ -114,26 +114,6 @@ func AdminAuth() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// DeveloperAuth 开发者权限中间件(管理员即开发者)
|
||||
func DeveloperAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
role, exists := c.Get("role")
|
||||
if !exists {
|
||||
response.Error(c, http.StatusForbidden, "Access denied")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if role != "admin" {
|
||||
response.Error(c, http.StatusForbidden, "Admin access required")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// AgentAuth 代理商授权中间件
|
||||
func AgentAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
@@ -365,7 +365,7 @@ type Ticket struct {
|
||||
Title string `gorm:"size:200" json:"title"`
|
||||
Content string `gorm:"type:text" json:"content"`
|
||||
Category string `gorm:"size:50" json:"category"` // account, payment, technical, feature, other
|
||||
Type string `gorm:"size:50" json:"type"` // user, agent, developer
|
||||
Type string `gorm:"size:50" json:"type"` // user, agent, admin
|
||||
Status string `gorm:"size:20;default:open" json:"status"` // open, processing, resolved, closed
|
||||
Priority string `gorm:"size:20;default:normal" json:"priority"` // low, normal, high, urgent
|
||||
AssignedTo *uint `json:"assigned_to"` // 分配给的应用开发者ID或平台管理员ID
|
||||
@@ -464,7 +464,7 @@ type CloudVariable struct {
|
||||
OriginalName string `gorm:"size:255" json:"original_name"`
|
||||
FileMD5 string `gorm:"size:32" json:"file_md5"`
|
||||
Scope string `gorm:"size:20;default:app" json:"scope"`
|
||||
WritePermission string `gorm:"size:20;default:developer" json:"write_permission"`
|
||||
WritePermission string `gorm:"size:20;default:admin" json:"write_permission"`
|
||||
Description string `gorm:"size:255" json:"description"`
|
||||
Status string `gorm:"size:20;default:active" json:"status"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
@@ -555,7 +555,7 @@ type AgentApplication struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
AgentID uint `json:"agent_id"`
|
||||
ApplicationID uint `json:"application_id"`
|
||||
DeveloperID uint `json:"developer_id"`
|
||||
AdminID uint `json:"admin_id"`
|
||||
Discount float64 `gorm:"default:1.0" json:"discount"`
|
||||
Status string `gorm:"size:20;default:active" json:"status"`
|
||||
IsReceived bool `gorm:"default:false" json:"is_received"`
|
||||
@@ -565,7 +565,7 @@ type AgentApplication struct {
|
||||
|
||||
Agent User `gorm:"foreignKey:AgentID;references:ID" json:"agent,omitempty"`
|
||||
Application Application `gorm:"foreignKey:ApplicationID;references:ID" json:"application,omitempty"`
|
||||
Developer User `gorm:"foreignKey:DeveloperID;references:ID" json:"developer,omitempty"`
|
||||
Admin User `gorm:"foreignKey:AdminID;references:ID" json:"admin,omitempty"`
|
||||
CardTypes []AgentApplicationCardType `gorm:"foreignKey:AgentApplicationID" json:"card_types,omitempty"`
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ type AgentApplicationCardType struct {
|
||||
type AgentApplicationRequest struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
AgentID uint `json:"agent_id"`
|
||||
DeveloperID uint `json:"developer_id"`
|
||||
AdminID uint `json:"admin_id"`
|
||||
ApplicationID uint `json:"application_id"`
|
||||
Type string `gorm:"size:20" json:"type"`
|
||||
Status string `gorm:"size:20;default:pending" json:"status"`
|
||||
@@ -597,7 +597,7 @@ type AgentApplicationRequest struct {
|
||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||
|
||||
Agent User `gorm:"foreignKey:AgentID" json:"agent,omitempty"`
|
||||
Developer User `gorm:"foreignKey:DeveloperID" json:"developer,omitempty"`
|
||||
Admin User `gorm:"foreignKey:AdminID" json:"admin,omitempty"`
|
||||
Application Application `gorm:"foreignKey:ApplicationID" json:"application,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
+36
-36
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -66,10 +66,10 @@ func checkAgentPermission(userID uint) bool {
|
||||
|
||||
func handleGetAgentApps(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
log.Printf("[DEBUG] handleGetAgentApps called for developer %d\n", userID)
|
||||
log.Printf("[DEBUG] handleGetAgentApps called for Admin %d\n", userID)
|
||||
|
||||
var myAuthorizations []model.AgentApplication
|
||||
if err := database.DB.Where("developer_id = ?", userID).
|
||||
if err := database.DB.Where("admin_id = ?", userID).
|
||||
Preload("CardTypes.CardType").
|
||||
Find(&myAuthorizations).Error; err != nil {
|
||||
response.Error(c, 500, "获取授权列表失败")
|
||||
@@ -84,7 +84,7 @@ func handleGetAgentApps(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("[DEBUG] Found %d my authorizations and %d received authorizations for developer %d\n",
|
||||
log.Printf("[DEBUG] Found %d my authorizations and %d received authorizations for Admin %d\n",
|
||||
len(myAuthorizations), len(receivedAuthorizations), userID)
|
||||
|
||||
var allAgentApps []model.AgentApplication
|
||||
@@ -92,17 +92,17 @@ func handleGetAgentApps(c *gin.Context) {
|
||||
allAgentApps = append(allAgentApps, receivedAuthorizations...)
|
||||
|
||||
var agentIDs []uint
|
||||
var developerIDs []uint
|
||||
var AdminIDs []uint
|
||||
var applicationIDs []uint
|
||||
|
||||
for _, aa := range allAgentApps {
|
||||
agentIDs = append(agentIDs, aa.AgentID)
|
||||
developerIDs = append(developerIDs, aa.DeveloperID)
|
||||
AdminIDs = append(AdminIDs, aa.AdminID)
|
||||
applicationIDs = append(applicationIDs, aa.ApplicationID)
|
||||
}
|
||||
|
||||
var users []model.User
|
||||
if err := database.DB.Where("id IN ?", append(agentIDs, developerIDs...)).Find(&users).Error; err != nil {
|
||||
if err := database.DB.Where("id IN ?", append(agentIDs, AdminIDs...)).Find(&users).Error; err != nil {
|
||||
log.Printf("[ERROR] Failed to query users: %v\n", err)
|
||||
} else {
|
||||
log.Printf("[DEBUG] Found %d users\n", len(users))
|
||||
@@ -199,7 +199,7 @@ func handleGetAgentRequests(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
requestType := c.Query("type")
|
||||
|
||||
query := database.DB.Where("developer_id = ?", userID)
|
||||
query := database.DB.Where("admin_id = ?", userID)
|
||||
if requestType == "invite" {
|
||||
query = query.Where("type = ?", "invite")
|
||||
} else if requestType == "request" {
|
||||
@@ -221,7 +221,7 @@ func handleGetAgentRequests(c *gin.Context) {
|
||||
AgentID uint `json:"agent_id"`
|
||||
AgentName string `json:"agent_name"`
|
||||
AgentEmail string `json:"agent_email"`
|
||||
DeveloperID uint `json:"developer_id"`
|
||||
AdminID uint `json:"admin_id"`
|
||||
ApplicationID uint `json:"application_id"`
|
||||
AppName string `json:"app_name"`
|
||||
Type string `json:"type"`
|
||||
@@ -252,7 +252,7 @@ func handleGetAgentRequests(c *gin.Context) {
|
||||
AgentID: req.AgentID,
|
||||
AgentName: agentName,
|
||||
AgentEmail: agentEmail,
|
||||
DeveloperID: req.DeveloperID,
|
||||
AdminID: req.AdminID,
|
||||
ApplicationID: req.ApplicationID,
|
||||
AppName: appName,
|
||||
Type: req.Type,
|
||||
@@ -282,7 +282,7 @@ func handleGetMyRequests(c *gin.Context) {
|
||||
|
||||
var requests []model.AgentApplicationRequest
|
||||
if err := query.
|
||||
Preload("Developer").
|
||||
Preload("Admin").
|
||||
Preload("Application").
|
||||
Order("created_at DESC").
|
||||
Find(&requests).Error; err != nil {
|
||||
@@ -293,8 +293,8 @@ func handleGetMyRequests(c *gin.Context) {
|
||||
type RequestResponse struct {
|
||||
ID uint `json:"id"`
|
||||
AgentID uint `json:"agent_id"`
|
||||
DeveloperID uint `json:"developer_id"`
|
||||
DeveloperName string `json:"developer_name"`
|
||||
AdminID uint `json:"admin_id"`
|
||||
AdminName string `json:"admin_name"`
|
||||
ApplicationID uint `json:"application_id"`
|
||||
AppName string `json:"app_name"`
|
||||
Type string `json:"type"`
|
||||
@@ -306,9 +306,9 @@ func handleGetMyRequests(c *gin.Context) {
|
||||
|
||||
var result []RequestResponse
|
||||
for _, req := range requests {
|
||||
developerName := ""
|
||||
if req.Developer.ID != 0 {
|
||||
developerName = req.Developer.Username
|
||||
AdminName := ""
|
||||
if req.Admin.ID != 0 {
|
||||
AdminName = req.Admin.Username
|
||||
}
|
||||
|
||||
appName := ""
|
||||
@@ -319,8 +319,8 @@ func handleGetMyRequests(c *gin.Context) {
|
||||
result = append(result, RequestResponse{
|
||||
ID: req.ID,
|
||||
AgentID: req.AgentID,
|
||||
DeveloperID: req.DeveloperID,
|
||||
DeveloperName: developerName,
|
||||
AdminID: req.AdminID,
|
||||
AdminName: AdminName,
|
||||
ApplicationID: req.ApplicationID,
|
||||
AppName: appName,
|
||||
Type: req.Type,
|
||||
@@ -379,7 +379,7 @@ func handleInviteAgent(c *gin.Context) {
|
||||
|
||||
request := model.AgentApplicationRequest{
|
||||
AgentID: req.AgentID,
|
||||
DeveloperID: userID,
|
||||
AdminID: userID,
|
||||
ApplicationID: req.ApplicationID,
|
||||
Type: "invite",
|
||||
Status: "pending",
|
||||
@@ -405,7 +405,7 @@ func handleInviteAgent(c *gin.Context) {
|
||||
func handleRequestAuthorization(c *gin.Context) {
|
||||
userID := c.GetUint("user_id")
|
||||
var req struct {
|
||||
DeveloperID uint `json:"developer_id"`
|
||||
AdminID uint `json:"admin_id"`
|
||||
ApplicationID uint `json:"application_id"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
@@ -414,18 +414,18 @@ func handleRequestAuthorization(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var developer model.User
|
||||
if err := database.DB.First(&developer, req.DeveloperID).Error; err != nil {
|
||||
var Admin model.User
|
||||
if err := database.DB.First(&Admin, req.AdminID).Error; err != nil {
|
||||
response.Error(c, 404, "开发者不存在")
|
||||
return
|
||||
}
|
||||
if developer.Role != "admin" {
|
||||
if Admin.Role != "admin" {
|
||||
response.Error(c, 400, "该用户不是管理员")
|
||||
return
|
||||
}
|
||||
|
||||
var app model.Application
|
||||
if err := database.DB.Where("id = ? AND user_id = ?", req.ApplicationID, req.DeveloperID).First(&app).Error; err != nil {
|
||||
if err := database.DB.Where("id = ? AND user_id = ?", req.ApplicationID, req.AdminID).First(&app).Error; err != nil {
|
||||
response.Error(c, 404, "应用不存在")
|
||||
return
|
||||
}
|
||||
@@ -437,15 +437,15 @@ func handleRequestAuthorization(c *gin.Context) {
|
||||
}
|
||||
|
||||
var existingRequest model.AgentApplicationRequest
|
||||
if err := database.DB.Where("agent_id = ? AND developer_id = ? AND application_id = ? AND status = ?",
|
||||
userID, req.DeveloperID, req.ApplicationID, "pending").First(&existingRequest).Error; err == nil {
|
||||
if err := database.DB.Where("agent_id = ? AND admin_id = ? AND application_id = ? AND status = ?",
|
||||
userID, req.AdminID, req.ApplicationID, "pending").First(&existingRequest).Error; err == nil {
|
||||
response.Error(c, 400, "您已有待处理的申请")
|
||||
return
|
||||
}
|
||||
|
||||
request := model.AgentApplicationRequest{
|
||||
AgentID: userID,
|
||||
DeveloperID: req.DeveloperID,
|
||||
AdminID: req.AdminID,
|
||||
ApplicationID: req.ApplicationID,
|
||||
Type: "request",
|
||||
Status: "pending",
|
||||
@@ -459,8 +459,8 @@ func handleRequestAuthorization(c *gin.Context) {
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"id": request.ID,
|
||||
"developer_id": request.DeveloperID,
|
||||
"developer_name": developer.Username,
|
||||
"admin_id": request.AdminID,
|
||||
"admin_name": Admin.Username,
|
||||
"app_id": request.ApplicationID,
|
||||
"app_name": app.Name,
|
||||
"type": request.Type,
|
||||
@@ -478,7 +478,7 @@ func handleApproveRequest(c *gin.Context) {
|
||||
}
|
||||
|
||||
var req model.AgentApplicationRequest
|
||||
if err := database.DB.Where("id = ? AND developer_id = ?", requestID, userID).First(&req).Error; err != nil {
|
||||
if err := database.DB.Where("id = ? AND admin_id = ?", requestID, userID).First(&req).Error; err != nil {
|
||||
response.Error(c, 404, "申请不存在")
|
||||
return
|
||||
}
|
||||
@@ -493,7 +493,7 @@ func handleApproveRequest(c *gin.Context) {
|
||||
agentApp := model.AgentApplication{
|
||||
AgentID: req.AgentID,
|
||||
ApplicationID: req.ApplicationID,
|
||||
DeveloperID: userID,
|
||||
AdminID: userID,
|
||||
Discount: 1.0,
|
||||
Status: "active",
|
||||
IsReceived: true,
|
||||
@@ -544,7 +544,7 @@ func handleRejectRequest(c *gin.Context) {
|
||||
}
|
||||
|
||||
var req model.AgentApplicationRequest
|
||||
if err := database.DB.Where("id = ? AND developer_id = ?", requestID, userID).First(&req).Error; err != nil {
|
||||
if err := database.DB.Where("id = ? AND admin_id = ?", requestID, userID).First(&req).Error; err != nil {
|
||||
response.Error(c, 404, "申请不存在")
|
||||
return
|
||||
}
|
||||
@@ -572,7 +572,7 @@ func handleGetAgentAppDetail(c *gin.Context) {
|
||||
agentAppID := c.Param("id")
|
||||
|
||||
var agentApp model.AgentApplication
|
||||
if err := database.DB.Where("id = ? AND developer_id = ?", agentAppID, userID).
|
||||
if err := database.DB.Where("id = ? AND admin_id = ?", agentAppID, userID).
|
||||
Preload("CardTypes.CardType").
|
||||
First(&agentApp).Error; err != nil {
|
||||
response.Error(c, 404, "授权记录不存在")
|
||||
@@ -652,7 +652,7 @@ func handleUpdateAgentApp(c *gin.Context) {
|
||||
}
|
||||
|
||||
var agentApp model.AgentApplication
|
||||
if err := database.DB.Where("id = ? AND developer_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||
if err := database.DB.Where("id = ? AND admin_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||
response.Error(c, 404, "授权记录不存在")
|
||||
return
|
||||
}
|
||||
@@ -693,7 +693,7 @@ func handleUpdateAgentCardTypes(c *gin.Context) {
|
||||
}
|
||||
|
||||
var agentApp model.AgentApplication
|
||||
if err := database.DB.Where("id = ? AND developer_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||
if err := database.DB.Where("id = ? AND admin_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||
response.Error(c, 404, "授权记录不存在")
|
||||
return
|
||||
}
|
||||
@@ -731,7 +731,7 @@ func handleRemoveAgentApp(c *gin.Context) {
|
||||
agentAppID := c.Param("id")
|
||||
|
||||
var agentApp model.AgentApplication
|
||||
if err := database.DB.Where("id = ? AND developer_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||
if err := database.DB.Where("id = ? AND admin_id = ?", agentAppID, userID).First(&agentApp).Error; err != nil {
|
||||
response.Error(c, 404, "授权记录不存在")
|
||||
return
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -48,9 +48,9 @@ func SetupCardRoutesWithoutPackage(r *gin.RouterGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
func checkDeveloperPackageValid(developerID uint) bool {
|
||||
func checkAdminPackageValid(AdminID uint) bool {
|
||||
var user model.User
|
||||
if err := database.DB.First(&user, developerID).Error; err != nil {
|
||||
if err := database.DB.First(&user, AdminID).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func checkDeveloperPackageValid(developerID uint) bool {
|
||||
|
||||
var userPackage model.UserPackage
|
||||
if err := database.DB.Where("user_id = ? AND package_id = ? AND status = ?",
|
||||
developerID, user.CurrentPackageID, "active").First(&userPackage).Error; err != nil {
|
||||
AdminID, user.CurrentPackageID, "active").First(&userPackage).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -678,7 +678,7 @@ func handleBatchGenerateCards(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("[DEBUG] AgentApp found - ID: %d, DeveloperID: %d\n", agentApp.ID, agentApp.DeveloperID)
|
||||
fmt.Printf("[DEBUG] AgentApp found - ID: %d, AdminID: %d\n", agentApp.ID, agentApp.AdminID)
|
||||
|
||||
var cardTypePerm *model.AgentApplicationCardType
|
||||
for _, ct := range agentApp.CardTypes {
|
||||
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"time"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"log"
|
||||
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package developer
|
||||
package admin
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
@@ -116,13 +116,24 @@ func handleAppHeartbeat(c *gin.Context) {
|
||||
}
|
||||
|
||||
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)
|
||||
if user.Balance == -1 {
|
||||
log.Printf("[DEBUG] User %d is permanent member, skip deduction", user.ID)
|
||||
} else if app.BillingType == "subscription" {
|
||||
if user.ExpiryAt == nil || user.ExpiryAt.Before(now) {
|
||||
log.Printf("[DEBUG] User %d subscription expired, ExpiryAt=%v", user.ID, user.ExpiryAt)
|
||||
response.Error(c, 403, "订阅已过期")
|
||||
return
|
||||
}
|
||||
log.Printf("[DEBUG] User %d subscription valid, skip balance deduction", user.ID)
|
||||
} else {
|
||||
log.Printf("[DEBUG] User %d has insufficient balance: %.2f < %.2f", user.ID, user.Balance, app.DeductionAmount)
|
||||
response.Error(c, 403, "余额不足")
|
||||
return
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,11 +456,24 @@ func handleAppLogin(c *gin.Context) {
|
||||
log.Printf("[DEBUG] isTrialValid=%v, IsTrialUser=%v, TrialEndAt=%v, Balance=%f", isTrialValid, user.IsTrialUser, user.TrialEndAt, user.Balance)
|
||||
|
||||
if !isTrialValid {
|
||||
if appModel.BillingType != "free" && user.Balance <= 0 {
|
||||
log.Printf("[DEBUG] User %d has no balance remaining", user.ID)
|
||||
service.LogVerification(c, &appModel.ID, &user.ID, "login_failed", "登录失败: 余额不足 - "+req.Username, req.DeviceID, fmt.Errorf("余额不足,请充值后继续使用"))
|
||||
response.Error(c, 403, "余额不足,请充值后继续使用")
|
||||
return
|
||||
if appModel.BillingType != "free" {
|
||||
if user.Balance == -1 {
|
||||
log.Printf("[DEBUG] User %d is permanent member, allowing login", user.ID)
|
||||
} else if appModel.BillingType == "subscription" {
|
||||
if user.ExpiryAt == nil || user.ExpiryAt.Before(time.Now()) {
|
||||
log.Printf("[DEBUG] User %d subscription expired, ExpiryAt=%v", user.ID, user.ExpiryAt)
|
||||
service.LogVerification(c, &appModel.ID, &user.ID, "login_failed", "登录失败: 订阅已过期 - "+req.Username, req.DeviceID, fmt.Errorf("订阅已过期,请充值后继续使用"))
|
||||
response.Error(c, 403, "订阅已过期,请充值后继续使用")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if user.Balance <= 0 {
|
||||
log.Printf("[DEBUG] User %d has no balance remaining", user.ID)
|
||||
service.LogVerification(c, &appModel.ID, &user.ID, "login_failed", "登录失败: 余额不足 - "+req.Username, req.DeviceID, fmt.Errorf("余额不足,请充值后继续使用"))
|
||||
response.Error(c, 403, "余额不足,请充值后继续使用")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -549,18 +549,18 @@ func handleAppUploadVariableBinary(c *gin.Context) {
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var developer model.User
|
||||
if err := database.DB.First(&developer, app.UserID).Error; err != nil {
|
||||
response.Error(c, 500, "获取开发者信息失败")
|
||||
var adminUser model.User
|
||||
if err := database.DB.First(&adminUser, app.UserID).Error; err != nil {
|
||||
response.Error(c, 500, "获取管理员信息失败")
|
||||
return
|
||||
}
|
||||
|
||||
if developer.CurrentPackageID != nil {
|
||||
if adminUser.CurrentPackageID != nil {
|
||||
var permission model.PackagePermission
|
||||
if err := database.DB.Where("package_id = ?", developer.CurrentPackageID).First(&permission).Error; err == nil {
|
||||
if err := database.DB.Where("package_id = ?", adminUser.CurrentPackageID).First(&permission).Error; err == nil {
|
||||
maxStorageBytes := int64(permission.MaxStorage) * 1024 * 1024
|
||||
if developer.StorageUsed+header.Size > maxStorageBytes {
|
||||
usedMB := float64(developer.StorageUsed) / 1024 / 1024
|
||||
if adminUser.StorageUsed+header.Size > maxStorageBytes {
|
||||
usedMB := float64(adminUser.StorageUsed) / 1024 / 1024
|
||||
maxMB := float64(permission.MaxStorage)
|
||||
response.Error(c, 403, fmt.Sprintf("存储空间不足,已使用 %.2f MB / %.2f MB", usedMB, maxMB))
|
||||
return
|
||||
|
||||
@@ -250,19 +250,35 @@ func handleExecuteDynamicCode(c *gin.Context) {
|
||||
|
||||
httpClient := httputil.NewHTTPClient(10 * time.Second)
|
||||
httpObj := map[string]interface{}{
|
||||
"get": func(url string, headers map[string]interface{}) *httputil.HTTPResponse {
|
||||
"get": func(url string, headers map[string]interface{}) map[string]interface{} {
|
||||
convertedHeaders := make(map[string]string)
|
||||
for k, v := range headers {
|
||||
convertedHeaders[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
return httpClient.Get(url, convertedHeaders)
|
||||
resp := httpClient.Get(url, convertedHeaders)
|
||||
return map[string]interface{}{
|
||||
"statusCode": resp.StatusCode,
|
||||
"status": resp.Status,
|
||||
"headers": resp.Headers,
|
||||
"body": resp.Body,
|
||||
"json": resp.JSON,
|
||||
"error": resp.Error,
|
||||
}
|
||||
},
|
||||
"post": func(url string, headers map[string]interface{}, body interface{}) *httputil.HTTPResponse {
|
||||
"post": func(url string, headers map[string]interface{}, body interface{}) map[string]interface{} {
|
||||
convertedHeaders := make(map[string]string)
|
||||
for k, v := range headers {
|
||||
convertedHeaders[k] = fmt.Sprintf("%v", v)
|
||||
}
|
||||
return httpClient.Post(url, convertedHeaders, body)
|
||||
resp := httpClient.Post(url, convertedHeaders, body)
|
||||
return map[string]interface{}{
|
||||
"statusCode": resp.StatusCode,
|
||||
"status": resp.Status,
|
||||
"headers": resp.Headers,
|
||||
"body": resp.Body,
|
||||
"json": resp.JSON,
|
||||
"error": resp.Error,
|
||||
}
|
||||
},
|
||||
}
|
||||
if err := vm.Set("http", httpObj); err != nil {
|
||||
|
||||
@@ -91,8 +91,14 @@ func handleAppRecharge(c *gin.Context) {
|
||||
user.IsTrialUser = false
|
||||
|
||||
if card.CardType.Value == -1 {
|
||||
user.Balance = -1
|
||||
user.ExpiryAt = nil
|
||||
if card.CardType.RechargeType == "subscription" {
|
||||
permanentExpiry := time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC)
|
||||
user.ExpiryAt = &permanentExpiry
|
||||
user.Balance = -1
|
||||
} else {
|
||||
user.Balance = -1
|
||||
user.ExpiryAt = nil
|
||||
}
|
||||
} else {
|
||||
switch card.CardType.RechargeType {
|
||||
case "subscription":
|
||||
|
||||
@@ -673,7 +673,7 @@ func HandleCreateOrder(c *gin.Context) {
|
||||
}
|
||||
|
||||
notifyURL := fmt.Sprintf("%s/api/v1/payment/callback/bepusdt", callbackBaseURL)
|
||||
redirectURL := fmt.Sprintf("%s/developer/finance?order=%s", callbackBaseURL, orderNo)
|
||||
redirectURL := fmt.Sprintf("%s/admin/finance?order=%s", callbackBaseURL, orderNo)
|
||||
|
||||
result, err := paymentService.CreateOrder(orderNo, pkg.Price, notifyURL, redirectURL, order.Title)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"verification-platform-backend/internal/middleware"
|
||||
"verification-platform-backend/internal/router/agent"
|
||||
"verification-platform-backend/internal/router/app"
|
||||
"verification-platform-backend/internal/router/developer"
|
||||
"verification-platform-backend/internal/router/admin"
|
||||
"verification-platform-backend/internal/router/extension"
|
||||
"verification-platform-backend/internal/router/frontend"
|
||||
"verification-platform-backend/pkg/response"
|
||||
@@ -32,9 +32,9 @@ func SetupRoutes(r *gin.Engine) {
|
||||
devGroup := api.Group("/dev")
|
||||
{
|
||||
devGroup.Use(middleware.JWT())
|
||||
devGroup.Use(middleware.DeveloperAuth())
|
||||
developer.SetupRoutes(devGroup)
|
||||
developer.SetupRoutesWithoutPackage(devGroup)
|
||||
devGroup.Use(middleware.AdminAuth())
|
||||
admin.SetupRoutes(devGroup)
|
||||
admin.SetupRoutesWithoutPackage(devGroup)
|
||||
}
|
||||
|
||||
agentGroup := api.Group("/agent")
|
||||
|
||||
@@ -32,9 +32,14 @@ func NewHTTPClient(timeout time.Duration) *HTTPClient {
|
||||
timeout = DefaultTimeout
|
||||
}
|
||||
|
||||
proxyURL, _ := url.Parse("http://127.0.0.1:10809")
|
||||
|
||||
return &HTTPClient{
|
||||
client: &http.Client{
|
||||
Timeout: timeout,
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyURL(proxyURL),
|
||||
},
|
||||
},
|
||||
timeout: timeout,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -145,7 +145,7 @@ func main() {
|
||||
},
|
||||
"如何获取API密钥?": {
|
||||
TitleEn: "How to Get API Key?",
|
||||
ContentEn: "# How to Get API Key?\n\n## Steps\n\n### 1. Register Account\nFirst, register a developer account on the platform.\n\n### 2. Create Application\n1. Go to Console > Applications\n2. Click \"Create Application\" button\n3. Fill in application name and description\n4. Click \"Create\"\n\n### 3. Get App Key\nAfter creating the application, you can find the App Key on the application details page.\n\n## App Key Format\nApp Key is a unique identifier in the format:\n```\napp_xxxxxxxxxxxxxxxx\n```\n\n## Security Notes\n- Do not share your App Key publicly\n- Regenerate App Key if it's compromised\n- Use environment variables to store App Key in production",
|
||||
ContentEn: "# How to Get API Key?\n\n## Steps\n\n### 1. Register Account\nFirst, register a admin account on the platform.\n\n### 2. Create Application\n1. Go to Console > Applications\n2. Click \"Create Application\" button\n3. Fill in application name and description\n4. Click \"Create\"\n\n### 3. Get App Key\nAfter creating the application, you can find the App Key on the application details page.\n\n## App Key Format\nApp Key is a unique identifier in the format:\n```\napp_xxxxxxxxxxxxxxxx\n```\n\n## Security Notes\n- Do not share your App Key publicly\n- Regenerate App Key if it's compromised\n- Use environment variables to store App Key in production",
|
||||
},
|
||||
"卡密验证失败怎么办?": {
|
||||
TitleEn: "What to Do When Card Key Verification Fails?",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -18,15 +18,15 @@ func main() {
|
||||
}{
|
||||
"免费版": {
|
||||
NameEn: "Free",
|
||||
DescriptionEn: "Basic features for individual developers",
|
||||
DescriptionEn: "Basic features for individual admins",
|
||||
},
|
||||
"公益版": {
|
||||
NameEn: "Community",
|
||||
DescriptionEn: "Free for individual developers",
|
||||
DescriptionEn: "Free for individual admins",
|
||||
},
|
||||
"专业版": {
|
||||
NameEn: "Professional",
|
||||
DescriptionEn: "Advanced features for professional developers",
|
||||
DescriptionEn: "Advanced features for professional admins",
|
||||
},
|
||||
"企业版": {
|
||||
NameEn: "Enterprise",
|
||||
@@ -46,7 +46,7 @@ func main() {
|
||||
},
|
||||
"开发者版": {
|
||||
NameEn: "Developer",
|
||||
DescriptionEn: "Perfect for individual developers",
|
||||
DescriptionEn: "Perfect for individual admins",
|
||||
},
|
||||
"团队版": {
|
||||
NameEn: "Team",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -131,7 +131,7 @@ func main() {
|
||||
},
|
||||
"faq-api-key": {
|
||||
TitleEn: "How to Get API Key?",
|
||||
ContentEn: "# How to Get API Key?\n\n## Steps\n\n1. Login to developer dashboard\n2. Go to \"Application Management\" page\n3. Create new application or select existing one\n4. In application details page you can see:\n - **AppID**: Application unique identifier\n - **AppKey**: Application key\n - **SecretKey**: Encryption key\n\n## Notes\n\n- AppKey is only shown once when created, please save it in time\n- Click \"Reset Key\" button to reset AppKey if needed\n- Old key becomes invalid immediately after reset\n- SecretKey is for server-side response verification, do not use it on client side",
|
||||
ContentEn: "# How to Get API Key?\n\n## Steps\n\n1. Login to admin dashboard\n2. Go to \"Application Management\" page\n3. Create new application or select existing one\n4. In application details page you can see:\n - **AppID**: Application unique identifier\n - **AppKey**: Application key\n - **SecretKey**: Encryption key\n\n## Notes\n\n- AppKey is only shown once when created, please save it in time\n- Click \"Reset Key\" button to reset AppKey if needed\n- Old key becomes invalid immediately after reset\n- SecretKey is for server-side response verification, do not use it on client side",
|
||||
SummaryEn: "Detailed steps to get API key",
|
||||
},
|
||||
"faq-card-fail": {
|
||||
@@ -141,7 +141,7 @@ func main() {
|
||||
},
|
||||
"faq-agent": {
|
||||
TitleEn: "How to Implement Agent Authorization?",
|
||||
ContentEn: "# How to Implement Agent Authorization?\n\n## What is Agent Authorization?\n\nAgent authorization allows developers to authorize their applications to other developers, who can then generate card keys and sell them.\n\n## Authorization Process\n\n1. **Apply for Authorization**\n - Authorized party submits application to authorizer\n - Enter application ID\n - Wait for authorizer approval\n\n2. **Approve Authorization**\n - Authorizer views application\n - Set card key type permissions after approval\n - Set agent discount\n\n3. **Generate Card Keys**\n - Authorized party selects authorized application\n - Select card key types with permission\n - Generate and sell card keys\n\n4. **Settle Revenue**\n - Sales revenue is settled proportionally\n - Authorized party receives income\n\n## Permission Management\n\n- Authorizer can modify card key type permissions at any time\n- Can pause or terminate authorization\n- Can view agent's sales data",
|
||||
ContentEn: "# How to Implement Agent Authorization?\n\n## What is Agent Authorization?\n\nAgent authorization allows admins to authorize their applications to other admins, who can then generate card keys and sell them.\n\n## Authorization Process\n\n1. **Apply for Authorization**\n - Authorized party submits application to authorizer\n - Enter application ID\n - Wait for authorizer approval\n\n2. **Approve Authorization**\n - Authorizer views application\n - Set card key type permissions after approval\n - Set agent discount\n\n3. **Generate Card Keys**\n - Authorized party selects authorized application\n - Select card key types with permission\n - Generate and sell card keys\n\n4. **Settle Revenue**\n - Sales revenue is settled proportionally\n - Authorized party receives income\n\n## Permission Management\n\n- Authorizer can modify card key type permissions at any time\n- Can pause or terminate authorization\n- Can view agent's sales data",
|
||||
SummaryEn: "Implementation process and permission management for agent authorization",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
Reference in New Issue
Block a user