Files
junhong_cmp_fiber/internal/model/dto/refund_dto.go
邹进 94cbf2adab feat: 采集记录成功失败分表并保存网关原始数据,到期归档 csv.gz,新增记录分页查询接口
- 网关客户端新增原始数据收集器,记录最后一次尝试的明文请求、HTTP 状态、响应体与 code/msg/trace_id
- tb_card_traffic_collection 只写成功读数,新增 carrier_id 与原始数据列;新增 tb_card_traffic_collection_failure,失败不判断有无套餐并带错误详情
- 采集记录在线保留当月加最近 2 个整月,到期分区摘出、导出 csv.gz、校验行数与 SHA-256 后删除,本地保留 3 个月
- 新增采集成功/失败、卡日流量、套餐日流量详单、冲正流水、退款后处理尝试列表接口:可查单个也可查全部,分页与条件筛选
- 停复机记录支持资产标识、场景、原因、指令号筛选;单卡历史支持动作、结果、来源筛选
- 单卡采集记录支持筛选与合并失败记录;套餐日流量详单支持分页;退款列表支持后处理状态筛选
- 测试环境 api、worker 日志分目录,保留 40 个压缩备份;worker 挂载 archive 目录
- 迁移 000272、000273 需维护者在测试库执行后再部署

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 18:47:00 +09:00

211 lines
22 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package dto
import "time"
// CreateRefundRequest 创建退款申请请求
type CreateRefundRequest struct {
OrderID uint `json:"order_id" validate:"required" required:"true" description:"关联订单ID"`
ActualReceivedAmount *int64 `json:"actual_received_amount" validate:"omitempty" description:"已废弃:实收金额由系统从原成功支付记录或订单实际收款派生并冻结,提交人填写无效"`
Method string `json:"method" validate:"required,oneof=original_route customer_account asset_wallet agent_wallet" required:"true" description:"退款方式 (original_route:原路退款, customer_account:客户收款信息退款, asset_wallet:退回资产钱包, agent_wallet:退回代理主钱包)"`
CustomerAccountInfo string `json:"customer_account_info" validate:"omitempty,max=1000" maxLength:"1000" description:"客户收款信息,仅客户收款信息退款方式必填,不得复用公司线下收款方式字典"`
RequestedRefundAmount int64 `json:"requested_refund_amount" validate:"min=0" required:"true" minimum:"0" description:"申请退款金额(分),0 ~ 冻结实收金额;0 元退款表示作废权益不退钱,仅限客户收款信息方式(钱包支付订单不支持),此时客户收款信息可不填但凭证必传"`
RefundVoucherKey []string `json:"refund_voucher_key" validate:"omitempty,max=5,dive,max=500" maxItems:"5" description:"退款凭证对象存储file_key列表(最多5个,仅客户收款信息退款方式必填,通过/storage/upload-url上传图片后获得)"`
RefundReason string `json:"refund_reason" validate:"required,max=1000" required:"true" maxLength:"1000" description:"退款原因(必填,去除首尾空白后不得为空)"`
// Remark 是申请人备注,随本次提交冻结进当次审批尝试快照;与审批备注(详情响应中的 remark)语义不同。
Remark *string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"申请人备注(可选,随本次提交冻结进审批尝试快照,不改变审批备注)"`
PackageUsageID *uint `json:"package_usage_id" validate:"omitempty" description:"关联套餐使用记录ID(可选)"`
}
// RefundIDRequest 退款申请ID路径参数
type RefundIDRequest struct {
ID uint `path:"id" description:"退款申请ID" required:"true"`
}
// RejectRefundRequest 拒绝退款申请请求
type RejectRefundRequest struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" description:"退款申请ID"`
RejectReason string `json:"reject_reason" validate:"required,max=500" required:"true" maxLength:"500" description:"拒绝原因(必填)"`
}
// ResubmitRefundRequest 重新提交退款申请请求
// 退款单被退回后,可修改部分字段后重新提交
type ResubmitRefundRequest struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" description:"退款申请ID"`
ActualReceivedAmount *int64 `json:"actual_received_amount" validate:"omitempty,min=1" minimum:"1" description:"已废弃:实收金额由系统从原成功支付记录或订单实际收款派生并冻结,提交人填写无效"`
Method *string `json:"method" validate:"omitempty,oneof=original_route customer_account asset_wallet agent_wallet" description:"退款方式 (original_route:原路退款, customer_account:客户收款信息退款, asset_wallet:退回资产钱包, agent_wallet:退回代理主钱包),不填沿用原有方式"`
CustomerAccountInfo *string `json:"customer_account_info" validate:"omitempty,max=1000" maxLength:"1000" description:"客户收款信息,仅客户收款信息退款方式必填,不得复用公司线下收款方式字典"`
RequestedRefundAmount *int64 `json:"requested_refund_amount" validate:"omitempty,min=0" minimum:"0" description:"申请退款金额(分),不填沿用原金额;0 元退款规则同创建:仅限客户收款信息方式(钱包支付订单不支持),客户收款信息可不填但凭证必传"`
RefundVoucherKey *[]string `json:"refund_voucher_key" validate:"omitempty,max=5,dive,max=500" maxItems:"5" description:"退款凭证对象存储file_key列表(重新提交时可替换;客户收款信息退款方式必填,最多5个)"`
RefundReason *string `json:"refund_reason" validate:"omitempty,max=1000" maxLength:"1000" description:"退款原因(不填沿用原有原因;重提时去除首尾空白后不得为空)"`
// Remark 是本次重提的申请人备注,随本次尝试冻结;不填沿用该退款单最近一次尝试的备注。
Remark *string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"申请人备注(可选,随本次重提冻结进新的审批尝试快照,历史尝试备注不被改写)"`
}
// ApproveRefundRequest 审批通过退款申请请求
type ApproveRefundRequest struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" description:"退款申请ID"`
ApprovedRefundAmount *int64 `json:"approved_refund_amount" validate:"omitempty,min=1" minimum:"1" description:"审批实际退款金额(分),不填则使用申请金额"`
Remark string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"审批备注"`
}
// ReturnRefundRequest 退回退款申请请求
type ReturnRefundRequest struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" description:"退款申请ID"`
Remark string `json:"remark" validate:"omitempty,max=500" maxLength:"500" description:"退回备注"`
}
// RefundListRequest 退款申请列表查询请求
type RefundListRequest struct {
Page int `json:"page" query:"page" validate:"omitempty,min=1" minimum:"1" description:"页码(默认1)"`
PageSize int `json:"page_size" query:"page_size" validate:"omitempty,min=1,max=100" minimum:"1" maximum:"100" description:"每页数量(默认20,最大100)"`
Status *int `json:"status" query:"status" validate:"omitempty,min=1,max=7" minimum:"1" maximum:"7" description:"状态 (1:待审批, 2:已通过, 3:已拒绝, 4:已退回, 5:原路退款处理中, 6:原路退款失败, 7:审批提交失败)"`
OrderID *uint `json:"order_id" query:"order_id" validate:"omitempty" description:"关联订单ID"`
ShopID *uint `json:"shop_id" query:"shop_id" validate:"omitempty" description:"店铺ID"`
AssetIdentifier string `json:"asset_identifier" query:"asset_identifier" validate:"omitempty,max=100" maxLength:"100" description:"资产标识精确检索(ICCID 或 设备虚拟号,非空时精确匹配)"`
AssetPostStatus *int `json:"asset_post_status" query:"asset_post_status" validate:"omitempty,min=0,max=3" minimum:"0" maximum:"3" description:"资产后处理状态 (0:未开始, 1:待重试, 2:已完成, 3:需人工处理)"`
}
// RefundResponse 退款申请详情响应
type RefundResponse struct {
ID uint `json:"id" description:"退款申请ID"`
RefundNo string `json:"refund_no" description:"退款单号"`
OrderID uint `json:"order_id" description:"关联订单ID"`
OrderNo string `json:"order_no" description:"订单号"`
AssetIdentifier string `json:"asset_identifier,omitempty" description:"下单时资产的标识符快照(卡为 ICCID,设备优先使用 VirtualNo,缺失时使用 IMEI)"`
AssetType string `json:"asset_type,omitempty" description:"资产类型 (card:单卡, device:设备)"`
IotCardID *uint `json:"iot_card_id,omitempty" description:"IoT卡ID"`
DeviceID *uint `json:"device_id,omitempty" description:"设备ID"`
PackageUsageID *uint `json:"package_usage_id,omitempty" description:"关联套餐使用记录ID"`
// 当前退款套餐用量:按冻结套餐使用记录 → 订单主套餐 → 订单任一套餐的优先级解析,
// 只用于展示、查询与导出,不参与退款金额校验、套餐失效或佣金回溯。
RefundPackageUsedMB int64 `json:"refund_package_used_mb" description:"当前退款套餐已用量(MB,真实流量;解析不到套餐时为0)"`
RefundPackageTotalMB int64 `json:"refund_package_total_mb" description:"当前退款套餐总量(MB,真实流量;解析不到套餐时为0)"`
ShopID *uint `json:"shop_id,omitempty" description:"店铺ID"`
ShopName string `json:"shop_name,omitempty" description:"店铺名称"`
ActualReceivedAmount int64 `json:"actual_received_amount" description:"实收金额(分)"`
RequestedRefundAmount int64 `json:"requested_refund_amount" description:"申请退款金额(分)"`
ApprovedRefundAmount *int64 `json:"approved_refund_amount,omitempty" description:"审批实际退款金额(分)"`
RefundVoucherKey []string `json:"refund_voucher_key" description:"退款凭证对象存储file_key列表(最多5个)"`
RefundReason string `json:"refund_reason" description:"退款原因"`
Status int `json:"status" description:"状态 (1:待审批, 2:已通过, 3:已拒绝, 4:已退回, 5:原路退款处理中, 6:原路退款失败, 7:审批提交失败)"`
StatusName string `json:"status_name" description:"状态名称(中文)"`
Method string `json:"method" description:"退款方式 (original_route:原路退款, customer_account:客户收款信息退款, asset_wallet:退回资产钱包, agent_wallet:退回代理主钱包),空表示未接入方式的存量申请"`
MethodName string `json:"method_name" description:"退款方式中文名称"`
FrozenActualReceivedAmount int64 `json:"frozen_actual_received_amount" description:"系统派生并冻结的权威实收金额(分),作为可退金额上限"`
CustomerAccountInfo string `json:"customer_account_info" description:"客户收款信息自由文本快照,仅客户收款信息退款方式有值"`
ChannelRefundStatus int `json:"channel_refund_status" description:"渠道原路退款状态 (0:未发起, 1:处理中, 2:已成功, 3:已失败),0 表示未发起渠道退款或不适用该退款方式"`
ChannelRefundStatusName string `json:"channel_refund_status_name" description:"渠道原路退款状态中文名称"`
ChannelRefundNo string `json:"channel_refund_no" description:"渠道退款流水号,渠道明确成功或失败后回填"`
ChannelRefundRequestNo string `json:"channel_refund_request_no" description:"渠道退款请求号快照,用于幂等与对账"`
ChannelRefundAmount int64 `json:"channel_refund_amount" description:"提交渠道的退款金额快照(分)"`
ChannelRefundedAt string `json:"channel_refunded_at,omitempty" description:"渠道明确退款成功时间"`
// 结算标识:前两项是冻结的来源支付事实,第三项是财务补录的线下处理流水号,语义互不相同。
SourcePaymentNo string `json:"source_payment_no" description:"冻结的来源支付单号,取创建申请时的原成功支付记录;线下订单无线上支付记录时为空字符串"`
OriginalChannelTradeNo string `json:"original_channel_trade_no" description:"冻结的原支付渠道交易流水号,取创建申请时的原成功支付记录;线下订单无线上支付记录时为空字符串"`
OfflineSettlementNo string `json:"offline_settlement_no" description:"线下退款处理流水号或凭证编号,由授权账号补录或更正;与渠道退款流水号语义不同"`
OfflineSettledAt string `json:"offline_settled_at,omitempty" description:"线下退款处理流水号最近一次登记或更正时间"`
OfflineSettledBy uint `json:"offline_settled_by" description:"线下退款处理流水号最近一次登记或更正的操作账号ID,0 表示未登记"`
FailureReason string `json:"failure_reason" description:"结构化失败分类稳定编码 (channel_rejected:渠道明确拒绝, credential_invalid:渠道凭证失效, insufficient_balance:渠道余额不足, timeout_unknown:超时或结果未知, approval_rejected:企业微信驳回或关闭, revoked_after_approved:企业微信通过后撤销, payment_fact_invalid:本地原支付事实不可用),空表示无失败"`
FailureReasonName string `json:"failure_reason_name" description:"失败分类中文名称"`
FailureMessage string `json:"failure_message" description:"失败安全摘要,供人工排查;不含渠道凭证等敏感内容"`
AnomalyFlag int `json:"anomaly_flag" description:"异常标记 (0:无异常, 1:有异常,需人工处理)"`
AnomalyReason string `json:"anomaly_reason" description:"异常原因说明,无异常时为空"`
LatestAttemptID uint `json:"latest_attempt_id" description:"最新审批尝试记录ID,仅用于展示"`
LatestApprovalInstanceID uint `json:"latest_approval_instance_id" description:"最新通用审批实例ID,仅用于展示"`
Attempts []RefundAttemptResponse `json:"attempts" description:"审批尝试记录,按提交顺序排列,历史材料不被覆盖;无尝试记录时为空数组"`
ProcessorID *uint `json:"processor_id,omitempty" description:"审批人ID"`
ProcessedAt string `json:"processed_at,omitempty" description:"审批时间"`
RejectReason string `json:"reject_reason,omitempty" description:"拒绝原因"`
Remark string `json:"remark,omitempty" description:"审批备注"`
CommissionDeducted bool `json:"commission_deducted" description:"佣金是否已回扣"`
AssetReset bool `json:"asset_reset" description:"退款后资产处理是否完成"`
AssetPostStatus int `json:"asset_post_status" description:"资产后处理状态 (0:未开始, 1:待重试, 2:已完成, 3:需人工处理)"`
AssetPostAttempts int `json:"asset_post_attempts" description:"资产后处理已尝试次数(自动重试上限 10 次)"`
AssetPostLastError string `json:"asset_post_last_error" description:"资产后处理最后一次错误"`
AssetPostNextAt *time.Time `json:"asset_post_next_at,omitempty" description:"资产后处理下次自动重试时间"`
SubmitterID uint `json:"submitter_id" description:"提交人账号ID"`
SubmitterName string `json:"submitter_name" description:"提交人账号名称"`
ApprovalInstanceID *uint `json:"approval_instance_id,omitempty" description:"通用审批实例ID"`
ApprovalProvider string `json:"approval_provider,omitempty" description:"审批渠道,企业微信为wecom"`
ApprovalStatus *int `json:"approval_status,omitempty" description:"审批状态"`
ApprovalStatusName string `json:"approval_status_name,omitempty" description:"审批状态名称(中文)"`
ApprovalSubmissionStatus *int `json:"submission_status,omitempty" description:"企业微信审批提交状态"`
ApprovalSubmissionStatusName string `json:"submission_status_name,omitempty" description:"企业微信审批提交状态名称(中文)"`
ApprovalRecoverable bool `json:"approval_recoverable" description:"是否允许恢复原审批提交"`
ApprovalFailureSummary string `json:"approval_failure_summary,omitempty" description:"脱敏审批失败摘要"`
ApprovalLastRecoveryAt string `json:"approval_last_recovery_at,omitempty" description:"最近一次审批恢复时间"`
Creator uint `json:"creator" description:"创建人ID"`
Updater uint `json:"updater" description:"更新人ID"`
CreatedAt string `json:"created_at" description:"创建时间"`
UpdatedAt string `json:"updated_at" description:"更新时间"`
}
// RefundListResponse 退款申请列表分页响应
type RefundListResponse struct {
Items []RefundResponse `json:"items" description:"退款申请列表"`
Total int64 `json:"total" description:"总记录数"`
Page int `json:"page" description:"当前页码"`
Size int `json:"size" description:"每页数量"`
}
// RefundAttemptResponse 退款审批尝试响应,材料为本次提交的冻结快照。
type RefundAttemptResponse struct {
ID uint `json:"id" description:"审批尝试记录ID,同时是通用审批业务ID"`
AttemptNo int `json:"attempt_no" description:"第几次提交,从 1 递增"`
Method string `json:"method" description:"本次冻结的退款方式 (original_route:原路退款, customer_account:客户收款信息退款, asset_wallet:退回资产钱包, agent_wallet:退回代理主钱包)"`
MethodName string `json:"method_name" description:"本次冻结的退款方式中文名称"`
RefundAmount int64 `json:"refund_amount" description:"本次提交冻结的申请退款金额(分)"`
FrozenActualReceivedAmount int64 `json:"frozen_actual_received_amount" description:"本次提交冻结的权威实收金额(分)"`
RefundReason string `json:"refund_reason" description:"本次提交的退款原因"`
Remark string `json:"remark" description:"本次提交冻结的申请人备注快照,未填写时为空;与审批备注(详情响应中的 remark)不是同一字段"`
CustomerAccountInfo string `json:"customer_account_info" description:"本次冻结的客户收款信息快照,非客户收款信息退款方式为空"`
CustomerVoucherKey []string `json:"customer_voucher_key" description:"客户收款凭证对象存储file_key列表,仅返回对象键引用"`
ChannelRefundRequestNo string `json:"channel_refund_request_no" description:"本次提交使用的渠道退款请求号快照,用于幂等与对账"`
SubmittedByAccountID uint `json:"submitted_by_account_id" description:"本次实际提交账号ID"`
ApprovalInstanceID uint `json:"approval_instance_id" description:"本次尝试关联的通用审批实例ID,0 表示未关联"`
ApprovalStatus *int `json:"approval_status,omitempty" description:"通用审批实例状态 (0:提交中, 1:审批中, 2:已通过, 3:已拒绝, 4:已撤销, 5:通过后撤销, 6:已删除, 7:提交失败, 8:提交结果未知)"`
ApprovalStatusName string `json:"approval_status_name" description:"通用审批实例状态中文名称"`
CreatedAt string `json:"created_at" description:"创建时间"`
}
// RefundOrderOptionsRequest 按来源订单查询可选退款方式请求。
type RefundOrderOptionsRequest struct {
OrderID uint `json:"order_id" query:"order_id" validate:"required,min=1" required:"true" minimum:"1" description:"来源订单ID"`
}
// RefundMethodOptionResponse 描述一种可选退款方式及其当前可用性。
type RefundMethodOptionResponse struct {
Method string `json:"method" description:"退款方式 (original_route:原路退款, customer_account:客户收款信息退款, asset_wallet:退回资产钱包, agent_wallet:退回代理主钱包)"`
MethodName string `json:"method_name" description:"退款方式中文名称"`
Available bool `json:"available" description:"该方式当前是否可用"`
Unavailable string `json:"unavailable_reason" description:"不可用原因;可用时为空字符串"`
}
// RefundCapabilityResponse 是原路退款能力校验结果投影。
type RefundCapabilityResponse struct {
Available bool `json:"available" description:"原路退款能力校验是否通过(凭证完整性、服务商类型与渠道可退时限)"`
Unavailable string `json:"unavailable_reason" description:"不通过原因;通过时为空字符串"`
}
// RefundOrderOptionsResponse 按来源订单查询可选退款方式响应。
type RefundOrderOptionsResponse struct {
OrderID uint `json:"order_id" description:"来源订单ID"`
OrderNo string `json:"order_no" description:"来源订单号"`
OrderPaymentType string `json:"order_payment_type" description:"来源订单支付方式 (wechat:微信, alipay:支付宝, wallet:钱包, offline:后台线下)"`
Methods []RefundMethodOptionResponse `json:"methods" description:"可选退款方式集合,含每种方式的可用性与不可用原因;判定事实缺失时为空数组"`
MerchantID *uint `json:"merchant_id,omitempty" description:"原收款商户ID,无实际收款商户时为空"`
MerchantName string `json:"merchant_name" description:"原收款商户名称快照,无实际收款商户时为空字符串"`
OriginalChannelTradeNo string `json:"original_channel_trade_no" description:"原支付渠道交易流水号,无线上支付记录时为空字符串"`
RefundCapability RefundCapabilityResponse `json:"refund_capability" description:"原路退款能力校验结果"`
// UnavailableReason 在判定所需事实缺失(如无原成功支付记录或订单支付方式不支持退款)时说明原因,
// 使调用方得到不可用原因而不是请求失败。
UnavailableReason string `json:"unavailable_reason" description:"整体判定不可用时的原因,可用时为空字符串"`
}
// OfflineSettlementRequest 登记或更正线下退款处理流水号请求。
type OfflineSettlementRequest struct {
ID uint `json:"-" params:"id" path:"id" validate:"required" description:"退款申请ID"`
// OfflineSettlementNo 记录财务实际执行的线下退款处理流水号或凭证编号;重复提交即更正,历史值由审计留存。
OfflineSettlementNo string `json:"offline_settlement_no" validate:"required,max=128" required:"true" maxLength:"128" description:"线下退款处理流水号或凭证编号(必填,去除首尾空白后不得为空)"`
}