整合数据
This commit is contained in:
@@ -0,0 +1,503 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// ApiCursorEquipmentController 开放接口:登录器上报 Cursor 设备信息(无需登录)
|
||||
type ApiCursorEquipmentController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
type cursorEquipmentReportPayload struct {
|
||||
DeviceInfo string `json:"deviceInfo"`
|
||||
DeviceInfoSnake string `json:"device_info"`
|
||||
MachineCode string `json:"machineCode"`
|
||||
MachineCodeSnake string `json:"machine_code"`
|
||||
Status *int8 `json:"status"`
|
||||
System string `json:"system"`
|
||||
Version string `json:"version"`
|
||||
BindAccount string `json:"bindAccount"`
|
||||
BindAccountSnake string `json:"bind_account"`
|
||||
OwnerUserID *uint64 `json:"ownerUserId"`
|
||||
OwnerUserIDSnake *uint64 `json:"owner_user_id"`
|
||||
OwnerUserName string `json:"ownerUserName"`
|
||||
OwnerUserNameSnake string `json:"owner_user_name"`
|
||||
ActivationTime string `json:"activationTime"`
|
||||
ActivationTimeSnake string `json:"activation_time"`
|
||||
ExpireTime string `json:"expireTime"`
|
||||
ExpireTimeSnake string `json:"expire_time"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
type cursorEquipmentActivatePayload struct {
|
||||
Code string `json:"code"`
|
||||
ActivationCode string `json:"activationCode"`
|
||||
ActivationCodeSnake string `json:"activation_code"`
|
||||
DeviceInfo string `json:"deviceInfo"`
|
||||
DeviceInfoSnake string `json:"device_info"`
|
||||
MachineCode string `json:"machineCode"`
|
||||
MachineCodeSnake string `json:"machine_code"`
|
||||
System string `json:"system"`
|
||||
Version string `json:"version"`
|
||||
BindAccount string `json:"bindAccount"`
|
||||
BindAccountSnake string `json:"bind_account"`
|
||||
OwnerUserID *uint64 `json:"ownerUserId"`
|
||||
OwnerUserIDSnake *uint64 `json:"owner_user_id"`
|
||||
OwnerUserName string `json:"ownerUserName"`
|
||||
OwnerUserNameSnake string `json:"owner_user_name"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
func cursorFirstNonEmpty(values ...string) string {
|
||||
for _, v := range values {
|
||||
if s := strings.TrimSpace(v); s != "" {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func cursorStringPtr(value string) *string {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
return &value
|
||||
}
|
||||
|
||||
func cursorParseTimePtr(value string) *time.Time {
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
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, value, time.Local); err == nil {
|
||||
return &t
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func cursorValidStatus(status int8) bool {
|
||||
return status == 0 || status == 1 || status == 2 || status == 3
|
||||
}
|
||||
|
||||
func (c *ApiCursorEquipmentController) jsonResult(code int, msg string, data interface{}) {
|
||||
resp := map[string]interface{}{"code": code, "msg": msg}
|
||||
if data != nil {
|
||||
resp["data"] = data
|
||||
}
|
||||
c.Data["json"] = resp
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
// Report POST /api/cursor/equipment/report
|
||||
//
|
||||
// JSON 示例:
|
||||
//
|
||||
// {
|
||||
// "machineCode": "ABC-123",
|
||||
// "deviceInfo": "CPU/RAM/磁盘等设备信息",
|
||||
// "system": "Windows",
|
||||
// "version": "1.0.0",
|
||||
// "bindAccount": "user@example.com",
|
||||
// "ownerUserId": 1,
|
||||
// "ownerUserName": "张三",
|
||||
// "activationTime": "2026-06-15 22:00:00",
|
||||
// "expireTime": "2026-07-15 22:00:00",
|
||||
// "remark": "登录器上报"
|
||||
// }
|
||||
//
|
||||
// 兼容 snake_case 字段,例如 machine_code、device_info、bind_account。
|
||||
func (c *ApiCursorEquipmentController) Report() {
|
||||
var p cursorEquipmentReportPayload
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &p); err != nil {
|
||||
c.jsonResult(400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
|
||||
machineCode := cursorFirstNonEmpty(p.MachineCode, p.MachineCodeSnake)
|
||||
if machineCode == "" {
|
||||
c.jsonResult(400, "缺少参数 machineCode/machine_code(机器码)", nil)
|
||||
return
|
||||
}
|
||||
if len(machineCode) > 128 {
|
||||
c.jsonResult(400, "机器码长度不能超过 128 个字符", nil)
|
||||
return
|
||||
}
|
||||
|
||||
status := int8(0)
|
||||
statusProvided := p.Status != nil
|
||||
if statusProvided {
|
||||
status = *p.Status
|
||||
if !cursorValidStatus(status) {
|
||||
c.jsonResult(400, "状态不合法,支持:0 未激活、1 激活中、2 已过期、3 已禁用", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
deviceInfo := cursorFirstNonEmpty(p.DeviceInfo, p.DeviceInfoSnake)
|
||||
system := cursorFirstNonEmpty(p.System)
|
||||
version := cursorFirstNonEmpty(p.Version)
|
||||
bindAccount := cursorFirstNonEmpty(p.BindAccount, p.BindAccountSnake)
|
||||
ownerUserID := p.OwnerUserID
|
||||
if ownerUserID == nil {
|
||||
ownerUserID = p.OwnerUserIDSnake
|
||||
}
|
||||
ownerUserName := cursorFirstNonEmpty(p.OwnerUserName, p.OwnerUserNameSnake)
|
||||
activationTime := cursorParseTimePtr(cursorFirstNonEmpty(p.ActivationTime, p.ActivationTimeSnake))
|
||||
expireTime := cursorParseTimePtr(cursorFirstNonEmpty(p.ExpireTime, p.ExpireTimeSnake))
|
||||
remark := cursorFirstNonEmpty(p.Remark)
|
||||
|
||||
now := time.Now()
|
||||
var row models.PlatformCursorEquipment
|
||||
err := models.Orm.QueryTable(new(models.PlatformCursorEquipment)).
|
||||
Filter("machine_code", machineCode).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&row)
|
||||
|
||||
created := false
|
||||
if err == orm.ErrNoRows {
|
||||
row = models.PlatformCursorEquipment{
|
||||
MachineCode: machineCode,
|
||||
Status: status,
|
||||
DeviceInfo: cursorStringPtr(deviceInfo),
|
||||
System: cursorStringPtr(system),
|
||||
Version: cursorStringPtr(version),
|
||||
BindAccount: cursorStringPtr(bindAccount),
|
||||
OwnerUserID: ownerUserID,
|
||||
OwnerUserName: cursorStringPtr(ownerUserName),
|
||||
ActivationTime: activationTime,
|
||||
ExpireTime: expireTime,
|
||||
Remark: cursorStringPtr(remark),
|
||||
CreateTime: now,
|
||||
}
|
||||
id, insertErr := models.Orm.Insert(&row)
|
||||
if insertErr != nil {
|
||||
c.jsonResult(500, "设备信息保存失败", nil)
|
||||
return
|
||||
}
|
||||
row.ID = uint64(id)
|
||||
created = true
|
||||
} else if err != nil {
|
||||
c.jsonResult(500, "设备信息查询失败", nil)
|
||||
return
|
||||
} else {
|
||||
update := map[string]interface{}{
|
||||
"device_info": cursorStringPtr(deviceInfo),
|
||||
"system": cursorStringPtr(system),
|
||||
"version": cursorStringPtr(version),
|
||||
"bind_account": cursorStringPtr(bindAccount),
|
||||
"owner_user_id": ownerUserID,
|
||||
"owner_user_name": cursorStringPtr(ownerUserName),
|
||||
"activation_time": activationTime,
|
||||
"expire_time": expireTime,
|
||||
"remark": cursorStringPtr(remark),
|
||||
"update_time": now,
|
||||
}
|
||||
if statusProvided {
|
||||
update["status"] = status
|
||||
row.Status = status
|
||||
}
|
||||
|
||||
if _, updateErr := models.Orm.QueryTable(new(models.PlatformCursorEquipment)).
|
||||
Filter("id", row.ID).
|
||||
Update(update); updateErr != nil {
|
||||
c.jsonResult(500, "设备信息更新失败", nil)
|
||||
return
|
||||
}
|
||||
|
||||
row.DeviceInfo = cursorStringPtr(deviceInfo)
|
||||
row.System = cursorStringPtr(system)
|
||||
row.Version = cursorStringPtr(version)
|
||||
row.BindAccount = cursorStringPtr(bindAccount)
|
||||
row.OwnerUserID = ownerUserID
|
||||
row.OwnerUserName = cursorStringPtr(ownerUserName)
|
||||
row.ActivationTime = activationTime
|
||||
row.ExpireTime = expireTime
|
||||
row.Remark = cursorStringPtr(remark)
|
||||
row.UpdateTime = &now
|
||||
}
|
||||
|
||||
c.jsonResult(200, "success", map[string]interface{}{
|
||||
"id": row.ID,
|
||||
"machineCode": row.MachineCode,
|
||||
"status": row.Status,
|
||||
"created": created,
|
||||
})
|
||||
}
|
||||
|
||||
// ActivateByCode POST /api/cursor/equipment/activateByCode
|
||||
//
|
||||
// 设备端使用激活码激活/续期 Cursor 设备(无需登录)。
|
||||
//
|
||||
// JSON 示例:
|
||||
//
|
||||
// {
|
||||
// "activationCode": "CUR-XXXXXXXX",
|
||||
// "machineCode": "ABC-123",
|
||||
// "deviceInfo": "CPU/RAM/磁盘等设备信息",
|
||||
// "system": "Windows",
|
||||
// "version": "1.0.0",
|
||||
// "bindAccount": "user@example.com",
|
||||
// "ownerUserId": 1,
|
||||
// "ownerUserName": "张三",
|
||||
// "remark": "登录器激活"
|
||||
// }
|
||||
//
|
||||
// 兼容字段:
|
||||
// - 激活码:activationCode / activation_code / code
|
||||
// - 机器码:machineCode / machine_code
|
||||
// - 设备信息:deviceInfo / device_info
|
||||
// - 绑定账号:bindAccount / bind_account
|
||||
func (c *ApiCursorEquipmentController) ActivateByCode() {
|
||||
var p cursorEquipmentActivatePayload
|
||||
if err := json.Unmarshal(c.Ctx.Input.RequestBody, &p); err != nil {
|
||||
c.jsonResult(400, "参数错误", nil)
|
||||
return
|
||||
}
|
||||
|
||||
code := cursorFirstNonEmpty(p.ActivationCode, p.ActivationCodeSnake, p.Code)
|
||||
if code == "" {
|
||||
c.jsonResult(400, "缺少参数 activationCode/activation_code/code(激活码)", nil)
|
||||
return
|
||||
}
|
||||
if len(code) > 128 {
|
||||
c.jsonResult(400, "激活码长度不能超过 128 个字符", nil)
|
||||
return
|
||||
}
|
||||
|
||||
machineCode := cursorFirstNonEmpty(p.MachineCode, p.MachineCodeSnake)
|
||||
if machineCode == "" {
|
||||
c.jsonResult(400, "缺少参数 machineCode/machine_code(机器码)", nil)
|
||||
return
|
||||
}
|
||||
if len(machineCode) > 128 {
|
||||
c.jsonResult(400, "机器码长度不能超过 128 个字符", nil)
|
||||
return
|
||||
}
|
||||
|
||||
deviceInfo := cursorFirstNonEmpty(p.DeviceInfo, p.DeviceInfoSnake)
|
||||
system := cursorFirstNonEmpty(p.System)
|
||||
version := cursorFirstNonEmpty(p.Version)
|
||||
bindAccount := cursorFirstNonEmpty(p.BindAccount, p.BindAccountSnake)
|
||||
ownerUserID := p.OwnerUserID
|
||||
if ownerUserID == nil {
|
||||
ownerUserID = p.OwnerUserIDSnake
|
||||
}
|
||||
ownerUserName := cursorFirstNonEmpty(p.OwnerUserName, p.OwnerUserNameSnake)
|
||||
remark := cursorFirstNonEmpty(p.Remark)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
var activationCode models.PlatformCursorActivationCode
|
||||
err := models.Orm.QueryTable(new(models.PlatformCursorActivationCode)).
|
||||
Filter("code", code).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&activationCode)
|
||||
if err == orm.ErrNoRows {
|
||||
c.jsonResult(404, "激活码不存在", nil)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
c.jsonResult(500, "激活码查询失败", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if activationCode.Status == 3 {
|
||||
c.jsonResult(403, "激活码已禁用", nil)
|
||||
return
|
||||
}
|
||||
if activationCode.Status == 2 || (activationCode.ExpiredAt != nil && activationCode.ExpiredAt.Before(now)) {
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformCursorActivationCode)).
|
||||
Filter("id", activationCode.ID).
|
||||
Update(map[string]interface{}{"status": int8(2), "update_time": now})
|
||||
c.jsonResult(410, "激活码已过期", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if activationCode.Status == 1 {
|
||||
if activationCode.MachineCode == nil || strings.TrimSpace(*activationCode.MachineCode) != machineCode {
|
||||
c.jsonResult(409, "激活码已被其他设备使用", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if activationCode.ExpiredAt != nil && activationCode.ExpiredAt.Before(now) {
|
||||
_, _ = models.Orm.QueryTable(new(models.PlatformCursorActivationCode)).
|
||||
Filter("id", activationCode.ID).
|
||||
Update(map[string]interface{}{"status": int8(2), "update_time": now})
|
||||
c.jsonResult(410, "激活码已过期", nil)
|
||||
return
|
||||
}
|
||||
|
||||
c.jsonResult(200, "success", map[string]interface{}{
|
||||
"activated": true,
|
||||
"reused": true,
|
||||
"activationId": activationCode.ID,
|
||||
"deviceId": activationCode.BindDeviceID,
|
||||
"machineCode": machineCode,
|
||||
"status": 1,
|
||||
"durationDays": activationCode.DurationDays,
|
||||
"activatedAt": activationCode.ActivatedAt,
|
||||
"expireTime": activationCode.ExpiredAt,
|
||||
"expiredAt": activationCode.ExpiredAt,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var device models.PlatformCursorEquipment
|
||||
deviceErr := models.Orm.QueryTable(new(models.PlatformCursorEquipment)).
|
||||
Filter("machine_code", machineCode).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&device)
|
||||
|
||||
created := false
|
||||
if deviceErr != nil && deviceErr != orm.ErrNoRows {
|
||||
c.jsonResult(500, "设备信息查询失败", nil)
|
||||
return
|
||||
}
|
||||
if deviceErr == nil && device.Status == 3 {
|
||||
c.jsonResult(403, "设备已禁用,无法激活", nil)
|
||||
return
|
||||
}
|
||||
|
||||
baseTime := now
|
||||
if deviceErr == nil && device.ExpireTime != nil && device.ExpireTime.After(now) {
|
||||
baseTime = *device.ExpireTime
|
||||
}
|
||||
|
||||
var expireTime *time.Time
|
||||
if activationCode.DurationDays > 0 {
|
||||
t := baseTime.AddDate(0, 0, activationCode.DurationDays)
|
||||
expireTime = &t
|
||||
}
|
||||
|
||||
txOrm, err := models.Orm.Begin()
|
||||
if err != nil {
|
||||
c.jsonResult(500, "开启事务失败", nil)
|
||||
return
|
||||
}
|
||||
|
||||
rollback := true
|
||||
defer func() {
|
||||
if rollback {
|
||||
_ = txOrm.Rollback()
|
||||
}
|
||||
}()
|
||||
|
||||
if deviceErr == orm.ErrNoRows {
|
||||
device = models.PlatformCursorEquipment{
|
||||
MachineCode: machineCode,
|
||||
Status: 1,
|
||||
DeviceInfo: cursorStringPtr(deviceInfo),
|
||||
System: cursorStringPtr(system),
|
||||
Version: cursorStringPtr(version),
|
||||
BindAccount: cursorStringPtr(bindAccount),
|
||||
OwnerUserID: ownerUserID,
|
||||
OwnerUserName: cursorStringPtr(ownerUserName),
|
||||
ActivationTime: &now,
|
||||
ExpireTime: expireTime,
|
||||
Remark: cursorStringPtr(remark),
|
||||
CreateTime: now,
|
||||
}
|
||||
id, insertErr := txOrm.Insert(&device)
|
||||
if insertErr != nil {
|
||||
c.jsonResult(500, "设备信息保存失败", nil)
|
||||
return
|
||||
}
|
||||
device.ID = uint64(id)
|
||||
created = true
|
||||
} else {
|
||||
if bindAccount == "" && device.BindAccount != nil {
|
||||
bindAccount = *device.BindAccount
|
||||
}
|
||||
if ownerUserID == nil {
|
||||
ownerUserID = device.OwnerUserID
|
||||
}
|
||||
if ownerUserName == "" && device.OwnerUserName != nil {
|
||||
ownerUserName = *device.OwnerUserName
|
||||
}
|
||||
|
||||
_, updateErr := txOrm.QueryTable(new(models.PlatformCursorEquipment)).
|
||||
Filter("id", device.ID).
|
||||
Update(map[string]interface{}{
|
||||
"device_info": cursorStringPtr(deviceInfo),
|
||||
"system": cursorStringPtr(system),
|
||||
"version": cursorStringPtr(version),
|
||||
"bind_account": cursorStringPtr(bindAccount),
|
||||
"owner_user_id": ownerUserID,
|
||||
"owner_user_name": cursorStringPtr(ownerUserName),
|
||||
"activation_time": now,
|
||||
"expire_time": expireTime,
|
||||
"status": int8(1),
|
||||
"remark": cursorStringPtr(remark),
|
||||
"update_time": now,
|
||||
})
|
||||
if updateErr != nil {
|
||||
c.jsonResult(500, "设备信息更新失败", nil)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
codeUpdateCount, updateCodeErr := txOrm.QueryTable(new(models.PlatformCursorActivationCode)).
|
||||
Filter("id", activationCode.ID).
|
||||
Filter("status", int8(0)).
|
||||
Filter("delete_time__isnull", true).
|
||||
Update(map[string]interface{}{
|
||||
"status": int8(1),
|
||||
"bind_account": cursorStringPtr(bindAccount),
|
||||
"bind_device_id": device.ID,
|
||||
"machine_code": machineCode,
|
||||
"device_info": cursorStringPtr(deviceInfo),
|
||||
"owner_user_id": ownerUserID,
|
||||
"owner_user_name": cursorStringPtr(ownerUserName),
|
||||
"activated_at": now,
|
||||
"expired_at": expireTime,
|
||||
"remark": cursorStringPtr(remark),
|
||||
"update_time": now,
|
||||
})
|
||||
if updateCodeErr != nil {
|
||||
c.jsonResult(500, "激活码绑定失败", nil)
|
||||
return
|
||||
}
|
||||
if codeUpdateCount == 0 {
|
||||
c.jsonResult(409, "激活码状态已变化,请重新查询后再试", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if err := txOrm.Commit(); err != nil {
|
||||
c.jsonResult(500, "提交事务失败", nil)
|
||||
return
|
||||
}
|
||||
rollback = false
|
||||
|
||||
c.jsonResult(200, "success", map[string]interface{}{
|
||||
"activated": true,
|
||||
"reused": false,
|
||||
"created": created,
|
||||
"activationId": activationCode.ID,
|
||||
"deviceId": device.ID,
|
||||
"machineCode": machineCode,
|
||||
"status": 1,
|
||||
"durationDays": activationCode.DurationDays,
|
||||
"activationAt": now,
|
||||
"activatedAt": now,
|
||||
"expireTime": expireTime,
|
||||
"expiredAt": expireTime,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user