This commit is contained in:
@@ -5,6 +5,7 @@ type CreateRefundRequest struct {
|
|||||||
OrderID uint `json:"order_id" validate:"required" required:"true" description:"关联订单ID"`
|
OrderID uint `json:"order_id" validate:"required" required:"true" description:"关联订单ID"`
|
||||||
ActualReceivedAmount int64 `json:"actual_received_amount" validate:"required,min=1" required:"true" minimum:"1" description:"实收金额(分)"`
|
ActualReceivedAmount int64 `json:"actual_received_amount" validate:"required,min=1" required:"true" minimum:"1" description:"实收金额(分)"`
|
||||||
RequestedRefundAmount int64 `json:"requested_refund_amount" validate:"required,min=1" required:"true" minimum:"1" description:"申请退款金额(分)"`
|
RequestedRefundAmount int64 `json:"requested_refund_amount" validate:"required,min=1" required:"true" minimum:"1" description:"申请退款金额(分)"`
|
||||||
|
RefundVoucherKey string `json:"refund_voucher_key" validate:"required,min=1,max=500" required:"true" minLength:"1" maxLength:"500" description:"退款凭证对象存储file_key(通过/storage/upload-url上传图片后获得)"`
|
||||||
RefundReason string `json:"refund_reason" validate:"omitempty,max=1000" maxLength:"1000" description:"退款原因"`
|
RefundReason string `json:"refund_reason" validate:"omitempty,max=1000" maxLength:"1000" description:"退款原因"`
|
||||||
PackageUsageID *uint `json:"package_usage_id" validate:"omitempty" description:"关联套餐使用记录ID(可选)"`
|
PackageUsageID *uint `json:"package_usage_id" validate:"omitempty" description:"关联套餐使用记录ID(可选)"`
|
||||||
}
|
}
|
||||||
@@ -26,6 +27,7 @@ type ResubmitRefundRequest struct {
|
|||||||
ID uint `json:"-" params:"id" path:"id" validate:"required" description:"退款申请ID"`
|
ID uint `json:"-" params:"id" path:"id" validate:"required" description:"退款申请ID"`
|
||||||
ActualReceivedAmount *int64 `json:"actual_received_amount" validate:"omitempty,min=1" minimum:"1" description:"实收金额(分)"`
|
ActualReceivedAmount *int64 `json:"actual_received_amount" validate:"omitempty,min=1" minimum:"1" description:"实收金额(分)"`
|
||||||
RequestedRefundAmount *int64 `json:"requested_refund_amount" validate:"omitempty,min=1" minimum:"1" description:"申请退款金额(分)"`
|
RequestedRefundAmount *int64 `json:"requested_refund_amount" validate:"omitempty,min=1" minimum:"1" description:"申请退款金额(分)"`
|
||||||
|
RefundVoucherKey *string `json:"refund_voucher_key" validate:"omitempty,max=500" maxLength:"500" description:"退款凭证对象存储file_key(重新提交时可替换;历史记录缺失时必填)"`
|
||||||
RefundReason *string `json:"refund_reason" validate:"omitempty,max=1000" maxLength:"1000" description:"退款原因"`
|
RefundReason *string `json:"refund_reason" validate:"omitempty,max=1000" maxLength:"1000" description:"退款原因"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,6 +69,7 @@ type RefundResponse struct {
|
|||||||
ActualReceivedAmount int64 `json:"actual_received_amount" description:"实收金额(分)"`
|
ActualReceivedAmount int64 `json:"actual_received_amount" description:"实收金额(分)"`
|
||||||
RequestedRefundAmount int64 `json:"requested_refund_amount" description:"申请退款金额(分)"`
|
RequestedRefundAmount int64 `json:"requested_refund_amount" description:"申请退款金额(分)"`
|
||||||
ApprovedRefundAmount *int64 `json:"approved_refund_amount,omitempty" description:"审批实际退款金额(分)"`
|
ApprovedRefundAmount *int64 `json:"approved_refund_amount,omitempty" description:"审批实际退款金额(分)"`
|
||||||
|
RefundVoucherKey string `json:"refund_voucher_key,omitempty" description:"退款凭证对象存储file_key"`
|
||||||
RefundReason string `json:"refund_reason" description:"退款原因"`
|
RefundReason string `json:"refund_reason" description:"退款原因"`
|
||||||
Status int `json:"status" description:"状态 (1:待审批, 2:已通过, 3:已拒绝, 4:已退回)"`
|
Status int `json:"status" description:"状态 (1:待审批, 2:已通过, 3:已拒绝, 4:已退回)"`
|
||||||
StatusName string `json:"status_name" description:"状态名称(中文)"`
|
StatusName string `json:"status_name" description:"状态名称(中文)"`
|
||||||
|
|||||||
@@ -30,9 +30,10 @@ type RefundRequest struct {
|
|||||||
RequestedRefundAmount int64 `gorm:"column:requested_refund_amount;type:bigint;not null;comment:申请退款金额(分)" json:"requested_refund_amount"`
|
RequestedRefundAmount int64 `gorm:"column:requested_refund_amount;type:bigint;not null;comment:申请退款金额(分)" json:"requested_refund_amount"`
|
||||||
ApprovedRefundAmount *int64 `gorm:"column:approved_refund_amount;type:bigint;comment:审批实际退款金额(分)" json:"approved_refund_amount,omitempty"`
|
ApprovedRefundAmount *int64 `gorm:"column:approved_refund_amount;type:bigint;comment:审批实际退款金额(分)" json:"approved_refund_amount,omitempty"`
|
||||||
|
|
||||||
// 退款原因和状态
|
// 退款凭证、原因和状态
|
||||||
RefundReason string `gorm:"column:refund_reason;type:text;comment:退款原因" json:"refund_reason"`
|
RefundVoucherKey string `gorm:"column:refund_voucher_key;type:varchar(500);not null;default:'';comment:退款凭证对象存储file_key" json:"refund_voucher_key,omitempty"`
|
||||||
Status int `gorm:"column:status;type:int;not null;default:1;index;comment:状态 1-待审批 2-已通过 3-已拒绝 4-已退回" json:"status"`
|
RefundReason string `gorm:"column:refund_reason;type:text;comment:退款原因" json:"refund_reason"`
|
||||||
|
Status int `gorm:"column:status;type:int;not null;default:1;index;comment:状态 1-待审批 2-已通过 3-已拒绝 4-已退回" json:"status"`
|
||||||
|
|
||||||
// 审批信息
|
// 审批信息
|
||||||
ProcessorID *uint `gorm:"column:processor_id;comment:审批人ID" json:"processor_id,omitempty"`
|
ProcessorID *uint `gorm:"column:processor_id;comment:审批人ID" json:"processor_id,omitempty"`
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@@ -99,6 +100,10 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateRefundRequest) (*dt
|
|||||||
if err := validateRequestedRefundAmountByOrder(req.RequestedRefundAmount, order); err != nil {
|
if err := validateRequestedRefundAmountByOrder(req.RequestedRefundAmount, order); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
refundVoucherKey, err := normalizeRefundVoucherKey(req.RefundVoucherKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否存在活跃退款申请(待审批、已通过、已退回状态)
|
// 检查是否存在活跃退款申请(待审批、已通过、已退回状态)
|
||||||
existing, err := s.refundStore.FindActiveByOrderID(ctx, req.OrderID)
|
existing, err := s.refundStore.FindActiveByOrderID(ctx, req.OrderID)
|
||||||
@@ -126,6 +131,7 @@ func (s *Service) Create(ctx context.Context, req *dto.CreateRefundRequest) (*dt
|
|||||||
ShopID: shopID,
|
ShopID: shopID,
|
||||||
ActualReceivedAmount: req.ActualReceivedAmount,
|
ActualReceivedAmount: req.ActualReceivedAmount,
|
||||||
RequestedRefundAmount: req.RequestedRefundAmount,
|
RequestedRefundAmount: req.RequestedRefundAmount,
|
||||||
|
RefundVoucherKey: refundVoucherKey,
|
||||||
RefundReason: req.RefundReason,
|
RefundReason: req.RefundReason,
|
||||||
Status: model.RefundStatusPending,
|
Status: model.RefundStatusPending,
|
||||||
}
|
}
|
||||||
@@ -606,6 +612,14 @@ func (s *Service) Resubmit(ctx context.Context, id uint, req *dto.ResubmitRefund
|
|||||||
if req.RequestedRefundAmount != nil {
|
if req.RequestedRefundAmount != nil {
|
||||||
requestedRefundAmount = *req.RequestedRefundAmount
|
requestedRefundAmount = *req.RequestedRefundAmount
|
||||||
}
|
}
|
||||||
|
refundVoucherKey := refund.RefundVoucherKey
|
||||||
|
if req.RefundVoucherKey != nil {
|
||||||
|
refundVoucherKey = *req.RefundVoucherKey
|
||||||
|
}
|
||||||
|
refundVoucherKey, err = normalizeRefundVoucherKey(refundVoucherKey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
order, err := s.orderStore.GetByID(ctx, refund.OrderID)
|
order, err := s.orderStore.GetByID(ctx, refund.OrderID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -627,6 +641,9 @@ func (s *Service) Resubmit(ctx context.Context, id uint, req *dto.ResubmitRefund
|
|||||||
if req.RequestedRefundAmount != nil {
|
if req.RequestedRefundAmount != nil {
|
||||||
updates["requested_refund_amount"] = *req.RequestedRefundAmount
|
updates["requested_refund_amount"] = *req.RequestedRefundAmount
|
||||||
}
|
}
|
||||||
|
if req.RefundVoucherKey != nil {
|
||||||
|
updates["refund_voucher_key"] = refundVoucherKey
|
||||||
|
}
|
||||||
if req.RefundReason != nil {
|
if req.RefundReason != nil {
|
||||||
updates["refund_reason"] = *req.RefundReason
|
updates["refund_reason"] = *req.RefundReason
|
||||||
}
|
}
|
||||||
@@ -892,6 +909,17 @@ func validateApprovedRefundAmount(approvedAmount int64, requestedRefundAmount in
|
|||||||
return validateRequestedRefundAmountByOrder(approvedAmount, order)
|
return validateRequestedRefundAmountByOrder(approvedAmount, order)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeRefundVoucherKey(voucherKey string) (string, error) {
|
||||||
|
normalized := strings.TrimSpace(voucherKey)
|
||||||
|
if normalized == "" {
|
||||||
|
return "", errors.New(errors.CodeInvalidParam, "退款申请必须上传退款凭证")
|
||||||
|
}
|
||||||
|
if len(normalized) > 500 {
|
||||||
|
return "", errors.New(errors.CodeInvalidParam, "退款凭证长度不能超过500")
|
||||||
|
}
|
||||||
|
return normalized, nil
|
||||||
|
}
|
||||||
|
|
||||||
// buildRefundResponse 将退款 Model 转换为 DTO 响应
|
// buildRefundResponse 将退款 Model 转换为 DTO 响应
|
||||||
func buildRefundResponse(r *model.RefundRequest) *dto.RefundResponse {
|
func buildRefundResponse(r *model.RefundRequest) *dto.RefundResponse {
|
||||||
assetType := ""
|
assetType := ""
|
||||||
@@ -916,6 +944,7 @@ func buildRefundResponse(r *model.RefundRequest) *dto.RefundResponse {
|
|||||||
ActualReceivedAmount: r.ActualReceivedAmount,
|
ActualReceivedAmount: r.ActualReceivedAmount,
|
||||||
RequestedRefundAmount: r.RequestedRefundAmount,
|
RequestedRefundAmount: r.RequestedRefundAmount,
|
||||||
ApprovedRefundAmount: r.ApprovedRefundAmount,
|
ApprovedRefundAmount: r.ApprovedRefundAmount,
|
||||||
|
RefundVoucherKey: r.RefundVoucherKey,
|
||||||
RefundReason: r.RefundReason,
|
RefundReason: r.RefundReason,
|
||||||
Status: r.Status,
|
Status: r.Status,
|
||||||
StatusName: constants.GetRefundStatusName(r.Status),
|
StatusName: constants.GetRefundStatusName(r.Status),
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE tb_iot_card
|
||||||
|
DROP COLUMN IF EXISTS gateway_card_imei;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
-- 为 tb_iot_card 新增 gateway_card_imei 字段
|
||||||
|
-- 该 IMEI 来自 Gateway 卡状态接口,属于插拔卡业务,与设备自身 IMEI 无关,仅作数据同步落库
|
||||||
|
ALTER TABLE tb_iot_card
|
||||||
|
ADD COLUMN IF NOT EXISTS gateway_card_imei VARCHAR(50) NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_iot_card.gateway_card_imei IS '插拔卡业务 IMEI,网关卡状态接口同步,非设备 IMEI';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE tb_refund_request
|
||||||
|
DROP COLUMN IF EXISTS refund_voucher_key;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE tb_refund_request
|
||||||
|
ADD COLUMN IF NOT EXISTS refund_voucher_key VARCHAR(500) NOT NULL DEFAULT '';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN tb_refund_request.refund_voucher_key IS '退款凭证对象存储file_key';
|
||||||
Reference in New Issue
Block a user