refactor(pricing): DAO 按 (subject_type, subject_id) 键查询/幂等

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-28 17:49:03 +08:00
parent 9b1aa1288d
commit 2e8a3663a5
2 changed files with 193 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
package dao
import (
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
pricingConsts "shop-user-trade/consts/pricing"
"shop-user-trade/consts/public"
pricingDto "shop-user-trade/model/dto/pricing"
pricingEntity "shop-user-trade/model/entity/pricing"
)
// chargeOrderDao 计费单数据访问:薄查询/状态迁移原语(同 model-gateway dao 规范)。
// 方法入参统一吃 DTOmodel/dto/pricing);无锁、无事务、不定义业务错误。
var ChargeOrder = &chargeOrderDao{}
type chargeOrderDao struct{}
// GetOrder 按 ID 或 subject_type+subject_id+biz_order_no 查询计费单(Id>0 走 ID)。
// 幂等查询复用:工作流断点续跑/恢复 execId 不变,据此去重。
func (d *chargeOrderDao) GetOrder(ctx context.Context, req *pricingDto.GetChargeOrderReq) (res *pricingEntity.ChargeOrder, err error) {
m := gfdb.DB(ctx).Model(ctx, public.TableNameChargeOrder).Model
if req.Id > 0 {
m = m.Where(pricingEntity.ChargeOrderCol.Id, req.Id)
} else {
m = m.Where(pricingEntity.ChargeOrderCol.SubjectType, req.SubjectType).
Where(pricingEntity.ChargeOrderCol.SubjectID, req.SubjectID).
Where(pricingEntity.ChargeOrderCol.BizOrderNo, req.BizOrderNo)
}
r, err := m.One()
if err != nil {
return
}
if r.IsEmpty() {
return nil, nil
}
err = r.Struct(&res)
return
}
// Insert 创建计费单(事务透明:在调用方事务内执行)
func (d *chargeOrderDao) Insert(ctx context.Context, req *pricingDto.CreateChargeOrderReq) (id int64, err error) {
entity := &pricingEntity.ChargeOrder{
SubjectType: req.SubjectType,
SubjectID: req.SubjectID,
BizOrderNo: req.BizOrderNo,
UserId: req.UserId,
ChargeMode: req.ChargeMode,
Status: req.Status,
RuleSnapshot: req.RuleSnapshot,
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNameChargeOrder).Data(entity).Insert()
if err != nil {
return
}
return r.LastInsertId()
}
// UpdateToSettled 结算迁移:仅 CREATED → SETTLEDrows=0 表示已处理(幂等)
func (d *chargeOrderDao) UpdateToSettled(ctx context.Context, req *pricingDto.SettleChargeOrderReq) (bool, error) {
return d.migrateStatus(ctx, req.Id, gdb.Map{
"status": int(pricingConsts.ChargeOrderStatusSettled),
"actual_amount": req.ActualAmount,
"usage": req.Usage,
"rule_snapshot": req.RuleSnapshot,
"settle_time": req.SettleTime,
})
}
// UpdateToFailed 失败迁移:仅 CREATED → FAILED(不扣费,不动钱)
func (d *chargeOrderDao) UpdateToFailed(ctx context.Context, req *pricingDto.FailChargeOrderReq) (bool, error) {
return d.migrateStatus(ctx, req.Id, gdb.Map{
"status": int(pricingConsts.ChargeOrderStatusFailed),
})
}
// migrateStatus 条件状态迁移(幂等:非 CREATED 时 Update 影响 0 行返回 false
func (d *chargeOrderDao) migrateStatus(ctx context.Context, id int64, data gdb.Map) (bool, error) {
result, err := gfdb.DB(ctx).Model(ctx, public.TableNameChargeOrder).
Data(data).
Where(pricingEntity.ChargeOrderCol.Id, id).
Where(pricingEntity.ChargeOrderCol.Status, int(pricingConsts.ChargeOrderStatusCreated)).
Update()
if err != nil {
return false, err
}
rows, err := result.RowsAffected()
if err != nil {
return false, err
}
return rows > 0, nil
}
// ListByUser 分页查询某用户的计费单
func (d *chargeOrderDao) ListByUser(ctx context.Context, req *pricingDto.ListOrdersReq) (res []pricingEntity.ChargeOrder, total int, err error) {
r, total, err := gfdb.DB(ctx).Model(ctx, public.TableNameChargeOrder).
Where(pricingEntity.ChargeOrderCol.UserId, req.UserId).
OrderDesc(pricingEntity.ChargeOrderCol.CreatedAt).
Page(req.Page, req.PageSize).
AllAndCount(false)
if err != nil {
return
}
err = r.Structs(&res)
return
}
+85
View File
@@ -0,0 +1,85 @@
package dao
import (
"context"
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
"github.com/gogf/gf/v2/database/gdb"
pricingConsts "shop-user-trade/consts/pricing"
"shop-user-trade/consts/public"
pricingDto "shop-user-trade/model/dto/pricing"
pricingEntity "shop-user-trade/model/entity/pricing"
)
// pricingConfigDao 计价配置数据访问:薄 CRUD(同 model-gateway dao 规范)。
// 方法入参统一吃 DTOmodel/dto/pricing);无锁、无事务、不定义业务错误。
var PricingConfig = &pricingConfigDao{}
type pricingConfigDao struct{}
// Get 按计价对象(subject_type + subject_id)获取计价配置
func (d *pricingConfigDao) Get(ctx context.Context, req *pricingDto.GetConfigReq) (res *pricingEntity.PricingConfig, err error) {
r, err := gfdb.DB(ctx).Model(ctx, public.TableNamePricingConfig).
Where(pricingEntity.PricingConfigCol.SubjectType, req.SubjectType).
Where(pricingEntity.PricingConfigCol.SubjectID, req.SubjectID).
One()
if err != nil {
return
}
if r.IsEmpty() {
return nil, nil
}
err = r.Struct(&res)
return
}
// Save 新增或更新计价配置(subject_type+subject_id 唯一,更新走整行覆盖)
func (d *pricingConfigDao) Save(ctx context.Context, req *pricingDto.SaveConfigReq) (id int64, err error) {
if req.Id > 0 {
_, err = gfdb.DB(ctx).Model(ctx, public.TableNamePricingConfig).
Data(gdb.Map{
"subject_type": req.SubjectType,
"subject_id": req.SubjectID,
"rules": req.Rules,
"min_balance": req.MinBalance,
"currency": req.Currency,
"enabled": req.Enabled,
"version": gdb.Raw("version + 1"),
}).
Where(pricingEntity.PricingConfigCol.Id, req.Id).
Update()
return req.Id, err
}
entity := &pricingEntity.PricingConfig{
SubjectType: pricingConsts.SubjectType(req.SubjectType),
SubjectID: req.SubjectID,
Rules: req.Rules,
MinBalance: req.MinBalance,
Currency: req.Currency,
Enabled: req.Enabled,
Version: 1,
}
r, err := gfdb.DB(ctx).Model(ctx, public.TableNamePricingConfig).Data(entity).Insert()
if err != nil {
return
}
return r.LastInsertId()
}
// List 分页查询计价配置(可按 subjectType / enabled 过滤)
func (d *pricingConfigDao) List(ctx context.Context, req *pricingDto.ListConfigsReq) (res []pricingEntity.PricingConfig, total int, err error) {
m := gfdb.DB(ctx).Model(ctx, public.TableNamePricingConfig).Model
if req.SubjectType != "" {
m = m.Where(pricingEntity.PricingConfigCol.SubjectType, req.SubjectType)
}
if req.Enabled != 0 {
m = m.Where(pricingEntity.PricingConfigCol.Enabled, req.Enabled)
}
r, total, err := m.OrderDesc(pricingEntity.PricingConfigCol.UpdatedAt).Page(req.Page, req.PageSize).AllAndCount(false)
if err != nil {
return
}
err = r.Structs(&res)
return
}