package refundapproval import ( "context" "gorm.io/gorm" "github.com/break/junhong_cmp_fiber/internal/model" "github.com/break/junhong_cmp_fiber/pkg/errors" ) // ResolveRefundInTx 按审批业务标识解析出退款申请与本次审批尝试记录。 // // 退款审批的业务标识在审批尝试模式下取尝试记录主键;本能力上线前的存量申请取退款申请主键。 // 尝试记录与退款申请来自两个独立序列,必然存在同值,因此不能只按 businessID 判定归属: // 必须同时匹配 approval_instance_id,才能唯一确定是尝试记录还是退款申请。 // // 解析顺序固定为「尝试记录优先、退款申请兜底」: // 1. tb_refund_request_attempt 中 id = businessID 且 approval_instance_id = instanceID; // 2. tb_refund_request 中 id = businessID 且 approval_instance_id = instanceID; // 3. 两者均不匹配返回稳定冲突错误,绝不回落到任一候选业务单。 // // attempt 在存量兼容路径下为 nil。 func ResolveRefundInTx(ctx context.Context, tx *gorm.DB, businessID, instanceID uint) (*model.RefundRequest, *model.RefundRequestAttempt, error) { if tx == nil || businessID == 0 || instanceID == 0 { return nil, nil, errors.New(errors.CodeInvalidParam, "退款审批业务标识参数无效") } var attempt model.RefundRequestAttempt err := tx.WithContext(ctx). Where("id = ? AND approval_instance_id = ?", businessID, instanceID). First(&attempt).Error switch { case err == nil: var refund model.RefundRequest if err := tx.WithContext(ctx).First(&refund, attempt.RefundID).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, nil, errors.New(errors.CodeConflict, "退款审批尝试记录所属退款申请不存在") } return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询退款审批关联退款申请失败") } return &refund, &attempt, nil case err != gorm.ErrRecordNotFound: return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询退款审批尝试记录失败") } var refund model.RefundRequest err = tx.WithContext(ctx). Where("id = ? AND approval_instance_id = ?", businessID, instanceID). First(&refund).Error switch { case err == nil: return &refund, nil, nil case err == gorm.ErrRecordNotFound: return nil, nil, errors.New(errors.CodeConflict, "退款申请的关联审批实例不一致") default: return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询退款审批关联退款申请失败") } } // ResolveRefundIDInTx 只解析退款申请标识,供审计资源构造与查询关联使用。 func ResolveRefundIDInTx(ctx context.Context, tx *gorm.DB, businessID, instanceID uint) (uint, error) { refund, _, err := ResolveRefundInTx(ctx, tx, businessID, instanceID) if err != nil { return 0, err } return refund.ID, nil } // ResolveRefundForApprovalRequestInTx 解析「审批申请已建立但审批实例尚未回写到业务记录」时刻的业务归属。 // // 通用审批创建用例在同一事务内先写审批实例并写审批申请审计,业务侧随后才把实例 ID 回写到 // 审批尝试记录。该审计时刻尝试记录已存在但其 approval_instance_id 仍为空,因此按实例一致性 // 校验的常规解析必然不命中。本函数只承认这一种在途形态: // // attempt.id = businessID AND attempt.approval_instance_id IS NULL // // 其余情况一律返回不存在,由调用方按常规解析的错误失败关闭,不得放宽为任意未回写记录。 func ResolveRefundForApprovalRequestInTx(ctx context.Context, tx *gorm.DB, businessID uint) (*model.RefundRequest, *model.RefundRequestAttempt, error) { if tx == nil || businessID == 0 { return nil, nil, errors.New(errors.CodeInvalidParam, "退款审批业务标识参数无效") } var attempt model.RefundRequestAttempt err := tx.WithContext(ctx). Where("id = ? AND approval_instance_id IS NULL", businessID). First(&attempt).Error if err != nil { if err == gorm.ErrRecordNotFound { return nil, nil, errors.New(errors.CodeNotFound, "退款审批尝试记录未回写审批实例") } return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询在途退款审批尝试记录失败") } var refund model.RefundRequest if err := tx.WithContext(ctx).First(&refund, attempt.RefundID).Error; err != nil { if err == gorm.ErrRecordNotFound { return nil, nil, errors.New(errors.CodeConflict, "退款审批尝试记录所属退款申请不存在") } return nil, nil, errors.Wrap(errors.CodeDatabaseError, err, "查询退款审批关联退款申请失败") } return &refund, &attempt, nil }