package controllers import ( "fmt" "strconv" "strings" "time" "server/models" "github.com/beego/beego/v2/client/orm" beego "github.com/beego/beego/v2/server/web" ) // ApiGetCardController 对外提卡接口(无需登录) // GET /api/getcard?type=xianyu&module=cursor type ApiGetCardController struct { beego.Controller } // validPlatformTypes 支持的来源平台 var validPlatformTypes = map[string]bool{ "xianyu": true, "pinduoduo": true, "jingdong": true, "douyin": true, "local": true, "xubei": true, } // validModules 支持的号池模块 var validModules = map[string]bool{ "cursor": true, "windsurf": true, "krio": true, } func (c *ApiGetCardController) cardErr(_ int, _ int, msg string) { c.Ctx.Output.SetStatus(200) c.Ctx.Output.Header("Content-Type", "text/plain; charset=utf-8") _ = c.Ctx.Output.Body([]byte("error:" + msg)) } func (c *ApiGetCardController) cardOK(text string) { c.Ctx.Output.SetStatus(200) c.Ctx.Output.Header("Content-Type", "text/plain; charset=utf-8") _ = c.Ctx.Output.Body([]byte(text)) } // GetCard 提取一张卡(不可重复提取) // GET /api/getcard?type=xianyu&module=cursor&data_type=tk // // 参数: // - type (必填) 来源平台:xianyu / taobao / pinduoduo / jingdong / local / xubei // - module (必填) 号池模块:cursor / windsurf / krio // - data_type (可选) 账号类型:account / tk / account_tk,不传则取任意未提取的 // - id/start_id/current_id (可选) 起始 ID:从该 ID 开始向后提取,避免传 896 却取到 892 func (c *ApiGetCardController) GetCard() { platform := c.GetString("type") module := c.GetString("module") dataType := c.GetString("data_type") startID, err := c.readOptionalStartID() if err != nil { c.cardErr(400, 400, err.Error()) return } // 参数校验 if platform == "" { c.cardErr(400, 400, "缺少参数 type(来源平台)") return } if !validPlatformTypes[platform] { c.cardErr(400, 400, fmt.Sprintf("不支持的平台类型: %s,支持: xianyu/taobao/pinduoduo/jingdong/local/xubei", platform)) return } if module == "" { c.cardErr(400, 400, "缺少参数 module(号池模块)") return } if !validModules[module] { c.cardErr(400, 400, fmt.Sprintf("不支持的模块: %s,支持: cursor/windsurf/krio", module)) return } if dataType != "" && !isValidPoolType(dataType) { c.cardErr(400, 400, "data_type 不合法,支持: account/tk/account_tk") return } now := time.Now() switch module { case "cursor": c.extractCursor(platform, dataType, startID, now) case "windsurf": c.extractWindsurf(platform, dataType, startID, now) case "krio": c.extractKrio(platform, dataType, startID, now) } } func (c *ApiGetCardController) readOptionalStartID() (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, nil } id, err := strconv.ParseUint(raw, 10, 64) if err != nil || id == 0 { return 0, fmt.Errorf("id/start_id/current_id 必须是大于 0 的整数") } return id, nil } func (c *ApiGetCardController) extractCursor(platform, dataType string, startID uint64, now time.Time) { c.extractWithProbe("cursor", platform, dataType, now, func() (uint64, *string, *string, string, string, *int8, error) { var row models.PlatformAccountPoolCursor qs := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)). Filter("is_extracted", 0). Filter("delete_time__isnull", true) if startID > 0 { qs = qs.Filter("id__gte", startID) } if dataType != "" { qs = qs.Filter("data_type", dataType) } if err := qs.OrderBy("id").One(&row); err != nil { return 0, nil, nil, "", "", nil, err } return row.ID, &row.Account, &row.Password, row.Token, row.DataType, row.IsUsed, nil }) } func (c *ApiGetCardController) extractWindsurf(platform, dataType string, startID uint64, now time.Time) { c.extractWithProbe("windsurf", platform, dataType, now, func() (uint64, *string, *string, string, string, *int8, error) { var row models.PlatformAccountPoolWindsurf qs := models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)). Filter("is_extracted", 0). Filter("delete_time__isnull", true) if startID > 0 { qs = qs.Filter("id__gte", startID) } if dataType != "" { qs = qs.Filter("data_type", dataType) } if err := qs.OrderBy("id").One(&row); err != nil { return 0, nil, nil, "", "", nil, err } return row.ID, &row.Account, &row.Password, row.Token, row.DataType, nil, nil }) } func (c *ApiGetCardController) extractKrio(platform, dataType string, startID uint64, now time.Time) { c.extractWithProbe("krio", platform, dataType, now, func() (uint64, *string, *string, string, string, *int8, error) { var row models.PlatformAccountPoolKiro qs := models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)). Filter("is_extracted", 0). Filter("delete_time__isnull", true) if startID > 0 { qs = qs.Filter("id__gte", startID) } if dataType != "" { qs = qs.Filter("data_type", dataType) } if err := qs.OrderBy("id").One(&row); err != nil { return 0, nil, nil, "", "", nil, err } return row.ID, &row.Account, &row.Password, row.Token, row.DataType, nil, nil }) } type poolRowFetcher func() (id uint64, account, password *string, token, rowDataType string, isUsed *int8, err error) // extractWithProbe 按 id 顺序提取并探测 Token 可用性;不可用则标记已提取并继续下一条。 func (c *ApiGetCardController) extractWithProbe( module, platform, dataType string, now time.Time, fetch poolRowFetcher, ) { for { id, account, password, token, rowDataType, isUsed, err := fetch() if err != nil { if err == orm.ErrNoRows { c.cardErr(404, 404, "暂无可用卡密") } else { c.cardErr(500, 500, "查询失败") } return } tableName := poolTableName(module) if tableName == "" { c.cardErr(500, 500, "无效模块") return } _, err = models.Orm.QueryTable(tableName). Filter("id", id). Update(map[string]interface{}{ "is_extracted": 1, "extracted_time": now, "extracted_platform": platform, "update_time": now, }) if err != nil { c.cardErr(500, 500, "提取失败") return } // 已有探测结论:可用则直接返回,不可用则继续下一条。 if known, available := poolIsUsedAvailable(isUsed); known { if available { c.cardOK(buildCardResult(account, password, token, rowDataType)) return } continue } if !poolProbeToken(module, rowDataType, token, id) { continue } c.cardOK(buildCardResult(account, password, token, rowDataType)) return } } // buildCardResult 根据账号类型返回格式化字符串 func buildCardResult(account, password *string, token string, dataType string) string { acc := "" pwd := "" if account != nil { acc = *account } if password != nil { pwd = *password } switch dataType { case "account": return fmt.Sprintf("账号:%s / 密码:%s", acc, pwd) case "account_tk": return fmt.Sprintf("账号:%s / 密码:%s / Token:%s", acc, pwd, token) default: // tk return token } }