2026-09-01 14:23:55 +08:00
|
|
|
import { reactive } from 'vue'
|
|
|
|
|
|
2026-10-19 17:56:30 +08:00
|
|
|
const STORAGE_KEY = 'overseas-pda-fulfillment-draft-v2'
|
2026-09-01 14:23:55 +08:00
|
|
|
|
|
|
|
|
const blankState = () => ({
|
|
|
|
|
scopeKey: '',
|
2026-10-20 10:36:04 +08:00
|
|
|
dispatch: { currentNo: '', palletCode: '', dispatchItemId: '', sourceKey: '', cartonCode: '', packages: 1, pendingCommand: null },
|
2026-09-01 14:23:55 +08:00
|
|
|
outbound: { currentTaskNo: '', scanCode: '', packages: 1, pendingKeys: {} }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export const fulfillment = reactive(blankState())
|
|
|
|
|
|
|
|
|
|
function storageAvailable() {
|
|
|
|
|
return typeof uni !== 'undefined' && typeof uni.setStorageSync === 'function' && typeof uni.getStorageSync === 'function'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function safeDomain(value, fallback) {
|
|
|
|
|
return {
|
|
|
|
|
...fallback,
|
|
|
|
|
...(value || {}),
|
2026-10-19 17:56:30 +08:00
|
|
|
...(Object.hasOwn(fallback, 'pendingKeys') ? { pendingKeys: { ...(value?.pendingKeys || {}) } } : {})
|
2026-09-01 14:23:55 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function persistFulfillmentDraft() {
|
|
|
|
|
if (!storageAvailable() || !fulfillment.scopeKey) return
|
|
|
|
|
uni.setStorageSync(STORAGE_KEY, JSON.parse(JSON.stringify(fulfillment)))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function restoreFulfillmentDraft(scopeKey) {
|
|
|
|
|
const expected = String(scopeKey || '')
|
|
|
|
|
if (!expected) return false
|
|
|
|
|
const saved = storageAvailable() ? uni.getStorageSync(STORAGE_KEY) : null
|
|
|
|
|
if (!saved || saved.scopeKey !== expected) {
|
|
|
|
|
Object.assign(fulfillment, blankState(), { scopeKey: expected })
|
|
|
|
|
if (storageAvailable()) uni.removeStorageSync(STORAGE_KEY)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
const fresh = blankState()
|
|
|
|
|
Object.assign(fulfillment, fresh, {
|
|
|
|
|
scopeKey: expected,
|
|
|
|
|
dispatch: safeDomain(saved.dispatch, fresh.dispatch),
|
|
|
|
|
outbound: safeDomain(saved.outbound, fresh.outbound)
|
|
|
|
|
})
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function updateFulfillmentDraft(domain, patch) {
|
|
|
|
|
if (!fulfillment[domain]) throw new Error('未知海外 PDA 履约草稿')
|
|
|
|
|
Object.assign(fulfillment[domain], patch)
|
|
|
|
|
persistFulfillmentDraft()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function randomKey(domain, action) {
|
|
|
|
|
return `pda-${domain}-${action}-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function fulfillmentIdempotencyKey(domain, action, payload = {}) {
|
|
|
|
|
if (!fulfillment[domain]) throw new Error('未知海外 PDA 履约类型')
|
2026-10-19 17:56:30 +08:00
|
|
|
if (domain === 'dispatch') throw new Error('排车必须保存完整原命令,不能只生成请求键')
|
2026-09-01 14:23:55 +08:00
|
|
|
const signature = `${action}:${JSON.stringify(payload)}`
|
|
|
|
|
const pendingKeys = fulfillment[domain].pendingKeys
|
|
|
|
|
if (!pendingKeys[signature]) pendingKeys[signature] = randomKey(domain, action)
|
|
|
|
|
persistFulfillmentDraft()
|
|
|
|
|
return { signature, value: pendingKeys[signature] }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function releaseFulfillmentKey(domain, signature) {
|
|
|
|
|
if (!fulfillment[domain]) return
|
2026-10-19 17:56:30 +08:00
|
|
|
if (domain === 'dispatch') throw new Error('排车原命令只能在服务端事实核对后清除')
|
2026-09-01 14:23:55 +08:00
|
|
|
delete fulfillment[domain].pendingKeys[signature]
|
|
|
|
|
persistFulfillmentDraft()
|
|
|
|
|
}
|
|
|
|
|
|
2026-10-19 17:56:30 +08:00
|
|
|
export function beginDispatchCommand(dispatchNo, action, body) {
|
|
|
|
|
if (!fulfillment.scopeKey) throw new Error('当前作业身份未恢复')
|
|
|
|
|
if (fulfillment.dispatch.pendingCommand) throw new Error('请先核对原排车操作结果')
|
|
|
|
|
const idempotencyKey = randomKey('dispatch', action)
|
|
|
|
|
fulfillment.dispatch.pendingCommand = { dispatchNo, action, body: JSON.parse(JSON.stringify({ ...body, idempotencyKey })), attempted: false, rejected: false, result: null }
|
|
|
|
|
persistFulfillmentDraft()
|
|
|
|
|
return fulfillment.dispatch.pendingCommand
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function patchDispatchCommand(patch) {
|
|
|
|
|
if (!fulfillment.dispatch.pendingCommand) throw new Error('原排车请求不存在')
|
|
|
|
|
Object.assign(fulfillment.dispatch.pendingCommand, patch)
|
|
|
|
|
persistFulfillmentDraft()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function finishDispatchCommand(key) {
|
|
|
|
|
if (fulfillment.dispatch.pendingCommand?.body.idempotencyKey !== key) throw new Error('核对结果与原请求键不一致')
|
|
|
|
|
fulfillment.dispatch.pendingCommand = null
|
|
|
|
|
persistFulfillmentDraft()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function discardRejectedDispatchCommand() {
|
|
|
|
|
if (fulfillment.dispatch.pendingCommand?.rejected === true) {
|
|
|
|
|
fulfillment.dispatch.pendingCommand = null
|
|
|
|
|
persistFulfillmentDraft()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-01 14:23:55 +08:00
|
|
|
export function resetFulfillmentDomain(domain) {
|
|
|
|
|
if (!fulfillment[domain]) return
|
|
|
|
|
fulfillment[domain] = blankState()[domain]
|
|
|
|
|
persistFulfillmentDraft()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resetFulfillmentSession() {
|
|
|
|
|
Object.assign(fulfillment, blankState())
|
|
|
|
|
if (typeof uni !== 'undefined' && typeof uni.removeStorageSync === 'function') uni.removeStorageSync(STORAGE_KEY)
|
|
|
|
|
}
|