1536 lines
65 KiB
PL/PgSQL
1536 lines
65 KiB
PL/PgSQL
BEGIN;
|
||
|
||
-- =====================================================================================
|
||
-- Delivery homecare action RPCs
|
||
-- Purpose: <20>?delivery 端补<E7ABAF>?rpc_delivery_* 契约,优先兼<E58588>?ec/hc 新链,回退 hss 旧链<E697A7>?
|
||
-- =====================================================================================
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_current_user_id()
|
||
RETURNS UUID
|
||
LANGUAGE sql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
SELECT u.id
|
||
FROM public.ak_users u
|
||
WHERE u.auth_id = auth.uid()
|
||
LIMIT 1
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_current_staff_id()
|
||
RETURNS UUID
|
||
LANGUAGE sql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
SELECT s.id
|
||
FROM public.ml_delivery_staff s
|
||
WHERE s.uid = public.delivery_current_user_id()
|
||
AND s.deleted_at IS NULL
|
||
ORDER BY COALESCE(s.updated_at, s.created_at) DESC, s.created_at DESC
|
||
LIMIT 1
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_table_exists(p_table_name TEXT)
|
||
RETURNS BOOLEAN
|
||
LANGUAGE sql
|
||
STABLE
|
||
SET search_path = public
|
||
AS $$
|
||
SELECT to_regclass('public.' || p_table_name) IS NOT NULL
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_front_status(p_normalized_status TEXT, p_raw JSONB)
|
||
RETURNS TEXT
|
||
LANGUAGE plpgsql
|
||
IMMUTABLE
|
||
AS $$
|
||
BEGIN
|
||
IF p_normalized_status = 'assigned' THEN
|
||
RETURN 'pending_assignment';
|
||
END IF;
|
||
IF p_normalized_status = 'accepted' THEN
|
||
IF COALESCE(p_raw ->> 'departed_at', '') <> '' THEN
|
||
RETURN 'departed';
|
||
END IF;
|
||
RETURN 'accepted';
|
||
END IF;
|
||
IF p_normalized_status = 'departed' THEN
|
||
RETURN 'departed';
|
||
END IF;
|
||
IF p_normalized_status = 'arrived' THEN
|
||
RETURN 'arrived';
|
||
END IF;
|
||
IF p_normalized_status = 'in_service' THEN
|
||
RETURN 'in_service';
|
||
END IF;
|
||
IF p_normalized_status = 'pending_acceptance' THEN
|
||
RETURN 'pending_acceptance';
|
||
END IF;
|
||
IF p_normalized_status IN ('reviewed', 'accepted_by_user', 'settled', 'completed') THEN
|
||
RETURN 'completed';
|
||
END IF;
|
||
IF p_normalized_status = 'rejected' THEN
|
||
RETURN 'rejected';
|
||
END IF;
|
||
IF p_normalized_status = 'cancelled' THEN
|
||
RETURN 'cancelled';
|
||
END IF;
|
||
IF p_normalized_status = 'exception' THEN
|
||
RETURN 'abnormal';
|
||
END IF;
|
||
RETURN 'pending_assignment';
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_status_text(p_front_status TEXT)
|
||
RETURNS TEXT
|
||
LANGUAGE plpgsql
|
||
IMMUTABLE
|
||
AS $$
|
||
BEGIN
|
||
CASE p_front_status
|
||
WHEN 'pending_assignment' THEN RETURN '待接<EFBFBD>?;
|
||
WHEN 'pending_accept' THEN RETURN '待接<EFBFBD>?;
|
||
WHEN 'accepted' THEN RETURN '待出<EFBFBD>?;
|
||
WHEN 'waiting_departure' THEN RETURN '待出<EFBFBD>?;
|
||
WHEN 'departed' THEN RETURN '已出<EFBFBD>?;
|
||
WHEN 'on_the_way' THEN RETURN '途中';
|
||
WHEN 'arrived' THEN RETURN '待签<EFBFBD>?;
|
||
WHEN 'checked_in' THEN RETURN '已签<EFBFBD>?;
|
||
WHEN 'in_service' THEN RETURN '服务<EFBFBD>?;
|
||
WHEN 'serving' THEN RETURN '服务<EFBFBD>?;
|
||
WHEN 'pending_confirm' THEN RETURN '待确<EFBFBD>?;
|
||
WHEN 'pending_submit' THEN RETURN '待提<EFBFBD>?;
|
||
WHEN 'pending_acceptance' THEN RETURN '待验<EFBFBD>?;
|
||
WHEN 'completed' THEN RETURN '已完<EFBFBD>?;
|
||
WHEN 'rejected' THEN RETURN '已拒<EFBFBD>?;
|
||
WHEN 'abnormal' THEN RETURN '异常处理<EFBFBD>?;
|
||
WHEN 'cancelled' THEN RETURN '已取<EFBFBD>?;
|
||
WHEN 'settled' THEN RETURN '已结<EFBFBD>?;
|
||
WHEN 'archived' THEN RETURN '已归<EFBFBD>?;
|
||
ELSE RETURN '待接<EFBFBD>?;
|
||
END CASE;
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_status_tone(p_front_status TEXT)
|
||
RETURNS TEXT
|
||
LANGUAGE plpgsql
|
||
IMMUTABLE
|
||
AS $$
|
||
BEGIN
|
||
IF p_front_status IN ('pending_assignment', 'pending_accept', 'pending_acceptance', 'pending_confirm', 'pending_submit') THEN
|
||
RETURN 'warning';
|
||
END IF;
|
||
IF p_front_status IN ('accepted', 'waiting_departure', 'departed', 'on_the_way', 'arrived', 'checked_in', 'in_service', 'serving') THEN
|
||
RETURN 'primary';
|
||
END IF;
|
||
IF p_front_status IN ('completed', 'settled', 'archived') THEN
|
||
RETURN 'success';
|
||
END IF;
|
||
IF p_front_status IN ('rejected', 'cancelled', 'abnormal', 'exception_pending', 'terminated') THEN
|
||
RETURN 'danger';
|
||
END IF;
|
||
RETURN 'warning';
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_default_service_items(p_order_id TEXT, p_service_name TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE sql
|
||
IMMUTABLE
|
||
AS $$
|
||
SELECT jsonb_build_array(
|
||
jsonb_build_object('id', p_order_id || '-item-1', 'name', '上门签到确认', 'required', true, 'completed', false, 'incompleteReason', '', 'remark', '', 'updatedAt', ''),
|
||
jsonb_build_object('id', p_order_id || '-item-2', 'name', CASE WHEN position('护理' IN COALESCE(p_service_name, '')) > 0 THEN '基础护理执行' WHEN position('随访' IN COALESCE(p_service_name, '')) > 0 THEN '慢病指标采集' ELSE '服务项目执行' END, 'required', true, 'completed', false, 'incompleteReason', '', 'remark', '', 'updatedAt', ''),
|
||
jsonb_build_object('id', p_order_id || '-item-3', 'name', '服务过程记录', 'required', true, 'completed', false, 'incompleteReason', '', 'remark', '', 'updatedAt', ''),
|
||
jsonb_build_object('id', p_order_id || '-item-4', 'name', '家属沟通反<EFBFBD>?, 'required', true, 'completed', false, 'incompleteReason', '', 'remark', '', 'updatedAt', '')
|
||
)
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_build_timeline(p_logs JSONB)
|
||
RETURNS JSONB
|
||
LANGUAGE sql
|
||
IMMUTABLE
|
||
AS $$
|
||
SELECT COALESCE(
|
||
(
|
||
SELECT jsonb_agg(
|
||
jsonb_build_object(
|
||
'id', COALESCE(item ->> 'id', md5(COALESCE(item::TEXT, ''))),
|
||
'title', public.delivery_status_text(
|
||
public.delivery_front_status(
|
||
CASE
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_ACCEPTED') THEN 'accepted'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_CHECKED_IN') THEN 'arrived'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_IN_SERVICE') THEN 'in_service'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ACCEPTANCE_PENDING') THEN 'pending_acceptance'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_EXCEPTION') THEN 'exception'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_REJECTED') THEN 'rejected'
|
||
ELSE lower(COALESCE(item ->> 'to_status', item ->> 'status', 'assigned'))
|
||
END,
|
||
item
|
||
)
|
||
),
|
||
'time', COALESCE(item ->> 'created_at', ''),
|
||
'description', COALESCE(item ->> 'remark', '')
|
||
)
|
||
ORDER BY COALESCE(item ->> 'created_at', '') DESC
|
||
)
|
||
FROM jsonb_array_elements(COALESCE(p_logs, '[]'::jsonb)) item
|
||
),
|
||
'[]'::jsonb
|
||
)
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_build_status_logs(p_logs JSONB, p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE sql
|
||
IMMUTABLE
|
||
AS $$
|
||
SELECT COALESCE(
|
||
(
|
||
SELECT jsonb_agg(
|
||
jsonb_build_object(
|
||
'id', COALESCE(item ->> 'id', md5(COALESCE(item::TEXT, ''))),
|
||
'orderId', p_order_id,
|
||
'fromStatus', public.delivery_front_status(lower(COALESCE(item ->> 'from_status', 'assigned')), item),
|
||
'toStatus', public.delivery_front_status(
|
||
CASE
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_ACCEPTED') THEN 'accepted'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_CHECKED_IN') THEN 'arrived'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_IN_SERVICE') THEN 'in_service'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ACCEPTANCE_PENDING') THEN 'pending_acceptance'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_EXCEPTION') THEN 'exception'
|
||
WHEN COALESCE(item ->> 'to_status', item ->> 'status', '') IN ('ORDER_REJECTED') THEN 'rejected'
|
||
ELSE lower(COALESCE(item ->> 'to_status', item ->> 'status', 'assigned'))
|
||
END,
|
||
item
|
||
),
|
||
'operatorRole', COALESCE(item ->> 'operator_role', item ->> 'actor_role', ''),
|
||
'operatorId', COALESCE(item ->> 'operator_id', item ->> 'actor_id', ''),
|
||
'remark', COALESCE(item ->> 'remark', ''),
|
||
'createdAt', COALESCE(item ->> 'created_at', '')
|
||
)
|
||
ORDER BY COALESCE(item ->> 'created_at', '') DESC
|
||
)
|
||
FROM jsonb_array_elements(COALESCE(p_logs, '[]'::jsonb)) item
|
||
),
|
||
'[]'::jsonb
|
||
)
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_build_evidence(p_evidence JSONB, p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE sql
|
||
IMMUTABLE
|
||
AS $$
|
||
SELECT COALESCE(
|
||
(
|
||
SELECT jsonb_agg(
|
||
jsonb_build_object(
|
||
'id', COALESCE(item ->> 'id', md5(COALESCE(item::TEXT, ''))),
|
||
'orderId', p_order_id,
|
||
'phase', COALESCE(item ->> 'phase', 'service'),
|
||
'fileType', COALESCE(NULLIF(item ->> 'file_type', ''), 'image'),
|
||
'name', COALESCE(NULLIF(item ->> 'storage_path', ''), item ->> 'file_url', ''),
|
||
'url', COALESCE(item ->> 'file_url', ''),
|
||
'localPath', '',
|
||
'status', 'success',
|
||
'progress', 100,
|
||
'retryable', false,
|
||
'createdAt', COALESCE(item ->> 'created_at', '')
|
||
)
|
||
ORDER BY COALESCE(item ->> 'created_at', '') DESC
|
||
)
|
||
FROM jsonb_array_elements(COALESCE(p_evidence, '[]'::jsonb)) item
|
||
),
|
||
'[]'::jsonb
|
||
)
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_build_abnormal(p_exception JSONB, p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE sql
|
||
IMMUTABLE
|
||
AS $$
|
||
SELECT CASE
|
||
WHEN p_exception IS NULL THEN NULL
|
||
ELSE jsonb_build_object(
|
||
'id', COALESCE(p_exception ->> 'id', ''),
|
||
'orderId', p_order_id,
|
||
'type', COALESCE(p_exception ->> 'exception_type', 'other'),
|
||
'description', COALESCE(p_exception ->> 'description', p_exception ->> 'remark', ''),
|
||
'occurredAt', COALESCE(p_exception ->> 'occurred_at', ''),
|
||
'locationText', COALESCE(p_exception ->> 'location_text', ''),
|
||
'images', COALESCE(p_exception -> 'images_json', '[]'::jsonb),
|
||
'needPlatformIntervention', COALESCE((p_exception ->> 'need_platform_intervention')::BOOLEAN, false),
|
||
'requestCancelOrder', COALESCE((p_exception ->> 'request_cancel_order')::BOOLEAN, false),
|
||
'requestReschedule', COALESCE((p_exception ->> 'request_reschedule')::BOOLEAN, false),
|
||
'createdAt', COALESCE(p_exception ->> 'created_at', '')
|
||
)
|
||
END
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_build_order_json(
|
||
p_raw JSONB,
|
||
p_logs JSONB DEFAULT '[]'::jsonb,
|
||
p_records JSONB DEFAULT '[]'::jsonb,
|
||
p_evidence JSONB DEFAULT '[]'::jsonb,
|
||
p_exception JSONB DEFAULT NULL,
|
||
p_source TEXT DEFAULT 'legacy'
|
||
)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
IMMUTABLE
|
||
AS $$
|
||
DECLARE
|
||
v_service JSONB := COALESCE(p_raw -> 'service_snapshot_json', jsonb_build_object('category', COALESCE(p_raw ->> 'service_category', ''), 'price', COALESCE((p_raw ->> 'service_price')::NUMERIC, 0)));
|
||
v_address JSONB := COALESCE(p_raw -> 'address_snapshot_json', '{}'::jsonb);
|
||
v_raw_status TEXT := COALESCE(p_raw ->> 'status', 'assigned');
|
||
v_normalized_status TEXT;
|
||
v_front_status TEXT;
|
||
v_checkin_record JSONB;
|
||
v_service_record JSONB;
|
||
v_service_items JSONB;
|
||
v_record_json JSONB;
|
||
v_timeline JSONB;
|
||
v_status_logs JSONB;
|
||
v_evidence_list JSONB;
|
||
BEGIN
|
||
IF p_source = 'care' THEN
|
||
IF COALESCE(p_raw ->> 'accepted_by_family_at', '') <> '' THEN
|
||
v_normalized_status := 'completed';
|
||
ELSIF COALESCE(p_raw ->> 'acceptance_pending_at', '') <> '' THEN
|
||
v_normalized_status := 'pending_acceptance';
|
||
ELSIF COALESCE(p_raw ->> 'service_started_at', '') <> '' THEN
|
||
v_normalized_status := 'in_service';
|
||
ELSIF COALESCE(p_raw ->> 'checked_in_at', '') <> '' THEN
|
||
v_normalized_status := 'arrived';
|
||
ELSIF COALESCE(p_raw ->> 'departed_at', '') <> '' THEN
|
||
v_normalized_status := 'departed';
|
||
ELSIF COALESCE(p_raw ->> 'accepted_at', '') <> '' THEN
|
||
v_normalized_status := 'accepted';
|
||
ELSE
|
||
v_normalized_status := CASE v_raw_status
|
||
WHEN 'ORDER_ACCEPTED' THEN 'accepted'
|
||
WHEN 'ORDER_CHECKED_IN' THEN 'arrived'
|
||
WHEN 'ORDER_IN_SERVICE' THEN 'in_service'
|
||
WHEN 'ACCEPTANCE_PENDING' THEN 'pending_acceptance'
|
||
WHEN 'ORDER_EXCEPTION' THEN 'exception'
|
||
WHEN 'ORDER_REJECTED' THEN 'rejected'
|
||
WHEN 'ORDER_CANCELLED' THEN 'cancelled'
|
||
WHEN 'ORDER_COMPLETED' THEN 'completed'
|
||
ELSE 'assigned'
|
||
END;
|
||
END IF;
|
||
ELSE
|
||
v_normalized_status := lower(v_raw_status);
|
||
END IF;
|
||
|
||
v_front_status := public.delivery_front_status(v_normalized_status, p_raw);
|
||
v_timeline := public.delivery_build_timeline(p_logs);
|
||
v_status_logs := public.delivery_build_status_logs(p_logs, COALESCE(p_raw ->> 'id', ''));
|
||
v_evidence_list := public.delivery_build_evidence(p_evidence, COALESCE(p_raw ->> 'id', ''));
|
||
|
||
SELECT item INTO v_checkin_record
|
||
FROM jsonb_array_elements(COALESCE(p_records, '[]'::jsonb)) item
|
||
WHERE COALESCE(item ->> 'record_type', item ->> 'care_record_type', '') = 'checkin'
|
||
ORDER BY COALESCE(item ->> 'created_at', '') DESC
|
||
LIMIT 1;
|
||
|
||
SELECT item INTO v_service_record
|
||
FROM jsonb_array_elements(COALESCE(p_records, '[]'::jsonb)) item
|
||
WHERE COALESCE(item ->> 'record_type', item ->> 'care_record_type', '') <> 'checkin'
|
||
AND COALESCE(item ->> 'record_type', item ->> 'care_record_type', '') <> 'review'
|
||
ORDER BY COALESCE(item ->> 'created_at', '') DESC
|
||
LIMIT 1;
|
||
|
||
IF v_service_record IS NULL THEN
|
||
v_service_record := v_checkin_record;
|
||
END IF;
|
||
|
||
v_service_items := COALESCE(v_service_record -> 'service_items_json', public.delivery_default_service_items(COALESCE(p_raw ->> 'id', ''), COALESCE(p_raw ->> 'service_name', '')));
|
||
|
||
IF v_service_record IS NULL THEN
|
||
v_record_json := NULL;
|
||
ELSE
|
||
v_record_json := jsonb_build_object(
|
||
'id', COALESCE(v_service_record ->> 'id', ''),
|
||
'orderId', COALESCE(p_raw ->> 'id', ''),
|
||
'startTime', COALESCE(v_service_record ->> 'started_at', v_service_record ->> 'service_started_at', ''),
|
||
'endTime', COALESCE(v_service_record ->> 'finished_at', v_service_record ->> 'service_finished_at', ''),
|
||
'actualDurationMinutes', COALESCE((v_service_record ->> 'duration_minutes')::INTEGER, (v_service_record ->> 'actual_duration_minutes')::INTEGER, 0),
|
||
'serviceItems', COALESCE(v_service_items, '[]'::jsonb),
|
||
'serviceContent', COALESCE(
|
||
(
|
||
SELECT jsonb_agg(elem ->> 'name')
|
||
FROM jsonb_array_elements(COALESCE(v_service_items, '[]'::jsonb)) elem
|
||
WHERE COALESCE((elem ->> 'completed')::BOOLEAN, false)
|
||
),
|
||
'[]'::jsonb
|
||
),
|
||
'processNote', COALESCE(v_service_record ->> 'summary', v_service_record ->> 'content', ''),
|
||
'elderStatus', '',
|
||
'healthMetrics', jsonb_build_object('bloodPressure', '', 'heartRate', '', 'bloodSugar', '', 'bloodOxygen', ''),
|
||
'materialsUsed', '',
|
||
'abnormalNote', '',
|
||
'photos', COALESCE(
|
||
(
|
||
SELECT jsonb_agg(item ->> 'file_url')
|
||
FROM jsonb_array_elements(COALESCE(p_evidence, '[]'::jsonb)) item
|
||
WHERE COALESCE(item ->> 'phase', '') = 'service'
|
||
),
|
||
'[]'::jsonb
|
||
),
|
||
'staffRemark', COALESCE(v_service_record ->> 'remark', ''),
|
||
'familyConfirmation', jsonb_build_object('method', 'none', 'code', '', 'signatureName', '', 'signatureUrl', '', 'confirmedAt', ''),
|
||
'createdAt', COALESCE(v_service_record ->> 'created_at', ''),
|
||
'updatedAt', COALESCE(v_service_record ->> 'updated_at', '')
|
||
);
|
||
END IF;
|
||
|
||
RETURN jsonb_build_object(
|
||
'id', COALESCE(p_raw ->> 'id', ''),
|
||
'orderNo', COALESCE(NULLIF(p_raw ->> 'task_no', ''), p_raw ->> 'order_no', ''),
|
||
'serviceType', COALESCE(NULLIF(v_service ->> 'category', ''), '居家服务'),
|
||
'serviceName', COALESCE(p_raw ->> 'service_name', ''),
|
||
'serviceCategory', COALESCE(v_service ->> 'category', ''),
|
||
'serviceItems', COALESCE(v_service_items, '[]'::jsonb),
|
||
'elderId', COALESCE(p_raw ->> 'elder_id', p_raw ->> 'user_id', ''),
|
||
'elderName', COALESCE(NULLIF(p_raw ->> 'recipient_name', ''), p_raw ->> 'elder_name', ''),
|
||
'elderNameMasked', COALESCE(NULLIF(p_raw ->> 'recipient_name', ''), p_raw ->> 'elder_name', ''),
|
||
'elderGender', '',
|
||
'elderAge', 0,
|
||
'elderPhone', COALESCE(NULLIF(p_raw ->> 'recipient_phone', ''), p_raw ->> 'elder_phone', ''),
|
||
'elderPhoneMasked', COALESCE(NULLIF(p_raw ->> 'recipient_phone', ''), p_raw ->> 'elder_phone', ''),
|
||
'fullElderName', COALESCE(NULLIF(p_raw ->> 'recipient_name', ''), p_raw ->> 'elder_name', ''),
|
||
'fullPhone', COALESCE(NULLIF(p_raw ->> 'recipient_phone', ''), p_raw ->> 'elder_phone', ''),
|
||
'contactRelation', '家属',
|
||
'addressSummary', COALESCE(NULLIF(v_address ->> 'fullAddress', ''), NULLIF(v_address ->> 'full_address', ''), NULLIF(v_address ->> 'address', ''), ''),
|
||
'address', COALESCE(NULLIF(v_address ->> 'fullAddress', ''), NULLIF(v_address ->> 'full_address', ''), NULLIF(v_address ->> 'address', ''), ''),
|
||
'addressDetail', COALESCE(NULLIF(v_address ->> 'detailAddress', ''), NULLIF(v_address ->> 'detail_address', ''), ''),
|
||
'fullAddress', COALESCE(NULLIF(v_address ->> 'fullAddress', ''), NULLIF(v_address ->> 'full_address', ''), NULLIF(v_address ->> 'address', ''), ''),
|
||
'latitude', COALESCE((v_address ->> 'latitude')::NUMERIC, 0),
|
||
'longitude', COALESCE((v_address ->> 'longitude')::NUMERIC, 0),
|
||
'appointmentTime', COALESCE(NULLIF(p_raw ->> 'appointment_time', ''), p_raw ->> 'scheduled_at', ''),
|
||
'appointmentStartTime', COALESCE(NULLIF(p_raw ->> 'appointment_time', ''), p_raw ->> 'scheduled_at', ''),
|
||
'appointmentEndTime', COALESCE(NULLIF(p_raw ->> 'appointment_time', ''), p_raw ->> 'scheduled_at', ''),
|
||
'duration', COALESCE((p_raw ->> 'duration_minutes')::INTEGER, 90),
|
||
'estimatedDuration', COALESCE((p_raw ->> 'duration_minutes')::INTEGER, 90),
|
||
'price', COALESCE((v_service ->> 'price')::NUMERIC, 0),
|
||
'staffIncome', COALESCE((v_service ->> 'price')::NUMERIC, 0),
|
||
'distance', '',
|
||
'actualStartTime', COALESCE(p_raw ->> 'service_started_at', ''),
|
||
'actualEndTime', COALESCE(NULLIF(p_raw ->> 'completed_at', ''), p_raw ->> 'service_completed_at', ''),
|
||
'status', v_front_status,
|
||
'statusText', public.delivery_status_text(v_front_status),
|
||
'statusTone', public.delivery_status_tone(v_front_status),
|
||
'riskTags', '[]'::jsonb,
|
||
'healthTags', '[]'::jsonb,
|
||
'careLevel', COALESCE(v_service ->> 'category', ''),
|
||
'needFamilyPresent', false,
|
||
'needMaterials', false,
|
||
'remark', COALESCE(p_raw ->> 'remark', ''),
|
||
'merchantId', COALESCE(p_raw ->> 'merchant_id', ''),
|
||
'merchantName', COALESCE(p_raw ->> 'merchant_name', ''),
|
||
'deliveryStaffId', COALESCE(NULLIF(p_raw ->> 'current_staff_id', ''), p_raw ->> 'assigned_to', ''),
|
||
'deliveryStaffName', COALESCE(p_raw ->> 'delivery_staff_name', ''),
|
||
'acceptTime', COALESCE(p_raw ->> 'accepted_at', ''),
|
||
'departTime', COALESCE(p_raw ->> 'departed_at', ''),
|
||
'arriveTime', COALESCE(NULLIF(p_raw ->> 'arrived_at', ''), p_raw ->> 'checked_in_at', ''),
|
||
'checkinTime', COALESCE(NULLIF(p_raw ->> 'checked_in_at', ''), p_raw ->> 'arrived_at', ''),
|
||
'startServiceTime', COALESCE(p_raw ->> 'service_started_at', ''),
|
||
'finishTime', COALESCE(NULLIF(p_raw ->> 'completed_at', ''), p_raw ->> 'service_completed_at', ''),
|
||
'cancelReason', COALESCE(p_raw ->> 'cancel_reason', ''),
|
||
'exceptionType', COALESCE(p_exception ->> 'exception_type', ''),
|
||
'exceptionDesc', COALESCE(p_exception ->> 'description', p_exception ->> 'remark', ''),
|
||
'evidenceList', COALESCE(v_evidence_list, '[]'::jsonb),
|
||
'signatureUrl', '',
|
||
'signatureName', '',
|
||
'satisfactionStatus', CASE WHEN v_front_status = 'pending_acceptance' THEN '待验<EFBFBD>? WHEN v_front_status = 'completed' THEN '已验<EFBFBD>? ELSE '待评<EFBFBD>? END,
|
||
'settlementStatus', CASE WHEN v_front_status = 'completed' THEN '待结<EFBFBD>? ELSE '待确<EFBFBD>? END,
|
||
'archiveStatus', '未归<EFBFBD>?,
|
||
'createdAt', COALESCE(p_raw ->> 'created_at', ''),
|
||
'updatedAt', COALESCE(p_raw ->> 'updated_at', ''),
|
||
'contactName', COALESCE(p_raw ->> 'contact_name', ''),
|
||
'contactPhone', COALESCE(p_raw ->> 'contact_phone', ''),
|
||
'notices', '[]'::jsonb,
|
||
'timeline', COALESCE(v_timeline, '[]'::jsonb),
|
||
'statusLog', COALESCE(v_status_logs, '[]'::jsonb),
|
||
'serviceSummary', COALESCE(v_service_record ->> 'summary', v_service_record ->> 'content', ''),
|
||
'progressNote', COALESCE(v_service_record ->> 'remark', ''),
|
||
'distanceKm', '',
|
||
'allowCheckinRadiusMeters', 100,
|
||
'lastLocation', CASE
|
||
WHEN v_checkin_record IS NULL THEN NULL
|
||
ELSE jsonb_build_object(
|
||
'latitude', COALESCE((v_checkin_record ->> 'latitude')::NUMERIC, (v_checkin_record ->> 'checkin_latitude')::NUMERIC, 0),
|
||
'longitude', COALESCE((v_checkin_record ->> 'longitude')::NUMERIC, (v_checkin_record ->> 'checkin_longitude')::NUMERIC, 0),
|
||
'address', COALESCE(v_checkin_record ->> 'location_text', v_checkin_record ->> 'checkin_address', ''),
|
||
'time', COALESCE(v_checkin_record ->> 'checked_in_at', v_checkin_record ->> 'checkin_time', '')
|
||
)
|
||
END,
|
||
'trackPoints', COALESCE(v_service_record -> 'track_points_json', '[]'::jsonb),
|
||
'serviceRecord', v_record_json,
|
||
'abnormalReport', public.delivery_build_abnormal(p_exception, COALESCE(p_raw ->> 'id', ''))
|
||
);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_get_legacy_order_json(p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
v_raw JSONB;
|
||
v_logs JSONB := '[]'::jsonb;
|
||
v_records JSONB := '[]'::jsonb;
|
||
v_evidence JSONB := '[]'::jsonb;
|
||
BEGIN
|
||
SELECT to_jsonb(o)
|
||
INTO v_raw
|
||
FROM public.hss_service_orders o
|
||
WHERE o.id = p_order_id
|
||
AND o.deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR o.current_staff_id = v_staff_id)
|
||
LIMIT 1;
|
||
|
||
IF v_raw IS NULL THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
|
||
SELECT COALESCE(jsonb_agg(to_jsonb(l) ORDER BY l.created_at DESC), '[]'::jsonb)
|
||
INTO v_logs
|
||
FROM public.hss_service_order_status_logs l
|
||
WHERE l.order_id = p_order_id;
|
||
|
||
SELECT COALESCE(jsonb_agg(to_jsonb(r) ORDER BY r.created_at DESC), '[]'::jsonb)
|
||
INTO v_records
|
||
FROM public.hss_service_execution_records r
|
||
WHERE r.order_id = p_order_id;
|
||
|
||
SELECT COALESCE(jsonb_agg(to_jsonb(e) ORDER BY e.created_at DESC), '[]'::jsonb)
|
||
INTO v_evidence
|
||
FROM public.hss_service_evidence_files e
|
||
WHERE e.order_id = p_order_id;
|
||
|
||
RETURN public.delivery_build_order_json(v_raw, v_logs, v_records, v_evidence, NULL, 'legacy');
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_get_care_order_json(p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_user_id UUID := public.delivery_current_user_id();
|
||
v_raw JSONB;
|
||
v_logs JSONB := '[]'::jsonb;
|
||
v_records JSONB := '[]'::jsonb;
|
||
v_evidence JSONB := '[]'::jsonb;
|
||
v_exception JSONB;
|
||
BEGIN
|
||
IF NOT public.delivery_table_exists('ec_care_tasks') THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
|
||
BEGIN
|
||
EXECUTE 'SELECT to_jsonb(t) FROM public.ec_care_tasks t WHERE t.id = $1::uuid AND ($2 IS NULL OR t.assigned_to = $2) LIMIT 1'
|
||
INTO v_raw
|
||
USING p_order_id, v_user_id;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
RETURN NULL;
|
||
END;
|
||
|
||
IF v_raw IS NULL THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
|
||
BEGIN
|
||
IF public.delivery_table_exists('hc_work_order_events') THEN
|
||
EXECUTE 'SELECT COALESCE(jsonb_agg(to_jsonb(e) ORDER BY e.created_at DESC), ''[]''::jsonb) FROM public.hc_work_order_events e WHERE e.task_id = $1::uuid'
|
||
INTO v_logs
|
||
USING p_order_id;
|
||
END IF;
|
||
IF public.delivery_table_exists('ec_care_records') THEN
|
||
EXECUTE 'SELECT COALESCE(jsonb_agg(to_jsonb(r) ORDER BY r.created_at DESC), ''[]''::jsonb) FROM public.ec_care_records r WHERE r.task_id = $1::uuid'
|
||
INTO v_records
|
||
USING p_order_id;
|
||
END IF;
|
||
IF public.delivery_table_exists('hc_evidence_files') THEN
|
||
EXECUTE 'SELECT COALESCE(jsonb_agg(to_jsonb(f) ORDER BY f.created_at DESC), ''[]''::jsonb) FROM public.hc_evidence_files f WHERE f.task_id = $1::uuid'
|
||
INTO v_evidence
|
||
USING p_order_id;
|
||
END IF;
|
||
IF public.delivery_table_exists('hc_work_order_exceptions') THEN
|
||
EXECUTE 'SELECT to_jsonb(x) FROM public.hc_work_order_exceptions x WHERE x.task_id = $1::uuid ORDER BY x.created_at DESC LIMIT 1'
|
||
INTO v_exception
|
||
USING p_order_id;
|
||
END IF;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
NULL;
|
||
END;
|
||
|
||
RETURN public.delivery_build_order_json(v_raw, v_logs, v_records, v_evidence, v_exception, 'care');
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_get_order_json(p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_order JSONB;
|
||
BEGIN
|
||
v_order := public.delivery_get_care_order_json(p_order_id);
|
||
IF v_order IS NOT NULL THEN
|
||
RETURN v_order;
|
||
END IF;
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_order_matches(p_order JSONB, p_tab TEXT, p_keyword TEXT)
|
||
RETURNS BOOLEAN
|
||
LANGUAGE plpgsql
|
||
IMMUTABLE
|
||
AS $$
|
||
DECLARE
|
||
v_status TEXT := COALESCE(p_order ->> 'status', 'pending_assignment');
|
||
v_keyword TEXT := btrim(COALESCE(p_keyword, ''));
|
||
BEGIN
|
||
IF COALESCE(p_tab, '') NOT IN ('', 'all') THEN
|
||
CASE p_tab
|
||
WHEN 'pending_accept' THEN
|
||
IF v_status NOT IN ('pending_assignment', 'pending_accept') THEN RETURN FALSE; END IF;
|
||
WHEN 'accepted' THEN
|
||
IF v_status NOT IN ('accepted', 'waiting_departure', 'departed', 'on_the_way', 'arrived') THEN RETURN FALSE; END IF;
|
||
WHEN 'serving' THEN
|
||
IF v_status NOT IN ('in_service', 'serving') THEN RETURN FALSE; END IF;
|
||
WHEN 'pending_submit' THEN
|
||
IF v_status NOT IN ('pending_confirm', 'pending_submit', 'pending_acceptance') THEN RETURN FALSE; END IF;
|
||
WHEN 'completed' THEN
|
||
IF v_status NOT IN ('completed', 'settled', 'archived') THEN RETURN FALSE; END IF;
|
||
WHEN 'exception' THEN
|
||
IF v_status NOT IN ('abnormal', 'exception_pending') THEN RETURN FALSE; END IF;
|
||
END CASE;
|
||
END IF;
|
||
|
||
IF v_keyword <> '' THEN
|
||
IF COALESCE(p_order ->> 'orderNo', '') NOT ILIKE '%' || v_keyword || '%'
|
||
AND COALESCE(p_order ->> 'serviceName', '') NOT ILIKE '%' || v_keyword || '%'
|
||
AND COALESCE(p_order ->> 'elderNameMasked', '') NOT ILIKE '%' || v_keyword || '%'
|
||
AND COALESCE(p_order ->> 'addressSummary', '') NOT ILIKE '%' || v_keyword || '%' THEN
|
||
RETURN FALSE;
|
||
END IF;
|
||
END IF;
|
||
|
||
RETURN TRUE;
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_append_if_match(p_result JSONB, p_order JSONB, p_tab TEXT, p_keyword TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
IMMUTABLE
|
||
AS $$
|
||
BEGIN
|
||
IF p_order IS NULL THEN
|
||
RETURN COALESCE(p_result, '[]'::jsonb);
|
||
END IF;
|
||
IF public.delivery_order_matches(p_order, p_tab, p_keyword) THEN
|
||
RETURN COALESCE(p_result, '[]'::jsonb) || jsonb_build_array(p_order);
|
||
END IF;
|
||
RETURN COALESCE(p_result, '[]'::jsonb);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_build_staff_info(p_staff_id UUID)
|
||
RETURNS JSONB
|
||
LANGUAGE sql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
SELECT jsonb_build_object(
|
||
'id', s.id,
|
||
'userId', COALESCE(s.uid::TEXT, ''),
|
||
'staffNo', COALESCE(NULLIF(s.staff_no, ''), 'DEL-' || upper(substr(s.id::TEXT, 1, 8))),
|
||
'name', COALESCE(NULLIF(s.nickname, ''), '服务人员'),
|
||
'phone', COALESCE(s.phone, ''),
|
||
'role', 'delivery',
|
||
'status', CASE WHEN s.status = 0 OR COALESCE(s.is_active, true) = false THEN 'disabled' ELSE 'active' END,
|
||
'organizationId', COALESCE(s.station_id::TEXT, ''),
|
||
'organizationName', COALESCE(st.name, COALESCE(s.station_id::TEXT, '')),
|
||
'certificates', '[]'::jsonb,
|
||
'certificateStatus', CASE WHEN COALESCE(s.certificate_status, '') IN ('valid', 'expired') THEN s.certificate_status ELSE 'pending' END,
|
||
'certificateExpireAt', COALESCE(to_char(s.certificate_expire_at, 'YYYY-MM-DD'), ''),
|
||
'onlineStatus', CASE WHEN s.online_status IN ('online', 'busy') THEN s.online_status ELSE 'resting' END,
|
||
'serviceArea', COALESCE(s.service_area, ''),
|
||
'skills', CASE WHEN COALESCE(s.skills, '') = '' THEN '[]'::jsonb ELSE COALESCE(s.skills::jsonb, '[]'::jsonb) END,
|
||
'avatarUrl', COALESCE(s.avatar, ''),
|
||
'todayAccepted', 0,
|
||
'todayServing', 0,
|
||
'todayCompleted', 0,
|
||
'usesMock', false
|
||
)
|
||
FROM public.ml_delivery_staff s
|
||
LEFT JOIN public.ml_delivery_stations st ON st.id = s.station_id
|
||
WHERE s.id = p_staff_id
|
||
AND s.deleted_at IS NULL
|
||
LIMIT 1
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_assert_staff_access(p_staff_id UUID)
|
||
RETURNS VOID
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_current_staff_id UUID := public.delivery_current_staff_id();
|
||
BEGIN
|
||
IF auth.uid() IS NULL THEN
|
||
RAISE EXCEPTION 'Permission denied';
|
||
END IF;
|
||
IF p_staff_id IS NOT NULL AND v_current_staff_id IS NOT NULL AND p_staff_id <> v_current_staff_id THEN
|
||
RAISE EXCEPTION 'Permission denied';
|
||
END IF;
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.delivery_try_update_care_task(p_order_id TEXT, p_status TEXT, p_patch JSONB, p_action TEXT, p_remark TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_user_id UUID := public.delivery_current_user_id();
|
||
v_now TIMESTAMPTZ := now();
|
||
BEGIN
|
||
IF NOT public.delivery_table_exists('ec_care_tasks') THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
|
||
BEGIN
|
||
EXECUTE $sql$
|
||
UPDATE public.ec_care_tasks
|
||
SET status = COALESCE($2, status),
|
||
accepted_at = COALESCE(($3 ->> 'accepted_at')::timestamptz, accepted_at),
|
||
departed_at = COALESCE(($3 ->> 'departed_at')::timestamptz, departed_at),
|
||
checked_in_at = COALESCE(($3 ->> 'checked_in_at')::timestamptz, checked_in_at),
|
||
service_started_at = COALESCE(($3 ->> 'service_started_at')::timestamptz, service_started_at),
|
||
service_completed_at = COALESCE(($3 ->> 'service_completed_at')::timestamptz, service_completed_at),
|
||
acceptance_pending_at = COALESCE(($3 ->> 'acceptance_pending_at')::timestamptz, acceptance_pending_at),
|
||
reject_reason = COALESCE($3 ->> 'reject_reason', reject_reason),
|
||
updated_at = COALESCE(($3 ->> 'updated_at')::timestamptz, $4)
|
||
WHERE id = $1::uuid
|
||
AND ($5 IS NULL OR assigned_to = $5)
|
||
$sql$
|
||
USING p_order_id, p_status, COALESCE(p_patch, '{}'::jsonb), v_now, v_user_id;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
RETURN NULL;
|
||
END;
|
||
|
||
BEGIN
|
||
IF public.delivery_table_exists('hc_work_order_events') THEN
|
||
EXECUTE $sql$
|
||
INSERT INTO public.hc_work_order_events(id, task_id, from_status, to_status, actor_id, actor_role, action, remark, created_at)
|
||
VALUES (md5(random()::text || clock_timestamp()::text), $1::uuid, NULL, $2, $3, 'delivery', $4, $5, $6)
|
||
$sql$
|
||
USING p_order_id, p_status, v_user_id, COALESCE(p_action, ''), COALESCE(p_remark, ''), v_now;
|
||
END IF;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
NULL;
|
||
END;
|
||
|
||
RETURN public.delivery_get_care_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_order_detail(p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
BEGIN
|
||
IF auth.uid() IS NULL THEN
|
||
RAISE EXCEPTION 'Permission denied';
|
||
END IF;
|
||
RETURN public.delivery_get_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_order_list(
|
||
p_staff_id UUID DEFAULT NULL,
|
||
p_tab TEXT DEFAULT 'all',
|
||
p_keyword TEXT DEFAULT ''
|
||
)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_user_id UUID := public.delivery_current_user_id();
|
||
v_staff_id UUID := COALESCE(p_staff_id, public.delivery_current_staff_id());
|
||
v_result JSONB := '[]'::jsonb;
|
||
v_task JSONB;
|
||
v_order JSONB;
|
||
v_order_id TEXT;
|
||
BEGIN
|
||
PERFORM public.delivery_assert_staff_access(v_staff_id);
|
||
|
||
IF public.delivery_table_exists('ec_care_tasks') THEN
|
||
BEGIN
|
||
FOR v_task IN EXECUTE 'SELECT to_jsonb(t) FROM public.ec_care_tasks t WHERE ($1 IS NULL OR t.assigned_to = $1) ORDER BY t.created_at DESC'
|
||
USING v_user_id
|
||
LOOP
|
||
v_order := public.delivery_get_care_order_json(v_task ->> 'id');
|
||
v_result := public.delivery_append_if_match(v_result, v_order, p_tab, p_keyword);
|
||
END LOOP;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
NULL;
|
||
END;
|
||
END IF;
|
||
|
||
FOR v_order_id IN
|
||
SELECT o.id
|
||
FROM public.hss_service_orders o
|
||
WHERE o.deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR o.current_staff_id = v_staff_id)
|
||
ORDER BY COALESCE(o.updated_at, o.created_at) DESC, o.created_at DESC
|
||
LOOP
|
||
v_order := public.delivery_get_legacy_order_json(v_order_id);
|
||
v_result := public.delivery_append_if_match(v_result, v_order, p_tab, p_keyword);
|
||
END LOOP;
|
||
|
||
RETURN COALESCE(v_result, '[]'::jsonb);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_dashboard(p_staff_id UUID DEFAULT NULL)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_staff_id UUID := COALESCE(p_staff_id, public.delivery_current_staff_id());
|
||
v_orders JSONB := public.rpc_delivery_order_list(v_staff_id, 'all', '');
|
||
v_item JSONB;
|
||
v_pending_assignment_count INTEGER := 0;
|
||
v_pending_accept_count INTEGER := 0;
|
||
v_today_order_count INTEGER := 0;
|
||
v_pending_depart_count INTEGER := 0;
|
||
v_serving_count INTEGER := 0;
|
||
v_completed_count INTEGER := 0;
|
||
v_exception_count INTEGER := 0;
|
||
v_next_order JSONB;
|
||
BEGIN
|
||
PERFORM public.delivery_assert_staff_access(v_staff_id);
|
||
|
||
FOR v_item IN SELECT value FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb))
|
||
LOOP
|
||
IF COALESCE(v_item ->> 'status', '') IN ('pending_assignment', 'pending_accept') THEN
|
||
v_pending_assignment_count := v_pending_assignment_count + 1;
|
||
v_pending_accept_count := v_pending_accept_count + 1;
|
||
END IF;
|
||
IF COALESCE(v_item ->> 'status', '') IN ('accepted', 'waiting_departure', 'departed', 'on_the_way', 'arrived') THEN
|
||
v_pending_depart_count := v_pending_depart_count + 1;
|
||
END IF;
|
||
IF COALESCE(v_item ->> 'status', '') IN ('pending_assignment', 'pending_accept', 'accepted', 'waiting_departure', 'departed', 'on_the_way', 'arrived', 'in_service', 'serving', 'pending_acceptance') THEN
|
||
v_today_order_count := v_today_order_count + 1;
|
||
END IF;
|
||
IF COALESCE(v_item ->> 'status', '') IN ('in_service', 'serving') THEN
|
||
v_serving_count := v_serving_count + 1;
|
||
END IF;
|
||
IF COALESCE(v_item ->> 'status', '') IN ('completed', 'pending_acceptance', 'settled', 'archived') THEN
|
||
v_completed_count := v_completed_count + 1;
|
||
END IF;
|
||
IF COALESCE(v_item ->> 'status', '') IN ('abnormal', 'exception_pending') THEN
|
||
v_exception_count := v_exception_count + 1;
|
||
END IF;
|
||
IF v_next_order IS NULL AND COALESCE(v_item ->> 'status', '') NOT IN ('completed', 'settled', 'archived', 'cancelled', 'rejected', 'abnormal') THEN
|
||
v_next_order := v_item;
|
||
END IF;
|
||
END LOOP;
|
||
|
||
RETURN jsonb_build_object(
|
||
'pendingAssignmentCount', v_pending_assignment_count,
|
||
'pendingAcceptCount', v_pending_accept_count,
|
||
'todayOrderCount', v_today_order_count,
|
||
'pendingDepartCount', v_pending_depart_count,
|
||
'servingCount', v_serving_count,
|
||
'completedCount', v_completed_count,
|
||
'exceptionCount', v_exception_count,
|
||
'expectedIncome', 0,
|
||
'onlineStatus', COALESCE(public.delivery_build_staff_info(v_staff_id) ->> 'onlineStatus', 'resting'),
|
||
'nextOrder', v_next_order,
|
||
'recentOrders', COALESCE((SELECT jsonb_agg(value) FROM (SELECT value FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb)) LIMIT 5) q), '[]'::jsonb)
|
||
);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_accept_order(p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
RETURN public.delivery_try_update_care_task(
|
||
p_order_id,
|
||
'ORDER_ACCEPTED',
|
||
jsonb_build_object('accepted_at', v_now, 'updated_at', v_now),
|
||
'accept_task',
|
||
'服务人员接单'
|
||
);
|
||
END IF;
|
||
|
||
UPDATE public.hss_service_assignments
|
||
SET status = 'accepted', accepted_at = v_now, updated_at = v_now
|
||
WHERE order_id = p_order_id
|
||
AND (v_staff_id IS NULL OR staff_id = v_staff_id);
|
||
|
||
UPDATE public.hss_service_orders
|
||
SET status = 'accepted', accepted_at = v_now, updated_at = v_now
|
||
WHERE id = p_order_id
|
||
AND deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);
|
||
|
||
INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
|
||
VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'assigned', 'accepted', public.delivery_current_user_id(), 'delivery', '服务人员接单', v_now);
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_reject_order(p_order_id TEXT, p_reason TEXT DEFAULT '')
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
v_reason TEXT := COALESCE(p_reason, '');
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
RETURN public.delivery_try_update_care_task(
|
||
p_order_id,
|
||
'ORDER_REJECTED',
|
||
jsonb_build_object('reject_reason', v_reason, 'updated_at', v_now),
|
||
'reject_task',
|
||
CASE WHEN v_reason = '' THEN '服务人员拒单' ELSE v_reason END
|
||
);
|
||
END IF;
|
||
|
||
UPDATE public.hss_service_assignments
|
||
SET status = 'rejected', rejected_at = v_now, reject_reason = v_reason, updated_at = v_now
|
||
WHERE order_id = p_order_id
|
||
AND (v_staff_id IS NULL OR staff_id = v_staff_id);
|
||
|
||
UPDATE public.hss_service_orders
|
||
SET status = 'rejected', updated_at = v_now
|
||
WHERE id = p_order_id
|
||
AND deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);
|
||
|
||
INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
|
||
VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'assigned', 'rejected', public.delivery_current_user_id(), 'delivery', CASE WHEN v_reason = '' THEN '服务人员拒单' ELSE v_reason END, v_now);
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_start_depart(p_order_id TEXT, p_location JSONB DEFAULT '{}'::jsonb)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
v_remark TEXT := CASE WHEN COALESCE(p_location ->> 'address', '') = '' THEN '服务人员出发' ELSE '服务人员出发<EFBFBD>? || (p_location ->> 'address') END;
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
RETURN public.delivery_try_update_care_task(
|
||
p_order_id,
|
||
'ORDER_ACCEPTED',
|
||
jsonb_build_object('departed_at', v_now, 'updated_at', v_now),
|
||
'depart_task',
|
||
v_remark
|
||
);
|
||
END IF;
|
||
|
||
UPDATE public.hss_service_orders
|
||
SET status = 'departed', departed_at = v_now, updated_at = v_now
|
||
WHERE id = p_order_id
|
||
AND deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);
|
||
|
||
INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
|
||
VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'accepted', 'departed', public.delivery_current_user_id(), 'delivery', v_remark, v_now);
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_arrive_order(p_order_id TEXT, p_location JSONB DEFAULT '{}'::jsonb)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
v_remark TEXT := CASE WHEN COALESCE(p_location ->> 'address', '') = '' THEN '服务人员到达' ELSE '服务人员到达<EFBFBD>? || (p_location ->> 'address') END;
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
RETURN public.delivery_try_update_care_task(
|
||
p_order_id,
|
||
'ORDER_CHECKED_IN',
|
||
jsonb_build_object('checked_in_at', v_now, 'updated_at', v_now),
|
||
'arrive_task',
|
||
v_remark
|
||
);
|
||
END IF;
|
||
|
||
UPDATE public.hss_service_orders
|
||
SET status = 'arrived', arrived_at = v_now, updated_at = v_now
|
||
WHERE id = p_order_id
|
||
AND deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);
|
||
|
||
INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
|
||
VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'departed', 'arrived', public.delivery_current_user_id(), 'delivery', v_remark, v_now);
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_checkin_order(p_order_id TEXT, p_payload JSONB DEFAULT '{}'::jsonb)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
v_assignment_id TEXT := '';
|
||
v_record_id TEXT := 'ser-' || replace(gen_random_uuid()::TEXT, '-', '');
|
||
v_file TEXT;
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
BEGIN
|
||
IF public.delivery_table_exists('ec_care_records') THEN
|
||
EXECUTE $sql$
|
||
INSERT INTO public.ec_care_records(id, task_id, record_type, started_at, checked_in_at, latitude, longitude, location_text, remark, created_at, updated_at)
|
||
VALUES ($1, $2::uuid, 'checkin', $3, $3, ($4 -> 'location' ->> 'latitude')::double precision, ($4 -> 'location' ->> 'longitude')::double precision, COALESCE($4 -> 'location' ->> 'address', ''), COALESCE($4 ->> 'note', ''), $3, $3)
|
||
$sql$
|
||
USING 'care-checkin-' || replace(gen_random_uuid()::TEXT, '-', ''), p_order_id, v_now, COALESCE(p_payload, '{}'::jsonb);
|
||
END IF;
|
||
IF public.delivery_table_exists('hc_evidence_files') THEN
|
||
FOR v_file IN SELECT jsonb_array_elements_text(COALESCE(p_payload -> 'photos', '[]'::jsonb))
|
||
LOOP
|
||
EXECUTE $sql$
|
||
INSERT INTO public.hc_evidence_files(id, task_id, care_record_id, phase, file_type, storage_path, file_url, latitude, longitude, captured_at, created_at)
|
||
VALUES ($1, $2::uuid, NULL, 'checkin', 'image', $3, $3, ($4 -> 'location' ->> 'latitude')::double precision, ($4 -> 'location' ->> 'longitude')::double precision, $5, $5)
|
||
$sql$
|
||
USING 'hc-ef-' || replace(gen_random_uuid()::TEXT, '-', ''), p_order_id, v_file, COALESCE(p_payload, '{}'::jsonb), v_now;
|
||
END LOOP;
|
||
END IF;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
NULL;
|
||
END;
|
||
|
||
RETURN public.delivery_try_update_care_task(
|
||
p_order_id,
|
||
'ORDER_CHECKED_IN',
|
||
jsonb_build_object('checked_in_at', v_now, 'updated_at', v_now),
|
||
'checkin_task',
|
||
COALESCE(NULLIF(p_payload ->> 'note', ''), '到场签到')
|
||
);
|
||
END IF;
|
||
|
||
SELECT COALESCE(a.id, '') INTO v_assignment_id
|
||
FROM public.hss_service_assignments a
|
||
WHERE a.order_id = p_order_id
|
||
AND (v_staff_id IS NULL OR a.staff_id = v_staff_id)
|
||
ORDER BY a.created_at DESC
|
||
LIMIT 1;
|
||
|
||
INSERT INTO public.hss_service_execution_records(
|
||
id, order_id, assignment_id, checkin_time, checkin_latitude, checkin_longitude, checkin_address, remark, created_at, updated_at
|
||
) VALUES (
|
||
v_record_id, p_order_id, v_assignment_id, v_now,
|
||
COALESCE((p_payload -> 'location' ->> 'latitude')::double precision, 0),
|
||
COALESCE((p_payload -> 'location' ->> 'longitude')::double precision, 0),
|
||
COALESCE(p_payload -> 'location' ->> 'address', ''),
|
||
COALESCE(p_payload ->> 'note', ''),
|
||
v_now, v_now
|
||
) ON CONFLICT (id) DO UPDATE SET
|
||
checkin_time = EXCLUDED.checkin_time,
|
||
checkin_latitude = EXCLUDED.checkin_latitude,
|
||
checkin_longitude = EXCLUDED.checkin_longitude,
|
||
checkin_address = EXCLUDED.checkin_address,
|
||
remark = EXCLUDED.remark,
|
||
updated_at = EXCLUDED.updated_at;
|
||
|
||
FOR v_file IN SELECT jsonb_array_elements_text(COALESCE(p_payload -> 'photos', '[]'::jsonb))
|
||
LOOP
|
||
INSERT INTO public.hss_service_evidence_files(id, order_id, execution_record_id, phase, file_type, storage_path, file_url, latitude, longitude, captured_at, created_at)
|
||
VALUES (
|
||
'sef-' || replace(gen_random_uuid()::TEXT, '-', ''),
|
||
p_order_id,
|
||
v_record_id,
|
||
'checkin',
|
||
'image',
|
||
v_file,
|
||
v_file,
|
||
COALESCE((p_payload -> 'location' ->> 'latitude')::double precision, 0),
|
||
COALESCE((p_payload -> 'location' ->> 'longitude')::double precision, 0),
|
||
v_now,
|
||
v_now
|
||
);
|
||
END LOOP;
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_start_service(p_order_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
RETURN public.delivery_try_update_care_task(
|
||
p_order_id,
|
||
'ORDER_IN_SERVICE',
|
||
jsonb_build_object('service_started_at', v_now, 'updated_at', v_now),
|
||
'start_service',
|
||
'开始服<EFBFBD>?
|
||
);
|
||
END IF;
|
||
|
||
UPDATE public.hss_service_execution_records
|
||
SET service_started_at = v_now, updated_at = v_now
|
||
WHERE order_id = p_order_id;
|
||
|
||
UPDATE public.hss_service_orders
|
||
SET status = 'in_service', service_started_at = v_now, updated_at = v_now
|
||
WHERE id = p_order_id
|
||
AND deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);
|
||
|
||
INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
|
||
VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'arrived', 'in_service', public.delivery_current_user_id(), 'delivery', '开始服<EFBFBD>?, v_now);
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_save_progress(p_order_id TEXT, p_payload JSONB DEFAULT '{}'::jsonb)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
v_assignment_id TEXT := '';
|
||
v_record_id TEXT := 'progress-' || replace(gen_random_uuid()::TEXT, '-', '');
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
BEGIN
|
||
IF public.delivery_table_exists('ec_care_records') THEN
|
||
EXECUTE $sql$
|
||
INSERT INTO public.ec_care_records(id, task_id, record_type, started_at, finished_at, duration_minutes, service_items_json, summary, remark, created_at, updated_at)
|
||
VALUES ($1, $2::uuid, 'service', now(), NULL, 0, COALESCE($3 -> 'items', '[]'::jsonb), COALESCE($3 ->> 'serviceSummary', ''), COALESCE($3 ->> 'progressNote', ''), $4, $4)
|
||
ON CONFLICT (id) DO UPDATE SET
|
||
service_items_json = EXCLUDED.service_items_json,
|
||
summary = EXCLUDED.summary,
|
||
remark = EXCLUDED.remark,
|
||
updated_at = EXCLUDED.updated_at
|
||
$sql$
|
||
USING v_record_id, p_order_id, COALESCE(p_payload, '{}'::jsonb), v_now;
|
||
END IF;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
NULL;
|
||
END;
|
||
RETURN public.delivery_get_care_order_json(p_order_id);
|
||
END IF;
|
||
|
||
SELECT COALESCE(a.id, '') INTO v_assignment_id
|
||
FROM public.hss_service_assignments a
|
||
WHERE a.order_id = p_order_id
|
||
AND (v_staff_id IS NULL OR a.staff_id = v_staff_id)
|
||
ORDER BY a.created_at DESC
|
||
LIMIT 1;
|
||
|
||
INSERT INTO public.hss_service_execution_records(
|
||
id, order_id, assignment_id, service_started_at, service_finished_at, actual_duration_minutes, service_items_json, summary, remark, created_at, updated_at
|
||
) VALUES (
|
||
v_record_id, p_order_id, v_assignment_id, v_now, NULL, 0,
|
||
COALESCE(p_payload -> 'items', '[]'::jsonb),
|
||
COALESCE(p_payload ->> 'serviceSummary', ''),
|
||
COALESCE(p_payload ->> 'progressNote', ''),
|
||
v_now, v_now
|
||
) ON CONFLICT (id) DO UPDATE SET
|
||
service_items_json = EXCLUDED.service_items_json,
|
||
summary = EXCLUDED.summary,
|
||
remark = EXCLUDED.remark,
|
||
updated_at = EXCLUDED.updated_at;
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_upload_evidence(
|
||
p_order_id TEXT,
|
||
p_phase TEXT DEFAULT 'service',
|
||
p_files TEXT[] DEFAULT ARRAY[]::TEXT[]
|
||
)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_file TEXT;
|
||
v_result JSONB := '[]'::jsonb;
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
BEGIN
|
||
IF public.delivery_table_exists('hc_evidence_files') THEN
|
||
FOREACH v_file IN ARRAY COALESCE(p_files, ARRAY[]::TEXT[])
|
||
LOOP
|
||
EXECUTE $sql$
|
||
INSERT INTO public.hc_evidence_files(id, task_id, care_record_id, phase, file_type, storage_path, file_url, captured_at, created_at)
|
||
VALUES ($1, $2::uuid, NULL, $3, 'image', $4, $4, $5, $5)
|
||
$sql$
|
||
USING 'hc-ef-' || replace(gen_random_uuid()::TEXT, '-', ''), p_order_id, COALESCE(p_phase, 'service'), v_file, v_now;
|
||
END LOOP;
|
||
END IF;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
NULL;
|
||
END;
|
||
|
||
v_result := COALESCE(public.delivery_get_care_order_json(p_order_id) -> 'evidenceList', '[]'::jsonb);
|
||
RETURN v_result;
|
||
END IF;
|
||
|
||
FOREACH v_file IN ARRAY COALESCE(p_files, ARRAY[]::TEXT[])
|
||
LOOP
|
||
INSERT INTO public.hss_service_evidence_files(id, order_id, execution_record_id, phase, file_type, storage_path, file_url, captured_at, created_at)
|
||
VALUES (
|
||
'sef-' || replace(gen_random_uuid()::TEXT, '-', ''),
|
||
p_order_id,
|
||
NULL,
|
||
COALESCE(p_phase, 'service'),
|
||
'image',
|
||
v_file,
|
||
v_file,
|
||
v_now,
|
||
v_now
|
||
);
|
||
END LOOP;
|
||
|
||
RETURN COALESCE(public.delivery_get_legacy_order_json(p_order_id) -> 'evidenceList', '[]'::jsonb);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_retry_evidence(p_order_id TEXT, p_evidence_id TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_order JSONB := public.delivery_get_order_json(p_order_id);
|
||
v_item JSONB;
|
||
BEGIN
|
||
FOR v_item IN SELECT value FROM jsonb_array_elements(COALESCE(v_order -> 'evidenceList', '[]'::jsonb))
|
||
LOOP
|
||
IF COALESCE(v_item ->> 'id', '') = COALESCE(p_evidence_id, '') THEN
|
||
RETURN v_item;
|
||
END IF;
|
||
END LOOP;
|
||
RETURN NULL;
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_submit_exception(p_order_id TEXT, p_payload JSONB DEFAULT '{}'::jsonb)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
BEGIN
|
||
IF public.delivery_table_exists('hc_work_order_exceptions') THEN
|
||
EXECUTE $sql$
|
||
INSERT INTO public.hc_work_order_exceptions(id, task_id, exception_type, description, occurred_at, location_text, images_json, need_platform_intervention, request_cancel_order, request_reschedule, created_by, created_at, updated_at)
|
||
VALUES ($1, $2::uuid, COALESCE($3 ->> 'exceptionType', 'other'), COALESCE($3 ->> 'description', ''), COALESCE(($3 ->> 'occurredAt')::timestamptz, $4), COALESCE($3 ->> 'locationText', ''), COALESCE($3 -> 'images', '[]'::jsonb), COALESCE(($3 ->> 'needPlatformIntervention')::boolean, false), COALESCE(($3 ->> 'requestCancelOrder')::boolean, false), COALESCE(($3 ->> 'requestReschedule')::boolean, false), $5, $4, $4)
|
||
$sql$
|
||
USING 'hc-ex-' || replace(gen_random_uuid()::TEXT, '-', ''), p_order_id, COALESCE(p_payload, '{}'::jsonb), v_now, public.delivery_current_user_id();
|
||
END IF;
|
||
EXCEPTION WHEN OTHERS THEN
|
||
NULL;
|
||
END;
|
||
|
||
RETURN public.delivery_try_update_care_task(
|
||
p_order_id,
|
||
'ORDER_EXCEPTION',
|
||
jsonb_build_object('updated_at', v_now),
|
||
'report_exception',
|
||
COALESCE(NULLIF(p_payload ->> 'description', ''), '异常上报')
|
||
);
|
||
END IF;
|
||
|
||
UPDATE public.hss_service_orders
|
||
SET status = 'exception', updated_at = v_now
|
||
WHERE id = p_order_id
|
||
AND deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);
|
||
|
||
INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
|
||
VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'in_service', 'exception', public.delivery_current_user_id(), 'delivery', COALESCE(NULLIF(p_payload ->> 'description', ''), '异常上报'), v_now);
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_finish_service(p_order_id TEXT, p_payload JSONB DEFAULT '{}'::jsonb)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_now TIMESTAMPTZ := now();
|
||
v_staff_id UUID := public.delivery_current_staff_id();
|
||
BEGIN
|
||
IF public.delivery_get_care_order_json(p_order_id) IS NOT NULL THEN
|
||
RETURN public.delivery_try_update_care_task(
|
||
p_order_id,
|
||
'ACCEPTANCE_PENDING',
|
||
jsonb_build_object('service_completed_at', v_now, 'acceptance_pending_at', v_now, 'updated_at', v_now),
|
||
'finish_service',
|
||
'服务完成,等待用户验<EFBFBD>?
|
||
);
|
||
END IF;
|
||
|
||
UPDATE public.hss_service_execution_records
|
||
SET service_finished_at = v_now, updated_at = v_now
|
||
WHERE order_id = p_order_id;
|
||
|
||
UPDATE public.hss_service_orders
|
||
SET status = 'pending_acceptance', completed_at = v_now, pending_acceptance_at = v_now, updated_at = v_now
|
||
WHERE id = p_order_id
|
||
AND deleted_at IS NULL
|
||
AND (v_staff_id IS NULL OR current_staff_id = v_staff_id);
|
||
|
||
INSERT INTO public.hss_service_order_status_logs(id, order_id, from_status, to_status, operator_id, operator_role, remark, created_at)
|
||
VALUES (md5(random()::text || clock_timestamp()::text), p_order_id, 'in_service', 'pending_acceptance', public.delivery_current_user_id(), 'delivery', '服务完成,等待用户验<EFBFBD>?, v_now);
|
||
|
||
RETURN public.delivery_get_legacy_order_json(p_order_id);
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_record_list(p_staff_id UUID DEFAULT NULL)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_staff_id UUID := COALESCE(p_staff_id, public.delivery_current_staff_id());
|
||
v_orders JSONB := public.rpc_delivery_order_list(v_staff_id, 'all', '');
|
||
v_result JSONB := '[]'::jsonb;
|
||
v_item JSONB;
|
||
BEGIN
|
||
PERFORM public.delivery_assert_staff_access(v_staff_id);
|
||
|
||
FOR v_item IN SELECT value FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb))
|
||
LOOP
|
||
IF COALESCE(v_item ->> 'status', '') IN ('pending_acceptance', 'completed', 'settled', 'archived', 'abnormal', 'exception_pending', 'cancelled', 'rejected') THEN
|
||
v_result := v_result || jsonb_build_array(jsonb_build_object(
|
||
'id', 'record-' || COALESCE(v_item ->> 'id', ''),
|
||
'orderId', COALESCE(v_item ->> 'id', ''),
|
||
'orderNo', COALESCE(v_item ->> 'orderNo', ''),
|
||
'serviceName', COALESCE(v_item ->> 'serviceName', ''),
|
||
'elderName', COALESCE(v_item ->> 'elderName', ''),
|
||
'elderNameMasked', COALESCE(v_item ->> 'elderNameMasked', ''),
|
||
'status', COALESCE(v_item ->> 'status', ''),
|
||
'statusText', COALESCE(v_item ->> 'statusText', ''),
|
||
'appointmentStartTime', COALESCE(v_item ->> 'appointmentStartTime', ''),
|
||
'appointmentTime', COALESCE(v_item ->> 'appointmentTime', ''),
|
||
'actualStartTime', COALESCE(v_item ->> 'actualStartTime', ''),
|
||
'actualEndTime', COALESCE(v_item ->> 'actualEndTime', COALESCE(v_item ->> 'updatedAt', '')),
|
||
'staffIncome', COALESCE((v_item ->> 'staffIncome')::NUMERIC, 0),
|
||
'settlementStatus', COALESCE(v_item ->> 'settlementStatus', ''),
|
||
'acceptanceStatus', COALESCE(v_item ->> 'satisfactionStatus', ''),
|
||
'exceptionDesc', COALESCE(v_item ->> 'exceptionDesc', ''),
|
||
'ratingText', CASE WHEN COALESCE(v_item ->> 'status', '') IN ('completed', 'settled', 'archived') THEN '已验<EFBFBD>? WHEN COALESCE(v_item ->> 'status', '') = 'pending_acceptance' THEN '待验<EFBFBD>? ELSE '待补<EFBFBD>? END,
|
||
'hasServiceRecord', (v_item -> 'serviceRecord') IS NOT NULL
|
||
));
|
||
END IF;
|
||
END LOOP;
|
||
|
||
RETURN v_result;
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_message_list()
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
STABLE
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_orders JSONB := public.rpc_delivery_order_list(public.delivery_current_staff_id(), 'all', '');
|
||
v_result JSONB := '[]'::jsonb;
|
||
v_item JSONB;
|
||
BEGIN
|
||
FOR v_item IN SELECT value FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb)) LIMIT 8
|
||
LOOP
|
||
v_result := v_result || jsonb_build_array(jsonb_build_object(
|
||
'id', 'msg-' || COALESCE(v_item ->> 'id', ''),
|
||
'title', COALESCE(v_item ->> 'statusText', '工单提醒'),
|
||
'content', COALESCE(v_item ->> 'serviceName', '居家服务') || ',服务对象:' || COALESCE(v_item ->> 'elderNameMasked', '') || ',预约时间:' || COALESCE(v_item ->> 'appointmentStartTime', ''),
|
||
'type', 'order',
|
||
'createdAt', COALESCE(v_item ->> 'updatedAt', v_item ->> 'createdAt', ''),
|
||
'read', false,
|
||
'orderId', COALESCE(v_item ->> 'id', '')
|
||
));
|
||
END LOOP;
|
||
|
||
RETURN v_result;
|
||
END;
|
||
$$;
|
||
|
||
CREATE OR REPLACE FUNCTION public.rpc_delivery_set_online_status(p_staff_id UUID, p_status TEXT)
|
||
RETURNS JSONB
|
||
LANGUAGE plpgsql
|
||
SECURITY DEFINER
|
||
SET search_path = public
|
||
AS $$
|
||
DECLARE
|
||
v_staff_id UUID := COALESCE(p_staff_id, public.delivery_current_staff_id());
|
||
v_info JSONB;
|
||
BEGIN
|
||
PERFORM public.delivery_assert_staff_access(v_staff_id);
|
||
|
||
UPDATE public.ml_delivery_staff
|
||
SET online_status = CASE WHEN p_status IN ('online', 'busy') THEN p_status ELSE 'resting' END,
|
||
updated_at = now()
|
||
WHERE id = v_staff_id
|
||
AND deleted_at IS NULL;
|
||
|
||
v_info := public.delivery_build_staff_info(v_staff_id);
|
||
|
||
IF v_info IS NULL THEN
|
||
RETURN NULL;
|
||
END IF;
|
||
|
||
RETURN v_info || jsonb_build_object('onlineStatus', CASE WHEN p_status IN ('online', 'busy') THEN p_status ELSE 'resting' END);
|
||
END;
|
||
$$;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_order_detail(TEXT) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_order_detail(TEXT) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_order_list(UUID, TEXT, TEXT) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_order_list(UUID, TEXT, TEXT) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_dashboard(UUID) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_dashboard(UUID) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_accept_order(TEXT) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_accept_order(TEXT) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_reject_order(TEXT, TEXT) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_reject_order(TEXT, TEXT) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_start_depart(TEXT, JSONB) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_start_depart(TEXT, JSONB) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_arrive_order(TEXT, JSONB) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_arrive_order(TEXT, JSONB) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_checkin_order(TEXT, JSONB) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_checkin_order(TEXT, JSONB) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_start_service(TEXT) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_start_service(TEXT) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_save_progress(TEXT, JSONB) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_save_progress(TEXT, JSONB) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_upload_evidence(TEXT, TEXT, TEXT[]) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_upload_evidence(TEXT, TEXT, TEXT[]) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_retry_evidence(TEXT, TEXT) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_retry_evidence(TEXT, TEXT) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_submit_exception(TEXT, JSONB) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_submit_exception(TEXT, JSONB) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_finish_service(TEXT, JSONB) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_finish_service(TEXT, JSONB) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_record_list(UUID) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_record_list(UUID) TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_message_list() FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_message_list() TO authenticated;
|
||
|
||
REVOKE ALL ON FUNCTION public.rpc_delivery_set_online_status(UUID, TEXT) FROM PUBLIC;
|
||
GRANT EXECUTE ON FUNCTION public.rpc_delivery_set_online_status(UUID, TEXT) TO authenticated;
|
||
|
||
COMMIT; |