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 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 { const value = rawValue.trim() if (value == '') { return [] as Array } try { const parsed = JSON.parse(value) if (parsed instanceof Array) { const result = [] as Array 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 } 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, 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 { 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 if (rows.length > 0) { return rows[0] } } catch (error) { console.warn('[delivery api] 查询服务请求失败:', requestId, error) } return null } async function fetchDeliveryProfileFromRemote(userId: string): Promise { 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 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 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 { 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 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 order.healthTags = [] as Array 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 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 order.timeline = [] as Array order.statusLog = [] as Array order.serviceSummary = '' order.progressNote = '' order.distanceKm = '' order.allowCheckinRadiusMeters = 100 order.lastLocation = null order.trackPoints = [] as Array 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 { if (item == null) return [] as Array const value = item[key] if (Array.isArray(value)) { return value } return [] as Array } function rpcStrCompat(item: any, keys: Array): 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): 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): Array { const result = [] as Array 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 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 order.healthTags = rpcArrayCompat(item, 'healthTags') as Array 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 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 order.timeline = mapRpcTimelineCompat(rpcArrayCompat(item, 'timeline')) order.statusLog = rpcArrayCompat(item, 'statusLog') as Array 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 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 { 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 | null { if (!Array.isArray(data)) { return null } const result = [] as Array for (let i = 0; i < data.length; i++) { result.push(mapRpcOrderItemCompat(data[i])) } return result } function normalizeRpcMessages(data: any): Array | null { if (!Array.isArray(data)) { return null } return data as Array } function normalizeRpcRecords(data: any): Array | null { if (!Array.isArray(data)) { return null } return data as Array } function normalizeRpcEvidenceList(data: any): Array | null { if (!Array.isArray(data)) { return null } return data as Array } 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): Array { return JSON.parse(JSON.stringify(orders)) as Array } function cloneDeliveryOrder(order: DeliveryOrderType): DeliveryOrderType { return JSON.parse(JSON.stringify(order)) as DeliveryOrderType } function cloneDeliveryMessages(messages: Array): Array { return JSON.parse(JSON.stringify(messages)) as Array } function cloneDeliveryRecords(records: Array): Array { return JSON.parse(JSON.stringify(records)) as Array } 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 { 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 } function buildMockServiceItems(orderId: string, serviceName: string): Array { 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 } function buildMockTimeline(orderNo: string, phase: string): Array { 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 } function buildMockEvidence(orderId: string): Array { 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 } 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 { 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, careLevel: '基础照护', merchantId: 'merchant-001', merchantName: '安心到家服务站', deliveryStaffId: profile.id, deliveryStaffName: profile.name, acceptTime: '', departTime: '', arriveTime: '', checkinTime: '', finishTime: '', cancelReason: '', exceptionType: '', exceptionDesc: '', evidenceList: [] as Array, 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, 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 } 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, 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, 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, timeline: buildMockTimeline('YS202605180002', 'Arrived at service location, waiting to start service'), serviceSummary: '', progressNote: '', distanceKm: '5.8km', allowCheckinRadiusMeters: 120, lastLocation: null, trackPoints: [] as Array } 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, 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, 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, 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 } 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, 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, 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, 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 } 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, 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 } 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, 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, 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, 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 } as DeliveryOrderType ] as Array } 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 | null { const cached = uni.getStorageSync(DELIVERY_MOCK_ORDERS_KEY) if (!isValidDeliveryOrdersCache(cached)) { return null } return cached as Array } function persistMockOrders(orders: Array): 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 { 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): Array { 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 } function buildMockRecords(orders: Array): Array { const result = [] as Array 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, orderId: string): number { for (let i = 0; i < orders.length; i++) { if (orders[i].id == orderId) { return i } } return -1 } function sortMockRecentOrders(orders: Array): Array { const pendingException = [] as Array const actionable = [] as Array const completed = [] as Array 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, query: DeliveryOrderQueryType): Array { const keyword = query.keyword.trim() const result = [] as Array 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 { const cached = readCachedDeliveryInfo(userId) if (cached != null) { return cached } return buildMockProfileWithStats(userId) } async function fallbackGetDashboard(staffId: string): Promise { 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 } as DeliveryDashboardType } async function fallbackGetOrders(staffId: string, query: DeliveryOrderQueryType): Promise> { const orders = ensureMockOrders(staffId) return filterMockOrders(orders, query) } async function fallbackGetOrderDetail(orderId: string): Promise { const orders = ensureMockOrders('') const index = findMockOrderIndex(orders, orderId) if (index < 0) { return null } return cloneDeliveryOrder(orders[index]) } async function fallbackAcceptOrder(orderId: string): Promise { 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 { 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 { 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 { 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 { 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 { 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 { const orders = ensureMockOrders('') const index = findMockOrderIndex(orders, orderId) if (index < 0) { return null } orders[index].serviceItems = JSON.parse(JSON.stringify(payload.items)) as Array 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): Promise> { const orders = ensureMockOrders('') const index = findMockOrderIndex(orders, orderId) if (index < 0) { return [] as Array } const added = [] as Array 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 } async function fallbackRetryEvidence(orderId: string, evidenceId: string): Promise { 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 { 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 { 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> { const orders = ensureMockOrders(staffId) return cloneDeliveryRecords(buildMockRecords(orders)) } async function fallbackGetMessages(): Promise> { const orders = ensureMockOrders('') return cloneDeliveryMessages(buildMockMessages(orders)) } async function fallbackUpdateOnlineStatus(staffId: string, status: string): Promise { 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 { 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 { const remoteInfo = await fetchDeliveryProfileFromRemote(userId) if (remoteInfo != null) { return remoteInfo } return await fallbackGetDeliveryProfileByUserId(userId) } export async function getDeliveryDashboardByStaffId(staffId: string): Promise { 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> { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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, signaturePayload: any | null = null, reason: string = 'worker_arrived' ): Promise { 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 { 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 { 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): Promise> { 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 { 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 { 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 { 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> { 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> { 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 { 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 { 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 { 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, signaturePayload: any | null, reason: string ): Promise { 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 } }