commits
This commit is contained in:
+258
-258
@@ -1,258 +1,258 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// PlatformHomeController 平台首页统计(需登录)
|
||||
type PlatformHomeController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func cellToDateKey(v interface{}) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
switch x := v.(type) {
|
||||
case []byte:
|
||||
s := strings.TrimSpace(string(x))
|
||||
if len(s) >= 10 {
|
||||
return s[:10]
|
||||
}
|
||||
return s
|
||||
case string:
|
||||
s := strings.TrimSpace(x)
|
||||
if len(s) >= 10 {
|
||||
return s[:10]
|
||||
}
|
||||
return s
|
||||
case time.Time:
|
||||
if x.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return x.In(time.Local).Format("2006-01-02")
|
||||
default:
|
||||
s := strings.TrimSpace(fmt.Sprint(x))
|
||||
if len(s) >= 10 {
|
||||
return s[:10]
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func cellToInt64(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 queryExtractedCountByDay(table string, start, endExclusive time.Time) (map[string]int64, error) {
|
||||
// 不按 delete_time 过滤:部分库未删除行存 0000-00-00 或非 NULL,会导致统计全空。
|
||||
// Raw + QueryRows 对别名映射不稳定,改用 Values 解析 d/c。
|
||||
sql := fmt.Sprintf(`
|
||||
SELECT DATE(extracted_time) AS d, COUNT(*) AS c
|
||||
FROM %s
|
||||
WHERE is_extracted IN (1, 2)
|
||||
AND extracted_time IS NOT NULL
|
||||
AND extracted_time >= ?
|
||||
AND extracted_time < ?
|
||||
GROUP BY DATE(extracted_time)
|
||||
ORDER BY d
|
||||
`, table)
|
||||
var maps []orm.Params
|
||||
_, err := models.Orm.Raw(sql, start, endExclusive).Values(&maps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[string]int64, len(maps))
|
||||
for _, m := range maps {
|
||||
var dk, ck interface{}
|
||||
for _, k := range []string{"d", "D"} {
|
||||
if v, ok := m[k]; ok {
|
||||
dk = v
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, k := range []string{"c", "C"} {
|
||||
if v, ok := m[k]; ok {
|
||||
ck = v
|
||||
break
|
||||
}
|
||||
}
|
||||
key := cellToDateKey(dk)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
out[key] = cellToInt64(ck)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AccountPoolDailyExtract GET /platform/home/accountPoolDailyExtract?days=14
|
||||
// 按天统计各号池「已提取」数量,依据 extracted_time 落在当天的记录。
|
||||
func (c *PlatformHomeController) AccountPoolDailyExtract() {
|
||||
if _, err := requirePlatformAuth(&c.Controller); err != nil {
|
||||
poolJSONErr(&c.Controller, 401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
n, _ := c.GetInt("days", 14)
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
if n > 90 {
|
||||
n = 90
|
||||
}
|
||||
|
||||
now := time.Now().In(time.Local)
|
||||
today0 := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
|
||||
firstDay := today0.AddDate(0, 0, -(n - 1))
|
||||
endExclusive := today0.AddDate(0, 0, 1)
|
||||
|
||||
cursorTable := (&models.PlatformAccountPoolCursor{}).TableName()
|
||||
windsurfTable := (&models.PlatformAccountPoolWindsurf{}).TableName()
|
||||
kiroTable := (&models.PlatformAccountPoolKiro{}).TableName()
|
||||
|
||||
mCursor, err := queryExtractedCountByDay(cursorTable, firstDay, endExclusive)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计 Cursor 失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
mWindsurf, err := queryExtractedCountByDay(windsurfTable, firstDay, endExclusive)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计 Windsurf 失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
mKiro, err := queryExtractedCountByDay(kiroTable, firstDay, endExclusive)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计 Kiro 失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
dayKeys := make([]string, 0, n)
|
||||
dayLabels := make([]string, 0, n)
|
||||
cursorVals := make([]int64, 0, n)
|
||||
windsurfVals := make([]int64, 0, n)
|
||||
kiroVals := make([]int64, 0, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
d := firstDay.AddDate(0, 0, i)
|
||||
key := d.Format("2006-01-02")
|
||||
dayKeys = append(dayKeys, key)
|
||||
dayLabels = append(dayLabels, d.Format("01/02"))
|
||||
cursorVals = append(cursorVals, mCursor[key])
|
||||
windsurfVals = append(windsurfVals, mWindsurf[key])
|
||||
kiroVals = append(kiroVals, mKiro[key])
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": map[string]interface{}{
|
||||
"days": dayLabels,
|
||||
"dayKeys": dayKeys,
|
||||
"cursor": int64SliceToInt(cursorVals),
|
||||
"windsurf": int64SliceToInt(windsurfVals),
|
||||
"kiro": int64SliceToInt(kiroVals),
|
||||
"daysLength": n,
|
||||
},
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func int64SliceToInt(in []int64) []int {
|
||||
out := make([]int, len(in))
|
||||
for i, v := range in {
|
||||
out[i] = int(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func countPoolInventory(mi interface{}, soldOnly bool) (int64, error) {
|
||||
qs := models.Orm.QueryTable(mi).Filter("delete_time__isnull", true)
|
||||
if soldOnly {
|
||||
qs = qs.Filter("is_extracted__in", 1, 2)
|
||||
}
|
||||
n, err := qs.Count()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// AccountPoolInventoryTotals GET /platform/home/accountPoolInventoryTotals
|
||||
// 各号池:账号总数(未删)、已售卖(is_extracted 为 1 或 2)
|
||||
func (c *PlatformHomeController) AccountPoolInventoryTotals() {
|
||||
if _, err := requirePlatformAuth(&c.Controller); err != nil {
|
||||
poolJSONErr(&c.Controller, 401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
type invModule struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Total int64 `json:"total"`
|
||||
Sold int64 `json:"sold"`
|
||||
}
|
||||
|
||||
modules := []invModule{
|
||||
{Key: "cursor", Label: "Cursor"},
|
||||
{Key: "krio", Label: "Kiro"},
|
||||
{Key: "windsurf", Label: "Windsurf"},
|
||||
{Key: "codex", Label: "Codex"},
|
||||
}
|
||||
modelsList := []interface{}{
|
||||
new(models.PlatformAccountPoolCursor),
|
||||
new(models.PlatformAccountPoolKiro),
|
||||
new(models.PlatformAccountPoolWindsurf),
|
||||
new(models.PlatformAccountPoolCodex),
|
||||
}
|
||||
|
||||
|
||||
var grandTotal, grandSold int64
|
||||
for i := range modules {
|
||||
tot, err := countPoolInventory(modelsList[i], false)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
sd, err := countPoolInventory(modelsList[i], true)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
modules[i].Total = tot
|
||||
modules[i].Sold = sd
|
||||
grandTotal += tot
|
||||
grandSold += sd
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": map[string]interface{}{
|
||||
"modules": modules,
|
||||
"grandTotal": grandTotal,
|
||||
"grandSold": grandSold,
|
||||
},
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
|
||||
"github.com/beego/beego/v2/client/orm"
|
||||
beego "github.com/beego/beego/v2/server/web"
|
||||
)
|
||||
|
||||
// PlatformHomeController 平台首页统计(需登录)
|
||||
type PlatformHomeController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
func cellToDateKey(v interface{}) string {
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
switch x := v.(type) {
|
||||
case []byte:
|
||||
s := strings.TrimSpace(string(x))
|
||||
if len(s) >= 10 {
|
||||
return s[:10]
|
||||
}
|
||||
return s
|
||||
case string:
|
||||
s := strings.TrimSpace(x)
|
||||
if len(s) >= 10 {
|
||||
return s[:10]
|
||||
}
|
||||
return s
|
||||
case time.Time:
|
||||
if x.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return x.In(time.Local).Format("2006-01-02")
|
||||
default:
|
||||
s := strings.TrimSpace(fmt.Sprint(x))
|
||||
if len(s) >= 10 {
|
||||
return s[:10]
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
func cellToInt64(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 queryExtractedCountByDay(table string, start, endExclusive time.Time) (map[string]int64, error) {
|
||||
// 不按 delete_time 过滤:部分库未删除行存 0000-00-00 或非 NULL,会导致统计全空。
|
||||
// Raw + QueryRows 对别名映射不稳定,改用 Values 解析 d/c。
|
||||
sql := fmt.Sprintf(`
|
||||
SELECT DATE(extracted_time) AS d, COUNT(*) AS c
|
||||
FROM %s
|
||||
WHERE is_extracted IN (1, 2)
|
||||
AND extracted_time IS NOT NULL
|
||||
AND extracted_time >= ?
|
||||
AND extracted_time < ?
|
||||
GROUP BY DATE(extracted_time)
|
||||
ORDER BY d
|
||||
`, table)
|
||||
var maps []orm.Params
|
||||
_, err := models.Orm.Raw(sql, start, endExclusive).Values(&maps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := make(map[string]int64, len(maps))
|
||||
for _, m := range maps {
|
||||
var dk, ck interface{}
|
||||
for _, k := range []string{"d", "D"} {
|
||||
if v, ok := m[k]; ok {
|
||||
dk = v
|
||||
break
|
||||
}
|
||||
}
|
||||
for _, k := range []string{"c", "C"} {
|
||||
if v, ok := m[k]; ok {
|
||||
ck = v
|
||||
break
|
||||
}
|
||||
}
|
||||
key := cellToDateKey(dk)
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
out[key] = cellToInt64(ck)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// AccountPoolDailyExtract GET /platform/home/accountPoolDailyExtract?days=14
|
||||
// 按天统计各号池「已提取」数量,依据 extracted_time 落在当天的记录。
|
||||
func (c *PlatformHomeController) AccountPoolDailyExtract() {
|
||||
if _, err := requirePlatformAuth(&c.Controller); err != nil {
|
||||
poolJSONErr(&c.Controller, 401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
n, _ := c.GetInt("days", 14)
|
||||
if n < 1 {
|
||||
n = 1
|
||||
}
|
||||
if n > 90 {
|
||||
n = 90
|
||||
}
|
||||
|
||||
now := time.Now().In(time.Local)
|
||||
today0 := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
|
||||
firstDay := today0.AddDate(0, 0, -(n - 1))
|
||||
endExclusive := today0.AddDate(0, 0, 1)
|
||||
|
||||
cursorTable := (&models.PlatformAccountPoolCursor{}).TableName()
|
||||
windsurfTable := (&models.PlatformAccountPoolWindsurf{}).TableName()
|
||||
kiroTable := (&models.PlatformAccountPoolKiro{}).TableName()
|
||||
|
||||
mCursor, err := queryExtractedCountByDay(cursorTable, firstDay, endExclusive)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计 Cursor 失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
mWindsurf, err := queryExtractedCountByDay(windsurfTable, firstDay, endExclusive)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计 Windsurf 失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
mKiro, err := queryExtractedCountByDay(kiroTable, firstDay, endExclusive)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计 Kiro 失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
dayKeys := make([]string, 0, n)
|
||||
dayLabels := make([]string, 0, n)
|
||||
cursorVals := make([]int64, 0, n)
|
||||
windsurfVals := make([]int64, 0, n)
|
||||
kiroVals := make([]int64, 0, n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
d := firstDay.AddDate(0, 0, i)
|
||||
key := d.Format("2006-01-02")
|
||||
dayKeys = append(dayKeys, key)
|
||||
dayLabels = append(dayLabels, d.Format("01/02"))
|
||||
cursorVals = append(cursorVals, mCursor[key])
|
||||
windsurfVals = append(windsurfVals, mWindsurf[key])
|
||||
kiroVals = append(kiroVals, mKiro[key])
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": map[string]interface{}{
|
||||
"days": dayLabels,
|
||||
"dayKeys": dayKeys,
|
||||
"cursor": int64SliceToInt(cursorVals),
|
||||
"windsurf": int64SliceToInt(windsurfVals),
|
||||
"kiro": int64SliceToInt(kiroVals),
|
||||
"daysLength": n,
|
||||
},
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
func int64SliceToInt(in []int64) []int {
|
||||
out := make([]int, len(in))
|
||||
for i, v := range in {
|
||||
out[i] = int(v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func countPoolInventory(mi interface{}, soldOnly bool) (int64, error) {
|
||||
qs := models.Orm.QueryTable(mi).Filter("delete_time__isnull", true)
|
||||
if soldOnly {
|
||||
qs = qs.Filter("is_extracted__in", 1, 2)
|
||||
}
|
||||
n, err := qs.Count()
|
||||
return n, err
|
||||
}
|
||||
|
||||
// AccountPoolInventoryTotals GET /platform/home/accountPoolInventoryTotals
|
||||
// 各号池:账号总数(未删)、已售卖(is_extracted 为 1 或 2)
|
||||
func (c *PlatformHomeController) AccountPoolInventoryTotals() {
|
||||
if _, err := requirePlatformAuth(&c.Controller); err != nil {
|
||||
poolJSONErr(&c.Controller, 401, 401, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
type invModule struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Total int64 `json:"total"`
|
||||
Sold int64 `json:"sold"`
|
||||
}
|
||||
|
||||
modules := []invModule{
|
||||
{Key: "cursor", Label: "Cursor"},
|
||||
{Key: "krio", Label: "Kiro"},
|
||||
{Key: "windsurf", Label: "Windsurf"},
|
||||
{Key: "codex", Label: "Codex"},
|
||||
}
|
||||
modelsList := []interface{}{
|
||||
new(models.PlatformAccountPoolCursor),
|
||||
new(models.PlatformAccountPoolKiro),
|
||||
new(models.PlatformAccountPoolWindsurf),
|
||||
new(models.PlatformAccountPoolCodex),
|
||||
}
|
||||
|
||||
|
||||
var grandTotal, grandSold int64
|
||||
for i := range modules {
|
||||
tot, err := countPoolInventory(modelsList[i], false)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
sd, err := countPoolInventory(modelsList[i], true)
|
||||
if err != nil {
|
||||
poolJSONErr(&c.Controller, 500, 500, "统计失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
modules[i].Total = tot
|
||||
modules[i].Sold = sd
|
||||
grandTotal += tot
|
||||
grandSold += sd
|
||||
}
|
||||
|
||||
c.Data["json"] = map[string]interface{}{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"data": map[string]interface{}{
|
||||
"modules": modules,
|
||||
"grandTotal": grandTotal,
|
||||
"grandSold": grandSold,
|
||||
},
|
||||
}
|
||||
_ = c.ServeJSON()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user