Files
medical-mall/docs/home-service/FIX_service_detail_no_payment_page.md

11 KiB
Raw Blame History

问题分析service-detail.uvue 点击"提交预约"后未出现付款页面

文件:医疗-consumer/pages/mall/consumer/home-service/service-detail.uvue 关联文件:order-detail.uvuepayment.uvueserviceOrderService.utshomeServiceService.uts 日期2026-06-02


一、现象描述

用户在 service-detail.uvue(服务单详情/预约页)填写完地址、时间、服务对象等信息后,点击底部"提交预约"按钮:

  • 页面提示"预约已提交"
  • 自动跳转到 order-detail.uvue(服务单详情页)
  • 订单状态显示为 "待付款"
  • 但用户从未进入过付款页面 (payment.uvue)
  • order-detail.uvue 中也找不到任何"去付款"按钮

二、根因分析(代码层面)

2.1 问题点 ①submitBooking 后直接跳 order-detail未经过 payment

文件service-detail.uvue 第 933~1012 行

async function submitBooking() {
    // ... 表单校验 ...
    const created = await createHomeServiceApplication(draft)
    if (created == null) {
        uni.showToast({ title: '预约提交失败,请稍后重试', icon: 'none' })
        return
    }
    uni.showToast({ title: '预约已提交', icon: 'success' })
    // ❌ 直接跳转到订单详情,没有经过付款页
    uni.navigateTo({ url: '/pages/mall/consumer/home-service/order-detail?id=' + created.id })
}

预期行为:预约提交成功后,应携带订单信息跳转到 payment.uvue,让用户完成支付。

实际行为:直接 uni.navigateToorder-detail.uvue,付款流程被完全跳过。


2.2 问题点 ②order-detail.uvue 没有"去付款"按钮

文件order-detail.uvue 第 89~96 行

<view class="action-row">
    <view v-if="detail.statusText == '派单未成功'" class="secondary-btn" @click="bookAgain">再次预约</view>
    <view v-else class="secondary-btn" @click="bookAgain">再次预约</view>
    <view v-if="detail.status == 'pending_acceptance'" class="primary-btn" @click="goFeedback">去验收反馈</view>
    <view v-else-if="consumerViewState.showRescheduleBtn" class="primary-btn" @click="bookAgain">重新选择时间</view>
    <view v-else-if="detail.statusText == '派单未成功'" class="primary-btn" @click="retryDispatch">重新派单</view>
    <view v-else class="primary-btn" @click="contactService">联系客服</view>
</view>

缺失:当订单状态为 "待付款"paymentStatus == 1 && status == 'created'action row 没有任何"去付款"或"立即支付"的入口。

用户一旦从预约页离开,再回到订单详情,也无法完成付款。


2.3 问题点 ③createServiceOrder 的双轨逻辑

文件serviceOrderService.uts 第 1125~1179 行

export async function createServiceOrder(params: CreateServiceOrderParams): Promise<ServiceOrderType | null> {
    // 非套餐单 → 走 ec_care_tasks 新链(前端直接 INSERT无支付概念
    if (params.packageInfo.id == '') {
        const newTask = await tryCreateCareTask(params)
        if (newTask != null) {
            return newTask
        }
    }
    // 套餐单 → 走 hss_service_orders 旧链(有 payment_status 字段)
    const response = await supa.from('hss_service_orders').insert({
        // ... 包含 payable_amount、payment_status: 1 等字段
        status: 'created',
        payment_status: 1,
        // ...
    }).execute()
}

现状

  • 如果用户选择了套餐(packageInfo.id != ''),订单写入 hss_service_orderspayment_status = 1(未付款)
  • 如果用户没有选择套餐(packageInfo.id == ''),订单写入 ec_care_tasks没有支付概念

截图中的订单有价格¥260且显示"待付款",说明走的是 hss_service_orders 旧链。


2.4 问题点 ④payment.uvue 的 source 推断对 UUID 不友好(潜在风险)

文件payment.uvue 第 1678~1683 行

if (source.value == '') {
    // so- 前缀 → service其他包括 UUID→ goods
    source.value = orderId.value.startsWith('so-') ? 'service' : 'goods'
}

风险:如果未来 service-detail.uvue 创建的是 ec_care_tasksUUID 格式 id跳转到 payment.uvue 时,source 会被误判为 'goods',导致:

  • 支付成功后走商品发货流程,而非服务派单流程
  • dispatchPaidHomecareOrder 对 UUID 订单返回"同步中"提示,但 UI 可能显示商品订单的完成页

三、数据流全景图

用户填写预约信息
       ↓
点击【提交预约】
       ↓
submitBooking()
       ↓
createHomeServiceApplication(draft)
       ↓
createServiceOrder(params)
       ↓
┌─────────────────┬─────────────────┐
│ packageInfo.id  │ packageInfo.id  │
│ == ''           │ != ''           │
│ (非套餐)         │ (套餐)           │
└─────────────────┴─────────────────┘
       ↓                   ↓
tryCreateCareTask    insert hss_service_orders
(insert ec_care_tasks)  payment_status = 1 (未付款)
       ↓                   ↓
   无支付概念            有支付概念
       ↓                   ↓
   但都被直接           但也被直接
   跳 order-detail      跳 order-detail
       ↓                   ↓
   ❌ 无付款入口        ❌ 无付款入口

四、修复方案

方案 A提交预约后自动跳转付款页最符合用户预期

修改文件service-detail.uvue submitBooking()

async function submitBooking() {
    // ... 原有校验逻辑保持不变 ...

    const created = await createHomeServiceApplication(draft)
    if (created == null) {
        uni.showToast({ title: '预约提交失败,请稍后重试', icon: 'none' })
        return
    }

    // 判断是否需要支付(仅 hss_service_orders 旧链有支付概念)
    if (shouldUseCareTaskPath(created.id)) {
        // ec_care_tasks 新链:无支付,直接到详情
        uni.showToast({ title: '预约已提交', icon: 'success' })
        uni.navigateTo({ url: '/pages/mall/consumer/home-service/order-detail?id=' + created.id })
        return
    }

    // hss_service_orders 旧链:需要支付,跳转到 payment.uvue
    uni.showToast({ title: '预约成功,请完成支付', icon: 'success' })
    uni.navigateTo({
        url: '/pages/mall/consumer/payment?orderId=' + created.id
            + '&source=service'
            + '&bizType=service'
            + '&amount=' + created.amount
            // + '&orderNo=' + created.caseNo  // 如需
    })
}

注意事项

  • shouldUseCareTaskPath(id)isUuidLike(id),用于区分 ec_care_tasksUUID和 hss_service_ordersso- 前缀)
  • 如果 created 对象中没有 amount,需要从 draftselectedPackage 中取 price

方案 Border-detail.uvue 增加"去付款"按钮(兜底补偿)

修改文件order-detail.uvue action row

<view class="action-row">
    <view v-if="detail.statusText == '派单未成功'" class="secondary-btn" @click="bookAgain">再次预约</view>
    <view v-else class="secondary-btn" @click="bookAgain">再次预约</view>

    <!-- ✅ 新增:待付款状态显示"去付款" -->
    <view v-if="detail.statusText == '待付款'" class="primary-btn" @click="goPayment">去付款</view>
    <view v-else-if="detail.status == 'pending_acceptance'" class="primary-btn" @click="goFeedback">去验收反馈</view>
    <view v-else-if="consumerViewState.showRescheduleBtn" class="primary-btn" @click="bookAgain">重新选择时间</view>
    <view v-else-if="detail.statusText == '派单未成功'" class="primary-btn" @click="retryDispatch">重新派单</view>
    <view v-else class="primary-btn" @click="contactService">联系客服</view>
</view>

新增方法

function goPayment() {
    if (detail.value == null) {
        return
    }
    uni.navigateTo({
        url: '/pages/mall/consumer/payment?orderId=' + detail.value.id
            + '&source=service'
            + '&bizType=service'
            + '&amount=' + detail.value.amount
    })
}

方案 C推荐组合A + B 同时实施

  • A 解决主流程:用户预约后自然进入支付环节
  • B 解决兜底场景:用户中途退出、支付失败、从订单列表重新进入等情况下,仍能在详情页找到付款入口

五、与刚才完善的双轨逻辑如何配合

5.1 当前已完善的逻辑

  1. dispatchPaidHomecareOrderserviceOrderService.uts

    • 对 UUID 格式订单ec_care_tasks返回 { success: true, code: 'SYNC_IN_PROGRESS', message: '付款成功,服务安排信息正在同步中...' }
    • so- 前缀订单hss_service_orders调用旧 rpc_homecare_auto_dispatch
  2. payment.uvue 支付成功后的派单

    • source == 'service' 时,调用 dispatchPaidHomecareOrder
    • 成功则跳 order-detail
    • 失败则显示重试弹窗

5.2 修复后配合方式

用户提交预约(选择了套餐)
       ↓
createServiceOrder → 写入 hss_service_orderspayment_status=1
       ↓
跳转 payment.uvue携带 orderId + source=service + amount
       ↓
用户完成支付
       ↓
payment.uvue 调用 dispatchPaidHomecareOrder(orderId)
       ↓
orderId 是 so- 前缀 → 调用 rpc_homecare_auto_dispatch旧链派单
       ↓
派单成功 → 跳转 order-detail.uvue
       ↓
用户看到"已派单"或"服务人员已接单"

如果后端补齐了 rpc_payment_callback_activate_care_task

  • 可将 createServiceOrder 改为统一创建 ec_care_tasks
  • payment.uvue 支付成功后,调用新 RPC 激活 care task
  • 届时 service-detail.uvuesubmitBooking 可统一跳 payment.uvue,无需区分 so- / UUID

六、需要修改的文件清单

文件 修改内容 优先级
service-detail.uvue submitBooking() 成功后跳 payment.uvue(旧链订单) P0
order-detail.uvue action row 增加"待付款" → "去付款"按钮 P0
order-detail.uvue 新增 goPayment() 方法 P0
service-detail.uvue 确保 created 对象包含 amount / price,或从 draft 取 P1

七、验证方式

  1. 进入 service-detail.uvue,选择套餐,填写信息,点击"提交预约"
  2. 预期:跳转到 payment.uvue,显示订单金额、支付方式、倒计时
  3. 完成支付
  4. 预期:自动派单,跳转到 order-detail.uvue,状态变为"已派单"或"待接单"
  5. 测试中途退出场景:在 order-detail.uvue 点击"去付款",应能重新进入 payment.uvue