import { HomeServiceAcceptanceType, HomeServiceAdminApplicationType, HomeServiceAssessmentType, HomeServiceApplicationDraftType, HomeServiceCatalogType, HomeServiceCaseType, HomeServiceOverviewCardType, HomeServicePlanType, HomeServiceRectificationType, HomeServiceSettlementType, HomeServiceTaskType, HomeServiceTimelineItemType } from '@/types/home-service.uts' import { confirmServiceOrder, createServiceOrder, getServiceOrderDetail, listConsumerServiceOrders, rejectServiceOrderAcceptance } from '@/services/serviceOrderService.uts' import supa from '@/components/supadb/aksupainstance.uts' import { getCurrentUserId } from '@/utils/store.uts' import { getServiceOrderStatusText, type ServiceOrderStatus, type ServiceOrderTimelineItemType, type ServiceOrderType } from '@/types/service-order.uts' function nowIso(): string { return new Date().toISOString() } function buildId(prefix: string): string { return prefix + '-' + String(Date.now()) + '-' + String(Math.floor(Math.random() * 100000)).padStart(5, '0') } function plainObject(source: any): any { return JSON.parse(JSON.stringify(source)) as any } function readString(source: any, key: string): string { const value = plainObject(source)[key] if (value == null) { return '' } return typeof value == 'string' ? value : String(value) } function readNumber(source: any, key: string): number { const value = plainObject(source)[key] if (typeof value == 'number') { return value } const parsed = Number(value) return isNaN(parsed) ? 0 : parsed } function readStringArray(source: any, key: string): Array { const value = plainObject(source)[key] if (Array.isArray(value)) { const result = [] as Array for (let i = 0; i < value.length; i++) { result.push(typeof value[i] == 'string' ? value[i] : String(value[i])) } return result } if (typeof value == 'string' && value != '') { try { const parsed = JSON.parse(value) as any if (Array.isArray(parsed)) { const result = [] as Array for (let i = 0; i < parsed.length; i++) { result.push(typeof parsed[i] == 'string' ? parsed[i] : String(parsed[i])) } return result } } catch (error) { return [] as Array } } return [] as Array } function parseCatalogItem(source: any): HomeServiceCatalogType { return { id: readString(source, 'id'), name: readString(source, 'name'), category: readString(source, 'category'), price: readNumber(source, 'price'), durationText: readString(source, 'duration_text'), summary: readString(source, 'summary'), tags: readStringArray(source, 'tags_json'), suitableFor: readString(source, 'suitable_for') } } function createTimeline(title1: string, title2: string, title3: string): Array { return [ { id: 'tl-1', title: title1, time: '2026-05-12 09:00', description: '系统已建立服务申请档案。' }, { id: 'tl-2', title: title2, time: '2026-05-12 11:30', description: '评估员已完成初步核对与上门安排。' }, { id: 'tl-3', title: title3, time: '2026-05-13 08:20', description: '当前环节已进入待执行状态。' } ] } const CASE_STORE: Array = [ { id: 'case-001', caseNo: 'HS202605130001', status: 'pending_dispatch', statusText: '待派单', statusTone: 'warning', serviceName: '基础上门护理', serviceTime: '2026-05-14 09:00-11:00', applicantName: '李晓兰', elderName: '李奶奶', age: 78, phone: '13800138000', address: '梅州市梅江区学海路 18 号 2 栋 602', summary: '老人近期行动困难,需要基础照护与生命体征监测。', currentStep: 3, totalSteps: 8, staffName: '待分配', staffPhone: '待分配', amount: 168, timeline: createTimeline('已提交申请', '待上门评估', '待派单') }, { id: 'case-002', caseNo: 'HS202605130002', status: 'in_service', statusText: '服务中', statusTone: 'primary', serviceName: '慢病健康随访', serviceTime: '2026-05-13 15:00-16:30', applicantName: '张春梅', elderName: '张爷爷', age: 82, phone: '13900139000', address: '梅州市梅县区华侨城康宁路 66 号', summary: '随访血压血糖,核对用药并记录健康建议。', currentStep: 6, totalSteps: 8, staffName: '陈护理', staffPhone: '13688886666', amount: 128, timeline: createTimeline('已提交申请', '已生成服务方案', '服务执行中') } ] const TASK_STORE: Array = [ { id: 'task-001', caseId: 'case-001', caseNo: 'HS202605130001', status: 'pending_visit', statusText: '待上门', statusTone: 'warning', serviceName: '基础上门护理', elderName: '李奶奶', address: '梅州市梅江区学海路 18 号 2 栋 602', appointmentTime: '2026-05-14 09:00', checkInStatus: '未签到', recordSummary: '待填写服务记录', staffName: '黄护理', staffPhone: '13777770001', actionText: '签到开始', timeline: createTimeline('调度已派单', '护理员已接单', '等待上门签到') }, { id: 'task-002', caseId: 'case-002', caseNo: 'HS202605130002', status: 'serving', statusText: '服务中', statusTone: 'primary', serviceName: '慢病健康随访', elderName: '张爷爷', address: '梅州市梅县区华侨城康宁路 66 号', appointmentTime: '2026-05-13 15:00', checkInStatus: '已签到', recordSummary: '已完成血压血糖记录,待补充宣教备注。', staffName: '陈护理', staffPhone: '13688886666', actionText: '完成提交', timeline: createTimeline('调度已派单', '已到岗签到', '服务执行中') } ] const ADMIN_APPLICATIONS: Array = [ { id: 'admin-app-001', caseId: 'case-001', caseNo: 'HS202605130001', status: 'pending_assessment', statusText: '待评估', statusTone: 'warning', elderName: '李奶奶', serviceName: '基础上门护理', preferredTime: '2026-05-14 上午', assessmentResult: '待评估员上门', dispatcherName: '刘调度', staffName: '待分配' }, { id: 'admin-app-002', caseId: 'case-002', caseNo: 'HS202605130002', status: 'pending_acceptance', statusText: '待验收', statusTone: 'success', elderName: '张爷爷', serviceName: '慢病健康随访', preferredTime: '2026-05-13 下午', assessmentResult: '评估通过,按标准随访执行', dispatcherName: '刘调度', staffName: '陈护理' } ] const ADMIN_OVERVIEW: Array = [ { id: 'overview-1', label: '待评估申请', value: '06', tone: 'warning' }, { id: 'overview-2', label: '待派单工单', value: '03', tone: 'primary' }, { id: 'overview-3', label: '服务中任务', value: '08', tone: 'success' }, { id: 'overview-4', label: '待验收任务', value: '04', tone: 'neutral' } ] const ADMIN_ASSESSMENTS: Array = [ { caseId: 'case-001', caseNo: 'HS202605130001', elderName: '李奶奶', serviceName: '基础上门护理', riskLevel: '中风险', careLevel: '护理二级', visitTime: '2026-05-14 09:00', assessmentSummary: '行动缓慢,需重点关注跌倒风险和晨间血压波动。', requirementTags: ['血压监测', '基础照护', '跌倒风险提醒'] }, { caseId: 'case-002', caseNo: 'HS202605130002', elderName: '张爷爷', serviceName: '慢病健康随访', riskLevel: '低风险', careLevel: '随访管理', visitTime: '2026-05-13 15:00', assessmentSummary: '生命体征稳定,重点跟踪慢病用药执行情况。', requirementTags: ['血糖记录', '用药核对', '家属宣教'] } ] const ADMIN_PLANS: Array = [ { caseId: 'case-001', caseNo: 'HS202605130001', elderName: '李奶奶', serviceName: '基础上门护理', planTitle: '基础护理 7 日方案', serviceFrequency: '每周 3 次', serviceCycle: '2026-05-14 至 2026-05-21', executorAdvice: '优先安排熟悉慢病照护的护理员,首次上门同步评估跌倒风险。', billingSummary: '基础护理 ¥168/次,计划总额 ¥504', planSummary: '围绕晨间照护、生命体征监测和风险提醒开展服务。' }, { caseId: 'case-002', caseNo: 'HS202605130002', elderName: '张爷爷', serviceName: '慢病健康随访', planTitle: '慢病随访连续方案', serviceFrequency: '每周 2 次', serviceCycle: '2026-05-13 至 2026-05-27', executorAdvice: '执行人员需同步上传血压血糖记录并完成家属宣教。', billingSummary: '随访服务 ¥128/次,计划总额 ¥512', planSummary: '持续随访血压血糖、用药和饮食管理情况。' } ] const ADMIN_RECTIFICATIONS: Array = [ { caseId: 'case-001', caseNo: 'HS202605130001', elderName: '李奶奶', serviceName: '基础上门护理', issueSummary: '家属要求补充护理动作说明和现场照片留痕。', deadline: '2026-05-14 18:00', ownerName: '黄护理', status: 'pending', statusText: '待整改' }, { caseId: 'case-002', caseNo: 'HS202605130002', elderName: '张爷爷', serviceName: '慢病健康随访', issueSummary: '记录完整,无需整改。', deadline: '2026-05-15 12:00', ownerName: '陈护理', status: 'closed', statusText: '已关闭' } ] const ADMIN_SETTLEMENTS: Array = [ { caseId: 'case-001', caseNo: 'HS202605130001', elderName: '李奶奶', serviceName: '基础上门护理', billingPeriod: '2026-05-14 至 2026-05-21', totalAmount: '¥504', insuranceAmount: '¥300', selfPayAmount: '¥204', archiveStatus: 'pending', archiveStatusText: '待归档' }, { caseId: 'case-002', caseNo: 'HS202605130002', elderName: '张爷爷', serviceName: '慢病健康随访', billingPeriod: '2026-05-13 至 2026-05-27', totalAmount: '¥512', insuranceAmount: '¥256', selfPayAmount: '¥256', archiveStatus: 'pending', archiveStatusText: '待归档' } ] function delay(): Promise { return new Promise((resolve) => { setTimeout(() => { resolve(true) }, 120) }) } function cloneCase(item: HomeServiceCaseType): HomeServiceCaseType { return { ...item, timeline: item.timeline.map((log) => ({ ...log })) } } function cloneTask(item: HomeServiceTaskType): HomeServiceTaskType { return { ...item, timeline: item.timeline.map((log) => ({ ...log })) } } function cloneAssessment(item: HomeServiceAssessmentType): HomeServiceAssessmentType { return { ...item, requirementTags: item.requirementTags.slice(0) } } function clonePlan(item: HomeServicePlanType): HomeServicePlanType { return { ...item } } function cloneRectification(item: HomeServiceRectificationType): HomeServiceRectificationType { return { ...item } } function cloneSettlement(item: HomeServiceSettlementType): HomeServiceSettlementType { return { ...item } } export async function fetchHomeServiceCatalog(): Promise> { const response = await supa .from('hss_service_catalog') .select('id, name, category, price, duration_text, summary, tags_json, suitable_for, sort_no') .eq('status', 1) .is('deleted_at', null) .order('sort_no', { ascending: true }) .execute() if (response.error != null || response.data == null || !Array.isArray(response.data)) { return [] as Array } const result = [] as Array for (let i = 0; i < response.data.length; i++) { result.push(parseCatalogItem(response.data[i])) } return result } export async function fetchConsumerHomeServiceCases(): Promise> { const orders = await listConsumerServiceOrders() const result = [] as Array for (let i = 0; i < orders.length; i++) { result.push(mapOrderToCase(orders[i])) } return result } export async function fetchConsumerHomeServiceCaseDetail(caseId: string): Promise { const detail = await getServiceOrderDetail(caseId) if (detail != null) { return mapOrderToCase(detail) } return null } export async function createHomeServiceApplication(draft: HomeServiceApplicationDraftType): Promise { const catalog = await fetchHomeServiceCatalog() let matchedService: HomeServiceCatalogType | null = null for (let i = 0; i < catalog.length; i++) { if (catalog[i].id == draft.serviceId) { matchedService = catalog[i] break } } if (matchedService != null && draft.serviceAddressSnapshot != null) { const createdOrder = await createServiceOrder({ service: matchedService, address: { addressId: draft.serviceAddressSnapshot.addressId, contactName: draft.serviceAddressSnapshot.contactName, contactPhone: draft.serviceAddressSnapshot.contactPhone, province: '', city: '', district: '', detailAddress: draft.serviceAddressSnapshot.addressDetail, fullAddress: draft.serviceAddressSnapshot.fullAddress, latitude: draft.serviceAddressSnapshot.latitude, longitude: draft.serviceAddressSnapshot.longitude, coordinateType: draft.serviceAddressSnapshot.coordinateType, remark: draft.serviceAddressSnapshot.remark }, recipientName: draft.elderName, recipientPhone: draft.phone, recipientAge: draft.age, recipientGender: draft.gender, contactName: draft.applicantName, contactPhone: draft.phone, appointmentTime: draft.preferredTime, remark: draft.demandSummary }) if (createdOrder != null) { return mapOrderToCase(createdOrder) } } return null } function getCaseStep(status: ServiceOrderStatus): number { if (status == 'created') return 1 if (status == 'assigned') return 3 if (status == 'accepted') return 4 if (status == 'departed') return 5 if (status == 'arrived' || status == 'in_service') return 6 if (status == 'pending_acceptance') return 7 if (status == 'accepted_by_user' || status == 'reviewed' || status == 'settled') return 8 if (status == 'rejected' || status == 'cancelled' || status == 'exception') return 7 return 2 } function getCaseTone(status: ServiceOrderStatus): string { if (status == 'created' || status == 'assigned' || status == 'pending_acceptance') return 'warning' if (status == 'accepted' || status == 'departed' || status == 'arrived' || status == 'in_service') return 'primary' if (status == 'accepted_by_user' || status == 'reviewed' || status == 'settled') return 'success' if (status == 'exception' || status == 'cancelled' || status == 'rejected') return 'danger' return 'neutral' } function getTimelineDescription(log: ServiceOrderTimelineItemType): string { if (log.remark != '') { return log.remark } if (log.toStatus == 'created') return '平台已接收预约申请,等待后续处理。' if (log.toStatus == 'assigned') return '系统已完成派单,正在通知服务人员。' if (log.toStatus == 'accepted') return '服务人员已接单,正在准备上门。' if (log.toStatus == 'departed') return '服务人员已出发,正在前往服务地点。' if (log.toStatus == 'arrived') return '服务人员已到达服务地点。' if (log.toStatus == 'in_service') return '服务已开始执行,请留意后续进度。' if (log.toStatus == 'completed') return '服务执行已完成,正在整理结果。' if (log.toStatus == 'pending_acceptance') return '服务记录已提交,等待家属验收。' if (log.toStatus == 'accepted_by_user') return '家属已确认本次服务结果。' if (log.toStatus == 'reviewed') return '家属已完成评价反馈。' if (log.toStatus == 'settled') return '本次服务已完成结算归档。' if (log.toStatus == 'cancelled') return '服务单已取消。' if (log.toStatus == 'rejected') return '服务人员未接受该工单。' return '服务过程状态已更新。' } function formatServiceAppointmentText(value: string): string { if (value == '') { return '' } if (value.indexOf('上午') >= 0 || value.indexOf('下午') >= 0 || value.indexOf('晚上') >= 0) { return value } const parsed = Date.parse(value) if (!isNaN(parsed)) { const date = new Date(parsed) let year = date.getFullYear() const currentYear = new Date().getFullYear() if (year < currentYear - 1) { year = currentYear } const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') const hour = String(date.getHours()).padStart(2, '0') const minute = String(date.getMinutes()).padStart(2, '0') return year + '-' + month + '-' + day + ' ' + hour + ':' + minute } const monthDayMatch = value.match(/(\d{2})\/(\d{2})/) const timeMatch = value.match(/(\d{2}:\d{2}(\s*-\s*\d{2}:\d{2})?)/) if (monthDayMatch != null) { const month = monthDayMatch[1] ?? '' const day = monthDayMatch[2] ?? '' const timeText = timeMatch != null ? (timeMatch[1] ?? '') : '' if (month != '' && day != '') { return String(new Date().getFullYear()) + '-' + month + '-' + day + (timeText != '' ? ' ' + timeText.replace(/\s+/g, '') : '') } } return value.replace('T', ' ') } function mapLogsToTimeline(logs: Array): Array { const timeline = [] as Array for (let i = 0; i < logs.length; i++) { timeline.push({ id: logs[i].id, title: getServiceOrderStatusText(logs[i].toStatus), time: logs[i].createdAt, description: getTimelineDescription(logs[i]) }) } return timeline } function mapOrderToCase(order: ServiceOrderType): HomeServiceCaseType { return { id: order.id, caseNo: order.orderNo, status: order.status, statusText: getServiceOrderStatusText(order.status), statusTone: getCaseTone(order.status), serviceName: order.serviceName, serviceTime: formatServiceAppointmentText(order.appointmentTime), applicantName: order.contactName, elderName: order.recipientName, age: order.recipientAge, phone: order.contactPhone, address: order.addressSnapshot.fullAddress, summary: order.remark, currentStep: getCaseStep(order.status), totalSteps: 8, staffName: order.staffName == '' ? '待分配' : order.staffName, staffPhone: order.staffPhone == '' ? '待分配' : order.staffPhone, amount: order.serviceSnapshot.price, checkinTime: order.executionRecord != null ? order.executionRecord.checkinTime : '', checkinAddress: order.executionRecord != null ? order.executionRecord.checkinAddress : '', serviceStartedAt: order.executionRecord != null ? order.executionRecord.serviceStartedAt : order.serviceStartedAt, serviceFinishedAt: order.executionRecord != null ? order.executionRecord.serviceFinishedAt : order.completedAt, executionSummary: order.executionRecord != null ? (order.executionRecord.summary != '' ? order.executionRecord.summary : order.executionRecord.remark) : '', evidenceCount: order.evidenceFiles.length, serviceAddressSnapshot: null, timeline: mapLogsToTimeline(order.logs) } } function mapOrderToWorkerTask(order: ServiceOrderType): HomeServiceTaskType { let taskStatus = 'pending_visit' let taskStatusText = '待上门' let taskStatusTone = 'warning' let actionText = '签到开始' if (order.status == 'arrived' || order.status == 'in_service') { taskStatus = 'serving' taskStatusText = '服务中' taskStatusTone = 'primary' actionText = '完成提交' } else if (order.status == 'pending_acceptance' || order.status == 'accepted_by_user' || order.status == 'reviewed' || order.status == 'settled') { taskStatus = 'completed' taskStatusText = '待验收' taskStatusTone = 'success' actionText = '已提交' } else if (order.status == 'exception' || order.status == 'rejected' || order.status == 'cancelled') { taskStatus = 'exception' taskStatusText = getServiceOrderStatusText(order.status) taskStatusTone = 'warning' actionText = '已上报' } return { id: order.id, caseId: order.id, caseNo: order.orderNo, status: taskStatus, statusText: taskStatusText, statusTone: taskStatusTone, serviceName: order.serviceName, elderName: order.recipientName, address: order.addressSnapshot.fullAddress, appointmentTime: formatServiceAppointmentText(order.appointmentTime), checkInStatus: order.executionRecord != null && order.executionRecord.checkinTime != '' ? '已签到' : '未签到', recordSummary: order.executionRecord != null ? (order.executionRecord.summary != '' ? order.executionRecord.summary : order.executionRecord.remark) : '待填写服务记录', staffName: order.staffName == '' ? '待分配' : order.staffName, staffPhone: order.staffPhone == '' ? '待分配' : order.staffPhone, actionText, timeline: mapLogsToTimeline(order.logs) } } async function listWorkerTaskIds(): Promise> { const userId = getCurrentUserId() if (userId == '') { return [] as Array } const careTaskResponse = await supa.from('ec_care_tasks').select('id').eq('assigned_to', userId).order('created_at', { ascending: false }).execute() if (careTaskResponse.error == null && careTaskResponse.data != null) { const result = [] as Array const rows = careTaskResponse.data as Array for (let i = 0; i < rows.length; i++) { const id = readString(rows[i], 'id') if (id != '') { result.push(id) } } if (result.length > 0) { return result } } let staffProfileId = '' const staffResponse = await supa.from('ml_delivery_staff').select('id').eq('uid', userId).limit(1).execute() if (staffResponse.error == null && staffResponse.data != null) { const staffRows = staffResponse.data as Array if (staffRows.length > 0) { staffProfileId = readString(staffRows[0], 'id') } } const legacyStaffId = staffProfileId != '' ? staffProfileId : userId const legacyResponse = await supa.from('hss_service_orders').select('id').eq('current_staff_id', legacyStaffId).order('created_at', { ascending: false }).execute() if (legacyResponse.error != null || legacyResponse.data == null) { return [] as Array } const legacyIds = [] as Array const legacyRows = legacyResponse.data as Array for (let i = 0; i < legacyRows.length; i++) { const id = readString(legacyRows[i], 'id') if (id != '') { legacyIds.push(id) } } return legacyIds } async function isCareTask(taskId: string): Promise { const response = await supa.from('ec_care_tasks').select('id').eq('id', taskId).limit(1).execute() return response.error == null && response.data != null && (response.data as Array).length > 0 } async function completeWorkerTask(taskId: string): Promise { const completedAt = nowIso() if (await isCareTask(taskId)) { await supa.from('ec_care_tasks').update({ status: 'ACCEPTANCE_PENDING', service_completed_at: completedAt, acceptance_pending_at: completedAt, updated_at: completedAt }).eq('id', taskId).execute() await supa.from('hc_work_order_events').insert({ id: buildId('hc-event'), task_id: taskId, from_status: 'ORDER_IN_SERVICE', to_status: 'ACCEPTANCE_PENDING', actor_id: getCurrentUserId(), actor_role: 'merchant', action: 'finish_service', remark: '服务记录和凭证已经提交。', created_at: completedAt }).execute() } else { await supa.from('hss_service_orders').update({ status: 'pending_acceptance', completed_at: completedAt, pending_acceptance_at: completedAt, updated_at: completedAt }).eq('id', taskId).execute() await supa.from('hss_service_order_status_logs').insert({ id: buildId('slog'), order_id: taskId, from_status: 'in_service', to_status: 'pending_acceptance', operator_id: getCurrentUserId(), operator_role: 'merchant', remark: '服务记录和凭证已经提交。', created_at: completedAt }).execute() } return await fetchWorkerTaskDetail(taskId) } const ADMIN_ASSESSMENT_PREFIX = '[admin_assessment]' const ADMIN_PLAN_PREFIX = '[admin_plan]' const ADMIN_RECTIFICATION_PREFIX = '[admin_rectification]' function encodeAdminRemark(prefix: string, payload: any): string { return prefix + JSON.stringify(payload) } function parseAdminRemark(remark: string, prefix: string): any | null { if (remark == '' || remark.indexOf(prefix) != 0) { return null } const body = remark.substring(prefix.length) if (body == '') { return null } try { return JSON.parse(body) as any } catch (error) { return null } } function getDefaultCareLevel(order: ServiceOrderType): string { if (order.serviceName.indexOf('随访') >= 0) { return '随访管理' } if (order.serviceName.indexOf('护理') >= 0) { return '护理二级' } return '护理一级' } function getOrderLatestRemark(order: ServiceOrderType): string { for (let i = 0; i < order.logs.length; i++) { if (order.logs[i].remark != '') { return order.logs[i].remark } } return '' } async function listAdminEventRows(caseId: string): Promise> { if (await isCareTask(caseId)) { const response = await supa.from('hc_work_order_events').select('*').eq('task_id', caseId).order('created_at', { ascending: false }).execute() return response.data != null ? response.data as Array : [] as Array } const response = await supa.from('hss_service_order_status_logs').select('*').eq('order_id', caseId).order('created_at', { ascending: false }).execute() return response.data != null ? response.data as Array : [] as Array } async function readAdminPayload(caseId: string, prefix: string): Promise { const rows = await listAdminEventRows(caseId) for (let i = 0; i < rows.length; i++) { const payload = parseAdminRemark(readString(rows[i], 'remark'), prefix) if (payload != null) { return payload } } return null } async function readLatestExceptionSummary(caseId: string, order: ServiceOrderType): Promise { if (await isCareTask(caseId)) { const response = await supa.from('hc_work_order_exceptions').select('description').eq('task_id', caseId).order('created_at', { ascending: false }).limit(1).execute() if (response.data != null && (response.data as Array).length > 0) { const rows = response.data as Array const text = readString(rows[0], 'description') if (text != '') { return text } } } const latestRemark = getOrderLatestRemark(order) if (latestRemark != '') { return latestRemark } if (order.review != null && order.review.content != '') { return order.review.content } return '待补充整改说明。' } function buildAssessmentDetail(order: ServiceOrderType, payload: any | null): HomeServiceAssessmentType { const requirementTags = payload != null ? readStringArray(payload, 'requirementTags') : [] as Array return { caseId: order.id, caseNo: order.orderNo, elderName: order.recipientName, serviceName: order.serviceName, riskLevel: payload != null && readString(payload, 'riskLevel') != '' ? readString(payload, 'riskLevel') : '中风险', careLevel: payload != null && readString(payload, 'careLevel') != '' ? readString(payload, 'careLevel') : getDefaultCareLevel(order), visitTime: formatServiceAppointmentText(order.appointmentTime), assessmentSummary: payload != null && readString(payload, 'assessmentSummary') != '' ? readString(payload, 'assessmentSummary') : (order.remark != '' ? order.remark : '待补充上门评估结论。'), requirementTags: requirementTags.length > 0 ? requirementTags : order.serviceSnapshot.tags } } function buildPlanDetail(order: ServiceOrderType, payload: any | null): HomeServicePlanType { const serviceCycle = payload != null && readString(payload, 'serviceCycle') != '' ? readString(payload, 'serviceCycle') : formatServiceAppointmentText(order.appointmentTime) const amountText = '¥' + String(order.serviceSnapshot.price) return { caseId: order.id, caseNo: order.orderNo, elderName: order.recipientName, serviceName: order.serviceName, planTitle: payload != null && readString(payload, 'planTitle') != '' ? readString(payload, 'planTitle') : (order.serviceName + '执行方案'), serviceFrequency: payload != null && readString(payload, 'serviceFrequency') != '' ? readString(payload, 'serviceFrequency') : '按预约执行', serviceCycle: serviceCycle, executorAdvice: payload != null && readString(payload, 'executorAdvice') != '' ? readString(payload, 'executorAdvice') : (order.staffName != '' ? ('优先由' + order.staffName + '承接本次服务并补全留痕。') : '优先安排熟悉该服务类型的执行人员。'), billingSummary: payload != null && readString(payload, 'billingSummary') != '' ? readString(payload, 'billingSummary') : ('本次服务金额 ' + amountText), planSummary: payload != null && readString(payload, 'planSummary') != '' ? readString(payload, 'planSummary') : (order.remark != '' ? order.remark : '待补充服务目标、执行重点和验收口径。') } } function buildRectificationDetail(order: ServiceOrderType, payload: any | null, issueSummary: string): HomeServiceRectificationType { const status = payload != null && readString(payload, 'status') != '' ? readString(payload, 'status') : (order.status == 'exception' ? 'pending' : 'closed') return { caseId: order.id, caseNo: order.orderNo, elderName: order.recipientName, serviceName: order.serviceName, issueSummary: payload != null && readString(payload, 'issueSummary') != '' ? readString(payload, 'issueSummary') : issueSummary, deadline: payload != null && readString(payload, 'deadline') != '' ? readString(payload, 'deadline') : (order.pendingAcceptanceAt != '' ? order.pendingAcceptanceAt : formatServiceAppointmentText(order.appointmentTime)), ownerName: payload != null && readString(payload, 'ownerName') != '' ? readString(payload, 'ownerName') : (order.staffName != '' ? order.staffName : '待分配'), status: status, statusText: status == 'closed' ? '已关闭' : '待整改' } } export async function fetchWorkerTasks(): Promise> { const taskIds = await listWorkerTaskIds() const result = [] as Array for (let i = 0; i < taskIds.length; i++) { const detail = await getServiceOrderDetail(taskIds[i]) if (detail != null) { result.push(mapOrderToWorkerTask(detail)) } } return result } export async function fetchWorkerTaskDetail(taskId: string): Promise { const detail = await getServiceOrderDetail(taskId) return detail == null ? null : mapOrderToWorkerTask(detail) } export async function advanceWorkerTask(taskId: string): Promise { const detail = await fetchWorkerTaskDetail(taskId) if (detail == null) { return null } if (detail.status == 'serving') { return await completeWorkerTask(taskId) } return detail } export async function submitWorkerCheckIn(taskId: string, note: string): Promise { const checkedInAt = nowIso() if (await isCareTask(taskId)) { const recordId = buildId('care-checkin') await supa.from('ec_care_records').insert({ id: recordId, task_id: taskId, record_type: 'checkin', started_at: checkedInAt, checked_in_at: checkedInAt, location_text: note, remark: note, created_at: checkedInAt, updated_at: checkedInAt }).execute() await supa.from('ec_care_tasks').update({ status: 'ORDER_IN_SERVICE', checked_in_at: checkedInAt, service_started_at: checkedInAt, updated_at: checkedInAt }).eq('id', taskId).execute() await supa.from('hc_work_order_events').insert({ id: buildId('hc-event'), task_id: taskId, from_status: 'ORDER_ACCEPTED', to_status: 'ORDER_IN_SERVICE', actor_id: getCurrentUserId(), actor_role: 'merchant', action: 'checkin_task', remark: note == '' ? '已完成签到,开始执行服务。' : note, created_at: checkedInAt }).execute() } else { const recordId = buildId('ser') await supa.from('hss_service_execution_records').upsert({ id: recordId, order_id: taskId, assignment_id: '', checkin_time: checkedInAt, checkin_address: note, service_started_at: checkedInAt, remark: note, created_at: checkedInAt, updated_at: checkedInAt }).execute() await supa.from('hss_service_orders').update({ status: 'in_service', arrived_at: checkedInAt, service_started_at: checkedInAt, updated_at: checkedInAt }).eq('id', taskId).execute() await supa.from('hss_service_order_status_logs').insert({ id: buildId('slog'), order_id: taskId, from_status: 'accepted', to_status: 'in_service', operator_id: getCurrentUserId(), operator_role: 'merchant', remark: note == '' ? '已完成签到,开始执行服务。' : note, created_at: checkedInAt }).execute() } return await fetchWorkerTaskDetail(taskId) } export async function submitWorkerServiceRecord(taskId: string, summary: string): Promise { const detail = await getServiceOrderDetail(taskId) if (detail == null) { return null } const now = nowIso() const recordId = detail.executionRecord != null && detail.executionRecord.id != '' ? detail.executionRecord.id : buildId('worker-rec') if (await isCareTask(taskId)) { await supa.from('ec_care_records').upsert({ id: recordId, task_id: taskId, record_type: 'service', started_at: detail.executionRecord != null ? detail.executionRecord.serviceStartedAt : detail.serviceStartedAt, summary: summary, remark: summary, created_at: detail.executionRecord != null && detail.executionRecord.createdAt != '' ? detail.executionRecord.createdAt : now, updated_at: now }).execute() await supa.from('ec_care_tasks').update({ status: 'ORDER_IN_SERVICE', updated_at: now }).eq('id', taskId).execute() await supa.from('hc_work_order_events').insert({ id: buildId('hc-event'), task_id: taskId, from_status: 'ORDER_IN_SERVICE', to_status: 'ORDER_IN_SERVICE', actor_id: getCurrentUserId(), actor_role: 'merchant', action: 'save_record', remark: summary == '' ? '已保存服务记录。' : summary, created_at: now }).execute() } else { await supa.from('hss_service_execution_records').upsert({ id: recordId, order_id: taskId, assignment_id: detail.currentAssignmentId, service_started_at: detail.executionRecord != null ? detail.executionRecord.serviceStartedAt : detail.serviceStartedAt, summary: summary, remark: summary, created_at: detail.executionRecord != null && detail.executionRecord.createdAt != '' ? detail.executionRecord.createdAt : now, updated_at: now }).execute() } return await fetchWorkerTaskDetail(taskId) } export async function submitWorkerException(taskId: string, exceptionType: string, description: string): Promise { const now = nowIso() if (await isCareTask(taskId)) { await supa.from('hc_work_order_exceptions').insert({ id: buildId('hc-ex'), task_id: taskId, exception_type: exceptionType, description: description, occurred_at: now, created_by: getCurrentUserId(), created_at: now, updated_at: now }).execute() await supa.from('ec_care_tasks').update({ status: 'ORDER_EXCEPTION', updated_at: now }).eq('id', taskId).execute() await supa.from('hc_work_order_events').insert({ id: buildId('hc-event'), task_id: taskId, from_status: '', to_status: 'ORDER_EXCEPTION', actor_id: getCurrentUserId(), actor_role: 'merchant', action: 'report_exception', remark: exceptionType + ':' + description, created_at: now }).execute() } else { await supa.from('hss_service_orders').update({ status: 'exception', updated_at: now }).eq('id', taskId).execute() await supa.from('hss_service_order_status_logs').insert({ id: buildId('slog'), order_id: taskId, from_status: 'in_service', to_status: 'exception', operator_id: getCurrentUserId(), operator_role: 'merchant', remark: exceptionType + ':' + description, created_at: now }).execute() } return await fetchWorkerTaskDetail(taskId) } export async function fetchAdminAssessmentDetail(caseId: string): Promise { const order = await getServiceOrderDetail(caseId) if (order == null) { return null } const payload = await readAdminPayload(caseId, ADMIN_ASSESSMENT_PREFIX) return buildAssessmentDetail(order, payload) } export async function submitAdminAssessment(caseId: string, riskLevel: string, careLevel: string, assessmentSummary: string): Promise { const order = await getServiceOrderDetail(caseId) if (order == null) { return null } const payload = { riskLevel, careLevel, assessmentSummary, requirementTags: order.serviceSnapshot.tags, updatedAt: nowIso() } if (await isCareTask(caseId)) { await supa.from('hc_work_order_events').insert({ id: buildId('hc-event'), task_id: caseId, from_status: order.status, to_status: order.status, actor_id: getCurrentUserId(), actor_role: 'admin', action: 'submit_assessment', remark: encodeAdminRemark(ADMIN_ASSESSMENT_PREFIX, payload), created_at: nowIso() }).execute() } else { await supa.from('hss_service_order_status_logs').insert({ id: buildId('slog'), order_id: caseId, from_status: order.status, to_status: order.status, operator_id: getCurrentUserId(), operator_role: 'admin', remark: encodeAdminRemark(ADMIN_ASSESSMENT_PREFIX, payload), created_at: nowIso() }).execute() } return buildAssessmentDetail(order, payload) } export async function fetchAdminServicePlanDetail(caseId: string): Promise { const order = await getServiceOrderDetail(caseId) if (order == null) { return null } const payload = await readAdminPayload(caseId, ADMIN_PLAN_PREFIX) return buildPlanDetail(order, payload) } export async function submitAdminServicePlan( caseId: string, planTitle: string, serviceFrequency: string, serviceCycle: string, planSummary: string ): Promise { const order = await getServiceOrderDetail(caseId) if (order == null) { return null } const payload = { planTitle, serviceFrequency, serviceCycle, executorAdvice: order.staffName != '' ? ('优先由' + order.staffName + '承接本次服务并补全留痕。') : '优先安排熟悉该服务类型的执行人员。', billingSummary: '本次服务金额 ¥' + String(order.serviceSnapshot.price), planSummary, updatedAt: nowIso() } if (await isCareTask(caseId)) { await supa.from('hc_work_order_events').insert({ id: buildId('hc-event'), task_id: caseId, from_status: order.status, to_status: order.status, actor_id: getCurrentUserId(), actor_role: 'admin', action: 'submit_plan', remark: encodeAdminRemark(ADMIN_PLAN_PREFIX, payload), created_at: nowIso() }).execute() } else { await supa.from('hss_service_order_status_logs').insert({ id: buildId('slog'), order_id: caseId, from_status: order.status, to_status: order.status, operator_id: getCurrentUserId(), operator_role: 'admin', remark: encodeAdminRemark(ADMIN_PLAN_PREFIX, payload), created_at: nowIso() }).execute() } return buildPlanDetail(order, payload) } export async function fetchConsumerAcceptanceDetail(caseId: string): Promise { const detail = await getServiceOrderDetail(caseId) if (detail != null) { return { caseId: detail.id, caseNo: detail.orderNo, elderName: detail.recipientName, serviceName: detail.serviceName, acceptanceStatus: detail.status, acceptanceStatusText: getServiceOrderStatusText(detail.status), rating: detail.review != null ? detail.review.rating : 5, feedback: detail.review != null ? detail.review.content : '', tags: detail.review != null ? detail.review.tags : [] as Array } } return null } export async function submitConsumerAcceptance( caseId: string, approved: boolean, rating: number, feedback: string, tags: Array ): Promise { if (approved) { const result = await confirmServiceOrder(caseId, rating, feedback, tags) if (result != null) { return await fetchConsumerAcceptanceDetail(caseId) } return null } const rejected = await rejectServiceOrderAcceptance(caseId, feedback) if (rejected != null) { return await fetchConsumerAcceptanceDetail(caseId) } return null } export async function fetchAdminRectificationDetail(caseId: string): Promise { const order = await getServiceOrderDetail(caseId) if (order == null) { return null } const payload = await readAdminPayload(caseId, ADMIN_RECTIFICATION_PREFIX) const issueSummary = await readLatestExceptionSummary(caseId, order) return buildRectificationDetail(order, payload, issueSummary) } export async function submitAdminRectification(caseId: string, issueSummary: string): Promise { const order = await getServiceOrderDetail(caseId) if (order == null) { return null } const payload = { issueSummary, deadline: order.pendingAcceptanceAt != '' ? order.pendingAcceptanceAt : formatServiceAppointmentText(order.appointmentTime), ownerName: order.staffName != '' ? order.staffName : '待分配', status: 'closed', updatedAt: nowIso() } if (await isCareTask(caseId)) { const reopenedAt = nowIso() await supa.from('ec_care_tasks').update({ status: 'ACCEPTANCE_PENDING', acceptance_pending_at: reopenedAt, updated_at: reopenedAt }).eq('id', caseId).execute() await supa.from('hc_work_order_events').insert({ id: buildId('hc-event'), task_id: caseId, from_status: 'ACCEPTANCE_REJECTED', to_status: 'ACCEPTANCE_PENDING', actor_id: getCurrentUserId(), actor_role: 'admin', action: 'submit_rectification', remark: encodeAdminRemark(ADMIN_RECTIFICATION_PREFIX, payload), created_at: reopenedAt }).execute() } else { const reopenedAt = nowIso() await supa.from('hss_service_orders').update({ status: 'pending_acceptance', pending_acceptance_at: reopenedAt, updated_at: reopenedAt }).eq('id', caseId).execute() await supa.from('hss_service_order_status_logs').insert({ id: buildId('slog'), order_id: caseId, from_status: order.status, to_status: 'pending_acceptance', operator_id: getCurrentUserId(), operator_role: 'admin', remark: encodeAdminRemark(ADMIN_RECTIFICATION_PREFIX, payload), created_at: reopenedAt }).execute() } const latest = await getServiceOrderDetail(caseId) if (latest == null) { return buildRectificationDetail(order, payload, issueSummary) } return buildRectificationDetail(latest, payload, issueSummary) } export async function fetchAdminSettlementDetail(caseId: string): Promise { await delay() const target = ADMIN_SETTLEMENTS.find((item) => item.caseId == caseId) return target == null ? null : cloneSettlement(target) } export async function submitAdminSettlementArchive(caseId: string): Promise { await delay() const target = ADMIN_SETTLEMENTS.find((item) => item.caseId == caseId) if (target == null) { return null } target.archiveStatus = 'archived' target.archiveStatusText = '已归档' const relatedCase = CASE_STORE.find((item) => item.id == caseId) if (relatedCase != null) { relatedCase.timeline.unshift({ id: 'case-settlement-' + String(relatedCase.timeline.length + 1), title: '结算归档完成', time: '2026-05-13 18:30', description: '结算单和执行材料已完成归档。' }) } return cloneSettlement(target) } export async function fetchAdminHomeServiceApplications(): Promise> { await delay() return ADMIN_APPLICATIONS.map((item) => ({ ...item })) } export async function fetchAdminHomeServiceOverview(): Promise> { await delay() return ADMIN_OVERVIEW.map((item) => ({ ...item })) }