新增接口

This commit is contained in:
2026-08-18 16:15:46 +08:00
parent d256f6d176
commit 247d7d9f6e
20 changed files with 523 additions and 16178 deletions

View File

@@ -8,6 +8,7 @@ import (
"github.com/bytedance/sonic"
"gorm.io/gorm"
"gorm.io/gorm/clause"
approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval"
"github.com/break/junhong_cmp_fiber/internal/model"
@@ -46,6 +47,94 @@ func NewOfflineCreationService(db *gorm.DB, approval approvalapp.Port, audit Rec
return &OfflineCreationService{db: db, approval: approval, audit: audit}
}
// TriggerHistorical 为历史待审批线下代充值补发一次企业微信审批。
func (s *OfflineCreationService) TriggerHistorical(ctx context.Context, recordID uint) (*CreateOfflineResult, error) {
if s == nil || s.db == nil || s.approval == nil || s.audit == nil || recordID == 0 {
return nil, errors.New(errors.CodeServiceUnavailable, "员工线下代充值审批能力未配置")
}
var record model.AgentRechargeRecord
if err := s.db.WithContext(ctx).First(&record, recordID).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "充值记录不存在")
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史线下代充值申请失败")
}
if record.PaymentMethod != constants.RechargeMethodOffline || record.Status != constants.RechargeStatusPending || record.ApprovalInstanceID != nil {
return nil, errors.New(errors.CodeConflict, "充值申请状态不允许补发审批")
}
account, shop, wallet, err := s.loadHistoricalFacts(ctx, &record)
if err != nil {
return nil, err
}
preparation, err := s.approval.Prepare(ctx, approvalapp.PrepareRequest{
BusinessType: constants.ApprovalBusinessTypeOfflineRecharge, SubmitterAccountID: record.UserID,
CorrelationID: record.RechargeNo,
})
if err != nil {
return nil, err
}
command := CreateOfflineCommand{
SubmitterAccountID: record.UserID, SubmitterUserType: account.UserType, ShopID: record.ShopID,
RechargeNo: record.RechargeNo, Amount: record.Amount,
PaymentVoucherKeys: []string(record.PaymentVoucherKey), Remark: record.Remark,
}
submitterSnapshot, requestSnapshot, err := offlineApprovalSnapshots(command, account.Username, shop.ShopName)
if err != nil {
return nil, err
}
var approvalStatus int
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var current model.AgentRechargeRecord
if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).First(&current, recordID).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return errors.New(errors.CodeNotFound, "充值记录不存在")
}
return errors.Wrap(errors.CodeDatabaseError, err, "锁定历史线下代充值申请失败")
}
if current.PaymentMethod != constants.RechargeMethodOffline || current.Status != constants.RechargeStatusPending || current.ApprovalInstanceID != nil {
return errors.New(errors.CodeConflict, "充值申请状态不允许补发审批")
}
reference, err := s.approval.CreateInTx(ctx, tx, approvalapp.CreateRequest{
Preparation: preparation, BusinessType: constants.ApprovalBusinessTypeOfflineRecharge,
BusinessID: current.ID, SubmitterAccountID: current.UserID,
SubmitterSnapshot: submitterSnapshot, RequestSnapshot: requestSnapshot,
CorrelationID: current.RechargeNo,
})
if err != nil {
return err
}
result := tx.WithContext(ctx).Model(&model.AgentRechargeRecord{}).
Where("id = ? AND payment_method = ? AND status = ? AND approval_instance_id IS NULL", current.ID, constants.RechargeMethodOffline, constants.RechargeStatusPending).
Update("approval_instance_id", reference.InstanceID)
if result.Error != nil {
return errors.Wrap(errors.CodeDatabaseError, result.Error, "关联线下代充值审批实例失败")
}
if result.RowsAffected != 1 {
return errors.New(errors.CodeConflict, "线下代充值审批实例关联已变化")
}
current.ApprovalInstanceID = &reference.InstanceID
record = current
approvalStatus = reference.Status
var instance model.ApprovalInstance
if err := tx.WithContext(ctx).First(&instance, reference.InstanceID).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询线下代充值审批审计快照失败")
}
return s.audit.WriteAgentRecharge(ctx, tx, RechargeAudit{
ActionCode: constants.AuditActionAgentRechargeCreated, Summary: "补发员工线下代充值审批",
Record: &current, Approval: &instance, Wallet: wallet,
AfterData: map[string]any{"status": current.Status, "approval_instance_id": current.ApprovalInstanceID},
})
})
if err != nil {
return nil, err
}
return &CreateOfflineResult{
Record: &record, ShopName: shop.ShopName, SubmitterName: account.Username, ApprovalStatus: approvalStatus,
}, nil
}
// Execute 在业务写入前校验审批渠道,并在同一事务保存充值申请、审批实例和提交 Outbox。
func (s *OfflineCreationService) Execute(ctx context.Context, command CreateOfflineCommand) (*CreateOfflineResult, error) {
if s == nil || s.db == nil || s.approval == nil || s.audit == nil {
@@ -140,6 +229,38 @@ func validateCreateOfflineCommand(command CreateOfflineCommand) error {
return nil
}
func (s *OfflineCreationService) loadHistoricalFacts(
ctx context.Context, record *model.AgentRechargeRecord,
) (*model.Account, *model.Shop, *model.AgentWallet, error) {
if record == nil || record.UserID == 0 || record.ShopID == 0 || record.AgentWalletID == 0 {
return nil, nil, nil, errors.New(errors.CodeInvalidParam)
}
var account model.Account
if err := s.db.WithContext(ctx).Where("id = ? AND status = ?", record.UserID, constants.StatusEnabled).First(&account).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil, nil, errors.New(errors.CodeForbidden, "原创建账号不可用")
}
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史线下代充值创建人失败")
}
var shop model.Shop
if err := s.db.WithContext(ctx).Where("id = ?", record.ShopID).First(&shop).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil, nil, errors.New(errors.CodeNotFound, "目标店铺不存在")
}
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史线下代充值目标店铺失败")
}
var wallet model.AgentWallet
if err := s.db.WithContext(ctx).
Where("id = ? AND shop_id = ? AND wallet_type = ? AND status = ?", record.AgentWalletID, record.ShopID, constants.AgentWalletTypeMain, constants.AgentWalletStatusNormal).
First(&wallet).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil, nil, errors.New(errors.CodeWalletNotFound, "原充值主钱包不存在或不可用")
}
return nil, nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史线下代充值主钱包失败")
}
return &account, &shop, &wallet, nil
}
func (s *OfflineCreationService) loadCreationFacts(
ctx context.Context,
command CreateOfflineCommand,

View File

@@ -8,6 +8,7 @@ import (
"github.com/bytedance/sonic"
"gorm.io/gorm"
"gorm.io/gorm/clause"
approvalapp "github.com/break/junhong_cmp_fiber/internal/application/approval"
"github.com/break/junhong_cmp_fiber/internal/model"
@@ -55,6 +56,99 @@ func NewCreationService(db *gorm.DB, approval approvalapp.Port, audit AuditWrite
}
// Execute 在业务写入前校验审批渠道,并在同一事务冻结退款事实和审批事实。
// TriggerHistorical 为历史待审批退款补发一次企业微信审批。
func (s *CreationService) TriggerHistorical(ctx context.Context, refundID uint) (*CreateResult, error) {
if s == nil || s.db == nil || s.approval == nil || s.audit == nil || refundID == 0 {
return nil, errors.New(errors.CodeServiceUnavailable, "退款审批能力未配置")
}
var refund model.RefundRequest
if err := s.db.WithContext(ctx).First(&refund, refundID).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "退款申请不存在")
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询历史退款申请失败")
}
if refund.Status != model.RefundStatusPending || refund.ApprovalInstanceID != nil {
return nil, errors.New(errors.CodeConflict, "退款申请状态不允许补发审批")
}
account, err := s.loadSubmitter(ctx, refund.Creator)
if err != nil {
return nil, err
}
var order model.Order
if err := s.db.WithContext(ctx).First(&order, refund.OrderID).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return nil, errors.New(errors.CodeNotFound, "退款关联订单不存在")
}
return nil, errors.Wrap(errors.CodeDatabaseError, err, "查询退款关联订单失败")
}
preparation, err := s.approval.Prepare(ctx, approvalapp.PrepareRequest{
BusinessType: constants.ApprovalBusinessTypeRefund, SubmitterAccountID: refund.Creator,
CorrelationID: refund.RefundNo,
})
if err != nil {
return nil, err
}
submitterSnapshot, requestSnapshot, err := refundSnapshots(&refund, account)
if err != nil {
return nil, err
}
var approvalStatus int
err = s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
var current model.RefundRequest
if err := tx.WithContext(ctx).Clauses(clause.Locking{Strength: "UPDATE"}).First(&current, refundID).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return errors.New(errors.CodeNotFound, "退款申请不存在")
}
return errors.Wrap(errors.CodeDatabaseError, err, "锁定历史退款申请失败")
}
if current.Status != model.RefundStatusPending || current.ApprovalInstanceID != nil {
return errors.New(errors.CodeConflict, "退款申请状态不允许补发审批")
}
var currentOrder model.Order
if err := tx.WithContext(ctx).First(&currentOrder, current.OrderID).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询退款关联订单失败")
}
reference, err := s.approval.CreateInTx(ctx, tx, approvalapp.CreateRequest{
Preparation: preparation, BusinessType: constants.ApprovalBusinessTypeRefund,
BusinessID: current.ID, SubmitterAccountID: current.Creator,
SubmitterSnapshot: submitterSnapshot, RequestSnapshot: requestSnapshot,
CorrelationID: current.RefundNo,
})
if err != nil {
return err
}
result := tx.WithContext(ctx).Model(&model.RefundRequest{}).
Where("id = ? AND status = ? AND approval_instance_id IS NULL", current.ID, model.RefundStatusPending).
Update("approval_instance_id", reference.InstanceID)
if result.Error != nil {
return errors.Wrap(errors.CodeDatabaseError, result.Error, "关联退款审批实例失败")
}
if result.RowsAffected != 1 {
return errors.New(errors.CodeConflict, "退款审批实例关联已变化")
}
current.ApprovalInstanceID = &reference.InstanceID
refund = current
order = currentOrder
approvalStatus = reference.Status
var instance model.ApprovalInstance
if err := tx.WithContext(ctx).First(&instance, reference.InstanceID).Error; err != nil {
return errors.Wrap(errors.CodeDatabaseError, err, "查询退款审批审计快照失败")
}
return s.audit.WriteRefundApplication(ctx, tx, ApplicationAudit{
Refund: &current, Order: &currentOrder, Approval: &instance, Submitter: account,
})
})
if err != nil {
return nil, err
}
return &CreateResult{Refund: &refund, SubmitterName: account.Username, ApprovalStatus: approvalStatus}, nil
}
func (s *CreationService) Execute(ctx context.Context, command CreateCommand) (*CreateResult, error) {
if s == nil || s.db == nil || s.approval == nil || s.audit == nil {
return nil, errors.New(errors.CodeServiceUnavailable, "退款审批能力未配置")

View File

@@ -143,6 +143,20 @@ func (h *AgentRechargeHandler) Get(c *fiber.Ctx) error {
return response.Success(c, result)
}
// TriggerApproval 主动补发历史线下代理充值审批。
// POST /api/admin/agent-recharges/:id/trigger-approval
func (h *AgentRechargeHandler) TriggerApproval(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil || id == 0 {
return errors.New(errors.CodeInvalidParam, "无效的充值记录ID")
}
result, err := h.service.TriggerApproval(c.UserContext(), uint(id))
if err != nil {
return err
}
return response.Success(c, result)
}
// PaymentStatus 查询代理充值本地支付与到账状态。
// GET /api/admin/agent-recharges/:id/payment-status
func (h *AgentRechargeHandler) PaymentStatus(c *fiber.Ctx) error {

View File

@@ -69,6 +69,20 @@ func (h *RefundHandler) GetByID(c *fiber.Ctx) error {
return response.Success(c, result)
}
// TriggerApproval 主动补发历史退款审批
// POST /api/admin/refunds/:id/trigger-approval
func (h *RefundHandler) TriggerApproval(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil || id == 0 {
return errors.New(errors.CodeInvalidParam, "无效的退款申请ID")
}
result, err := h.service.TriggerApproval(c.UserContext(), uint(id))
if err != nil {
return err
}
return response.Success(c, result)
}
// Approve 审批通过退款申请
// POST /api/admin/refunds/:id/approve
func (h *RefundHandler) Approve(c *fiber.Ctx) error {

View File

@@ -1,82 +0,0 @@
package audit
import (
"context"
"encoding/json"
"testing"
"gorm.io/gorm"
accessauditapp "github.com/break/junhong_cmp_fiber/internal/application/accessaudit"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/auditfailure"
"github.com/break/junhong_cmp_fiber/pkg/constants"
)
func TestAppendFailureDoesNotReturnToBusiness(t *testing.T) {
writer := NewWriter(nil, nil)
input := AppendInput{ActionCode: "missing_action"}
before := auditfailure.SecondaryWriteFailureCount()
if err := writer.Append(context.Background(), nil, input); err != nil {
t.Fatalf("Append 返回审计失败: %v", err)
}
if err := writer.WriteAccessChange(context.Background(), nil, accessauditapp.ChangeAudit{
ActionCode: constants.AuditActionPersonalCustomerAssetBound,
OperatorID: 1,
}); err != nil {
t.Fatalf("资源构造失败返回业务: %v", err)
}
if got := auditfailure.SecondaryWriteFailureCount(); got != before+2 {
t.Fatalf("二次失败记录次数 = %d, want %d", got, before+2)
}
if _, err := writer.AppendAndGet(context.Background(), nil, input); err == nil {
t.Fatal("AppendAndGet 未保留错误语义")
}
}
func TestPersonalCustomerAssetBoundProjectsOnlyPersonalResources(t *testing.T) {
action, ok := NewRegistry().Action(constants.AuditActionPersonalCustomerAssetBound)
if !ok {
t.Fatal("未注册个人客户资产绑定审计动作")
}
resources, err := accessResources(accessauditapp.ChangeAudit{
ActionCode: constants.AuditActionPersonalCustomerAssetBound,
PersonalCustomer: &model.PersonalCustomer{Model: gorm.Model{ID: 1}, Nickname: "客户"},
PersonalDevices: []accessauditapp.PersonalCustomerDeviceChange{{
Binding: &model.PersonalCustomerDevice{Model: gorm.Model{ID: 2}, CustomerID: 1, VirtualNo: "DEVICE-1"},
}},
PersonalICCIDs: []accessauditapp.PersonalCustomerICCIDChange{{
Binding: &model.PersonalCustomerICCID{Model: gorm.Model{ID: 3}, CustomerID: 1, ICCID: "ICCID-1"},
}},
SubjectVisibility: constants.AuditSubjectDetail,
SubjectSummary: "绑定个人客户资产",
SubjectData: map[string]any{"asset_type": constants.AuditResourceIotCard, "asset_id": uint(9)},
}, action.PrimaryResource)
if err != nil {
t.Fatalf("构造绑定审计资源失败: %v", err)
}
projected, err := NewWriter(nil, nil).buildResources(resources, action)
if err != nil {
t.Fatalf("构造绑定审计投影失败: %v", err)
}
want := map[string]bool{
constants.AuditResourcePersonalCustomer: true,
constants.AuditResourcePersonalCustomerDevice: true,
constants.AuditResourcePersonalCustomerICCID: true,
}
for _, resource := range projected {
if resource.ResourceType == constants.AuditResourceIotCard || resource.ResourceType == constants.AuditResourceDevice {
t.Fatalf("绑定审计投影包含内部资源: %s", resource.ResourceType)
}
delete(want, resource.ResourceType)
if resource.ResourceType == constants.AuditResourcePersonalCustomer {
var subjectData map[string]any
if err := json.Unmarshal(resource.SubjectData, &subjectData); err != nil || resource.SubjectVisibility != constants.AuditSubjectDetail || subjectData["asset_type"] != constants.AuditResourceIotCard || subjectData["asset_id"] != float64(9) {
t.Fatalf("主个人客户主体投影不完整: %#v", resource)
}
}
}
for resourceType := range want {
t.Fatalf("绑定审计投影缺少合法资源: %s", resourceType)
}
}

View File

@@ -61,6 +61,14 @@ func registerAgentRechargeRoutes(router fiber.Router, handler *admin.AgentRechar
Auth: true,
})
Register(group, doc, groupPath, "POST", "/:id/trigger-approval", handler.TriggerApproval, RouteSpec{
Summary: "补发历史线下代理充值审批",
Tags: []string{"代理预充值"},
Input: new(dto.IDReq),
Output: new(dto.AgentRechargeResponse),
Auth: true,
})
Register(group, doc, groupPath, "POST", "/:id/offline-pay", handler.OfflinePay, RouteSpec{
Summary: "确认线下充值",
Tags: []string{"代理预充值"},

View File

@@ -49,6 +49,14 @@ func registerRefundRoutes(router fiber.Router, handler *admin.RefundHandler, doc
Auth: true,
})
Register(refund, doc, groupPath, "POST", "/:id/trigger-approval", handler.TriggerApproval, RouteSpec{
Summary: "补发历史退款审批",
Tags: []string{"退款管理"},
Input: new(dto.RefundIDRequest),
Output: new(dto.RefundResponse),
Auth: true,
})
Register(refund, doc, groupPath, "POST", "/:id/approve", handler.Approve, RouteSpec{
Summary: "审批通过退款申请",
Tags: []string{"退款管理"},

View File

@@ -434,6 +434,27 @@ func (s *Service) appendCreditedAudit(ctx context.Context, tx *gorm.DB, record *
})
}
// TriggerApproval 为历史线下代理充值主动补发企业微信审批。
func (s *Service) TriggerApproval(ctx context.Context, id uint) (*dto.AgentRechargeResponse, error) {
if s.offlineCreation == nil {
return nil, errors.New(errors.CodeServiceUnavailable, "员工线下代充值审批能力未配置")
}
record, err := s.agentRechargeStore.GetByID(ctx, id)
if err != nil {
return nil, errors.New(errors.CodeNotFound, "充值记录不存在")
}
result, err := s.offlineCreation.TriggerHistorical(ctx, record.ID)
if err != nil {
return nil, err
}
resp := toResponse(result.Record, result.ShopName)
resp.SubmitterName = result.SubmitterName
resp.ApprovalProvider = constants.IntegrationProviderWeCom
resp.ApprovalStatus = &result.ApprovalStatus
resp.ApprovalStatusName = constants.GetApprovalStatusName(result.ApprovalStatus)
return resp, nil
}
// GetByID 根据ID查询充值订单详情
// GET /api/admin/agent-recharges/:id
func (s *Service) GetByID(ctx context.Context, id uint) (*dto.AgentRechargeResponse, error) {

View File

@@ -1,83 +0,0 @@
package customer_binding
import (
"context"
"database/sql"
"database/sql/driver"
"io"
"testing"
"gorm.io/driver/postgres"
"gorm.io/gorm"
accessauditapp "github.com/break/junhong_cmp_fiber/internal/application/accessaudit"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
)
func init() { sql.Register("customer_binding_audit_test", customerAuditDriver{}) }
type customerAuditDriver struct{}
func (customerAuditDriver) Open(string) (driver.Conn, error) { return customerAuditConn{}, nil }
type customerAuditConn struct{}
func (customerAuditConn) Prepare(string) (driver.Stmt, error) { return nil, driver.ErrSkip }
func (customerAuditConn) Close() error { return nil }
func (customerAuditConn) Begin() (driver.Tx, error) { return nil, driver.ErrSkip }
func (customerAuditConn) QueryContext(context.Context, string, []driver.NamedValue) (driver.Rows, error) {
return &customerAuditRows{}, nil
}
type customerAuditRows struct{ sent bool }
func (*customerAuditRows) Columns() []string { return []string{"id", "nickname"} }
func (r *customerAuditRows) Close() error { return nil }
func (r *customerAuditRows) Next(dest []driver.Value) error {
if r.sent {
return io.EOF
}
r.sent = true
dest[0], dest[1] = int64(7), "客户"
return nil
}
type captureAuditWriter struct{ change accessauditapp.ChangeAudit }
func (w *captureAuditWriter) WriteAccessChange(_ context.Context, _ *gorm.DB, change accessauditapp.ChangeAudit) error {
w.change = change
return nil
}
func TestWriteBindingAuditOmitsInternalAssets(t *testing.T) {
db, err := sql.Open("customer_binding_audit_test", "")
if err != nil {
t.Fatal(err)
}
defer db.Close()
tx, err := gorm.Open(postgres.New(postgres.Config{Conn: db}), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
writer := &captureAuditWriter{}
service := &Service{accessAudit: writer}
personalDevices := []accessauditapp.PersonalCustomerDeviceChange{{Binding: &model.PersonalCustomerDevice{Model: gorm.Model{ID: 2}, CustomerID: 7, VirtualNo: "DEVICE-1"}}}
personalICCIDs := []accessauditapp.PersonalCustomerICCIDChange{{Binding: &model.PersonalCustomerICCID{Model: gorm.Model{ID: 3}, CustomerID: 7, ICCID: "ICCID-1"}}}
cards := []accessauditapp.IotCardChange{{Card: &model.IotCard{Model: gorm.Model{ID: 9}}}}
devices := []accessauditapp.DeviceChange{{Device: &model.Device{Model: gorm.Model{ID: 10}}}}
if err := service.writeBindingAudit(context.Background(), tx, constants.AuditActionPersonalCustomerAssetBound, "绑定个人客户资产", 7, personalDevices, personalICCIDs, cards, devices); err != nil {
t.Fatalf("写入绑定审计失败: %v", err)
}
change := writer.change
if len(change.Cards) != 0 || len(change.Devices) != 0 {
t.Fatalf("绑定审计泄露内部资源: Cards=%d Devices=%d", len(change.Cards), len(change.Devices))
}
if change.PersonalCustomer == nil || change.PersonalCustomer.ID != 7 || len(change.PersonalDevices) != 1 || len(change.PersonalICCIDs) != 1 {
t.Fatalf("绑定审计未保留个人客户字段: %#v", change)
}
if change.SubjectData["asset_type"] != constants.AuditResourceIotCard || change.SubjectData["asset_id"] != uint(9) {
t.Fatalf("绑定审计未保留主体摘要: %#v", change.SubjectData)
}
}

View File

@@ -238,6 +238,27 @@ func (s *Service) List(ctx context.Context, req *dto.RefundListRequest) (*dto.Re
}
// GetByID 根据 ID 查询退款申请详情
// TriggerApproval 为历史退款申请主动补发企业微信审批。
func (s *Service) TriggerApproval(ctx context.Context, id uint) (*dto.RefundResponse, error) {
if s.refundApprovalCreation == nil {
return nil, errors.New(errors.CodeServiceUnavailable, "退款审批能力未配置")
}
refund, err := s.refundStore.GetByIDForOperation(ctx, id)
if err != nil {
return nil, errors.New(errors.CodeNotFound, "退款申请不存在")
}
result, err := s.refundApprovalCreation.TriggerHistorical(ctx, refund.ID)
if err != nil {
return nil, err
}
resp := buildRefundResponse(result.Refund)
resp.SubmitterName = result.SubmitterName
resp.ApprovalProvider = constants.IntegrationProviderWeCom
resp.ApprovalStatus = &result.ApprovalStatus
resp.ApprovalStatusName = constants.GetApprovalStatusName(result.ApprovalStatus)
return resp, nil
}
func (s *Service) GetByID(ctx context.Context, id uint) (*dto.RefundResponse, error) {
refund, err := s.refundStore.GetByID(ctx, id)
if err != nil {