1283 lines
43 KiB
Plaintext
1283 lines
43 KiB
Plaintext
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<string> {
|
||
const value = plainObject(source)[key]
|
||
if (Array.isArray(value)) {
|
||
const result = [] as Array<string>
|
||
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<string>
|
||
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<string>
|
||
}
|
||
}
|
||
return [] as Array<string>
|
||
}
|
||
|
||
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<HomeServiceTimelineItemType> {
|
||
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<HomeServiceCaseType> = [
|
||
{
|
||
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<HomeServiceTaskType> = [
|
||
{
|
||
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<HomeServiceAdminApplicationType> = [
|
||
{
|
||
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<HomeServiceOverviewCardType> = [
|
||
{ 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<HomeServiceAssessmentType> = [
|
||
{
|
||
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<HomeServicePlanType> = [
|
||
{
|
||
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<HomeServiceRectificationType> = [
|
||
{
|
||
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<HomeServiceSettlementType> = [
|
||
{
|
||
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<boolean> {
|
||
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<Array<HomeServiceCatalogType>> {
|
||
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<HomeServiceCatalogType>
|
||
}
|
||
const result = [] as Array<HomeServiceCatalogType>
|
||
for (let i = 0; i < response.data.length; i++) {
|
||
result.push(parseCatalogItem(response.data[i]))
|
||
}
|
||
return result
|
||
}
|
||
|
||
export async function fetchConsumerHomeServiceCases(): Promise<Array<HomeServiceCaseType>> {
|
||
const orders = await listConsumerServiceOrders()
|
||
const result = [] as Array<HomeServiceCaseType>
|
||
for (let i = 0; i < orders.length; i++) {
|
||
result.push(mapOrderToCase(orders[i]))
|
||
}
|
||
return result
|
||
}
|
||
|
||
export async function fetchConsumerHomeServiceCaseDetail(caseId: string): Promise<HomeServiceCaseType | null> {
|
||
const detail = await getServiceOrderDetail(caseId)
|
||
if (detail != null) {
|
||
return mapOrderToCase(detail)
|
||
}
|
||
return null
|
||
}
|
||
|
||
export async function createHomeServiceApplication(draft: HomeServiceApplicationDraftType): Promise<HomeServiceCaseType | null> {
|
||
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<ServiceOrderTimelineItemType>): Array<HomeServiceTimelineItemType> {
|
||
const timeline = [] as Array<HomeServiceTimelineItemType>
|
||
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<Array<string>> {
|
||
const userId = getCurrentUserId()
|
||
if (userId == '') {
|
||
return [] as Array<string>
|
||
}
|
||
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<string>
|
||
const rows = careTaskResponse.data as Array<any>
|
||
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<any>
|
||
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<string>
|
||
}
|
||
const legacyIds = [] as Array<string>
|
||
const legacyRows = legacyResponse.data as Array<any>
|
||
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<boolean> {
|
||
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<any>).length > 0
|
||
}
|
||
|
||
async function completeWorkerTask(taskId: string): Promise<HomeServiceTaskType | null> {
|
||
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<Array<any>> {
|
||
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<any> : [] as Array<any>
|
||
}
|
||
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<any> : [] as Array<any>
|
||
}
|
||
|
||
async function readAdminPayload(caseId: string, prefix: string): Promise<any | null> {
|
||
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<string> {
|
||
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<any>).length > 0) {
|
||
const rows = response.data as Array<any>
|
||
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<string>
|
||
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<Array<HomeServiceTaskType>> {
|
||
const taskIds = await listWorkerTaskIds()
|
||
const result = [] as Array<HomeServiceTaskType>
|
||
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<HomeServiceTaskType | null> {
|
||
const detail = await getServiceOrderDetail(taskId)
|
||
return detail == null ? null : mapOrderToWorkerTask(detail)
|
||
}
|
||
|
||
export async function advanceWorkerTask(taskId: string): Promise<HomeServiceTaskType | null> {
|
||
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<HomeServiceTaskType | null> {
|
||
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<HomeServiceTaskType | null> {
|
||
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<HomeServiceTaskType | null> {
|
||
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<HomeServiceAssessmentType | null> {
|
||
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<HomeServiceAssessmentType | null> {
|
||
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<HomeServicePlanType | null> {
|
||
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<HomeServicePlanType | null> {
|
||
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<HomeServiceAcceptanceType | null> {
|
||
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<string>
|
||
}
|
||
}
|
||
return null
|
||
}
|
||
|
||
export async function submitConsumerAcceptance(
|
||
caseId: string,
|
||
approved: boolean,
|
||
rating: number,
|
||
feedback: string,
|
||
tags: Array<string>
|
||
): Promise<HomeServiceAcceptanceType | null> {
|
||
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<HomeServiceRectificationType | null> {
|
||
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<HomeServiceRectificationType | null> {
|
||
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<HomeServiceSettlementType | null> {
|
||
await delay()
|
||
const target = ADMIN_SETTLEMENTS.find((item) => item.caseId == caseId)
|
||
return target == null ? null : cloneSettlement(target)
|
||
}
|
||
|
||
export async function submitAdminSettlementArchive(caseId: string): Promise<HomeServiceSettlementType | null> {
|
||
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<Array<HomeServiceAdminApplicationType>> {
|
||
await delay()
|
||
return ADMIN_APPLICATIONS.map((item) => ({ ...item }))
|
||
}
|
||
|
||
export async function fetchAdminHomeServiceOverview(): Promise<Array<HomeServiceOverviewCardType>> {
|
||
await delay()
|
||
return ADMIN_OVERVIEW.map((item) => ({ ...item }))
|
||
} |