From 2e8a3663a515fdb8da37f4f61fc421121926de0f Mon Sep 17 00:00:00 2001 From: qhd <1766646056@qq.com> Date: Fri, 28 Aug 2026 17:49:03 +0800 Subject: [PATCH] =?UTF-8?q?refactor(pricing):=20DAO=20=E6=8C=89=20(subject?= =?UTF-8?q?=5Ftype,=20subject=5Fid)=20=E9=94=AE=E6=9F=A5=E8=AF=A2/?= =?UTF-8?q?=E5=B9=82=E7=AD=89=20Co-Authored-By:=20Claude=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dao/pricing/charge_order_dao.go | 108 ++++++++++++++++++++++++++++++ dao/pricing/pricing_config_dao.go | 85 +++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 dao/pricing/charge_order_dao.go create mode 100644 dao/pricing/pricing_config_dao.go diff --git a/dao/pricing/charge_order_dao.go b/dao/pricing/charge_order_dao.go new file mode 100644 index 0000000..f8d17ae --- /dev/null +++ b/dao/pricing/charge_order_dao.go @@ -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 规范)。 +// 方法入参统一吃 DTO(model/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 → SETTLED;rows=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 +} diff --git a/dao/pricing/pricing_config_dao.go b/dao/pricing/pricing_config_dao.go new file mode 100644 index 0000000..d92e6a9 --- /dev/null +++ b/dao/pricing/pricing_config_dao.go @@ -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 规范)。 +// 方法入参统一吃 DTO(model/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 +}