yunzerwebsiteallinone/go/controllers/platform_cursor_activation_code.go
2026-06-16 01:30:39 +08:00

751 lines
19 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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")
}