feat(order): 重构退款全流程并新增申请退款中状态
- 订单状态新增「6 申请退款中」,用户申请退款原子流转并落待审核退款流水 - 退款失败回退订单至已支付并恢复被取消的权益发放任务 - 新增虚拟支付退款查单兜底任务,反查超时订单并自动订正 - 会员包来源额度不叠加,扣减/回退/回收口径统一为只取生效一份 - 退款回收流水按吊销前后有效剩余差值记账,避免无效回收记录 - 同步更新 README 与 Stitch 设计文档
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/internal/dto"
|
||||
"git.lzcwy.cn/zhengchiliu/zt-closet-backend/pkg/errcode"
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
)
|
||||
|
||||
// TestRefundApplyMarksOrderRefunding 申请退款受理成功:返回待处理退款流水信息,订单 2 -> 6 由 DAO 事务内完成。
|
||||
func TestRefundApplyMarksOrderRefunding(t *testing.T) {
|
||||
mock := setupOrderQuotaTest(t)
|
||||
mock.ExpectQuery("SELECT .* FROM `orders` WHERE order_no = \\?").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "order_no", "user_id", "order_status", "pay_amount", "pay_channel"}).
|
||||
AddRow(8, "ORDER-1", 7, 2, 990, 1))
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectQuery("SELECT .* FROM `orders` .*FOR UPDATE").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "order_no", "user_id", "order_status", "pay_amount"}).
|
||||
AddRow(8, "ORDER-1", 7, 2, 990))
|
||||
mock.ExpectQuery("SELECT .* FROM `refund_flow_logs`").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id"}))
|
||||
mock.ExpectExec("INSERT INTO `refund_flow_logs`").WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
mock.ExpectExec("UPDATE `orders` SET").WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
mock.ExpectCommit()
|
||||
|
||||
resp, e := NewOrderService().RefundApply(context.Background(), 7,
|
||||
&dto.RefundApplyRequest{OrderNo: "ORDER-1", Reason: "不想要了"})
|
||||
if e != nil {
|
||||
t.Fatalf("unexpected error: %v", e)
|
||||
}
|
||||
if resp == nil || resp.RefundAmount != 990 || resp.Status != RefundStatusPending || resp.RefundNo == "" {
|
||||
t.Fatalf("unexpected response: %+v", resp)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRefundApplyRejectsRepeatApply 重复/并发申请被 DAO 拒绝时映射为「当前状态不允许退款」。
|
||||
func TestRefundApplyRejectsRepeatApply(t *testing.T) {
|
||||
mock := setupOrderQuotaTest(t)
|
||||
mock.ExpectQuery("SELECT .* FROM `orders` WHERE order_no = \\?").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "order_no", "user_id", "order_status", "pay_amount", "pay_channel"}).
|
||||
AddRow(8, "ORDER-1", 7, 2, 990, 1))
|
||||
// 行锁内读到订单已被并发请求置为 6 申请退款中:拒绝且不产生任何写入。
|
||||
mock.ExpectBegin()
|
||||
mock.ExpectQuery("SELECT .* FROM `orders` .*FOR UPDATE").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "order_no", "user_id", "order_status"}).
|
||||
AddRow(8, "ORDER-1", 7, OrderStatusRefunding))
|
||||
mock.ExpectCommit()
|
||||
|
||||
resp, e := NewOrderService().RefundApply(context.Background(), 7,
|
||||
&dto.RefundApplyRequest{OrderNo: "ORDER-1", Reason: "重复申请"})
|
||||
if resp != nil || e != errcode.RefundNotAllowed {
|
||||
t.Fatalf("resp=%v err=%v, want RefundNotAllowed", resp, e)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRefundApplyRejectsIOSVirtualOrder iOS 虚拟支付订单不走本接口,引导用户走 App Store。
|
||||
func TestRefundApplyRejectsIOSVirtualOrder(t *testing.T) {
|
||||
mock := setupOrderQuotaTest(t)
|
||||
mock.ExpectQuery("SELECT .* FROM `orders` WHERE order_no = \\?").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "order_no", "user_id", "order_status", "pay_amount", "pay_channel", "xpay_platform"}).
|
||||
AddRow(8, "ORDER-1", 7, 2, 990, 2, "ios"))
|
||||
|
||||
resp, e := NewOrderService().RefundApply(context.Background(), 7,
|
||||
&dto.RefundApplyRequest{OrderNo: "ORDER-1", Reason: "不想要了"})
|
||||
if resp != nil || e != errcode.IOSRefundViaAppStore {
|
||||
t.Fatalf("resp=%v err=%v, want IOSRefundViaAppStore", resp, e)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRefundApplyRejectsOtherUserOrder 非本人订单不允许申请退款(归属校验先于状态校验)。
|
||||
func TestRefundApplyRejectsOtherUserOrder(t *testing.T) {
|
||||
mock := setupOrderQuotaTest(t)
|
||||
mock.ExpectQuery("SELECT .* FROM `orders` WHERE order_no = \\?").
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "order_no", "user_id", "order_status", "pay_amount", "pay_channel"}).
|
||||
AddRow(8, "ORDER-1", 9, 2, 990, 1))
|
||||
|
||||
resp, e := NewOrderService().RefundApply(context.Background(), 7,
|
||||
&dto.RefundApplyRequest{OrderNo: "ORDER-1", Reason: "越权申请"})
|
||||
if resp != nil || e != errcode.Forbidden {
|
||||
t.Fatalf("resp=%v err=%v, want Forbidden", resp, e)
|
||||
}
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user