feat(t219): show pending-payment reconciliation

This commit is contained in:
QiuSW
2026-07-28 18:16:09 +08:00
parent c2ec98d5be
commit 78dc595815
20 changed files with 768 additions and 37 deletions
@@ -14,6 +14,7 @@ import (
"testing"
"time"
"cmroubao/backend-api/internal/domain"
"cmroubao/backend-api/internal/usecase"
"github.com/gin-gonic/gin"
@@ -627,6 +628,196 @@ func TestTaskDetailDisablesAuthorizationAfterDelivery(t *testing.T) {
}
}
func TestTaskDetailRendersOrderSubmissionStatesWithoutPaymentActions(
t *testing.T,
) {
now := time.Date(2026, 7, 28, 9, 30, 0, 0, time.UTC)
tests := []struct {
name string
taskStatus string
submission OrderSubmission
expected []string
forbidden []string
}{
{
name: "reconciling",
taskStatus: "WAITING_CONFIRMATION",
submission: OrderSubmission{
Status: "FENCED",
StatusLabel: "正在对账",
FencedAt: now,
},
expected: []string{
"订单提交结果正在对账",
"禁止重复提交",
},
forbidden: []string{"待人工确认付款", "验证完成,未提交订单"},
},
{
name: "manual review",
taskStatus: "WAITING_CONFIRMATION",
submission: OrderSubmission{
Status: "MANUAL_REVIEW",
StatusLabel: "需要人工对账",
ManualReasonLabel: "找到多个可能订单",
FencedAt: now,
ManualReviewAt: now.Add(time.Minute),
},
expected: []string{
"需要人工对账",
"找到多个可能订单",
"禁止重新提交订单",
},
forbidden: []string{"待人工确认付款", "验证完成,未提交订单"},
},
{
name: "pending payment",
taskStatus: "SUCCEEDED",
submission: OrderSubmission{
Status: "RECONCILED",
StatusLabel: "待人工确认付款",
ExpectedTitle: "已授权灰色上衣",
ExpectedSKU: "灰色,2XL",
ExpectedQuantity: 2,
ExpectedUnitPrice: "21.50",
ExpectedTotalPrice: "43.00",
PlatformOrderNo: "12345678901234567890",
PlatformOrderedAt: now,
PlatformOrderStatus: "PENDING_PAYMENT",
EvidenceContentURL: "/api/v1/tasks/" + testTaskID +
"/evidence/00000000-0000-4000-8000-000000000078/content",
FencedAt: now.Add(-time.Minute),
ReconciledAt: now.Add(time.Minute),
},
expected: []string{
"待人工确认付款",
"请采购员打开拼多多订单列表",
"12345678901234567890",
"灰色,2XL",
"¥43.00",
"待付款订单列表对账截图",
},
forbidden: []string{
"验证完成,未提交订单",
"重试提交",
`href="pinduoduo`,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
service := &fakeService{
getResult: Task{
ID: testTaskID,
Title: "订单状态任务",
SKU: "灰色,2XL",
Quantity: 2,
Status: test.taskStatus,
ReferenceAssetID: "00000000-0000-4000-8000-000000000009",
CreatedAt: now,
UpdatedAt: now,
OrderSubmissions: []OrderSubmission{test.submission},
},
}
response := performRequest(
t,
newTestRouter(t, service),
http.MethodGet,
"/tasks/"+testTaskID,
nil,
"",
)
if response.Code != http.StatusOK {
t.Fatalf(
"detail status/body = %d/%s",
response.Code,
response.Body,
)
}
body := response.Body.String()
for _, expected := range test.expected {
if !strings.Contains(body, expected) {
t.Fatalf("detail missing %q: %s", expected, body)
}
}
for _, forbidden := range test.forbidden {
if strings.Contains(body, forbidden) {
t.Fatalf("detail contains forbidden %q", forbidden)
}
}
if regexp.MustCompile(
`(?s)<(?:a|button)[^>]*>[^<]*(?:立即支付|确认支付|自动付款)`,
).MatchString(body) {
t.Fatal("detail exposes a payment action")
}
})
}
}
func TestTaskDetailKeepsHistoricalSucceededCopyWithoutSubmission(t *testing.T) {
now := time.Date(2026, 7, 28, 9, 30, 0, 0, time.UTC)
response := performRequest(
t,
newTestRouter(t, &fakeService{getResult: Task{
ID: testTaskID,
Title: "历史验证任务",
SKU: "HISTORY-SKU",
Quantity: 1,
Status: "SUCCEEDED",
ReferenceAssetID: "00000000-0000-4000-8000-000000000009",
CreatedAt: now,
UpdatedAt: now,
}}),
http.MethodGet,
"/tasks/"+testTaskID,
nil,
"",
)
if response.Code != http.StatusOK ||
!strings.Contains(
response.Body.String(),
"验证完成,未提交订单",
) {
t.Fatalf(
"historical succeeded detail = %d/%s",
response.Code,
response.Body,
)
}
}
func TestOrderSubmissionAdapterBuildsAuthenticatedEvidenceURL(t *testing.T) {
orderNo := "12345678901234567890"
status := "PENDING_PAYMENT"
evidenceID := "00000000-0000-4000-8000-000000000078"
orderedAt := time.Date(2026, 7, 28, 9, 30, 0, 0, time.UTC)
submission := orderSubmissionFrom(domain.OrderSubmission{
ID: "00000000-0000-4000-8000-000000000079",
TaskID: testTaskID,
Status: domain.OrderSubmissionReconciled,
ExpectedTitle: "已授权商品",
ExpectedSKU: "灰色,2XL",
ExpectedQuantity: 2,
ExpectedUnitPriceCents: 2150,
ExpectedTotalPriceCents: 4300,
PlatformOrderNo: &orderNo,
PlatformOrderedAt: &orderedAt,
PlatformOrderStatus: &status,
ReconciliationEvidenceAssetID: &evidenceID,
FencedAt: orderedAt.Add(-time.Minute),
ReconciledAt: &orderedAt,
})
if submission.StatusLabel != "待人工确认付款" ||
submission.PlatformOrderNo != orderNo ||
submission.ExpectedUnitPrice != "21.50" ||
submission.ExpectedTotalPrice != "43.00" ||
submission.EvidenceContentURL !=
"/api/v1/tasks/"+testTaskID+"/evidence/"+evidenceID+"/content" ||
strings.Contains(submission.EvidenceContentURL, orderNo) {
t.Fatalf("submission view = %+v", submission)
}
}
func TestTaskDetailCancelModeFollowsLifecycleStatus(t *testing.T) {
tests := []struct {
status string