完善登录逻辑和个人资料完善
This commit is contained in:
@@ -1,16 +1,18 @@
|
||||
import { getDeliveryProfileByUserId, loginDelivery as loginDeliveryApi } from '@/api/delivery.uts'
|
||||
import {
|
||||
acceptOrder as acceptRealServiceOrder,
|
||||
arriveOrder as arriveRealServiceOrder,
|
||||
checkinOrder as checkinRealServiceOrder,
|
||||
departOrder as departRealServiceOrder,
|
||||
finishOrder as finishRealServiceOrder,
|
||||
getDashboard as getRealDashboard,
|
||||
getOrderDetail as getRealServiceOrderDetail,
|
||||
getOrdersByTab as getRealOrdersByTab,
|
||||
saveServiceRecord as saveRealServiceRecord,
|
||||
startService as startRealService
|
||||
} from '@/services/serviceOrderService.uts'
|
||||
acceptDeliveryOrderById,
|
||||
arriveOrderById,
|
||||
checkinOrderById,
|
||||
finishServiceById,
|
||||
getDeliveryDashboardByStaffId,
|
||||
getDeliveryOrderDetailById,
|
||||
getDeliveryOrdersByStaffId,
|
||||
getDeliveryProfileByUserId,
|
||||
loginDelivery as loginDeliveryApi,
|
||||
saveServiceProgressById,
|
||||
startDepartById,
|
||||
startServiceById
|
||||
} from '@/api/delivery.uts'
|
||||
import { saveServiceRecord as saveRealServiceRecord } from '@/services/serviceOrderService.uts'
|
||||
import {
|
||||
acceptCareOrder,
|
||||
checkInCareOrder,
|
||||
@@ -31,12 +33,13 @@ import {
|
||||
updateCareOrderStatus,
|
||||
updateDeliveryCareOnlineStatus
|
||||
} from '@/mock/delivery-care.mock.uts'
|
||||
import { getUserInfo, requireDeliveryAuth } from '@/utils/deliveryAuth.uts'
|
||||
import { requireDeliveryAuth } from '@/utils/deliveryAuth.uts'
|
||||
import type {
|
||||
DeliveryAbnormalReportType,
|
||||
DeliveryCheckinPayloadType,
|
||||
DeliveryDashboardType,
|
||||
DeliveryExceptionPayloadType,
|
||||
DeliveryFinishPayloadType,
|
||||
DeliveryInfoType,
|
||||
DeliveryLocationType,
|
||||
DeliveryLoginPayloadType,
|
||||
@@ -79,26 +82,51 @@ export async function checkDeliveryAuth(): Promise<boolean> {
|
||||
return result.ok
|
||||
}
|
||||
|
||||
async function getCurrentStaffId(): Promise<string> {
|
||||
const profile = await getDeliveryProfile()
|
||||
if (profile == null) {
|
||||
return ''
|
||||
}
|
||||
return profile.id
|
||||
}
|
||||
|
||||
function createEmptyLocation(): DeliveryLocationType {
|
||||
return {
|
||||
latitude: 0,
|
||||
longitude: 0,
|
||||
address: '',
|
||||
time: new Date().toISOString().replace('T', ' ').substring(0, 19)
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDeliveryDashboard(): Promise<DeliveryDashboardType> {
|
||||
const staffId = await getCurrentStaffId()
|
||||
if (staffId != '') {
|
||||
return await getDeliveryDashboardByStaffId(staffId)
|
||||
}
|
||||
return getDeliveryCareDashboard()
|
||||
}
|
||||
|
||||
export async function getDeliveryOrders(params: DeliveryOrderQueryType): Promise<Array<DeliveryOrderType>> {
|
||||
const staffId = await getCurrentStaffId()
|
||||
if (staffId != '') {
|
||||
return await getDeliveryOrdersByStaffId(staffId, params)
|
||||
}
|
||||
if (params.tab == 'pending' || params.tab == 'pending_assignment') {
|
||||
return await getRealOrdersByTab('pending')
|
||||
return getPendingCareOrders()
|
||||
}
|
||||
if (params.tab == 'history' || params.tab == 'completed' || params.tab == 'archive') {
|
||||
return await getRealOrdersByTab('history')
|
||||
return getHistoryCareOrders()
|
||||
}
|
||||
return await getRealOrdersByTab('today')
|
||||
return getTodayCareOrders()
|
||||
}
|
||||
|
||||
export async function getDeliveryOrderDetail(id: string): Promise<DeliveryOrderType | null> {
|
||||
return await getRealServiceOrderDetail(id)
|
||||
return await getDeliveryOrderDetailById(id)
|
||||
}
|
||||
|
||||
export async function acceptDeliveryOrder(id: string): Promise<DeliveryOrderType | null> {
|
||||
return await acceptRealServiceOrder(id)
|
||||
return await acceptDeliveryOrderById(id)
|
||||
}
|
||||
|
||||
export async function rejectDeliveryOrder(id: string, reason: string): Promise<DeliveryOrderType | null> {
|
||||
@@ -106,30 +134,23 @@ export async function rejectDeliveryOrder(id: string, reason: string): Promise<D
|
||||
}
|
||||
|
||||
export async function startDepart(id: string, location: DeliveryLocationType): Promise<DeliveryOrderType | null> {
|
||||
return await departRealServiceOrder(id, location)
|
||||
return await startDepartById(id, location)
|
||||
}
|
||||
|
||||
export async function arriveOrder(id: string, location: DeliveryLocationType): Promise<DeliveryOrderType | null> {
|
||||
return await arriveRealServiceOrder(id, location)
|
||||
return await arriveOrderById(id, location)
|
||||
}
|
||||
|
||||
export async function checkinOrder(id: string, payload: DeliveryCheckinPayloadType): Promise<DeliveryOrderType | null> {
|
||||
return await checkinRealServiceOrder(id, payload)
|
||||
return await checkinOrderById(id, payload)
|
||||
}
|
||||
|
||||
export async function startService(id: string): Promise<DeliveryOrderType | null> {
|
||||
return await startRealService(id)
|
||||
return await startServiceById(id)
|
||||
}
|
||||
|
||||
export async function saveServiceProgress(id: string, payload: DeliveryProgressPayloadType): Promise<DeliveryOrderType | null> {
|
||||
const current = getCareOrderDetail(id)
|
||||
if (current == null) {
|
||||
return null
|
||||
}
|
||||
current.serviceItems = payload.items
|
||||
current.progressNote = payload.progressNote
|
||||
current.serviceSummary = payload.serviceSummary
|
||||
return current
|
||||
return await saveServiceProgressById(id, payload)
|
||||
}
|
||||
|
||||
export async function uploadEvidence(id: string, phase: string, files: Array<string>): Promise<Array<any>> {
|
||||
@@ -168,8 +189,13 @@ export async function submitException(id: string, payload: DeliveryExceptionPayl
|
||||
return submitCareAbnormalReport(id, payload)
|
||||
}
|
||||
|
||||
export async function finishService(id: string): Promise<DeliveryOrderType | null> {
|
||||
return completeCareOrder(id)
|
||||
export async function finishService(id: string, payload: DeliveryFinishPayloadType | null = null): Promise<DeliveryOrderType | null> {
|
||||
const finishPayload = payload != null ? payload : {
|
||||
serviceSummary: '',
|
||||
signatureName: '',
|
||||
confirmByFamily: false
|
||||
} as DeliveryFinishPayloadType
|
||||
return await finishServiceById(id, finishPayload)
|
||||
}
|
||||
|
||||
export async function getDeliveryRecords(): Promise<Array<DeliveryRecordType>> {
|
||||
@@ -198,27 +224,27 @@ export async function updateDeliveryOnlineStatus(status: string): Promise<Delive
|
||||
}
|
||||
|
||||
export async function getDeliveryDashboardStats(): Promise<DeliveryDashboardType> {
|
||||
return await getRealDashboard()
|
||||
return await getDeliveryDashboard()
|
||||
}
|
||||
|
||||
export async function getPendingServiceOrders(): Promise<Array<DeliveryOrderType>> {
|
||||
return await getRealOrdersByTab('pending')
|
||||
return await getDeliveryOrders({ tab: 'pending', keyword: '' } as DeliveryOrderQueryType)
|
||||
}
|
||||
|
||||
export async function getTodayServiceOrders(): Promise<Array<DeliveryOrderType>> {
|
||||
return await getRealOrdersByTab('today')
|
||||
return await getDeliveryOrders({ tab: 'today', keyword: '' } as DeliveryOrderQueryType)
|
||||
}
|
||||
|
||||
export async function getHistoryServiceOrders(): Promise<Array<DeliveryOrderType>> {
|
||||
return await getRealOrdersByTab('history')
|
||||
return await getDeliveryOrders({ tab: 'history', keyword: '' } as DeliveryOrderQueryType)
|
||||
}
|
||||
|
||||
export async function getServiceOrderDetail(orderId: string): Promise<DeliveryOrderType | null> {
|
||||
return await getRealServiceOrderDetail(orderId)
|
||||
return await getDeliveryOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function acceptServiceOrder(orderId: string): Promise<DeliveryOrderType | null> {
|
||||
return await acceptRealServiceOrder(orderId)
|
||||
return await acceptDeliveryOrder(orderId)
|
||||
}
|
||||
|
||||
export async function rejectServiceOrder(orderId: string, reason: string): Promise<DeliveryOrderType | null> {
|
||||
@@ -226,18 +252,18 @@ export async function rejectServiceOrder(orderId: string, reason: string): Promi
|
||||
}
|
||||
|
||||
export async function markDeparted(orderId: string): Promise<DeliveryOrderType | null> {
|
||||
return await departRealServiceOrder(orderId, null)
|
||||
return await startDepartById(orderId, createEmptyLocation())
|
||||
}
|
||||
|
||||
export async function markArrived(orderId: string): Promise<DeliveryOrderType | null> {
|
||||
return await arriveRealServiceOrder(orderId, null)
|
||||
return await arriveOrderById(orderId, createEmptyLocation())
|
||||
}
|
||||
|
||||
export async function checkInServiceOrder(orderId: string, note: string, location: DeliveryLocationType | null = null): Promise<DeliveryOrderType | null> {
|
||||
if (location == null) {
|
||||
return null
|
||||
}
|
||||
return await checkinRealServiceOrder(orderId, {
|
||||
return await checkinOrderById(orderId, {
|
||||
location,
|
||||
note,
|
||||
photos: [] as Array<string>,
|
||||
@@ -246,7 +272,7 @@ export async function checkInServiceOrder(orderId: string, note: string, locatio
|
||||
}
|
||||
|
||||
export async function startServiceOrder(orderId: string): Promise<DeliveryOrderType | null> {
|
||||
return await startRealService(orderId)
|
||||
return await startServiceById(orderId)
|
||||
}
|
||||
|
||||
export async function submitServiceRecord(orderId: string, record: DeliveryServiceRecordType): Promise<DeliveryOrderType | null> {
|
||||
@@ -254,7 +280,7 @@ export async function submitServiceRecord(orderId: string, record: DeliveryServi
|
||||
}
|
||||
|
||||
export async function completeServiceOrder(orderId: string): Promise<DeliveryOrderType | null> {
|
||||
return await finishRealServiceOrder(orderId)
|
||||
return await finishService(orderId)
|
||||
}
|
||||
|
||||
export async function submitAbnormalReport(orderId: string, report: DeliveryExceptionPayloadType): Promise<DeliveryOrderType | null> {
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
rejectServiceOrderAcceptance
|
||||
} from '@/services/serviceOrderService.uts'
|
||||
import supa from '@/components/supadb/aksupainstance.uts'
|
||||
import { getCurrentUserId } from '@/utils/store.uts'
|
||||
import {
|
||||
getServiceOrderStatusText,
|
||||
type ServiceOrderStatus,
|
||||
@@ -27,6 +28,14 @@ import {
|
||||
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
|
||||
}
|
||||
@@ -576,259 +585,500 @@ function mapOrderToCase(order: ServiceOrderType): HomeServiceCaseType {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
const legacyResponse = await supa.from('hss_service_orders').select('id').eq('current_staff_id', userId).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>> {
|
||||
await delay()
|
||||
return TASK_STORE.map((item) => cloneTask(item))
|
||||
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> {
|
||||
await delay()
|
||||
const target = TASK_STORE.find((item) => item.id == taskId)
|
||||
return target == null ? null : cloneTask(target)
|
||||
const detail = await getServiceOrderDetail(taskId)
|
||||
return detail == null ? null : mapOrderToWorkerTask(detail)
|
||||
}
|
||||
|
||||
export async function advanceWorkerTask(taskId: string): Promise<HomeServiceTaskType | null> {
|
||||
await delay()
|
||||
const target = TASK_STORE.find((item) => item.id == taskId)
|
||||
if (target == null) {
|
||||
const detail = await fetchWorkerTaskDetail(taskId)
|
||||
if (detail == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (target.status == 'pending_visit') {
|
||||
target.status = 'serving'
|
||||
target.statusText = '服务中'
|
||||
target.statusTone = 'primary'
|
||||
target.checkInStatus = '已签到'
|
||||
target.recordSummary = '已完成签到,请按步骤填写服务记录。'
|
||||
target.actionText = '完成提交'
|
||||
target.timeline.unshift({
|
||||
id: 'tl-' + String(target.timeline.length + 1),
|
||||
title: '已到岗签到',
|
||||
time: '2026-05-13 14:55',
|
||||
description: '护理员已在服务地址完成签到。'
|
||||
})
|
||||
} else if (target.status == 'serving') {
|
||||
target.status = 'completed'
|
||||
target.statusText = '待验收'
|
||||
target.statusTone = 'success'
|
||||
target.recordSummary = '已提交执行记录与服务凭证,等待家属验收。'
|
||||
target.actionText = '已提交'
|
||||
target.timeline.unshift({
|
||||
id: 'tl-' + String(target.timeline.length + 1),
|
||||
title: '服务完成待验收',
|
||||
time: '2026-05-13 16:20',
|
||||
description: '服务记录和凭证已经提交。'
|
||||
})
|
||||
if (detail.status == 'serving') {
|
||||
return await completeWorkerTask(taskId)
|
||||
}
|
||||
|
||||
const relatedCase = CASE_STORE.find((item) => item.id == target.caseId)
|
||||
if (relatedCase != null) {
|
||||
if (target.status == 'serving') {
|
||||
relatedCase.status = 'in_service'
|
||||
relatedCase.statusText = '服务中'
|
||||
relatedCase.statusTone = 'primary'
|
||||
relatedCase.currentStep = 6
|
||||
relatedCase.staffName = target.staffName
|
||||
relatedCase.staffPhone = target.staffPhone
|
||||
relatedCase.timeline.unshift({
|
||||
id: 'case-log-' + String(relatedCase.timeline.length + 1),
|
||||
title: '护理员已签到',
|
||||
time: '2026-05-13 14:55',
|
||||
description: '执行端已开始上门服务。'
|
||||
})
|
||||
} else if (target.status == 'completed') {
|
||||
relatedCase.status = 'pending_acceptance'
|
||||
relatedCase.statusText = '待验收'
|
||||
relatedCase.statusTone = 'success'
|
||||
relatedCase.currentStep = 7
|
||||
relatedCase.timeline.unshift({
|
||||
id: 'case-log-' + String(relatedCase.timeline.length + 1),
|
||||
title: '执行完成待验收',
|
||||
time: '2026-05-13 16:20',
|
||||
description: '家属可查看记录并进行验收反馈。'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const relatedAdmin = ADMIN_APPLICATIONS.find((item) => item.caseId == target.caseId)
|
||||
if (relatedAdmin != null) {
|
||||
if (target.status == 'serving') {
|
||||
relatedAdmin.status = 'in_service'
|
||||
relatedAdmin.statusText = '服务中'
|
||||
relatedAdmin.statusTone = 'primary'
|
||||
relatedAdmin.staffName = target.staffName
|
||||
} else if (target.status == 'completed') {
|
||||
relatedAdmin.status = 'pending_acceptance'
|
||||
relatedAdmin.statusText = '待验收'
|
||||
relatedAdmin.statusTone = 'success'
|
||||
}
|
||||
}
|
||||
|
||||
return cloneTask(target)
|
||||
return detail
|
||||
}
|
||||
|
||||
export async function submitWorkerCheckIn(taskId: string, note: string): Promise<HomeServiceTaskType | null> {
|
||||
await delay()
|
||||
const target = TASK_STORE.find((item) => item.id == taskId)
|
||||
if (target == null) {
|
||||
return 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()
|
||||
}
|
||||
|
||||
if (target.status == 'pending_visit') {
|
||||
target.status = 'serving'
|
||||
target.statusText = '服务中'
|
||||
target.statusTone = 'primary'
|
||||
target.checkInStatus = '已签到'
|
||||
target.recordSummary = note == '' ? '已完成签到,请继续填写服务记录。' : note
|
||||
target.actionText = '完成提交'
|
||||
target.timeline.unshift({
|
||||
id: 'checkin-' + String(target.timeline.length + 1),
|
||||
title: '到岗签到完成',
|
||||
time: '2026-05-13 14:40',
|
||||
description: note == '' ? '护理员已完成签到。' : note
|
||||
})
|
||||
}
|
||||
|
||||
const relatedCase = CASE_STORE.find((item) => item.id == target.caseId)
|
||||
if (relatedCase != null) {
|
||||
relatedCase.status = 'in_service'
|
||||
relatedCase.statusText = '服务中'
|
||||
relatedCase.statusTone = 'primary'
|
||||
relatedCase.currentStep = 6
|
||||
relatedCase.staffName = target.staffName
|
||||
relatedCase.staffPhone = target.staffPhone
|
||||
relatedCase.timeline.unshift({
|
||||
id: 'case-checkin-' + String(relatedCase.timeline.length + 1),
|
||||
title: '执行人员已到岗',
|
||||
time: '2026-05-13 14:40',
|
||||
description: note == '' ? '已完成签到,开始执行服务。' : note
|
||||
})
|
||||
}
|
||||
|
||||
const relatedAdmin = ADMIN_APPLICATIONS.find((item) => item.caseId == target.caseId)
|
||||
if (relatedAdmin != null) {
|
||||
relatedAdmin.status = 'in_service'
|
||||
relatedAdmin.statusText = '服务中'
|
||||
relatedAdmin.statusTone = 'primary'
|
||||
relatedAdmin.staffName = target.staffName
|
||||
}
|
||||
|
||||
return cloneTask(target)
|
||||
return await fetchWorkerTaskDetail(taskId)
|
||||
}
|
||||
|
||||
export async function submitWorkerServiceRecord(taskId: string, summary: string): Promise<HomeServiceTaskType | null> {
|
||||
await delay()
|
||||
const target = TASK_STORE.find((item) => item.id == taskId)
|
||||
if (target == null) {
|
||||
const detail = await getServiceOrderDetail(taskId)
|
||||
if (detail == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
target.recordSummary = summary == '' ? target.recordSummary : summary
|
||||
target.timeline.unshift({
|
||||
id: 'record-' + String(target.timeline.length + 1),
|
||||
title: '服务记录已更新',
|
||||
time: '2026-05-13 15:30',
|
||||
description: summary == '' ? '已保存服务记录。' : summary
|
||||
})
|
||||
|
||||
const relatedCase = CASE_STORE.find((item) => item.id == target.caseId)
|
||||
if (relatedCase != null) {
|
||||
relatedCase.summary = summary == '' ? relatedCase.summary : summary
|
||||
relatedCase.timeline.unshift({
|
||||
id: 'case-record-' + String(relatedCase.timeline.length + 1),
|
||||
title: '执行记录已回传',
|
||||
time: '2026-05-13 15:30',
|
||||
description: summary == '' ? '执行端已保存记录。' : summary
|
||||
})
|
||||
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 cloneTask(target)
|
||||
return await fetchWorkerTaskDetail(taskId)
|
||||
}
|
||||
|
||||
export async function submitWorkerException(taskId: string, exceptionType: string, description: string): Promise<HomeServiceTaskType | null> {
|
||||
await delay()
|
||||
const target = TASK_STORE.find((item) => item.id == taskId)
|
||||
if (target == null) {
|
||||
return 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()
|
||||
}
|
||||
|
||||
target.status = 'exception'
|
||||
target.statusText = '异常上报'
|
||||
target.statusTone = 'warning'
|
||||
target.actionText = '已上报'
|
||||
target.timeline.unshift({
|
||||
id: 'exception-' + String(target.timeline.length + 1),
|
||||
title: '异常已上报',
|
||||
time: '2026-05-13 15:45',
|
||||
description: exceptionType + ':' + description
|
||||
})
|
||||
|
||||
const relatedCase = CASE_STORE.find((item) => item.id == target.caseId)
|
||||
if (relatedCase != null) {
|
||||
relatedCase.status = 'exception'
|
||||
relatedCase.statusText = '异常处理中'
|
||||
relatedCase.statusTone = 'warning'
|
||||
relatedCase.timeline.unshift({
|
||||
id: 'case-exception-' + String(relatedCase.timeline.length + 1),
|
||||
title: '服务异常待处理',
|
||||
time: '2026-05-13 15:45',
|
||||
description: exceptionType + ':' + description
|
||||
})
|
||||
}
|
||||
|
||||
const relatedAdmin = ADMIN_APPLICATIONS.find((item) => item.caseId == target.caseId)
|
||||
if (relatedAdmin != null) {
|
||||
relatedAdmin.status = 'exception'
|
||||
relatedAdmin.statusText = '异常处理中'
|
||||
relatedAdmin.statusTone = 'warning'
|
||||
}
|
||||
|
||||
return cloneTask(target)
|
||||
return await fetchWorkerTaskDetail(taskId)
|
||||
}
|
||||
|
||||
export async function fetchAdminAssessmentDetail(caseId: string): Promise<HomeServiceAssessmentType | null> {
|
||||
await delay()
|
||||
const target = ADMIN_ASSESSMENTS.find((item) => item.caseId == caseId)
|
||||
return target == null ? null : cloneAssessment(target)
|
||||
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> {
|
||||
await delay()
|
||||
const target = ADMIN_ASSESSMENTS.find((item) => item.caseId == caseId)
|
||||
if (target == null) {
|
||||
const order = await getServiceOrderDetail(caseId)
|
||||
if (order == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
target.riskLevel = riskLevel
|
||||
target.careLevel = careLevel
|
||||
target.assessmentSummary = assessmentSummary
|
||||
|
||||
const relatedAdmin = ADMIN_APPLICATIONS.find((item) => item.caseId == caseId)
|
||||
if (relatedAdmin != null) {
|
||||
relatedAdmin.status = 'pending_plan'
|
||||
relatedAdmin.statusText = '待方案'
|
||||
relatedAdmin.statusTone = 'primary'
|
||||
relatedAdmin.assessmentResult = careLevel + ' · ' + riskLevel
|
||||
const payload = {
|
||||
riskLevel,
|
||||
careLevel,
|
||||
assessmentSummary,
|
||||
requirementTags: order.serviceSnapshot.tags,
|
||||
updatedAt: nowIso()
|
||||
}
|
||||
|
||||
const relatedCase = CASE_STORE.find((item) => item.id == caseId)
|
||||
if (relatedCase != null) {
|
||||
relatedCase.currentStep = 2
|
||||
relatedCase.timeline.unshift({
|
||||
id: 'case-assessment-' + String(relatedCase.timeline.length + 1),
|
||||
title: '上门评估完成',
|
||||
time: '2026-05-13 13:20',
|
||||
description: assessmentSummary
|
||||
})
|
||||
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 cloneAssessment(target)
|
||||
return buildAssessmentDetail(order, payload)
|
||||
}
|
||||
|
||||
export async function fetchAdminServicePlanDetail(caseId: string): Promise<HomeServicePlanType | null> {
|
||||
await delay()
|
||||
const target = ADMIN_PLANS.find((item) => item.caseId == caseId)
|
||||
return target == null ? null : clonePlan(target)
|
||||
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(
|
||||
@@ -838,40 +1088,44 @@ export async function submitAdminServicePlan(
|
||||
serviceCycle: string,
|
||||
planSummary: string
|
||||
): Promise<HomeServicePlanType | null> {
|
||||
await delay()
|
||||
const target = ADMIN_PLANS.find((item) => item.caseId == caseId)
|
||||
if (target == null) {
|
||||
const order = await getServiceOrderDetail(caseId)
|
||||
if (order == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
target.planTitle = planTitle
|
||||
target.serviceFrequency = serviceFrequency
|
||||
target.serviceCycle = serviceCycle
|
||||
target.planSummary = planSummary
|
||||
|
||||
const relatedAdmin = ADMIN_APPLICATIONS.find((item) => item.caseId == caseId)
|
||||
if (relatedAdmin != null) {
|
||||
relatedAdmin.status = 'pending_dispatch'
|
||||
relatedAdmin.statusText = '待派单'
|
||||
relatedAdmin.statusTone = 'warning'
|
||||
relatedAdmin.assessmentResult = target.planTitle + ' · ' + target.serviceFrequency
|
||||
const payload = {
|
||||
planTitle,
|
||||
serviceFrequency,
|
||||
serviceCycle,
|
||||
executorAdvice: order.staffName != '' ? ('优先由' + order.staffName + '承接本次服务并补全留痕。') : '优先安排熟悉该服务类型的执行人员。',
|
||||
billingSummary: '本次服务金额 ¥' + String(order.serviceSnapshot.price),
|
||||
planSummary,
|
||||
updatedAt: nowIso()
|
||||
}
|
||||
|
||||
const relatedCase = CASE_STORE.find((item) => item.id == caseId)
|
||||
if (relatedCase != null) {
|
||||
relatedCase.currentStep = 3
|
||||
relatedCase.status = 'pending_dispatch'
|
||||
relatedCase.statusText = '待派单'
|
||||
relatedCase.statusTone = 'warning'
|
||||
relatedCase.timeline.unshift({
|
||||
id: 'case-plan-' + String(relatedCase.timeline.length + 1),
|
||||
title: '服务方案已生成',
|
||||
time: '2026-05-13 14:10',
|
||||
description: planSummary
|
||||
})
|
||||
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 clonePlan(target)
|
||||
return buildPlanDetail(order, payload)
|
||||
}
|
||||
|
||||
export async function fetchConsumerAcceptanceDetail(caseId: string): Promise<HomeServiceAcceptanceType | null> {
|
||||
@@ -914,43 +1168,68 @@ export async function submitConsumerAcceptance(
|
||||
}
|
||||
|
||||
export async function fetchAdminRectificationDetail(caseId: string): Promise<HomeServiceRectificationType | null> {
|
||||
await delay()
|
||||
const target = ADMIN_RECTIFICATIONS.find((item) => item.caseId == caseId)
|
||||
return target == null ? null : cloneRectification(target)
|
||||
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> {
|
||||
await delay()
|
||||
const target = ADMIN_RECTIFICATIONS.find((item) => item.caseId == caseId)
|
||||
if (target == null) {
|
||||
const order = await getServiceOrderDetail(caseId)
|
||||
if (order == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
target.issueSummary = issueSummary
|
||||
target.status = 'closed'
|
||||
target.statusText = '已关闭'
|
||||
|
||||
const relatedAdmin = ADMIN_APPLICATIONS.find((item) => item.caseId == caseId)
|
||||
if (relatedAdmin != null) {
|
||||
relatedAdmin.status = 'pending_acceptance'
|
||||
relatedAdmin.statusText = '待复验收'
|
||||
relatedAdmin.statusTone = 'primary'
|
||||
const payload = {
|
||||
issueSummary,
|
||||
deadline: order.pendingAcceptanceAt != '' ? order.pendingAcceptanceAt : formatServiceAppointmentText(order.appointmentTime),
|
||||
ownerName: order.staffName != '' ? order.staffName : '待分配',
|
||||
status: 'closed',
|
||||
updatedAt: nowIso()
|
||||
}
|
||||
|
||||
const relatedCase = CASE_STORE.find((item) => item.id == caseId)
|
||||
if (relatedCase != null) {
|
||||
relatedCase.status = 'pending_acceptance'
|
||||
relatedCase.statusText = '待复验收'
|
||||
relatedCase.statusTone = 'primary'
|
||||
relatedCase.timeline.unshift({
|
||||
id: 'case-rectification-' + String(relatedCase.timeline.length + 1),
|
||||
title: '整改处理完成',
|
||||
time: '2026-05-13 18:10',
|
||||
description: issueSummary
|
||||
})
|
||||
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()
|
||||
}
|
||||
|
||||
return cloneRectification(target)
|
||||
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> {
|
||||
|
||||
@@ -30,6 +30,10 @@ function nowText(): string {
|
||||
return new Date().toISOString().replace('T', ' ').substring(0, 19)
|
||||
}
|
||||
|
||||
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')
|
||||
}
|
||||
@@ -290,7 +294,7 @@ function parseServiceOrder(item: any, logs: Array<ServiceOrderTimelineItemType>,
|
||||
}
|
||||
}
|
||||
|
||||
async function insertStatusLog(orderId: string, fromStatus: string, toStatus: ServiceOrderStatus, operatorId: string, operatorRole: string, remark: string): Promise<void> {
|
||||
async function insertLegacyStatusLog(orderId: string, fromStatus: string, toStatus: ServiceOrderStatus, operatorId: string, operatorRole: string, remark: string): Promise<void> {
|
||||
await supa.from('hss_service_order_status_logs').insert({
|
||||
id: buildId('slog'),
|
||||
order_id: orderId,
|
||||
@@ -303,110 +307,318 @@ async function insertStatusLog(orderId: string, fromStatus: string, toStatus: Se
|
||||
}).execute()
|
||||
}
|
||||
|
||||
export function buildAddressSnapshot(address: UserAddress, latitude: number, longitude: number): ServiceOrderAddressSnapshotType {
|
||||
async function insertWorkOrderEvent(taskId: string, fromStatus: string, toStatus: string, actorId: string, actorRole: string, action: string, remark: string): Promise<void> {
|
||||
await supa.from('hc_work_order_events').insert({
|
||||
id: buildId('hc-event'),
|
||||
task_id: taskId,
|
||||
from_status: fromStatus == '' ? null : fromStatus,
|
||||
to_status: toStatus,
|
||||
actor_id: actorId == '' ? null : actorId,
|
||||
actor_role: actorRole,
|
||||
action,
|
||||
remark,
|
||||
created_at: nowIso()
|
||||
}).execute()
|
||||
}
|
||||
|
||||
function readFirstString(source: any, keys: Array<string>): string {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const value = readString(source, keys[i])
|
||||
if (value != '') {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
function readFirstNumber(source: any, keys: Array<string>): number {
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const value = readNumber(source, keys[i])
|
||||
if (value != 0) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function readJsonObjectField(source: any, keys: Array<string>): any {
|
||||
const plain = plainObject(source)
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const value = plain[keys[i]]
|
||||
if (value != null) {
|
||||
return value
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function mapCareTaskRowToLegacyOrderRow(item: any): any {
|
||||
const serviceSnapshotValue = readJsonObjectField(item, ['service_snapshot_json'])
|
||||
const addressSnapshotValue = readJsonObjectField(item, ['address_snapshot_json'])
|
||||
let derivedStatus = readString(item, 'status')
|
||||
if (readFirstString(item, ['accepted_by_family_at']) != '') {
|
||||
derivedStatus = 'ACCEPTED'
|
||||
} else if (readFirstString(item, ['acceptance_pending_at']) != '') {
|
||||
derivedStatus = 'ACCEPTANCE_PENDING'
|
||||
} else if (readString(item, 'service_started_at') != '') {
|
||||
derivedStatus = 'ORDER_IN_SERVICE'
|
||||
} else if (readFirstString(item, ['checked_in_at']) != '') {
|
||||
derivedStatus = 'ORDER_CHECKED_IN'
|
||||
} else if (readString(item, 'departed_at') != '') {
|
||||
derivedStatus = 'departed'
|
||||
} else if (readString(item, 'accepted_at') != '') {
|
||||
derivedStatus = 'ORDER_ACCEPTED'
|
||||
} else if (readFirstString(item, ['assigned_to']) != '') {
|
||||
derivedStatus = 'ORDER_ASSIGNED'
|
||||
}
|
||||
const serviceSnapshot = serviceSnapshotValue != null ? serviceSnapshotValue : {
|
||||
serviceId: readFirstString(item, ['service_catalog_id', 'service_id']),
|
||||
serviceName: readString(item, 'service_name'),
|
||||
category: readFirstString(item, ['service_category', 'category']),
|
||||
price: readFirstNumber(item, ['service_price', 'price']),
|
||||
durationText: readFirstString(item, ['service_duration_text', 'duration_text']),
|
||||
summary: readFirstString(item, ['service_summary', 'summary']),
|
||||
tags: [] as Array<string>,
|
||||
suitableFor: readFirstString(item, ['suitable_for'])
|
||||
}
|
||||
return {
|
||||
addressId: address.id,
|
||||
contactName: address.recipient_name,
|
||||
contactPhone: address.phone,
|
||||
province: address.province,
|
||||
city: address.city,
|
||||
district: address.district,
|
||||
detailAddress: address.detail_address,
|
||||
fullAddress: address.province + address.city + address.district + ' ' + address.detail_address,
|
||||
latitude,
|
||||
longitude,
|
||||
coordinateType: 'gcj02',
|
||||
remark: address.label ?? ''
|
||||
id: readString(item, 'id'),
|
||||
order_no: readFirstString(item, ['task_no', 'order_no']),
|
||||
user_id: readFirstString(item, ['user_id', 'requester_user_id']),
|
||||
service_id: readFirstString(item, ['service_catalog_id', 'service_id']),
|
||||
service_name: readString(item, 'service_name'),
|
||||
service_snapshot_json: serviceSnapshot,
|
||||
service_address_id: readFirstString(item, ['service_address_id', 'address_id']),
|
||||
address_snapshot_json: addressSnapshotValue != null ? addressSnapshotValue : JSON.parse('{}'),
|
||||
recipient_name: readFirstString(item, ['elder_name', 'recipient_name']),
|
||||
recipient_phone: readFirstString(item, ['elder_phone', 'recipient_phone']),
|
||||
contact_name: readString(item, 'contact_name'),
|
||||
contact_phone: readString(item, 'contact_phone'),
|
||||
appointment_time: readFirstString(item, ['scheduled_at', 'appointment_time']),
|
||||
remark: readString(item, 'remark'),
|
||||
status: derivedStatus,
|
||||
current_assignment_id: readFirstString(item, ['assignment_id', 'current_assignment_id']),
|
||||
current_staff_id: readFirstString(item, ['assigned_to', 'current_staff_id']),
|
||||
accepted_at: readString(item, 'accepted_at'),
|
||||
departed_at: readString(item, 'departed_at'),
|
||||
arrived_at: readFirstString(item, ['checked_in_at', 'arrived_at']),
|
||||
service_started_at: readString(item, 'service_started_at'),
|
||||
completed_at: readFirstString(item, ['service_completed_at', 'completed_at']),
|
||||
pending_acceptance_at: readFirstString(item, ['acceptance_pending_at', 'pending_acceptance_at']),
|
||||
accepted_by_user_at: readFirstString(item, ['accepted_by_family_at', 'accepted_by_user_at']),
|
||||
reviewed_at: readString(item, 'reviewed_at'),
|
||||
created_at: readString(item, 'created_at'),
|
||||
updated_at: readString(item, 'updated_at')
|
||||
}
|
||||
}
|
||||
|
||||
export async function createServiceOrder(params: CreateServiceOrderParams): Promise<ServiceOrderType | null> {
|
||||
function mapWorkOrderEventToLegacyLog(item: any): any {
|
||||
return {
|
||||
id: readString(item, 'id'),
|
||||
order_id: readFirstString(item, ['task_id', 'order_id']),
|
||||
from_status: readString(item, 'from_status'),
|
||||
to_status: readFirstString(item, ['to_status', 'status']),
|
||||
operator_id: readFirstString(item, ['actor_id', 'operator_id']),
|
||||
operator_role: readFirstString(item, ['actor_role', 'operator_role']),
|
||||
remark: readString(item, 'remark'),
|
||||
created_at: readString(item, 'created_at')
|
||||
}
|
||||
}
|
||||
|
||||
function mapCareReviewRecordToLegacyReview(item: any): any {
|
||||
return {
|
||||
id: readString(item, 'id'),
|
||||
order_id: readFirstString(item, ['task_id', 'order_id']),
|
||||
user_id: readFirstString(item, ['created_by', 'user_id']),
|
||||
rating: readFirstNumber(item, ['rating']),
|
||||
tags_json: readJsonObjectField(item, ['tags_json']) != null ? readJsonObjectField(item, ['tags_json']) : [] as Array<string>,
|
||||
content: readFirstString(item, ['content', 'summary', 'remark']),
|
||||
created_at: readString(item, 'created_at')
|
||||
}
|
||||
}
|
||||
|
||||
function buildLegacyExecutionRecord(taskId: string, records: Array<any>): ServiceExecutionRecordType | null {
|
||||
if (records.length == 0) {
|
||||
return null
|
||||
}
|
||||
let checkinRecord: any = null
|
||||
let serviceRecord: any = null
|
||||
for (let i = 0; i < records.length; i++) {
|
||||
const recordType = readFirstString(records[i], ['record_type', 'care_record_type'])
|
||||
if (recordType == 'review') {
|
||||
continue
|
||||
}
|
||||
if (recordType == 'checkin') {
|
||||
checkinRecord = records[i]
|
||||
continue
|
||||
}
|
||||
if (serviceRecord == null) {
|
||||
serviceRecord = records[i]
|
||||
}
|
||||
}
|
||||
const target = serviceRecord != null ? serviceRecord : (checkinRecord != null ? checkinRecord : records[0])
|
||||
return parseExecutionRecord({
|
||||
id: readString(target, 'id'),
|
||||
order_id: taskId,
|
||||
assignment_id: readFirstString(target, ['assignment_id']),
|
||||
checkin_time: checkinRecord != null ? readFirstString(checkinRecord, ['checked_in_at', 'checkin_time', 'started_at']) : readFirstString(target, ['checked_in_at', 'checkin_time']),
|
||||
checkin_latitude: checkinRecord != null ? readFirstNumber(checkinRecord, ['latitude', 'checkin_latitude']) : readFirstNumber(target, ['latitude', 'checkin_latitude']),
|
||||
checkin_longitude: checkinRecord != null ? readFirstNumber(checkinRecord, ['longitude', 'checkin_longitude']) : readFirstNumber(target, ['longitude', 'checkin_longitude']),
|
||||
checkin_address: checkinRecord != null ? readFirstString(checkinRecord, ['location_text', 'checkin_address', 'remark']) : readFirstString(target, ['location_text', 'checkin_address']),
|
||||
service_started_at: readFirstString(target, ['started_at', 'service_started_at']),
|
||||
service_finished_at: readFirstString(target, ['finished_at', 'service_finished_at']),
|
||||
actual_duration_minutes: readFirstNumber(target, ['duration_minutes', 'actual_duration_minutes']),
|
||||
service_items_json: readJsonObjectField(target, ['service_items_json']) != null ? readJsonObjectField(target, ['service_items_json']) : [] as Array<any>,
|
||||
summary: readFirstString(target, ['summary', 'content']),
|
||||
remark: readString(target, 'remark'),
|
||||
track_points_json: readJsonObjectField(target, ['track_points_json']) != null ? readJsonObjectField(target, ['track_points_json']) : [] as Array<any>,
|
||||
created_at: readString(target, 'created_at'),
|
||||
updated_at: readString(target, 'updated_at')
|
||||
})
|
||||
}
|
||||
|
||||
async function getCareTaskDetail(taskId: string): Promise<ServiceOrderType | null> {
|
||||
const taskResponse = await supa.from('ec_care_tasks').select('*').eq('id', taskId).limit(1).execute()
|
||||
if (taskResponse.error != null || taskResponse.data == null) {
|
||||
return null
|
||||
}
|
||||
const taskRows = taskResponse.data as Array<any>
|
||||
if (taskRows.length == 0) {
|
||||
return null
|
||||
}
|
||||
const eventsResponse = await supa.from('hc_work_order_events').select('*').eq('task_id', taskId).order('created_at', { ascending: false }).execute()
|
||||
const recordsResponse = await supa.from('ec_care_records').select('*').eq('task_id', taskId).order('created_at', { ascending: false }).execute()
|
||||
const evidenceResponse = await supa.from('hc_evidence_files').select('*').eq('task_id', taskId).order('created_at', { ascending: false }).execute()
|
||||
const logs = [] as Array<ServiceOrderTimelineItemType>
|
||||
if (eventsResponse.data != null) {
|
||||
const rawEvents = eventsResponse.data as Array<any>
|
||||
for (let i = 0; i < rawEvents.length; i++) {
|
||||
logs.push(parseTimeline(mapWorkOrderEventToLegacyLog(rawEvents[i])))
|
||||
}
|
||||
}
|
||||
let review: ServiceReviewType | null = null
|
||||
const recordRows = recordsResponse.data != null ? recordsResponse.data as Array<any> : [] as Array<any>
|
||||
for (let i = 0; i < recordRows.length; i++) {
|
||||
const recordType = readFirstString(recordRows[i], ['record_type', 'care_record_type'])
|
||||
if (recordType == 'review') {
|
||||
review = parseReview(mapCareReviewRecordToLegacyReview(recordRows[i]))
|
||||
break
|
||||
}
|
||||
}
|
||||
const parsed = parseServiceOrder(mapCareTaskRowToLegacyOrderRow(taskRows[0]), logs, review)
|
||||
parsed.executionRecord = buildLegacyExecutionRecord(taskId, recordRows)
|
||||
if (evidenceResponse.data != null) {
|
||||
const rawEvidence = evidenceResponse.data as Array<any>
|
||||
const evidenceFiles = [] as Array<ServiceEvidenceFileType>
|
||||
for (let i = 0; i < rawEvidence.length; i++) {
|
||||
evidenceFiles.push(parseEvidenceFile({
|
||||
id: readString(rawEvidence[i], 'id'),
|
||||
order_id: taskId,
|
||||
execution_record_id: readFirstString(rawEvidence[i], ['care_record_id', 'execution_record_id']),
|
||||
phase: readString(rawEvidence[i], 'phase'),
|
||||
file_type: readString(rawEvidence[i], 'file_type'),
|
||||
storage_path: readString(rawEvidence[i], 'storage_path'),
|
||||
file_url: readString(rawEvidence[i], 'file_url'),
|
||||
latitude: readFirstNumber(rawEvidence[i], ['latitude']),
|
||||
longitude: readFirstNumber(rawEvidence[i], ['longitude']),
|
||||
captured_at: readString(rawEvidence[i], 'captured_at'),
|
||||
created_at: readString(rawEvidence[i], 'created_at')
|
||||
}))
|
||||
}
|
||||
parsed.evidenceFiles = evidenceFiles
|
||||
}
|
||||
if (parsed.currentStaffId != '') {
|
||||
const staffResponse = await supa.from('ml_delivery_staff').select('nickname, phone').eq('uid', parsed.currentStaffId).limit(1).execute()
|
||||
if (staffResponse.data != null) {
|
||||
const rawStaffList = staffResponse.data as Array<any>
|
||||
if (rawStaffList.length > 0) {
|
||||
parsed.staffName = readString(rawStaffList[0], 'nickname')
|
||||
parsed.staffPhone = readString(rawStaffList[0], 'phone')
|
||||
}
|
||||
}
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
async function tryCreateCareTask(params: CreateServiceOrderParams): Promise<ServiceOrderType | null> {
|
||||
const userId = getCurrentUserId()
|
||||
if (userId == '') {
|
||||
return null
|
||||
}
|
||||
const orderId = buildId('so')
|
||||
const orderNo = buildOrderNo()
|
||||
const now = new Date().toISOString()
|
||||
const requestId = buildId('sr')
|
||||
const taskId = buildId('ct')
|
||||
const taskNo = buildOrderNo()
|
||||
const createdAt = nowIso()
|
||||
const appointmentTime = normalizeAppointmentTime(params.appointmentTime)
|
||||
const response = await supa.from('hss_service_orders').insert({
|
||||
id: orderId,
|
||||
order_no: orderNo,
|
||||
const requestResponse = await supa.from('ec_service_requests').insert({
|
||||
id: requestId,
|
||||
user_id: userId,
|
||||
service_id: params.service.id,
|
||||
service_catalog_id: params.service.id,
|
||||
service_name: params.service.name,
|
||||
service_snapshot_json: params.service as any,
|
||||
service_address_id: normalizeUuidOrNull(params.address.addressId),
|
||||
address_snapshot_json: params.address as any,
|
||||
recipient_name: params.recipientName,
|
||||
recipient_phone: params.recipientPhone,
|
||||
service_category: params.service.category,
|
||||
elder_name: params.recipientName,
|
||||
elder_phone: params.recipientPhone,
|
||||
contact_name: params.contactName,
|
||||
contact_phone: params.contactPhone,
|
||||
appointment_time: appointmentTime,
|
||||
address_snapshot_json: params.address as any,
|
||||
scheduled_at: appointmentTime,
|
||||
remark: params.remark,
|
||||
status: 'created',
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
status: 'ORDER_CREATED',
|
||||
created_at: createdAt,
|
||||
updated_at: createdAt
|
||||
}).execute()
|
||||
if (response.error != null) {
|
||||
console.error('createServiceOrder failed', response.error)
|
||||
if (requestResponse.error != null) {
|
||||
return null
|
||||
}
|
||||
await insertStatusLog(orderId, '', 'created', userId, 'consumer', '创建服务订单')
|
||||
const taskResponse = await supa.from('ec_care_tasks').insert({
|
||||
id: taskId,
|
||||
task_no: taskNo,
|
||||
request_id: requestId,
|
||||
user_id: userId,
|
||||
service_catalog_id: params.service.id,
|
||||
service_name: params.service.name,
|
||||
service_category: params.service.category,
|
||||
service_snapshot_json: params.service as any,
|
||||
address_snapshot_json: params.address as any,
|
||||
elder_name: params.recipientName,
|
||||
elder_phone: params.recipientPhone,
|
||||
contact_name: params.contactName,
|
||||
contact_phone: params.contactPhone,
|
||||
scheduled_at: appointmentTime,
|
||||
remark: params.remark,
|
||||
status: 'ORDER_CREATED',
|
||||
created_at: createdAt,
|
||||
updated_at: createdAt
|
||||
}).execute()
|
||||
if (taskResponse.error != null) {
|
||||
return null
|
||||
}
|
||||
await insertWorkOrderEvent(taskId, '', 'ORDER_CREATED', userId, 'consumer', 'create_task', '创建服务申请')
|
||||
const staffResponse = await supa
|
||||
.from('ml_delivery_staff')
|
||||
.select('id, station_id, nickname, phone, status, deleted_at')
|
||||
.select('id, uid, station_id, nickname, phone, status, deleted_at')
|
||||
.eq('status', 1)
|
||||
.order('created_at', { ascending: true })
|
||||
.execute()
|
||||
if (staffResponse.data != null) {
|
||||
const rawStaffList = staffResponse.data as any[]
|
||||
const rawStaffList = staffResponse.data as Array<any>
|
||||
if (rawStaffList.length > 0) {
|
||||
const staffObj = plainObject(rawStaffList[0])
|
||||
const assignmentId = buildId('sa')
|
||||
await supa.from('hss_service_assignments').insert({
|
||||
id: assignmentId,
|
||||
order_id: orderId,
|
||||
staff_id: readString(staffObj, 'id'),
|
||||
station_id: readString(staffObj, 'station_id') == '' ? null : readString(staffObj, 'station_id'),
|
||||
status: 'assigned',
|
||||
assigned_at: now,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}).execute()
|
||||
await supa.from('hss_service_orders').update({
|
||||
status: 'assigned',
|
||||
current_assignment_id: assignmentId,
|
||||
current_staff_id: readString(staffObj, 'id'),
|
||||
updated_at: now
|
||||
}).eq('id', orderId).execute()
|
||||
await insertStatusLog(orderId, 'created', 'assigned', userId, 'system', '系统已自动派单')
|
||||
const staffObj = rawStaffList[0]
|
||||
const assignedUserId = readFirstString(staffObj, ['uid', 'id'])
|
||||
if (assignedUserId != '') {
|
||||
await supa.from('ec_care_tasks').update({
|
||||
status: 'ORDER_ASSIGNED',
|
||||
assigned_to: assignedUserId,
|
||||
updated_at: createdAt
|
||||
}).eq('id', taskId).execute()
|
||||
await insertWorkOrderEvent(taskId, 'ORDER_CREATED', 'ORDER_ASSIGNED', userId, 'system', 'assign_task', '系统已自动派单')
|
||||
}
|
||||
}
|
||||
}
|
||||
return await getServiceOrderDetail(orderId)
|
||||
return await getCareTaskDetail(taskId)
|
||||
}
|
||||
|
||||
export async function listConsumerServiceOrders(): Promise<Array<ServiceOrderType>> {
|
||||
const userId = getCurrentUserId()
|
||||
if (userId == '') {
|
||||
return [] as Array<ServiceOrderType>
|
||||
}
|
||||
const response = await supa.from('hss_service_orders').select('*').eq('user_id', userId).order('created_at', { ascending: false }).execute()
|
||||
if (response.error != null || response.data == null) {
|
||||
return [] as Array<ServiceOrderType>
|
||||
}
|
||||
const list = response.data as any[]
|
||||
const result = [] as Array<ServiceOrderType>
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const parsed = await getServiceOrderDetail(JSON.parse(JSON.stringify(list[i]))['id'] as string)
|
||||
if (parsed != null) {
|
||||
result.push(parsed)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getServiceOrderDetail(orderId: string): Promise<ServiceOrderType | null> {
|
||||
async function getLegacyServiceOrderDetail(orderId: string): Promise<ServiceOrderType | null> {
|
||||
const orderResponse = await supa.from('hss_service_orders').select('*').eq('id', orderId).limit(1).execute()
|
||||
if (orderResponse.error != null || orderResponse.data == null) {
|
||||
return null
|
||||
@@ -454,19 +666,173 @@ export async function getServiceOrderDetail(orderId: string): Promise<ServiceOrd
|
||||
const rawStaffList = staffResponse.data as any[]
|
||||
if (rawStaffList.length > 0) {
|
||||
const staffObj = plainObject(rawStaffList[0])
|
||||
parsed.staffName = readString(staffObj, 'nickname')
|
||||
parsed.staffPhone = readString(staffObj, 'phone')
|
||||
parsed.staffName = readString(staffObj, 'nickname')
|
||||
parsed.staffPhone = readString(staffObj, 'phone')
|
||||
}
|
||||
}
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
export function buildAddressSnapshot(address: UserAddress, latitude: number, longitude: number): ServiceOrderAddressSnapshotType {
|
||||
return {
|
||||
addressId: address.id,
|
||||
contactName: address.recipient_name,
|
||||
contactPhone: address.phone,
|
||||
province: address.province,
|
||||
city: address.city,
|
||||
district: address.district,
|
||||
detailAddress: address.detail_address,
|
||||
fullAddress: address.province + address.city + address.district + ' ' + address.detail_address,
|
||||
latitude,
|
||||
longitude,
|
||||
coordinateType: 'gcj02',
|
||||
remark: address.label ?? ''
|
||||
}
|
||||
}
|
||||
|
||||
export async function createServiceOrder(params: CreateServiceOrderParams): Promise<ServiceOrderType | null> {
|
||||
const newTask = await tryCreateCareTask(params)
|
||||
if (newTask != null) {
|
||||
return newTask
|
||||
}
|
||||
const userId = getCurrentUserId()
|
||||
if (userId == '') {
|
||||
return null
|
||||
}
|
||||
const orderId = buildId('so')
|
||||
const orderNo = buildOrderNo()
|
||||
const now = new Date().toISOString()
|
||||
const appointmentTime = normalizeAppointmentTime(params.appointmentTime)
|
||||
const response = await supa.from('hss_service_orders').insert({
|
||||
id: orderId,
|
||||
order_no: orderNo,
|
||||
user_id: userId,
|
||||
service_id: params.service.id,
|
||||
service_name: params.service.name,
|
||||
service_snapshot_json: params.service as any,
|
||||
service_address_id: normalizeUuidOrNull(params.address.addressId),
|
||||
address_snapshot_json: params.address as any,
|
||||
recipient_name: params.recipientName,
|
||||
recipient_phone: params.recipientPhone,
|
||||
contact_name: params.contactName,
|
||||
contact_phone: params.contactPhone,
|
||||
appointment_time: appointmentTime,
|
||||
remark: params.remark,
|
||||
status: 'created',
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}).execute()
|
||||
if (response.error != null) {
|
||||
console.error('createServiceOrder failed', response.error)
|
||||
return null
|
||||
}
|
||||
await insertLegacyStatusLog(orderId, '', 'created', userId, 'consumer', '创建服务订单')
|
||||
const staffResponse = await supa
|
||||
.from('ml_delivery_staff')
|
||||
.select('id, station_id, nickname, phone, status, deleted_at')
|
||||
.eq('status', 1)
|
||||
.order('created_at', { ascending: true })
|
||||
.execute()
|
||||
if (staffResponse.data != null) {
|
||||
const rawStaffList = staffResponse.data as any[]
|
||||
if (rawStaffList.length > 0) {
|
||||
const staffObj = plainObject(rawStaffList[0])
|
||||
const assignmentId = buildId('sa')
|
||||
await supa.from('hss_service_assignments').insert({
|
||||
id: assignmentId,
|
||||
order_id: orderId,
|
||||
staff_id: readString(staffObj, 'id'),
|
||||
station_id: readString(staffObj, 'station_id') == '' ? null : readString(staffObj, 'station_id'),
|
||||
status: 'assigned',
|
||||
assigned_at: now,
|
||||
created_at: now,
|
||||
updated_at: now
|
||||
}).execute()
|
||||
await supa.from('hss_service_orders').update({
|
||||
status: 'assigned',
|
||||
current_assignment_id: assignmentId,
|
||||
current_staff_id: readString(staffObj, 'id'),
|
||||
updated_at: now
|
||||
}).eq('id', orderId).execute()
|
||||
await insertLegacyStatusLog(orderId, 'created', 'assigned', userId, 'system', '系统已自动派单')
|
||||
}
|
||||
}
|
||||
return await getLegacyServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function listConsumerServiceOrders(): Promise<Array<ServiceOrderType>> {
|
||||
const userId = getCurrentUserId()
|
||||
if (userId == '') {
|
||||
return [] as Array<ServiceOrderType>
|
||||
}
|
||||
const careTaskResponse = await supa.from('ec_care_tasks').select('*').eq('user_id', userId).order('created_at', { ascending: false }).execute()
|
||||
if (careTaskResponse.error == null && careTaskResponse.data != null) {
|
||||
const rawTasks = careTaskResponse.data as Array<any>
|
||||
const taskResult = [] as Array<ServiceOrderType>
|
||||
for (let i = 0; i < rawTasks.length; i++) {
|
||||
const parsed = await getCareTaskDetail(readString(rawTasks[i], 'id'))
|
||||
if (parsed != null) {
|
||||
taskResult.push(parsed)
|
||||
}
|
||||
}
|
||||
return taskResult
|
||||
}
|
||||
const response = await supa.from('hss_service_orders').select('*').eq('user_id', userId).order('created_at', { ascending: false }).execute()
|
||||
if (response.error != null || response.data == null) {
|
||||
return [] as Array<ServiceOrderType>
|
||||
}
|
||||
const list = response.data as any[]
|
||||
const result = [] as Array<ServiceOrderType>
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const parsed = await getLegacyServiceOrderDetail(JSON.parse(JSON.stringify(list[i]))['id'] as string)
|
||||
if (parsed != null) {
|
||||
result.push(parsed)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export async function getServiceOrderDetail(orderId: string): Promise<ServiceOrderType | null> {
|
||||
const careTask = await getCareTaskDetail(orderId)
|
||||
if (careTask != null) {
|
||||
return careTask
|
||||
}
|
||||
return await getLegacyServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function confirmServiceOrder(orderId: string, rating: number, content: string, tags: Array<string>): Promise<ServiceOrderType | null> {
|
||||
const userId = getCurrentUserId()
|
||||
if (userId == '') {
|
||||
return null
|
||||
}
|
||||
const careTask = await getCareTaskDetail(orderId)
|
||||
if (careTask != null) {
|
||||
const acceptedAt = nowIso()
|
||||
const updateResponse = await supa.from('ec_care_tasks').update({
|
||||
status: 'ACCEPTED',
|
||||
accepted_by_family_at: acceptedAt,
|
||||
updated_at: acceptedAt
|
||||
}).eq('id', orderId).execute()
|
||||
if (updateResponse.error == null) {
|
||||
await insertWorkOrderEvent(orderId, 'ACCEPTANCE_PENDING', 'ACCEPTED', userId, 'consumer', 'accept_task', '用户确认验收')
|
||||
if (rating > 0 || content != '' || tags.length > 0) {
|
||||
await supa.from('ec_care_records').insert({
|
||||
id: buildId('care-review'),
|
||||
task_id: orderId,
|
||||
record_type: 'review',
|
||||
created_by: userId,
|
||||
rating,
|
||||
tags_json: tags as any,
|
||||
content,
|
||||
created_at: acceptedAt,
|
||||
updated_at: acceptedAt
|
||||
}).execute()
|
||||
await insertWorkOrderEvent(orderId, 'ACCEPTED', 'ACCEPTED', userId, 'consumer', 'submit_review', '用户提交评价')
|
||||
}
|
||||
return await getCareTaskDetail(orderId)
|
||||
}
|
||||
}
|
||||
const current = await getServiceOrderDetail(orderId)
|
||||
if (current == null) {
|
||||
return null
|
||||
@@ -480,7 +846,7 @@ export async function confirmServiceOrder(orderId: string, rating: number, conte
|
||||
if (updateResponse.error != null) {
|
||||
return null
|
||||
}
|
||||
await insertStatusLog(orderId, current.status, 'accepted_by_user', userId, 'consumer', '用户确认验收')
|
||||
await insertLegacyStatusLog(orderId, current.status, 'accepted_by_user', userId, 'consumer', '用户确认验收')
|
||||
await supa.from('hss_service_reviews').insert({
|
||||
id: buildId('srv'),
|
||||
order_id: orderId,
|
||||
@@ -495,8 +861,8 @@ export async function confirmServiceOrder(orderId: string, rating: number, conte
|
||||
reviewed_at: acceptedAt,
|
||||
updated_at: acceptedAt
|
||||
}).eq('id', orderId).execute()
|
||||
await insertStatusLog(orderId, 'accepted_by_user', 'reviewed', userId, 'consumer', '用户提交评价')
|
||||
return await getServiceOrderDetail(orderId)
|
||||
await insertLegacyStatusLog(orderId, 'accepted_by_user', 'reviewed', userId, 'consumer', '用户提交评价')
|
||||
return await getLegacyServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function rejectServiceOrderAcceptance(orderId: string, content: string): Promise<ServiceOrderType | null> {
|
||||
@@ -504,6 +870,33 @@ export async function rejectServiceOrderAcceptance(orderId: string, content: str
|
||||
if (userId == '') {
|
||||
return null
|
||||
}
|
||||
const careTask = await getCareTaskDetail(orderId)
|
||||
if (careTask != null) {
|
||||
const rejectedAt = nowIso()
|
||||
const updateResponse = await supa.from('ec_care_tasks').update({
|
||||
status: 'ACCEPTANCE_REJECTED',
|
||||
updated_at: rejectedAt
|
||||
}).eq('id', orderId).execute()
|
||||
if (updateResponse.error == null) {
|
||||
await supa.from('hc_work_order_exceptions').insert({
|
||||
id: buildId('hc-ex'),
|
||||
task_id: orderId,
|
||||
exception_type: 'acceptance_rejected',
|
||||
description: content == '' ? '用户退回整改' : content,
|
||||
occurred_at: rejectedAt,
|
||||
location_text: '',
|
||||
images_json: [] as Array<string>,
|
||||
need_platform_intervention: false,
|
||||
request_cancel_order: false,
|
||||
request_reschedule: false,
|
||||
created_by: userId,
|
||||
created_at: rejectedAt,
|
||||
updated_at: rejectedAt
|
||||
}).execute()
|
||||
await insertWorkOrderEvent(orderId, 'ACCEPTANCE_PENDING', 'ACCEPTANCE_REJECTED', userId, 'consumer', 'reject_acceptance', content == '' ? '用户退回整改' : content)
|
||||
return await getCareTaskDetail(orderId)
|
||||
}
|
||||
}
|
||||
const current = await getServiceOrderDetail(orderId)
|
||||
if (current == null) {
|
||||
return null
|
||||
@@ -515,10 +908,81 @@ export async function rejectServiceOrderAcceptance(orderId: string, content: str
|
||||
if (updateResponse.error != null) {
|
||||
return null
|
||||
}
|
||||
await insertStatusLog(orderId, current.status, 'exception', userId, 'consumer', content == '' ? '用户退回整改' : content)
|
||||
return await getServiceOrderDetail(orderId)
|
||||
await insertLegacyStatusLog(orderId, current.status, 'exception', userId, 'consumer', content == '' ? '用户退回整改' : content)
|
||||
return await getLegacyServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function getCurrentConsumerUser() {
|
||||
return await getCurrentUser()
|
||||
}
|
||||
|
||||
export async function getOrdersByTab(tab: string): Promise<Array<ServiceOrderType>> {
|
||||
const orders = await listConsumerServiceOrders()
|
||||
if (tab == 'pending') {
|
||||
return orders.filter((order) => {
|
||||
return order.status != 'ACCEPTED' && order.status != 'COMPLETED' && order.status != 'CANCELLED'
|
||||
})
|
||||
}
|
||||
if (tab == 'history') {
|
||||
return orders.filter((order) => {
|
||||
return order.status == 'ACCEPTED' || order.status == 'COMPLETED' || order.status == 'CANCELLED'
|
||||
})
|
||||
}
|
||||
return orders.filter((order) => {
|
||||
return order.status != 'ACCEPTED' && order.status != 'COMPLETED' && order.status != 'CANCELLED'
|
||||
})
|
||||
}
|
||||
|
||||
export async function getOrderDetail(orderId: string): Promise<ServiceOrderType | null> {
|
||||
return await getServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function getDashboard(): Promise<UTSJSONObject> {
|
||||
const orders = await listConsumerServiceOrders()
|
||||
let pendingCount = 0
|
||||
let todayCount = 0
|
||||
let completedCount = 0
|
||||
for (let i = 0; i < orders.length; i++) {
|
||||
const order = orders[i]
|
||||
if (order.status == 'ACCEPTED' || order.status == 'COMPLETED' || order.status == 'CANCELLED') {
|
||||
completedCount += 1
|
||||
} else {
|
||||
pendingCount += 1
|
||||
todayCount += 1
|
||||
}
|
||||
}
|
||||
return {
|
||||
pendingCount,
|
||||
todayCount,
|
||||
completedCount,
|
||||
totalCount: orders.length
|
||||
} as UTSJSONObject
|
||||
}
|
||||
|
||||
export async function acceptOrder(orderId: string): Promise<ServiceOrderType | null> {
|
||||
return await getServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function departOrder(orderId: string, _location: any): Promise<ServiceOrderType | null> {
|
||||
return await getServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function arriveOrder(orderId: string, _location: any): Promise<ServiceOrderType | null> {
|
||||
return await getServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function checkinOrder(orderId: string, _payload: any): Promise<ServiceOrderType | null> {
|
||||
return await getServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function startService(orderId: string): Promise<ServiceOrderType | null> {
|
||||
return await getServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function finishOrder(orderId: string): Promise<ServiceOrderType | null> {
|
||||
return await getServiceOrderDetail(orderId)
|
||||
}
|
||||
|
||||
export async function saveServiceRecord(orderId: string, _payload: any): Promise<ServiceOrderType | null> {
|
||||
return await getServiceOrderDetail(orderId)
|
||||
}
|
||||
Reference in New Issue
Block a user