Files
medical-mall/api/delivery.uts

2687 lines
87 KiB
Plaintext

import type {
DeliveryAbnormalReportType,
DeliveryCertificateType,
DeliveryCheckinPayloadType,
DeliveryDashboardType,
DeliveryTimelineItemType,
DeliveryExceptionPayloadType,
DeliveryFinishPayloadType,
DeliveryInfoType,
DeliveryLocationType,
DeliveryLoginPayloadType,
DeliveryLoginResultType,
DeliveryMessageType,
DeliveryOrderQueryType,
DeliveryOrderStatus,
DeliveryOrderType,
DeliveryProgressPayloadType,
DeliveryRecordType,
DeliveryEvidenceRecordType,
DeliveryServiceRecordType,
DeliveryServiceItemType
} from '@/types/delivery.uts'
import supa, { ensureSupabaseReady } from '@/components/supadb/aksupainstance.uts'
import { getCurrentUser } from '@/utils/store.uts'
import type { UserProfile } from '@/types/mall-types.uts'
import { IS_TEST_MODE } from '@/ak/config.uts'
const DELIVERY_RPC_DASHBOARD = 'rpc_delivery_dashboard'
const DELIVERY_RPC_ORDER_LIST = 'rpc_delivery_order_list'
const DELIVERY_RPC_ORDER_DETAIL = 'rpc_delivery_order_detail'
const DELIVERY_RPC_ACCEPT_ORDER = 'rpc_delivery_accept_order'
const DELIVERY_RPC_HOMECARE_ACCEPT_ASSIGNMENT_V2 = 'rpc_homecare_accept_assignment_v2'
const DELIVERY_RPC_REJECT_ORDER = 'rpc_delivery_reject_order'
const DELIVERY_RPC_START_DEPART = 'rpc_delivery_start_depart'
const DELIVERY_RPC_ARRIVE_ORDER = 'rpc_delivery_arrive_order'
const DELIVERY_RPC_CHECKIN_ORDER = 'rpc_delivery_checkin_order'
const DELIVERY_RPC_HOMECARE_CHECKIN_PRECHECK_COMPAT = 'rpc_homecare_checkin_precheck_compat'
const DELIVERY_RPC_CREATE_CHECKIN_EVIDENCE_COMPAT = 'rpc_homecare_create_checkin_evidence_compat'
const DELIVERY_RPC_HOMECARE_CHECKIN_SUBMIT_COMPAT = 'rpc_homecare_checkin_submit_compat'
const DELIVERY_RPC_HOMECARE_CHECKIN_SUBMIT = 'rpc_homecare_checkin_submit'
const DELIVERY_RPC_START_SERVICE = 'rpc_delivery_start_service'
const DELIVERY_RPC_SAVE_PROGRESS = 'rpc_delivery_save_progress'
const DELIVERY_RPC_UPLOAD_EVIDENCE = 'rpc_delivery_upload_evidence'
const DELIVERY_RPC_RETRY_EVIDENCE = 'rpc_delivery_retry_evidence'
const DELIVERY_RPC_SUBMIT_EXCEPTION = 'rpc_delivery_submit_exception'
const DELIVERY_RPC_FINISH_SERVICE = 'rpc_delivery_finish_service'
const DELIVERY_RPC_RECORD_LIST = 'rpc_delivery_record_list'
const DELIVERY_RPC_MESSAGE_LIST = 'rpc_delivery_message_list'
const DELIVERY_RPC_SET_ONLINE_STATUS = 'rpc_delivery_set_online_status'
const DELIVERY_MOCK_PROFILE_KEY = 'delivery_mock_profile'
const DELIVERY_MOCK_ORDERS_KEY = 'delivery_mock_orders'
const DELIVERY_FORCE_REMOTE_KEY = 'delivery_force_remote'
const missingDeliveryRpcNames = [] as Array<string>
function isMissingDeliveryRpc(functionName: string): boolean {
for (let i = 0; i < missingDeliveryRpcNames.length; i++) {
if (missingDeliveryRpcNames[i] == functionName) {
return true
}
}
return false
}
function markMissingDeliveryRpc(functionName: string): void {
if (!isMissingDeliveryRpc(functionName)) {
missingDeliveryRpcNames.push(functionName)
}
}
function mapLoginError(rawData: UTSJSONObject): string {
const errorMsg = rawData.getString('msg') ?? rawData.getString('message') ?? rawData.getString('error') ?? ''
const errorCode = rawData.getString('error_code') ?? ''
if (errorCode == 'email_not_confirmed') {
return '邮箱未验证,请先完成邮箱验证'
}
if (errorMsg.includes('email') && errorMsg.includes('confirm')) {
return '邮箱未验证,请先完成邮箱验证'
}
if (errorMsg.includes('Invalid authentication credentials')) {
return '配送账号或密码错误'
}
if (errorCode == 'invalid_credentials' || errorMsg.includes('Invalid login credentials') || errorMsg.includes('Invalid credentials')) {
return '配送账号或密码错误'
}
return errorMsg != '' ? errorMsg : '登录失败,请稍后重试'
}
function readObjectField(raw: any, key: string): any {
if (raw == null) {
return null
}
if (typeof raw.getJSONObject === 'function') {
const nested = raw.getJSONObject(key)
if (nested != null) {
return nested
}
}
try {
const plain = JSON.parse(JSON.stringify(raw)) as any
return plain[key]
} catch (error) {}
return null
}
function readStringField(raw: any, key: string): string {
if (raw == null) {
return ''
}
if (typeof raw.getString === 'function') {
return raw.getString(key) ?? ''
}
try {
const plain = JSON.parse(JSON.stringify(raw)) as any
const value = plain[key]
if (typeof value == 'string') {
return value
}
if (value != null) {
return String(value)
}
} catch (error) {}
return ''
}
function readNumberField(raw: any, key: string): number | null {
if (raw == null) {
return null
}
if (typeof raw.getNumber === 'function') {
return raw.getNumber(key)
}
try {
const plain = JSON.parse(JSON.stringify(raw)) as any
const value = plain[key]
if (typeof value == 'number') {
return value
}
} catch (error) {}
return null
}
function readBooleanField(raw: any, key: string): boolean | null {
if (raw == null) {
return null
}
if (typeof raw.getBoolean === 'function') {
return raw.getBoolean(key)
}
try {
const plain = JSON.parse(JSON.stringify(raw)) as any
const value = plain[key]
if (typeof value == 'boolean') {
return value
}
} catch (error) {}
return null
}
function normalizeDeliveryStatus(raw: any): 'active' | 'disabled' | 'locked' | 'suspended' {
const status = readNumberField(raw, 'status')
const isActive = readBooleanField(raw, 'is_active')
if (status === 0 || isActive === false) {
return 'disabled'
}
return 'active'
}
function normalizeOnlineStatus(raw: any): 'online' | 'resting' | 'busy' {
const value = readStringField(raw, 'online_status')
if (value == 'online' || value == 'busy') {
return value
}
return 'resting'
}
function normalizeCertificateStatus(raw: any): 'valid' | 'expired' | 'pending' {
const value = readStringField(raw, 'certificate_status')
if (value == 'valid' || value == 'expired') {
return value
}
return 'pending'
}
function normalizeStringArray(rawValue: string): Array<string> {
const value = rawValue.trim()
if (value == '') {
return [] as Array<string>
}
try {
const parsed = JSON.parse(value)
if (parsed instanceof Array) {
const result = [] as Array<string>
for (let i = 0; i < parsed.length; i++) {
const item = parsed[i]
if (typeof item == 'string') {
result.push(item)
}
}
return result
}
} catch (error) {}
return [] as Array<string>
}
function readStationObject(raw: any): any {
const station = readObjectField(raw, 'station')
if (station != null) {
return station
}
const nested = readObjectField(raw, 'ml_delivery_stations')
if (nested != null) {
return nested
}
return null
}
function buildDeliveryInfoFromStaff(raw: any): DeliveryInfoType {
const id = readStringField(raw, 'id')
const station = readStationObject(raw)
const stationId = readStringField(raw, 'station_id')
const skillsRaw = readStringField(raw, 'skills')
const certificateExpireAt = readStringField(raw, 'certificate_expire_at')
const organizationName = station != null ? readStringField(station, 'name') : ''
return {
id,
userId: readStringField(raw, 'uid'),
staffNo: readStringField(raw, 'staff_no') != '' ? readStringField(raw, 'staff_no') : (id != '' ? 'DEL-' + id.substring(0, Math.min(8, id.length)).toUpperCase() : ''),
name: '配送员',
phone: readStringField(raw, 'phone'),
role: 'delivery',
status: normalizeDeliveryStatus(raw),
organizationId: stationId,
organizationName: organizationName != '' ? organizationName : stationId,
certificates: [] as Array<any>,
certificateStatus: normalizeCertificateStatus(raw),
certificateExpireAt,
onlineStatus: normalizeOnlineStatus(raw),
serviceArea: readStringField(raw, 'service_area'),
skills: normalizeStringArray(skillsRaw),
avatarUrl: readStringField(raw, 'avatar'),
todayAccepted: 0,
todayServing: 0,
todayCompleted: 0,
usesMock: false
} as DeliveryInfoType
}
async function fetchCareRequestById(requestId: string): Promise<any | null> {
if (requestId == '') {
return null
}
try {
await ensureSupabaseReady()
const response = await supa.from('ec_service_requests').select('*').eq('id', requestId).limit(1).execute()
if (response.error != null || response.data == null) {
return null
}
const rows = response.data as Array<any>
if (rows.length > 0) {
return rows[0]
}
} catch (error) {
console.warn('[delivery api] 查询服务请求失败:', requestId, error)
}
return null
}
async function fetchDeliveryProfileFromRemote(userId: string): Promise<DeliveryInfoType | null> {
if (userId == '') {
return null
}
const selectFields = '*, station:ml_delivery_stations(id, name)'
try {
let response = await supa.from('ml_delivery_staff').select(selectFields).eq('uid', userId).limit(1).execute()
if (response.error == null && response.data != null) {
const rows = response.data as Array<UTSJSONObject>
if (rows.length > 0) {
return buildDeliveryInfoFromStaff(rows[0])
}
}
response = await supa.from('ml_delivery_staff').select(selectFields).eq('id', userId).limit(1).execute()
if (response.error == null && response.data != null) {
const rows = response.data as Array<UTSJSONObject>
if (rows.length > 0) {
return buildDeliveryInfoFromStaff(rows[0])
}
}
} catch (error) {
console.warn('[delivery api] 操作失败:', error)
}
return null
}
function isDelivererRole(userInfo: UserProfile | null): boolean {
if (userInfo == null) {
return false
}
const role = userInfo.role ?? ''
return role == 'delivery' || role == 'Deliverer'
}
function shouldBypassDeliveryRpc(): boolean {
// 兼容旧数据处理
return false
// if (IS_TEST_MODE !== true) {
// return false
// }
// const forceRemote = uni.getStorageSync(DELIVERY_FORCE_REMOTE_KEY)
// if (forceRemote === true) {
// return false
// }
// if (typeof forceRemote == 'string' && (forceRemote == 'true' || forceRemote == '1')) {
// return false
// }
// return true
}
async function callDeliveryRpc(functionName: string, params: UTSJSONObject): Promise<any> {
console.log('[DELIVERY_RPC_TRACE][REQUEST]', {
name: functionName,
params: params
})
if (shouldBypassDeliveryRpc()) {
console.log('[DELIVERY_RPC_TRACE][RESPONSE]', {
name: functionName,
data: null,
error: 'bypassed by test mode'
})
return null
}
if (isMissingDeliveryRpc(functionName)) {
console.log('[DELIVERY_RPC_TRACE][RESPONSE]', {
name: functionName,
data: null,
error: 'previously marked missing'
})
return null
}
try {
await ensureSupabaseReady()
console.warn('[delivery api] RPC request:', functionName, JSON.stringify(params))
const res: any = await supa.rpc(functionName, params)
if (res?.status === 404) {
markMissingDeliveryRpc(functionName)
console.warn('[delivery api] RPC 不可用,使用 mock fallback:', functionName)
console.log('[DELIVERY_RPC_TRACE][RESPONSE]', {
name: functionName,
data: null,
error: 'rpc 404 not found'
})
return null
}
if (res?.error != null) {
console.log('[DELIVERY_RPC_TRACE][RESPONSE]', {
name: functionName,
data: null,
error: res.error
})
throw res.error
}
console.warn('[delivery api] RPC response:', functionName, JSON.stringify(res.data))
console.log('[DELIVERY_RPC_TRACE][RESPONSE]', {
name: functionName,
data: res.data,
error: null
})
return res.data
} catch (error) {
console.error('[DELIVERY_RPC_TRACE][ERROR]', {
name: functionName,
params: params,
errorMessage: error != null ? String(error.message ?? error) : '',
errorStack: error != null ? String(error.stack ?? '') : ''
})
console.warn('[delivery api] RPC 调用失败,使用 mock fallback:', functionName, error)
return null
}
}
function rpcStr(item: any, key: string): string {
if (item == null) return ''
if (typeof item.getString === 'function') {
const v = item.getString(key)
return v != null ? v : ''
}
const v = item[key]
return v != null && v !== undefined ? String(v) : ''
}
function rpcNum(item: any, key: string): number {
if (item == null) return 0
if (typeof item.getNumber === 'function') {
const v = item.getNumber(key)
return v != null ? v : 0
}
const v = item[key]
if (typeof v === 'number') return v
const parsed = Number(v)
return isNaN(parsed) ? 0 : parsed
}
function rpcObj(item: any, key: string): any {
if (item == null) return null
if (typeof item.getJSONObj === 'function') {
const v = item.getJSONObj(key)
return v != null ? v : null
}
return item[key] ?? null
}
function mapRpcOrderItem(item: any): DeliveryOrderType {
const addressObj = rpcObj(item, 'address_snapshot_json') ?? {}
const serviceObj = rpcObj(item, 'service_snapshot_json') ?? {}
const order = {} as DeliveryOrderType
order.id = rpcStr(item, 'id')
order.orderNo = rpcStr(item, 'order_no')
order.serviceType = '居家服务'
order.serviceName = rpcStr(item, 'service_name')
order.serviceCategory = rpcStr(serviceObj, 'category')
order.serviceItems = [] as Array<DeliveryServiceItemType>
order.elderId = ''
order.elderName = rpcStr(item, 'recipient_name')
order.elderNameMasked = order.elderName
order.elderGender = ''
order.elderAge = 0
order.elderPhone = rpcStr(item, 'recipient_phone')
order.elderPhoneMasked = order.elderPhone
order.fullElderName = order.elderName
order.fullPhone = order.elderPhone
order.contactRelation = '家属'
order.contactName = rpcStr(item, 'contact_name')
order.contactPhone = rpcStr(item, 'contact_phone')
order.addressSummary = rpcStr(addressObj, 'fullAddress') || rpcStr(addressObj, 'full_address')
order.address = order.addressSummary
order.addressDetail = rpcStr(addressObj, 'detailAddress') || rpcStr(addressObj, 'detail_address')
order.fullAddress = order.address + ' ' + order.addressDetail
order.latitude = rpcNum(addressObj, 'latitude')
order.longitude = rpcNum(addressObj, 'longitude')
order.appointmentTime = rpcStr(item, 'appointment_time')
order.appointmentStartTime = order.appointmentTime
order.appointmentEndTime = order.appointmentTime
order.duration = 90
order.estimatedDuration = 90
order.price = rpcNum(serviceObj, 'price')
order.staffIncome = order.price
order.distance = ''
order.actualStartTime = rpcStr(item, 'service_started_at')
order.actualEndTime = rpcStr(item, 'completed_at')
order.status = rpcStr(item, 'status') as DeliveryOrderStatus
order.statusText = ''
order.statusTone = ''
order.riskTags = [] as Array<string>
order.healthTags = [] as Array<string>
order.careLevel = ''
order.needFamilyPresent = false
order.needMaterials = false
order.remark = rpcStr(item, 'remark')
order.merchantId = ''
order.merchantName = ''
order.deliveryStaffId = rpcStr(item, 'current_staff_id')
order.deliveryStaffName = ''
order.acceptTime = rpcStr(item, 'accepted_at')
order.rejectTime = ''
order.departTime = rpcStr(item, 'departed_at')
order.arriveTime = rpcStr(item, 'arrived_at')
order.checkinTime = rpcStr(item, 'checked_in_at') || rpcStr(item, 'arrived_at')
order.startServiceTime = order.actualStartTime
order.finishTime = rpcStr(item, 'completed_at') || rpcStr(item, 'service_completed_at')
order.cancelReason = rpcStr(item, 'cancel_reason')
order.exceptionType = ''
order.exceptionDesc = ''
order.evidenceList = [] as Array<DeliveryEvidenceRecordType>
order.signatureUrl = ''
order.signatureName = ''
order.satisfactionStatus = '待评价'
order.settlementStatus = '待结算'
order.archiveStatus = '未归档'
order.createdAt = rpcStr(item, 'created_at')
order.updatedAt = rpcStr(item, 'updated_at')
order.notices = [] as Array<string>
order.timeline = [] as Array<DeliveryTimelineItemType>
order.statusLog = [] as Array<DeliveryStatusLogType>
order.serviceSummary = ''
order.progressNote = ''
order.distanceKm = ''
order.allowCheckinRadiusMeters = 100
order.lastLocation = null
order.trackPoints = [] as Array<DeliveryLocationType>
order.serviceRecord = null
order.abnormalReport = null
return order
}
function rpcBoolCompat(item: any, key: string): boolean {
if (item == null) return false
if (typeof item.getBoolean === 'function') {
const v = item.getBoolean(key)
return v != null ? v : false
}
return item[key] === true
}
function rpcArrayCompat(item: any, key: string): Array<any> {
if (item == null) return [] as Array<any>
const value = item[key]
if (Array.isArray(value)) {
return value
}
return [] as Array<any>
}
function rpcStrCompat(item: any, keys: Array<string>): string {
for (let i = 0; i < keys.length; i++) {
const value = rpcStr(item, keys[i])
if (value != '') {
return value
}
}
return ''
}
function rpcNumCompat(item: any, keys: Array<string>): number {
for (let i = 0; i < keys.length; i++) {
const value = rpcNum(item, keys[i])
if (value != 0) {
return value
}
}
return 0
}
function mapRpcTimelineCompat(list: Array<any>): Array<DeliveryTimelineItemType> {
const result = [] as Array<DeliveryTimelineItemType>
for (let i = 0; i < list.length; i++) {
const item = list[i]
result.push({
id: rpcStrCompat(item, ['id']),
title: rpcStrCompat(item, ['title']),
time: rpcStrCompat(item, ['time', 'createdAt', 'created_at']),
description: rpcStrCompat(item, ['description', 'remark'])
} as DeliveryTimelineItemType)
}
return result
}
function mapRpcOrderItemCompat(item: any): DeliveryOrderType {
const legacy = mapRpcOrderItem(item)
const addressObj = rpcObj(item, 'address_snapshot_json') ?? rpcObj(item, 'addressSnapshotJson') ?? {}
const serviceObj = rpcObj(item, 'service_snapshot_json') ?? rpcObj(item, 'serviceSnapshotJson') ?? {}
const order = legacy
const addressSummary = rpcStrCompat(item, ['addressSummary', 'address']) != '' ? rpcStrCompat(item, ['addressSummary', 'address']) : rpcStrCompat(addressObj, ['fullAddress', 'full_address', 'address', 'name'])
const addressDetail = rpcStrCompat(item, ['addressDetail']) != '' ? rpcStrCompat(item, ['addressDetail']) : rpcStrCompat(addressObj, ['detailAddress', 'detail_address'])
order.id = rpcStrCompat(item, ['id'])
order.orderNo = rpcStrCompat(item, ['orderNo', 'order_no', 'task_no'])
order.sourceOrderId = rpcStrCompat(item, ['sourceOrderId', 'source_order_id'])
order.legacyServiceOrderId = rpcStrCompat(item, ['legacyServiceOrderId', 'legacy_service_order_id'])
order.resolvedWorkOrderId = rpcStrCompat(item, ['resolvedWorkOrderId', 'work_order_id'])
order.serviceType = rpcStrCompat(item, ['serviceType'])
if (order.serviceType == '') {
order.serviceType = rpcStrCompat(serviceObj, ['category'])
}
if (order.serviceType == '') {
order.serviceType = '居家服务'
}
order.serviceName = rpcStrCompat(item, ['serviceName', 'service_name'])
if (order.serviceName == '') {
order.serviceName = rpcStrCompat(serviceObj, ['name', 'serviceName'])
}
if (order.serviceName == '') {
order.serviceName = '上门服务'
}
order.serviceCategory = rpcStrCompat(item, ['serviceCategory'])
if (order.serviceCategory == '') {
order.serviceCategory = rpcStrCompat(serviceObj, ['category'])
}
order.serviceItems = rpcArrayCompat(item, 'serviceItems') as Array<DeliveryServiceItemType>
order.elderId = rpcStrCompat(item, ['elderId', 'elder_id', 'user_id'])
order.elderName = rpcStrCompat(item, ['elderName', 'recipient_name', 'elder_name'])
if (order.elderName == '') {
order.elderName = '用户'
}
order.elderNameMasked = order.elderName
order.elderGender = rpcStrCompat(item, ['elderGender'])
order.elderAge = rpcNumCompat(item, ['elderAge'])
order.elderPhone = rpcStrCompat(item, ['elderPhone', 'recipient_phone', 'elder_phone'])
order.elderPhoneMasked = order.elderPhone
order.fullElderName = order.elderName
order.fullPhone = order.elderPhone
order.contactRelation = rpcStrCompat(item, ['contactRelation'])
if (order.contactRelation == '') {
order.contactRelation = '家属'
}
order.contactName = rpcStrCompat(item, ['contactName', 'contact_name'])
order.contactPhone = rpcStrCompat(item, ['contactPhone', 'contact_phone'])
order.addressSummary = addressSummary
order.address = rpcStrCompat(item, ['address']) != '' ? rpcStrCompat(item, ['address']) : addressSummary
if (order.address == '') {
order.address = rpcStrCompat(addressObj, ['fullAddress', 'full_address', 'address', 'name'])
}
if (order.address == '') {
order.address = '暂无地址'
}
order.addressDetail = addressDetail
order.fullAddress = rpcStrCompat(item, ['fullAddress'])
if (order.fullAddress == '') {
order.fullAddress = order.address
}
if (order.addressDetail != '' && order.fullAddress.indexOf(order.addressDetail) < 0) {
order.fullAddress = order.fullAddress + ' ' + order.addressDetail
}
order.latitude = rpcNumCompat(item, ['latitude'])
if (order.latitude == 0) {
order.latitude = rpcNumCompat(addressObj, ['latitude'])
}
order.longitude = rpcNumCompat(item, ['longitude'])
if (order.longitude == 0) {
order.longitude = rpcNumCompat(addressObj, ['longitude'])
}
order.appointmentTime = rpcStrCompat(item, ['appointmentTime', 'appointment_time', 'scheduled_at'])
if (order.appointmentTime == '') {
order.appointmentTime = rpcStrCompat(item, ['createdAt', 'created_at'])
}
if (order.appointmentTime == '') {
order.appointmentTime = '待预约'
}
order.appointmentStartTime = rpcStrCompat(item, ['appointmentStartTime'])
if (order.appointmentStartTime == '') {
order.appointmentStartTime = order.appointmentTime
}
order.appointmentEndTime = rpcStrCompat(item, ['appointmentEndTime'])
if (order.appointmentEndTime == '') {
order.appointmentEndTime = order.appointmentTime
}
order.duration = rpcNumCompat(item, ['duration', 'duration_minutes'])
if (order.duration == 0) {
order.duration = 90
}
order.estimatedDuration = rpcNumCompat(item, ['estimatedDuration', 'duration', 'duration_minutes'])
if (order.estimatedDuration == 0) {
order.estimatedDuration = order.duration
}
order.price = rpcNumCompat(item, ['price'])
if (order.price == 0) {
order.price = rpcNumCompat(serviceObj, ['price'])
}
order.staffIncome = rpcNumCompat(item, ['staffIncome'])
if (order.staffIncome == 0) {
order.staffIncome = rpcNumCompat(item, ['price'])
}
if (order.staffIncome == 0) {
order.staffIncome = rpcNumCompat(serviceObj, ['price'])
}
if (order.staffIncome == 0) {
order.staffIncome = 0
}
order.distance = rpcStrCompat(item, ['distance'])
order.actualStartTime = rpcStrCompat(item, ['actualStartTime', 'service_started_at'])
order.actualEndTime = rpcStrCompat(item, ['actualEndTime', 'completed_at', 'service_completed_at'])
order.status = rpcStrCompat(item, ['status']) as DeliveryOrderStatus
order.statusText = rpcStrCompat(item, ['statusText'])
order.statusTone = rpcStrCompat(item, ['statusTone'])
// 根据订单状态补全文案
if (order.statusText == '') {
const rawStatus = order.status
if (rawStatus == 'assigned' || rawStatus == 'ASSIGNED') {
order.statusText = '待接单'
order.statusTone = 'primary'
} else if (rawStatus == 'arrival_pending' || rawStatus == 'ARRIVAL_PENDING') {
order.statusText = '待到达'
order.statusTone = 'warning'
} else if (rawStatus == 'PENDING_ASSIGNMENT' || rawStatus == 'CREATED') {
order.statusText = '待出发'
order.statusTone = 'default'
} else if (rawStatus == 'PENDING_ACCEPT' || rawStatus == 'PENDING_ACCEPTANCE') {
order.statusText = '待出发'
order.statusTone = 'warning'
} else if (rawStatus == 'ACCEPTED' || rawStatus == 'ORDER_ACCEPTED') {
order.statusText = '赶往中'
order.statusTone = 'primary'
} else if (rawStatus == 'WAITING_DEPARTURE') {
order.statusText = '已到达'
order.statusTone = 'primary'
} else if (rawStatus == 'departed' || rawStatus == 'on_the_way' || rawStatus == 'order_departed' || rawStatus == 'DEPARTED' || rawStatus == 'ON_THE_WAY' || rawStatus == 'ORDER_DEPARTED') {
order.statusText = '服务中'
order.statusTone = 'primary'
} else if (rawStatus == 'arrived' || rawStatus == 'checked_in' || rawStatus == 'order_checked_in' || rawStatus == 'ARRIVED' || rawStatus == 'CHECKED_IN' || rawStatus == 'ORDER_CHECKED_IN') {
order.statusText = '服务中'
order.statusTone = 'success'
} else if (rawStatus == 'IN_SERVICE' || rawStatus == 'SERVING' || rawStatus == 'ORDER_IN_SERVICE') {
order.statusText = '已完成'
order.statusTone = 'success'
} else if (rawStatus == 'COMPLETED' || rawStatus == 'ORDER_COMPLETED' || rawStatus == 'SETTLED' || rawStatus == 'SETTLED') {
order.statusText = '已取消'
order.statusTone = 'success'
} else if (rawStatus == 'CANCELLED' || rawStatus == 'CANCELED' || rawStatus == 'ORDER_CANCELLED') {
order.statusText = '异常'
order.statusTone = 'info'
} else if (rawStatus == 'ABNORMAL' || rawStatus == 'EXCEPTION' || rawStatus == 'EXCEPTION_PENDING') {
order.statusText = '已拒单'
order.statusTone = 'danger'
} else if (rawStatus == 'REJECTED') {
order.statusText = '已归档'
order.statusTone = 'danger'
} else if (rawStatus == 'ARCHIVED') {
order.statusText = '待处理'
order.statusTone = 'default'
} else {
order.statusText = rawStatus
order.statusTone = 'default'
}
}
order.riskTags = rpcArrayCompat(item, 'riskTags') as Array<string>
order.healthTags = rpcArrayCompat(item, 'healthTags') as Array<string>
order.careLevel = rpcStrCompat(item, ['careLevel'])
order.needFamilyPresent = rpcBoolCompat(item, 'needFamilyPresent')
order.needMaterials = rpcBoolCompat(item, 'needMaterials')
order.remark = rpcStrCompat(item, ['remark'])
order.merchantId = rpcStrCompat(item, ['merchantId', 'merchant_id'])
order.merchantName = rpcStrCompat(item, ['merchantName', 'merchant_name'])
order.deliveryStaffId = rpcStrCompat(item, ['deliveryStaffId', 'current_staff_id', 'assigned_to'])
order.deliveryStaffName = rpcStrCompat(item, ['deliveryStaffName', 'delivery_staff_name'])
order.acceptedBy = ''
order.acceptedByName = ''
const statusLog = order.statusLog
for (let i = 0; i < statusLog.length; i++) {
if (statusLog[i].toStatus == 'accepted' || statusLog[i].toStatus == 'pending_accept') {
order.acceptedBy = statusLog[i].operatorId != '' ? statusLog[i].operatorId : order.deliveryStaffId
order.acceptedByName = statusLog[i].operatorRole != '' ? statusLog[i].operatorRole : order.deliveryStaffName
break
}
}
// 兼容旧数据处理
if (order.acceptedBy == '') {
order.acceptedBy = rpcStrCompat(item, ['acceptedBy', 'accepted_by', 'accepted_staff_id'])
order.acceptedByName = rpcStrCompat(item, ['acceptedByName', 'accepted_staff_name', 'accepted_by_name'])
}
order.acceptTime = rpcStrCompat(item, ['acceptTime', 'accepted_at'])
order.departTime = rpcStrCompat(item, ['departTime', 'departed_at'])
order.arriveTime = rpcStrCompat(item, ['arriveTime', 'arrived_at'])
order.checkinTime = rpcStrCompat(item, ['checkinTime', 'checked_in_at', 'arrived_at'])
order.startServiceTime = rpcStrCompat(item, ['startServiceTime', 'service_started_at'])
order.finishTime = rpcStrCompat(item, ['finishTime', 'completed_at', 'service_completed_at'])
const normalizedStatus = String(order.status).trim().toLowerCase()
if (order.checkinTime != '') {
order.status = 'arrived' as DeliveryOrderStatus
order.statusText = '已到达'
order.statusTone = 'success'
} else if (order.arriveTime != '') {
order.status = 'arrived' as DeliveryOrderStatus
order.statusText = '已到达'
order.statusTone = 'success'
} else if (order.departTime != '' && (normalizedStatus == 'assigned' || normalizedStatus == 'accepted')) {
order.status = 'departed' as DeliveryOrderStatus
order.statusText = '已出发'
order.statusTone = 'primary'
}
order.cancelReason = rpcStrCompat(item, ['cancelReason', 'cancel_reason'])
order.exceptionType = rpcStrCompat(item, ['exceptionType'])
order.exceptionDesc = rpcStrCompat(item, ['exceptionDesc'])
order.evidenceList = rpcArrayCompat(item, 'evidenceList') as Array<DeliveryEvidenceRecordType>
order.signatureUrl = rpcStrCompat(item, ['signatureUrl'])
order.signatureName = rpcStrCompat(item, ['signatureName'])
order.satisfactionStatus = rpcStrCompat(item, ['satisfactionStatus'])
order.settlementStatus = rpcStrCompat(item, ['settlementStatus'])
order.archiveStatus = rpcStrCompat(item, ['archiveStatus'])
order.createdAt = rpcStrCompat(item, ['createdAt', 'created_at'])
order.updatedAt = rpcStrCompat(item, ['updatedAt', 'updated_at'])
order.notices = rpcArrayCompat(item, 'notices') as Array<string>
order.timeline = mapRpcTimelineCompat(rpcArrayCompat(item, 'timeline'))
order.statusLog = rpcArrayCompat(item, 'statusLog') as Array<DeliveryStatusLogType>
order.serviceSummary = rpcStrCompat(item, ['serviceSummary'])
order.progressNote = rpcStrCompat(item, ['progressNote'])
order.distanceKm = rpcStrCompat(item, ['distanceKm'])
order.allowCheckinRadiusMeters = rpcNumCompat(item, ['allowCheckinRadiusMeters'])
if (order.allowCheckinRadiusMeters == 0) {
order.allowCheckinRadiusMeters = 100
}
order.lastLocation = rpcObj(item, 'lastLocation') as DeliveryLocationType | null
order.trackPoints = rpcArrayCompat(item, 'trackPoints') as Array<DeliveryLocationType>
order.serviceRecord = rpcObj(item, 'serviceRecord') as DeliveryServiceRecordType | null
order.abnormalReport = rpcObj(item, 'abnormalReport') as DeliveryAbnormalReportType | null
console.warn('[delivery api] mapped order:', JSON.stringify({
id: order.id,
orderNo: order.orderNo,
serviceName: order.serviceName,
elderName: order.elderName,
contactName: order.contactName,
address: order.address,
addressDetail: order.addressDetail,
appointmentTime: order.appointmentTime,
price: order.price,
staffIncome: order.staffIncome,
status: order.status,
statusText: order.statusText,
requestId: rpcStrCompat(item, ['request_id', 'requestId'])
}))
return order
}
function needsRequestFallback(order: DeliveryOrderType): boolean {
return order.serviceName == '' || order.elderName == '' || order.address == '' || order.contactName == ''
}
// 兼容旧数据处理
let _missingRequestIdLogCount = 0
function fillOrderFromRequestSnapshot(order: DeliveryOrderType, requestItem: any): DeliveryOrderType {
const addressObj = readObjectField(requestItem, 'address_snapshot_json') ?? readObjectField(requestItem, 'address_snapshot') ?? null
const addressFull = addressObj != null ? (readStringField(addressObj, 'fullAddress') != '' ? readStringField(addressObj, 'fullAddress') : readStringField(addressObj, 'full_address')) : ''
const addressDetail = addressObj != null ? (readStringField(addressObj, 'detailAddress') != '' ? readStringField(addressObj, 'detailAddress') : readStringField(addressObj, 'detail_address')) : ''
if (order.serviceName == '') {
order.serviceName = readStringField(requestItem, 'service_name')
}
if (order.serviceCategory == '') {
order.serviceCategory = readStringField(requestItem, 'service_category')
}
if (order.serviceType == '') {
const requestCategory = readStringField(requestItem, 'service_category')
if (requestCategory != '') {
order.serviceType = requestCategory
}
}
if (order.elderName == '') {
order.elderName = readStringField(requestItem, 'elder_name')
order.elderNameMasked = order.elderName
order.fullElderName = order.elderName
}
if (order.elderPhone == '') {
order.elderPhone = readStringField(requestItem, 'elder_phone')
order.elderPhoneMasked = order.elderPhone
order.fullPhone = order.elderPhone
}
if (order.contactName == '') {
order.contactName = readStringField(requestItem, 'contact_name')
}
if (order.contactPhone == '') {
order.contactPhone = readStringField(requestItem, 'contact_phone')
}
if (order.address == '') {
order.address = addressFull
order.addressSummary = addressFull
}
if (order.addressDetail == '') {
order.addressDetail = addressDetail
}
if (order.fullAddress == '' || order.fullAddress == order.address) {
order.fullAddress = order.address
if (order.addressDetail != '' && order.fullAddress.indexOf(order.addressDetail) < 0) {
order.fullAddress = order.fullAddress + ' ' + order.addressDetail
}
}
if (order.latitude == 0 && addressObj != null) {
const latitude = readNumberField(addressObj, 'latitude')
order.latitude = latitude != null ? latitude : 0
}
if (order.longitude == 0 && addressObj != null) {
const longitude = readNumberField(addressObj, 'longitude')
order.longitude = longitude != null ? longitude : 0
}
if (order.appointmentTime == '') {
order.appointmentTime = readStringField(requestItem, 'scheduled_at')
order.appointmentStartTime = order.appointmentTime
order.appointmentEndTime = order.appointmentTime
}
if (order.remark == '') {
order.remark = readStringField(requestItem, 'remark')
}
return order
}
async function enrichOrderWithRequestFallback(rawItem: any, order: DeliveryOrderType): Promise<DeliveryOrderType> {
if (!needsRequestFallback(order)) {
return order
}
const requestId = rpcStrCompat(rawItem, ['request_id', 'requestId'])
if (requestId == '') {
// 兼容旧数据处理
if (_missingRequestIdLogCount <= 3) {
console.warn('[delivery api] request fallback skipped: missing request_id for order', order.id, '(count:', _missingRequestIdLogCount, ')')
}
return order
}
const requestItem = await fetchCareRequestById(requestId)
if (requestItem == null) {
console.warn('[delivery api] request fallback miss:', order.id, requestId)
return order
}
console.warn('[delivery api] order snapshot missing, fallback to ec_service_requests:', order.id, requestId)
const nextOrder = fillOrderFromRequestSnapshot(order, requestItem)
console.warn('[delivery api] request fallback merged:', JSON.stringify({
id: nextOrder.id,
requestId,
serviceName: nextOrder.serviceName,
elderName: nextOrder.elderName,
contactName: nextOrder.contactName,
address: nextOrder.address,
addressDetail: nextOrder.addressDetail,
appointmentTime: nextOrder.appointmentTime
}))
return nextOrder
}
function normalizeRpcOrderList(data: any): Array<DeliveryOrderType> | null {
if (!Array.isArray(data)) {
return null
}
const result = [] as Array<DeliveryOrderType>
for (let i = 0; i < data.length; i++) {
result.push(mapRpcOrderItemCompat(data[i]))
}
return result
}
function normalizeRpcMessages(data: any): Array<DeliveryMessageType> | null {
if (!Array.isArray(data)) {
return null
}
return data as Array<DeliveryMessageType>
}
function normalizeRpcRecords(data: any): Array<DeliveryRecordType> | null {
if (!Array.isArray(data)) {
return null
}
return data as Array<DeliveryRecordType>
}
function normalizeRpcEvidenceList(data: any): Array<DeliveryEvidenceRecordType> | null {
if (!Array.isArray(data)) {
return null
}
return data as Array<DeliveryEvidenceRecordType>
}
function normalizeRpcOrder(data: any): DeliveryOrderType | null {
if (data == null) {
return null
}
return mapRpcOrderItemCompat(data)
}
function normalizeRpcDashboard(data: any): DeliveryDashboardType | null {
if (data == null) {
return null
}
return data as DeliveryDashboardType
}
function normalizeRpcDeliveryInfo(data: any): DeliveryInfoType | null {
if (data == null) {
return null
}
return data as DeliveryInfoType
}
function cloneDeliveryInfo(info: DeliveryInfoType): DeliveryInfoType {
return JSON.parse(JSON.stringify(info)) as DeliveryInfoType
}
function cloneDeliveryOrders(orders: Array<DeliveryOrderType>): Array<DeliveryOrderType> {
return JSON.parse(JSON.stringify(orders)) as Array<DeliveryOrderType>
}
function cloneDeliveryOrder(order: DeliveryOrderType): DeliveryOrderType {
return JSON.parse(JSON.stringify(order)) as DeliveryOrderType
}
function cloneDeliveryMessages(messages: Array<DeliveryMessageType>): Array<DeliveryMessageType> {
return JSON.parse(JSON.stringify(messages)) as Array<DeliveryMessageType>
}
function cloneDeliveryRecords(records: Array<DeliveryRecordType>): Array<DeliveryRecordType> {
return JSON.parse(JSON.stringify(records)) as Array<DeliveryRecordType>
}
function isObjectLike(value: any): boolean {
return value != null && typeof value == 'object'
}
function isValidDeliveryInfoCache(value: any): boolean {
if (!isObjectLike(value)) {
return false
}
const info = value as DeliveryInfoType
return typeof info.id == 'string' && typeof info.userId == 'string' && typeof info.name == 'string'
}
function isValidDeliveryOrdersCache(value: any): boolean {
if (!Array.isArray(value)) {
return false
}
for (let i = 0; i < value.length; i++) {
const item = value[i] as any
if (!isObjectLike(item) || typeof item.id != 'string' || typeof item.orderNo != 'string' || typeof item.status != 'string') {
return false
}
}
return true
}
function currentDateTimeText(): string {
return new Date().toISOString().replace('T', ' ').substring(0, 19)
}
function buildMockCertificates(): Array<DeliveryCertificateType> {
return [
{
id: 'mock-cert-1',
name: '配送员',
status: 'valid',
expireAt: '2027-12-31',
issuer: '社区卫生服务中心',
imageUrl: ''
} as DeliveryCertificateType,
{
id: 'mock-cert-2',
name: '配送员',
status: 'valid',
expireAt: '2026-12-31',
issuer: '社区卫生服务中心',
imageUrl: ''
} as DeliveryCertificateType
] as Array<DeliveryCertificateType>
}
function buildMockServiceItems(orderId: string, serviceName: string): Array<DeliveryServiceItemType> {
return [
{
id: orderId + '-item-1',
name: '配送员',
required: true,
completed: true,
incompleteReason: '',
remark: '已记录服务情况',
updatedAt: '2026-05-18 08:45:00'
} as DeliveryServiceItemType,
{
id: orderId + '-item-2',
name: '配送员',
required: true,
completed: true,
incompleteReason: '',
remark: '已记录服务情况',
updatedAt: '2026-05-18 09:10:00'
} as DeliveryServiceItemType,
{
id: orderId + '-item-3',
name: '配送员',
required: true,
completed: false,
incompleteReason: '',
remark: '',
updatedAt: ''
} as DeliveryServiceItemType
] as Array<DeliveryServiceItemType>
}
function buildMockTimeline(orderNo: string, phase: string): Array<DeliveryTimelineItemType> {
return [
{
id: orderNo + '-tl-1',
title: '服务通知',
time: '2026-05-18 07:30:00',
description: '服务状态已更新'
} as DeliveryTimelineItemType,
{
id: orderNo + '-tl-2',
title: '服务通知',
time: '2026-05-18 07:50:00',
description: '服务状态已更新'
} as DeliveryTimelineItemType,
{
id: orderNo + '-tl-3',
title: phase,
time: '2026-05-18 08:20:00',
description: '服务状态已更新'
} as DeliveryTimelineItemType
] as Array<DeliveryTimelineItemType>
}
function buildMockEvidence(orderId: string): Array<DeliveryEvidenceRecordType> {
return [
{
id: orderId + '-evi-1',
orderId,
phase: 'checkin',
fileType: 'image',
name: '配送员',
url: '',
localPath: '/static/mock/checkin-' + orderId + '.jpg',
status: 'success',
progress: 100,
retryable: false,
createdAt: '2026-05-18 08:42:00'
} as DeliveryEvidenceRecordType
] as Array<DeliveryEvidenceRecordType>
}
function buildMockProfile(userId: string): DeliveryInfoType {
const resolvedUserId = userId != '' ? userId : 'mock-user-delivery-001'
return {
id: 'mock-staff-001',
userId: resolvedUserId,
staffNo: 'DEL-0520',
name: '配送员',
phone: '13688886666',
role: 'delivery',
status: 'active',
organizationId: 'org-001',
organizationName: '安心到家服务站',
certificates: buildMockCertificates(),
certificateStatus: 'valid',
certificateExpireAt: '2027-12-31',
onlineStatus: 'online',
serviceArea: '梅州市区 / 周边社区',
skills: ['Home care', 'Rehab guidance', 'Health follow-up'],
avatarUrl: '',
todayAccepted: 0,
todayServing: 0,
todayCompleted: 0,
usesMock: true
} as DeliveryInfoType
}
function buildMockOrders(profile: DeliveryInfoType): Array<DeliveryOrderType> {
return [
{
id: 'mock-order-001',
orderNo: 'YS202605180001',
serviceType: '居家服务',
serviceName: '上门服务',
serviceItems: buildMockServiceItems('mock-order-001', '上门照护'),
elderId: 'elder-001',
elderNameMasked: '张阿姨',
elderGender: '女',
elderAge: 78,
elderPhoneMasked: '138****1024',
fullElderName: '张阿姨',
fullPhone: '13800131024',
addressSummary: '暂无地址',
fullAddress: '暂无地址',
latitude: 24.2898,
longitude: 116.1179,
appointmentStartTime: '2026-05-18 09:00',
appointmentEndTime: '2026-05-18 11:00',
estimatedDuration: 120,
actualStartTime: '',
actualEndTime: '',
status: 'pending_accept',
statusText: '待处理',
statusTone: 'warning',
riskTags: [] as Array<string>,
careLevel: '基础照护',
merchantId: 'merchant-001',
merchantName: '安心到家服务站',
deliveryStaffId: profile.id,
deliveryStaffName: profile.name,
acceptTime: '',
departTime: '',
arriveTime: '',
checkinTime: '',
finishTime: '',
cancelReason: '',
exceptionType: '',
exceptionDesc: '',
evidenceList: [] as Array<DeliveryEvidenceRecordType>,
signatureUrl: '',
signatureName: '',
satisfactionStatus: '待评价',
settlementStatus: '待结算',
archiveStatus: '未归档',
createdAt: '2026-05-18 07:30:00',
updatedAt: '2026-05-18 07:50:00',
contactName: '家属',
contactPhone: '13900139000',
notices: [] as Array<string>,
timeline: buildMockTimeline('YS202605180001', 'Staff has departed and is heading to the service location'),
serviceSummary: '',
progressNote: '',
distanceKm: '2.4km',
allowCheckinRadiusMeters: 100,
lastLocation: null,
trackPoints: [] as Array<DeliveryLocationType>
} as DeliveryOrderType,
{
id: 'mock-order-002',
orderNo: 'YS202605180002',
serviceType: 'Health follow-up',
serviceName: '上门服务',
serviceItems: buildMockServiceItems('mock-order-002', '上门照护'),
elderId: 'elder-002',
elderNameMasked: '张阿姨',
elderGender: '女',
elderAge: 82,
elderPhoneMasked: '137****2233',
fullElderName: '张阿姨',
fullPhone: '13700132233',
addressSummary: '暂无地址',
fullAddress: '暂无地址',
latitude: 24.3071,
longitude: 116.1365,
appointmentStartTime: '2026-05-18 10:30',
appointmentEndTime: '2026-05-18 12:00',
estimatedDuration: 90,
actualStartTime: '',
actualEndTime: '',
status: 'accepted',
statusText: '待处理',
statusTone: 'primary',
riskTags: [] as Array<string>,
careLevel: '基础照护',
merchantId: 'merchant-001',
merchantName: '安心到家服务站',
deliveryStaffId: profile.id,
deliveryStaffName: profile.name,
acceptTime: '2026-05-18 08:00:00',
departTime: '',
arriveTime: '',
checkinTime: '',
finishTime: '',
cancelReason: '',
exceptionType: '',
exceptionDesc: '',
evidenceList: [] as Array<DeliveryEvidenceRecordType>,
signatureUrl: '',
signatureName: '',
satisfactionStatus: '待评价',
settlementStatus: '待结算',
archiveStatus: '未归档',
createdAt: '2026-05-18 07:40:00',
updatedAt: '2026-05-18 08:00:00',
contactName: '家属',
contactPhone: '13600136666',
notices: [] as Array<string>,
timeline: buildMockTimeline('YS202605180002', 'Arrived at service location, waiting to start service'),
serviceSummary: '',
progressNote: '',
distanceKm: '5.8km',
allowCheckinRadiusMeters: 120,
lastLocation: null,
trackPoints: [] as Array<DeliveryLocationType>
} as DeliveryOrderType,
{
id: 'mock-order-003',
orderNo: 'YS202605180003',
serviceType: '居家服务',
serviceName: '上门服务',
serviceItems: buildMockServiceItems('mock-order-003', '上门照护'),
elderId: 'elder-003',
elderNameMasked: '张阿姨',
elderGender: '女',
elderAge: 68,
elderPhoneMasked: '135****5566',
fullElderName: '张阿姨',
fullPhone: '13500135566',
addressSummary: '暂无地址',
fullAddress: '暂无地址',
latitude: 24.2861,
longitude: 116.1231,
appointmentStartTime: '2026-05-18 13:30',
appointmentEndTime: '2026-05-18 15:00',
estimatedDuration: 90,
actualStartTime: '',
actualEndTime: '',
status: 'arrived',
statusText: '待处理',
statusTone: 'primary',
riskTags: [] as Array<string>,
careLevel: '基础照护',
merchantId: 'merchant-001',
merchantName: '安心到家服务站',
deliveryStaffId: profile.id,
deliveryStaffName: profile.name,
acceptTime: '2026-05-18 08:10:00',
departTime: '2026-05-18 13:00:00',
arriveTime: '2026-05-18 13:22:00',
checkinTime: '',
finishTime: '',
cancelReason: '',
exceptionType: '',
exceptionDesc: '',
evidenceList: [] as Array<DeliveryEvidenceRecordType>,
signatureUrl: '',
signatureName: '',
satisfactionStatus: '待评价',
settlementStatus: '待结算',
archiveStatus: '未归档',
createdAt: '2026-05-18 08:00:00',
updatedAt: '2026-05-18 13:22:00',
contactName: '家属',
contactPhone: '13500130088',
notices: [] as Array<string>,
timeline: buildMockTimeline('YS202605180003', '状态更新'),
serviceSummary: '',
progressNote: '服务状态已更新',
distanceKm: '1.1km',
allowCheckinRadiusMeters: 80,
lastLocation: {
latitude: 24.2861,
longitude: 116.1231,
address: '暂无地址',
time: '2026-05-18 13:22:00'
} as DeliveryLocationType,
trackPoints: [] as Array<DeliveryLocationType>
} as DeliveryOrderType,
{
id: 'mock-order-004',
orderNo: 'YS202605180004',
serviceType: '居家服务',
serviceName: '上门服务',
serviceItems: [
{
id: 'mock-order-004-item-1',
name: '配送员',
required: true,
completed: true,
incompleteReason: '',
remark: '已记录服务情况',
updatedAt: '2026-05-18 14:25:00'
} as DeliveryServiceItemType,
{
id: 'mock-order-004-item-2',
name: '配送员',
required: true,
completed: true,
incompleteReason: '',
remark: '已记录服务情况',
updatedAt: '2026-05-18 14:40:00'
} as DeliveryServiceItemType,
{
id: 'mock-order-004-item-3',
name: '配送员',
required: true,
completed: false,
incompleteReason: '',
remark: '',
updatedAt: ''
} as DeliveryServiceItemType
] as Array<DeliveryServiceItemType>,
elderId: 'elder-004',
elderNameMasked: '张阿姨',
elderGender: '女',
elderAge: 75,
elderPhoneMasked: '139****3301',
fullElderName: '张阿姨',
fullPhone: '13900133301',
addressSummary: '暂无地址',
fullAddress: '暂无地址',
latitude: 24.3022,
longitude: 116.1441,
appointmentStartTime: '2026-05-18 14:00',
appointmentEndTime: '2026-05-18 16:00',
estimatedDuration: 120,
actualStartTime: '2026-05-18 14:05:00',
actualEndTime: '',
status: 'serving',
statusText: '待处理',
statusTone: 'primary',
riskTags: [] as Array<string>,
careLevel: '基础照护',
merchantId: 'merchant-001',
merchantName: '安心到家服务站',
deliveryStaffId: profile.id,
deliveryStaffName: profile.name,
acceptTime: '2026-05-18 09:20:00',
departTime: '2026-05-18 13:20:00',
arriveTime: '2026-05-18 13:55:00',
checkinTime: '2026-05-18 14:00:00',
finishTime: '',
cancelReason: '',
exceptionType: '',
exceptionDesc: '',
evidenceList: buildMockEvidence('mock-order-004'),
signatureUrl: '',
signatureName: '',
satisfactionStatus: '待评价',
settlementStatus: '待结算',
archiveStatus: '未归档',
createdAt: '2026-05-18 09:00:00',
updatedAt: '2026-05-18 14:40:00',
contactName: '家属',
contactPhone: '13800136660',
notices: [] as Array<string>,
timeline: buildMockTimeline('YS202605180004', '状态更新'),
serviceSummary: '已完成基础服务',
progressNote: '服务状态已更新',
distanceKm: '3.6km',
allowCheckinRadiusMeters: 100,
lastLocation: {
latitude: 24.3022,
longitude: 116.1441,
address: '暂无地址',
time: '2026-05-18 14:00:00'
} as DeliveryLocationType,
trackPoints: [
{
latitude: 24.3001,
longitude: 116.1412,
address: '暂无地址',
time: '2026-05-18 13:48:00'
} as DeliveryLocationType,
{
latitude: 24.3022,
longitude: 116.1441,
address: '暂无地址',
time: '2026-05-18 14:00:00'
} as DeliveryLocationType
] as Array<DeliveryLocationType>
} as DeliveryOrderType,
{
id: 'mock-order-005',
orderNo: 'YS202605180005',
serviceType: '居家服务',
serviceName: '上门服务',
serviceItems: buildMockServiceItems('mock-order-005', '上门照护'),
elderId: 'elder-005',
elderNameMasked: '张阿姨',
elderGender: '女',
elderAge: 80,
elderPhoneMasked: '134****7744',
fullElderName: '张阿姨',
fullPhone: '13400137744',
addressSummary: '暂无地址',
fullAddress: '暂无地址',
latitude: 24.2932,
longitude: 116.1205,
appointmentStartTime: '2026-05-18 16:00',
appointmentEndTime: '2026-05-18 17:00',
estimatedDuration: 60,
actualStartTime: '2026-05-18 16:05:00',
actualEndTime: '',
status: 'exception_pending',
statusText: '待处理',
statusTone: 'warning',
riskTags: ['Medication issue', 'Doctor follow-up required'],
careLevel: '基础照护',
merchantId: 'merchant-001',
merchantName: '安心到家服务站',
deliveryStaffId: profile.id,
deliveryStaffName: profile.name,
acceptTime: '2026-05-18 15:20:00',
departTime: '2026-05-18 15:35:00',
arriveTime: '2026-05-18 15:58:00',
checkinTime: '2026-05-18 16:03:00',
finishTime: '',
cancelReason: '',
exceptionType: 'elder_health_abnormal',
exceptionDesc: '服务异常,请及时处理',
evidenceList: buildMockEvidence('mock-order-005'),
signatureUrl: '',
signatureName: '',
satisfactionStatus: '待评价',
settlementStatus: '待结算',
archiveStatus: '未归档',
createdAt: '2026-05-18 15:00:00',
updatedAt: '2026-05-18 16:10:00',
contactName: '家属',
contactPhone: '13600139922',
notices: [] as Array<string>,
timeline: buildMockTimeline('YS202605180005', '状态更新'),
serviceSummary: '已完成基础服务',
progressNote: '服务状态已更新',
distanceKm: '4.2km',
allowCheckinRadiusMeters: 100,
lastLocation: {
latitude: 24.2932,
longitude: 116.1205,
address: '暂无地址',
time: '2026-05-18 16:03:00'
} as DeliveryLocationType,
trackPoints: [] as Array<DeliveryLocationType>
} as DeliveryOrderType,
{
id: 'mock-order-006',
orderNo: 'YS202605170006',
serviceType: '居家服务',
serviceName: '上门服务',
serviceItems: [
{
id: 'mock-order-006-item-1',
name: '配送员',
required: true,
completed: true,
incompleteReason: '',
remark: '已记录服务情况',
updatedAt: '2026-05-17 15:10:00'
} as DeliveryServiceItemType,
{
id: 'mock-order-006-item-2',
name: '配送员',
required: true,
completed: true,
incompleteReason: '',
remark: '已记录服务情况',
updatedAt: '2026-05-17 15:28:00'
} as DeliveryServiceItemType
] as Array<DeliveryServiceItemType>,
elderId: 'elder-006',
elderNameMasked: '张阿姨',
elderGender: '女',
elderAge: 70,
elderPhoneMasked: '133****6655',
fullElderName: '张阿姨',
fullPhone: '13300136655',
addressSummary: '暂无地址',
fullAddress: '暂无地址',
latitude: 24.2806,
longitude: 116.1115,
appointmentStartTime: '2026-05-17 14:00',
appointmentEndTime: '2026-05-17 15:30',
estimatedDuration: 90,
actualStartTime: '2026-05-17 14:05:00',
actualEndTime: '2026-05-17 15:32:00',
status: 'completed',
statusText: '待处理',
statusTone: 'success',
riskTags: [] as Array<string>,
careLevel: '基础照护',
merchantId: 'merchant-001',
merchantName: '安心到家服务站',
deliveryStaffId: profile.id,
deliveryStaffName: profile.name,
acceptTime: '2026-05-17 12:40:00',
departTime: '2026-05-17 13:20:00',
arriveTime: '2026-05-17 13:55:00',
checkinTime: '2026-05-17 14:00:00',
finishTime: '2026-05-17 15:32:00',
cancelReason: '',
exceptionType: '',
exceptionDesc: '',
evidenceList: buildMockEvidence('mock-order-006'),
signatureUrl: '',
signatureName: '家属确认',
satisfactionStatus: '待评价',
settlementStatus: '待结算',
archiveStatus: '未归档',
createdAt: '2026-05-17 12:20:00',
updatedAt: '2026-05-17 15:32:00',
contactName: '家属',
contactPhone: '13800132299',
notices: [] as Array<string>,
timeline: buildMockTimeline('YS202605170006', '状态更新'),
serviceSummary: '已完成基础服务',
progressNote: '服务状态已更新',
distanceKm: '6.1km',
allowCheckinRadiusMeters: 100,
lastLocation: {
latitude: 24.2806,
longitude: 116.1115,
address: '暂无地址',
time: '2026-05-17 15:32:00'
} as DeliveryLocationType,
trackPoints: [] as Array<DeliveryLocationType>
} as DeliveryOrderType
] as Array<DeliveryOrderType>
}
function readMockDeliveryInfo(): DeliveryInfoType | null {
const cached = uni.getStorageSync(DELIVERY_MOCK_PROFILE_KEY)
if (!isValidDeliveryInfoCache(cached)) {
return null
}
return cached as DeliveryInfoType
}
function persistMockDeliveryInfo(info: DeliveryInfoType): void {
uni.setStorageSync(DELIVERY_MOCK_PROFILE_KEY, cloneDeliveryInfo(info))
}
function readMockOrders(): Array<DeliveryOrderType> | null {
const cached = uni.getStorageSync(DELIVERY_MOCK_ORDERS_KEY)
if (!isValidDeliveryOrdersCache(cached)) {
return null
}
return cached as Array<DeliveryOrderType>
}
function persistMockOrders(orders: Array<DeliveryOrderType>): void {
uni.setStorageSync(DELIVERY_MOCK_ORDERS_KEY, cloneDeliveryOrders(orders))
}
function ensureMockProfile(userId: string): DeliveryInfoType {
const cached = readMockDeliveryInfo()
if (cached != null) {
if (cached.userId == '' && userId != '') {
cached.userId = userId
persistMockDeliveryInfo(cached)
}
return cached
}
const profile = buildMockProfile(userId)
persistMockDeliveryInfo(profile)
return profile
}
function ensureMockOrders(userId: string): Array<DeliveryOrderType> {
const cached = readMockOrders()
if (cached != null && cached.length > 0) {
return cached
}
const profile = ensureMockProfile(userId)
const orders = buildMockOrders(profile)
persistMockOrders(orders)
return orders
}
function isPendingDepartStatus(status: string): boolean {
return status == 'accepted' || status == 'on_the_way' || status == 'arrived'
}
function isServingStatus(status: string): boolean {
return status == 'checked_in' || status == 'serving' || status == 'pending_submit' || status == 'pending_acceptance'
}
function isCompletedStatus(status: string): boolean {
return status == 'completed' || status == 'settled' || status == 'archived'
}
function buildMockProfileWithStats(userId: string): DeliveryInfoType {
const profile = cloneDeliveryInfo(ensureMockProfile(userId))
const orders = ensureMockOrders(userId)
let acceptedCount = 0
let servingCount = 0
let completedCount = 0
for (let i = 0; i < orders.length; i++) {
if (orders[i].acceptTime != '') {
acceptedCount++
}
if (isServingStatus(orders[i].status)) {
servingCount++
}
if (isCompletedStatus(orders[i].status)) {
completedCount++
}
}
profile.todayAccepted = acceptedCount
profile.todayServing = servingCount
profile.todayCompleted = completedCount
profile.usesMock = true
return profile
}
function buildMockMessages(orders: Array<DeliveryOrderType>): Array<DeliveryMessageType> {
return [
{
id: 'mock-msg-1',
title: '服务通知',
content: '服务状态已更新,请及时查看。',
type: 'order',
createdAt: '2026-05-18 08:20',
read: false,
orderId: orders[0].id
} as DeliveryMessageType,
{
id: 'mock-msg-2',
title: '服务通知',
content: '服务状态已更新,请及时查看。',
type: 'reminder',
createdAt: '2026-05-18 10:00',
read: false,
orderId: orders[1].id
} as DeliveryMessageType,
{
id: 'mock-msg-3',
title: '服务通知',
content: '服务状态已更新,请及时查看。',
type: 'exception',
createdAt: '2026-05-18 10:35',
read: false,
orderId: orders[4].id
} as DeliveryMessageType,
{
id: 'mock-msg-4',
title: '服务通知',
content: '服务状态已更新,请及时查看。',
type: 'dispatch',
createdAt: '2026-05-18 11:10',
read: false,
orderId: orders[3].id
} as DeliveryMessageType,
{
id: 'mock-msg-5',
title: '服务通知',
content: '服务状态已更新,请及时查看。',
type: 'checkin',
createdAt: '2026-05-18 13:20',
read: true,
orderId: orders[2].id
} as DeliveryMessageType,
{
id: 'mock-msg-6',
title: '服务通知',
content: '服务状态已更新,请及时查看。',
type: 'result',
createdAt: '2026-05-17 18:40',
read: true,
orderId: orders[5].id
} as DeliveryMessageType,
{
id: 'mock-msg-7',
title: 'Settlement notice',
content: '服务状态已更新,请及时查看。',
type: 'settlement',
createdAt: '2026-05-17 09:15',
read: true,
orderId: ''
} as DeliveryMessageType,
{
id: 'mock-msg-8',
title: '服务通知',
content: '服务状态已更新,请及时查看。',
type: 'notice',
createdAt: '2026-05-16 16:00',
read: true,
orderId: ''
} as DeliveryMessageType
] as Array<DeliveryMessageType>
}
function buildMockRecords(orders: Array<DeliveryOrderType>): Array<DeliveryRecordType> {
const result = [] as Array<DeliveryRecordType>
for (let i = 0; i < orders.length; i++) {
const item = orders[i]
if (isCompletedStatus(item.status) || item.status == 'exception_pending') {
result.push({
id: 'record-' + item.id,
orderId: item.id,
orderNo: item.orderNo,
serviceName: item.serviceName,
elderNameMasked: item.elderNameMasked,
status: item.status,
statusText: item.statusText,
appointmentStartTime: item.appointmentStartTime,
actualStartTime: item.actualStartTime,
actualEndTime: item.actualEndTime != '' ? item.actualEndTime : item.updatedAt,
settlementStatus: item.settlementStatus,
acceptanceStatus: item.satisfactionStatus,
exceptionDesc: item.exceptionDesc,
ratingText: '待评价'
} as DeliveryRecordType)
}
}
return result
}
function findMockOrderIndex(orders: Array<DeliveryOrderType>, orderId: string): number {
for (let i = 0; i < orders.length; i++) {
if (orders[i].id == orderId) {
return i
}
}
return -1
}
function sortMockRecentOrders(orders: Array<DeliveryOrderType>): Array<DeliveryOrderType> {
const pendingException = [] as Array<DeliveryOrderType>
const actionable = [] as Array<DeliveryOrderType>
const completed = [] as Array<DeliveryOrderType>
for (let i = 0; i < orders.length; i++) {
const item = orders[i]
if (item.status == 'exception_pending') {
pendingException.push(item)
} else if (!isCompletedStatus(item.status)) {
actionable.push(item)
} else {
completed.push(item)
}
}
return pendingException.concat(actionable).concat(completed)
}
function filterMockOrders(orders: Array<DeliveryOrderType>, query: DeliveryOrderQueryType): Array<DeliveryOrderType> {
const keyword = query.keyword.trim()
const result = [] as Array<DeliveryOrderType>
for (let i = 0; i < orders.length; i++) {
const item = orders[i]
let tabMatched = false
if (query.tab == 'all' || query.tab == '') {
tabMatched = true
} else if (query.tab == 'pending_accept') {
tabMatched = item.status == 'pending_accept'
} else if (query.tab == 'accepted') {
tabMatched = isPendingDepartStatus(item.status)
} else if (query.tab == 'serving') {
tabMatched = isServingStatus(item.status)
} else if (query.tab == 'pending_submit') {
tabMatched = item.status == 'pending_submit' || item.status == 'pending_acceptance'
} else if (query.tab == 'completed') {
tabMatched = isCompletedStatus(item.status)
} else if (query.tab == 'exception') {
tabMatched = item.status == 'exception_pending'
}
if (!tabMatched) {
continue
}
if (keyword != '') {
const matched = item.orderNo.includes(keyword) || item.serviceName.includes(keyword) || item.elderNameMasked.includes(keyword) || item.addressSummary.includes(keyword)
if (!matched) {
continue
}
}
result.push(cloneDeliveryOrder(item))
}
return result
}
function readCachedDeliveryInfo(userId: string): DeliveryInfoType | null {
const cached = uni.getStorageSync('delivery_info')
if (!isValidDeliveryInfoCache(cached)) {
return null
}
const info = cached as DeliveryInfoType
if (userId == '' || info.userId == userId || info.id == userId) {
return info
}
return null
}
async function fallbackGetDeliveryProfileByUserId(userId: string): Promise<DeliveryInfoType | null> {
const cached = readCachedDeliveryInfo(userId)
if (cached != null) {
return cached
}
return buildMockProfileWithStats(userId)
}
async function fallbackGetDashboard(staffId: string): Promise<DeliveryDashboardType> {
const profile = buildMockProfileWithStats(staffId)
const orders = ensureMockOrders(staffId)
let pendingAcceptCount = 0
let pendingDepartCount = 0
let servingCount = 0
let completedCount = 0
let exceptionCount = 0
for (let i = 0; i < orders.length; i++) {
const status = orders[i].status
if (status == 'pending_accept') {
pendingAcceptCount++
} else if (isPendingDepartStatus(status)) {
pendingDepartCount++
} else if (isServingStatus(status)) {
servingCount++
} else if (status == 'exception_pending') {
exceptionCount++
} else if (isCompletedStatus(status)) {
completedCount++
}
}
return {
pendingAcceptCount,
pendingDepartCount,
servingCount,
completedCount,
exceptionCount,
onlineStatus: profile.onlineStatus,
recentOrders: sortMockRecentOrders(cloneDeliveryOrders(orders)).slice(0, 5) as Array<DeliveryOrderType>
} as DeliveryDashboardType
}
async function fallbackGetOrders(staffId: string, query: DeliveryOrderQueryType): Promise<Array<DeliveryOrderType>> {
const orders = ensureMockOrders(staffId)
return filterMockOrders(orders, query)
}
async function fallbackGetOrderDetail(orderId: string): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
return cloneDeliveryOrder(orders[index])
}
async function fallbackAcceptOrder(orderId: string): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].status = 'accepted'
orders[index].statusText = '服务中'
orders[index].statusTone = 'primary'
orders[index].acceptTime = currentDateTimeText()
orders[index].updatedAt = currentDateTimeText()
orders[index].timeline.push({
id: orders[index].orderNo + '-accepted',
title: '服务通知',
time: orders[index].updatedAt,
description: '服务状态已更新'
} as DeliveryTimelineItemType)
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackRejectOrder(orderId: string, _reason: string): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].status = 'cancelled'
orders[index].statusText = '已完成'
orders[index].statusTone = 'neutral'
orders[index].updatedAt = currentDateTimeText()
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackStartDepart(orderId: string, _location: DeliveryLocationType): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].status = 'on_the_way'
orders[index].statusText = '已取消'
orders[index].statusTone = 'primary'
orders[index].departTime = currentDateTimeText()
orders[index].updatedAt = orders[index].departTime
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackArriveOrder(orderId: string, _location: DeliveryLocationType): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].status = 'arrived'
orders[index].statusText = '异常'
orders[index].statusTone = 'primary'
orders[index].arriveTime = currentDateTimeText()
orders[index].updatedAt = orders[index].arriveTime
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackCheckinOrder(orderId: string, payload: DeliveryCheckinPayloadType): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].status = 'checked_in'
orders[index].statusText = '服务中'
orders[index].statusTone = 'primary'
orders[index].checkinTime = currentDateTimeText()
orders[index].updatedAt = orders[index].checkinTime
orders[index].lastLocation = payload.location
if (payload.photos.length > 0) {
for (let i = 0; i < payload.photos.length; i++) {
orders[index].evidenceList.push({
id: orderId + '-checkin-photo-' + String(i + 1),
orderId,
phase: 'checkin',
fileType: 'image',
name: '配送员',
url: '',
localPath: payload.photos[i],
status: 'success',
progress: 100,
retryable: false,
createdAt: currentDateTimeText()
} as DeliveryEvidenceRecordType)
}
}
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackStartService(orderId: string): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].status = 'serving'
orders[index].statusText = '待处理'
orders[index].statusTone = 'primary'
if (orders[index].actualStartTime == '') {
orders[index].actualStartTime = currentDateTimeText()
}
orders[index].updatedAt = currentDateTimeText()
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackSaveProgress(orderId: string, payload: DeliveryProgressPayloadType): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].serviceItems = JSON.parse(JSON.stringify(payload.items)) as Array<DeliveryServiceItemType>
orders[index].progressNote = payload.progressNote
orders[index].serviceSummary = payload.serviceSummary
orders[index].updatedAt = currentDateTimeText()
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackUploadEvidence(orderId: string, phase: string, files: Array<string>): Promise<Array<DeliveryEvidenceRecordType>> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return [] as Array<DeliveryEvidenceRecordType>
}
const added = [] as Array<DeliveryEvidenceRecordType>
for (let i = 0; i < files.length; i++) {
const item = {
id: orderId + '-evidence-' + String(orders[index].evidenceList.length + i + 1),
orderId,
phase,
fileType: 'image',
name: '配送员',
url: '',
localPath: files[i],
status: 'success',
progress: 100,
retryable: false,
createdAt: currentDateTimeText()
} as DeliveryEvidenceRecordType
orders[index].evidenceList.push(item)
added.push(item)
}
orders[index].updatedAt = currentDateTimeText()
persistMockOrders(orders)
return JSON.parse(JSON.stringify(added)) as Array<DeliveryEvidenceRecordType>
}
async function fallbackRetryEvidence(orderId: string, evidenceId: string): Promise<DeliveryEvidenceRecordType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
for (let i = 0; i < orders[index].evidenceList.length; i++) {
if (orders[index].evidenceList[i].id == evidenceId) {
orders[index].evidenceList[i].status = 'success'
orders[index].evidenceList[i].progress = 100
orders[index].evidenceList[i].retryable = false
persistMockOrders(orders)
return orders[index].evidenceList[i]
}
}
return null
}
async function fallbackSubmitException(orderId: string, payload: DeliveryExceptionPayloadType): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].status = 'exception_pending'
orders[index].statusText = '已拒单'
orders[index].statusTone = 'warning'
orders[index].exceptionType = payload.exceptionType
orders[index].exceptionDesc = payload.description
orders[index].updatedAt = currentDateTimeText()
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackFinishService(orderId: string, payload: DeliveryFinishPayloadType): Promise<DeliveryOrderType | null> {
const orders = ensureMockOrders('')
const index = findMockOrderIndex(orders, orderId)
if (index < 0) {
return null
}
orders[index].status = 'completed'
orders[index].statusText = '已完成'
orders[index].statusTone = 'success'
orders[index].serviceSummary = payload.serviceSummary
orders[index].signatureName = payload.signatureName
orders[index].actualEndTime = currentDateTimeText()
orders[index].finishTime = orders[index].actualEndTime
orders[index].satisfactionStatus = '待评价'
orders[index].settlementStatus = '待结算'
orders[index].archiveStatus = '未归档'
orders[index].updatedAt = orders[index].actualEndTime
persistMockOrders(orders)
return cloneDeliveryOrder(orders[index])
}
async function fallbackGetRecords(staffId: string): Promise<Array<DeliveryRecordType>> {
const orders = ensureMockOrders(staffId)
return cloneDeliveryRecords(buildMockRecords(orders))
}
async function fallbackGetMessages(): Promise<Array<DeliveryMessageType>> {
const orders = ensureMockOrders('')
return cloneDeliveryMessages(buildMockMessages(orders))
}
async function fallbackUpdateOnlineStatus(staffId: string, status: string): Promise<DeliveryInfoType | null> {
const cachedInfo = buildMockProfileWithStats(staffId)
const nextInfo = {
...cachedInfo,
onlineStatus: status as 'online' | 'resting' | 'busy'
} as DeliveryInfoType
persistMockDeliveryInfo(nextInfo)
return nextInfo
}
export async function loginDelivery(payload: DeliveryLoginPayloadType): Promise<DeliveryLoginResultType> {
const account = payload.account.trim()
if (!account.includes('@')) {
throw new Error('请输入有效的邮箱账号')
}
const result = await supa.signIn(account, payload.password)
if (result.user == null) {
const rawData = result.raw as UTSJSONObject
throw new Error(mapLoginError(rawData))
}
const profile = await getCurrentUser()
if (profile == null || profile.id == null || profile.id == '') {
try {
await supa.signOut()
} catch (error) {}
throw new Error('登录失败,未获取到用户信息')
}
if (!isDelivererRole(profile)) {
try {
await supa.signOut()
} catch (error) {}
throw new Error('当前账号不是配送员账号')
}
let deliveryInfo = await fetchDeliveryProfileFromRemote(profile.id)
let usesMock = false
if (deliveryInfo == null) {
if (IS_TEST_MODE) {
deliveryInfo = ensureMockProfile(profile.id)
usesMock = true
console.log('[delivery api] 使用 mock 配送员档案')
} else {
try {
await supa.signOut()
} catch (error) {}
throw new Error('当前账号不是配送员账号')
}
}
return {
token: result.access_token,
userInfo: profile,
deliveryInfo,
usesMock
}
}
export async function getDeliveryProfileByUserId(userId: string): Promise<DeliveryInfoType | null> {
const remoteInfo = await fetchDeliveryProfileFromRemote(userId)
if (remoteInfo != null) {
return remoteInfo
}
return await fallbackGetDeliveryProfileByUserId(userId)
}
export async function getDeliveryDashboardByStaffId(staffId: string): Promise<DeliveryDashboardType> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_DASHBOARD, {
p_staff_id: staffId
} as UTSJSONObject)
const dashboard = normalizeRpcDashboard(rpcData)
if (dashboard != null) {
return dashboard
}
return await fallbackGetDashboard(staffId)
}
export async function getDeliveryOrdersByStaffId(staffId: string, query: DeliveryOrderQueryType): Promise<Array<DeliveryOrderType>> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_ORDER_LIST, {
p_staff_id: staffId,
p_tab: query.tab,
p_keyword: query.keyword
} as UTSJSONObject)
const orders = normalizeRpcOrderList(rpcData)
if (orders != null) {
for (let i = 0; i < orders.length; i++) {
orders[i] = await enrichOrderWithRequestFallback(rpcData[i], orders[i])
}
return orders
}
return await fallbackGetOrders(staffId, query)
}
export async function getDeliveryOrderDetailById(orderId: string): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_ORDER_DETAIL, {
p_order_id: orderId
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return await enrichOrderWithRequestFallback(rpcData, order)
}
return await fallbackGetOrderDetail(orderId)
}
export async function acceptDeliveryOrderById(orderId: string): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_ACCEPT_ORDER, {
p_order_id: orderId
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackAcceptOrder(orderId)
}
/**
* 兼容旧数据处理
* 兼容旧数据处理
*/
export async function acceptHomecareAssignmentV2(workOrderId: string, workerId: string): Promise<UTSJSONObject | null> {
console.log('[delivery api] 使用 mock 配送员档案')
const rpcData = await callDeliveryRpc(DELIVERY_RPC_HOMECARE_ACCEPT_ASSIGNMENT_V2, {
p_work_order_id: workOrderId,
p_worker_id: workerId
} as UTSJSONObject)
console.warn('[HOMECARE ACCEPT] rpc_homecare_accept_assignment_v2 result:', rpcData)
return rpcData as UTSJSONObject | null
}
export async function rejectDeliveryOrderById(orderId: string, reason: string): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_REJECT_ORDER, {
p_order_id: orderId,
p_reason: reason
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackRejectOrder(orderId, reason)
}
export async function startDepartById(orderId: string, location: DeliveryLocationType): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_START_DEPART, {
p_order_id: orderId,
p_location: location
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackStartDepart(orderId, location)
}
export async function arriveOrderById(orderId: string, location: DeliveryLocationType): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_ARRIVE_ORDER, {
p_order_id: orderId,
p_location: location
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackArriveOrder(orderId, location)
}
export async function checkinOrderById(orderId: string, payload: DeliveryCheckinPayloadType): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_CHECKIN_ORDER, {
p_order_id: orderId,
p_payload: payload
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackCheckinOrder(orderId, payload)
}
/**
* 兼容旧数据处理
* 兼容旧数据处理
*/
export async function submitHomecareCheckin(
workOrderId: string,
workerId: string,
latitude: number,
longitude: number,
coordinateType: string,
accuracy: number,
reportedAt: string,
evidenceFileIds: Array<string>,
signaturePayload: any | null = null,
reason: string = 'worker_arrived'
): Promise<UTSJSONObject | null> {
console.log('[delivery api] 使用 mock 配送员档案')
console.warn('[CHECKIN SUBMIT] workOrderId=', workOrderId, ' workerId=', workerId)
console.warn('[CHECKIN SUBMIT] lat=', latitude, ' lng=', longitude, ' accuracy=', accuracy)
console.warn('[CHECKIN SUBMIT] evidenceFileIds=', evidenceFileIds)
const rpcParams = new UTSJSONObject()
rpcParams.set('p_work_order_id', workOrderId)
rpcParams.set('p_worker_id', workerId)
rpcParams.set('p_latitude', latitude)
rpcParams.set('p_longitude', longitude)
rpcParams.set('p_coordinate_type', coordinateType)
rpcParams.set('p_accuracy', accuracy)
rpcParams.set('p_reported_at', reportedAt)
rpcParams.set('p_evidence_file_ids', evidenceFileIds)
rpcParams.set('p_signature_payload', signaturePayload)
rpcParams.set('p_reason', reason)
const rpcData = await callDeliveryRpc(DELIVERY_RPC_HOMECARE_CHECKIN_SUBMIT, rpcParams)
console.warn('[CHECKIN SUBMIT] rpc_homecare_checkin_submit result:', rpcData)
return rpcData as UTSJSONObject | null
}
export async function startServiceById(orderId: string): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_START_SERVICE, {
p_order_id: orderId
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackStartService(orderId)
}
export async function saveServiceProgressById(orderId: string, payload: DeliveryProgressPayloadType): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_SAVE_PROGRESS, {
p_order_id: orderId,
p_payload: payload
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackSaveProgress(orderId, payload)
}
export async function uploadEvidenceById(orderId: string, phase: string, files: Array<string>): Promise<Array<DeliveryEvidenceRecordType>> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_UPLOAD_EVIDENCE, {
p_order_id: orderId,
p_phase: phase,
p_files: files
} as UTSJSONObject)
const evidenceList = normalizeRpcEvidenceList(rpcData)
if (evidenceList != null) {
return evidenceList
}
return await fallbackUploadEvidence(orderId, phase, files)
}
export async function retryEvidenceById(orderId: string, evidenceId: string): Promise<DeliveryEvidenceRecordType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_RETRY_EVIDENCE, {
p_order_id: orderId,
p_evidence_id: evidenceId
} as UTSJSONObject)
if (rpcData != null) {
return rpcData as DeliveryEvidenceRecordType
}
return await fallbackRetryEvidence(orderId, evidenceId)
}
export async function submitExceptionById(orderId: string, payload: DeliveryExceptionPayloadType): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_SUBMIT_EXCEPTION, {
p_order_id: orderId,
p_payload: payload
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackSubmitException(orderId, payload)
}
export async function finishServiceById(orderId: string, payload: DeliveryFinishPayloadType): Promise<DeliveryOrderType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_FINISH_SERVICE, {
p_order_id: orderId,
p_payload: payload
} as UTSJSONObject)
const order = normalizeRpcOrder(rpcData)
if (order != null) {
return order
}
return await fallbackFinishService(orderId, payload)
}
export async function getDeliveryRecordsByStaffId(staffId: string): Promise<Array<DeliveryRecordType>> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_RECORD_LIST, {
p_staff_id: staffId
} as UTSJSONObject)
const records = normalizeRpcRecords(rpcData)
if (records != null) {
return records
}
return await fallbackGetRecords(staffId)
}
export async function getDeliveryMessagesList(): Promise<Array<DeliveryMessageType>> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_MESSAGE_LIST, {} as UTSJSONObject)
const messages = normalizeRpcMessages(rpcData)
if (messages != null && messages.length > 0) {
return messages
}
return await fallbackGetMessages()
}
export async function updateDeliveryOnlineStatusByStaffId(staffId: string, status: string): Promise<DeliveryInfoType | null> {
const rpcData = await callDeliveryRpc(DELIVERY_RPC_SET_ONLINE_STATUS, {
p_staff_id: staffId,
p_status: status
} as UTSJSONObject)
const deliveryInfo = normalizeRpcDeliveryInfo(rpcData)
if (deliveryInfo != null) {
return deliveryInfo
}
return await fallbackUpdateOnlineStatus(staffId, status)
}
// ========================
// Compat RPC functions for so-xxx order ID support
// ========================
/**
* 兼容旧数据处理
*/
export async function checkinPrecheckCompat(
orderId: string,
workerId: string,
latitude: number,
longitude: number,
accuracy: number
): Promise<UTSJSONObject> {
console.log('[DELIVERY_SERVICE_TRACE][checkinPrecheckCompat][START]', {
orderId: orderId,
workerId: workerId,
latitude: latitude,
longitude: longitude,
accuracy: accuracy
})
const params = new UTSJSONObject()
params.set('p_order_id', orderId)
params.set('p_worker_id', workerId)
params.set('p_latitude', latitude)
params.set('p_longitude', longitude)
params.set('p_coordinate_type', 'gcj02')
params.set('p_accuracy', accuracy)
params.set('p_reported_at', new Date().toISOString())
console.warn('[CHECKIN PRECHECK COMPAT] request:', JSON.stringify(params))
try {
const res: any = await supa.rpc(DELIVERY_RPC_HOMECARE_CHECKIN_PRECHECK_COMPAT, params)
console.warn('[CHECKIN PRECHECK COMPAT] data =', res)
if (res?.error != null) {
const error = res.error as UTSJSONObject
console.warn('[CHECKIN PRECHECK COMPAT] error =', JSON.stringify(error))
const result = new UTSJSONObject()
result.set('ok', false)
result.set('canCheckin', false)
result.set('reasonCode', error.getString('code') ?? 'RPC_ERROR')
result.set('message', error.getString('message') ?? '操作失败,请稍后重试')
result.set('debug', error)
console.log('[DELIVERY_SERVICE_TRACE][checkinPrecheckCompat][SUCCESS]', {
result: result
})
return result
}
const data = res?.data as UTSJSONObject | null
if (data != null) {
console.log('[DELIVERY_SERVICE_TRACE][checkinPrecheckCompat][SUCCESS]', {
result: data
})
return data
}
// 兼容旧数据处理
const fallback = new UTSJSONObject()
fallback.set('ok', false)
fallback.set('canCheckin', false)
fallback.set('reasonCode', 'RPC_ERROR')
fallback.set('message', '操作失败,请稍后重试')
console.log('[DELIVERY_SERVICE_TRACE][checkinPrecheckCompat][SUCCESS]', {
result: fallback
})
return fallback
} catch (error) {
console.error('[DELIVERY_SERVICE_TRACE][checkinPrecheckCompat][ERROR]', {
errorMessage: error != null ? String(error.message ?? error) : '',
errorStack: error != null ? String(error.stack ?? '') : '',
params: {
orderId: orderId,
workerId: workerId,
latitude: latitude,
longitude: longitude,
accuracy: accuracy
}
})
console.warn('[CHECKIN PRECHECK COMPAT] error =', error)
const result = new UTSJSONObject()
result.set('ok', false)
result.set('canCheckin', false)
result.set('reasonCode', 'RPC_ERROR')
result.set('message', '操作失败,请稍后重试')
result.set('debug', error)
return result
}
}
/**
* 兼容旧数据处理
*/
export async function createCheckinEvidenceCompat(
orderId: string,
uploaderId: string,
fileUrl: string,
note: string
): Promise<UTSJSONObject> {
console.log('[DELIVERY_SERVICE_TRACE][createCheckinEvidenceCompat][START]', {
orderId: orderId,
uploaderId: uploaderId,
fileUrl: fileUrl,
note: note
})
const params = new UTSJSONObject()
params.set('p_order_id', orderId)
params.set('p_uploader_id', uploaderId)
params.set('p_file_url', fileUrl)
params.set('p_note', note)
console.warn('[CREATE EVIDENCE COMPAT] request:', JSON.stringify(params))
try {
const res: any = await supa.rpc(DELIVERY_RPC_CREATE_CHECKIN_EVIDENCE_COMPAT, params)
console.warn('[CREATE EVIDENCE COMPAT] data =', res)
if (res?.error != null) {
const error = res.error as UTSJSONObject
console.warn('[CREATE EVIDENCE COMPAT] error =', JSON.stringify(error))
const result = new UTSJSONObject()
result.set('ok', false)
result.set('reasonCode', error.getString('code') ?? 'RPC_ERROR')
result.set('message', error.getString('message') ?? '操作失败,请稍后重试')
result.set('debug', error)
console.log('[DELIVERY_SERVICE_TRACE][createCheckinEvidenceCompat][SUCCESS]', {
result: result
})
return result
}
const data = res?.data as UTSJSONObject | null
if (data != null) {
console.log('[DELIVERY_SERVICE_TRACE][createCheckinEvidenceCompat][SUCCESS]', {
result: data
})
return data
}
const fallback = new UTSJSONObject()
fallback.set('ok', false)
fallback.set('reasonCode', 'RPC_ERROR')
fallback.set('message', '操作失败,请稍后重试')
console.log('[DELIVERY_SERVICE_TRACE][createCheckinEvidenceCompat][SUCCESS]', {
result: fallback
})
return fallback
} catch (error) {
console.error('[DELIVERY_SERVICE_TRACE][createCheckinEvidenceCompat][ERROR]', {
errorMessage: error != null ? String(error.message ?? error) : '',
errorStack: error != null ? String(error.stack ?? '') : '',
params: {
orderId: orderId,
uploaderId: uploaderId,
fileUrl: fileUrl,
note: note
}
})
console.warn('[CREATE EVIDENCE COMPAT] error =', error)
const result = new UTSJSONObject()
result.set('ok', false)
result.set('reasonCode', 'RPC_ERROR')
result.set('message', '操作失败,请稍后重试')
result.set('debug', error)
return result
}
}
/**
* 兼容旧数据处理
*/
export async function submitHomecareCheckinCompat(
orderId: string,
workerId: string,
latitude: number,
longitude: number,
accuracy: number,
evidenceFileIds: Array<string>,
signaturePayload: any | null,
reason: string
): Promise<UTSJSONObject> {
console.log('[DELIVERY_SERVICE_TRACE][submitHomecareCheckinCompat][START]', {
orderId: orderId,
workerId: workerId,
latitude: latitude,
longitude: longitude,
accuracy: accuracy,
evidenceFileIds: evidenceFileIds,
signaturePayload: signaturePayload,
reason: reason
})
const params = new UTSJSONObject()
params.set('p_order_id', orderId)
params.set('p_worker_id', workerId)
params.set('p_latitude', latitude)
params.set('p_longitude', longitude)
params.set('p_coordinate_type', 'gcj02')
params.set('p_accuracy', accuracy)
params.set('p_reported_at', new Date().toISOString())
params.set('p_evidence_file_ids', evidenceFileIds)
params.set('p_signature_payload', signaturePayload)
params.set('p_reason', reason)
console.warn('[CHECKIN SUBMIT COMPAT] request:', JSON.stringify(params))
try {
const res: any = await supa.rpc(DELIVERY_RPC_HOMECARE_CHECKIN_SUBMIT_COMPAT, params)
console.warn('[CHECKIN SUBMIT COMPAT] data =', res)
if (res?.error != null) {
const error = res.error as UTSJSONObject
console.warn('[CHECKIN SUBMIT COMPAT] error =', JSON.stringify(error))
const result = new UTSJSONObject()
result.set('ok', false)
result.set('reasonCode', error.getString('code') ?? 'RPC_ERROR')
result.set('message', error.getString('message') ?? '操作失败,请稍后重试')
result.set('debug', error)
console.log('[DELIVERY_SERVICE_TRACE][submitHomecareCheckinCompat][SUCCESS]', {
result: result
})
return result
}
const data = res?.data as UTSJSONObject | null
if (data != null) {
console.log('[DELIVERY_SERVICE_TRACE][submitHomecareCheckinCompat][SUCCESS]', {
result: data
})
return data
}
const fallback = new UTSJSONObject()
fallback.set('ok', false)
fallback.set('reasonCode', 'RPC_ERROR')
fallback.set('message', '操作失败,请稍后重试')
console.log('[DELIVERY_SERVICE_TRACE][submitHomecareCheckinCompat][SUCCESS]', {
result: fallback
})
return fallback
} catch (error) {
console.error('[DELIVERY_SERVICE_TRACE][submitHomecareCheckinCompat][ERROR]', {
errorMessage: error != null ? String(error.message ?? error) : '',
errorStack: error != null ? String(error.stack ?? '') : '',
params: {
orderId: orderId,
workerId: workerId,
latitude: latitude,
longitude: longitude,
accuracy: accuracy,
evidenceFileIds: evidenceFileIds,
signaturePayload: signaturePayload,
reason: reason
}
})
console.warn('[CHECKIN SUBMIT COMPAT] error =', error)
const result = new UTSJSONObject()
result.set('ok', false)
result.set('reasonCode', 'RPC_ERROR')
result.set('message', '操作失败,请稍后重试')
result.set('debug', error)
return result
}
}