为用户在 anthropic/openai/gemini/antigravity 四个平台上提供日/周/月 三个窗口的 USD 配额管控。配额语义:未设置=不限制,0=禁用,>0=美元上限。 两层模型: - 配置层:系统默认配额,以及 email/linuxdo/oidc/wechat/github/google/ dingtalk 七个鉴权来源的默认配额,存于 settings,以嵌套 JSON 整体读写 (系统 1 个 key + 每个来源 1 个 key),整体替换语义。 - 运行时层:user_platform_quota 表按用户记录实际配额,与配置层解耦。 后端:新增 ent schema 与 140_user_platform_quotas.sql 迁移、repository 与 service 端口、计费链路集成、管理端与用户端读写接口。 前端:管理端设置页配额编辑、用户配额管理 Modal、用户 Dashboard 展示、 中英文案。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
2.5 KiB
Go
51 lines
2.5 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"time"
|
||
)
|
||
|
||
// ErrUserPlatformQuotaNotFound service 层 sentinel:quota 记录不存在。
|
||
// adapter 将 repository.ErrUserPlatformQuotaNotFound 包装为此错误,
|
||
// handler 只需引用 service 包,无需直接依赖 repository 包。
|
||
var ErrUserPlatformQuotaNotFound = errors.New("user platform quota not found")
|
||
|
||
// UserPlatformQuotaRecord service 层传输结构体(与 repository 层解耦)。
|
||
type UserPlatformQuotaRecord struct {
|
||
UserID int64
|
||
Platform string
|
||
DailyLimitUSD *float64
|
||
WeeklyLimitUSD *float64
|
||
MonthlyLimitUSD *float64
|
||
DailyUsageUSD float64
|
||
WeeklyUsageUSD float64
|
||
MonthlyUsageUSD float64
|
||
// 窗口起始时间(可选,用于未来 reset 校验)
|
||
DailyWindowStart *time.Time
|
||
WeeklyWindowStart *time.Time
|
||
MonthlyWindowStart *time.Time
|
||
}
|
||
|
||
// UserPlatformQuotaRepository 定义 service 层所需的 user × platform quota 数据访问端口。
|
||
// repository 包的 userPlatformQuotaRepository 实现此接口。
|
||
type UserPlatformQuotaRepository interface {
|
||
// GetByUserPlatform 查询单条配额记录,未找到时返回 (nil, nil)。
|
||
GetByUserPlatform(ctx context.Context, userID int64, platform string) (*UserPlatformQuotaRecord, error)
|
||
// BulkInsertInitial 幂等批量插入初始配额记录(ON CONFLICT DO NOTHING)。
|
||
BulkInsertInitial(ctx context.Context, records []UserPlatformQuotaRecord) error
|
||
// IncrementUsageWithReset 原子地累加用量,若窗口已过期则先重置再累加。
|
||
IncrementUsageWithReset(ctx context.Context, userID int64, platform string, cost float64, now time.Time) error
|
||
// ListByUser 查询用户的所有平台配额记录。
|
||
ListByUser(ctx context.Context, userID int64) ([]UserPlatformQuotaRecord, error)
|
||
// UpsertForUser 全量替换该用户所有平台限额配置(事务内):
|
||
// 1. 软删除未在 records 中出现的所有 active 行
|
||
// 2. 对 records 中每条:UPDATE 已存在的(含重新激活软删行);UPDATE 未命中时 INSERT
|
||
// 仅改 *_limit_usd + deleted_at + updated_at,保留 *_usage_usd / *_window_start。
|
||
// records 为空时仅执行步骤 1。
|
||
UpsertForUser(ctx context.Context, userID int64, records []UserPlatformQuotaRecord) error
|
||
// ResetExpiredWindow 重置指定窗口("daily"|"weekly"|"monthly")的用量与起始时间。
|
||
// 未命中活跃记录时返回(service-side wrapper of repository.ErrUserPlatformQuotaNotFound)。
|
||
ResetExpiredWindow(ctx context.Context, userID int64, platform string, window string, newStart time.Time) error
|
||
}
|