diff --git a/backend/internal/database/database.go b/backend/internal/database/database.go index 6236ba6..21f294d 100644 --- a/backend/internal/database/database.go +++ b/backend/internal/database/database.go @@ -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) diff --git a/backend/internal/middleware/middleware.go b/backend/internal/middleware/middleware.go index fb5c062..94580a0 100644 --- a/backend/internal/middleware/middleware.go +++ b/backend/internal/middleware/middleware.go @@ -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) { diff --git a/backend/internal/model/models.go b/backend/internal/model/models.go index 89aa970..a22b901 100644 --- a/backend/internal/model/models.go +++ b/backend/internal/model/models.go @@ -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"` } diff --git a/backend/internal/router/developer/agent_apps.go b/backend/internal/router/admin/agent_apps.go similarity index 90% rename from backend/internal/router/developer/agent_apps.go rename to backend/internal/router/admin/agent_apps.go index 2e4890d..f31d63f 100644 --- a/backend/internal/router/developer/agent_apps.go +++ b/backend/internal/router/admin/agent_apps.go @@ -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 } diff --git a/backend/internal/router/developer/agents.go b/backend/internal/router/admin/agents.go similarity index 99% rename from backend/internal/router/developer/agents.go rename to backend/internal/router/admin/agents.go index d031324..c464ff9 100644 --- a/backend/internal/router/developer/agents.go +++ b/backend/internal/router/admin/agents.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/announcements.go b/backend/internal/router/admin/announcements.go similarity index 99% rename from backend/internal/router/developer/announcements.go rename to backend/internal/router/admin/announcements.go index f28f538..508b7dd 100644 --- a/backend/internal/router/developer/announcements.go +++ b/backend/internal/router/admin/announcements.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/applications.go b/backend/internal/router/admin/applications.go similarity index 99% rename from backend/internal/router/developer/applications.go rename to backend/internal/router/admin/applications.go index f0a65f3..291aa97 100644 --- a/backend/internal/router/developer/applications.go +++ b/backend/internal/router/admin/applications.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "crypto/rand" diff --git a/backend/internal/router/developer/cards.go b/backend/internal/router/admin/cards.go similarity index 98% rename from backend/internal/router/developer/cards.go rename to backend/internal/router/admin/cards.go index 03a5fc9..d316342 100644 --- a/backend/internal/router/developer/cards.go +++ b/backend/internal/router/admin/cards.go @@ -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 { diff --git a/backend/internal/router/developer/cloud.go b/backend/internal/router/admin/cloud.go similarity index 99% rename from backend/internal/router/developer/cloud.go rename to backend/internal/router/admin/cloud.go index f260fe1..33a063e 100644 --- a/backend/internal/router/developer/cloud.go +++ b/backend/internal/router/admin/cloud.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "crypto/md5" diff --git a/backend/internal/router/developer/dashboard.go b/backend/internal/router/admin/dashboard.go similarity index 99% rename from backend/internal/router/developer/dashboard.go rename to backend/internal/router/admin/dashboard.go index 9673636..edea357 100644 --- a/backend/internal/router/developer/dashboard.go +++ b/backend/internal/router/admin/dashboard.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "time" diff --git a/backend/internal/router/developer/developer.go b/backend/internal/router/admin/developer.go similarity index 98% rename from backend/internal/router/developer/developer.go rename to backend/internal/router/admin/developer.go index 7108b4d..5ea8f2a 100644 --- a/backend/internal/router/developer/developer.go +++ b/backend/internal/router/admin/developer.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "github.com/gin-gonic/gin" diff --git a/backend/internal/router/developer/devices.go b/backend/internal/router/admin/devices.go similarity index 99% rename from backend/internal/router/developer/devices.go rename to backend/internal/router/admin/devices.go index 0d5a6bd..7d09a5a 100644 --- a/backend/internal/router/developer/devices.go +++ b/backend/internal/router/admin/devices.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/dynamic.go b/backend/internal/router/admin/dynamic.go similarity index 99% rename from backend/internal/router/developer/dynamic.go rename to backend/internal/router/admin/dynamic.go index d7ad999..2ff28b4 100644 --- a/backend/internal/router/developer/dynamic.go +++ b/backend/internal/router/admin/dynamic.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "log" diff --git a/backend/internal/router/developer/email.go b/backend/internal/router/admin/email.go similarity index 99% rename from backend/internal/router/developer/email.go rename to backend/internal/router/admin/email.go index 262d8b7..d23592b 100644 --- a/backend/internal/router/developer/email.go +++ b/backend/internal/router/admin/email.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/extension.go b/backend/internal/router/admin/extension.go similarity index 99% rename from backend/internal/router/developer/extension.go rename to backend/internal/router/admin/extension.go index 621ca23..2c6bdb4 100644 --- a/backend/internal/router/developer/extension.go +++ b/backend/internal/router/admin/extension.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "bytes" diff --git a/backend/internal/router/developer/finance.go b/backend/internal/router/admin/finance.go similarity index 99% rename from backend/internal/router/developer/finance.go rename to backend/internal/router/admin/finance.go index a779b75..db01abc 100644 --- a/backend/internal/router/developer/finance.go +++ b/backend/internal/router/admin/finance.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/logs.go b/backend/internal/router/admin/logs.go similarity index 99% rename from backend/internal/router/developer/logs.go rename to backend/internal/router/admin/logs.go index c33d5cb..315f782 100644 --- a/backend/internal/router/developer/logs.go +++ b/backend/internal/router/admin/logs.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "strconv" diff --git a/backend/internal/router/developer/orders.go b/backend/internal/router/admin/orders.go similarity index 99% rename from backend/internal/router/developer/orders.go rename to backend/internal/router/admin/orders.go index 3215e85..13164ee 100644 --- a/backend/internal/router/developer/orders.go +++ b/backend/internal/router/admin/orders.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/profile.go b/backend/internal/router/admin/profile.go similarity index 99% rename from backend/internal/router/developer/profile.go rename to backend/internal/router/admin/profile.go index 6f1b016..2aa715a 100644 --- a/backend/internal/router/developer/profile.go +++ b/backend/internal/router/admin/profile.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "crypto/rand" diff --git a/backend/internal/router/developer/tickets.go b/backend/internal/router/admin/tickets.go similarity index 99% rename from backend/internal/router/developer/tickets.go rename to backend/internal/router/admin/tickets.go index 8c4bd1f..d44d723 100644 --- a/backend/internal/router/developer/tickets.go +++ b/backend/internal/router/admin/tickets.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/usage.go b/backend/internal/router/admin/usage.go similarity index 99% rename from backend/internal/router/developer/usage.go rename to backend/internal/router/admin/usage.go index 9e2c7fb..aa93b21 100644 --- a/backend/internal/router/developer/usage.go +++ b/backend/internal/router/admin/usage.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/users.go b/backend/internal/router/admin/users.go similarity index 99% rename from backend/internal/router/developer/users.go rename to backend/internal/router/admin/users.go index f68f7b2..84ba39d 100644 --- a/backend/internal/router/developer/users.go +++ b/backend/internal/router/admin/users.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "fmt" diff --git a/backend/internal/router/developer/versions.go b/backend/internal/router/admin/versions.go similarity index 99% rename from backend/internal/router/developer/versions.go rename to backend/internal/router/admin/versions.go index 272bc5d..213d164 100644 --- a/backend/internal/router/developer/versions.go +++ b/backend/internal/router/admin/versions.go @@ -1,4 +1,4 @@ -package developer +package admin import ( "archive/zip" diff --git a/backend/internal/router/app/account.go b/backend/internal/router/app/account.go index 88e33ba..dd67e44 100644 --- a/backend/internal/router/app/account.go +++ b/backend/internal/router/app/account.go @@ -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 + } } } } diff --git a/backend/internal/router/app/auth.go b/backend/internal/router/app/auth.go index 0a816c2..0ec23a7 100644 --- a/backend/internal/router/app/auth.go +++ b/backend/internal/router/app/auth.go @@ -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 { diff --git a/backend/internal/router/app/cloud.go b/backend/internal/router/app/cloud.go index a4bd422..c216bd9 100644 --- a/backend/internal/router/app/cloud.go +++ b/backend/internal/router/app/cloud.go @@ -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 diff --git a/backend/internal/router/app/dynamic.go b/backend/internal/router/app/dynamic.go index 4617a6d..91599be 100644 --- a/backend/internal/router/app/dynamic.go +++ b/backend/internal/router/app/dynamic.go @@ -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 { diff --git a/backend/internal/router/app/payment.go b/backend/internal/router/app/payment.go index 05762fe..87add52 100644 --- a/backend/internal/router/app/payment.go +++ b/backend/internal/router/app/payment.go @@ -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": diff --git a/backend/internal/router/frontend/frontend.go b/backend/internal/router/frontend/frontend.go index 33ac13e..a4dd376 100644 --- a/backend/internal/router/frontend/frontend.go +++ b/backend/internal/router/frontend/frontend.go @@ -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 { diff --git a/backend/internal/router/router.go b/backend/internal/router/router.go index c101e1b..352f171 100644 --- a/backend/internal/router/router.go +++ b/backend/internal/router/router.go @@ -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") diff --git a/backend/internal/utils/httputil/client.go b/backend/internal/utils/httputil/client.go index 306f9b8..8324ea6 100644 --- a/backend/internal/utils/httputil/client.go +++ b/backend/internal/utils/httputil/client.go @@ -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, } diff --git a/backend/scripts/check_all_deleted.go b/backend/scripts/check_all_deleted.go index a99a7bd..690b43b 100644 --- a/backend/scripts/check_all_deleted.go +++ b/backend/scripts/check_all_deleted.go @@ -1,4 +1,4 @@ -package main +package main import ( "fmt" diff --git a/backend/scripts/check_code.go b/backend/scripts/check_code.go index 134277b..41dba00 100644 --- a/backend/scripts/check_code.go +++ b/backend/scripts/check_code.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/check_code_raw.go b/backend/scripts/check_code_raw.go index 758e9b0..986fec9 100644 --- a/backend/scripts/check_code_raw.go +++ b/backend/scripts/check_code_raw.go @@ -1,4 +1,4 @@ -package main +package main import ( "fmt" diff --git a/backend/scripts/check_deleted_dynamic_codes.go b/backend/scripts/check_deleted_dynamic_codes.go index 053d81e..bd0a490 100644 --- a/backend/scripts/check_deleted_dynamic_codes.go +++ b/backend/scripts/check_deleted_dynamic_codes.go @@ -1,4 +1,4 @@ -package main +package main import ( "fmt" diff --git a/backend/scripts/check_dynamic_codes.go b/backend/scripts/check_dynamic_codes.go index 43aa08a..311f507 100644 --- a/backend/scripts/check_dynamic_codes.go +++ b/backend/scripts/check_dynamic_codes.go @@ -1,4 +1,4 @@ -package main +package main import ( "fmt" diff --git a/backend/scripts/check_i18n_data.go b/backend/scripts/check_i18n_data.go index 6009a91..21433cb 100644 --- a/backend/scripts/check_i18n_data.go +++ b/backend/scripts/check_i18n_data.go @@ -1,4 +1,4 @@ -package main +package main import ( "fmt" diff --git a/backend/scripts/check_packages.go b/backend/scripts/check_packages.go index 0943ffb..9474b53 100644 --- a/backend/scripts/check_packages.go +++ b/backend/scripts/check_packages.go @@ -1,4 +1,4 @@ -package main +package main import ( "fmt" diff --git a/backend/scripts/create_cloud_function_doc.go b/backend/scripts/create_cloud_function_doc.go index 198d6b9..7ec30e1 100644 --- a/backend/scripts/create_cloud_function_doc.go +++ b/backend/scripts/create_cloud_function_doc.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/create_manual_deduct_doc.go b/backend/scripts/create_manual_deduct_doc.go index 4658c9d..2178aa6 100644 --- a/backend/scripts/create_manual_deduct_doc.go +++ b/backend/scripts/create_manual_deduct_doc.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/fix_package_data.go b/backend/scripts/fix_package_data.go index 1f4af74..f7e98c8 100644 --- a/backend/scripts/fix_package_data.go +++ b/backend/scripts/fix_package_data.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/init_order_functions.go b/backend/scripts/init_order_functions.go index 48f6151..d582d71 100644 --- a/backend/scripts/init_order_functions.go +++ b/backend/scripts/init_order_functions.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/init_packages.go b/backend/scripts/init_packages.go index 0455062..62e5910 100644 --- a/backend/scripts/init_packages.go +++ b/backend/scripts/init_packages.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/list_docs.go b/backend/scripts/list_docs.go index 90a58d0..ade62b8 100644 --- a/backend/scripts/list_docs.go +++ b/backend/scripts/list_docs.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/migrate_docs_i18n.go b/backend/scripts/migrate_docs_i18n.go index 9c9d173..d7bbbaf 100644 --- a/backend/scripts/migrate_docs_i18n.go +++ b/backend/scripts/migrate_docs_i18n.go @@ -1,4 +1,4 @@ -package main +package main import ( "fmt" diff --git a/backend/scripts/translate_docs_i18n.go b/backend/scripts/translate_docs_i18n.go index a4bf4a9..0717e69 100644 --- a/backend/scripts/translate_docs_i18n.go +++ b/backend/scripts/translate_docs_i18n.go @@ -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?", diff --git a/backend/scripts/translate_packages_i18n.go b/backend/scripts/translate_packages_i18n.go index 9bb68c2..63e5efa 100644 --- a/backend/scripts/translate_packages_i18n.go +++ b/backend/scripts/translate_packages_i18n.go @@ -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", diff --git a/backend/scripts/update_api_docs.go b/backend/scripts/update_api_docs.go index 7bb8e68..353433a 100644 --- a/backend/scripts/update_api_docs.go +++ b/backend/scripts/update_api_docs.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/update_doc_translations.go b/backend/scripts/update_doc_translations.go index 8b0c525..40bb375 100644 --- a/backend/scripts/update_doc_translations.go +++ b/backend/scripts/update_doc_translations.go @@ -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", }, } diff --git a/backend/scripts/update_dynamic_code_doc.go b/backend/scripts/update_dynamic_code_doc.go index a7a7545..410f3e0 100644 --- a/backend/scripts/update_dynamic_code_doc.go +++ b/backend/scripts/update_dynamic_code_doc.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/update_dynamic_code_docs.go b/backend/scripts/update_dynamic_code_docs.go index f1d7a1d..40576d1 100644 --- a/backend/scripts/update_dynamic_code_docs.go +++ b/backend/scripts/update_dynamic_code_docs.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/backend/scripts/update_to_cloud_function.go b/backend/scripts/update_to_cloud_function.go index 2e8ba99..e09c9c9 100644 --- a/backend/scripts/update_to_cloud_function.go +++ b/backend/scripts/update_to_cloud_function.go @@ -1,4 +1,4 @@ -package main +package main import ( "log" diff --git a/frontend/src/components/ui/sidebar/SidebarContent.vue b/frontend/src/components/ui/sidebar/SidebarContent.vue index bb6d9f1..6ce5596 100644 --- a/frontend/src/components/ui/sidebar/SidebarContent.vue +++ b/frontend/src/components/ui/sidebar/SidebarContent.vue @@ -1,14 +1,57 @@