shinnosuke
2026-08-31 dfdaa3e75a5d2e8779c8463673484eeea1f1e813
fix: show hotel free stay verification records in merchant orders
4个文件已修改
75 ■■■■■ 已修改文件
rencaika_merchant/pages/free-stay-confirm/free-stay-confirm.uvue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika_merchant/pages/orders/orders.uvue 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika_merchant/pages/verification-success/verification-success.uvue 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantWorkService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
rencaika_merchant/pages/free-stay-confirm/free-stay-confirm.uvue
@@ -60,9 +60,10 @@
            success: (modal: any) => {
                if (!modal.confirm) return
                submitting.value = true
                confirmVerificationCode(verificationId.value, 'merchant-free-stay-' + verificationId.value + '-' + Date.now(), (_result: any, _traceId: string) => {
                confirmVerificationCode(verificationId.value, 'merchant-free-stay-' + verificationId.value + '-' + Date.now(), (result: any, _traceId: string) => {
                    submitting.value = false
                    uni.redirectTo({ url: '/pages/verification-success/verification-success?orderNo=' + encodeURIComponent('免费入住券') })
                    const verifyNo = result != null && result.verifyNo != null ? '' + result.verifyNo : ''
                    uni.redirectTo({ url: '/pages/verification-success/verification-success?orderNo=' + encodeURIComponent(verifyNo) + '&credentialType=FREE_STAY_CODE' })
                }, (error: any) => {
                    submitting.value = false
                    uni.showToast({ title: error != null && error.message != null ? '' + error.message : '核销失败,请重试', icon: 'none' })
rencaika_merchant/pages/orders/orders.uvue
@@ -283,7 +283,6 @@
    const formatDateTime = (value: string): string => value.length > 0 ? value.replace('T', ' ') : '—'
    const openOrderItem = (order: MerchantOrder) => {
        if (order.category == 'stay') return
        const query = 'id=' + encodeURIComponent(order.id) + '&orderNo=' + encodeURIComponent(order.orderNo)
        if (order.status == 'pending_verification') {
            uni.navigateTo({ url: '/pages/verification-detail/verification-detail?' + query })
rencaika_merchant/pages/verification-success/verification-success.uvue
@@ -1,13 +1,17 @@
<template>
    <view class="page">
        <view class="navigation-bar"><view class="back-button" @tap="returnToScan"><image class="back-icon" src="/static/icons/lucide/back-left.svg" mode="aspectFit"></image></view></view>
        <view class="result-content"><view class="success-shell"><image class="success-icon" src="/static/icons/lucide/badge-check-orange.svg" mode="aspectFit"></image></view><text class="result-title">核销成功</text><text class="result-message">订单号:{{orderNo}}</text><view class="return-button" @tap="viewOrder"><text>查看订单</text></view></view>
        <view class="result-content"><view class="success-shell"><image class="success-icon" src="/static/icons/lucide/badge-check-orange.svg" mode="aspectFit"></image></view><text class="result-title">核销成功</text><text class="result-message">{{isFreeStay ? '核销单号:' : '订单号:'}}{{orderNo}}</text><view class="return-button" @tap="viewOrder"><text>查看订单</text></view></view>
    </view>
</template>
<script setup lang="uts">
    const orderNo = ref('-')
    onLoad((options: any) => { if (options != null && options.orderNo != null && ('' + options.orderNo).length > 0) orderNo.value = decodeURIComponent('' + options.orderNo) })
    const isFreeStay = ref(false)
    onLoad((options: any) => {
        if (options != null && options.orderNo != null && ('' + options.orderNo).length > 0) orderNo.value = decodeURIComponent('' + options.orderNo)
        isFreeStay.value = options != null && options.credentialType != null && decodeURIComponent('' + options.credentialType) == 'FREE_STAY_CODE'
    })
    const viewOrder = () => {
        if (orderNo.value == '-' || orderNo.value.length == 0) {
            uni.showToast({ title: '订单号缺失', icon: 'none' })
talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantWorkService.java
@@ -39,6 +39,9 @@
    @Transactional(readOnly = true)
    public PageResult<Map<String, Object>> orders(Long merchantId, Long storeId, int page, int size, MerchantOrderQuery query) {
        if (query != null && "stay".equals(normalize(query.category()))) {
            return freeStayOrders(merchantId, storeId, page, size, query);
        }
        int safePage = Math.max(page, 1);
        int safeSize = Math.min(Math.max(size, 1), 100);
        OrderFilters filters = filters(query);
@@ -92,11 +95,67 @@
                where o.deleted = 0 and o.order_no = ? and o.merchant_id = ? and (o.store_id is null or o.store_id = ?)
                """, orderNo, merchantId, storeId);
        if (rows.isEmpty()) {
            throw new BusinessException(ErrorCode.NOT_FOUND, "订单不存在");
            List<Map<String, Object>> freeStayRows = query("""
                    select r.id, r.verify_no as order_no, r.talent_id, tu.name as talent_name, tu.mobile as talent_mobile, tu.gender,
                           r.card_id, tc.card_no, tc.card_level, r.merchant_id, m.merchant_name, m.merchant_rights_config,
                           '免费入住' as benefit_name, '1天免费入住' as product_name_snapshot,
                           'FREE_STAY' as order_type, 0 as order_amount, 0 as base_pay_amount, 0 as coupon_amount,
                           r.subsidy_amount, 0 as pay_amount, 1 as order_status, 1 as verify_status,
                           r.verify_time as created_at, r.verify_time, r.status as verify_record_status
                    from verify_record r
                    left join talent_user tu on tu.id = r.talent_id
                    left join talent_card tc on tc.id = r.card_id
                    left join merchant m on m.id = r.merchant_id
                    where r.verify_no = ? and r.verify_type = 'HOTEL_FREE' and r.status = 1
                      and r.merchant_id = ? and (r.store_id is null or r.store_id = ?)
                    limit 1
                    """, orderNo, merchantId, storeId);
            if (freeStayRows.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "订单不存在");
            return enrichOrder(freeStayRows.get(0));
        }
        return enrichOrder(rows.get(0));
    }
    private PageResult<Map<String, Object>> freeStayOrders(Long merchantId, Long storeId, int page, int size, MerchantOrderQuery query) {
        int safePage = Math.max(page, 1);
        int safeSize = Math.min(Math.max(size, 1), 100);
        StringBuilder condition = new StringBuilder(" where r.verify_type = 'HOTEL_FREE' and r.status = 1 and r.merchant_id = ? and (r.store_id is null or r.store_id = ?)");
        List<Object> arguments = new ArrayList<>();
        arguments.add(merchantId);
        arguments.add(storeId);
        LocalDate startDate = date(query.startDate(), "开始日期");
        LocalDate endDate = date(query.endDate(), "结束日期");
        if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "开始日期不能晚于结束日期");
        }
        if (startDate != null) {
            condition.append(" and r.verify_time >= ?");
            arguments.add(Timestamp.valueOf(startDate.atStartOfDay()));
        }
        if (endDate != null) {
            condition.append(" and r.verify_time < ?");
            arguments.add(Timestamp.valueOf(endDate.plusDays(1).atStartOfDay()));
        }
        String select = """
                select r.id, r.verify_no as order_no, r.talent_id, tu.name as talent_name, tu.mobile as talent_mobile, tu.gender,
                       r.card_id, tc.card_level, r.merchant_id, m.merchant_name,
                       '免费入住' as benefit_name, '1天免费入住' as product_name_snapshot,
                       'FREE_STAY' as order_type, 0 as order_amount, 0 as base_pay_amount, 0 as coupon_amount,
                       r.subsidy_amount, 0 as pay_amount, 1 as order_status, 1 as verify_status,
                       r.verify_time as created_at, r.verify_time
                from verify_record r
                left join talent_user tu on tu.id = r.talent_id
                left join talent_card tc on tc.id = r.card_id
                left join merchant m on m.id = r.merchant_id
                """ + condition + " order by r.verify_time desc, r.id desc limit ? offset ?";
        List<Object> recordArguments = new ArrayList<>(arguments);
        recordArguments.add(safeSize);
        recordArguments.add((safePage - 1) * safeSize);
        List<Map<String, Object>> records = query(select, recordArguments.toArray()).stream().map(this::toListItem).toList();
        Long total = jdbcTemplate.queryForObject("select count(*) from verify_record r" + condition, Long.class, arguments.toArray());
        return new PageResult<>(records, safePage, safeSize, total == null ? 0 : total);
    }
    @Transactional
    public Map<String, Object> verify(String orderNo, MerchantPrincipal principal, VerifyRequest request, String idempotencyKey) {
        if (idempotencyKey == null || idempotencyKey.isBlank()) {