refactor(pricing): entity/DTO 换 subject_type+subject_id(含 subject_dto 枚举 DTO)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-28 17:44:22 +08:00
parent 3e0b7bafc3
commit 9b1aa1288d
5 changed files with 337 additions and 0 deletions
+138
View File
@@ -0,0 +1,138 @@
package dto
import (
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
pricingConsts "shop-user-trade/consts/pricing"
)
// ====================== 建单(不动钱) ======================
// OpenOrderReq 建单请求(执行开始调用,只建单不动钱),幂等键 subjectType+subjectId+bizOrderNo
type OpenOrderReq struct {
g.Meta `path:"/open_order" method:"post" tags:"计价扣费" summary:"建单" dc:"执行开始建计费单(不动钱),幂等键 subjectType+subjectId+bizOrderNo"`
UserId int64 `json:"userId" v:"required|min:1" dc:"用户ID"`
SubjectType string `json:"subjectType" v:"required|in:workflow,model,business" dc:"计价对象类型"`
SubjectID string `json:"subjectId" v:"required" dc:"计价对象ID"`
ChargeMode string `json:"chargeMode" dc:"计费方式:workflow 必填(rules 已配三模式之一);model/business 可空(默认取 unit/per_period"`
BizOrderNo string `json:"bizOrderNo" v:"required" dc:"业务单据号(幂等键,如工作流 execId)"`
}
// ====================== 结算(成功:按最终产出) ======================
// SettleReq 结算请求(按实际用量计价实收,余额不足允许为负)
type SettleReq struct {
g.Meta `path:"/settle" method:"post" tags:"计价扣费" summary:"结算" dc:"按实际用量计价实收(允许负余额),幂等"`
OrderId int64 `json:"orderId" dc:"计费单ID(与 subjectType+subjectId+bizOrderNo 二选一)"`
SubjectType string `json:"subjectType" dc:"计价对象类型(orderId 为空时必填)"`
SubjectID string `json:"subjectId" dc:"计价对象IDorderId 为空时必填)"`
BizOrderNo string `json:"bizOrderNo" dc:"业务单据号(orderId 为空时必填)"`
Usage string `json:"usage" v:"required" dc:"用量JSON{durationSec,itemCount,tokensByModel,...}"`
}
// ====================== 取消(按已消耗用量结算) ======================
// CancelReq 取消请求(中途取消按已消耗用量计价扣费,非退款)
type CancelReq struct {
g.Meta `path:"/cancel" method:"post" tags:"计价扣费" summary:"取消" dc:"中途取消按已消耗用量计价实收(非退款),幂等"`
OrderId int64 `json:"orderId" dc:"计费单ID(与 subjectType+subjectId+bizOrderNo 二选一)"`
SubjectType string `json:"subjectType" dc:"计价对象类型(orderId 为空时必填)"`
SubjectID string `json:"subjectId" dc:"计价对象IDorderId 为空时必填)"`
BizOrderNo string `json:"bizOrderNo" dc:"业务单据号(orderId 为空时必填)"`
Usage string `json:"usage" v:"required" dc:"已消耗用量JSON"`
}
// ====================== 失败(不扣费) ======================
// FailReq 失败请求(执行失败/无产出,不扣费)
type FailReq struct {
g.Meta `path:"/fail" method:"post" tags:"计价扣费" summary:"失败处理" dc:"执行失败/无产出,不扣费,幂等"`
OrderId int64 `json:"orderId" dc:"计费单ID(与 subjectType+subjectId+bizOrderNo 二选一)"`
SubjectType string `json:"subjectType" dc:"计价对象类型(orderId 为空时必填)"`
SubjectID string `json:"subjectId" dc:"计价对象IDorderId 为空时必填)"`
BizOrderNo string `json:"bizOrderNo" dc:"业务单据号(orderId 为空时必填)"`
Reason string `json:"reason" dc:"失败原因"`
}
// ====================== 查询 ======================
// GetOrderReq 查询计费单请求
type GetOrderReq struct {
g.Meta `path:"/get_order" method:"get" tags:"计价扣费" summary:"查询计费单" dc:"按ID或 subjectType+subjectId+bizOrderNo 查询"`
OrderId int64 `json:"orderId" dc:"计费单ID(与 subjectType+subjectId+bizOrderNo 二选一)"`
SubjectType string `json:"subjectType" dc:"计价对象类型(orderId 为空时必填)"`
SubjectID string `json:"subjectId" dc:"计价对象IDorderId 为空时必填)"`
BizOrderNo string `json:"bizOrderNo" dc:"业务单据号(orderId 为空时必填)"`
}
// ListOrdersReq 分页查询计费单请求
type ListOrdersReq struct {
g.Meta `path:"/orders" method:"get" tags:"计价扣费" summary:"分页查询计费单" dc:"按用户分页查询计费单"`
UserId int64 `json:"userId" v:"required|min:1" dc:"用户ID"`
Page int `json:"page" v:"required|min:1" dc:"页码"`
PageSize int `json:"pageSize" v:"required|min:1|max:100" dc:"每页大小"`
}
// ChargeOrderList 计费单列表
type ChargeOrderList struct {
Orders []ChargeOrderInfo `json:"orders"`
Total int `json:"total"`
}
// ChargeOrderInfo 计费单信息
type ChargeOrderInfo struct {
ID int64 `json:"id"`
SubjectType string `json:"subjectType"`
SubjectID string `json:"subjectId"`
BizOrderNo string `json:"bizOrderNo"`
UserId int64 `json:"userId"`
ChargeMode string `json:"chargeMode"`
Status int `json:"status"` // 1已建单 2已结算 3已失败
ActualAmount float64 `json:"actualAmount"`
Usage string `json:"usage"`
RuleSnapshot string `json:"ruleSnapshot"`
SettleTime string `json:"settleTime"`
CreatedAt string `json:"createdAt"`
}
// ====================== DAO 入参(统一吃 DTO,同 model-gateway ======================
// GetChargeOrderReq DAO 查询计费单:Id 与 SubjectType+SubjectID+BizOrderNo 二选一
type GetChargeOrderReq struct {
Id int64 `json:"id"`
SubjectType string `json:"subjectType"`
SubjectID string `json:"subjectId"`
BizOrderNo string `json:"bizOrderNo"`
}
// CreateChargeOrderReq DAO 创建计费单
type CreateChargeOrderReq struct {
SubjectType pricingConsts.SubjectType `json:"subjectType"`
SubjectID string `json:"subjectId"`
BizOrderNo string `json:"bizOrderNo"`
UserId int64 `json:"userId"`
ChargeMode pricingConsts.ChargeMode `json:"chargeMode"`
Status pricingConsts.ChargeOrderStatus `json:"status"`
RuleSnapshot string `json:"ruleSnapshot"`
}
// SettleChargeOrderReq DAO 结算迁移(CREATED→SETTLED,条件更新幂等)
type SettleChargeOrderReq struct {
Id int64 `json:"id"`
ActualAmount float64 `json:"actualAmount"`
Usage string `json:"usage"`
RuleSnapshot string `json:"ruleSnapshot"`
SettleTime *gtime.Time `json:"settleTime"`
}
// FailChargeOrderReq DAO 失败迁移(CREATED→FAILED,条件更新幂等)
type FailChargeOrderReq struct {
Id int64 `json:"id"`
}
+63
View File
@@ -0,0 +1,63 @@
package dto
import (
"github.com/gogf/gf/v2/frame/g"
)
// ====================== 配置管理 ======================
// SaveConfigReq 新增/更新计价配置请求(费率全 DB 配置,改价/加档只改 rules)
type SaveConfigReq struct {
g.Meta `path:"/config/save" method:"post" tags:"计价配置" summary:"保存计价配置" dc:"新增或更新计价配置(subject_type+subject_id 唯一)"`
Id int64 `json:"id" dc:"配置ID>0 更新,0 新增)"`
SubjectType string `json:"subjectType" v:"required|in:workflow,model,business" dc:"计价对象类型"`
SubjectID string `json:"subjectId" v:"required" dc:"计价对象ID"`
Rules string `json:"rules" v:"required" dc:"费率JSON(见设计§4"`
MinBalance float64 `json:"minBalance" dc:"门禁:调用前可用余额须>=该值(元),0=不校验"`
Currency string `json:"currency" dc:"货币类型,默认CNY"`
Enabled int `json:"enabled" dc:"1启用 0停用"`
}
// SaveConfigData 保存计价配置数据
type SaveConfigData struct {
Id int64 `json:"id"`
}
// GetConfigReq 查询计价配置请求
type GetConfigReq struct {
g.Meta `path:"/config/get" method:"get" tags:"计价配置" summary:"查询计价配置" dc:"按 subjectType+subjectId 查询计价配置"`
SubjectType string `json:"subjectType" v:"required" dc:"计价对象类型"`
SubjectID string `json:"subjectId" v:"required" dc:"计价对象ID"`
}
// ListConfigsReq 分页查询计价配置请求
type ListConfigsReq struct {
g.Meta `path:"/config/list" method:"get" tags:"计价配置" summary:"分页查询计价配置" dc:"可按 subjectType/enabled 过滤"`
SubjectType string `json:"subjectType" dc:"计价对象类型"`
Enabled int `json:"enabled" dc:"1启用 0停用,0不过滤"`
Page int `json:"page" v:"required|min:1" dc:"页码"`
PageSize int `json:"pageSize" v:"required|min:1|max:100" dc:"每页大小"`
}
// PricingConfigList 计价配置列表
type PricingConfigList struct {
Configs []PricingConfigInfo `json:"configs"`
Total int `json:"total"`
}
// PricingConfigInfo 计价配置信息
type PricingConfigInfo struct {
ID int64 `json:"id"`
SubjectType string `json:"subjectType"`
SubjectID string `json:"subjectId"`
Rules string `json:"rules"`
MinBalance float64 `json:"minBalance"`
Currency string `json:"currency"`
Enabled int `json:"enabled"`
Version int64 `json:"version"`
CreatedAt string `json:"createdAt"`
UpdatedAt string `json:"updatedAt"`
}
+27
View File
@@ -0,0 +1,27 @@
package dto
import (
"github.com/gogf/gf/v2/frame/g"
)
// SubjectsReq 计价对象枚举请求
type SubjectsReq struct {
g.Meta `path:"/subjects" method:"get" tags:"计价配置" summary:"计价对象枚举" dc:"workflow + 实时model-gateway系统模型 + 固定业务模块"`
}
// SubjectInfo 计价对象枚举项
type SubjectInfo struct {
SubjectType string `json:"subjectType"`
SubjectID string `json:"subjectId"`
Name string `json:"name"`
ModelType string `json:"modelType,omitempty"` // 模型类型名(管理端分组展示,如 推理/视频)
ModelTypeCode int `json:"modelTypeCode,omitempty"` // 模型类型编码:100推理/200图片/300音频/600视频
ChargeModes []string `json:"chargeModes,omitempty"` // workflow/business 用(可选的计费方式)
HasConfig bool `json:"hasConfig"` // 是否已配置价格
Enabled int `json:"enabled"` // 已配置时的启用状态
}
// SubjectListRes 计价对象枚举响应
type SubjectListRes struct {
Subjects []SubjectInfo `json:"subjects"`
}
+53
View File
@@ -0,0 +1,53 @@
package entity
import (
pricingConsts "shop-user-trade/consts/pricing"
"gitea.redpowerfuture.com/red-future/common/beans"
"github.com/gogf/gf/v2/os/gtime"
)
type chargeOrderCol struct {
beans.SQLBaseCol
SubjectType string
SubjectID string
BizOrderNo string
UserId string
ChargeMode string
Status string
ActualAmount string
Usage string
RuleSnapshot string
SettleTime string
}
var ChargeOrderCol = chargeOrderCol{
SQLBaseCol: beans.DefSQLBaseCol,
SubjectType: "subject_type",
SubjectID: "subject_id",
BizOrderNo: "biz_order_no",
UserId: "user_id",
ChargeMode: "charge_mode",
Status: "status",
ActualAmount: "actual_amount",
Usage: "usage",
RuleSnapshot: "rule_snapshot",
SettleTime: "settle_time",
}
// ChargeOrder 计费单实体:一次计价调用 = 一张计费单(幂等键 subject_type+subject_id+biz_order_no
// 状态机:CREATED(已建单,不动钱) → SETTLED(已结算) / FAILED(已失败,不扣费)
type ChargeOrder struct {
beans.SQLBaseDO `orm:",inherit"`
SubjectType pricingConsts.SubjectType `orm:"subject_type" json:"subjectType" description:"计价对象类型"`
SubjectID string `orm:"subject_id" json:"subjectId" description:"计价对象ID"`
BizOrderNo string `orm:"biz_order_no" json:"bizOrderNo" description:"业务单据号(幂等键,如工作流 execId)"`
UserId int64 `orm:"user_id" json:"userId,string" description:"扣费用户ID"`
ChargeMode pricingConsts.ChargeMode `orm:"charge_mode" json:"chargeMode" description:"计费方式(建单快照)"`
Status pricingConsts.ChargeOrderStatus `orm:"status" json:"status" description:"状态:1已建单 2已结算 3已失败"`
ActualAmount float64 `orm:"actual_amount" json:"actualAmount" description:"实收金额(元,保留2位小数)"`
Usage string `orm:"usage" json:"usage" description:"实际用量JSON"`
RuleSnapshot string `orm:"rule_snapshot" json:"ruleSnapshot" description:"结算用费率快照JSON(防改价影响在途单)"`
SettleTime *gtime.Time `orm:"settle_time" json:"settleTime" description:"结算时间"`
}
+56
View File
@@ -0,0 +1,56 @@
package entity
import (
"encoding/json"
pricingConsts "shop-user-trade/consts/pricing"
"gitea.redpowerfuture.com/red-future/common/beans"
)
type pricingConfigCol struct {
beans.SQLBaseCol
SubjectType string
SubjectID string
Rules string
MinBalance string
Currency string
Enabled string
Version string
}
var PricingConfigCol = pricingConfigCol{
SQLBaseCol: beans.DefSQLBaseCol,
SubjectType: "subject_type",
SubjectID: "subject_id",
Rules: "rules",
MinBalance: "min_balance",
Currency: "currency",
Enabled: "enabled",
Version: "version",
}
// PricingConfig 计价配置实体(费率全 DB 配置,改价/加档只改 rules,代码不写死金额)
type PricingConfig struct {
beans.SQLBaseDO `orm:",inherit"`
SubjectType pricingConsts.SubjectType `orm:"subject_type" json:"subjectType" description:"计价对象类型:workflow/model/business"`
SubjectID string `orm:"subject_id" json:"subjectId" description:"计价对象IDworkflow/模型ID/业务模块标识)"`
Rules string `orm:"rules" json:"rules" description:"费率JSON(见设计§4rules 自描述不再有 charge_mode 列)"`
MinBalance float64 `orm:"min_balance" json:"minBalance" description:"门禁:调用前可用余额须>=该值(元),0=不校验"`
Currency string `orm:"currency" json:"currency" description:"货币类型"`
Enabled int `orm:"enabled" json:"enabled" description:"1启用 0停用"`
Version int64 `orm:"version" json:"version" description:"乐观锁版本号"`
}
// Unit 从 rules 中解析模型计费单位(仅 subject_type=model 有效,其余返回空)。
// service 层 per_token 结算/建单解析计费方式时使用,避免重复解析 rules。
func (e *PricingConfig) Unit() pricingConsts.ChargeMode {
var cfg struct {
Unit pricingConsts.ChargeMode `json:"unit"`
}
if err := json.Unmarshal([]byte(e.Rules), &cfg); err != nil {
return ""
}
return cfg.Unit
}