忽略本地报错信息文件

This commit is contained in:
2026-06-17 10:20:43 +08:00
parent 6b11144366
commit 090362c32d
191 changed files with 5434 additions and 76452 deletions

View File

@@ -0,0 +1,563 @@
-- ============================================================================
-- 浼樺寲娲惧崟 RPC锛氫慨澶嶆椂闂村啿绐佹鏌ヨ繃浜庝弗鏍肩殑闂
-- 鎵ц鏃堕棿: 2026-06-10
-- 闂: 褰撹鍗?scheduled_start_at/scheduled_end_at 涓?NULL 鏃讹紝
-- 浠讳綍鏈夋椿璺冧换鍔$殑浜哄憳閮戒細琚帓闄わ紝鍗充娇鏃堕棿鏍规湰涓嶅啿绐?
-- ============================================================================
CREATE OR REPLACE FUNCTION public.rpc_homecare_auto_dispatch_optimized(
p_order_id TEXT DEFAULT NULL
)
RETURNS JSONB
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $function$
DECLARE
v_now TIMESTAMPTZ := clock_timestamp();
v_current_user_id UUID;
v_order public.hss_service_orders%ROWTYPE;
v_existing_assignment RECORD;
v_candidate RECORD;
v_candidate_found BOOLEAN := FALSE;
v_assignment_id TEXT;
v_status_log_id TEXT;
v_attempt_log_id TEXT;
v_failure_code TEXT;
v_failure_message TEXT;
v_retryable BOOLEAN := FALSE;
BEGIN
-- A. 鍩虹鍙傛暟涓庤韩浠芥牎楠岋紙鍚屽師鍑芥暟锛?
IF p_order_id IS NULL OR btrim(p_order_id) = '' THEN
RETURN jsonb_build_object(
'success', FALSE,
'code', 'ORDER_ID_REQUIRED',
'message', '璁㈠崟ID涓嶈兘涓虹┖',
'display_type', 'toast',
'retryable', FALSE
);
END IF;
IF auth.uid() IS NULL THEN
RETURN jsonb_build_object(
'success', FALSE,
'code', 'UNAUTHENTICATED',
'message', '鐧诲綍鐘舵€佸凡澶辨晥锛岃閲嶆柊鐧诲綍鍚庡啀鎿嶄綔',
'display_type', 'modal',
'retryable', FALSE
);
END IF;
SELECT u.id
INTO v_current_user_id
FROM public.ak_users u
WHERE u.auth_id = auth.uid()
LIMIT 1;
IF v_current_user_id IS NULL THEN
RETURN jsonb_build_object(
'success', FALSE,
'code', 'USER_PROFILE_NOT_FOUND',
'message', '褰撳墠璐︽埛淇℃伅寮傚父锛岃閲嶆柊鐧诲綍鎴栬仈绯诲鏈?,
'display_type', 'modal',
'retryable', FALSE
);
END IF;
-- B. 閿佸畾璁㈠崟
SELECT o.*
INTO v_order
FROM public.hss_service_orders o
WHERE o.id = p_order_id
AND o.deleted_at IS NULL
FOR UPDATE OF o;
IF NOT FOUND THEN
RETURN jsonb_build_object(
'success', FALSE,
'code', 'ORDER_NOT_FOUND',
'message', '',
'display_type', 'modal',
'retryable', FALSE
);
END IF;
IF v_order.user_id IS DISTINCT FROM v_current_user_id THEN
RETURN jsonb_build_object(
'success', FALSE,
'code', 'ORDER_ACCESS_DENIED',
'message', '',
'display_type', 'modal',
'retryable', FALSE
);
END IF;
-- C. 骞傜瓑澶勭悊
IF v_order.status = 'assigned'
AND v_order.current_assignment_id IS NOT NULL THEN
RETURN jsonb_build_object(
'success', TRUE,
'code', 'ALREADY_ASSIGNED',
'message', '?,
'display_type', 'none',
'retryable', FALSE,
'dispatch_status', 'assigned',
'order_id', p_order_id,
'assignment_id', v_order.current_assignment_id,
'staff_id', v_order.current_staff_id
);
END IF;
-- D. 鏀粯涓庤鍗曠姸鎬佹牎楠?
IF COALESCE(v_order.payment_status, 1) <> 2 THEN
RETURN jsonb_build_object(
'success', FALSE,
'code', 'ORDER_NOT_PAID',
'message', '璁㈠崟灏氭湭瀹屾垚鏀粯锛屾殏涓嶈兘瀹夋帓鏈嶅姟浜哄憳',
'display_type', 'modal',
'retryable', FALSE,
'dispatch_status', COALESCE(v_order.dispatch_status, 'pending'),
'order_id', p_order_id
);
END IF;
IF v_order.status NOT IN ('created', 'paid') THEN
RETURN jsonb_build_object(
'success', FALSE,
'code', 'ORDER_STATUS_NOT_DISPATCHABLE',
'message', '褰撳墠璁㈠崟鐘舵€佷笉鍏佽閲嶆柊娲惧崟',
'display_type', 'modal',
'retryable', FALSE,
'dispatch_status', COALESCE(v_order.dispatch_status, 'pending'),
'order_id', p_order_id
);
END IF;
-- E. 鍏煎鍘嗗彶鏁版嵁
SELECT
a.id,
a.staff_id,
a.station_id
INTO v_existing_assignment
FROM public.hss_service_assignments a
WHERE a.order_id = p_order_id
AND a.deleted_at IS NULL
AND a.status IN ('assigned', 'accepted', 'departed', 'arrived', 'serving', 'in_service')
ORDER BY a.assigned_at DESC
LIMIT 1;
IF FOUND THEN
UPDATE public.hss_service_orders
SET status = 'assigned',
dispatch_status = 'assigned',
current_assignment_id = v_existing_assignment.id,
current_staff_id = v_existing_assignment.staff_id,
dispatch_error_code = NULL,
dispatch_error_message = NULL,
dispatch_failed_at = NULL,
updated_at = v_now
WHERE id = p_order_id;
RETURN jsonb_build_object(
'success', TRUE,
'code', 'ALREADY_ASSIGNED_RECOVERED',
'message', '璁㈠崟宸插瓨鍦ㄦ湁鏁堟淳鍗曡褰曪紝绯荤粺宸插畬鎴愮姸鎬佸悓姝?,
'display_type', 'none',
'retryable', FALSE,
'dispatch_status', 'assigned',
'order_id', p_order_id,
'assignment_id', v_existing_assignment.id,
'staff_id', v_existing_assignment.staff_id,
'station_id', COALESCE(v_existing_assignment.station_id::text, '')
);
END IF;
-- F. 杩涘叆娲惧崟涓姸鎬?
UPDATE public.hss_service_orders
SET dispatch_status = 'dispatching',
dispatch_attempt_count = COALESCE(dispatch_attempt_count, 0) + 1,
dispatch_error_code = NULL,
dispatch_error_message = NULL,
dispatch_failed_at = NULL,
updated_at = v_now
WHERE id = p_order_id;
-- G. 銆愭牳蹇冧紭鍖栥€戞煡鎵惧彲娲惧崟鏈嶅姟浜哄憳
-- 鍏抽敭淇锛氬彧鏈夊綋璁㈠崟鏈夋槑纭殑鏃堕棿鑼冨洿鏃讹紝鎵嶆鏌ユ椂闂村啿绐?
-- 濡傛灉璁㈠崟娌℃湁 scheduled_start_at/scheduled_end_at锛屽垯涓嶆鏌ユ椂闂村啿绐?
SELECT
s.id,
s.station_id,
distance_calc.distance_km
INTO v_candidate
FROM public.ml_delivery_staff s
CROSS JOIN LATERAL (
SELECT
CASE
WHEN v_order.service_lat IS NULL
OR v_order.service_lng IS NULL
OR s.current_lat IS NULL
OR s.current_lng IS NULL
THEN NULL
ELSE (
6371 * 2 * asin(
sqrt(
power(
sin(
radians((s.current_lat - v_order.service_lat)::DOUBLE PRECISION) / 2
),
2
)
+
cos(radians(v_order.service_lat::DOUBLE PRECISION))
* cos(radians(s.current_lat::DOUBLE PRECISION))
* power(
sin(
radians((s.current_lng - v_order.service_lng)::DOUBLE PRECISION) / 2
),
2
)
)
)
)::NUMERIC(8,3)
END AS distance_km
) distance_calc
WHERE s.deleted_at IS NULL
AND s.status = 1
AND COALESCE(s.is_active, TRUE) = TRUE
AND s.online_status = 'online'
AND s.uid IS NOT NULL
AND (
v_order.dispatch_station_id IS NULL
OR s.station_id::TEXT = v_order.dispatch_station_id
)
AND (
v_order.required_qualification_code IS NULL
OR EXISTS (
SELECT 1
FROM public.hc_worker_qualifications q
WHERE q.staff_id::TEXT = s.id::TEXT
AND q.qualification_code = v_order.required_qualification_code
AND q.deleted_at IS NULL
AND q.status::TEXT IN ('1', 'active', 'approved', 'valid')
AND (q.valid_from IS NULL OR q.valid_from <= v_now)
AND (q.valid_until IS NULL OR q.valid_until >= v_now)
)
)
AND (
v_order.service_lat IS NULL
OR v_order.service_lng IS NULL
OR (
s.current_lat IS NOT NULL
AND s.current_lng IS NOT NULL
AND distance_calc.distance_km <= COALESCE(s.dispatch_radius_km, 20)
)
)
-- 銆愪慨澶嶇偣銆戝彧鏈夊綋璁㈠崟鏈夋槑纭椂闂磋寖鍥存椂锛屾墠妫€鏌ユ椂闂村啿绐?
AND (
v_order.scheduled_start_at IS NULL
OR v_order.scheduled_end_at IS NULL
OR NOT EXISTS (
SELECT 1
FROM public.hss_service_assignments existing_assignment
JOIN public.hss_service_orders existing_order
ON existing_order.id = existing_assignment.order_id
WHERE existing_assignment.staff_id::TEXT = s.id::TEXT
AND existing_assignment.deleted_at IS NULL
AND existing_assignment.status IN (
'assigned',
'accepted',
'departed',
'arrived',
'serving',
'in_service'
)
AND existing_order.scheduled_start_at IS NOT NULL
AND existing_order.scheduled_end_at IS NOT NULL
AND tstzrange(
existing_order.scheduled_start_at,
existing_order.scheduled_end_at,
'[)'
) && tstzrange(
v_order.scheduled_start_at,
v_order.scheduled_end_at,
'[)'
)
)
)
ORDER BY
CASE
WHEN v_order.dispatch_station_id IS NOT NULL
AND s.station_id::TEXT = v_order.dispatch_station_id
THEN 0
ELSE 1
END,
distance_calc.distance_km ASC NULLS LAST,
COALESCE(s.last_dispatched_at, '-infinity'::TIMESTAMPTZ) ASC,
COALESCE(s.updated_at, s.created_at) DESC
LIMIT 1
FOR UPDATE OF s;
v_candidate_found := FOUND;
-- H. 娌℃湁鎵惧埌鍊欓€変汉鍛樻椂锛岃瘑鍒叿浣撳け璐ュ師鍥?
IF NOT v_candidate_found THEN
IF NOT EXISTS (
SELECT 1
FROM public.ml_delivery_staff s
WHERE s.deleted_at IS NULL
AND s.status = 1
AND COALESCE(s.is_active, TRUE) = TRUE
AND s.online_status = 'online'
AND s.uid IS NOT NULL
) THEN
v_failure_code := 'NO_ONLINE_STAFF';
v_failure_message := '';
v_retryable := TRUE;
ELSIF v_order.dispatch_station_id IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM public.ml_delivery_staff s
WHERE s.deleted_at IS NULL
AND s.status = 1
AND COALESCE(s.is_active, TRUE) = TRUE
AND s.online_status = 'online'
AND s.uid IS NOT NULL
AND s.station_id::TEXT = v_order.dispatch_station_id
) THEN
v_failure_code := 'NO_STAFF_IN_SERVICE_STATION';
v_failure_message := '?;
v_retryable := TRUE;
ELSIF v_order.required_qualification_code IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM public.ml_delivery_staff s
WHERE s.deleted_at IS NULL
AND s.status = 1
AND COALESCE(s.is_active, TRUE) = TRUE
AND s.online_status = 'online'
AND s.uid IS NOT NULL
AND (
v_order.dispatch_station_id IS NULL
OR s.station_id::TEXT = v_order.dispatch_station_id
)
AND EXISTS (
SELECT 1
FROM public.hc_worker_qualifications q
WHERE q.staff_id::TEXT = s.id::TEXT
AND q.qualification_code = v_order.required_qualification_code
AND q.deleted_at IS NULL
AND q.status::TEXT IN ('1', 'active', 'approved', 'valid')
AND (q.valid_from IS NULL OR q.valid_from <= v_now)
AND (q.valid_until IS NULL OR q.valid_until >= v_now)
)
) THEN
v_failure_code := 'NO_QUALIFIED_STAFF';
v_failure_message := '褰撳墠鏆傛棤鍏峰璇ユ湇鍔¤祫璐ㄧ殑鏈嶅姟浜哄憳锛岃绋嶅悗閲嶈瘯鎴栬仈绯诲鏈?;
v_retryable := TRUE;
ELSIF v_order.service_lat IS NOT NULL
AND v_order.service_lng IS NOT NULL
AND NOT EXISTS (
SELECT 1
FROM public.ml_delivery_staff s
CROSS JOIN LATERAL (
SELECT
(
6371 * 2 * asin(
sqrt(
power(
sin(
radians((s.current_lat - v_order.service_lat)::DOUBLE PRECISION) / 2
),
2
)
+
cos(radians(v_order.service_lat::DOUBLE PRECISION))
* cos(radians(s.current_lat::DOUBLE PRECISION))
* power(
sin(
radians((s.current_lng - v_order.service_lng)::DOUBLE PRECISION) / 2
),
2
)
)
)
)::NUMERIC(8,3) AS distance_km
) distance_calc
WHERE s.deleted_at IS NULL
AND s.status = 1
AND COALESCE(s.is_active, TRUE) = TRUE
AND s.online_status = 'online'
AND s.uid IS NOT NULL
AND s.current_lat IS NOT NULL
AND s.current_lng IS NOT NULL
AND (
v_order.dispatch_station_id IS NULL
OR s.station_id::TEXT = v_order.dispatch_station_id
)
AND (
v_order.required_qualification_code IS NULL
OR EXISTS (
SELECT 1
FROM public.hc_worker_qualifications q
WHERE q.staff_id::TEXT = s.id::TEXT
AND q.qualification_code = v_order.required_qualification_code
AND q.deleted_at IS NULL
AND q.status::TEXT IN ('1', 'active', 'approved', 'valid')
AND (q.valid_from IS NULL OR q.valid_from <= v_now)
AND (q.valid_until IS NULL OR q.valid_until >= v_now)
)
)
AND distance_calc.distance_km <= COALESCE(s.dispatch_radius_km, 20)
) THEN
v_failure_code := 'NO_NEARBY_STAFF';
v_failure_message := '';
v_retryable := TRUE;
ELSE
v_failure_code := 'ALL_ELIGIBLE_STAFF_BUSY';
v_failure_message := '';
v_retryable := TRUE;
END IF;
UPDATE public.hss_service_orders
SET dispatch_status = 'failed',
dispatch_error_code = v_failure_code,
dispatch_error_message = v_failure_message,
dispatch_failed_at = v_now,
updated_at = v_now
WHERE id = p_order_id;
v_attempt_log_id :=
'dalog-'
|| floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT
|| '-'
|| upper(substr(md5(p_order_id || ':' || random()::TEXT), 1, 10));
INSERT INTO public.hss_service_dispatch_attempt_logs (
id,
order_id,
requested_by_user_id,
selected_staff_id,
selected_station_id,
success,
result_code,
result_message,
retryable,
filters_snapshot,
created_at
) VALUES (
v_attempt_log_id,
p_order_id,
v_current_user_id,
NULL,
v_order.dispatch_station_id,
FALSE,
v_failure_code,
v_failure_message,
v_retryable,
jsonb_build_object(
'required_qualification_code', v_order.required_qualification_code,
'dispatch_station_id', v_order.dispatch_station_id,
'service_lat', v_order.service_lat,
'service_lng', v_order.service_lng,
'scheduled_start_at', v_order.scheduled_start_at,
'scheduled_end_at', v_order.scheduled_end_at
),
v_now
);
RETURN jsonb_build_object(
'success', FALSE,
'code', v_failure_code,
'message', v_failure_message,
'display_type', 'modal',
'retryable', v_retryable,
'dispatch_status', 'failed',
'order_id', p_order_id
);
END IF;
-- I. 娲惧崟鎴愬姛锛屽垱寤烘淳鍗曡褰?
v_assignment_id :=
'dsa-'
|| floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT
|| '-'
|| upper(substr(md5(p_order_id || ':' || v_candidate.id::TEXT), 1, 10));
INSERT INTO public.hss_service_assignments (
id,
order_id,
staff_id,
station_id,
status,
assigned_at,
created_at
) VALUES (
v_assignment_id,
p_order_id,
v_candidate.id,
v_candidate.station_id,
'assigned',
v_now,
v_now
);
UPDATE public.hss_service_orders
SET status = 'assigned',
dispatch_status = 'assigned',
current_assignment_id = v_assignment_id,
current_staff_id = v_candidate.id,
updated_at = v_now
WHERE id = p_order_id;
v_status_log_id :=
'slg-'
|| floor(extract(epoch FROM v_now) * 1000)::BIGINT::TEXT
|| '-'
|| upper(substr(md5(p_order_id || ':assigned'), 1, 10));
INSERT INTO public.hss_service_order_status_logs (
id,
order_id,
from_status,
to_status,
operator_type,
operator_id,
remark,
created_at
) VALUES (
v_status_log_id,
p_order_id,
'created',
'assigned',
'system',
v_current_user_id,
'',
v_now
);
RETURN jsonb_build_object(
'success', TRUE,
'code', 'DISPATCH_ASSIGNED',
'message', '?,
'display_type', 'none',
'retryable', FALSE,
'dispatch_status', 'assigned',
'order_id', p_order_id,
'assignment_id', v_assignment_id,
'staff_id', v_candidate.id::TEXT,
'station_id', COALESCE(v_candidate.station_id::TEXT, '')
);
END;
$function$;
COMMENT ON FUNCTION public.rpc_homecare_auto_dispatch_optimized(TEXT) IS '浼樺寲鐗堟淳鍗昍PC锛氫慨澶嶆椂闂村啿绐佹鏌ヨ繃浜庝弗鏍肩殑闂锛屼粎褰撹鍗曟湁鏄庣鏃堕棿鑼冨洿鏃舵墠妫€鏌ユ椂闂村啿绐?;