yeg
2026-09-16 612e8562925ce73cb3858318175c4e6ca854d0f5
fix(payment): 人才端列表把「已退款」和「未支付取消」分开显示

退款订单在人才端列表里显示成「已取消」——这是上一轮刻意的取舍:列表状态只有
pending_payment / pending_verification / completed / cancelled 这套桶值,退款被我并进了
cancelled 桶(避免给列表的页签与筛选凭空加词),精确状态只在订单状态页(退款处理 / 退款成功)。
于是列表徽标一律渲染成「已取消」,退款中和已退款都看不出来,容易让人以为钱没回来。

改法:桶值不动(页签、筛选、点击跳转都不变),把退款维度带进列表记录,文案在这一桶内细分。

- 后端 UsageRecordPortalService:订单分支加选 o.refund_status(联合查询的另外三个分支补
null as refund_status,保持列数一致),列表记录因此带出 refundStatus。
- 人才端订单列表:statusCode 仍是 cancelled,徽标文案按 refundStatus 细分——1 退款中、
2 已退款、否则仍是已取消;页签「已取消」改为「已取消/已退款」,与商户端口径一致。

测试:PendingRefundConfirmationTests 增至 9 项,新增「列表要能区分未支付取消与钱已退回」,
同时钉住退款中(refundStatus=1 且状态仍是 cancelled)与已退款(refundStatus=2)。
全量 274 项,仅 docs/agents/testing.md 所列 3 项基线失败,无新增。
3个文件已修改
43 ■■■■■ 已修改文件
rencaika/pages/order/order.uvue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/portal/UsageRecordPortalService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/test/java/com/talentcard/server/business/payment/PendingRefundConfirmationTests.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika/pages/order/order.uvue
@@ -48,7 +48,7 @@
        { label: '待支付', value: 'pending_payment' },
        { label: '待核销', value: 'pending_verification' },
        { label: '已完成', value: 'completed' },
        { label: '已取消', value: 'cancelled' },
        { label: '已取消/已退款', value: 'cancelled' },
        { label: '待处理', value: 'processing' },
        { label: '已领取', value: 'received' }
    ]
@@ -125,6 +125,18 @@
        const amount = Number(value)
        return isNaN(amount) ? '' : '¥' + amount.toFixed(2)
    }
    /**
     * 已完成/待核销/待支付之外的订单都归在「已取消」这一桶里,但离开支付流程的原因不同:未支付被取消,
     * 还是钱已经退回来了。退款维度只改这一桶的文案,状态码与点击跳转不变。
     */
    const statusLabel = (status: string, source: any): string => {
        if (status == 'cancelled') {
            const refundStatus = source != null && source.refundStatus != null ? Number(source.refundStatus) : 0
            if (refundStatus == 1) return '退款中'
            if (refundStatus == 2) return '已退款'
        }
        return statusText(status)
    }
    const mapRecord = (source: any): any => {
        const merchantType = source != null && source.merchantType != null ? source.merchantType as string : 'service'
        const status = source != null && source.status != null ? source.status as string : ''
@@ -140,7 +152,7 @@
            merchantType: merchantType,
            time: formatTime(source != null ? source.occurredAt : null),
            statusCode: status,
            status: statusText(status),
            status: statusLabel(status, source),
            amount: formatAmount(source != null ? source.amount : null),
            icon: getMerchantIcon(merchantType),
            iconTone: getMerchantIconTone(merchantType),
talent-card-server/src/main/java/com/talentcard/server/business/portal/UsageRecordPortalService.java
@@ -92,6 +92,7 @@
                         when o.verify_status = 1 then 'completed'
                         else 'pending_verification'
                       end as status,
                       o.refund_status as refund_status,
                       o.pay_amount as amount_cent,
                       'ORDER_DETAIL' as target_detail_type
                from benefit_order o
@@ -120,7 +121,8 @@
                         else 'processing'
                       end as status,
                       null as amount_cent,
                       'SERVICE_APPLICATION_DETAIL' as target_detail_type
                       'SERVICE_APPLICATION_DETAIL' as target_detail_type,
                       null as refund_status
                from service_application s
                where s.deleted = 0
                  and exists (
@@ -139,7 +141,8 @@
                       r.received_at as occurred_at,
                       'received' as status,
                       null as amount_cent,
                       'MOVIE_TICKET_DETAIL' as target_detail_type
                       'MOVIE_TICKET_DETAIL' as target_detail_type,
                       null as refund_status
                from movie_ticket_record r
                left join cinema_coupon_claim cc on cc.claim_no = r.receive_no and cc.deleted = 0
                where r.deleted = 0
@@ -166,7 +169,8 @@
                       cl.created_at as occurred_at,
                       'received' as status,
                       null as amount_cent,
                       'TICKET_PACKAGE_DETAIL' as target_detail_type
                       'TICKET_PACKAGE_DETAIL' as target_detail_type,
                       null as refund_status
                from ticket_package_claim cl
                join ticket_package p on p.id = cl.package_id and p.deleted = 0
                left join merchant m on m.id = cl.merchant_id and m.deleted = 0
talent-card-server/src/test/java/com/talentcard/server/business/payment/PendingRefundConfirmationTests.java
@@ -171,6 +171,23 @@
                .containsEntry("statusText", "已退款");
    }
    @Test
    void theTalentOrderListCarriesTheRefundStateSoARefundIsNotJustAnotherCancellation() {
        RefundingOrder refunded = refundingOrder(2);
        assertThat(((Number) recordOf(refunded.orderNo()).get("refundStatus")).intValue())
                .as("列表要能区分「未支付被取消」和「钱已经退回来」")
                .isEqualTo(2);
        RefundingOrder refunding = refundingOrder(1);
        when(refundGateway.queryRefund(any(), any())).thenReturn(providerResponse(Map.of(
                "trans_stat", "P", "resp_code", "00000100", "sub_resp_desc", "交易处理中", "hf_seq_id", "HF-PENDING-" + suffix)));
        Map<String, Object> refundingRecord = recordOf(refunding.orderNo());
        assertThat(((Number) refundingRecord.get("refundStatus")).intValue()).isEqualTo(1);
        assertThat(refundingRecord).containsEntry("status", "cancelled");
    }
    /** A paid order whose refund was submitted and has no final provider answer yet. */
    private RefundingOrder refundingOrder(int refundStatus) {
        String orderNo = "REFUND-" + suffix + "-" + (++sequence);