Files
medical-mall/mall_sql/migrations/20260610_fix_delivery_pending_blank.sql
2026-06-10 20:20:47 +08:00

766 lines
31 KiB
PL/PgSQL
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.
-- =====================================================================================
-- 20260610_fix_delivery_order_list_pending_blank.sql
-- Purpose:
-- 修复配送端"待接单"Tab 订单卡片显示空白的问题。
-- 根因:
-- 1. hss_service_orders 表缺少 request_id / price / staff_income / service_category 等列
-- 2. delivery_build_order_json 对空字段的 COALESCE 兜底不足,返回 "" / 0
-- 3. delivery_order_matches 对 pending tab 的过滤逻辑正确,但返回的订单核心字段为空
-- 4. 前端 enrichOrderWithRequestFallback 依赖 request_id 补全,但 hss_service_orders 无此列
-- 5. pending_assignment / pending_accept 不在原表 CHECK 约束中
-- =====================================================================================
BEGIN;
-- ============================================================================
-- 1. 为 hss_service_orders 补齐缺失的核心列
-- ============================================================================
ALTER TABLE public.hss_service_orders
ADD COLUMN IF NOT EXISTS request_id TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS service_category TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS price NUMERIC(10, 2) NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS staff_income NUMERIC(10, 2) NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS elder_name TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS elder_phone TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS contact_name TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS contact_phone TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS scheduled_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS dispatch_status TEXT NOT NULL DEFAULT 'pending',
ADD COLUMN IF NOT EXISTS dispatch_attempt_count INTEGER NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS dispatch_error_code TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS dispatch_error_message TEXT NOT NULL DEFAULT '',
ADD COLUMN IF NOT EXISTS dispatch_failed_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS service_price NUMERIC(10, 2) NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS duration_minutes INTEGER NOT NULL DEFAULT 90;
-- 扩展 CHECK 约束:允许 pending_assignment / pending_accept 状态
-- 由于 PostgreSQL 不支持直接修改 CHECK需要 DROP + CREATE
ALTER TABLE public.hss_service_orders DROP CONSTRAINT IF EXISTS chk_hss_service_orders_status;
ALTER TABLE public.hss_service_orders
ADD CONSTRAINT chk_hss_service_orders_status CHECK (
status IN (
'created', 'paid', 'assigned', 'accepted', 'rejected', 'departed', 'arrived',
'in_service', 'completed', 'pending_acceptance', 'accepted_by_user',
'reviewed', 'settled', 'cancelled', 'exception',
'pending_assignment', 'pending_accept',
'waiting_departure', 'on_the_way', 'serving', 'pending_confirm',
'pending_submit', 'abnormal', 'exception_pending', 'archived'
)
);
-- ============================================================================
-- 2. 修复 delivery_build_order_json所有展示字段必须有兜底值
-- ============================================================================
DROP FUNCTION IF EXISTS public.delivery_build_order_json(JSONB, JSONB, JSONB, JSONB, JSONB, TEXT);
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, COALESCE((p_raw ->> 'price')::NUMERIC, 0))));
v_address JSONB := COALESCE(p_raw -> 'address_snapshot_json', COALESCE(p_raw -> 'address_snapshot', '{}'::jsonb));
v_raw_status TEXT := COALESCE(p_raw ->> 'status', 'pending_assignment');
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;
v_service_name TEXT;
v_address_text TEXT;
v_address_detail TEXT;
v_full_address TEXT;
v_appointment_time TEXT;
v_staff_income NUMERIC;
v_price NUMERIC;
BEGIN
-- 优先从 service_snapshot_json 获取 category/price
IF v_service ->> 'category' = '' THEN
v_service := v_service || jsonb_build_object(
'category', COALESCE(NULLIF(p_raw ->> 'service_category', ''), '居家服务'),
'price', COALESCE(
NULLIF(p_raw ->> 'price', '')::NUMERIC,
NULLIF(p_raw ->> 'service_price', '')::NUMERIC,
0
)
);
END IF;
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 'pending_assignment'
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;
-- 【核心修复】serviceName 多级兜底
v_service_name := COALESCE(
NULLIF(p_raw ->> 'service_name', ''),
COALESCE(v_service ->> 'category', ''),
COALESCE(p_raw ->> 'service_category', ''),
'居家服务订单'
);
-- 【核心修复】address 多级兜底
v_address_text := COALESCE(
NULLIF(v_address ->> 'fullAddress', ''),
NULLIF(v_address ->> 'full_address', ''),
NULLIF(v_address ->> 'address', ''),
NULLIF(v_address ->> 'name', ''),
''
);
v_address_detail := COALESCE(
NULLIF(v_address ->> 'detailAddress', ''),
NULLIF(v_address ->> 'detail_address', ''),
''
);
IF v_address_text = '' THEN
v_address_text := '地址待补充';
END IF;
v_full_address := v_address_text;
IF v_address_detail != '' AND v_full_address !~ v_address_detail THEN
v_full_address := v_full_address || ' ' || v_address_detail;
END IF;
-- 【核心修复】appointmentTime 多级兜底
v_appointment_time := COALESCE(
NULLIF(p_raw ->> 'appointment_time', ''),
NULLIF(p_raw ->> 'scheduled_at', ''),
NULLIF(p_raw ->> 'appointment_start_time', ''),
COALESCE(to_char((p_raw ->> 'created_at')::timestamptz, 'YYYY-MM-DD HH24:MI:SS'), ''),
'时间待补充'
);
-- 【核心修复】staffIncome / price 多级兜底
v_price := COALESCE(
NULLIF(v_service ->> 'price', '')::NUMERIC,
NULLIF(p_raw ->> 'price', '')::NUMERIC,
NULLIF(p_raw ->> 'service_price', '')::NUMERIC,
0
);
v_staff_income := COALESCE(
NULLIF(p_raw ->> 'staff_income', '')::NUMERIC,
v_price,
0
);
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', v_service_name,
'serviceCategory', COALESCE(NULLIF(p_raw ->> 'service_category', ''), 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 ->> 'elder_name', ''),
NULLIF(p_raw ->> 'recipient_name', ''),
'服务对象待补充'
),
'elderNameMasked', COALESCE(
NULLIF(p_raw ->> 'elder_name', ''),
NULLIF(p_raw ->> 'recipient_name', ''),
'服务对象待补充'
),
'elderGender', '',
'elderAge', 0,
'elderPhone', COALESCE(
NULLIF(p_raw ->> 'elder_phone', ''),
NULLIF(p_raw ->> 'recipient_phone', ''),
''
),
'elderPhoneMasked', COALESCE(
NULLIF(p_raw ->> 'elder_phone', ''),
NULLIF(p_raw ->> 'recipient_phone', ''),
''
),
'fullElderName', COALESCE(
NULLIF(p_raw ->> 'elder_name', ''),
NULLIF(p_raw ->> 'recipient_name', ''),
'服务对象待补充'
),
'fullPhone', COALESCE(
NULLIF(p_raw ->> 'elder_phone', ''),
NULLIF(p_raw ->> 'recipient_phone', ''),
''
),
'contactRelation', '家属',
'addressSummary', v_address_text,
'address', v_address_text,
'addressDetail', v_address_detail,
'fullAddress', v_full_address,
'latitude', COALESCE(NULLIF(v_address ->> 'latitude', '')::NUMERIC, 0),
'longitude', COALESCE(NULLIF(v_address ->> 'longitude', '')::NUMERIC, 0),
'appointmentTime', v_appointment_time,
'appointmentStartTime', v_appointment_time,
'appointmentEndTime', v_appointment_time,
'duration', COALESCE(
NULLIF(p_raw ->> 'duration_minutes', '')::INTEGER,
90
),
'estimatedDuration', COALESCE(
NULLIF(p_raw ->> 'duration_minutes', '')::INTEGER,
90
),
'price', v_price,
'staffIncome', v_staff_income,
'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', '')
) || jsonb_build_object(
'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 '待验收' WHEN v_front_status = 'completed' THEN '已验收' ELSE '待评价' END,
'settlementStatus', CASE WHEN v_front_status = 'completed' THEN '已结算' ELSE '待确认' END,
'archiveStatus', '未归档',
'createdAt', COALESCE(p_raw ->> 'created_at', ''),
'updatedAt', COALESCE(p_raw ->> 'updated_at', ''),
'contactName', COALESCE(
NULLIF(p_raw ->> 'contact_name', ''),
'家属待补充'
),
'contactPhone', COALESCE(NULLIF(p_raw ->> 'contact_phone', ''), ''),
'requestId', COALESCE(NULLIF(p_raw ->> 'request_id', ''), '')
) || jsonb_build_object(
'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;
$$;
REVOKE ALL ON FUNCTION public.delivery_build_order_json(JSONB, JSONB, JSONB, JSONB, JSONB, TEXT) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.delivery_build_order_json(JSONB, JSONB, JSONB, JSONB, JSONB, TEXT) FROM anon;
GRANT EXECUTE ON FUNCTION public.delivery_build_order_json(JSONB, JSONB, JSONB, JSONB, JSONB, TEXT) TO authenticated;
-- ============================================================================
-- 3. 修复 delivery_get_legacy_order_json确保所有状态统一返回完整字段
-- ============================================================================
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;
$$;
REVOKE ALL ON FUNCTION public.delivery_get_legacy_order_json(TEXT) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.delivery_get_legacy_order_json(TEXT) FROM anon;
GRANT EXECUTE ON FUNCTION public.delivery_get_legacy_order_json(TEXT) TO authenticated;
-- ============================================================================
-- 4. 修复 rpc_delivery_order_listpending tab 必须包含 pending_assignment + pending_accept
-- ============================================================================
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 = pg_catalog, public, pg_temp
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_order JSONB;
v_order_id TEXT;
v_order_key TEXT;
v_seen_order_ids TEXT[] := ARRAY[]::TEXT[];
v_max_rows INTEGER := 200;
BEGIN
IF v_user_id IS NULL THEN
RAISE EXCEPTION USING
ERRCODE = '42501',
MESSAGE = 'delivery user context is required';
END IF;
IF v_staff_id IS NULL THEN
RAISE EXCEPTION USING
ERRCODE = '42501',
MESSAGE = 'delivery staff context is required';
END IF;
PERFORM public.delivery_assert_staff_access(v_staff_id);
-- 查询 hss_service_orders
IF public.delivery_table_exists('hss_service_orders') THEN
FOR v_order_id IN
SELECT o.id::text
FROM public.hss_service_orders o
WHERE o.deleted_at IS NULL
AND o.current_staff_id = v_staff_id
AND o.status IN (
'pending_assignment',
'pending_accept',
'accepted',
'waiting_departure',
'departed',
'on_the_way',
'arrived',
'in_service',
'serving',
'pending_acceptance',
'completed',
'settled',
'archived',
'abnormal',
'exception_pending'
)
ORDER BY COALESCE(o.updated_at, o.created_at) DESC, o.created_at DESC
LIMIT v_max_rows
LOOP
v_order := public.delivery_get_legacy_order_json(v_order_id);
IF v_order IS NULL THEN
CONTINUE;
END IF;
v_order_key := COALESCE(v_order ->> 'id', v_order_id);
IF v_order_key = ANY(v_seen_order_ids) THEN
CONTINUE;
END IF;
v_seen_order_ids := array_append(v_seen_order_ids, v_order_key);
v_result := public.delivery_append_if_match(
COALESCE(v_result, '[]'::jsonb),
v_order,
COALESCE(p_tab, 'all'),
COALESCE(p_keyword, '')
);
END LOOP;
END IF;
-- 兼容 ec_care_tasks 新链
IF public.delivery_table_exists('ec_care_tasks') THEN
BEGIN
FOR v_order_id IN EXECUTE
'SELECT t.id::text
FROM public.ec_care_tasks t
WHERE t.assigned_to = $1
AND t.status NOT IN (''ORDER_COMPLETED'', ''ORDER_CANCELLED'')
ORDER BY t.created_at DESC
LIMIT $2'
USING v_user_id, v_max_rows
LOOP
v_order := public.delivery_get_care_order_json(v_order_id);
IF v_order IS NULL THEN
CONTINUE;
END IF;
v_order_key := COALESCE(v_order ->> 'id', v_order_id);
IF v_order_key = ANY(v_seen_order_ids) THEN
CONTINUE;
END IF;
v_seen_order_ids := array_append(v_seen_order_ids, v_order_key);
v_result := public.delivery_append_if_match(
COALESCE(v_result, '[]'::jsonb),
v_order,
COALESCE(p_tab, 'all'),
COALESCE(p_keyword, '')
);
END LOOP;
EXCEPTION
WHEN undefined_table OR undefined_column OR undefined_function THEN
RAISE NOTICE 'Skip legacy ec_care_tasks compatibility query: %', SQLERRM;
END;
END IF;
RETURN COALESCE(v_result, '[]'::jsonb);
END;
$$;
REVOKE ALL ON FUNCTION public.rpc_delivery_order_list(UUID, TEXT, TEXT) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.rpc_delivery_order_list(UUID, TEXT, TEXT) FROM anon;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_order_list(UUID, TEXT, TEXT) TO authenticated;
-- ============================================================================
-- 5. 修复 rpc_delivery_dashboard同步更新
-- ============================================================================
CREATE OR REPLACE FUNCTION public.rpc_delivery_dashboard(p_staff_id UUID DEFAULT NULL)
RETURNS JSONB
LANGUAGE plpgsql
STABLE
SECURITY DEFINER
SET search_path = pg_catalog, public, pg_temp
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_orders JSONB := '[]'::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;
v_item JSONB;
v_order_id TEXT;
v_order_key TEXT;
v_seen_order_ids TEXT[] := ARRAY[]::TEXT[];
v_max_rows INTEGER := 200;
BEGIN
IF v_user_id IS NULL THEN
RAISE EXCEPTION USING
ERRCODE = '42501',
MESSAGE = 'delivery user context is required';
END IF;
IF v_staff_id IS NULL THEN
RAISE EXCEPTION USING
ERRCODE = '42501',
MESSAGE = 'delivery staff context is required';
END IF;
PERFORM public.delivery_assert_staff_access(v_staff_id);
IF public.delivery_table_exists('hss_service_orders') THEN
FOR v_order_id IN
SELECT o.id::text
FROM public.hss_service_orders o
WHERE o.deleted_at IS NULL
AND o.current_staff_id = v_staff_id
AND o.status IN (
'pending_assignment',
'pending_accept',
'accepted',
'waiting_departure',
'departed',
'on_the_way',
'arrived',
'in_service',
'serving',
'pending_acceptance',
'completed',
'settled',
'archived',
'abnormal',
'exception_pending'
)
ORDER BY COALESCE(o.updated_at, o.created_at) DESC, o.created_at DESC
LIMIT v_max_rows
LOOP
v_item := public.delivery_get_legacy_order_json(v_order_id);
IF v_item IS NULL THEN
CONTINUE;
END IF;
v_order_key := COALESCE(v_item ->> 'id', v_order_id);
IF v_order_key = ANY(v_seen_order_ids) THEN
CONTINUE;
END IF;
v_seen_order_ids := array_append(v_seen_order_ids, v_order_key);
v_orders := COALESCE(v_orders, '[]'::jsonb) || jsonb_build_array(v_item);
END LOOP;
END IF;
IF public.delivery_table_exists('ec_care_tasks') THEN
BEGIN
FOR v_order_id IN EXECUTE
'SELECT t.id::text
FROM public.ec_care_tasks t
WHERE t.assigned_to = $1
AND t.status NOT IN (''ORDER_COMPLETED'', ''ORDER_CANCELLED'')
ORDER BY t.created_at DESC
LIMIT $2'
USING v_user_id, v_max_rows
LOOP
v_item := public.delivery_get_care_order_json(v_order_id);
IF v_item IS NULL THEN
CONTINUE;
END IF;
v_order_key := COALESCE(v_item ->> 'id', v_order_id);
IF v_order_key = ANY(v_seen_order_ids) THEN
CONTINUE;
END IF;
v_seen_order_ids := array_append(v_seen_order_ids, v_order_key);
v_orders := COALESCE(v_orders, '[]'::jsonb) || jsonb_build_array(v_item);
END LOOP;
EXCEPTION
WHEN undefined_table OR undefined_column OR undefined_function THEN
RAISE NOTICE 'Skip legacy ec_care_tasks compatibility query: %', SQLERRM;
END;
END IF;
FOR v_item IN
SELECT value
FROM jsonb_array_elements(COALESCE(v_orders, '[]'::jsonb))
LOOP
IF v_item IS NULL THEN
CONTINUE;
END IF;
IF COALESCE(v_item ->> 'status', '') = 'pending_assignment' THEN
v_pending_assignment_count := v_pending_assignment_count + 1;
END IF;
IF COALESCE(v_item ->> 'status', '') = 'pending_accept' THEN
v_pending_accept_count := v_pending_accept_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 (
'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 ('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',
'exception_pending'
) 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;
$$;
REVOKE ALL ON FUNCTION public.rpc_delivery_dashboard(UUID) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.rpc_delivery_dashboard(UUID) FROM anon;
GRANT EXECUTE ON FUNCTION public.rpc_delivery_dashboard(UUID) TO authenticated;
COMMIT;