diff --git a/controller/token.go b/controller/token.go index 180c4259..62f9ba81 100644 --- a/controller/token.go +++ b/controller/token.go @@ -96,6 +96,8 @@ func GetTokenStatus(c *gin.Context) { "total_used": 0, // not supported currently "total_available": token.RemainQuota, "expires_at": expiredAt * 1000, + "hard_limit_usd": token.RemainQuota, // 兼容openai的接口 + "total_usage": token.TotalUsage * 100, // 兼容openai的接口 }) } @@ -125,6 +127,7 @@ func AddToken(c *gin.Context) { ExpiredTime: token.ExpiredTime, RemainQuota: token.RemainQuota, UnlimitedQuota: token.UnlimitedQuota, + TotalUsage: token.TotalUsage, } err = cleanToken.Insert() if err != nil { @@ -203,6 +206,7 @@ func UpdateToken(c *gin.Context) { cleanToken.ExpiredTime = token.ExpiredTime cleanToken.RemainQuota = token.RemainQuota cleanToken.UnlimitedQuota = token.UnlimitedQuota + cleanToken.TotalUsage = token.TotalUsage } err = cleanToken.Update() if err != nil { diff --git a/model/token.go b/model/token.go index ef2d914b..5d71f617 100644 --- a/model/token.go +++ b/model/token.go @@ -9,16 +9,17 @@ import ( ) type Token struct { - Id int `json:"id"` - UserId int `json:"user_id"` - Key string `json:"key" gorm:"type:char(32);uniqueIndex"` - Status int `json:"status" gorm:"default:1"` - Name string `json:"name" gorm:"index" ` - CreatedTime int64 `json:"created_time" gorm:"bigint"` - AccessedTime int64 `json:"accessed_time" gorm:"bigint"` - ExpiredTime int64 `json:"expired_time" gorm:"bigint;default:-1"` // -1 means never expired - RemainQuota int `json:"remain_quota" gorm:"default:0"` - UnlimitedQuota bool `json:"unlimited_quota" gorm:"default:false"` + Id int `json:"id"` + UserId int `json:"user_id"` + Key string `json:"key" gorm:"type:char(32);uniqueIndex"` + Status int `json:"status" gorm:"default:1"` + Name string `json:"name" gorm:"index" ` + CreatedTime int64 `json:"created_time" gorm:"bigint"` + AccessedTime int64 `json:"accessed_time" gorm:"bigint"` + ExpiredTime int64 `json:"expired_time" gorm:"bigint;default:-1"` // -1 means never expired + RemainQuota int `json:"remain_quota" gorm:"default:0"` + UnlimitedQuota bool `json:"unlimited_quota" gorm:"default:false"` + TotalUsage float64 `json:"total_usage" gorm:"default:0"` } func GetAllUserTokens(userId int, startIdx int, num int) ([]*Token, error) { @@ -100,7 +101,7 @@ func (token *Token) Insert() error { // Update Make sure your token's fields is completed, because this will update non-zero values func (token *Token) Update() error { var err error - err = DB.Model(token).Select("name", "status", "expired_time", "remain_quota", "unlimited_quota").Updates(token).Error + err = DB.Model(token).Select("name", "status", "expired_time", "remain_quota", "unlimited_quota", "total_usage").Updates(token).Error return err } @@ -132,7 +133,13 @@ func IncreaseTokenQuota(id int, quota int) (err error) { if quota < 0 { return errors.New("quota 不能为负数!") } - err = DB.Model(&Token{}).Where("id = ?", id).Update("remain_quota", gorm.Expr("remain_quota + ?", quota)).Error + err = DB.Model(&Token{}).Where("id = ?", id).Updates( + map[string]interface{}{ + "remain_quota": gorm.Expr("remain_quota + ?", quota), + "total_usage": gorm.Expr("total_usage - ?", quota), + }, + ).Error + return err } @@ -140,7 +147,12 @@ func DecreaseTokenQuota(id int, quota int) (err error) { if quota < 0 { return errors.New("quota 不能为负数!") } - err = DB.Model(&Token{}).Where("id = ?", id).Update("remain_quota", gorm.Expr("remain_quota - ?", quota)).Error + err = DB.Model(&Token{}).Where("id = ?", id).Updates( + map[string]interface{}{ + "remain_quota": gorm.Expr("remain_quota - ?", quota), + "total_usage": gorm.Expr("total_usage + ?", quota), + }, + ).Error return err } diff --git a/router/dashboard.go b/router/dashboard.go index 3eacaf9a..4cc1a4f5 100644 --- a/router/dashboard.go +++ b/router/dashboard.go @@ -14,5 +14,7 @@ func SetDashboardRouter(router *gin.Engine) { apiRouter.Use(middleware.TokenAuth()) { apiRouter.GET("/billing/credit_grants", controller.GetTokenStatus) + apiRouter.GET("/billing/subscription", controller.GetTokenStatus) + apiRouter.GET("/billing/usage", controller.GetTokenStatus) } } diff --git a/web/src/components/TokensTable.js b/web/src/components/TokensTable.js index fa77a32d..360213e1 100644 --- a/web/src/components/TokensTable.js +++ b/web/src/components/TokensTable.js @@ -196,6 +196,14 @@ const TokensTable = () => { > 额度 + { + sortToken('total_usage'); + }} + > + 已使用 + { @@ -230,6 +238,7 @@ const TokensTable = () => { {token.name ? token.name : '无'} {renderStatus(token.status)} {token.unlimited_quota ? '无限制' : token.remain_quota} + {token.total_usage} {renderTimestamp(token.created_time)} {token.expired_time === -1 ? '永不过期' : renderTimestamp(token.expired_time)} diff --git a/web/src/pages/Token/EditToken.js b/web/src/pages/Token/EditToken.js index dd8022e1..d1ce0a71 100644 --- a/web/src/pages/Token/EditToken.js +++ b/web/src/pages/Token/EditToken.js @@ -11,11 +11,12 @@ const EditToken = () => { const originInputs = { name: '', remain_quota: 0, + total_usage: 0, expired_time: -1, unlimited_quota: false }; const [inputs, setInputs] = useState(originInputs); - const { name, remain_quota, expired_time, unlimited_quota } = inputs; + const { name, remain_quota, total_usage, expired_time, unlimited_quota } = inputs; const handleInputChange = (e, { name, value }) => { setInputs((inputs) => ({ ...inputs, [name]: value })); @@ -63,6 +64,7 @@ const EditToken = () => { if (!isEdit && inputs.name === '') return; let localInputs = inputs; localInputs.remain_quota = parseInt(localInputs.remain_quota); + localInputs.total_usage = parseInt(localInputs.total_usage); if (localInputs.expired_time !== -1) { let time = Date.parse(localInputs.expired_time); if (isNaN(time)) { @@ -106,6 +108,17 @@ const EditToken = () => { required={!isEdit} /> + + + 注意,令牌的额度仅用于限制令牌本身的最大额度使用量,实际的使用受到账户的剩余额度限制。