yunzerwebsiteallinone/go/controllers/platform_account_pool.go
2026-06-05 13:18:57 +08:00

1161 lines
35 KiB
Go
Raw 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 (
"encoding/json"
"fmt"
"io"
"net/http"
"strconv"
"strings"
"time"
"server/models"
"server/pkg/jwtutil"
"server/pkg/tokenprobe"
"github.com/beego/beego/v2/client/orm"
beego "github.com/beego/beego/v2/server/web"
)
type PlatformAccountPoolCursorController struct{ beego.Controller }
type PlatformAccountPoolWindsurfController struct{ beego.Controller }
type PlatformAccountPoolKrioController struct{ beego.Controller }
type accountPoolCreateRow struct {
DataType string `json:"type"`
Account string `json:"account"`
Password string `json:"password"`
Token string `json:"token"`
Remark string `json:"remark"`
}
func requirePlatformAuth(c *beego.Controller) (*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 poolJSONErr(c *beego.Controller, httpStatus, code int, msg string) {
c.Ctx.Output.SetStatus(httpStatus)
c.Data["json"] = map[string]interface{}{"code": code, "msg": msg}
_ = c.ServeJSON()
}
func isValidPoolType(t string) bool {
return t == "account" || t == "tk" || t == "account_tk"
}
func validateCreateRow(row accountPoolCreateRow) error {
if !isValidPoolType(row.DataType) {
return fmt.Errorf("账号类型不正确")
}
row.Account = strings.TrimSpace(row.Account)
row.Password = strings.TrimSpace(row.Password)
row.Token = strings.TrimSpace(row.Token)
if row.DataType == "account" && (row.Account == "" || row.Password == "") {
return fmt.Errorf("账号密码类型必须填写账号和密码")
}
if row.DataType == "tk" && row.Token == "" {
return fmt.Errorf("token类型必须填写token")
}
if row.DataType == "account_tk" && (row.Account == "" || row.Token == "") {
return fmt.Errorf("账号密码+token类型必须填写账号和token密码可为空")
}
return nil
}
// accountPoolListWhere 列表筛选(与各号池表字段一致)
func accountPoolListWhere(dataType, status, platform, keyword, account, token, remark string) (where string, args []interface{}) {
var parts []string
if dataType != "" && isValidPoolType(dataType) {
parts = append(parts, "data_type = ?")
args = append(args, dataType)
}
switch status {
case "unused":
parts = append(parts, "is_extracted = ?")
args = append(args, int8(0))
case "extracted":
parts = append(parts, "is_extracted = ?")
args = append(args, int8(1))
case "replenished":
parts = append(parts, "is_extracted = ?")
args = append(args, int8(2))
case "renewed":
parts = append(parts, "is_extracted = ?")
args = append(args, int8(3))
}
if p := strings.TrimSpace(platform); p != "" {
parts = append(parts, "extracted_platform = ?")
args = append(args, p)
}
if acc := strings.TrimSpace(account); acc != "" {
parts = append(parts, "account LIKE ?")
args = append(args, "%"+acc+"%")
}
if tk := strings.TrimSpace(token); tk != "" {
parts = append(parts, "token LIKE ?")
args = append(args, "%"+tk+"%")
}
if rm := strings.TrimSpace(remark); rm != "" {
parts = append(parts, "remark LIKE ?")
args = append(args, "%"+rm+"%")
}
// 兼容旧版前端 keyword 参数:仅作为账号查询,不再合并 token/备注。
if kw := strings.TrimSpace(keyword); kw != "" {
parts = append(parts, "account LIKE ?")
args = append(args, "%"+kw+"%")
}
if len(parts) == 0 {
return "1=1", args
}
return strings.Join(parts, " AND "), args
}
func accountPoolCountMySQL(table, where string, whereArgs []interface{}) (int64, error) {
sqlStr := fmt.Sprintf("SELECT COUNT(*) AS cnt FROM `%s` WHERE %s", table, where)
var maps []orm.Params
_, err := models.Orm.Raw(sqlStr, whereArgs...).Values(&maps)
if err != nil {
return 0, err
}
if len(maps) == 0 {
return 0, nil
}
return paramsCellToInt64(maps[0]["cnt"]), nil
}
func paramsCellToInt64(v interface{}) int64 {
if v == nil {
return 0
}
switch x := v.(type) {
case []byte:
n, _ := strconv.ParseInt(strings.TrimSpace(string(x)), 10, 64)
return n
case int64:
return x
case int32:
return int64(x)
case int:
return int64(x)
default:
n, _ := strconv.ParseInt(strings.TrimSpace(fmt.Sprint(x)), 10, 64)
return n
}
}
func listPoolRows(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
page, _ := c.GetInt("page", 1)
pageSize, _ := c.GetInt("pageSize", 30)
if page < 1 {
page = 1
}
if pageSize < 1 {
pageSize = 30
}
if pageSize > 200 {
pageSize = 200
}
keyword := strings.TrimSpace(c.GetString("keyword"))
account := strings.TrimSpace(c.GetString("account"))
token := strings.TrimSpace(c.GetString("token"))
remark := strings.TrimSpace(c.GetString("remark"))
dataType := strings.TrimSpace(c.GetString("type"))
status := strings.TrimSpace(c.GetString("status"))
platform := strings.TrimSpace(c.GetString("platform"))
where, whereArgs := accountPoolListWhere(dataType, status, platform, keyword, account, token, remark)
if module == "cursor" {
u := strings.TrimSpace(c.GetString("usable"))
if u == "1" || u == "0" {
if v, err := strconv.ParseInt(u, 10, 8); err == nil {
where = "(" + where + ") AND is_used = ?"
whereArgs = append(whereArgs, int8(v))
}
}
}
offset := (page - 1) * pageSize
var list interface{}
var total int64
var err error
switch module {
case "cursor":
table := (&models.PlatformAccountPoolCursor{}).TableName()
total, err = accountPoolCountMySQL(table, where, whereArgs)
if err != nil {
poolJSONErr(c, 500, 500, "获取列表失败: "+err.Error())
return
}
sqlStr := fmt.Sprintf(
"SELECT * FROM `%s` WHERE %s ORDER BY FIELD(is_extracted, 0, 2, 3, 1) ASC, id DESC LIMIT ? OFFSET ?",
table, where,
)
args := append(append([]interface{}{}, whereArgs...), pageSize, offset)
var rows []models.PlatformAccountPoolCursor
_, err = models.Orm.Raw(sqlStr, args...).QueryRows(&rows)
if err != nil && err != orm.ErrNoRows {
poolJSONErr(c, 500, 500, "cursor查询失败: "+err.Error())
return
}
if rows == nil {
rows = []models.PlatformAccountPoolCursor{}
}
list = rows
case "windsurf":
table := (&models.PlatformAccountPoolWindsurf{}).TableName()
total, err = accountPoolCountMySQL(table, where, whereArgs)
if err != nil {
poolJSONErr(c, 500, 500, "获取列表失败: "+err.Error())
return
}
sqlStr := fmt.Sprintf(
"SELECT * FROM `%s` WHERE %s ORDER BY FIELD(is_extracted, 0, 2, 3, 1) ASC, id DESC LIMIT ? OFFSET ?",
table, where,
)
args := append(append([]interface{}{}, whereArgs...), pageSize, offset)
var rows []models.PlatformAccountPoolWindsurf
_, err = models.Orm.Raw(sqlStr, args...).QueryRows(&rows)
if err != nil && err != orm.ErrNoRows {
poolJSONErr(c, 500, 500, "获取列表失败: "+err.Error())
return
}
if rows == nil {
rows = []models.PlatformAccountPoolWindsurf{}
}
list = rows
case "krio":
table := (&models.PlatformAccountPoolKiro{}).TableName()
total, err = accountPoolCountMySQL(table, where, whereArgs)
if err != nil {
poolJSONErr(c, 500, 500, "获取列表失败: "+err.Error())
return
}
sqlStr := fmt.Sprintf(
"SELECT * FROM `%s` WHERE %s ORDER BY FIELD(is_extracted, 0, 2, 3, 1) ASC, id DESC LIMIT ? OFFSET ?",
table, where,
)
args := append(append([]interface{}{}, whereArgs...), pageSize, offset)
var rows []models.PlatformAccountPoolKiro
_, err = models.Orm.Raw(sqlStr, args...).QueryRows(&rows)
if err != nil && err != orm.ErrNoRows {
poolJSONErr(c, 500, 500, "获取列表失败: "+err.Error())
return
}
if rows == nil {
rows = []models.PlatformAccountPoolKiro{}
}
list = rows
default:
poolJSONErr(c, 400, 400, "无效模块")
return
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": map[string]interface{}{
"list": list,
"total": total,
},
}
_ = c.ServeJSON()
}
func addPoolRow(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var row accountPoolCreateRow
if err := json.Unmarshal(raw, &row); err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
if err := validateCreateRow(row); err != nil {
poolJSONErr(c, 400, 400, err.Error())
return
}
switch module {
case "cursor":
r := &models.PlatformAccountPoolCursor{
DataType: strings.TrimSpace(row.DataType),
Account: strings.TrimSpace(row.Account),
Password: strings.TrimSpace(row.Password),
Token: strings.TrimSpace(row.Token),
Remark: strings.TrimSpace(row.Remark),
IsExtracted: 0,
}
_, err = models.Orm.Insert(r)
case "windsurf":
r := &models.PlatformAccountPoolWindsurf{
DataType: strings.TrimSpace(row.DataType),
Account: strings.TrimSpace(row.Account),
Password: strings.TrimSpace(row.Password),
Token: strings.TrimSpace(row.Token),
Remark: strings.TrimSpace(row.Remark),
IsExtracted: 0,
}
_, err = models.Orm.Insert(r)
case "krio":
r := &models.PlatformAccountPoolKiro{
DataType: strings.TrimSpace(row.DataType),
Account: strings.TrimSpace(row.Account),
Password: strings.TrimSpace(row.Password),
Token: strings.TrimSpace(row.Token),
Remark: strings.TrimSpace(row.Remark),
IsExtracted: 0,
}
_, err = models.Orm.Insert(r)
default:
poolJSONErr(c, 400, 400, "无效模块")
return
}
if err != nil {
poolJSONErr(c, 500, 500, "添加失败: "+err.Error())
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "添加成功"}
_ = c.ServeJSON()
}
func batchAddPoolRows(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var payload struct {
Rows []accountPoolCreateRow `json:"rows"`
}
if err := json.Unmarshal(raw, &payload); err != nil || len(payload.Rows) == 0 {
poolJSONErr(c, 400, 400, "参数错误")
return
}
for _, row := range payload.Rows {
if err := validateCreateRow(row); err != nil {
poolJSONErr(c, 400, 400, err.Error())
return
}
}
for _, row := range payload.Rows {
switch module {
case "cursor":
_, err = models.Orm.Insert(&models.PlatformAccountPoolCursor{
DataType: strings.TrimSpace(row.DataType),
Account: strings.TrimSpace(row.Account),
Password: strings.TrimSpace(row.Password),
Token: strings.TrimSpace(row.Token),
Remark: strings.TrimSpace(row.Remark),
IsExtracted: 0,
})
case "windsurf":
_, err = models.Orm.Insert(&models.PlatformAccountPoolWindsurf{
DataType: strings.TrimSpace(row.DataType),
Account: strings.TrimSpace(row.Account),
Password: strings.TrimSpace(row.Password),
Token: strings.TrimSpace(row.Token),
Remark: strings.TrimSpace(row.Remark),
IsExtracted: 0,
})
case "krio":
_, err = models.Orm.Insert(&models.PlatformAccountPoolKiro{
DataType: strings.TrimSpace(row.DataType),
Account: strings.TrimSpace(row.Account),
Password: strings.TrimSpace(row.Password),
Token: strings.TrimSpace(row.Token),
Remark: strings.TrimSpace(row.Remark),
IsExtracted: 0,
})
default:
poolJSONErr(c, 400, 400, "无效模块")
return
}
if err != nil {
poolJSONErr(c, 500, 500, "批量添加失败: "+err.Error())
return
}
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "批量添加成功"}
_ = c.ServeJSON()
}
func getPoolDetail(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
id, err := strconv.ParseUint(c.Ctx.Input.Param(":id"), 10, 64)
if err != nil || id == 0 {
poolJSONErr(c, 400, 400, "无效ID")
return
}
switch module {
case "cursor":
var row models.PlatformAccountPoolCursor
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("id", id).One(&row); err != nil {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": row}
case "windsurf":
var row models.PlatformAccountPoolWindsurf
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).Filter("id", id).One(&row); err != nil {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": row}
case "krio":
var row models.PlatformAccountPoolKiro
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).Filter("id", id).One(&row); err != nil {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "success", "data": row}
default:
poolJSONErr(c, 400, 400, "无效模块")
return
}
_ = c.ServeJSON()
}
func extractPoolRow(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var payload struct {
ID uint64 `json:"id"`
Type string `json:"type"`
Platform string `json:"platform"` // local | xianyu | taobao | pinduoduo | jingdong | douyin | ziyoushangcheng
Remark string `json:"remark"`
Replenish bool `json:"replenish"` // true 时写入 is_extracted=2补号否则为 1已提取
}
if err := json.Unmarshal(raw, &payload); err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
if payload.Platform != "local" &&
payload.Platform != "xianyu" &&
payload.Platform != "taobao" &&
payload.Platform != "pinduoduo" &&
payload.Platform != "jingdong" &&
payload.Platform != "douyin" &&
payload.Platform != "ziyoushangcheng" {
poolJSONErr(c, 400, 400, "提取平台错误")
return
}
if payload.Type != "" && !isValidPoolType(payload.Type) {
poolJSONErr(c, 400, 400, "提取类型错误")
return
}
now := time.Now()
platform := payload.Platform
remark := strings.TrimSpace(payload.Remark)
extractStatus := int8(1)
if payload.Replenish {
extractStatus = 2
}
switch module {
case "cursor":
var row models.PlatformAccountPoolCursor
qs := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("is_extracted", 0)
if payload.ID > 0 {
qs = qs.Filter("id", payload.ID)
} else if payload.Type != "" {
qs = qs.Filter("data_type", payload.Type)
}
if err := qs.OrderBy("id").One(&row); err != nil {
poolJSONErr(c, 404, 404, "没有可提取数据")
return
}
_, err = models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("id", row.ID).Update(map[string]interface{}{
"is_extracted": extractStatus,
"extracted_time": now,
"extracted_platform": platform,
"remark": remark,
})
if err != nil {
poolJSONErr(c, 500, 500, "提取失败: "+err.Error())
return
}
row.IsExtracted = extractStatus
row.ExtractedTime = &now
pf := platform
row.ExtractedPlatform = &pf
row.Remark = remark
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "提取成功", "data": row}
case "windsurf":
var row models.PlatformAccountPoolWindsurf
qs := models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).Filter("is_extracted", 0)
if payload.ID > 0 {
qs = qs.Filter("id", payload.ID)
} else if payload.Type != "" {
qs = qs.Filter("data_type", payload.Type)
}
if err := qs.OrderBy("id").One(&row); err != nil {
poolJSONErr(c, 404, 404, "没有可提取数据")
return
}
_, err = models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).Filter("id", row.ID).Update(map[string]interface{}{
"is_extracted": extractStatus,
"extracted_time": now,
"extracted_platform": platform,
"remark": remark,
})
if err != nil {
poolJSONErr(c, 500, 500, "提取失败: "+err.Error())
return
}
row.IsExtracted = extractStatus
row.ExtractedTime = &now
pf := platform
row.ExtractedPlatform = &pf
row.Remark = remark
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "提取成功", "data": row}
case "krio":
var row models.PlatformAccountPoolKiro
qs := models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).Filter("is_extracted", 0)
if payload.ID > 0 {
qs = qs.Filter("id", payload.ID)
} else if payload.Type != "" {
qs = qs.Filter("data_type", payload.Type)
}
if err := qs.OrderBy("id").One(&row); err != nil {
poolJSONErr(c, 404, 404, "没有可提取数据")
return
}
_, err = models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).Filter("id", row.ID).Update(map[string]interface{}{
"is_extracted": extractStatus,
"extracted_time": now,
"extracted_platform": platform,
"remark": remark,
})
if err != nil {
poolJSONErr(c, 500, 500, "提取失败: "+err.Error())
return
}
row.IsExtracted = extractStatus
row.ExtractedTime = &now
pf := platform
row.ExtractedPlatform = &pf
row.Remark = remark
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "提取成功", "data": row}
default:
poolJSONErr(c, 400, 400, "无效模块")
return
}
_ = c.ServeJSON()
}
func replenishPoolRow(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var payload struct {
Type string `json:"type"`
Platform string `json:"platform"`
Remark string `json:"remark"`
}
if err := json.Unmarshal(raw, &payload); err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
if !isValidPoolType(payload.Type) {
poolJSONErr(c, 400, 400, "账号类型不正确")
return
}
validPlatforms := map[string]bool{"local": true, "xianyu": true, "pinduoduo": true, "jingdong": true, "douyin": true}
if !validPlatforms[payload.Platform] {
poolJSONErr(c, 400, 400, "提取平台错误")
return
}
now := time.Now()
platform := payload.Platform
remark := strings.TrimSpace(payload.Remark)
switch module {
case "cursor":
checkedCount := 0
unavailableCount := 0
for {
var row models.PlatformAccountPoolCursor
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).
Filter("is_extracted", 0).Filter("data_type", payload.Type).
OrderBy("id").One(&row); err != nil {
msg := "暂无可用账号"
if checkedCount > 0 {
msg = fmt.Sprintf("已检测%d个账号其中%d个不可用暂无可用账号", checkedCount, unavailableCount)
}
poolJSONErr(c, 404, 404, msg)
return
}
checkedCount++
isAvailable := poolProbeToken("cursor", row.DataType, row.Token, row.ID)
if !isAvailable {
unavailableCount++
if _, err := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("id", row.ID).Update(map[string]interface{}{
// 补号流程检测出来不可用/已用完的号,仍然归类为“补号”记录。
// 不要写成已提取/已用完状态;只有接口提取后再标记不可用的号才归到已提取侧。
"is_extracted": int8(2),
"is_used": int8(0),
"extracted_time": now,
"extracted_platform": platform,
"remark": remark,
"update_time": now,
}); err != nil {
poolJSONErr(c, 500, 500, "补号检测失败: "+err.Error())
return
}
continue
}
if _, err := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("id", row.ID).Update(map[string]interface{}{
"is_extracted": int8(2),
"is_used": int8(1),
"extracted_time": now,
"extracted_platform": platform,
"remark": remark,
"update_time": now,
}); err != nil {
poolJSONErr(c, 500, 500, "补号失败: "+err.Error())
return
}
row.IsExtracted = 2
isUsed := int8(1)
row.IsUsed = &isUsed
row.ExtractedTime = &now
row.ExtractedPlatform = &platform
row.Remark = remark
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "补号成功",
"data": row,
"probe": map[string]interface{}{
"checkedCount": checkedCount,
"unavailableCount": unavailableCount,
},
}
break
}
case "windsurf":
var row models.PlatformAccountPoolWindsurf
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).
Filter("is_extracted", 0).Filter("data_type", payload.Type).
OrderBy("id").One(&row); err != nil {
poolJSONErr(c, 404, 404, "暂无可用账号")
return
}
if _, err = models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).Filter("id", row.ID).Update(map[string]interface{}{
"is_extracted": int8(2), "extracted_time": now, "extracted_platform": platform, "remark": remark,
}); err != nil {
poolJSONErr(c, 500, 500, "补号失败: "+err.Error())
return
}
row.IsExtracted = 2
row.ExtractedTime = &now
row.ExtractedPlatform = &platform
row.Remark = remark
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "补号成功", "data": row}
case "krio":
var row models.PlatformAccountPoolKiro
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).
Filter("is_extracted", 0).Filter("data_type", payload.Type).
OrderBy("id").One(&row); err != nil {
poolJSONErr(c, 404, 404, "暂无可用账号")
return
}
if _, err = models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).Filter("id", row.ID).Update(map[string]interface{}{
"is_extracted": int8(2), "extracted_time": now, "extracted_platform": platform, "remark": remark,
}); err != nil {
poolJSONErr(c, 500, 500, "补号失败: "+err.Error())
return
}
row.IsExtracted = 2
row.ExtractedTime = &now
row.ExtractedPlatform = &platform
row.Remark = remark
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "补号成功", "data": row}
default:
poolJSONErr(c, 400, 400, "无效模块")
return
}
_ = c.ServeJSON()
}
func updatePoolRemark(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var payload struct {
ID uint64 `json:"id"`
Remark string `json:"remark"`
}
if err := json.Unmarshal(raw, &payload); err != nil || payload.ID == 0 {
poolJSONErr(c, 400, 400, "参数错误")
return
}
remark := strings.TrimSpace(payload.Remark)
var updated int64
switch module {
case "cursor":
updated, err = models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("id", payload.ID).Update(map[string]interface{}{
"remark": remark,
})
case "windsurf":
updated, err = models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).Filter("id", payload.ID).Update(map[string]interface{}{
"remark": remark,
})
case "krio":
updated, err = models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).Filter("id", payload.ID).Update(map[string]interface{}{
"remark": remark,
})
default:
poolJSONErr(c, 400, 400, "无效模块")
return
}
if err != nil {
poolJSONErr(c, 500, 500, "备注更新失败: "+err.Error())
return
}
if updated == 0 {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "备注更新成功"}
_ = c.ServeJSON()
}
func validExtractPlatform(platform string) bool {
switch platform {
case "local", "xianyu", "taobao", "pinduoduo", "jingdong", "douyin", "ziyoushangcheng":
return true
default:
return false
}
}
func updatePoolExtractFields(module string, id uint64, fields map[string]interface{}) (int64, error) {
switch module {
case "cursor":
return models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("id", id).Update(fields)
case "windsurf":
return models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).Filter("id", id).Update(fields)
case "krio":
return models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).Filter("id", id).Update(fields)
default:
return 0, fmt.Errorf("无效模块")
}
}
func setPoolUnavailable(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var payload struct {
ID uint64 `json:"id"`
}
if err := json.Unmarshal(raw, &payload); err != nil || payload.ID == 0 {
poolJSONErr(c, 400, 400, "参数错误")
return
}
now := time.Now()
fields := map[string]interface{}{
"is_extracted": int8(1),
"extracted_time": now,
"extracted_platform": "local",
"update_time": now,
}
if module == "cursor" {
fields["is_used"] = int8(0)
}
updated, err := updatePoolExtractFields(module, payload.ID, fields)
if err != nil {
poolJSONErr(c, 500, 500, "改不可用失败: "+err.Error())
return
}
if updated == 0 {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "已标记不可用"}
_ = c.ServeJSON()
}
func updatePoolPlatform(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var payload struct {
ID uint64 `json:"id"`
Platform string `json:"platform"`
}
if err := json.Unmarshal(raw, &payload); err != nil || payload.ID == 0 {
poolJSONErr(c, 400, 400, "参数错误")
return
}
platform := strings.TrimSpace(payload.Platform)
if !validExtractPlatform(platform) {
poolJSONErr(c, 400, 400, "提取平台错误")
return
}
now := time.Now()
updated, err := updatePoolExtractFields(module, payload.ID, map[string]interface{}{
"extracted_platform": platform,
"update_time": now,
})
if err != nil {
poolJSONErr(c, 500, 500, "平台更新失败: "+err.Error())
return
}
if updated == 0 {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "平台更新成功"}
_ = c.ServeJSON()
}
func unextractPoolRow(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var payload struct {
ID uint64 `json:"id"`
}
if err := json.Unmarshal(raw, &payload); err != nil || payload.ID == 0 {
poolJSONErr(c, 400, 400, "参数错误")
return
}
now := time.Now()
updated, err := updatePoolExtractFields(module, payload.ID, map[string]interface{}{
"is_extracted": int8(0),
"extracted_time": nil,
"extracted_platform": nil,
"update_time": now,
})
if err != nil {
poolJSONErr(c, 500, 500, "反提取失败: "+err.Error())
return
}
if updated == 0 {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
c.Data["json"] = map[string]interface{}{"code": 200, "msg": "反提取成功"}
_ = c.ServeJSON()
}
func probePoolToken(c *beego.Controller, module string) {
if _, err := requirePlatformAuth(c); err != nil {
poolJSONErr(c, 401, 401, err.Error())
return
}
raw, err := io.ReadAll(c.Ctx.Request.Body)
if err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var payload struct {
ID uint64 `json:"id"`
AccessToken string `json:"accessToken"`
Token string `json:"token"`
}
if err := json.Unmarshal(raw, &payload); err != nil {
poolJSONErr(c, 400, 400, "参数错误")
return
}
var token string
switch module {
case "cursor":
token = strings.TrimSpace(payload.AccessToken)
if token == "" {
token = strings.TrimSpace(payload.Token)
}
if token == "" {
if payload.ID == 0 {
poolJSONErr(c, 400, 400, "请传入 Cursor 的 accessToken会话 JWT或传 id 从库中读取")
return
}
var row models.PlatformAccountPoolCursor
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("id", payload.ID).One(&row); err != nil {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
token = strings.TrimSpace(row.Token)
}
case "windsurf":
if payload.ID == 0 {
poolJSONErr(c, 400, 400, "缺少有效 id")
return
}
var row models.PlatformAccountPoolWindsurf
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolWindsurf)).Filter("id", payload.ID).One(&row); err != nil {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
token = strings.TrimSpace(row.Token)
case "krio":
if payload.ID == 0 {
poolJSONErr(c, 400, 400, "缺少有效 id")
return
}
var row models.PlatformAccountPoolKiro
if err := models.Orm.QueryTable(new(models.PlatformAccountPoolKiro)).Filter("id", payload.ID).One(&row); err != nil {
poolJSONErr(c, 404, 404, "记录不存在")
return
}
token = strings.TrimSpace(row.Token)
default:
poolJSONErr(c, 400, 400, "无效模块")
return
}
if token == "" {
poolJSONErr(c, 400, 400, "该记录无 Token无法探测")
return
}
r := tokenprobe.ProbeOfficial(module, token)
data := map[string]interface{}{
"ok": r.OK,
"detail": r.Detail,
"httpStatus": r.HTTPStatus,
}
if r.ProbeMessage != "" {
data["probeMessage"] = r.ProbeMessage
}
if r.Endpoint != "" {
data["endpoint"] = r.Endpoint
}
if r.BytesRead > 0 {
data["bytesRead"] = r.BytesRead
}
if r.RawPreview != "" {
data["rawPreview"] = r.RawPreview
}
if r.RequestBodyPrefixHex != "" {
data["requestBodyPrefixHex"] = r.RequestBodyPrefixHex
}
if r.StreamProtocol != "" {
data["streamProtocol"] = r.StreamProtocol
}
if r.StreamNote != "" {
data["streamNote"] = r.StreamNote
}
// Cursor 探测状态只按底层探针结论 r.OK 保存。
// 注意:客户端版本过旧只是 warningToken 仍可用时 r.OK=true不能因此写成已用完。
if module == "cursor" && payload.ID > 0 && r.HTTPStatus == http.StatusOK {
isUsed := int8(0)
if r.OK {
isUsed = 1
}
if _, uerr := models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).Filter("id", payload.ID).Update(orm.Params{
"is_used": isUsed,
"update_time": time.Now(),
}); uerr == nil {
data["is_used"] = int(isUsed)
}
}
c.Data["json"] = map[string]interface{}{
"code": 200,
"msg": "success",
"data": data,
}
_ = c.ServeJSON()
}
func poolTableName(module string) string {
switch module {
case "cursor":
return (&models.PlatformAccountPoolCursor{}).TableName()
case "windsurf":
return (&models.PlatformAccountPoolWindsurf{}).TableName()
case "krio":
return (&models.PlatformAccountPoolKiro{}).TableName()
default:
return ""
}
}
func poolIsUsedAvailable(isUsed *int8) (known bool, available bool) {
if isUsed == nil {
return false, false
}
switch *isUsed {
case 1:
return true, true
case 0:
return true, false
default:
return false, false
}
}
func poolProbeToken(module, rowDataType, token string, id uint64) bool {
token = strings.TrimSpace(token)
if rowDataType == "account" || token == "" {
return true
}
r := tokenprobe.ProbeOfficial(module, token)
// Cursor 自动探测只按底层探针结论 r.OK 判定。
// 客户端版本过旧是 warning不代表 Token 已用完;只有 tokenprobe 明确判定额度用尽/不可用时 r.OK 才为 false。
available := r.OK
// 更新数据库中的 is_used 字段
if module == "cursor" && id > 0 {
isUsed := int8(0)
if available {
isUsed = 1
}
_, _ = models.Orm.QueryTable(new(models.PlatformAccountPoolCursor)).
Filter("id", id).
Update(orm.Params{
"is_used": isUsed,
"update_time": time.Now(),
})
}
return available
}
func (c *PlatformAccountPoolCursorController) List() { listPoolRows(&c.Controller, "cursor") }
func (c *PlatformAccountPoolCursorController) Add() { addPoolRow(&c.Controller, "cursor") }
func (c *PlatformAccountPoolCursorController) BatchAdd() { batchAddPoolRows(&c.Controller, "cursor") }
func (c *PlatformAccountPoolCursorController) Detail() { getPoolDetail(&c.Controller, "cursor") }
func (c *PlatformAccountPoolCursorController) Extract() { extractPoolRow(&c.Controller, "cursor") }
func (c *PlatformAccountPoolCursorController) Replenish() { replenishPoolRow(&c.Controller, "cursor") }
func (c *PlatformAccountPoolCursorController) UpdateRemark() {
updatePoolRemark(&c.Controller, "cursor")
}
func (c *PlatformAccountPoolCursorController) SetUnavailable() {
setPoolUnavailable(&c.Controller, "cursor")
}
func (c *PlatformAccountPoolCursorController) UpdatePlatform() {
updatePoolPlatform(&c.Controller, "cursor")
}
func (c *PlatformAccountPoolCursorController) Unextract() { unextractPoolRow(&c.Controller, "cursor") }
func (c *PlatformAccountPoolCursorController) ProbeToken() { probePoolToken(&c.Controller, "cursor") }
func (c *PlatformAccountPoolWindsurfController) List() { listPoolRows(&c.Controller, "windsurf") }
func (c *PlatformAccountPoolWindsurfController) Add() { addPoolRow(&c.Controller, "windsurf") }
func (c *PlatformAccountPoolWindsurfController) BatchAdd() {
batchAddPoolRows(&c.Controller, "windsurf")
}
func (c *PlatformAccountPoolWindsurfController) Detail() { getPoolDetail(&c.Controller, "windsurf") }
func (c *PlatformAccountPoolWindsurfController) Extract() { extractPoolRow(&c.Controller, "windsurf") }
func (c *PlatformAccountPoolWindsurfController) Replenish() {
replenishPoolRow(&c.Controller, "windsurf")
}
func (c *PlatformAccountPoolWindsurfController) UpdateRemark() {
updatePoolRemark(&c.Controller, "windsurf")
}
func (c *PlatformAccountPoolWindsurfController) SetUnavailable() {
setPoolUnavailable(&c.Controller, "windsurf")
}
func (c *PlatformAccountPoolWindsurfController) UpdatePlatform() {
updatePoolPlatform(&c.Controller, "windsurf")
}
func (c *PlatformAccountPoolWindsurfController) Unextract() {
unextractPoolRow(&c.Controller, "windsurf")
}
func (c *PlatformAccountPoolWindsurfController) ProbeToken() {
probePoolToken(&c.Controller, "windsurf")
}
func (c *PlatformAccountPoolKrioController) List() { listPoolRows(&c.Controller, "krio") }
func (c *PlatformAccountPoolKrioController) Add() { addPoolRow(&c.Controller, "krio") }
func (c *PlatformAccountPoolKrioController) BatchAdd() { batchAddPoolRows(&c.Controller, "krio") }
func (c *PlatformAccountPoolKrioController) Detail() { getPoolDetail(&c.Controller, "krio") }
func (c *PlatformAccountPoolKrioController) Extract() { extractPoolRow(&c.Controller, "krio") }
func (c *PlatformAccountPoolKrioController) Replenish() { replenishPoolRow(&c.Controller, "krio") }
func (c *PlatformAccountPoolKrioController) UpdateRemark() {
updatePoolRemark(&c.Controller, "krio")
}
func (c *PlatformAccountPoolKrioController) SetUnavailable() {
setPoolUnavailable(&c.Controller, "krio")
}
func (c *PlatformAccountPoolKrioController) UpdatePlatform() {
updatePoolPlatform(&c.Controller, "krio")
}
func (c *PlatformAccountPoolKrioController) Unextract() { unextractPoolRow(&c.Controller, "krio") }
func (c *PlatformAccountPoolKrioController) ProbeToken() { probePoolToken(&c.Controller, "krio") }