Skip to content

feature: 在 /api/accounts 的 quota 中透出积分包到期时间(到期面) #213

Description

@UselsCoder

需求

希望在 GET /api/accounts 返回的 quota 中,透出每个积分包的到期时间(到期面),让控制台和外部集成能回答「哪些积分将在什么时候过期、最近一笔先到期多少」。

目前只能看到总量:used / total / remaining / percentage / unit,看不到时间维度。

现状(v0.6.2 实测)

quota 的键集合恒为:

used, total, remaining, percentage, unit, exceeded,
has_add_on, add_on_{used,total,remaining,unit,available},
has_resource_package, resource_package_{used,total,remaining,unit,available},
fetched_at

无任何 end/expire 字段。另外穷举过下列接口,同样都没有到期信息:

  • /api/accounts/{id}POST /api/accounts/{id}/refresh?quota=1/api/accounts/{id}/checkins
  • /api/overview/api/overview/summary/api/providers/api/models
  • /api/logs/stats/api/keys/api/system/settings
  • GET /api/accounts/{id}/export(只有登录 token 的 expires_at,不是积分到期)
  • /api/accounts/{id}/{quota,worker/quota,admin/quota} 均为 404;worker admin 是白名单(internal/providers/qoder/worker.goAdminAction 只含 login/devicelogin/statuslogin/patrewarm),无法借控制台读取 worker 原始 quota

容器内直连 qoder worker GET /admin/quota 的原始返回也只有 total/used/remaining/percentage/unit(addOn 多一个 detailUrl)。

根因(源码位置)

WorkBuddy —— 上游有到期时间,但求和时被丢弃:

  • internal/providers/workbuddy/client.go:899-940UserResource)按 PackageEndTimeRangeBegin/End 过滤套餐,说明上游是按结束时间筛包的;
  • 解析用的结构体 internal/providers/workbuddy/client.go:942-949 没有时间字段
type resourcePackage struct {
    CapacityRemain      int64 `json:"CapacityRemain"`
    CapacityUsed        int64 `json:"CapacityUsed"`
    CapacitySize        int64 `json:"CapacitySize"`
    CycleCapacityRemain int64 `json:"CycleCapacityRemain"`
    CycleCapacityUsed   int64 `json:"CycleCapacityUsed"`
    CycleCapacitySize   int64 `json:"CycleCapacitySize"`
}
  • aggregateUserResource(同文件 :991)只累加 remain/used/size。

⚠️ 字段名提醒:上游真实下发的是 CycleEndTime"2006-01-02 15:04:05",UTC+8 墙钟),不是 PackageEndTime。CN / global 两域字段全集里都没有 PackageEndTime,用它做判据会恒 miss —— 这一点在另一个同上游的实现里有实测记录(workbuddy2api/internal/upstream/client.go:1451internal/upstream/expiring_test.go:95-119)。所以新增解析请用 CycleEndTime

Trae —— internal/providers/trae/client.go:963parseEntitlementUsage 只取 entitlement_base_info.quota.credits_limitusage.credits_amount;额度包自身的有效期字段没有被读取(上游是否下发需确认)。

Qoder —— worker 的 normalizeQuotaBlockworker/src/daemon.mjs:555)只保留 total/used/remaining/percentage/unit(外加 detailUrl/available),原始 getQuotaUsage() 响应即便带时间信息也会被丢弃。

建议改动

  1. internal/accounts/quota.go:给 QuotaSnapshot 增加可选到期面,例如
// ExpiresAt: 最近一次到期的 Unix 秒;0 = 无到期/未提供
ExpiresAt      int64         `json:"expires_at,omitempty"`
// ExpiringRemain: 在 ExpiresAt 之前将要过期的剩余量
ExpiringRemain float64       `json:"expiring_remain,omitempty"`
// Packages: 逐包到期明细,便于展示「N 个套餐」
Packages       []QuotaPackage `json:"packages,omitempty"`

QuotaPackage 建议为 {remain, used, size, unit, ends_at, end_time}

  1. internal/providers/workbuddy/client.goresourcePackage 增加 CycleEndTime string \json:"CycleEndTime"`,在 aggregateUserResource里按 UTC+8 解析为 Unix 秒,取最小非零值作为ExpiresAt,并按包填充 Packages`。

  2. internal/providers/interfaces.goQuotaInfo,约 :133)增加同组可选字段,并在 internal/runtime/manager_quota.go:28QuotaSnapshot{...} 构造处补齐透传。

  3. Trae / Qoder:先核实上游是否真下发有效期;没有就留空,不要伪造。

兼容性

  • 全部字段 omitempty,老客户端不受影响;语义与 fetched_at 一致(快照时刻的上游读数)。
  • 不参与路由决策,仅用于展示,Exceeded 判定逻辑不变。

参考实现(同机、同上游,已上线可对照)

workbuddy2api 已在 GET /credit 透出 packages_detail: [{end_time, ends_at, remain, used, size}]expiring 分桶,逻辑见其 internal/server/handler.go:212-282internal/upstream/client.go:1433-1580,字段命名与解析方式可直接复用。

感谢维护,这个网关很好用。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions