修复订单显示bug
This commit is contained in:
765
mall_sql/migrations/20260610_fix_delivery_pending_blank.sql
Normal file
765
mall_sql/migrations/20260610_fix_delivery_pending_blank.sql
Normal file
@@ -0,0 +1,765 @@
|
||||
-- =====================================================================================
|
||||
-- 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_list:pending 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;
|
||||
Reference in New Issue
Block a user