设备管理优化
This commit is contained in:
parent
6788d49f47
commit
3a57f1cf14
@ -37,6 +37,25 @@ type cursorEquipmentReportPayload struct {
|
||||
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 != "" {
|
||||
@ -222,3 +241,263 @@ func (c *ApiCursorEquipmentController) Report() {
|
||||
"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,
|
||||
})
|
||||
}
|
||||
|
||||
@ -87,22 +87,86 @@ func cursorEquipmentStatusValid(status int8) bool {
|
||||
return status == 0 || status == 1 || status == 2 || status == 3
|
||||
}
|
||||
|
||||
func (c *PlatformCursorEquipmentController) cursorActivationSummary(row *models.PlatformCursorEquipment) (int64, *models.PlatformCursorActivationCode) {
|
||||
cond := orm.NewCondition().
|
||||
And("delete_time__isnull", true).
|
||||
AndCond(orm.NewCondition().
|
||||
Or("bind_device_id", row.ID).
|
||||
Or("machine_code", row.MachineCode))
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.PlatformCursorActivationCode)).SetCond(cond)
|
||||
count, _ := qs.Count()
|
||||
|
||||
var latest models.PlatformCursorActivationCode
|
||||
if err := qs.OrderBy("-activated_at", "-id").One(&latest); err != nil {
|
||||
return count, nil
|
||||
}
|
||||
|
||||
return count, &latest
|
||||
}
|
||||
|
||||
func (c *PlatformCursorEquipmentController) cursorExtractSummary() (int64, *models.PlatformAccountPoolCursor) {
|
||||
qs := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("is_extracted__gt", 0)
|
||||
|
||||
count, _ := qs.Count()
|
||||
|
||||
var latest models.PlatformAccountPoolCursor
|
||||
if err := qs.OrderBy("-extracted_time", "-id").One(&latest); err != nil {
|
||||
return count, nil
|
||||
}
|
||||
|
||||
return count, &latest
|
||||
}
|
||||
|
||||
func (c *PlatformCursorEquipmentController) rowToMap(row *models.PlatformCursorEquipment) map[string]interface{} {
|
||||
activationCount, latestActivation := c.cursorActivationSummary(row)
|
||||
extractCount, latestExtract := c.cursorExtractSummary()
|
||||
|
||||
var bindActivationCode interface{}
|
||||
var activationCodeId interface{}
|
||||
var lastActivatedAt interface{} = row.ActivationTime
|
||||
var expireTime interface{} = row.ExpireTime
|
||||
var lastExtractedAt interface{}
|
||||
if latestActivation != nil {
|
||||
bindActivationCode = latestActivation.Code
|
||||
activationCodeId = latestActivation.ID
|
||||
if latestActivation.ActivatedAt != nil {
|
||||
lastActivatedAt = latestActivation.ActivatedAt
|
||||
}
|
||||
if latestActivation.ExpiredAt != nil {
|
||||
expireTime = latestActivation.ExpiredAt
|
||||
}
|
||||
}
|
||||
if latestExtract != nil {
|
||||
lastExtractedAt = latestExtract.ExtractedTime
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"id": row.ID,
|
||||
"deviceInfo": row.DeviceInfo,
|
||||
"machineCode": row.MachineCode,
|
||||
"status": row.Status,
|
||||
"system": row.System,
|
||||
"version": row.Version,
|
||||
"bindAccount": row.BindAccount,
|
||||
"ownerUserId": row.OwnerUserID,
|
||||
"ownerUserName": row.OwnerUserName,
|
||||
"activationTime": row.ActivationTime,
|
||||
"expireTime": row.ExpireTime,
|
||||
"remark": row.Remark,
|
||||
"createTime": row.CreateTime,
|
||||
"updateTime": row.UpdateTime,
|
||||
"id": row.ID,
|
||||
"deviceInfo": row.DeviceInfo,
|
||||
"machineCode": row.MachineCode,
|
||||
"status": row.Status,
|
||||
"system": row.System,
|
||||
"os": row.System,
|
||||
"version": row.Version,
|
||||
"bindAccount": row.BindAccount,
|
||||
"bindActivationCode": bindActivationCode,
|
||||
"activationCode": bindActivationCode,
|
||||
"activationCodeId": activationCodeId,
|
||||
"ownerUserId": row.OwnerUserID,
|
||||
"ownerUserName": row.OwnerUserName,
|
||||
"activationTime": lastActivatedAt,
|
||||
"lastActivatedAt": lastActivatedAt,
|
||||
"expireTime": expireTime,
|
||||
"expiredAt": expireTime,
|
||||
"activationCount": activationCount,
|
||||
"extractCount": extractCount,
|
||||
"lastExtractedAt": lastExtractedAt,
|
||||
"remark": row.Remark,
|
||||
"createTime": row.CreateTime,
|
||||
"updateTime": row.UpdateTime,
|
||||
}
|
||||
}
|
||||
|
||||
@ -464,11 +528,74 @@ func (c *PlatformCursorEquipmentController) ActivationRecords() {
|
||||
return
|
||||
}
|
||||
|
||||
equipmentID, _ := c.GetUint64("equipmentId")
|
||||
if equipmentID == 0 {
|
||||
equipmentID, _ = c.GetUint64("id")
|
||||
}
|
||||
if equipmentID == 0 {
|
||||
c.jsonErr(400, 400, "缺少设备ID")
|
||||
return
|
||||
}
|
||||
|
||||
var equipment models.PlatformCursorEquipment
|
||||
if err := models.Orm.QueryTable(new(models.PlatformCursorEquipment)).
|
||||
Filter("id", equipmentID).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&equipment); err != nil {
|
||||
c.jsonErr(404, 404, "设备不存在")
|
||||
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
|
||||
}
|
||||
|
||||
cond := orm.NewCondition().
|
||||
And("delete_time__isnull", true).
|
||||
AndCond(orm.NewCondition().
|
||||
Or("bind_device_id", equipment.ID).
|
||||
Or("machine_code", equipment.MachineCode))
|
||||
|
||||
qs := models.Orm.QueryTable(new(models.PlatformCursorActivationCode)).SetCond(cond)
|
||||
total, _ := qs.Count()
|
||||
|
||||
var rows []models.PlatformCursorActivationCode
|
||||
if _, err := qs.OrderBy("-activated_at", "-id").Limit(pageSize, (page-1)*pageSize).All(&rows); err != nil {
|
||||
c.jsonErr(500, 500, "获取激活记录失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
list := make([]map[string]interface{}, 0, len(rows))
|
||||
for i := range rows {
|
||||
row := rows[i]
|
||||
list = append(list, map[string]interface{}{
|
||||
"id": row.ID,
|
||||
"code": row.Code,
|
||||
"activationCode": row.Code,
|
||||
"status": row.Status,
|
||||
"durationDays": row.DurationDays,
|
||||
"machineCode": row.MachineCode,
|
||||
"deviceInfo": row.DeviceInfo,
|
||||
"ownerUserId": row.OwnerUserID,
|
||||
"ownerUserName": row.OwnerUserName,
|
||||
"activatedAt": row.ActivatedAt,
|
||||
"expiredAt": row.ExpiredAt,
|
||||
"createdAt": row.CreateTime,
|
||||
"remark": row.Remark,
|
||||
})
|
||||
}
|
||||
|
||||
c.ok(map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"total": 0,
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
})
|
||||
@ -481,11 +608,73 @@ func (c *PlatformCursorEquipmentController) ExtractRecords() {
|
||||
return
|
||||
}
|
||||
|
||||
equipmentID, _ := c.GetUint64("equipmentId")
|
||||
if equipmentID == 0 {
|
||||
equipmentID, _ = c.GetUint64("id")
|
||||
}
|
||||
if equipmentID == 0 {
|
||||
c.jsonErr(400, 400, "缺少设备ID")
|
||||
return
|
||||
}
|
||||
|
||||
var equipment models.PlatformCursorEquipment
|
||||
if err := models.Orm.QueryTable(new(models.PlatformCursorEquipment)).
|
||||
Filter("id", equipmentID).
|
||||
Filter("delete_time__isnull", true).
|
||||
One(&equipment); err != nil {
|
||||
c.jsonErr(404, 404, "设备不存在")
|
||||
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 := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).
|
||||
Filter("delete_time__isnull", true).
|
||||
Filter("is_extracted__gt", 0)
|
||||
|
||||
total, _ := qs.Count()
|
||||
|
||||
var rows []models.PlatformAccountPoolCursor
|
||||
if _, err := qs.OrderBy("-extracted_time", "-id").Limit(pageSize, (page-1)*pageSize).All(&rows); err != nil {
|
||||
c.jsonErr(500, 500, "获取提取记录失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
list := make([]map[string]interface{}, 0, len(rows))
|
||||
for i := range rows {
|
||||
row := rows[i]
|
||||
content := buildCardResult(&row.Account, &row.Password, row.Token, row.DataType)
|
||||
list = append(list, map[string]interface{}{
|
||||
"id": row.ID,
|
||||
"status": row.IsExtracted,
|
||||
"isExtracted": row.IsExtracted,
|
||||
"platform": row.ExtractedPlatform,
|
||||
"extractedPlatform": row.ExtractedPlatform,
|
||||
"dataType": row.DataType,
|
||||
"type": row.DataType,
|
||||
"account": row.Account,
|
||||
"password": row.Password,
|
||||
"token": row.Token,
|
||||
"content": content,
|
||||
"extractedAt": row.ExtractedTime,
|
||||
"createdAt": row.ExtractedTime,
|
||||
"remark": row.Remark,
|
||||
})
|
||||
}
|
||||
|
||||
c.ok(map[string]interface{}{
|
||||
"list": []interface{}{},
|
||||
"total": 0,
|
||||
"list": list,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"pageSize": pageSize,
|
||||
})
|
||||
|
||||
@ -14,6 +14,9 @@ func Register() {
|
||||
// 登录器上报 Cursor 设备信息(无需登录)
|
||||
beego.Router("/api/cursor/equipment/report", &controllers.ApiCursorEquipmentController{}, "post:Report")
|
||||
|
||||
// 登录器使用激活码激活/续期 Cursor 设备(无需登录)
|
||||
beego.Router("/api/cursor/equipment/activateByCode", &controllers.ApiCursorEquipmentController{}, "post:ActivateByCode")
|
||||
|
||||
// 对外提卡接口(无需登录)
|
||||
// GET /api/getcard?type=xianyu&module=cursor&data_type=tk
|
||||
beego.Router("/api/getcard", &controllers.ApiGetCardController{}, "get:GetCard")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user