fix(first-leg): freeze versioned submission commands across retries

This commit is contained in:
2026-10-02 23:08:58 +08:00
parent 187deb5047
commit e564353a80
6 changed files with 251 additions and 30 deletions
@@ -636,8 +636,8 @@ test('shows billing-weight confirmation only when the backend marks it pending',
test('submits only selected draft orders in one batch action', () => {
assert.match(source, /selectedDraftOrders = computed\(\(\) => selectedOrders\.value\.filter\(\(order\) => order\.status === 'DRAFT'\)\)/)
assert.match(source, /Promise\.allSettled\(drafts\.map\(\(order\) => submitMerchantFirstLegOrder/)
assert.match(source, /createIdempotencyKey\(`first-leg-batch-submit-\$\{order\.id\}`\)/)
assert.match(source, /Promise\.allSettled\(ids\.map\(id => store\.submitSaved/)
assert.doesNotMatch(source, /submitMerchantFirstLegOrder/)
assert.match(source, /v-if="canSubmitOrder"[\s\S]*批量提交草稿/)
})
@@ -29,7 +29,6 @@ import {
getMerchantOrderAddresses,
getMerchantPricingChannels,
importMerchantFirstLegOrders,
submitMerchantFirstLegOrder,
updateMerchantOrderAddress,
uploadMerchantProductImage,
type CustomerOrderAddress,
@@ -237,6 +236,7 @@ const tablePageSize = ref(10)
const downloadingMarks = ref(false)
const exportingOrders = ref(false)
const batchSubmitting = ref(false)
const pendingBatchSubmissionIds = ref<string[]>([])
const selectedOrders = ref<FirstLegOrder[]>([])
const selectedOrderCount = computed(() => selectedOrders.value.length)
const selectedDraftOrders = computed(() => selectedOrders.value.filter((order) => order.status === 'DRAFT'))
@@ -1738,14 +1738,14 @@ function handleOrderSelectionChange(rows: FirstLegOrder[]) {
}
async function submitSelectedDraftOrders() {
const drafts = selectedDraftOrders.value
if (batchSubmitting.value || !drafts.length) return
const ids = [...new Set([...pendingBatchSubmissionIds.value, ...selectedDraftOrders.value.map(order => order.id)])]
if (batchSubmitting.value || !ids.length) return
if (companySubmissionBlocked.value) {
ElMessage.warning('企业资料审核通过后才能提交订单')
return
}
try {
await ElMessageBox.confirm(`确认提交选中的 ${drafts.length} 条草稿订单吗?`, '批量提交订单', {
await ElMessageBox.confirm(`确认提交 ${ids.length} 条草稿订单吗?${pendingBatchSubmissionIds.value.length ? '上次结果未确认的订单将使用原提交命令重试。' : ''}`, '批量提交订单', {
type: 'warning',
confirmButtonText: '确认提交',
cancelButtonText: '取消',
@@ -1757,10 +1757,8 @@ async function submitSelectedDraftOrders() {
batchSubmitting.value = true
try {
const results = await Promise.allSettled(drafts.map((order) => submitMerchantFirstLegOrder(
order.id,
createIdempotencyKey(`first-leg-batch-submit-${order.id}`),
)))
const results = await Promise.allSettled(ids.map(id => store.submitSaved(id, companyAuditLoaded.value ? tenantStore.company.auditStatus : undefined)))
pendingBatchSubmissionIds.value = ids.filter((id, index) => results[index]?.status === 'rejected' && store.hasPendingSubmission(id))
const successCount = results.filter((result) => result.status === 'fulfilled').length
const failures = results.filter((result): result is PromiseRejectedResult => result.status === 'rejected')
await searchOrders(appliedFilters.value)
@@ -2012,6 +2010,7 @@ async function saveDraft() {
if (!await validateEditorForm()) return
saving.value = true
try {
if (!await confirmSubmissionEdit()) return
const order = await store.saveDraft(cloneDraft(form), tenantStore.company.companyName, editingId.value, editingSession.value)
editingId.value = order.id
editingSession.value = store.beginDraftEdit(order)
@@ -2026,6 +2025,20 @@ async function saveDraft() {
}
}
async function confirmSubmissionEdit() {
if (!editingId.value || !store.hasPendingSubmission(editingId.value)) return true
try {
await ElMessageBox.confirm('上次提交结果尚未确认。确认重新读取订单并按当前表单重新编辑吗?只有服务端仍为草稿时才能开始新的提交决定。', '重新编辑订单', {
type: 'warning', center: true, confirmButtonText: '确认重新编辑', cancelButtonText: '返回',
})
} catch (error) {
if (error === 'cancel' || error === 'close') return false
throw error
}
editingSession.value = await store.beginNewSubmissionEdit(editingId.value)
return true
}
async function submitOrder() {
if (saving.value) return
if (companySubmissionBlocked.value) {
@@ -2044,12 +2057,14 @@ async function submitOrder() {
}
return
}
if (!await validateEditorForm()) return
const retry = store.isSubmissionRetry(editingId.value, cloneDraft(form))
if (!retry && !await validateEditorForm()) return
try {
saving.value = true
if (!retry && !await confirmSubmissionEdit()) return
const knownCompanyAuditStatus = companyAuditLoaded.value ? tenantStore.company.auditStatus : undefined
const savedOrder = await store.saveDraft(cloneDraft(form), tenantStore.company.companyName, editingId.value, editingSession.value)
const savedOrder = await store.saveDraftForSubmission(cloneDraft(form), tenantStore.company.companyName, editingId.value, editingSession.value)
editingId.value = savedOrder.id
editingSession.value = store.beginDraftEdit(savedOrder)
await store.submitSaved(savedOrder.id, knownCompanyAuditStatus)
@@ -2149,7 +2164,7 @@ async function searchOrders(model: QueryModel) {
</el-popover>
<el-button :loading="downloadingMarks" :disabled="selectedOrderCount === 0" @click="downloadShippingMarks"><Download :size="16" />下载唛头</el-button>
<el-button :loading="exportingOrders" :disabled="selectedOrderCount === 0" @click="exportOrderInformation"><Download :size="16" />导出订单信息</el-button>
<el-button v-if="canSubmitOrder" :loading="batchSubmitting" :disabled="selectedDraftOrders.length === 0 || companySubmissionBlocked" @click="submitSelectedDraftOrders"><Send :size="17" />批量提交草稿</el-button>
<el-button v-if="canSubmitOrder" :loading="batchSubmitting" :disabled="(selectedDraftOrders.length === 0 && pendingBatchSubmissionIds.length === 0) || companySubmissionBlocked" @click="submitSelectedDraftOrders"><Send :size="17" />{{ pendingBatchSubmissionIds.length ? '重试批量提交' : '批量提交草稿' }}</el-button>
<el-button v-if="auth.hasPermission('order:first-leg:create')" @click="openBatchImport"><Upload :size="17" />批量导入订单</el-button>
<el-button v-if="props.showCreateAction && auth.hasPermission('order:first-leg:create')" type="primary" :loading="pageDataLoading" :disabled="pageDataLoading || availableWarehouses.length === 0" @click="openCreate"><FilePlus2 :size="17" />创建订单</el-button>