package controllers import ( "fmt" "strconv" "strings" "time" "server/models" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" ) // ApiCursorDetectController Cursor Token 顺序读取接口(不改变号池状态) // // 用途: // - 前端传入 start_id/current_id/id,直接读取该 ID 对应的 Cursor Token。 // - 只读取记录,不更新 is_extracted、extracted_time、extracted_platform、is_used 等任何状态。 // - 不再跳过已提取的记录,输入什么 ID 就提取什么 ID。 // - 返回 next_id,前端下一次点击时传 next_id,即可实现 11 -> 12 -> 13 递增读取。 // // 示例: // // GET /api/cursor/token/peek?id=11 // GET /api/cursor/token/peek?start_id=11 // GET /api/cursor/token/peek?current_id=11 // // 可选参数: // - data_type=tk/account/account_tk,默认 tk type ApiCursorDetectController struct { beego.Controller } func (c *ApiCursorDetectController) cursorDetectJSONErr(httpStatus, code int, msg string) { c.Ctx.Output.SetStatus(httpStatus) c.Data["json"] = map[string]interface{}{ "code": code, "msg": msg, } _ = c.ServeJSON() } func (c *ApiCursorDetectController) setTokenUsableByID(isUsed int8) { id, err := c.readStartID() if err != nil { c.cursorDetectJSONErr(400, 400, err.Error()) return } now := time.Now() updated, err := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)). Filter("id", id). Filter("delete_time__isnull", true). Update(orm.Params{ "is_used": isUsed, "update_time": now, }) if err != nil { c.cursorDetectJSONErr(500, 500, "更新失败: "+err.Error()) return } if updated == 0 { c.cursorDetectJSONErr(404, 404, "当前 ID 数据不存在或已删除") return } c.Data["json"] = map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "id": id, "is_used": isUsed, "is_available": isUsed == 1, "state_changed": true, "update_time": now, }, } _ = c.ServeJSON() } // MarkTokenAvailable 将当前 ID 的 Cursor Token 标记为可用。 func (c *ApiCursorDetectController) MarkTokenAvailable() { c.setTokenUsableByID(1) } // MarkTokenUnavailable 将当前 ID 的 Cursor Token 标记为不可用/用完。 func (c *ApiCursorDetectController) MarkTokenUnavailable() { c.setTokenUsableByID(0) } // PeekToken 按 ID 顺序读取 Cursor Token,不改变任何状态。 func (c *ApiCursorDetectController) PeekToken() { startID, err := c.readStartID() if err != nil { c.cursorDetectJSONErr(400, 400, err.Error()) return } dataType := strings.TrimSpace(c.GetString("data_type")) if dataType == "" { dataType = "tk" } if !isValidPoolType(dataType) { c.cursorDetectJSONErr(400, 400, "data_type 不合法,支持: account/tk/account_tk") return } var row models.PlatformAccountPoolCursor qs := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)). Filter("id", startID). Filter("data_type", dataType). Filter("delete_time__isnull", true). Exclude("token", "") err = qs.One(&row) if err != nil { if err == orm.ErrNoRows { c.cursorDetectJSONErr(404, 404, "指定 ID 的 Cursor Token 不存在或已删除") return } c.cursorDetectJSONErr(500, 500, "查询失败: "+err.Error()) return } c.Data["json"] = map[string]interface{}{ "code": 200, "msg": "success", "data": map[string]interface{}{ "id": row.ID, "next_id": row.ID + 1, "data_type": row.DataType, "account": row.Account, "password": row.Password, "token": row.Token, "remark": row.Remark, "is_used": row.IsUsed, "create_time": row.CreateTime, // 明确告诉前端:本接口只是读取,没有更新号池状态。 "state_changed": false, }, } _ = c.ServeJSON() } func (c *ApiCursorDetectController) readStartID() (uint64, error) { raw := strings.TrimSpace(c.GetString("id")) if raw == "" { raw = strings.TrimSpace(c.GetString("start_id")) } if raw == "" { raw = strings.TrimSpace(c.GetString("current_id")) } if raw == "" { return 0, fmt.Errorf("缺少参数 id/start_id/current_id") } id, err := strconv.ParseUint(raw, 10, 64) if err != nil || id == 0 { return 0, fmt.Errorf("id 必须是大于 0 的整数") } return id, nil }