import type { DeliveryCertificateType, DeliveryCheckinPayloadType, DeliveryDashboardType, DeliveryTimelineItemType, DeliveryExceptionPayloadType, DeliveryFinishPayloadType, DeliveryInfoType, DeliveryLocationType, DeliveryLoginPayloadType, DeliveryLoginResultType, DeliveryMessageType, DeliveryOrderQueryType, DeliveryOrderType, DeliveryProgressPayloadType, DeliveryRecordType, DeliveryEvidenceRecordType, 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_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_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 ((errorMsg.includes('email') && errorMsg.includes('confirm')) || errorCode === 'email_not_confirmed' || (errorMsg.includes('邮箱') && errorMsg.includes('确认'))) { return '邮箱未确认,请先检查邮箱并点击确认链接' } if (errorMsg.includes('Invalid authentication credentials')) { return '网关认证失败,请检查 delivery 端的 SUPA_URL 与 SUPA_KEY 是否属于同一套 Supabase 实例' } if (errorMsg.includes('Invalid login credentials') || errorCode === 'invalid_credentials' || errorMsg.includes('Invalid credentials') || errorMsg.includes('credentials') || errorMsg.includes('invalid')) { 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: readStringField(raw, 'nickname') != '' ? readStringField(raw, 'nickname') : '服务人员', 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 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] 读取 ml_delivery_staff 失败,回退 mock:', 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 { 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 { if (shouldBypassDeliveryRpc()) { return null } if (isMissingDeliveryRpc(functionName)) { return null } try { await ensureSupabaseReady() const res: any = await supa.rpc(functionName, params) if (res?.status === 404) { markMissingDeliveryRpc(functionName) console.warn('[delivery api] RPC 不存在,已切换 fallback:' + functionName) return null } if (res?.error != null) { throw res.error } return res.data } catch (error) { console.warn('[delivery api] RPC 调用失败,回退 mock:' + functionName, error) return null } } function normalizeRpcOrderList(data: any): Array | null { if (!Array.isArray(data)) { return null } return data as Array } 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 data as DeliveryOrderType } 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: '长期有效', issuer: '粤东健康照护培训中心', imageUrl: '' } as DeliveryCertificateType ] as Array } function buildMockServiceItems(orderId: string, serviceName: string): Array { return [ { id: orderId + '-item-1', name: serviceName + '签到核验', 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: ['基础护理', '慢病随访', '康复指导'], 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: '李奶奶', elderPhoneMasked: '138****1024', fullElderName: '李秀珍', fullPhone: '13800131024', addressSummary: '梅江区江南路 18 号 2 栋 602', fullAddress: '广东省梅州市梅江区江南路 18 号 2 栋 602 室', 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: ['跌倒风险', '晨间血压波动'], 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: ['入户前需电话确认', '注意老人步态不稳'], timeline: buildMockTimeline('YS202605180001', '等待服务人员接单'), serviceSummary: '', progressNote: '', distanceKm: '2.4km', allowCheckinRadiusMeters: 100, lastLocation: null, trackPoints: [] as Array } as DeliveryOrderType, { id: 'mock-order-002', orderNo: 'YS202605180002', serviceType: '康复指导', serviceName: '居家康复训练指导', serviceItems: buildMockServiceItems('mock-order-002', '康复训练'), elderId: 'elder-002', elderNameMasked: '张爷爷', elderPhoneMasked: '137****2233', fullElderName: '张志坤', fullPhone: '13700132233', addressSummary: '梅县区新城锦绣花园 5 栋 1403', fullAddress: '广东省梅州市梅县区新城锦绣花园 5 栋 1403 室', 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: ['术后恢复'], 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: ['需携带康复弹力带', '家属会同步在场'], timeline: buildMockTimeline('YS202605180002', '已接单,等待出发'), 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: '黄阿姨', elderPhoneMasked: '135****5566', fullElderName: '黄玉英', fullPhone: '13500135566', addressSummary: '梅江区学海路康养公寓 A 座 907', fullAddress: '广东省梅州市梅江区学海路康养公寓 A 座 907 室', 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: ['糖尿病', '需测血糖'], 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: ['入户前请确认血糖试纸数量', '注意老人午后低血糖风险'], 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: '陈伯伯', elderPhoneMasked: '139****3301', fullElderName: '陈国辉', fullPhone: '13900133301', addressSummary: '梅县区扶老社区 11 栋 203', fullAddress: '广东省梅州市梅县区扶老社区 11 栋 203 室', 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: ['卧床护理', '家属需培训'], 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: ['护理过程注意皮肤完整性', '家属需学习翻身技巧'], timeline: buildMockTimeline('YS202605180004', '服务执行中'), serviceSummary: '已完成基础护理与翻身指导,待家属签字确认。', progressNote: '创面情况稳定,已完成阶段性护理。', distanceKm: '3.6km', allowCheckinRadiusMeters: 100, lastLocation: { latitude: 24.3022, longitude: 116.1441, address: '扶老社区 11 栋 203 室', 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: '扶老社区 11 栋 203 室', 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: '王阿婆', elderPhoneMasked: '134****7744', fullElderName: '王月兰', fullPhone: '13400137744', addressSummary: '梅江区东汇城旁康颐楼 3 单元 1204', fullAddress: '广东省梅州市梅江区东汇城旁康颐楼 3 单元 1204 室', 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: ['用药异常', '需医生回访'], 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: ['优先联系家属', '记录老人异常表现并上传照片'], timeline: buildMockTimeline('YS202605180005', '异常已上报,等待处理'), serviceSummary: '发现老人血压异常升高,已完成初步安抚并联系家属。', progressNote: '异常已同步机构值班护士。', distanceKm: '4.2km', allowCheckinRadiusMeters: 100, lastLocation: { latitude: 24.2932, longitude: 116.1205, address: '康颐楼 3 单元 1204 室', 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: '完成 15 分钟辅助行走训练。', 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: '刘叔叔', elderPhoneMasked: '133****6655', fullElderName: '刘建华', fullPhone: '13300136655', addressSummary: '梅江区和顺苑 9 栋 401', fullAddress: '广东省梅州市梅江区和顺苑 9 栋 401 室', 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: ['术后恢复'], 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: ['术后行动缓慢,注意搀扶'], timeline: buildMockTimeline('YS202605170006', '服务完成待回访'), serviceSummary: '术后恢复训练顺利完成,家属已确认并签字。', progressNote: '服务完成,建议两日后再次回访。', distanceKm: '6.1km', allowCheckinRadiusMeters: 100, lastLocation: { latitude: 24.2806, longitude: 116.1115, address: '和顺苑 9 栋 401 室', 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: '基础照护 · 基础上门护理已分配给你,服务对象:李奶奶,预约时间:2026-05-18 09:00,请及时确认接单并做好上门准备。', type: 'order', createdAt: '2026-05-18 08:20', read: false, orderId: orders[0].id } as DeliveryMessageType, { id: 'mock-msg-2', title: '出发提醒', content: '康复指导 · 居家康复训练指导将在 30 分钟后开始,服务地址:梅县区新城锦绣花园 5 栋 1403,请提前规划路线并按时出发。', 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: '平台已将“陪诊协助 · 复诊陪同”改派给你,预约时间:2026-05-18 14:30,请及时查看服务要求与上门信息。', 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: '结算通知', content: '昨日完成的 3 个服务工单已进入结算流程,请在“我的-服务记录”中查看结算进度。', 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: item.status == 'completed' ? '五星好评' : '待补充说明' } 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: '签到照片' + String(i + 1), 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: '服务留痕图片' + String(i + 1), 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 = payload.confirmByFamily ? '待回访' : '待确认' 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('当前账号不是上门服务人员账号') } const deliveryInfo = await fetchDeliveryProfileFromRemote(profile.id) if (deliveryInfo == null) { try { await supa.signOut() } catch (error) {} throw new Error('当前账号未绑定服务人员档案') } return { token: result.access_token, userInfo: profile, deliveryInfo, usesMock: false } } 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) { 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 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 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 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) }