package controllers import ( "crypto/rand" "encoding/csv" "encoding/hex" "encoding/json" "fmt" "io" "strconv" "strings" "time" "server/models" "server/pkg/jwtutil" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" ) // PlatformCursorActivationCodeController 平台端 Cursor 激活码管理 type PlatformCursorActivationCodeController struct { beego.Controller } func (c *PlatformCursorActivationCodeController) platformClaims() (*jwtutil.Claims, error) { auth := c.Ctx.Request.Header.Get("Authorization") if auth == "" { return nil, fmt.Errorf("未登录") } parts := strings.SplitN(auth, " ", 2) if len(parts) != 2 || parts[0] != "Bearer" { return nil, fmt.Errorf("认证信息格式错误") } claims, err := jwtutil.ParseToken(parts[1]) if err != nil { return nil, fmt.Errorf("无效的token") } if claims.UserType != "platform" { return nil, fmt.Errorf("无权访问") } return claims, nil } func (c *PlatformCursorActivationCodeController) jsonErr(httpStatus, bizCode int, msg string) { c.Ctx.Output.SetStatus(httpStatus) c.Data["json"] = map[string]interface{}{"code": bizCode, "msg": msg} _ = c.ServeJSON() } func (c *PlatformCursorActivationCodeController) ok(data interface{}) { c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": data} _ = c.ServeJSON() } func cursorActivationCodeTrimPtr(value *string) *string { if value == nil { return nil } v := strings.TrimSpace(*value) if v == "" { return nil } return &v } func cursorActivationCodeTimePtr(value *string) *time.Time { if value == nil { return nil } v := strings.TrimSpace(*value) if v == "" { return nil } layouts := []string{ time.RFC3339, "2006-01-02 15:04:05", "2006-01-02 15:04", "2006-01-02", } for _, layout := range layouts { if t, err := time.ParseInLocation(layout, v, time.Local); err == nil { return &t } } return nil } func cursorActivationCodeStatusValid(status int8) bool { return status == 0 || status == 1 || status == 2 || status == 3 } func cursorActivationCodeTypeName(cardType int) string { switch cardType { case 1: return "天卡" case 7: return "周卡" case 30: return "月卡" case 90: return "季卡" case 365: return "年卡" case 0: return "自定义" default: return fmt.Sprintf("%d天", cardType) } } func (c *PlatformCursorActivationCodeController) rowToMap(row *models.PlatformCursorActivationCode) map[string]interface{} { bindStatus := 0 if row.BindAccount != nil || row.BindDeviceID != nil || row.MachineCode != nil { bindStatus = 1 } return map[string]interface{}{ "id": row.ID, "code": row.Code, "type": row.Type, "typeName": cursorActivationCodeTypeName(row.Type), "status": row.Status, "durationDays": row.DurationDays, "bindAccount": row.BindAccount, "bindDeviceId": row.BindDeviceID, "bindStatus": bindStatus, "machineCode": row.MachineCode, "deviceInfo": row.DeviceInfo, "ownerUserId": row.OwnerUserID, "ownerUserName": row.OwnerUserName, "activatedAt": row.ActivatedAt, "expiredAt": row.ExpiredAt, "createdAt": row.CreateTime, "updatedAt": row.UpdateTime, "createTime": row.CreateTime, "updateTime": row.UpdateTime, "remark": row.Remark, } } func (c *PlatformCursorActivationCodeController) filteredQuery() orm.QuerySeter { keyword := strings.TrimSpace(c.GetString("keyword")) statusText := strings.TrimSpace(c.GetString("status")) typeText := strings.TrimSpace(c.GetString("type")) bindStatusText := strings.TrimSpace(c.GetString("bindStatus")) qs := models.Orm.QueryTable(new(models.PlatformCursorActivationCode)).Filter("delete_time__isnull", true) if keyword != "" { cond := orm.NewCondition(). Or("code__icontains", keyword). Or("bind_account__icontains", keyword). Or("machine_code__icontains", keyword). Or("device_info__icontains", keyword). Or("owner_user_name__icontains", keyword). Or("remark__icontains", keyword) qs = qs.SetCond(cond) qs = qs.Filter("delete_time__isnull", true) } if statusText != "" { status, err := strconv.ParseInt(statusText, 10, 8) if err == nil && cursorActivationCodeStatusValid(int8(status)) { qs = qs.Filter("status", int8(status)) } } if typeText != "" { cardType, err := strconv.Atoi(typeText) if err == nil { qs = qs.Filter("type", cardType) } } if bindStatusText != "" { bindStatus, err := strconv.Atoi(bindStatusText) if err == nil { if bindStatus == 0 { qs = qs.Filter("bind_account__isnull", true).Filter("bind_device_id__isnull", true).Filter("machine_code__isnull", true) } else if bindStatus == 1 { cond := orm.NewCondition(). Or("bind_account__isnull", false). Or("bind_device_id__isnull", false). Or("machine_code__isnull", false) qs = qs.SetCond(cond) qs = qs.Filter("delete_time__isnull", true) if statusText != "" { status, err := strconv.ParseInt(statusText, 10, 8) if err == nil && cursorActivationCodeStatusValid(int8(status)) { qs = qs.Filter("status", int8(status)) } } if typeText != "" { cardType, err := strconv.Atoi(typeText) if err == nil { qs = qs.Filter("type", cardType) } } } } } return qs } // List GET /platform/cursor/activationcode/list func (c *PlatformCursorActivationCodeController) List() { if _, err := c.platformClaims(); err != nil { c.jsonErr(401, 401, err.Error()) return } page, _ := c.GetInt("page", 1) pageSize, _ := c.GetInt("pageSize", 20) if page < 1 { page = 1 } if pageSize < 1 { pageSize = 20 } if pageSize > 200 { pageSize = 200 } qs := c.filteredQuery() total, _ := qs.Count() var rows []models.PlatformCursorActivationCode _, err := qs.OrderBy("-id").Limit(pageSize, (page-1)*pageSize).All(&rows) if err != nil { c.jsonErr(500, 500, "获取激活码列表失败: "+err.Error()) return } list := make([]map[string]interface{}, 0, len(rows)) for i := range rows { list = append(list, c.rowToMap(&rows[i])) } c.ok(map[string]interface{}{ "list": list, "total": total, "page": page, "pageSize": pageSize, }) } // Detail GET /platform/cursor/activationcode/detail/:id func (c *PlatformCursorActivationCodeController) Detail() { if _, err := c.platformClaims(); err != nil { c.jsonErr(401, 401, err.Error()) return } id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if err != nil || id == 0 { c.jsonErr(400, 400, "无效ID") return } var row models.PlatformCursorActivationCode err = models.Orm.QueryTable(new(models.PlatformCursorActivationCode)). Filter("id", id). Filter("delete_time__isnull", true). One(&row) if err != nil { c.jsonErr(404, 404, "激活码不存在") return } c.ok(c.rowToMap(&row)) } type platformCursorActivationCodePayload struct { ID *uint64 `json:"id"` Code *string `json:"code"` Type *int `json:"type"` Status *int8 `json:"status"` DurationDays *int `json:"durationDays"` BindAccount *string `json:"bindAccount"` BindDeviceID *uint64 `json:"bindDeviceId"` OwnerUserID *uint64 `json:"ownerUserId"` OwnerUserName *string `json:"ownerUserName"` ActivatedAt *string `json:"activatedAt"` ExpiredAt *string `json:"expiredAt"` Remark *string `json:"remark"` } func (c *PlatformCursorActivationCodeController) readPayload() (*platformCursorActivationCodePayload, error) { body, _ := io.ReadAll(c.Ctx.Request.Body) var p platformCursorActivationCodePayload if err := json.Unmarshal(body, &p); err != nil { return nil, err } return &p, nil } func (c *PlatformCursorActivationCodeController) fillDeviceSnapshot(up map[string]interface{}, bindDeviceID *uint64) { if bindDeviceID == nil || *bindDeviceID == 0 { up["bind_device_id"] = nil up["machine_code"] = nil up["device_info"] = nil return } var device models.PlatformCursorEquipment err := models.Orm.QueryTable(new(models.PlatformCursorEquipment)). Filter("id", *bindDeviceID). Filter("delete_time__isnull", true). One(&device) if err == nil { up["bind_device_id"] = *bindDeviceID up["machine_code"] = device.MachineCode up["device_info"] = device.DeviceInfo return } up["bind_device_id"] = *bindDeviceID } func (c *PlatformCursorActivationCodeController) payloadToUpdateMap(p *platformCursorActivationCodePayload, includeCode bool) (map[string]interface{}, error) { up := map[string]interface{}{} if includeCode { if p.Code == nil || strings.TrimSpace(*p.Code) == "" { return nil, fmt.Errorf("激活码不能为空") } up["code"] = strings.TrimSpace(*p.Code) } else if p.Code != nil { if strings.TrimSpace(*p.Code) == "" { return nil, fmt.Errorf("激活码不能为空") } up["code"] = strings.TrimSpace(*p.Code) } if p.Type != nil { if *p.Type < 0 { return nil, fmt.Errorf("卡密类型不合法") } up["type"] = *p.Type } if p.Status != nil { if !cursorActivationCodeStatusValid(*p.Status) { return nil, fmt.Errorf("状态不合法,支持:0 未使用、1 已使用、2 已过期、3 已禁用") } up["status"] = *p.Status } if p.DurationDays != nil { if *p.DurationDays < 0 || *p.DurationDays > 9999 { return nil, fmt.Errorf("有效天数范围为 0-9999") } up["duration_days"] = *p.DurationDays } if p.BindAccount != nil { up["bind_account"] = cursorActivationCodeTrimPtr(p.BindAccount) } if p.BindDeviceID != nil { c.fillDeviceSnapshot(up, p.BindDeviceID) } if p.OwnerUserID != nil { if *p.OwnerUserID == 0 { up["owner_user_id"] = nil } else { up["owner_user_id"] = *p.OwnerUserID } } if p.OwnerUserName != nil { up["owner_user_name"] = cursorActivationCodeTrimPtr(p.OwnerUserName) } if p.ActivatedAt != nil { up["activated_at"] = cursorActivationCodeTimePtr(p.ActivatedAt) } if p.ExpiredAt != nil { up["expired_at"] = cursorActivationCodeTimePtr(p.ExpiredAt) } if p.Remark != nil { up["remark"] = cursorActivationCodeTrimPtr(p.Remark) } return up, nil } // Add POST /platform/cursor/activationcode/add func (c *PlatformCursorActivationCodeController) Add() { if _, err := c.platformClaims(); err != nil { c.jsonErr(401, 401, err.Error()) return } p, err := c.readPayload() if err != nil { c.jsonErr(400, 400, "参数错误") return } up, err := c.payloadToUpdateMap(p, true) if err != nil { c.jsonErr(400, 400, err.Error()) return } row := models.PlatformCursorActivationCode{ Code: up["code"].(string), Type: 30, Status: 0, DurationDays: 30, BindAccount: cursorActivationCodeTrimPtr(p.BindAccount), BindDeviceID: p.BindDeviceID, OwnerUserID: p.OwnerUserID, OwnerUserName: cursorActivationCodeTrimPtr(p.OwnerUserName), ActivatedAt: cursorActivationCodeTimePtr(p.ActivatedAt), ExpiredAt: cursorActivationCodeTimePtr(p.ExpiredAt), Remark: cursorActivationCodeTrimPtr(p.Remark), CreateTime: time.Now(), } if p.Type != nil { row.Type = *p.Type } if p.Status != nil { row.Status = *p.Status } if p.DurationDays != nil { row.DurationDays = *p.DurationDays } if row.BindDeviceID != nil && *row.BindDeviceID == 0 { row.BindDeviceID = nil } if row.OwnerUserID != nil && *row.OwnerUserID == 0 { row.OwnerUserID = nil } if row.BindDeviceID != nil { var device models.PlatformCursorEquipment if err := models.Orm.QueryTable(new(models.PlatformCursorEquipment)). Filter("id", *row.BindDeviceID). Filter("delete_time__isnull", true). One(&device); err == nil { row.MachineCode = &device.MachineCode row.DeviceInfo = device.DeviceInfo } } id, err := models.Orm.Insert(&row) if err != nil { if strings.Contains(strings.ToLower(err.Error()), "duplicate") { c.jsonErr(400, 400, "激活码已存在") return } c.jsonErr(500, 500, "新增激活码失败: "+err.Error()) return } c.ok(map[string]interface{}{"id": id}) } // Update POST /platform/cursor/activationcode/update func (c *PlatformCursorActivationCodeController) Update() { if _, err := c.platformClaims(); err != nil { c.jsonErr(401, 401, err.Error()) return } p, err := c.readPayload() if err != nil { c.jsonErr(400, 400, "参数错误") return } if p.ID == nil || *p.ID == 0 { c.jsonErr(400, 400, "无效ID") return } up, err := c.payloadToUpdateMap(p, false) if err != nil { c.jsonErr(400, 400, err.Error()) return } if len(up) == 0 { c.jsonErr(400, 400, "无更新字段") return } now := time.Now() up["update_time"] = now n, err := models.Orm.QueryTable(new(models.PlatformCursorActivationCode)). Filter("id", *p.ID). Filter("delete_time__isnull", true). Update(up) if err != nil { if strings.Contains(strings.ToLower(err.Error()), "duplicate") { c.jsonErr(400, 400, "激活码已存在") return } c.jsonErr(500, 500, "更新激活码失败: "+err.Error()) return } if n == 0 { c.jsonErr(404, 404, "激活码不存在") return } c.ok(nil) } // Delete POST /platform/cursor/activationcode/delete/:id func (c *PlatformCursorActivationCodeController) Delete() { if _, err := c.platformClaims(); err != nil { c.jsonErr(401, 401, err.Error()) return } id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if err != nil || id == 0 { c.jsonErr(400, 400, "无效ID") return } now := time.Now() n, err := models.Orm.QueryTable(new(models.PlatformCursorActivationCode)). Filter("id", id). Filter("delete_time__isnull", true). Update(map[string]interface{}{"delete_time": now, "update_time": now}) if err != nil { c.jsonErr(500, 500, "删除激活码失败: "+err.Error()) return } if n == 0 { c.jsonErr(404, 404, "激活码不存在") return } c.ok(nil) } type platformCursorActivationCodeGeneratePayload struct { Count int `json:"count"` Type int `json:"type"` DurationDays int `json:"durationDays"` OwnerUserID *uint64 `json:"ownerUserId"` OwnerUserName *string `json:"ownerUserName"` Remark *string `json:"remark"` } func randomCursorActivationCode() (string, error) { b := make([]byte, 12) if _, err := rand.Read(b); err != nil { return "", err } return "CUR-" + strings.ToUpper(hex.EncodeToString(b)), nil } // Generate POST /platform/cursor/activationcode/generate func (c *PlatformCursorActivationCodeController) Generate() { if _, err := c.platformClaims(); err != nil { c.jsonErr(401, 401, err.Error()) return } body, _ := io.ReadAll(c.Ctx.Request.Body) var p platformCursorActivationCodeGeneratePayload if err := json.Unmarshal(body, &p); err != nil { c.jsonErr(400, 400, "参数错误") return } if p.Count < 1 { p.Count = 1 } if p.Count > 10000 { c.jsonErr(400, 400, "单次最多生成 10000 个激活码") return } if p.Type < 0 { c.jsonErr(400, 400, "卡密类型不合法") return } if p.DurationDays < 0 || p.DurationDays > 9999 { c.jsonErr(400, 400, "有效天数范围为 0-9999") return } if p.Type == 0 && p.DurationDays == 0 { p.DurationDays = 30 } if p.Type > 0 && p.DurationDays == 0 { p.DurationDays = p.Type } createdIDs := make([]int64, 0, p.Count) codes := make([]string, 0, p.Count) now := time.Now() for len(createdIDs) < p.Count { code, err := randomCursorActivationCode() if err != nil { c.jsonErr(500, 500, "生成激活码失败: "+err.Error()) return } row := models.PlatformCursorActivationCode{ Code: code, Type: p.Type, Status: 0, DurationDays: p.DurationDays, OwnerUserID: p.OwnerUserID, OwnerUserName: cursorActivationCodeTrimPtr(p.OwnerUserName), Remark: cursorActivationCodeTrimPtr(p.Remark), CreateTime: now, } if row.OwnerUserID != nil && *row.OwnerUserID == 0 { row.OwnerUserID = nil } id, err := models.Orm.Insert(&row) if err != nil { if strings.Contains(strings.ToLower(err.Error()), "duplicate") { continue } c.jsonErr(500, 500, "生成激活码失败: "+err.Error()) return } createdIDs = append(createdIDs, id) codes = append(codes, code) } c.ok(map[string]interface{}{ "count": len(createdIDs), "ids": createdIDs, "codes": codes, }) } // Enable POST /platform/cursor/activationcode/enable/:id func (c *PlatformCursorActivationCodeController) Enable() { c.changeStatus(0, "启用激活码失败") } // Disable POST /platform/cursor/activationcode/disable/:id func (c *PlatformCursorActivationCodeController) Disable() { c.changeStatus(3, "禁用激活码失败") } func (c *PlatformCursorActivationCodeController) changeStatus(status int8, failMsg string) { if _, err := c.platformClaims(); err != nil { c.jsonErr(401, 401, err.Error()) return } id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64) if err != nil || id == 0 { c.jsonErr(400, 400, "无效ID") return } now := time.Now() n, err := models.Orm.QueryTable(new(models.PlatformCursorActivationCode)). Filter("id", id). Filter("delete_time__isnull", true). Update(map[string]interface{}{ "status": status, "update_time": now, }) if err != nil { c.jsonErr(500, 500, failMsg+": "+err.Error()) return } if n == 0 { c.jsonErr(404, 404, "激活码不存在") return } c.ok(nil) } // Export GET /platform/cursor/activationcode/export func (c *PlatformCursorActivationCodeController) Export() { if _, err := c.platformClaims(); err != nil { c.jsonErr(401, 401, err.Error()) return } var rows []models.PlatformCursorActivationCode _, err := c.filteredQuery().OrderBy("-id").Limit(50000).All(&rows) if err != nil { c.jsonErr(500, 500, "导出激活码失败: "+err.Error()) return } filename := fmt.Sprintf("cursor-activation-code-%s.csv", time.Now().Format("20060102150405")) c.Ctx.Output.Header("Content-Type", "text/csv; charset=utf-8") c.Ctx.Output.Header("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, filename)) _, _ = c.Ctx.ResponseWriter.Write([]byte{0xEF, 0xBB, 0xBF}) writer := csv.NewWriter(c.Ctx.ResponseWriter) _ = writer.Write([]string{ "ID", "激活码", "类型", "有效天数", "状态", "绑定账号", "绑定设备ID", "机器码", "归属用户ID", "归属用户", "激活时间", "过期时间", "创建时间", "备注", }) statusText := map[int8]string{ 0: "未使用", 1: "已使用", 2: "已过期", 3: "已禁用", } for i := range rows { row := rows[i] _ = writer.Write([]string{ strconv.FormatUint(row.ID, 10), row.Code, cursorActivationCodeTypeName(row.Type), strconv.Itoa(row.DurationDays), statusText[row.Status], stringPtrValue(row.BindAccount), uint64PtrValue(row.BindDeviceID), stringPtrValue(row.MachineCode), uint64PtrValue(row.OwnerUserID), stringPtrValue(row.OwnerUserName), timePtrValue(row.ActivatedAt), timePtrValue(row.ExpiredAt), row.CreateTime.Format("2006-01-02 15:04:05"), stringPtrValue(row.Remark), }) } writer.Flush() } func stringPtrValue(value *string) string { if value == nil { return "" } return *value } func uint64PtrValue(value *uint64) string { if value == nil { return "" } return strconv.FormatUint(*value, 10) } func timePtrValue(value *time.Time) string { if value == nil { return "" } return value.Format("2006-01-02 15:04:05") }