feat(order): complete 7.1 order full lifecycle state transitions, 15m/30m/48h auto-timeouts, expert reject/apply-complete, and partial refund arbitration

这个提交包含在:
xcbyy
2026-10-14 15:52:56 +08:00
父节点 a14f27d641
当前提交 3bf38db8b6
共有 10 个文件被更改,包括 551 次插入 和 34 次删除

查看文件

@@ -242,9 +242,22 @@ export function formatOrderData(raw) {
let statusKey = raw.status || 'IN_SERVICE';
if (statusKey === '待支付' || statusKey === '待付款') statusKey = 'PENDING_PAY';
else if (statusKey === '已到场核销' || statusKey === '履约中' || statusKey === '服务中') statusKey = 'IN_SERVICE';
else if (
statusKey === '待接单' ||
statusKey === '已接单' ||
statusKey === '已到场核销' ||
statusKey === '履约中' ||
statusKey === '服务中' ||
statusKey === '待确认'
) statusKey = 'IN_SERVICE';
else if (statusKey === '已完成') statusKey = 'COMPLETED';
else if (statusKey === '争议仲裁中' || statusKey === '退款中' || statusKey === '已退款' || statusKey === '已全额退款') statusKey = 'REFUNDING';
else if (
statusKey === '争议仲裁中' ||
statusKey === '退款中' ||
statusKey === '已退款' ||
statusKey === '已全额退款' ||
statusKey === '已部分退款'
) statusKey = 'REFUNDING';
else if (statusKey === '已取消') statusKey = 'CANCELLED';
const companionAvatar = raw.expertAvatar || raw.expert_avatar || raw.avatar ||
@@ -265,6 +278,7 @@ export function formatOrderData(raw) {
serviceTitle: raw.serviceTitle || raw.service_title || `${game} · 专业陪伴服务`,
quantity: Number(raw.durationHours || raw.duration_hours || raw.quantity || 2),
totalAmount: Number(raw.amount || raw.totalAmount || 0),
refundAmount: Number(raw.refundAmount || raw.refund_amount || 0),
status: statusKey,
rawStatus: raw.status || '履约中',
meetVenue: raw.location || raw.meetVenue || '北京市公开商圈',
@@ -332,7 +346,33 @@ export async function payOrder(orderId) {
}
/**
* 确认验收订单
* 大神拒单自动退款 (待接单 -> 已退款)
*/
export async function rejectOrder(orderId, payload = {}) {
try {
const res = await http.post(`/orders/${orderId}/reject`, payload);
return formatOrderData(res);
} catch (err) {
console.warn('[API] 拒单接口网络异常:', err.message);
throw err;
}
}
/**
* 大神点击申请完成服务 (服务中 -> 待确认)
*/
export async function applyCompleteOrder(orderId, payload = {}) {
try {
const res = await http.post(`/orders/${orderId}/apply-complete`, payload);
return formatOrderData(res);
} catch (err) {
console.warn('[API] 申请完成服务网络异常:', err.message);
throw err;
}
}
/**
* 买家确认验收订单 (待确认 -> 已完成)
*/
export async function completeOrder(orderId) {
try {

查看文件

@@ -300,7 +300,10 @@
<text class="pool-desc">{{ order.desc }}</text>
<view class="pool-foot">
<text class="pool-time">倒计时: {{ order.countdown }}s</text>
<button class="btn-grab btn-active" @click="grabOrder(order)">立即抢单</button>
<view class="pool-btn-group">
<button class="btn-reject btn-active" @click="handleRejectOrder(order)">拒单</button>
<button class="btn-grab btn-active" @click="grabOrder(order)">立即抢单</button>
</view>
</view>
</view>
</view>
@@ -404,6 +407,8 @@ import {
getOrdersPool,
getExpertStats,
claimOrder,
rejectOrder,
applyCompleteOrder,
completeOrder as completeOrderApi,
updateCompanionProfile,
getExpertRevenueTrends,
@@ -763,13 +768,37 @@ const grabOrder = async (order) => {
});
} catch (_) {}
uni.showToast({
title: `抢单成功!已接单 ¥${order.price}`,
title: '抢单成功已接单',
icon: 'success'
});
poolOrders.value = poolOrders.value.filter(o => o.id !== order.id);
loadWorkspaceOrders();
};
const handleRejectOrder = (order) => {
uni.showModal({
title: '确认拒单并退款',
content: '拒单后系统将自动将该订单款项原路全额退还给买家,确定拒单吗?',
confirmColor: '#EF4444',
success: async (res) => {
if (res.confirm) {
try {
await rejectOrder(order.id, {
expertId: expert.value.id || 101,
reason: '大神当前档期冲突拒单'
});
} catch (_) {}
poolOrders.value = poolOrders.value.filter(o => o.id !== order.id);
uni.showToast({
title: '已拒单并自动退款',
icon: 'none'
});
loadWorkspaceOrders();
}
}
});
};
const chatWithBuyer = () => {
uni.showActionSheet({
@@ -798,15 +827,19 @@ const chatWithBuyer = () => {
const completeOrder = (id) => {
uni.showModal({
title: '确认提交服务验收',
content: '服务已完成?提交后将完成服务结算并自动解冻订单资金打入您的佣金钱包。',
content: '服务已完成?提交后订单将进入【待确认】状态,买家确认或48小时后自动解冻资金结算至您的佣金钱包。',
confirmColor: '#10B981',
success: async (res) => {
if (res.confirm) {
try {
await completeOrderApi(id);
} catch (_) {}
await applyCompleteOrder(id, { expertId: expert.value.id || 101 });
} catch (_) {
try {
await completeOrderApi(id);
} catch (_) {}
}
activeOrders.value = activeOrders.value.filter(o => o.id !== id);
uni.showToast({ title: '已验收结算并入账!', icon: 'success' });
uni.showToast({ title: '已提交待买家确认', icon: 'success' });
loadWorkspaceOrders();
}
}
@@ -1698,6 +1731,26 @@ const switchBackToPlayer = () => {
font-variant-numeric: tabular-nums;
}
.pool-btn-group {
display: flex;
align-items: center;
gap: 12rpx;
}
.btn-reject {
background: rgba(239, 68, 68, 0.14);
border: 1px solid rgba(239, 68, 68, 0.35);
color: #F87171;
font-size: 22rpx;
font-weight: 600;
border-radius: 28rpx;
height: 56rpx;
line-height: 56rpx;
padding: 0 22rpx;
margin: 0;
&::after { display: none; }
}
.btn-grab {
background: linear-gradient(135deg, #6366F1 0%, #8B5CF6 100%);
color: #FFF;
@@ -1709,6 +1762,7 @@ const switchBackToPlayer = () => {
padding: 0 28rpx;
margin: 0;
box-shadow: 0 4rpx 16rpx rgba(99, 102, 241, 0.4);
&::after { display: none; }
}
}
}

查看文件

@@ -466,8 +466,11 @@ const goToExplore = () => {
};
const getStatusText = (status, order) => {
if (order && order.rawStatus === '已退款') return '已全额退款';
if (order && (order.rawStatus === '已退款' || order.rawStatus === '已全额退款')) return '已全额退款';
if (order && order.rawStatus === '已部分退款') return '已部分退款';
if (order && order.rawStatus === '争议仲裁中') return '争议仲裁中';
if (order && order.rawStatus === '待接单') return '待接单';
if (order && order.rawStatus === '待确认') return '待确认验收';
const map = {
PENDING_PAY: '待付款',
IN_SERVICE: '服务中',