Files
medical-mall/services/deliveryService.uts

646 lines
20 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
acceptDeliveryOrderById,
acceptHomecareAssignmentV2,
arriveOrderById,
checkinOrderById,
finishServiceById,
getDeliveryDashboardByStaffId,
getDeliveryOrderDetailById,
getDeliveryOrdersByStaffId,
getDeliveryProfileByUserId,
loginDelivery as loginDeliveryApi,
saveServiceProgressById,
startDepartById,
startServiceById
} from '@/api/delivery.uts'
import {
getOrderDetail as getDirectServiceOrderDetail,
getOrdersByTab as getDirectOrdersByTab
} from '@/services/serviceOrderService.uts'
import {
getDeliveryInfo,
getUserInfo,
requireDeliveryAuth,
setDeliveryInfo
} from '@/utils/deliveryAuth.uts'
import { getCurrentAkUserId as readCurrentAkUserId } from '@/utils/akUserMapping.uts'
import type {
DeliveryAbnormalReportType,
DeliveryCheckinPayloadType,
DeliveryDashboardType,
DeliveryExceptionPayloadType,
DeliveryFinishPayloadType,
DeliveryInfoType,
DeliveryLocationType,
DeliveryLoginPayloadType,
DeliveryLoginResultType,
DeliveryActionResultType,
DeliveryMessageType,
DeliveryOrderQueryType,
DeliveryOrderStatus,
DeliveryOrderType,
DeliveryProgressPayloadType,
DeliveryRecordType,
DeliveryServiceRecordType
} from '@/types/delivery.uts'
function nowText(): string {
return new Date().toISOString().replace('T', ' ').substring(0, 19)
}
function emptyDashboard(): DeliveryDashboardType {
return {
pendingAssignmentCount: 0,
pendingAcceptCount: 0,
todayOrderCount: 0,
pendingDepartCount: 0,
servingCount: 0,
completedCount: 0,
exceptionCount: 0,
expectedIncome: 0,
onlineStatus: 'resting',
nextOrder: null,
recentOrders: [] as Array<DeliveryOrderType>
} as DeliveryDashboardType
}
async function getCurrentStaffIdOrEmpty(): Promise<string> {
const authResult = await requireDeliveryAuth({ redirectOnFail: false, toastOnFail: false })
if (!authResult.ok || authResult.deliveryInfo == null) {
return ''
}
return authResult.deliveryInfo.id
}
function hasOrderCoreInfo(order: DeliveryOrderType | null): boolean {
if (order == null) {
return false
}
return order.serviceName != '' || order.elderName != '' || order.address != '' || order.contactName != ''
}
function filterOrdersWithCoreInfo(orders: Array<DeliveryOrderType>): Array<DeliveryOrderType> {
const result = [] as Array<DeliveryOrderType>
for (let i = 0; i < orders.length; i++) {
if (hasOrderCoreInfo(orders[i])) {
result.push(orders[i])
}
}
return result
}
function shouldFallbackOrders(orders: Array<DeliveryOrderType>): boolean {
if (orders.length == 0) {
return false
}
for (let i = 0; i < orders.length; i++) {
if (hasOrderCoreInfo(orders[i])) {
return false
}
}
return true
}
function getOrderCanonicalKey(order: DeliveryOrderType): string {
const orderNo = String(order.orderNo).trim().toLowerCase()
if (orderNo != '') {
return orderNo
}
const sourceOrderId = String(order.sourceOrderId).trim().toLowerCase()
if (sourceOrderId != '') {
return sourceOrderId
}
const legacyServiceOrderId = String(order.legacyServiceOrderId).trim().toLowerCase()
if (legacyServiceOrderId != '') {
return legacyServiceOrderId
}
return String(order.id).trim().toLowerCase()
}
function isActiveHomecareStatus(status: string): boolean {
const s = String(status).trim().toLowerCase()
return s == 'assigned' ||
s == 'accepted' ||
s == 'departed' ||
s == 'arrived' ||
s == 'in_service' ||
s == 'serving' ||
s == 'service_started' ||
s == 'completed'
}
function buildActiveOrderKeys(orders: Array<DeliveryOrderType>): Array<string> {
const keys = [] as Array<string>
for (let i = 0; i < orders.length; i++) {
const order = orders[i]
if (!isActiveHomecareStatus(String(order.status))) {
continue
}
const key = getOrderCanonicalKey(order)
if (key == '') {
continue
}
let exists = false
for (let j = 0; j < keys.length; j++) {
if (keys[j] == key) {
exists = true
break
}
}
if (!exists) {
keys.push(key)
}
}
return keys
}
function filterPendingOrdersAgainstActiveKeys(orders: Array<DeliveryOrderType>, activeKeys: Array<string>): Array<DeliveryOrderType> {
if (orders.length == 0 || activeKeys.length == 0) {
return orders
}
const result = [] as Array<DeliveryOrderType>
for (let i = 0; i < orders.length; i++) {
const key = getOrderCanonicalKey(orders[i])
let duplicated = false
for (let j = 0; j < activeKeys.length; j++) {
if (activeKeys[j] == key) {
duplicated = true
break
}
}
if (!duplicated) {
result.push(orders[i])
}
}
return result
}
function mapOrderToRecord(order: DeliveryOrderType): DeliveryRecordType {
const isAccepted = order.status == 'completed' || order.status == 'pending_acceptance' || order.status == 'settled' || order.status == 'archived'
return {
id: order.id,
orderId: order.id,
orderNo: order.orderNo,
serviceName: order.serviceName,
elderName: order.fullElderName != '' ? order.fullElderName : order.elderName,
elderNameMasked: order.elderNameMasked != '' ? order.elderNameMasked : order.elderName,
status: order.status,
statusText: order.statusText,
appointmentStartTime: order.appointmentStartTime,
appointmentTime: order.appointmentTime,
actualStartTime: order.actualStartTime,
actualEndTime: order.actualEndTime != '' ? order.actualEndTime : order.finishTime,
staffIncome: order.staffIncome,
settlementStatus: order.settlementStatus != '' ? order.settlementStatus : '待结算',
acceptanceStatus: order.status,
exceptionDesc: order.exceptionDesc,
ratingText: isAccepted ? '已验收' : (order.status == 'pending_acceptance' ? '待验收' : '待补充'),
hasServiceRecord: order.serviceRecord != null
} as DeliveryRecordType
}
export async function loginDelivery(payload: DeliveryLoginPayloadType): Promise<DeliveryLoginResultType> {
return await loginDeliveryApi(payload)
}
export async function getDeliveryProfile(): Promise<DeliveryInfoType | null> {
const authResult = await requireDeliveryAuth({ redirectOnFail: false, toastOnFail: false })
if (!authResult.ok) {
}
return authResult.deliveryInfo
}
export async function getDeliveryByUserId(userId: string): Promise<DeliveryInfoType | null> {
return await getDeliveryProfileByUserId(userId)
}
export async function getCurrentDeliverer(): Promise<DeliveryInfoType | null> {
return await getDeliveryProfile()
}
export async function checkDeliveryAccountByUserId(userId: string): Promise<DeliveryInfoType | null> {
return await getDeliveryProfileByUserId(userId)
}
export async function checkDeliveryAuth(): Promise<boolean> {
const result = await requireDeliveryAuth({ redirectOnFail: false, toastOnFail: false })
return result.ok
}
async function getCurrentStaffId(): Promise<string> {
console.log('[DELIVERY_SERVICE_TRACE][getCurrentStaffId][START]', {
hasDeliveryInfo: getDeliveryInfo() != null
})
// delivery RPC (dashboard / order_list) 需要使用 ml_delivery_staff.id
const deliveryInfo = getDeliveryInfo()
if (deliveryInfo != null && deliveryInfo.id != null && String(deliveryInfo.id) != '') {
const resolvedId = String(deliveryInfo.id)
console.log('[DELIVERY_SERVICE_TRACE][getCurrentStaffId][RESOLVED]', {
source: 'deliveryStaffId',
resolvedId: resolvedId
})
return resolvedId
}
console.error('[DELIVERY_SERVICE_TRACE][getCurrentStaffId][ERROR]', {
errorMessage: 'deliveryStaffId not available',
errorStack: '',
params: {
hasDeliveryInfo: deliveryInfo != null
}
})
return ''
}
function createEmptyLocation(): DeliveryLocationType {
return {
latitude: 0,
longitude: 0,
address: '',
time: new Date().toISOString().replace('T', ' ').substring(0, 19)
}
}
export async function getDeliveryDashboard(): Promise<DeliveryDashboardType> {
console.log('[DELIVERY_SERVICE_TRACE][getDeliveryDashboard][START]', {})
const staffId = await getCurrentStaffId()
if (staffId != '') {
try {
const result = await getDeliveryDashboardByStaffId(staffId)
console.log('[DELIVERY_SERVICE_TRACE][getDeliveryDashboard][SUCCESS]', {
result: result
})
return result
} catch (e) {
console.error('[DELIVERY_SERVICE_TRACE][getDeliveryDashboard][ERROR]', {
errorMessage: e != null ? String(e.message ?? e) : '',
errorStack: e != null ? String(e.stack ?? '') : '',
params: {
staffId: staffId
}
})
return emptyDashboard()
}
}
console.error('[DELIVERY_SERVICE_TRACE][getDeliveryDashboard][ERROR]', {
errorMessage: 'staffId is empty',
errorStack: '',
params: {}
})
return emptyDashboard()
}
export async function getDeliveryOrders(params: DeliveryOrderQueryType): Promise<Array<DeliveryOrderType>> {
console.log('[DELIVERY_SERVICE_TRACE][getDeliveryOrders][START]', {
params: params
})
const staffId = await getCurrentStaffId()
if (staffId != '') {
try {
const result = await getDeliveryOrdersByStaffId(staffId, params)
console.log('[DELIVERY_SERVICE_TRACE][getDeliveryOrders][SUCCESS]', {
count: result.length,
result: result
})
return result
} catch (e) {
console.error('[DELIVERY_SERVICE_TRACE][getDeliveryOrders][ERROR]', {
errorMessage: e != null ? String(e.message ?? e) : '',
errorStack: e != null ? String(e.stack ?? '') : '',
params: {
staffId: staffId,
query: params
}
})
return [] as Array<DeliveryOrderType>
}
}
console.error('[DELIVERY_SERVICE_TRACE][getDeliveryOrders][ERROR]', {
errorMessage: 'staffId is empty',
errorStack: '',
params: {
query: params
}
})
return [] as Array<DeliveryOrderType>
}
export async function getDeliveryOrderDetail(id: string): Promise<DeliveryOrderType | null> {
console.log('[DELIVERY_SERVICE_TRACE][getDeliveryOrderDetail][START]', {
id: id
})
try {
const result = await getDeliveryOrderDetailById(id)
console.log('[DELIVERY_SERVICE_TRACE][getDeliveryOrderDetail][SUCCESS]', {
id: id,
hasResult: result != null,
result: result
})
return result
} catch (e) {
console.error('[DELIVERY_SERVICE_TRACE][getDeliveryOrderDetail][ERROR]', {
errorMessage: e != null ? String(e.message ?? e) : '',
errorStack: e != null ? String(e.stack ?? '') : '',
params: {
id: id
}
})
return null
}
}
export async function acceptDeliveryOrder(id: string): Promise<DeliveryOrderType | null> {
return await acceptDeliveryOrderById(id)
}
export async function rejectDeliveryOrder(id: string, reason: string): Promise<DeliveryOrderType | null> {
return rejectCareOrder(id, reason)
}
export async function startDepart(id: string, location: DeliveryLocationType): Promise<DeliveryOrderType | null> {
return await startDepartById(id, location)
}
export async function arriveOrder(id: string, location: DeliveryLocationType): Promise<DeliveryOrderType | null> {
return await arriveOrderById(id, location)
}
export async function checkinOrder(id: string, payload: DeliveryCheckinPayloadType): Promise<DeliveryOrderType | null> {
return await checkinOrderById(id, payload)
}
export async function startService(id: string): Promise<DeliveryOrderType | null> {
return await startServiceById(id)
}
export async function saveServiceProgress(id: string, payload: DeliveryProgressPayloadType): Promise<DeliveryOrderType | null> {
return await saveServiceProgressById(id, payload)
}
export async function uploadEvidence(id: string, phase: string, files: Array<string>): Promise<Array<any>> {
return files.map((item, index) => ({
id: id + '-evidence-' + String(index + 1),
orderId: id,
phase,
fileType: 'image',
name: 'mock-image-' + String(index + 1),
url: '',
localPath: item,
status: 'success',
progress: 100,
retryable: false,
createdAt: new Date().toISOString().replace('T', ' ').substring(0, 19)
})) as Array<any>
}
export async function retryUploadEvidence(id: string, evidenceId: string): Promise<any | null> {
return {
id: evidenceId,
orderId: id,
phase: 'service',
fileType: 'image',
name: 'mock-retry',
url: '',
localPath: '',
status: 'success',
progress: 100,
retryable: false,
createdAt: new Date().toISOString().replace('T', ' ').substring(0, 19)
} as any
}
export async function submitException(id: string, payload: DeliveryExceptionPayloadType): Promise<DeliveryOrderType | null> {
return submitCareAbnormalReport(id, payload)
}
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>> {
return getCareRecords()
}
export async function getDeliveryMessages(): Promise<Array<DeliveryMessageType>> {
return [
{
id: 'delivery-msg-01',
title: '今日待服务提醒',
content: '请优先处理上午 9:00 的上门助浴订单。',
type: 'workbench',
createdAt: new Date().toISOString().replace('T', ' ').substring(0, 19),
read: false,
orderId: 'mock-care-001'
} as DeliveryMessageType
]
}
export async function updateDeliveryOnlineStatus(status: string): Promise<DeliveryInfoType | null> {
if (status == 'online' || status == 'resting' || status == 'busy') {
return updateDeliveryCareOnlineStatus(status)
}
return getDeliveryCareProfile()
}
export async function getDeliveryDashboardStats(): Promise<DeliveryDashboardType> {
return await getDeliveryDashboard()
}
export async function getPendingServiceOrders(): Promise<Array<DeliveryOrderType>> {
const orders = await getDeliveryOrders({ tab: 'pending', keyword: '' } as DeliveryOrderQueryType)
let pendingOrders = filterOrdersWithCoreInfo(orders)
if (pendingOrders.length == 0) {
pendingOrders = orders
}
let todayOrders = await getDeliveryOrders({ tab: 'today', keyword: '' } as DeliveryOrderQueryType)
let validTodayOrders = filterOrdersWithCoreInfo(todayOrders)
if (validTodayOrders.length == 0) {
validTodayOrders = todayOrders
}
if (shouldFallbackOrders(todayOrders)) {
const fallbackTodayOrders = await getDirectOrdersByTab('today')
if (fallbackTodayOrders.length > 0) {
validTodayOrders = fallbackTodayOrders
}
}
const activeKeys = buildActiveOrderKeys(validTodayOrders)
const filteredPendingOrders = filterPendingOrdersAgainstActiveKeys(pendingOrders, activeKeys)
if (filteredPendingOrders.length > 0 || pendingOrders.length == 0) {
return filteredPendingOrders
}
if (shouldFallbackOrders(orders)) {
console.warn('[deliveryService] pending orders missing core info, fallback to direct query')
const fallbackOrders = await getDirectOrdersByTab('pending')
if (fallbackOrders.length > 0) {
return filterPendingOrdersAgainstActiveKeys(fallbackOrders, activeKeys)
}
}
return filteredPendingOrders
}
export async function getTodayServiceOrders(): Promise<Array<DeliveryOrderType>> {
const orders = await getDeliveryOrders({ tab: 'today', keyword: '' } as DeliveryOrderQueryType)
const validOrders = filterOrdersWithCoreInfo(orders)
if (validOrders.length > 0) {
return validOrders
}
if (shouldFallbackOrders(orders)) {
console.warn('[deliveryService] today orders missing core info, fallback to direct query')
const fallbackOrders = await getDirectOrdersByTab('today')
if (fallbackOrders.length > 0) {
return fallbackOrders
}
}
return orders
}
export async function getHistoryServiceOrders(): Promise<Array<DeliveryOrderType>> {
const orders = await getDeliveryOrders({ tab: 'history', keyword: '' } as DeliveryOrderQueryType)
const validOrders = filterOrdersWithCoreInfo(orders)
if (validOrders.length > 0) {
return validOrders
}
if (shouldFallbackOrders(orders)) {
console.warn('[deliveryService] history orders missing core info, fallback to direct query')
const fallbackOrders = await getDirectOrdersByTab('history')
if (fallbackOrders.length > 0) {
return fallbackOrders
}
}
return orders
}
export async function getServiceOrderDetail(orderId: string): Promise<DeliveryOrderType | null> {
const order = await getDeliveryOrderDetailById(orderId)
if (!hasOrderCoreInfo(order)) {
console.warn('[deliveryService] order detail missing core info, fallback to direct query:', orderId)
const fallbackOrder = await getDirectServiceOrderDetail(orderId)
if (hasOrderCoreInfo(fallbackOrder)) {
return fallbackOrder
}
}
return order
}
export async function acceptServiceOrder(orderId: string): Promise<DeliveryActionResultType> {
const akUserId = await resolveCurrentAkUserIdForDelivery()
console.warn('[HOMECARE ACCEPT] acceptServiceOrder: orderId=', orderId, ' akUserId=', akUserId)
if (akUserId == '') {
return {
ok: false,
message: '未获取到业务用户 ID请重新登录',
order: null
} as DeliveryActionResultType
}
const result = await acceptHomecareAssignmentV2(orderId, akUserId)
console.warn('[HOMECARE ACCEPT] rpc_homecare_accept_assignment_v2 result:', result)
if (result == null) {
return {
ok: false,
message: '接单失败,请稍后重试',
order: null
} as DeliveryActionResultType
}
const ok = result.getBoolean('ok') ?? false
const message = result.getString('message') ?? ''
return {
ok,
message,
order: null
} as DeliveryActionResultType
}
/**
* 鑾峰彇褰撳墠涓氬姟鐢ㄦ埛 ID锛堜粠 akUserMapping 鎴栫紦瀛橈級
*/
async function resolveCurrentAkUserIdForDelivery(): Promise<string> {
try {
const id = await readCurrentAkUserId()
if (id != null && id != '') {
console.warn('[deliveryService] resolveCurrentAkUserIdForDelivery: from akUserMapping =', id)
return id
}
} catch (e) {
console.warn('[deliveryService] readCurrentAkUserId failed:', e)
}
try {
const cached = uni.getStorageSync('ak_user_id')
if (cached != null && String(cached) != '') {
console.warn('[deliveryService] resolveCurrentAkUserIdForDelivery: from storage ak_user_id =', String(cached))
return String(cached)
}
} catch (e) {}
try {
const currentAkUser = uni.getStorageSync('current_ak_user')
if (currentAkUser != null && String(currentAkUser) != '') {
const obj = JSON.parse(String(currentAkUser)) as any
if (obj != null && obj.id != null && String(obj.id) != '') {
console.warn('[deliveryService] resolveCurrentAkUserIdForDelivery: from current_ak_user.id =', String(obj.id))
return String(obj.id)
}
}
} catch (e) {}
return ''
}
export async function rejectServiceOrder(orderId: string, reason: string): Promise<DeliveryOrderType | null> {
return rejectCareOrder(orderId, reason)
}
export async function markDeparted(orderId: string): Promise<DeliveryOrderType | null> {
return await startDepartById(orderId, createEmptyLocation())
}
export async function markArrived(orderId: string): Promise<DeliveryOrderType | 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 checkinOrderById(orderId, {
location,
note,
photos: [] as Array<string>,
checkinMode: 'gps'
})
}
export async function startServiceOrder(orderId: string): Promise<DeliveryOrderType | null> {
return await startServiceById(orderId)
}
export async function submitServiceRecord(orderId: string, record: DeliveryServiceRecordType): Promise<DeliveryOrderType | null> {
return await saveRealServiceRecord(orderId, record)
}
export async function completeServiceOrder(orderId: string): Promise<DeliveryOrderType | null> {
return await finishService(orderId)
}
export async function submitAbnormalReport(orderId: string, report: DeliveryExceptionPayloadType): Promise<DeliveryOrderType | null> {
return submitCareAbnormalReport(orderId, report)
}
export async function updateOrderStatus(orderId: string, nextStatus: DeliveryOrderStatus, extraRemark: string = ''): Promise<DeliveryOrderType | null> {
return updateCareOrderStatus(orderId, nextStatus, extraRemark)
}
export async function getAbnormalReport(orderId: string): Promise<DeliveryAbnormalReportType | null> {
const order = getCareOrderDetail(orderId)
return order != null ? order.abnormalReport : null
}