feat(tenant):新增用户等级功能
This commit is contained in:
1 parent
ded149ed2f
commit
f3e87866c4
49 files changed
+963
-156
No files matched your search
@@ -208,6 +208,10 @@ type IAdminV1 interface {
|
||||
TenantExport(ctx context.Context, req *v1.TenantExportReq) (res *v1.TenantExportRes, err error)
|
||||
AdminMemberExport(ctx context.Context, req *v1.AdminMemberExportReq) (res *v1.AdminMemberExportRes, err error)
|
||||
TenantSelect(ctx context.Context, req *v1.TenantSelectReq) (res *v1.TenantSelectRes, err error)
|
||||
TenantLevelConfigList(ctx context.Context, req *v1.TenantLevelConfigListReq) (res *v1.TenantLevelConfigListRes, err error)
|
||||
TenantLevelConfigCreate(ctx context.Context, req *v1.TenantLevelConfigCreateReq) (res *v1.TenantLevelConfigCreateRes, err error)
|
||||
TenantLevelConfigUpdate(ctx context.Context, req *v1.TenantLevelConfigUpdateReq) (res *v1.TenantLevelConfigUpdateRes, err error)
|
||||
TenantLevelConfigDelete(ctx context.Context, req *v1.TenantLevelConfigDeleteReq) (res *v1.TenantLevelConfigDeleteRes, err error)
|
||||
TenantModelList(ctx context.Context, req *v1.TenantModelListReq) (res *v1.TenantModelListRes, err error)
|
||||
TenantModelBatchAssign(ctx context.Context, req *v1.TenantModelBatchAssignReq) (res *v1.TenantModelBatchAssignRes, err error)
|
||||
TenantModelUpdate(ctx context.Context, req *v1.TenantModelUpdateReq) (res *v1.TenantModelUpdateRes, err error)
|
||||
|
||||
@@ -47,6 +47,8 @@ type TenantItem struct {
|
||||
DefaultChannelScope string `json:"default_channel_scope"`
|
||||
MemberCount int `json:"member_count"`
|
||||
WalletBalance string `json:"wallet_balance"`
|
||||
Level int `json:"level"`
|
||||
LevelName string `json:"level_name"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
@@ -85,6 +87,7 @@ type TenantUpdateReq struct {
|
||||
Name string `json:"name" dc:"租户名称"`
|
||||
MaxMembers *int `json:"max_members" dc:"最大成员数"`
|
||||
MaxConcurrency *int `json:"max_concurrency" dc:"租户总并发上限(0表示不限制)"`
|
||||
Level *int `json:"level" dc:"租户等级(调整等级会同步更新成员数和并发数为该等级的配置值)"`
|
||||
}
|
||||
|
||||
type TenantUpdateRes struct{}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package v1
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// === 租户等级配置管理 ===
|
||||
|
||||
type TenantLevelConfigItem struct {
|
||||
Id int64 `json:"id"`
|
||||
Level int `json:"level"`
|
||||
Name string `json:"name"`
|
||||
CumulativeRechargeThreshold float64 `json:"cumulative_recharge_threshold"`
|
||||
MaxMembers int `json:"max_members"`
|
||||
MaxConcurrency int `json:"max_concurrency"`
|
||||
PriceMultiplier float64 `json:"price_multiplier"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
CreatedAt *gtime.Time `json:"created_at"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type TenantLevelConfigListReq struct {
|
||||
g.Meta `path:"/tenant-level-configs" method:"get" mime:"json" tags:"管理后台-租户等级" summary:"等级配置列表"`
|
||||
}
|
||||
|
||||
type TenantLevelConfigListRes struct {
|
||||
List []*TenantLevelConfigItem `json:"list"`
|
||||
}
|
||||
|
||||
type TenantLevelConfigCreateReq struct {
|
||||
g.Meta `path:"/tenant-level-configs" method:"post" mime:"json" tags:"管理后台-租户等级" summary:"创建等级配置"`
|
||||
Level int `json:"level" v:"required|min:1#请输入等级号|等级号最小为1"`
|
||||
Name string `json:"name" v:"required#请输入等级名称"`
|
||||
CumulativeRechargeThreshold float64 `json:"cumulative_recharge_threshold" v:"required|min:0#请输入累计充值阈值|阈值不能为负数"`
|
||||
MaxMembers int `json:"max_members" v:"min:0#最大成员数不能为负数"`
|
||||
MaxConcurrency int `json:"max_concurrency" v:"min:0#最大并发数不能为负数"`
|
||||
PriceMultiplier float64 `json:"price_multiplier" d:"1.0000"`
|
||||
SortOrder int `json:"sort_order" d:"0"`
|
||||
}
|
||||
|
||||
type TenantLevelConfigCreateRes struct {
|
||||
ID int64 `json:"id"`
|
||||
}
|
||||
|
||||
type TenantLevelConfigUpdateReq struct {
|
||||
g.Meta `path:"/tenant-level-configs/{id}" method:"put" mime:"json" tags:"管理后台-租户等级" summary:"更新等级配置"`
|
||||
Id int64 `json:"id" in:"path" v:"required|min:1"`
|
||||
Update map[string]interface{} `json:"update" v:"required"`
|
||||
}
|
||||
|
||||
type TenantLevelConfigUpdateRes struct{}
|
||||
|
||||
type TenantLevelConfigDeleteReq struct {
|
||||
g.Meta `path:"/tenant-level-configs/{id}" method:"delete" mime:"json" tags:"管理后台-租户等级" summary:"删除等级配置"`
|
||||
Id int64 `json:"id" in:"path" v:"required|min:1"`
|
||||
}
|
||||
|
||||
type TenantLevelConfigDeleteRes struct{}
|
||||
@@ -13,6 +13,8 @@ type TenantOrgInfoRes struct {
|
||||
Code string `json:"code"`
|
||||
LogoURL string `json:"logo_url"`
|
||||
Status string `json:"status"`
|
||||
Level int `json:"level"`
|
||||
LevelName string `json:"level_name"`
|
||||
MaxMembers int `json:"max_members"`
|
||||
MemberCount int `json:"member_count"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
|
||||
@@ -9,11 +9,16 @@ type TenantWalletReq struct {
|
||||
}
|
||||
|
||||
type TenantWalletRes struct {
|
||||
Balance float64 `json:"balance"`
|
||||
FrozenBalance float64 `json:"frozen_balance"`
|
||||
AvailableBalance float64 `json:"available_balance"`
|
||||
WarningThreshold float64 `json:"warning_threshold"`
|
||||
Currency string `json:"currency"`
|
||||
Balance float64 `json:"balance"`
|
||||
FrozenBalance float64 `json:"frozen_balance"`
|
||||
AvailableBalance float64 `json:"available_balance"`
|
||||
WarningThreshold float64 `json:"warning_threshold"`
|
||||
Currency string `json:"currency"`
|
||||
Level int `json:"level"`
|
||||
LevelName string `json:"level_name"`
|
||||
CumulativeRecharge float64 `json:"cumulative_recharge"`
|
||||
NextLevelThreshold float64 `json:"next_level_threshold"`
|
||||
NextLevelName string `json:"next_level_name"`
|
||||
}
|
||||
|
||||
type TenantWalletTransactionsReq struct {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "github.com/qianfree/team-api/api/admin/v1"
|
||||
"github.com/qianfree/team-api/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) TenantLevelConfigCreate(ctx context.Context, req *v1.TenantLevelConfigCreateReq) (res *v1.TenantLevelConfigCreateRes, err error) {
|
||||
return service.Admin().CreateTenantLevelConfig(ctx, req)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "github.com/qianfree/team-api/api/admin/v1"
|
||||
"github.com/qianfree/team-api/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) TenantLevelConfigDelete(ctx context.Context, req *v1.TenantLevelConfigDeleteReq) (res *v1.TenantLevelConfigDeleteRes, err error) {
|
||||
return service.Admin().DeleteTenantLevelConfig(ctx, req)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "github.com/qianfree/team-api/api/admin/v1"
|
||||
"github.com/qianfree/team-api/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) TenantLevelConfigList(ctx context.Context, req *v1.TenantLevelConfigListReq) (res *v1.TenantLevelConfigListRes, err error) {
|
||||
return service.Admin().ListTenantLevelConfigs(ctx, req)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "github.com/qianfree/team-api/api/admin/v1"
|
||||
"github.com/qianfree/team-api/internal/service"
|
||||
)
|
||||
|
||||
func (c *ControllerV1) TenantLevelConfigUpdate(ctx context.Context, req *v1.TenantLevelConfigUpdateReq) (res *v1.TenantLevelConfigUpdateRes, err error) {
|
||||
return service.Admin().UpdateTenantLevelConfig(ctx, req)
|
||||
}
|
||||
@@ -25,6 +25,7 @@ type AudContentFilterLogsColumns struct {
|
||||
TenantId string // 租户ID
|
||||
UserId string // 用户ID
|
||||
ApiKeyId string // API Key ID
|
||||
ProjectId string // 项目ID
|
||||
RequestId string // 请求唯一ID
|
||||
Method string // HTTP 方法
|
||||
Path string // 请求路径
|
||||
@@ -34,7 +35,6 @@ type AudContentFilterLogsColumns struct {
|
||||
OriginalSnippet string // 原始请求体片段(截断存储,仅 replace 模式)
|
||||
Blocked string // 是否被拦截(mode=block 时为 true)
|
||||
CreatedAt string // 创建时间
|
||||
ProjectId string // 项目ID
|
||||
}
|
||||
|
||||
// audContentFilterLogsColumns holds the columns for the table aud_content_filter_logs.
|
||||
@@ -43,6 +43,7 @@ var audContentFilterLogsColumns = AudContentFilterLogsColumns{
|
||||
TenantId: "tenant_id",
|
||||
UserId: "user_id",
|
||||
ApiKeyId: "api_key_id",
|
||||
ProjectId: "project_id",
|
||||
RequestId: "request_id",
|
||||
Method: "method",
|
||||
Path: "path",
|
||||
@@ -52,7 +53,6 @@ var audContentFilterLogsColumns = AudContentFilterLogsColumns{
|
||||
OriginalSnippet: "original_snippet",
|
||||
Blocked: "blocked",
|
||||
CreatedAt: "created_at",
|
||||
ProjectId: "project_id",
|
||||
}
|
||||
|
||||
// NewAudContentFilterLogsDao creates and returns a new DAO object for table data access.
|
||||
|
||||
@@ -21,26 +21,28 @@ type BilWalletsDao struct {
|
||||
|
||||
// BilWalletsColumns defines and stores column names for the table bil_wallets.
|
||||
type BilWalletsColumns struct {
|
||||
Id string // 主键ID
|
||||
TenantId string // 租户ID(每个租户一个钱包)
|
||||
Balance string // 总余额
|
||||
FrozenBalance string // 冻结余额(支付中/退款中,可用余额 = balance - frozen_balance)
|
||||
WarningThreshold string // 余额预警线(低于此值触发通知)
|
||||
Currency string // 货币(USD)
|
||||
CreatedAt string // 创建时间
|
||||
UpdatedAt string // 更新时间
|
||||
Id string // 主键ID
|
||||
TenantId string // 租户ID(每个租户一个钱包)
|
||||
Balance string // 总余额
|
||||
FrozenBalance string // 冻结余额(支付中/退款中,可用余额 = balance - frozen_balance)
|
||||
WarningThreshold string // 余额预警线(低于此值触发通知)
|
||||
Currency string // 货币(USD)
|
||||
CreatedAt string // 创建时间
|
||||
UpdatedAt string // 更新时间
|
||||
CumulativeRecharge string // 累计充值总额(USD)
|
||||
}
|
||||
|
||||
// bilWalletsColumns holds the columns for the table bil_wallets.
|
||||
var bilWalletsColumns = BilWalletsColumns{
|
||||
Id: "id",
|
||||
TenantId: "tenant_id",
|
||||
Balance: "balance",
|
||||
FrozenBalance: "frozen_balance",
|
||||
WarningThreshold: "warning_threshold",
|
||||
Currency: "currency",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
Id: "id",
|
||||
TenantId: "tenant_id",
|
||||
Balance: "balance",
|
||||
FrozenBalance: "frozen_balance",
|
||||
WarningThreshold: "warning_threshold",
|
||||
Currency: "currency",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
CumulativeRecharge: "cumulative_recharge",
|
||||
}
|
||||
|
||||
// NewBilWalletsDao creates and returns a new DAO object for table data access.
|
||||
|
||||
@@ -29,9 +29,9 @@ type ChnChannelKeysColumns struct {
|
||||
LastUsedAt string // 最后使用时间
|
||||
LastError string // 最后一次错误信息
|
||||
CreatedAt string // 创建时间
|
||||
UpdatedAt string // 更新时间
|
||||
KeyType string // Key 类型:apikey(传统静态密钥)/ oauth(OAuth 令牌)
|
||||
TokenExpiresAt string // OAuth access_token 过期时间(仅 key_type=oauth 时有值)
|
||||
UpdatedAt string // 更新时间
|
||||
}
|
||||
|
||||
// chnChannelKeysColumns holds the columns for the table chn_channel_keys.
|
||||
@@ -44,9 +44,9 @@ var chnChannelKeysColumns = ChnChannelKeysColumns{
|
||||
LastUsedAt: "last_used_at",
|
||||
LastError: "last_error",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
KeyType: "key_type",
|
||||
TokenExpiresAt: "token_expires_at",
|
||||
UpdatedAt: "updated_at",
|
||||
}
|
||||
|
||||
// NewChnChannelKeysDao creates and returns a new DAO object for table data access.
|
||||
|
||||
@@ -30,12 +30,12 @@ type MdlModelsColumns struct {
|
||||
MaxOutputTokens string // 最大输出 token 数
|
||||
Description string // 模型描述
|
||||
Tags string // 标签(如 reasoning、vision、function_calling)
|
||||
Capabilities string // 模型能力特性(如 vision、function_calling、reasoning 等)
|
||||
CreatedAt string // 创建时间
|
||||
UpdatedAt string // 更新时间
|
||||
DeprecatedAt string // 标记弃用的时间(NULL表示未弃用)
|
||||
SunsetDate string // 计划下线日期(到达后返回410 Gone,NULL表示未设置)
|
||||
ReplacementModel string // 推荐替代模型名(NULL表示无替代)
|
||||
Capabilities string // 模型能力特性(如 vision、function_calling、reasoning 等)
|
||||
}
|
||||
|
||||
// mdlModelsColumns holds the columns for the table mdl_models.
|
||||
@@ -49,12 +49,12 @@ var mdlModelsColumns = MdlModelsColumns{
|
||||
MaxOutputTokens: "max_output_tokens",
|
||||
Description: "description",
|
||||
Tags: "tags",
|
||||
Capabilities: "capabilities",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
DeprecatedAt: "deprecated_at",
|
||||
SunsetDate: "sunset_date",
|
||||
ReplacementModel: "replacement_model",
|
||||
Capabilities: "capabilities",
|
||||
}
|
||||
|
||||
// NewMdlModelsDao creates and returns a new DAO object for table data access.
|
||||
|
||||
@@ -31,8 +31,8 @@ type NtfMessagesColumns struct {
|
||||
IsRead string // 是否已读:0=未读, 1=已读
|
||||
IsBroadcast string // 是否广播消息:0=个人消息, 1=广播消息
|
||||
Metadata string // 附加元数据(JSONB,如关联资源ID、跳转链接等)
|
||||
CreatedAt string // 创建时间
|
||||
TargetRoles string // 目标角色(NULL=全部角色,逗号分隔如 owner,admin 表示仅限这些角色)
|
||||
CreatedAt string // 创建时间
|
||||
}
|
||||
|
||||
// ntfMessagesColumns holds the columns for the table ntf_messages.
|
||||
@@ -47,8 +47,8 @@ var ntfMessagesColumns = NtfMessagesColumns{
|
||||
IsRead: "is_read",
|
||||
IsBroadcast: "is_broadcast",
|
||||
Metadata: "metadata",
|
||||
CreatedAt: "created_at",
|
||||
TargetRoles: "target_roles",
|
||||
CreatedAt: "created_at",
|
||||
}
|
||||
|
||||
// NewNtfMessagesDao creates and returns a new DAO object for table data access.
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
// ==========================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// ==========================================================================
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// TntTenantLevelConfigsDao is the data access object for the table tnt_tenant_level_configs.
|
||||
type TntTenantLevelConfigsDao struct {
|
||||
table string // table is the underlying table name of the DAO.
|
||||
group string // group is the database configuration group name of the current DAO.
|
||||
columns TntTenantLevelConfigsColumns // columns contains all the column names of Table for convenient usage.
|
||||
handlers []gdb.ModelHandler // handlers for customized model modification.
|
||||
}
|
||||
|
||||
// TntTenantLevelConfigsColumns defines and stores column names for the table tnt_tenant_level_configs.
|
||||
type TntTenantLevelConfigsColumns struct {
|
||||
Id string //
|
||||
Level string // 等级号(1, 2, 3...)
|
||||
Name string // 等级名称
|
||||
CumulativeRechargeThreshold string // 累计充值阈值(USD),达到此值自动升级
|
||||
MaxMembers string // 该等级最大成员数
|
||||
MaxConcurrency string // 该等级最大并发数,0=无限
|
||||
PriceMultiplier string // 价格乘数(折扣,如 0.9=九折)
|
||||
SortOrder string // 排序权重
|
||||
CreatedAt string //
|
||||
UpdatedAt string //
|
||||
}
|
||||
|
||||
// tntTenantLevelConfigsColumns holds the columns for the table tnt_tenant_level_configs.
|
||||
var tntTenantLevelConfigsColumns = TntTenantLevelConfigsColumns{
|
||||
Id: "id",
|
||||
Level: "level",
|
||||
Name: "name",
|
||||
CumulativeRechargeThreshold: "cumulative_recharge_threshold",
|
||||
MaxMembers: "max_members",
|
||||
MaxConcurrency: "max_concurrency",
|
||||
PriceMultiplier: "price_multiplier",
|
||||
SortOrder: "sort_order",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
}
|
||||
|
||||
// NewTntTenantLevelConfigsDao creates and returns a new DAO object for table data access.
|
||||
func NewTntTenantLevelConfigsDao(handlers ...gdb.ModelHandler) *TntTenantLevelConfigsDao {
|
||||
return &TntTenantLevelConfigsDao{
|
||||
group: "default",
|
||||
table: "tnt_tenant_level_configs",
|
||||
columns: tntTenantLevelConfigsColumns,
|
||||
handlers: handlers,
|
||||
}
|
||||
}
|
||||
|
||||
// DB retrieves and returns the underlying raw database management object of the current DAO.
|
||||
func (dao *TntTenantLevelConfigsDao) DB() gdb.DB {
|
||||
return g.DB(dao.group)
|
||||
}
|
||||
|
||||
// Table returns the table name of the current DAO.
|
||||
func (dao *TntTenantLevelConfigsDao) Table() string {
|
||||
return dao.table
|
||||
}
|
||||
|
||||
// Columns returns all column names of the current DAO.
|
||||
func (dao *TntTenantLevelConfigsDao) Columns() TntTenantLevelConfigsColumns {
|
||||
return dao.columns
|
||||
}
|
||||
|
||||
// Group returns the database configuration group name of the current DAO.
|
||||
func (dao *TntTenantLevelConfigsDao) Group() string {
|
||||
return dao.group
|
||||
}
|
||||
|
||||
// Ctx creates and returns a Model for the current DAO. It automatically sets the context for the current operation.
|
||||
func (dao *TntTenantLevelConfigsDao) Ctx(ctx context.Context) *gdb.Model {
|
||||
model := dao.DB().Model(dao.table)
|
||||
for _, handler := range dao.handlers {
|
||||
model = handler(model)
|
||||
}
|
||||
return model.Safe().Ctx(ctx)
|
||||
}
|
||||
|
||||
// Transaction wraps the transaction logic using function f.
|
||||
// It rolls back the transaction and returns the error if function f returns a non-nil error.
|
||||
// It commits the transaction and returns nil if function f returns nil.
|
||||
//
|
||||
// Note: Do not commit or roll back the transaction in function f,
|
||||
// as it is automatically handled by this function.
|
||||
func (dao *TntTenantLevelConfigsDao) Transaction(ctx context.Context, f func(ctx context.Context, tx gdb.TX) error) (err error) {
|
||||
return dao.Ctx(ctx).Transaction(ctx, f)
|
||||
}
|
||||
@@ -38,6 +38,7 @@ type TntTenantsColumns struct {
|
||||
DataRemovalAt string // 数据清除时间(冻结 30 天后)
|
||||
MaxConcurrency string // 租户总并发上限(0表示不限制)
|
||||
DefaultChannelScope string // 默认渠道范围(NULL或[]表示全部可用,否则为channel_id数组)
|
||||
Level string // 当前等级(对应 tnt_tenant_level_configs.level)
|
||||
}
|
||||
|
||||
// tntTenantsColumns holds the columns for the table tnt_tenants.
|
||||
@@ -59,6 +60,7 @@ var tntTenantsColumns = TntTenantsColumns{
|
||||
DataRemovalAt: "data_removal_at",
|
||||
MaxConcurrency: "max_concurrency",
|
||||
DefaultChannelScope: "default_channel_scope",
|
||||
Level: "level",
|
||||
}
|
||||
|
||||
// NewTntTenantsDao creates and returns a new DAO object for table data access.
|
||||
|
||||
@@ -21,30 +21,30 @@ type TskModelTasksDao struct {
|
||||
|
||||
// TskModelTasksColumns defines and stores column names for the table tsk_model_tasks.
|
||||
type TskModelTasksColumns struct {
|
||||
Id string //
|
||||
PublicTaskId string //
|
||||
Platform string //
|
||||
Action string //
|
||||
Status string //
|
||||
Progress string //
|
||||
FailReason string //
|
||||
TenantId string //
|
||||
UserId string //
|
||||
ApiKeyId string //
|
||||
ChannelId string //
|
||||
ModelName string //
|
||||
UpstreamModel string //
|
||||
PreDeductAmount string //
|
||||
ActualCost string //
|
||||
BillingSettled string //
|
||||
ResultUrl string //
|
||||
Data string //
|
||||
PrivateData string //
|
||||
SubmitTime string //
|
||||
StartTime string //
|
||||
FinishTime string //
|
||||
CreatedAt string //
|
||||
UpdatedAt string //
|
||||
Id string // 主键ID
|
||||
PublicTaskId string // 对外公开的任务ID(task_xxxxx 格式),用于 API 响应和客户端查询
|
||||
Platform string // 任务所属平台:sora、kling、suno、midjourney、volcengine
|
||||
Action string // 任务动作类型。通用:generate;Suno:music、lyrics;
|
||||
Status string // 任务状态:NOT_START-未开始、SUBMITTED-已提交、IN_PROGRESS-进行中、SUCCESS-成功、FAILURE-失败
|
||||
Progress string // 任务进度,字符串格式百分比(如 0%、50%、100%)
|
||||
FailReason string // 任务失败原因文本
|
||||
TenantId string // 发起任务的租户ID
|
||||
UserId string // 发起任务的租户用户ID
|
||||
ApiKeyId string // 调用方使用的 API Key ID
|
||||
ChannelId string // 转发请求的渠道ID
|
||||
ModelName string // 用户请求的模型名称(如 sora-1.0-turbo、midjourney 等)
|
||||
UpstreamModel string // 上游供应商实际使用的模型名称,可能与请求模型不同
|
||||
PreDeductAmount string // 提交任务时的预扣金额(USD),任务完成后根据实际用量结算差额
|
||||
ActualCost string // 任务实际消费金额(USD),成功时按此结算,失败时退还预扣金额
|
||||
BillingSettled string // 计费是否已结算(true 表示已完成预扣与实际金额的多退少补)
|
||||
ResultUrl string // 任务完成后的结果资源 URL(如生成的视频/图片/音频的下载地址)
|
||||
Data string // 上游供应商返回的完整响应数据(JSONB),可返回给用户查看
|
||||
PrivateData string // 内部私有数据(JSONB),含 upstream_task_id 等敏感字段,用于轮询上游状态,不返回给用户
|
||||
SubmitTime string // 任务提交到上游供应商的时间
|
||||
StartTime string // 上游供应商开始执行任务的时间
|
||||
FinishTime string // 任务完成(成功或失败)的时间
|
||||
CreatedAt string // 记录创建时间
|
||||
UpdatedAt string // 记录更新时间
|
||||
RequestId string // 任务提交时的原始请求 ID(req_xxxxx),关联 aud_request_logs.request_id
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
// =================================================================================
|
||||
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
|
||||
// =================================================================================
|
||||
|
||||
package dao
|
||||
|
||||
import (
|
||||
"github.com/qianfree/team-api/internal/dao/internal"
|
||||
)
|
||||
|
||||
// tntTenantLevelConfigsDao is the data access object for the table tnt_tenant_level_configs.
|
||||
// You can define custom methods on it to extend its functionality as needed.
|
||||
type tntTenantLevelConfigsDao struct {
|
||||
*internal.TntTenantLevelConfigsDao
|
||||
}
|
||||
|
||||
var (
|
||||
// TntTenantLevelConfigs is a globally accessible object for table tnt_tenant_level_configs operations.
|
||||
TntTenantLevelConfigs = tntTenantLevelConfigsDao{internal.NewTntTenantLevelConfigsDao()}
|
||||
)
|
||||
|
||||
// Add your custom methods and functionality below.
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/qianfree/team-api/internal/dao"
|
||||
"github.com/qianfree/team-api/internal/logic/common"
|
||||
do "github.com/qianfree/team-api/internal/model/do"
|
||||
"github.com/qianfree/team-api/internal/model/entity"
|
||||
"github.com/qianfree/team-api/internal/utility/crypto"
|
||||
"github.com/qianfree/team-api/internal/utility/export"
|
||||
)
|
||||
@@ -118,6 +119,7 @@ func (s *sAdmin) CreateTenant(ctx context.Context, req *v1.TenantCreateReq) (*v1
|
||||
Code: tenantCode,
|
||||
MaxMembers: maxMembers,
|
||||
MaxConcurrency: maxConcurrency,
|
||||
Level: 1,
|
||||
Settings: "{}",
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
@@ -289,6 +291,7 @@ func (s *sAdmin) GetTenant(ctx context.Context, req *v1.TenantGetReq) (*v1.Tenan
|
||||
MaxConcurrency int `json:"max_concurrency"`
|
||||
DefaultChannelScope string `json:"default_channel_scope"`
|
||||
Settings string `json:"settings"`
|
||||
Level int `json:"level"`
|
||||
CreatedAt *gtime.Time `json:"created_at"`
|
||||
UpdatedAt *gtime.Time `json:"updated_at"`
|
||||
}
|
||||
@@ -327,6 +330,15 @@ func (s *sAdmin) GetTenant(ctx context.Context, req *v1.TenantGetReq) (*v1.Tenan
|
||||
if owner != nil {
|
||||
ownerName = owner.DisplayName
|
||||
}
|
||||
|
||||
// Get level name
|
||||
levelName := ""
|
||||
var levelConfig entity.TntTenantLevelConfigs
|
||||
_ = dao.TntTenantLevelConfigs.Ctx(ctx).Where("level", tenant.Level).Scan(&levelConfig)
|
||||
if levelConfig.Id > 0 {
|
||||
levelName = levelConfig.Name
|
||||
}
|
||||
|
||||
return &v1.TenantGetRes{
|
||||
TenantItem: v1.TenantItem{
|
||||
ID: tenant.Id,
|
||||
@@ -341,6 +353,8 @@ func (s *sAdmin) GetTenant(ctx context.Context, req *v1.TenantGetReq) (*v1.Tenan
|
||||
DefaultChannelScope: tenant.DefaultChannelScope,
|
||||
MemberCount: memberCount,
|
||||
WalletBalance: walletBalance,
|
||||
Level: tenant.Level,
|
||||
LevelName: levelName,
|
||||
CreatedAt: tenant.CreatedAt.String(),
|
||||
UpdatedAt: tenant.UpdatedAt.String(),
|
||||
},
|
||||
@@ -394,6 +408,20 @@ func (s *sAdmin) UpdateTenant(ctx context.Context, req *v1.TenantUpdateReq) (*v1
|
||||
data.MaxConcurrency = *req.MaxConcurrency
|
||||
}
|
||||
|
||||
// 管理员手动调整等级:同步更新 max_members 和 max_concurrency
|
||||
if req.Level != nil {
|
||||
data.Level = *req.Level
|
||||
var config entity.TntTenantLevelConfigs
|
||||
err := dao.TntTenantLevelConfigs.Ctx(ctx).Where("level", *req.Level).Scan(&config)
|
||||
if err != nil {
|
||||
return nil, common.NewBadRequestError("等级配置不存在")
|
||||
}
|
||||
if config.Id > 0 {
|
||||
data.MaxMembers = config.MaxMembers
|
||||
data.MaxConcurrency = config.MaxConcurrency
|
||||
}
|
||||
}
|
||||
|
||||
_, err := dao.TntTenants.Ctx(ctx).Where("id", req.Id).Update(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
|
||||
"github.com/qianfree/team-api/api/admin/v1"
|
||||
"github.com/qianfree/team-api/internal/dao"
|
||||
"github.com/qianfree/team-api/internal/model/do"
|
||||
"github.com/qianfree/team-api/internal/model/entity"
|
||||
)
|
||||
|
||||
func (s *sAdmin) ListTenantLevelConfigs(ctx context.Context, _ *v1.TenantLevelConfigListReq) (*v1.TenantLevelConfigListRes, error) {
|
||||
var configs []*entity.TntTenantLevelConfigs
|
||||
err := dao.TntTenantLevelConfigs.Ctx(ctx).
|
||||
OrderAsc("level").
|
||||
Scan(&configs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list := make([]*v1.TenantLevelConfigItem, 0, len(configs))
|
||||
for _, c := range configs {
|
||||
list = append(list, &v1.TenantLevelConfigItem{
|
||||
Id: c.Id,
|
||||
Level: c.Level,
|
||||
Name: c.Name,
|
||||
CumulativeRechargeThreshold: c.CumulativeRechargeThreshold,
|
||||
MaxMembers: c.MaxMembers,
|
||||
MaxConcurrency: c.MaxConcurrency,
|
||||
PriceMultiplier: c.PriceMultiplier,
|
||||
SortOrder: c.SortOrder,
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
return &v1.TenantLevelConfigListRes{List: list}, nil
|
||||
}
|
||||
|
||||
func (s *sAdmin) CreateTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigCreateReq) (*v1.TenantLevelConfigCreateRes, error) {
|
||||
// 检查 level 是否已存在
|
||||
count, _ := dao.TntTenantLevelConfigs.Ctx(ctx).Where("level", req.Level).Count()
|
||||
if count > 0 {
|
||||
return nil, gerror.New("等级号已存在")
|
||||
}
|
||||
|
||||
result, err := dao.TntTenantLevelConfigs.Ctx(ctx).Insert(do.TntTenantLevelConfigs{
|
||||
Level: req.Level,
|
||||
Name: req.Name,
|
||||
CumulativeRechargeThreshold: req.CumulativeRechargeThreshold,
|
||||
MaxMembers: req.MaxMembers,
|
||||
MaxConcurrency: req.MaxConcurrency,
|
||||
PriceMultiplier: req.PriceMultiplier,
|
||||
SortOrder: req.SortOrder,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
id, _ := result.LastInsertId()
|
||||
return &v1.TenantLevelConfigCreateRes{ID: id}, nil
|
||||
}
|
||||
|
||||
func (s *sAdmin) UpdateTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigUpdateReq) (*v1.TenantLevelConfigUpdateRes, error) {
|
||||
_, err := dao.TntTenantLevelConfigs.Ctx(ctx).
|
||||
Where("id", req.Id).
|
||||
Data(req.Update).
|
||||
Update()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v1.TenantLevelConfigUpdateRes{}, nil
|
||||
}
|
||||
|
||||
func (s *sAdmin) DeleteTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigDeleteReq) (*v1.TenantLevelConfigDeleteRes, error) {
|
||||
// 不允许删除 level=1(默认等级)
|
||||
var config entity.TntTenantLevelConfigs
|
||||
err := dao.TntTenantLevelConfigs.Ctx(ctx).Where("id", req.Id).Scan(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.Id == 0 {
|
||||
return nil, gerror.New("等级配置不存在")
|
||||
}
|
||||
if config.Level == 1 {
|
||||
return nil, gerror.New("不允许删除默认等级 LV1")
|
||||
}
|
||||
|
||||
_, err = dao.TntTenantLevelConfigs.Ctx(ctx).Where("id", req.Id).Delete()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v1.TenantLevelConfigDeleteRes{}, nil
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package billing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
|
||||
"github.com/qianfree/team-api/internal/dao"
|
||||
"github.com/qianfree/team-api/internal/model/entity"
|
||||
)
|
||||
|
||||
// GetTenantLevelConfig 获取指定等级的配置
|
||||
func GetTenantLevelConfig(ctx context.Context, level int) (*entity.TntTenantLevelConfigs, error) {
|
||||
var config entity.TntTenantLevelConfigs
|
||||
err := dao.TntTenantLevelConfigs.Ctx(ctx).
|
||||
Where("level", level).
|
||||
Scan(&config)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.Id == 0 {
|
||||
return nil, fmt.Errorf("level config not found for level %d", level)
|
||||
}
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// CheckAndUpgradeLevel 检查并升级租户等级(充值后调用)
|
||||
// 升级策略:仅升不降 — max_members/max_concurrency 仅在新值 > 当前值时才更新
|
||||
func CheckAndUpgradeLevel(ctx context.Context, tenantID int64) error {
|
||||
// 1. 读取租户当前等级
|
||||
var tenant entity.TntTenants
|
||||
err := dao.TntTenants.Ctx(ctx).Where("id", tenantID).Scan(&tenant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tenant.Id == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 2. 读取累计充值
|
||||
var wallet entity.BilWallets
|
||||
err = dao.BilWallets.Ctx(ctx).Where("tenant_id", tenantID).Scan(&wallet)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if wallet.Id == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 3. 根据累计充值计算应得等级
|
||||
newLevel, err := RecalculateLevel(ctx, wallet.CumulativeRecharge)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 4. 仅当等级提升时才更新
|
||||
if newLevel <= tenant.Level {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 5. 获取新等级配置
|
||||
config, err := GetTenantLevelConfig(ctx, newLevel)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 6. 构建更新数据:仅升不降策略
|
||||
updateData := g.Map{
|
||||
"level": newLevel,
|
||||
"updated_at": "NOW()",
|
||||
}
|
||||
|
||||
// max_members:新值 > 当前值才更新(0=无限,视为最大)
|
||||
if config.MaxMembers == 0 {
|
||||
// 新等级允许无限成员,直接更新
|
||||
updateData["max_members"] = 0
|
||||
} else if config.MaxMembers > tenant.MaxMembers {
|
||||
updateData["max_members"] = config.MaxMembers
|
||||
}
|
||||
|
||||
// max_concurrency:新值 > 当前值才更新(0=无限,视为最大)
|
||||
if config.MaxConcurrency == 0 {
|
||||
updateData["max_concurrency"] = 0
|
||||
} else if config.MaxConcurrency > tenant.MaxConcurrency {
|
||||
updateData["max_concurrency"] = config.MaxConcurrency
|
||||
}
|
||||
|
||||
_, err = dao.TntTenants.Ctx(ctx).
|
||||
Where("id", tenantID).
|
||||
Data(updateData).
|
||||
Update()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 7. 清除并发限制缓存
|
||||
_, _ = g.Redis().Do(ctx, "DEL", fmt.Sprintf("tenant:conc_limit:%d", tenantID))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RecalculateLevel 根据累计充值金额重新计算等级
|
||||
func RecalculateLevel(ctx context.Context, cumulativeRecharge float64) (int, error) {
|
||||
var config entity.TntTenantLevelConfigs
|
||||
err := dao.TntTenantLevelConfigs.Ctx(ctx).
|
||||
Where("cumulative_recharge_threshold <= ?", cumulativeRecharge).
|
||||
OrderDesc("level").
|
||||
Limit(1).
|
||||
Scan(&config)
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
if config.Id == 0 {
|
||||
return 1, nil
|
||||
}
|
||||
return config.Level, nil
|
||||
}
|
||||
@@ -53,6 +53,8 @@ func FulfillOrder(ctx context.Context, orderID int64) error {
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, "credit wallet failed")
|
||||
}
|
||||
// 检查并升级租户等级
|
||||
_ = billing.CheckAndUpgradeLevel(ctx, order.TenantID)
|
||||
|
||||
default:
|
||||
return gerror.Newf("unsupported order type for fulfillment: %s", order.OrderType)
|
||||
@@ -134,8 +136,8 @@ func creditWallet(ctx context.Context, tenantID int64, amount float64, descripti
|
||||
|
||||
err = g.DB().Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
_, err := tx.Ctx(ctx).Exec(
|
||||
"UPDATE bil_wallets SET balance = balance + ?, updated_at = NOW() WHERE id = ?",
|
||||
amount, w.ID)
|
||||
"UPDATE bil_wallets SET balance = balance + ?, cumulative_recharge = cumulative_recharge + ?, updated_at = NOW() WHERE id = ?",
|
||||
amount, amount, w.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -80,10 +80,12 @@ func (s *sTenant) Register(ctx context.Context, req *v1.TenantRegisterReq) (*v1.
|
||||
err = dao.TntTenants.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
// Create tenant
|
||||
tenantResult, err := tx.Model("tnt_tenants").Ctx(ctx).Data(do.TntTenants{
|
||||
Name: strings.TrimSpace(req.TenantName),
|
||||
Code: tenantCode,
|
||||
MaxMembers: 10,
|
||||
Settings: "{}",
|
||||
Name: strings.TrimSpace(req.TenantName),
|
||||
Code: tenantCode,
|
||||
MaxMembers: 10,
|
||||
MaxConcurrency: 10,
|
||||
Level: 1,
|
||||
Settings: "{}",
|
||||
}).Insert()
|
||||
if err != nil {
|
||||
return gerror.Wrapf(err, "create tenant")
|
||||
|
||||
@@ -26,16 +26,17 @@ func (s *sTenant) Wallet(ctx context.Context, req *v1.TenantWalletReq) (*v1.Tena
|
||||
tenantID := ctxTenantID(ctx)
|
||||
|
||||
type walletRow struct {
|
||||
Balance float64 `json:"balance"`
|
||||
FrozenBalance float64 `json:"frozen_balance"`
|
||||
WarningThreshold float64 `json:"warning_threshold"`
|
||||
Currency string `json:"currency"`
|
||||
Balance float64 `json:"balance"`
|
||||
FrozenBalance float64 `json:"frozen_balance"`
|
||||
WarningThreshold float64 `json:"warning_threshold"`
|
||||
Currency string `json:"currency"`
|
||||
CumulativeRecharge float64 `json:"cumulative_recharge"`
|
||||
}
|
||||
|
||||
var w *walletRow
|
||||
err := dao.BilWallets.Ctx(ctx).
|
||||
Where("tenant_id", tenantID).
|
||||
Fields("balance, frozen_balance, warning_threshold, currency").
|
||||
Fields("balance, frozen_balance, warning_threshold, currency, cumulative_recharge").
|
||||
Scan(&w)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -30,6 +30,7 @@ func (s *sTenant) GetOrgInfo(ctx context.Context, req *v1.TenantOrgInfoReq) (*v1
|
||||
Code string `json:"code"`
|
||||
LogoUrl string `json:"logo_url"`
|
||||
Status string `json:"status"`
|
||||
Level int `json:"level"`
|
||||
MaxMembers int `json:"max_members"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
@@ -50,12 +51,23 @@ func (s *sTenant) GetOrgInfo(ctx context.Context, req *v1.TenantOrgInfoReq) (*v1
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Look up level name
|
||||
var levelName string
|
||||
if tenant.Level > 0 {
|
||||
_ = dao.TntTenantLevelConfigs.Ctx(ctx).
|
||||
Where("level", tenant.Level).
|
||||
Fields("name").
|
||||
Scan(&levelName)
|
||||
}
|
||||
|
||||
return &v1.TenantOrgInfoRes{
|
||||
ID: tenant.Id,
|
||||
Name: tenant.Name,
|
||||
Code: tenant.Code,
|
||||
LogoURL: tenant.LogoUrl,
|
||||
Status: tenant.Status,
|
||||
Level: tenant.Level,
|
||||
LevelName: levelName,
|
||||
MaxMembers: tenant.MaxMembers,
|
||||
MemberCount: int(memberCount),
|
||||
CreatedAt: tenant.CreatedAt,
|
||||
|
||||
@@ -16,6 +16,7 @@ type AudContentFilterLogs struct {
|
||||
TenantId any // 租户ID
|
||||
UserId any // 用户ID
|
||||
ApiKeyId any // API Key ID
|
||||
ProjectId any // 项目ID
|
||||
RequestId any // 请求唯一ID
|
||||
Method any // HTTP 方法
|
||||
Path any // 请求路径
|
||||
@@ -25,5 +26,4 @@ type AudContentFilterLogs struct {
|
||||
OriginalSnippet any // 原始请求体片段(截断存储,仅 replace 模式)
|
||||
Blocked any // 是否被拦截(mode=block 时为 true)
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
ProjectId any // 项目ID
|
||||
}
|
||||
@@ -11,13 +11,14 @@ import (
|
||||
|
||||
// BilWallets is the golang structure of table bil_wallets for DAO operations like Where/Data.
|
||||
type BilWallets struct {
|
||||
g.Meta `orm:"table:bil_wallets, do:true"`
|
||||
Id any // 主键ID
|
||||
TenantId any // 租户ID(每个租户一个钱包)
|
||||
Balance any // 总余额
|
||||
FrozenBalance any // 冻结余额(支付中/退款中,可用余额 = balance - frozen_balance)
|
||||
WarningThreshold any // 余额预警线(低于此值触发通知)
|
||||
Currency any // 货币(USD)
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
g.Meta `orm:"table:bil_wallets, do:true"`
|
||||
Id any // 主键ID
|
||||
TenantId any // 租户ID(每个租户一个钱包)
|
||||
Balance any // 总余额
|
||||
FrozenBalance any // 冻结余额(支付中/退款中,可用余额 = balance - frozen_balance)
|
||||
WarningThreshold any // 余额预警线(低于此值触发通知)
|
||||
Currency any // 货币(USD)
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
CumulativeRecharge any // 累计充值总额(USD)
|
||||
}
|
||||
@@ -20,7 +20,7 @@ type ChnChannelKeys struct {
|
||||
LastUsedAt *gtime.Time // 最后使用时间
|
||||
LastError any // 最后一次错误信息
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
KeyType any // Key 类型:apikey(传统静态密钥)/ oauth(OAuth 令牌)
|
||||
TokenExpiresAt *gtime.Time // OAuth access_token 过期时间(仅 key_type=oauth 时有值)
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
}
|
||||
@@ -21,10 +21,10 @@ type MdlModels struct {
|
||||
MaxOutputTokens any // 最大输出 token 数
|
||||
Description any // 模型描述
|
||||
Tags []string // 标签(如 reasoning、vision、function_calling)
|
||||
Capabilities any // 模型能力特性(如 vision、function_calling、reasoning 等)
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
UpdatedAt *gtime.Time // 更新时间
|
||||
DeprecatedAt *gtime.Time // 标记弃用的时间(NULL表示未弃用)
|
||||
SunsetDate *gtime.Time // 计划下线日期(到达后返回410 Gone,NULL表示未设置)
|
||||
ReplacementModel any // 推荐替代模型名(NULL表示无替代)
|
||||
Capabilities any // 模型能力特性(如 vision、function_calling、reasoning 等)
|
||||
}
|
||||
@@ -22,6 +22,6 @@ type NtfMessages struct {
|
||||
IsRead any // 是否已读:0=未读, 1=已读
|
||||
IsBroadcast any // 是否广播消息:0=个人消息, 1=广播消息
|
||||
Metadata any // 附加元数据(JSONB,如关联资源ID、跳转链接等)
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
TargetRoles any // 目标角色(NULL=全部角色,逗号分隔如 owner,admin 表示仅限这些角色)
|
||||
CreatedAt *gtime.Time // 创建时间
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package do
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// TntTenantLevelConfigs is the golang structure of table tnt_tenant_level_configs for DAO operations like Where/Data.
|
||||
type TntTenantLevelConfigs struct {
|
||||
g.Meta `orm:"table:tnt_tenant_level_configs, do:true"`
|
||||
Id any //
|
||||
Level any // 等级号(1, 2, 3...)
|
||||
Name any // 等级名称
|
||||
CumulativeRechargeThreshold any // 累计充值阈值(USD),达到此值自动升级
|
||||
MaxMembers any // 该等级最大成员数
|
||||
MaxConcurrency any // 该等级最大并发数,0=无限
|
||||
PriceMultiplier any // 价格乘数(折扣,如 0.9=九折)
|
||||
SortOrder any // 排序权重
|
||||
CreatedAt *gtime.Time //
|
||||
UpdatedAt *gtime.Time //
|
||||
}
|
||||
@@ -29,4 +29,5 @@ type TntTenants struct {
|
||||
DataRemovalAt *gtime.Time // 数据清除时间(冻结 30 天后)
|
||||
MaxConcurrency any // 租户总并发上限(0表示不限制)
|
||||
DefaultChannelScope any // 默认渠道范围(NULL或[]表示全部可用,否则为channel_id数组)
|
||||
Level any // 当前等级(对应 tnt_tenant_level_configs.level)
|
||||
}
|
||||
@@ -12,29 +12,29 @@ import (
|
||||
// TskModelTasks is the golang structure of table tsk_model_tasks for DAO operations like Where/Data.
|
||||
type TskModelTasks struct {
|
||||
g.Meta `orm:"table:tsk_model_tasks, do:true"`
|
||||
Id any //
|
||||
PublicTaskId any //
|
||||
Platform any //
|
||||
Action any //
|
||||
Status any //
|
||||
Progress any //
|
||||
FailReason any //
|
||||
TenantId any //
|
||||
UserId any //
|
||||
ApiKeyId any //
|
||||
ChannelId any //
|
||||
ModelName any //
|
||||
UpstreamModel any //
|
||||
PreDeductAmount any //
|
||||
ActualCost any //
|
||||
BillingSettled any //
|
||||
ResultUrl any //
|
||||
Data any //
|
||||
PrivateData any //
|
||||
SubmitTime *gtime.Time //
|
||||
StartTime *gtime.Time //
|
||||
FinishTime *gtime.Time //
|
||||
CreatedAt *gtime.Time //
|
||||
UpdatedAt *gtime.Time //
|
||||
Id any // 主键ID
|
||||
PublicTaskId any // 对外公开的任务ID(task_xxxxx 格式),用于 API 响应和客户端查询
|
||||
Platform any // 任务所属平台:sora、kling、suno、midjourney、volcengine
|
||||
Action any // 任务动作类型。通用:generate;Suno:music、lyrics;
|
||||
Status any // 任务状态:NOT_START-未开始、SUBMITTED-已提交、IN_PROGRESS-进行中、SUCCESS-成功、FAILURE-失败
|
||||
Progress any // 任务进度,字符串格式百分比(如 0%、50%、100%)
|
||||
FailReason any // 任务失败原因文本
|
||||
TenantId any // 发起任务的租户ID
|
||||
UserId any // 发起任务的租户用户ID
|
||||
ApiKeyId any // 调用方使用的 API Key ID
|
||||
ChannelId any // 转发请求的渠道ID
|
||||
ModelName any // 用户请求的模型名称(如 sora-1.0-turbo、midjourney 等)
|
||||
UpstreamModel any // 上游供应商实际使用的模型名称,可能与请求模型不同
|
||||
PreDeductAmount any // 提交任务时的预扣金额(USD),任务完成后根据实际用量结算差额
|
||||
ActualCost any // 任务实际消费金额(USD),成功时按此结算,失败时退还预扣金额
|
||||
BillingSettled any // 计费是否已结算(true 表示已完成预扣与实际金额的多退少补)
|
||||
ResultUrl any // 任务完成后的结果资源 URL(如生成的视频/图片/音频的下载地址)
|
||||
Data any // 上游供应商返回的完整响应数据(JSONB),可返回给用户查看
|
||||
PrivateData any // 内部私有数据(JSONB),含 upstream_task_id 等敏感字段,用于轮询上游状态,不返回给用户
|
||||
SubmitTime *gtime.Time // 任务提交到上游供应商的时间
|
||||
StartTime *gtime.Time // 上游供应商开始执行任务的时间
|
||||
FinishTime *gtime.Time // 任务完成(成功或失败)的时间
|
||||
CreatedAt *gtime.Time // 记录创建时间
|
||||
UpdatedAt *gtime.Time // 记录更新时间
|
||||
RequestId any // 任务提交时的原始请求 ID(req_xxxxx),关联 aud_request_logs.request_id
|
||||
}
|
||||
@@ -14,6 +14,7 @@ type AudContentFilterLogs struct {
|
||||
TenantId int64 `json:"tenant_id" orm:"tenant_id" description:"租户ID"` // 租户ID
|
||||
UserId int64 `json:"user_id" orm:"user_id" description:"用户ID"` // 用户ID
|
||||
ApiKeyId int64 `json:"api_key_id" orm:"api_key_id" description:"API Key ID"` // API Key ID
|
||||
ProjectId int64 `json:"project_id" orm:"project_id" description:"项目ID"` // 项目ID
|
||||
RequestId string `json:"request_id" orm:"request_id" description:"请求唯一ID"` // 请求唯一ID
|
||||
Method string `json:"method" orm:"method" description:"HTTP 方法"` // HTTP 方法
|
||||
Path string `json:"path" orm:"path" description:"请求路径"` // 请求路径
|
||||
@@ -23,5 +24,4 @@ type AudContentFilterLogs struct {
|
||||
OriginalSnippet string `json:"original_snippet" orm:"original_snippet" description:"原始请求体片段(截断存储,仅 replace 模式)"` // 原始请求体片段(截断存储,仅 replace 模式)
|
||||
Blocked bool `json:"blocked" orm:"blocked" description:"是否被拦截(mode=block 时为 true)"` // 是否被拦截(mode=block 时为 true)
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
ProjectId int64 `json:"project_id" orm:"project_id" description:"项目ID"` // 项目ID
|
||||
}
|
||||
@@ -10,22 +10,22 @@ import (
|
||||
|
||||
// BilTransactions is the golang structure for table bil_transactions.
|
||||
type BilTransactions struct {
|
||||
Id int64 `json:"id" orm:"id" description:"主键ID"` // 主键ID
|
||||
TenantId int64 `json:"tenant_id" orm:"tenant_id" description:"租户ID"` // 租户ID
|
||||
WalletId int64 `json:"wallet_id" orm:"wallet_id" description:"关联钱包ID"` // 关联钱包ID
|
||||
Type string `json:"type" orm:"type" description:"类型:consume(消费)/ recharge(充值)/ adjust(调整)/ pre_deduct(预扣,已废弃)/ settle(结算,已废弃)/ refund(退款,已废弃)/ freeze(冻结,已废弃)/ unfreeze(解冻,已废弃)"` // 类型:consume(消费)/ recharge(充值)/ adjust(调整)/ pre_deduct(预扣,已废弃)/ settle(结算,已废弃)/ refund(退款,已废弃)/ freeze(冻结,已废弃)/ unfreeze(解冻,已废弃)
|
||||
Amount float64 `json:"amount" orm:"amount" description:"变动金额(正数=收入,负数=支出)"` // 变动金额(正数=收入,负数=支出)
|
||||
BalanceAfter float64 `json:"balance_after" orm:"balance_after" description:"变动后总余额"` // 变动后总余额
|
||||
FrozenAfter float64 `json:"frozen_after" orm:"frozen_after" description:"变动后冻结余额"` // 变动后冻结余额
|
||||
RelatedId int64 `json:"related_id" orm:"related_id" description:"关联业务ID(如计费记录ID、订单ID等)"` // 关联业务ID(如计费记录ID、订单ID等)
|
||||
RelatedType string `json:"related_type" orm:"related_type" description:"关联业务类型:billing_record / order / refund / adjustment / redemption"` // 关联业务类型:billing_record / order / refund / adjustment / redemption
|
||||
Description string `json:"description" orm:"description" description:"交易描述"` // 交易描述
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"` // 更新时间
|
||||
UserId int64 `json:"user_id" orm:"user_id" description:"关联用户ID(consume 类型为实际消费用户,recharge 类型为操作用户,adjust 类型为空)"` // 关联用户ID(consume 类型为实际消费用户,recharge 类型为操作用户,adjust 类型为空)
|
||||
RequestId string `json:"request_id" orm:"request_id" description:"关联请求ID(consume 类型对应 API 调用的 request_id,其他类型为空)"` // 关联请求ID(consume 类型对应 API 调用的 request_id,其他类型为空)
|
||||
ModelName string `json:"model_name" orm:"model_name" description:"关联模型名(consume 类型为调用的模型名,其他类型为空)"` // 关联模型名(consume 类型为调用的模型名,其他类型为空)
|
||||
ProjectId int64 `json:"project_id" orm:"project_id" description:"关联项目ID(consume 类型为 API Key 所属项目,个人密钥为空)"` // 关联项目ID(consume 类型为 API Key 所属项目,个人密钥为空)
|
||||
ApiKeyId int64 `json:"api_key_id" orm:"api_key_id" description:"关联API密钥ID(consume 类型为发起请求的密钥)"` // 关联API密钥ID(consume 类型为发起请求的密钥)
|
||||
TaskId string `json:"task_id" orm:"task_id" description:"关联异步任务公开ID(consume+relay_mode=task 时关联 tsk_model_tasks.public_task_id)"` // 关联异步任务公开ID(consume+relay_mode=task 时关联 tsk_model_tasks.public_task_id)
|
||||
Id int64 `json:"id" orm:"id" description:"主键ID"` // 主键ID
|
||||
TenantId int64 `json:"tenant_id" orm:"tenant_id" description:"租户ID"` // 租户ID
|
||||
WalletId int64 `json:"wallet_id" orm:"wallet_id" description:"关联钱包ID"` // 关联钱包ID
|
||||
Type string `json:"type" orm:"type" description:"类型:consume(消费)/ recharge(充值)/ adjust(调整)/ pre_deduct(预扣,已废弃)/ settle(结算,已废弃)/ refund(退款,已废弃)/ freeze(冻结,已废弃)/ unfreeze(解冻,已废弃)"` // 类型:consume(消费)/ recharge(充值)/ adjust(调整)/ pre_deduct(预扣,已废弃)/ settle(结算,已废弃)/ refund(退款,已废弃)/ freeze(冻结,已废弃)/ unfreeze(解冻,已废弃)
|
||||
Amount float64 `json:"amount" orm:"amount" description:"变动金额(正数=收入,负数=支出)"` // 变动金额(正数=收入,负数=支出)
|
||||
BalanceAfter float64 `json:"balance_after" orm:"balance_after" description:"变动后总余额"` // 变动后总余额
|
||||
FrozenAfter float64 `json:"frozen_after" orm:"frozen_after" description:"变动后冻结余额"` // 变动后冻结余额
|
||||
RelatedId int64 `json:"related_id" orm:"related_id" description:"关联业务ID(如计费记录ID、订单ID等)"` // 关联业务ID(如计费记录ID、订单ID等)
|
||||
RelatedType string `json:"related_type" orm:"related_type" description:"关联业务类型:billing_record / order / refund / adjustment / redemption"` // 关联业务类型:billing_record / order / refund / adjustment / redemption
|
||||
Description string `json:"description" orm:"description" description:"交易描述"` // 交易描述
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"` // 更新时间
|
||||
UserId int64 `json:"user_id" orm:"user_id" description:"关联用户ID(consume 类型为实际消费用户,recharge 类型为操作用户,adjust 类型为空)"` // 关联用户ID(consume 类型为实际消费用户,recharge 类型为操作用户,adjust 类型为空)
|
||||
RequestId string `json:"request_id" orm:"request_id" description:"关联请求ID(consume 类型对应 API 调用的 request_id,其他类型为空)"` // 关联请求ID(consume 类型对应 API 调用的 request_id,其他类型为空)
|
||||
ModelName string `json:"model_name" orm:"model_name" description:"关联模型名(consume 类型为调用的模型名,其他类型为空)"` // 关联模型名(consume 类型为调用的模型名,其他类型为空)
|
||||
ProjectId int64 `json:"project_id" orm:"project_id" description:"关联项目ID(consume 类型为 API Key 所属项目,个人密钥为空)"` // 关联项目ID(consume 类型为 API Key 所属项目,个人密钥为空)
|
||||
ApiKeyId int64 `json:"api_key_id" orm:"api_key_id" description:"关联API密钥ID(consume 类型为发起请求的密钥)"` // 关联API密钥ID(consume 类型为发起请求的密钥)
|
||||
TaskId string `json:"task_id" orm:"task_id" description:"关联异步任务公开ID(consume+relay_mode=task 时关联 tsk_model_tasks.public_task_id)"` // 关联异步任务公开ID(consume+relay_mode=task 时关联 tsk_model_tasks.public_task_id)
|
||||
}
|
||||
@@ -10,12 +10,13 @@ import (
|
||||
|
||||
// BilWallets is the golang structure for table bil_wallets.
|
||||
type BilWallets struct {
|
||||
Id int64 `json:"id" orm:"id" description:"主键ID"` // 主键ID
|
||||
TenantId int64 `json:"tenant_id" orm:"tenant_id" description:"租户ID(每个租户一个钱包)"` // 租户ID(每个租户一个钱包)
|
||||
Balance float64 `json:"balance" orm:"balance" description:"总余额"` // 总余额
|
||||
FrozenBalance float64 `json:"frozen_balance" orm:"frozen_balance" description:"冻结余额(支付中/退款中,可用余额 = balance - frozen_balance)"` // 冻结余额(支付中/退款中,可用余额 = balance - frozen_balance)
|
||||
WarningThreshold float64 `json:"warning_threshold" orm:"warning_threshold" description:"余额预警线(低于此值触发通知)"` // 余额预警线(低于此值触发通知)
|
||||
Currency string `json:"currency" orm:"currency" description:"货币(USD)"` // 货币(USD)
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"` // 更新时间
|
||||
Id int64 `json:"id" orm:"id" description:"主键ID"` // 主键ID
|
||||
TenantId int64 `json:"tenant_id" orm:"tenant_id" description:"租户ID(每个租户一个钱包)"` // 租户ID(每个租户一个钱包)
|
||||
Balance float64 `json:"balance" orm:"balance" description:"总余额"` // 总余额
|
||||
FrozenBalance float64 `json:"frozen_balance" orm:"frozen_balance" description:"冻结余额(支付中/退款中,可用余额 = balance - frozen_balance)"` // 冻结余额(支付中/退款中,可用余额 = balance - frozen_balance)
|
||||
WarningThreshold float64 `json:"warning_threshold" orm:"warning_threshold" description:"余额预警线(低于此值触发通知)"` // 余额预警线(低于此值触发通知)
|
||||
Currency string `json:"currency" orm:"currency" description:"货币(USD)"` // 货币(USD)
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"` // 更新时间
|
||||
CumulativeRecharge float64 `json:"cumulative_recharge" orm:"cumulative_recharge" description:"累计充值总额(USD)"` // 累计充值总额(USD)
|
||||
}
|
||||
@@ -18,7 +18,7 @@ type ChnChannelKeys struct {
|
||||
LastUsedAt *gtime.Time `json:"last_used_at" orm:"last_used_at" description:"最后使用时间"` // 最后使用时间
|
||||
LastError string `json:"last_error" orm:"last_error" description:"最后一次错误信息"` // 最后一次错误信息
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"` // 更新时间
|
||||
KeyType string `json:"key_type" orm:"key_type" description:"Key 类型:apikey(传统静态密钥)/ oauth(OAuth 令牌)"` // Key 类型:apikey(传统静态密钥)/ oauth(OAuth 令牌)
|
||||
TokenExpiresAt *gtime.Time `json:"token_expires_at" orm:"token_expires_at" description:"OAuth access_token 过期时间(仅 key_type=oauth 时有值)"` // OAuth access_token 过期时间(仅 key_type=oauth 时有值)
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"` // 更新时间
|
||||
}
|
||||
@@ -19,10 +19,10 @@ type MdlModels struct {
|
||||
MaxOutputTokens int `json:"max_output_tokens" orm:"max_output_tokens" description:"最大输出 token 数"` // 最大输出 token 数
|
||||
Description string `json:"description" orm:"description" description:"模型描述"` // 模型描述
|
||||
Tags []string `json:"tags" orm:"tags" description:"标签(如 reasoning、vision、function_calling)"` // 标签(如 reasoning、vision、function_calling)
|
||||
Capabilities string `json:"capabilities" orm:"capabilities" description:"模型能力特性(如 vision、function_calling、reasoning 等)"` // 模型能力特性(如 vision、function_calling、reasoning 等)
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"更新时间"` // 更新时间
|
||||
DeprecatedAt *gtime.Time `json:"deprecated_at" orm:"deprecated_at" description:"标记弃用的时间(NULL表示未弃用)"` // 标记弃用的时间(NULL表示未弃用)
|
||||
SunsetDate *gtime.Time `json:"sunset_date" orm:"sunset_date" description:"计划下线日期(到达后返回410 Gone,NULL表示未设置)"` // 计划下线日期(到达后返回410 Gone,NULL表示未设置)
|
||||
ReplacementModel string `json:"replacement_model" orm:"replacement_model" description:"推荐替代模型名(NULL表示无替代)"` // 推荐替代模型名(NULL表示无替代)
|
||||
Capabilities string `json:"capabilities" orm:"capabilities" description:"模型能力特性(如 vision、function_calling、reasoning 等)"` // 模型能力特性(如 vision、function_calling、reasoning 等)
|
||||
}
|
||||
@@ -20,6 +20,6 @@ type NtfMessages struct {
|
||||
IsRead int `json:"is_read" orm:"is_read" description:"是否已读:0=未读, 1=已读"` // 是否已读:0=未读, 1=已读
|
||||
IsBroadcast int `json:"is_broadcast" orm:"is_broadcast" description:"是否广播消息:0=个人消息, 1=广播消息"` // 是否广播消息:0=个人消息, 1=广播消息
|
||||
Metadata string `json:"metadata" orm:"metadata" description:"附加元数据(JSONB,如关联资源ID、跳转链接等)"` // 附加元数据(JSONB,如关联资源ID、跳转链接等)
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
TargetRoles string `json:"target_roles" orm:"target_roles" description:"目标角色(NULL=全部角色,逗号分隔如 owner,admin 表示仅限这些角色)"` // 目标角色(NULL=全部角色,逗号分隔如 owner,admin 表示仅限这些角色)
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"创建时间"` // 创建时间
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// =================================================================================
|
||||
// Code generated and maintained by GoFrame CLI tool. DO NOT EDIT.
|
||||
// =================================================================================
|
||||
|
||||
package entity
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// TntTenantLevelConfigs is the golang structure for table tnt_tenant_level_configs.
|
||||
type TntTenantLevelConfigs struct {
|
||||
Id int64 `json:"id" orm:"id" description:""` //
|
||||
Level int `json:"level" orm:"level" description:"等级号(1, 2, 3...)"` // 等级号(1, 2, 3...)
|
||||
Name string `json:"name" orm:"name" description:"等级名称"` // 等级名称
|
||||
CumulativeRechargeThreshold float64 `json:"cumulative_recharge_threshold" orm:"cumulative_recharge_threshold" description:"累计充值阈值(USD),达到此值自动升级"` // 累计充值阈值(USD),达到此值自动升级
|
||||
MaxMembers int `json:"max_members" orm:"max_members" description:"该等级最大成员数"` // 该等级最大成员数
|
||||
MaxConcurrency int `json:"max_concurrency" orm:"max_concurrency" description:"该等级最大并发数,0=无限"` // 该等级最大并发数,0=无限
|
||||
PriceMultiplier float64 `json:"price_multiplier" orm:"price_multiplier" description:"价格乘数(折扣,如 0.9=九折)"` // 价格乘数(折扣,如 0.9=九折)
|
||||
SortOrder int `json:"sort_order" orm:"sort_order" description:"排序权重"` // 排序权重
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:""` //
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:""` //
|
||||
}
|
||||
@@ -27,4 +27,5 @@ type TntTenants struct {
|
||||
DataRemovalAt *gtime.Time `json:"data_removal_at" orm:"data_removal_at" description:"数据清除时间(冻结 30 天后)"` // 数据清除时间(冻结 30 天后)
|
||||
MaxConcurrency int `json:"max_concurrency" orm:"max_concurrency" description:"租户总并发上限(0表示不限制)"` // 租户总并发上限(0表示不限制)
|
||||
DefaultChannelScope string `json:"default_channel_scope" orm:"default_channel_scope" description:"默认渠道范围(NULL或[]表示全部可用,否则为channel_id数组)"` // 默认渠道范围(NULL或[]表示全部可用,否则为channel_id数组)
|
||||
Level int `json:"level" orm:"level" description:"当前等级(对应 tnt_tenant_level_configs.level)"` // 当前等级(对应 tnt_tenant_level_configs.level)
|
||||
}
|
||||
@@ -10,29 +10,29 @@ import (
|
||||
|
||||
// TskModelTasks is the golang structure for table tsk_model_tasks.
|
||||
type TskModelTasks struct {
|
||||
Id int64 `json:"id" orm:"id" description:""` //
|
||||
PublicTaskId string `json:"public_task_id" orm:"public_task_id" description:""` //
|
||||
Platform string `json:"platform" orm:"platform" description:""` //
|
||||
Action string `json:"action" orm:"action" description:""` //
|
||||
Status string `json:"status" orm:"status" description:""` //
|
||||
Progress string `json:"progress" orm:"progress" description:""` //
|
||||
FailReason string `json:"fail_reason" orm:"fail_reason" description:""` //
|
||||
TenantId int64 `json:"tenant_id" orm:"tenant_id" description:""` //
|
||||
UserId int64 `json:"user_id" orm:"user_id" description:""` //
|
||||
ApiKeyId int64 `json:"api_key_id" orm:"api_key_id" description:""` //
|
||||
ChannelId int64 `json:"channel_id" orm:"channel_id" description:""` //
|
||||
ModelName string `json:"model_name" orm:"model_name" description:""` //
|
||||
UpstreamModel string `json:"upstream_model" orm:"upstream_model" description:""` //
|
||||
PreDeductAmount float64 `json:"pre_deduct_amount" orm:"pre_deduct_amount" description:""` //
|
||||
ActualCost float64 `json:"actual_cost" orm:"actual_cost" description:""` //
|
||||
BillingSettled bool `json:"billing_settled" orm:"billing_settled" description:""` //
|
||||
ResultUrl string `json:"result_url" orm:"result_url" description:""` //
|
||||
Data string `json:"data" orm:"data" description:""` //
|
||||
PrivateData string `json:"private_data" orm:"private_data" description:""` //
|
||||
SubmitTime *gtime.Time `json:"submit_time" orm:"submit_time" description:""` //
|
||||
StartTime *gtime.Time `json:"start_time" orm:"start_time" description:""` //
|
||||
FinishTime *gtime.Time `json:"finish_time" orm:"finish_time" description:""` //
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:""` //
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:""` //
|
||||
RequestId string `json:"request_id" orm:"request_id" description:"任务提交时的原始请求 ID(req_xxxxx),关联 aud_request_logs.request_id"` // 任务提交时的原始请求 ID(req_xxxxx),关联 aud_request_logs.request_id
|
||||
Id int64 `json:"id" orm:"id" description:"主键ID"` // 主键ID
|
||||
PublicTaskId string `json:"public_task_id" orm:"public_task_id" description:"对外公开的任务ID(task_xxxxx 格式),用于 API 响应和客户端查询"` // 对外公开的任务ID(task_xxxxx 格式),用于 API 响应和客户端查询
|
||||
Platform string `json:"platform" orm:"platform" description:"任务所属平台:sora、kling、suno、midjourney、volcengine"` // 任务所属平台:sora、kling、suno、midjourney、volcengine
|
||||
Action string `json:"action" orm:"action" description:"任务动作类型。通用:generate;Suno:music、lyrics;"` // 任务动作类型。通用:generate;Suno:music、lyrics;
|
||||
Status string `json:"status" orm:"status" description:"任务状态:NOT_START-未开始、SUBMITTED-已提交、IN_PROGRESS-进行中、SUCCESS-成功、FAILURE-失败"` // 任务状态:NOT_START-未开始、SUBMITTED-已提交、IN_PROGRESS-进行中、SUCCESS-成功、FAILURE-失败
|
||||
Progress string `json:"progress" orm:"progress" description:"任务进度,字符串格式百分比(如 0%、50%、100%)"` // 任务进度,字符串格式百分比(如 0%、50%、100%)
|
||||
FailReason string `json:"fail_reason" orm:"fail_reason" description:"任务失败原因文本"` // 任务失败原因文本
|
||||
TenantId int64 `json:"tenant_id" orm:"tenant_id" description:"发起任务的租户ID"` // 发起任务的租户ID
|
||||
UserId int64 `json:"user_id" orm:"user_id" description:"发起任务的租户用户ID"` // 发起任务的租户用户ID
|
||||
ApiKeyId int64 `json:"api_key_id" orm:"api_key_id" description:"调用方使用的 API Key ID"` // 调用方使用的 API Key ID
|
||||
ChannelId int64 `json:"channel_id" orm:"channel_id" description:"转发请求的渠道ID"` // 转发请求的渠道ID
|
||||
ModelName string `json:"model_name" orm:"model_name" description:"用户请求的模型名称(如 sora-1.0-turbo、midjourney 等)"` // 用户请求的模型名称(如 sora-1.0-turbo、midjourney 等)
|
||||
UpstreamModel string `json:"upstream_model" orm:"upstream_model" description:"上游供应商实际使用的模型名称,可能与请求模型不同"` // 上游供应商实际使用的模型名称,可能与请求模型不同
|
||||
PreDeductAmount float64 `json:"pre_deduct_amount" orm:"pre_deduct_amount" description:"提交任务时的预扣金额(USD),任务完成后根据实际用量结算差额"` // 提交任务时的预扣金额(USD),任务完成后根据实际用量结算差额
|
||||
ActualCost float64 `json:"actual_cost" orm:"actual_cost" description:"任务实际消费金额(USD),成功时按此结算,失败时退还预扣金额"` // 任务实际消费金额(USD),成功时按此结算,失败时退还预扣金额
|
||||
BillingSettled bool `json:"billing_settled" orm:"billing_settled" description:"计费是否已结算(true 表示已完成预扣与实际金额的多退少补)"` // 计费是否已结算(true 表示已完成预扣与实际金额的多退少补)
|
||||
ResultUrl string `json:"result_url" orm:"result_url" description:"任务完成后的结果资源 URL(如生成的视频/图片/音频的下载地址)"` // 任务完成后的结果资源 URL(如生成的视频/图片/音频的下载地址)
|
||||
Data string `json:"data" orm:"data" description:"上游供应商返回的完整响应数据(JSONB),可返回给用户查看"` // 上游供应商返回的完整响应数据(JSONB),可返回给用户查看
|
||||
PrivateData string `json:"private_data" orm:"private_data" description:"内部私有数据(JSONB),含 upstream_task_id 等敏感字段,用于轮询上游状态,不返回给用户"` // 内部私有数据(JSONB),含 upstream_task_id 等敏感字段,用于轮询上游状态,不返回给用户
|
||||
SubmitTime *gtime.Time `json:"submit_time" orm:"submit_time" description:"任务提交到上游供应商的时间"` // 任务提交到上游供应商的时间
|
||||
StartTime *gtime.Time `json:"start_time" orm:"start_time" description:"上游供应商开始执行任务的时间"` // 上游供应商开始执行任务的时间
|
||||
FinishTime *gtime.Time `json:"finish_time" orm:"finish_time" description:"任务完成(成功或失败)的时间"` // 任务完成(成功或失败)的时间
|
||||
CreatedAt *gtime.Time `json:"created_at" orm:"created_at" description:"记录创建时间"` // 记录创建时间
|
||||
UpdatedAt *gtime.Time `json:"updated_at" orm:"updated_at" description:"记录更新时间"` // 记录更新时间
|
||||
RequestId string `json:"request_id" orm:"request_id" description:"任务提交时的原始请求 ID(req_xxxxx),关联 aud_request_logs.request_id"` // 任务提交时的原始请求 ID(req_xxxxx),关联 aud_request_logs.request_id
|
||||
}
|
||||
@@ -363,6 +363,10 @@ type (
|
||||
UpdateTenantChannelScope(ctx context.Context, req *v1.TenantChannelScopeUpdateReq) (*v1.TenantChannelScopeUpdateRes, error)
|
||||
// ExportTenants exports tenant list to CSV or Excel.
|
||||
ExportTenants(ctx context.Context, req *v1.TenantExportReq) (*v1.TenantExportRes, error)
|
||||
ListTenantLevelConfigs(ctx context.Context, _ *v1.TenantLevelConfigListReq) (*v1.TenantLevelConfigListRes, error)
|
||||
CreateTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigCreateReq) (*v1.TenantLevelConfigCreateRes, error)
|
||||
UpdateTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigUpdateReq) (*v1.TenantLevelConfigUpdateRes, error)
|
||||
DeleteTenantLevelConfig(ctx context.Context, req *v1.TenantLevelConfigDeleteReq) (*v1.TenantLevelConfigDeleteRes, error)
|
||||
// ListTenantModels 列出租户已分配的模型
|
||||
ListTenantModels(ctx context.Context, req *v1.TenantModelListReq) (*v1.TenantModelListRes, error)
|
||||
// BatchAssignModels 批量分配模型给租户
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
-- +goose Up
|
||||
-- 租户等级系统:基于累计充值的自动等级体系
|
||||
|
||||
-- 1. 新建等级配置表
|
||||
CREATE TABLE tnt_tenant_level_configs (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
level INT NOT NULL,
|
||||
name VARCHAR(32) NOT NULL,
|
||||
cumulative_recharge_threshold NUMERIC(20,10) NOT NULL,
|
||||
max_members INT NOT NULL DEFAULT 5,
|
||||
max_concurrency INT NOT NULL DEFAULT 10,
|
||||
price_multiplier NUMERIC(5,4) NOT NULL DEFAULT 1.0000,
|
||||
sort_order INT NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||||
updated_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
||||
CONSTRAINT uk_tnt_tenant_level_configs_level UNIQUE (level)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE tnt_tenant_level_configs IS '租户等级配置表';
|
||||
COMMENT ON COLUMN tnt_tenant_level_configs.level IS '等级号(1, 2, 3...)';
|
||||
COMMENT ON COLUMN tnt_tenant_level_configs.name IS '等级名称';
|
||||
COMMENT ON COLUMN tnt_tenant_level_configs.cumulative_recharge_threshold IS '累计充值阈值(USD),达到此值自动升级';
|
||||
COMMENT ON COLUMN tnt_tenant_level_configs.max_members IS '该等级最大成员数';
|
||||
COMMENT ON COLUMN tnt_tenant_level_configs.max_concurrency IS '该等级最大并发数,0=无限';
|
||||
COMMENT ON COLUMN tnt_tenant_level_configs.price_multiplier IS '价格乘数(折扣,如 0.9=九折)';
|
||||
COMMENT ON COLUMN tnt_tenant_level_configs.sort_order IS '排序权重';
|
||||
|
||||
-- 2. 插入默认等级数据
|
||||
INSERT INTO tnt_tenant_level_configs (level, name, cumulative_recharge_threshold, max_members, max_concurrency, price_multiplier, sort_order) VALUES
|
||||
(1, 'LV1', 0, 5, 10, 1.0000, 1),
|
||||
(2, 'LV2', 100, 10, 20, 0.9500, 2),
|
||||
(3, 'LV3', 500, 20, 50, 0.9000, 3),
|
||||
(4, 'LV4', 2000, 50, 100, 0.8500, 4),
|
||||
(5, 'LV5', 10000, 0, 0, 0.8000, 5);
|
||||
|
||||
-- 3. bil_wallets 添加累计充值字段
|
||||
ALTER TABLE bil_wallets ADD COLUMN cumulative_recharge NUMERIC(20,10) DEFAULT 0 NOT NULL;
|
||||
COMMENT ON COLUMN bil_wallets.cumulative_recharge IS '累计充值总额(USD)';
|
||||
|
||||
-- 4. tnt_tenants 添加等级字段
|
||||
ALTER TABLE tnt_tenants ADD COLUMN level INT DEFAULT 1 NOT NULL;
|
||||
COMMENT ON COLUMN tnt_tenants.level IS '当前等级(对应 tnt_tenant_level_configs.level)';
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE tnt_tenants DROP COLUMN IF EXISTS level;
|
||||
ALTER TABLE bil_wallets DROP COLUMN IF EXISTS cumulative_recharge;
|
||||
DROP TABLE IF EXISTS tnt_tenant_level_configs;
|
||||
@@ -150,6 +150,12 @@ const adminRoutes: RouteRecordRaw[] = [
|
||||
component: () => import('@/views/tenant/TenantDetailPage.vue'),
|
||||
meta: { title: '租户详情' },
|
||||
},
|
||||
{
|
||||
path: 'tenant-levels',
|
||||
name: 'AdminTenantLevels',
|
||||
component: () => import('@/views/tenant/TenantLevelsPage.vue'),
|
||||
meta: { title: '租户级别' },
|
||||
},
|
||||
{
|
||||
path: 'members',
|
||||
name: 'AdminMembers',
|
||||
|
||||
@@ -163,6 +163,7 @@ const menuGroups = [
|
||||
icon: IconUserGroup,
|
||||
items: [
|
||||
{ name: 'AdminTenants', label: '租户列表', icon: IconHome },
|
||||
{ name: 'AdminTenantLevels', label: '租户级别', icon: IconLayers },
|
||||
{ name: 'AdminMembers', label: '成员列表', icon: IconUser },
|
||||
{ name: 'AdminUsageLogs', label: '用量日志', icon: IconFile },
|
||||
],
|
||||
|
||||
@@ -63,6 +63,7 @@ function startEdit() {
|
||||
editForm.name = detail.value.name || ''
|
||||
editForm.max_members = detail.value.max_members || null
|
||||
editForm.max_concurrency = detail.value.max_concurrency || null
|
||||
editForm.level = detail.value.level || null
|
||||
}
|
||||
|
||||
async function saveEdit() {
|
||||
@@ -497,6 +498,9 @@ onMounted(() => {
|
||||
<AFormItem label="并发上限">
|
||||
<AInputNumber v-model="editForm.max_concurrency" :min="0" placeholder="0=不限" style="width: 120px" />
|
||||
</AFormItem>
|
||||
<AFormItem label="等级">
|
||||
<AInputNumber v-model="editForm.level" :min="1" :max="99" placeholder="等级号" style="width: 120px" />
|
||||
</AFormItem>
|
||||
<AFormItem>
|
||||
<AButton type="primary" :loading="editLoading" @click="saveEdit">保存</AButton>
|
||||
</AFormItem>
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted, h } from 'vue'
|
||||
import {
|
||||
Tag, Button, Space, Message, Modal,
|
||||
} from '@arco-design/web-vue'
|
||||
import type { TableColumnData, FormInstance } from '@arco-design/web-vue'
|
||||
import PageHeader from '@/components/PageHeader.vue'
|
||||
import request from '@/utils/request'
|
||||
|
||||
const loading = ref(false)
|
||||
const levels = ref<any[]>([])
|
||||
|
||||
const columns: TableColumnData[] = [
|
||||
{ title: '等级号', dataIndex: 'level', width: 80 },
|
||||
{ title: '名称', dataIndex: 'name', width: 120 },
|
||||
{
|
||||
title: '累计充值阈值(USD)', dataIndex: 'cumulative_recharge_threshold', width: 180,
|
||||
render({ record }) { return `$${Number(record.cumulative_recharge_threshold).toFixed(2)}` },
|
||||
},
|
||||
{
|
||||
title: '最大成员数', dataIndex: 'max_members', width: 100,
|
||||
render({ record }) { return record.max_members === 0 ? '不限' : record.max_members },
|
||||
},
|
||||
{
|
||||
title: '最大并发数', dataIndex: 'max_concurrency', width: 100,
|
||||
render({ record }) { return record.max_concurrency === 0 ? '不限' : record.max_concurrency },
|
||||
},
|
||||
{
|
||||
title: '价格乘数', dataIndex: 'price_multiplier', width: 100,
|
||||
render({ record }) {
|
||||
const v = Number(record.price_multiplier)
|
||||
if (v === 1) return '-'
|
||||
return `${(v * 100).toFixed(1)}%`
|
||||
},
|
||||
},
|
||||
{ title: '排序', dataIndex: 'sort_order', width: 70 },
|
||||
{
|
||||
title: '操作', dataIndex: 'actions', width: 150, fixed: 'right',
|
||||
render({ record }) {
|
||||
return h(Space, { size: 4 }, () => [
|
||||
h(Button, { size: 'small', type: 'primary', onClick: () => openEditModal(record) }, () => '编辑'),
|
||||
record.level === 1 ? null : h(Button, { size: 'small', status: 'danger', onClick: () => deleteLevel(record) }, () => '删除'),
|
||||
])
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
async function fetchLevels() {
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await request.get('/admin/tenant-level-configs')
|
||||
levels.value = res.data?.data?.list || []
|
||||
} catch { /* interceptor handles error toast */ } finally { loading.value = false }
|
||||
}
|
||||
|
||||
const showModal = ref(false)
|
||||
const editingId = ref<number | null>(null)
|
||||
const formRef = ref<FormInstance | null>(null)
|
||||
const formLoading = ref(false)
|
||||
const formData = reactive({
|
||||
level: 1,
|
||||
name: '',
|
||||
cumulative_recharge_threshold: 0,
|
||||
max_members: 5,
|
||||
max_concurrency: 10,
|
||||
price_multiplier: 1.0,
|
||||
sort_order: 0,
|
||||
})
|
||||
|
||||
function resetForm() {
|
||||
formData.level = 1
|
||||
formData.name = ''
|
||||
formData.cumulative_recharge_threshold = 0
|
||||
formData.max_members = 5
|
||||
formData.max_concurrency = 10
|
||||
formData.price_multiplier = 1.0
|
||||
formData.sort_order = 0
|
||||
}
|
||||
|
||||
function openCreateModal() {
|
||||
editingId.value = null
|
||||
resetForm()
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
function openEditModal(row: any) {
|
||||
editingId.value = row.id
|
||||
formData.level = row.level
|
||||
formData.name = row.name
|
||||
formData.cumulative_recharge_threshold = row.cumulative_recharge_threshold
|
||||
formData.max_members = row.max_members
|
||||
formData.max_concurrency = row.max_concurrency
|
||||
formData.price_multiplier = row.price_multiplier
|
||||
formData.sort_order = row.sort_order
|
||||
showModal.value = true
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
formLoading.value = true
|
||||
try {
|
||||
if (editingId.value) {
|
||||
await request.put(`/admin/tenant-level-configs/${editingId.value}`, {
|
||||
update: {
|
||||
name: formData.name,
|
||||
cumulative_recharge_threshold: formData.cumulative_recharge_threshold,
|
||||
max_members: formData.max_members,
|
||||
max_concurrency: formData.max_concurrency,
|
||||
price_multiplier: formData.price_multiplier,
|
||||
sort_order: formData.sort_order,
|
||||
},
|
||||
})
|
||||
Message.success('更新成功')
|
||||
} else {
|
||||
await request.post('/admin/tenant-level-configs', formData)
|
||||
Message.success('创建成功')
|
||||
}
|
||||
showModal.value = false
|
||||
fetchLevels()
|
||||
} catch { /* interceptor handles error toast */ } finally { formLoading.value = false }
|
||||
}
|
||||
|
||||
function deleteLevel(row: any) {
|
||||
Modal.warning({
|
||||
title: '确认删除',
|
||||
content: `确定要删除等级「${row.name}」吗?`,
|
||||
okText: '确认', cancelText: '取消',
|
||||
onOk: async () => {
|
||||
try { await request.delete(`/admin/tenant-level-configs/${row.id}`); Message.success('已删除'); fetchLevels() }
|
||||
catch { /* interceptor handles error toast */ }
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(fetchLevels)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<PageHeader title="租户级别" description="配置租户等级体系,基于累计充值自动升级">
|
||||
<template #actions>
|
||||
<AButton type="primary" @click="openCreateModal">创建等级</AButton>
|
||||
</template>
|
||||
</PageHeader>
|
||||
|
||||
<ACard :bordered="false">
|
||||
<template #title>等级配置列表</template>
|
||||
<ATable :columns="columns" :data="levels" :loading="loading" row-key="id" :scroll="{ x: 900 }" :pagination="false" />
|
||||
</ACard>
|
||||
|
||||
<ADrawer v-model:visible="showModal" :title="editingId ? '编辑等级' : '创建等级'" :width="520" :mask-closable="false" :footer="true" unmount-on-close>
|
||||
<AForm ref="formRef" :model="formData" :auto-label-width="true" layout="vertical">
|
||||
<AFormItem label="等级号" required>
|
||||
<AInputNumber v-model="formData.level" :min="1" :max="99" class="w-full" :disabled="!!editingId" placeholder="1, 2, 3..." />
|
||||
</AFormItem>
|
||||
<AFormItem label="等级名称" required>
|
||||
<AInput v-model="formData.name" placeholder="如:LV2 白银用户" />
|
||||
</AFormItem>
|
||||
<AFormItem label="累计充值阈值(USD)" required>
|
||||
<AInputNumber v-model="formData.cumulative_recharge_threshold" :min="0" :precision="2" class="w-full" placeholder="达到此金额自动升级" />
|
||||
</AFormItem>
|
||||
<AFormItem label="最大成员数">
|
||||
<AInputNumber v-model="formData.max_members" :min="0" class="w-full" placeholder="0 = 不限制" />
|
||||
</AFormItem>
|
||||
<AFormItem label="最大并发数">
|
||||
<AInputNumber v-model="formData.max_concurrency" :min="0" class="w-full" placeholder="0 = 不限制" />
|
||||
</AFormItem>
|
||||
<AFormItem label="价格乘数">
|
||||
<AInputNumber v-model="formData.price_multiplier" :min="0.01" :max="2" :step="0.05" :precision="4" class="w-full" placeholder="1.0 = 原价,0.9 = 九折" />
|
||||
</AFormItem>
|
||||
<AFormItem label="排序权重">
|
||||
<AInputNumber v-model="formData.sort_order" :min="0" class="w-full" />
|
||||
</AFormItem>
|
||||
</AForm>
|
||||
<template #footer>
|
||||
<Space>
|
||||
<AButton @click="showModal = false">取消</AButton>
|
||||
<AButton type="primary" :loading="formLoading" @click="handleSubmit">确认</AButton>
|
||||
</Space>
|
||||
</template>
|
||||
</ADrawer>
|
||||
</div>
|
||||
</template>
|
||||
@@ -214,6 +214,14 @@ onMounted(() => {
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Level -->
|
||||
<div>
|
||||
<p class="input-label">等级</p>
|
||||
<span class="badge badge-primary">
|
||||
{{ orgInfo.level_name || 'LV' + orgInfo.level }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Member Count -->
|
||||
<div>
|
||||
<p class="input-label">成员数</p>
|
||||
|
||||
Reference in new issue
Block a user