From 1bf9d11c35f9672f1bb57023790aed4c4917c17c Mon Sep 17 00:00:00 2001 From: huangzhenbao <17818024429@163.com> Date: Wed, 27 May 2026 19:09:17 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=91=E9=80=9Aconsumer=E4=B8=8B=E5=8D=95?= =?UTF-8?q?=E4=B9=8B=E5=90=8E=E6=B4=BE=E5=8D=95=E7=BB=99delivery2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ges.consumer.2026-05-27T00-43-38-881Z.json | 465 +++++ .../homecare/homecare_foundation_v1.sql | 11 + .../20_rls/delivery/ak_delivery_rls_v2.sql | 19 +- .../rpc_delivery_homecare_actions_v1.sql | 11 + .../rpc_homecare_dispatch_candidate_v1.sql | 54 + .../20260515_delivery_staff_rpc_v2.sql | 49 + .../20260515_delivery_staff_v2_minimal.sql | 15 +- .../20260526_delivery_homecare_rpc_v1.sql | 1536 +++++++++++++++++ .../20260526_homecare_foundation_v1.sql | 336 ++++ pages.json | 5 +- services/homeServiceService.uts | 11 +- services/serviceOrderService.uts | 38 +- 报错信息.txt | 440 +---- 13 files changed, 2554 insertions(+), 436 deletions(-) create mode 100644 .pages-backup/pages.consumer.2026-05-27T00-43-38-881Z.json create mode 100644 docs/sql/10_schema/homecare/homecare_foundation_v1.sql create mode 100644 docs/sql/30_rpc/delivery/rpc_delivery_homecare_actions_v1.sql create mode 100644 docs/sql/30_rpc/delivery/rpc_homecare_dispatch_candidate_v1.sql create mode 100644 mall_sql/migrations/20260526_delivery_homecare_rpc_v1.sql create mode 100644 mall_sql/migrations/20260526_homecare_foundation_v1.sql diff --git a/.pages-backup/pages.consumer.2026-05-27T00-43-38-881Z.json b/.pages-backup/pages.consumer.2026-05-27T00-43-38-881Z.json new file mode 100644 index 00000000..b534020b --- /dev/null +++ b/.pages-backup/pages.consumer.2026-05-27T00-43-38-881Z.json @@ -0,0 +1,465 @@ +{ + "pages": [ + { + "path": "pages/main/index", + "style": { + "navigationBarTitleText": "首页", + "navigationStyle": "custom", + "enablePullDownRefresh": false + } + }, + { + "path": "pages/user/boot", + "style": { + "navigationBarTitleText": "" + } + }, + { + "path": "pages/user/login", + "style": { + "navigationBarTitleText": "登录", + "navigationBarBackgroundColor": "#ffffff", + "navigationBarTextStyle": "black" + } + }, + { + "path": "pages/user/register", + "style": { + "navigationBarTitleText": "注册" + } + }, + { + "path": "pages/user/forgot-password", + "style": { + "navigationBarTitleText": "忘记密码" + } + }, + { + "path": "pages/user/terms", + "style": { + "navigationBarTitleText": "用户协议与隐私政策" + } + }, + { + "path": "pages/user/center", + "style": { + "navigationBarTitleText": "用户中心" + } + }, + { + "path": "pages/user/profile", + "style": { + "navigationBarTitleText": "个人资料" + } + }, + { + "path": "pages/user/change-password", + "style": { + "navigationBarTitleText": "修改密码" + } + }, + { + "path": "pages/user/bind-phone", + "style": { + "navigationBarTitleText": "绑定手机" + } + }, + { + "path": "pages/user/bind-email", + "style": { + "navigationBarTitleText": "绑定邮箱" + } + }, + { + "path": "pages/main/messages", + "style": { + "navigationBarTitleText": "消息", + "enablePullDownRefresh": true + } + }, + { + "path": "pages/main/cart", + "style": { + "navigationBarTitleText": "购物车", + "navigationStyle": "custom" + } + }, + { + "path": "pages/main/profile", + "style": { + "navigationBarTitleText": "我的", + "navigationStyle": "custom" + } + }, + { + "path": "pages/main/category", + "style": { + "navigationBarTitleText": "分类", + "navigationStyle": "custom" + } + } + ], + "subPackages": [ + { + "root": "pages/mall/consumer", + "pages": [ + { + "path": "settings", + "style": { + "navigationBarTitleText": "设置" + } + }, + { + "path": "edit-profile", + "style": { + "navigationBarTitleText": "编辑资料" + } + }, + { + "path": "wallet", + "style": { + "navigationBarTitleText": "我的钱包" + } + }, + { + "path": "withdraw", + "style": { + "navigationBarTitleText": "余额提现" + } + }, + { + "path": "search", + "style": { + "navigationBarTitleText": "搜索", + "navigationStyle": "custom" + } + }, + { + "path": "product-detail", + "style": { + "navigationBarTitleText": "", + "navigationStyle": "custom" + } + }, + { + "path": "channel-detail", + "style": { + "navigationBarTitleText": "频道详情", + "navigationStyle": "custom" + } + }, + { + "path": "shop-detail", + "style": { + "navigationBarTitleText": "店铺详情" + } + }, + { + "path": "coupons", + "style": { + "navigationBarTitleText": "我的优惠券" + } + }, + { + "path": "favorites", + "style": { + "navigationBarTitleText": "我的收藏" + } + }, + { + "path": "footprint", + "style": { + "navigationBarTitleText": "我的足迹" + } + }, + { + "path": "address", + "style": { + "navigationBarTitleText": "地址" + } + }, + { + "path": "address-list", + "style": { + "navigationBarTitleText": "收货地址" + } + }, + { + "path": "address-edit", + "style": { + "navigationBarTitleText": "编辑地址" + } + }, + { + "path": "checkout", + "style": { + "navigationBarTitleText": "确认订单" + } + }, + { + "path": "payment", + "style": { + "navigationBarTitleText": "", + "navigationStyle": "custom" + } + }, + { + "path": "payment-success", + "style": { + "navigationBarTitleText": "支付成功", + "navigationStyle": "custom" + } + }, + { + "path": "orders", + "style": { + "navigationBarTitleText": "", + "navigationStyle": "custom", + "enablePullDownRefresh": true, + "backgroundColor": "#f5f5f5" + } + }, + { + "path": "order-detail", + "style": { + "navigationBarTitleText": "订单详情" + } + }, + { + "path": "logistics", + "style": { + "navigationBarTitleText": "物流详情" + } + }, + { + "path": "review", + "style": { + "navigationBarTitleText": "评价晒单" + } + }, + { + "path": "refund", + "style": { + "navigationBarTitleText": "退款/售后" + } + }, + { + "path": "apply-refund", + "style": { + "navigationBarTitleText": "申请售后" + } + }, + { + "path": "refund-review", + "style": { + "navigationBarTitleText": "服务评价" + } + }, + { + "path": "chat", + "style": { + "navigationBarTitleText": "客服聊天", + "navigationStyle": "custom" + } + }, + { + "path": "chat_new", + "style": { + "navigationBarTitleText": "客服聊天(新版)" + } + }, + { + "path": "subscription/plan-list", + "style": { + "navigationBarTitleText": "软件订阅" + } + }, + { + "path": "subscription/plan-detail", + "style": { + "navigationBarTitleText": "订阅详情" + } + }, + { + "path": "subscription/subscribe-checkout", + "style": { + "navigationBarTitleText": "确认订阅" + } + }, + { + "path": "subscription/my-subscriptions", + "style": { + "navigationBarTitleText": "我的订阅" + } + }, + { + "path": "subscription/followed-shops", + "style": { + "navigationBarTitleText": "关注店铺" + } + }, + { + "path": "points/index", + "style": { + "navigationBarTitleText": "积分管理" + } + }, + { + "path": "points/signin", + "style": { + "navigationBarTitleText": "签到" + } + }, + { + "path": "points/exchange", + "style": { + "navigationBarTitleText": "积分兑换" + } + }, + { + "path": "points/exchange-records", + "style": { + "navigationBarTitleText": "兑换记录" + } + }, + { + "path": "red-packets/index", + "style": { + "navigationBarTitleText": "我的红包" + } + }, + { + "path": "bank-cards/index", + "style": { + "navigationBarTitleText": "银行卡管理" + } + }, + { + "path": "bank-cards/add", + "style": { + "navigationBarTitleText": "添加银行卡" + } + }, + { + "path": "home-service/index", + "style": { + "navigationBarTitleText": "居家上门服务", + "navigationStyle": "custom" + } + }, + { + "path": "home-service/apply", + "style": { + "navigationBarTitleText": "提交服务申请", + "navigationStyle": "custom" + } + }, + { + "path": "home-service/service-detail", + "style": { + "navigationBarTitleText": "预约服务", + "navigationStyle": "custom" + } + }, + { + "path": "home-service/order-detail", + "style": { + "navigationBarTitleText": "服务单详情", + "navigationStyle": "custom" + } + }, + { + "path": "home-service/feedback", + "style": { + "navigationBarTitleText": "验收反馈", + "navigationStyle": "custom" + } + }, + { + "path": "bank-cards/verify", + "style": { + "navigationBarTitleText": "银行卡验证" + } + }, + { + "path": "balance/index", + "style": { + "navigationBarTitleText": "余额" + } + }, + { + "path": "my-reviews", + "style": { + "navigationBarTitleText": "我的评价" + } + }, + { + "path": "message-detail", + "style": { + "navigationBarTitleText": "消息详情" + } + }, + { + "path": "member/index", + "style": { + "navigationBarTitleText": "会员中心" + } + }, + { + "path": "product-reviews", + "style": { + "navigationBarTitleText": "商品评价" + } + } + ] + } + ], + "tabBar": { + "color": "#999999", + "selectedColor": "#ff5000", + "backgroundColor": "#ffffff", + "borderStyle": "black", + "list": [ + { + "pagePath": "pages/main/index", + "text": "首页", + "iconPath": "static/tabbar/home.png", + "selectedIconPath": "static/tabbar/home-active.png" + }, + { + "pagePath": "pages/main/messages", + "text": "消息", + "iconPath": "static/tabbar/message.png", + "selectedIconPath": "static/tabbar/message.png" + }, + { + "pagePath": "pages/main/cart", + "text": "购物车", + "iconPath": "static/tabbar/cart.png", + "selectedIconPath": "static/tabbar/cart.png" + }, + { + "pagePath": "pages/main/profile", + "text": "我的", + "iconPath": "static/tabbar/user.png", + "selectedIconPath": "static/tabbar/user.png" + } + ] + }, + "globalStyle": { + "navigationBarTextStyle": "black", + "navigationBarTitleText": "mall", + "navigationBarBackgroundColor": "#FFFFFF", + "backgroundColor": "#F8F8F8" + }, + "condition": { + "current": 0, + "list": [ + { + "name": "consumer端", + "path": "pages/main/index", + "query": "role=consumer" + } + ] + } +} diff --git a/docs/sql/10_schema/homecare/homecare_foundation_v1.sql b/docs/sql/10_schema/homecare/homecare_foundation_v1.sql new file mode 100644 index 00000000..47e338b6 --- /dev/null +++ b/docs/sql/10_schema/homecare/homecare_foundation_v1.sql @@ -0,0 +1,11 @@ +-- ===================================================================================== +-- Schema: homecare foundation +-- Version: v1 +-- Purpose: 为 ec_service_requests / ec_care_tasks / ec_care_records / hc_* 新链补齐最小结构、索引与基础 RLS。 +-- Coverage: +-- 1. consumer 下单与自动派单字段 +-- 2. consumer 验收 / 退回整改 / 评价字段 +-- 3. delivery 执行记录、异常、证据、事件字段 +-- ===================================================================================== + +-- 执行稿见:mall_sql/migrations/20260526_homecare_foundation_v1.sql \ No newline at end of file diff --git a/docs/sql/20_rls/delivery/ak_delivery_rls_v2.sql b/docs/sql/20_rls/delivery/ak_delivery_rls_v2.sql index 4dd9e327..cbfdc15f 100644 --- a/docs/sql/20_rls/delivery/ak_delivery_rls_v2.sql +++ b/docs/sql/20_rls/delivery/ak_delivery_rls_v2.sql @@ -11,7 +11,6 @@ ALTER TABLE public.ml_delivery_stations ENABLE ROW LEVEL SECURITY; -- 清理旧策略 DROP POLICY IF EXISTS delivery_staff_self_select ON public.ml_delivery_staff; -DROP POLICY IF EXISTS delivery_staff_assignable_select ON public.ml_delivery_staff; DROP POLICY IF EXISTS delivery_staff_self_update ON public.ml_delivery_staff; DROP POLICY IF EXISTS delivery_stations_select_active ON public.ml_delivery_stations; @@ -30,20 +29,7 @@ CREATE POLICY delivery_staff_self_select ) ); --- 2. 已登录用户仅可读取可派单的在线服务人员,用于自动派单 -CREATE POLICY delivery_staff_assignable_select - ON public.ml_delivery_staff - FOR SELECT - TO authenticated - USING ( - deleted_at IS NULL - AND status = 1 - AND COALESCE(is_active, true) = true - AND online_status = 'online' - AND uid IS NOT NULL - ); - --- 3. 执行人员本人可更新自己的在线状态等自有档案字段 +-- 2. 执行人员本人可更新自己的在线状态等自有档案字段 CREATE POLICY delivery_staff_self_update ON public.ml_delivery_staff FOR UPDATE @@ -67,11 +53,12 @@ CREATE POLICY delivery_staff_self_update ) ); --- 4. 提货点/机构对前台保持只读,仅返回启用且未删除数据 +-- 3. 提货点/机构对前台保持只读,仅返回启用且未删除数据 CREATE POLICY delivery_stations_select_active ON public.ml_delivery_stations FOR SELECT TO anon, authenticated USING (status = 1 AND deleted_at IS NULL); +-- 4. 派单候选人读取统一走 SECURITY DEFINER RPC,不再开放公开可派单列表 RLS -- 5. 其余直连写操作默认不开放,管理端统一走 SECURITY DEFINER RPC diff --git a/docs/sql/30_rpc/delivery/rpc_delivery_homecare_actions_v1.sql b/docs/sql/30_rpc/delivery/rpc_delivery_homecare_actions_v1.sql new file mode 100644 index 00000000..c96e0911 --- /dev/null +++ b/docs/sql/30_rpc/delivery/rpc_delivery_homecare_actions_v1.sql @@ -0,0 +1,11 @@ +-- ===================================================================================== +-- RPC: rpc_delivery_* homecare actions +-- Version: v1 +-- Purpose: 为 delivery 页面当前 api/delivery.uts 已固定的 rpc_delivery_* 契约补齐 SQL 端实现。 +-- Notes: +-- 1. 优先兼容 ec/hc 新链,检测不到表或执行失败时回退 hss 旧链。 +-- 2. 返回 JSON 结构直接对齐 delivery/types/delivery.uts。 +-- 3. message_list 与 record_list 采用最小可用实现,避免前端继续落入 fallback。 +-- ===================================================================================== + +-- 迁移正文见同批文件:mall_sql/migrations/20260526_delivery_homecare_rpc_v1.sql \ No newline at end of file diff --git a/docs/sql/30_rpc/delivery/rpc_homecare_dispatch_candidate_v1.sql b/docs/sql/30_rpc/delivery/rpc_homecare_dispatch_candidate_v1.sql new file mode 100644 index 00000000..e237e507 --- /dev/null +++ b/docs/sql/30_rpc/delivery/rpc_homecare_dispatch_candidate_v1.sql @@ -0,0 +1,54 @@ +-- ===================================================================================== +-- RPC: rpc_homecare_dispatch_candidate +-- Version: v1 +-- Purpose: 为居家上门服务返回单个可派单候选人,替代公开可派单人员 RLS。 +-- Security: SECURITY DEFINER + 固定 search_path +-- Depends: public.ak_users, public.ml_delivery_staff +-- ===================================================================================== + +CREATE OR REPLACE FUNCTION public.rpc_homecare_dispatch_candidate( + p_service_code TEXT DEFAULT NULL, + p_station_id UUID DEFAULT NULL +) +RETURNS JSONB +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $$ +DECLARE + v_candidate JSONB; +BEGIN + IF auth.uid() IS NULL OR NOT EXISTS ( + SELECT 1 + FROM public.ak_users u + WHERE u.auth_id = auth.uid() + ) THEN + RAISE EXCEPTION 'Permission denied'; + END IF; + + SELECT jsonb_build_object( + 'id', s.id, + 'uid', s.uid, + 'station_id', s.station_id, + 'status', s.status, + 'online_status', s.online_status, + 'updated_at', s.updated_at, + 'created_at', s.created_at + ) + INTO v_candidate + 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 (p_station_id IS NULL OR s.station_id = p_station_id) + ORDER BY COALESCE(s.updated_at, s.created_at) DESC, s.created_at DESC + LIMIT 1; + + RETURN v_candidate; +END; +$$; + +REVOKE ALL ON FUNCTION public.rpc_homecare_dispatch_candidate(TEXT, UUID) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION public.rpc_homecare_dispatch_candidate(TEXT, UUID) TO authenticated; \ No newline at end of file diff --git a/mall_sql/migrations/20260515_delivery_staff_rpc_v2.sql b/mall_sql/migrations/20260515_delivery_staff_rpc_v2.sql index 10656a53..57b0012a 100644 --- a/mall_sql/migrations/20260515_delivery_staff_rpc_v2.sql +++ b/mall_sql/migrations/20260515_delivery_staff_rpc_v2.sql @@ -173,4 +173,53 @@ $$; REVOKE ALL ON FUNCTION public.rpc_admin_get_delivery_staff_list(TEXT, SMALLINT, INTEGER, INTEGER) FROM PUBLIC; GRANT EXECUTE ON FUNCTION public.rpc_admin_get_delivery_staff_list(TEXT, SMALLINT, INTEGER, INTEGER) TO authenticated; +DROP FUNCTION IF EXISTS public.rpc_homecare_dispatch_candidate(TEXT, UUID); + +CREATE OR REPLACE FUNCTION public.rpc_homecare_dispatch_candidate( + p_service_code TEXT DEFAULT NULL, + p_station_id UUID DEFAULT NULL +) +RETURNS JSONB +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $$ +DECLARE + v_candidate JSONB; +BEGIN + IF auth.uid() IS NULL OR NOT EXISTS ( + SELECT 1 + FROM public.ak_users u + WHERE u.auth_id = auth.uid() + ) THEN + RAISE EXCEPTION 'Permission denied'; + END IF; + + SELECT jsonb_build_object( + 'id', s.id, + 'uid', s.uid, + 'station_id', s.station_id, + 'status', s.status, + 'online_status', s.online_status, + 'updated_at', s.updated_at, + 'created_at', s.created_at + ) + INTO v_candidate + 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 (p_station_id IS NULL OR s.station_id = p_station_id) + ORDER BY COALESCE(s.updated_at, s.created_at) DESC, s.created_at DESC + LIMIT 1; + + RETURN v_candidate; +END; +$$; + +REVOKE ALL ON FUNCTION public.rpc_homecare_dispatch_candidate(TEXT, UUID) FROM PUBLIC; +GRANT EXECUTE ON FUNCTION public.rpc_homecare_dispatch_candidate(TEXT, UUID) TO authenticated; + COMMIT; \ No newline at end of file diff --git a/mall_sql/migrations/20260515_delivery_staff_v2_minimal.sql b/mall_sql/migrations/20260515_delivery_staff_v2_minimal.sql index 171831eb..817dd88b 100644 --- a/mall_sql/migrations/20260515_delivery_staff_v2_minimal.sql +++ b/mall_sql/migrations/20260515_delivery_staff_v2_minimal.sql @@ -79,7 +79,6 @@ ALTER TABLE public.ml_delivery_staff ENABLE ROW LEVEL SECURITY; ALTER TABLE public.ml_delivery_stations ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS delivery_staff_self_select ON public.ml_delivery_staff; -DROP POLICY IF EXISTS delivery_staff_assignable_select ON public.ml_delivery_staff; DROP POLICY IF EXISTS delivery_staff_self_update ON public.ml_delivery_staff; CREATE POLICY delivery_staff_self_select ON public.ml_delivery_staff @@ -95,18 +94,6 @@ CREATE POLICY delivery_staff_self_select ) ); -CREATE POLICY delivery_staff_assignable_select - ON public.ml_delivery_staff - FOR SELECT - TO authenticated - USING ( - deleted_at IS NULL - AND status = 1 - AND COALESCE(is_active, true) = true - AND online_status = 'online' - AND uid IS NOT NULL - ); - CREATE POLICY delivery_staff_self_update ON public.ml_delivery_staff FOR UPDATE @@ -149,4 +136,6 @@ COMMENT ON COLUMN public.ml_delivery_staff.deleted_by IS '软删除操作人'; COMMENT ON COLUMN public.ml_delivery_stations.deleted_at IS '软删除时间'; COMMENT ON COLUMN public.ml_delivery_stations.deleted_by IS '软删除操作人'; +-- 派单候选人读取统一走 SECURITY DEFINER RPC,避免公开在线人员列表。 + COMMIT; diff --git a/mall_sql/migrations/20260526_delivery_homecare_rpc_v1.sql b/mall_sql/migrations/20260526_delivery_homecare_rpc_v1.sql new file mode 100644 index 00000000..3357c11b --- /dev/null +++ b/mall_sql/migrations/20260526_delivery_homecare_rpc_v1.sql @@ -0,0 +1,1536 @@ +BEGIN; + +-- ===================================================================================== +-- Delivery homecare action RPCs +-- Purpose: �?delivery 端补�?rpc_delivery_* 契约,优先兼�?ec/hc 新链,回退 hss 旧链�? +-- ===================================================================================== + +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 '待接�?; + WHEN 'pending_accept' THEN RETURN '待接�?; + WHEN 'accepted' THEN RETURN '待出�?; + WHEN 'waiting_departure' THEN RETURN '待出�?; + WHEN 'departed' THEN RETURN '已出�?; + WHEN 'on_the_way' THEN RETURN '途中'; + WHEN 'arrived' THEN RETURN '待签�?; + WHEN 'checked_in' THEN RETURN '已签�?; + WHEN 'in_service' THEN RETURN '服务�?; + WHEN 'serving' THEN RETURN '服务�?; + WHEN 'pending_confirm' THEN RETURN '待确�?; + WHEN 'pending_submit' THEN RETURN '待提�?; + WHEN 'pending_acceptance' THEN RETURN '待验�?; + WHEN 'completed' THEN RETURN '已完�?; + WHEN 'rejected' THEN RETURN '已拒�?; + WHEN 'abnormal' THEN RETURN '异常处理�?; + WHEN 'cancelled' THEN RETURN '已取�?; + WHEN 'settled' THEN RETURN '已结�?; + WHEN 'archived' THEN RETURN '已归�?; + ELSE RETURN '待接�?; + 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', '家属沟通反�?, '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 '待验�? 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(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 '服务人员出发�? || (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 '服务人员到达�? || (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', + '开始服�? + ); + 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', '开始服�?, 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', + '服务完成,等待用户验�? + ); + 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', '服务完成,等待用户验�?, 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 '已验�? WHEN COALESCE(v_item ->> 'status', '') = 'pending_acceptance' THEN '待验�? ELSE '待补�? 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; \ No newline at end of file diff --git a/mall_sql/migrations/20260526_homecare_foundation_v1.sql b/mall_sql/migrations/20260526_homecare_foundation_v1.sql new file mode 100644 index 00000000..826bd0ae --- /dev/null +++ b/mall_sql/migrations/20260526_homecare_foundation_v1.sql @@ -0,0 +1,336 @@ +BEGIN; + +-- 与 delivery 侧保持同版,供 consumer/delivery 双端使用同一套 homecare foundation schema。 + +CREATE TABLE IF NOT EXISTS public.ec_service_requests ( + id UUID PRIMARY KEY, + user_id UUID NOT NULL, + service_catalog_id TEXT NOT NULL DEFAULT '', + service_name TEXT NOT NULL DEFAULT '', + service_category TEXT NOT NULL DEFAULT '', + elder_name TEXT NOT NULL DEFAULT '', + elder_phone TEXT NOT NULL DEFAULT '', + contact_name TEXT NOT NULL DEFAULT '', + contact_phone TEXT NOT NULL DEFAULT '', + address_snapshot JSONB, + address_snapshot_json JSONB, + scheduled_at TIMESTAMPTZ, + remark TEXT NOT NULL DEFAULT '', + status TEXT NOT NULL DEFAULT 'ORDER_CREATED', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.ec_care_tasks ( + id UUID PRIMARY KEY, + task_no TEXT NOT NULL UNIQUE, + request_id UUID REFERENCES public.ec_service_requests(id) ON DELETE SET NULL, + user_id UUID NOT NULL, + assigned_to UUID, + service_catalog_id TEXT NOT NULL DEFAULT '', + service_name TEXT NOT NULL DEFAULT '', + service_category TEXT NOT NULL DEFAULT '', + service_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb, + elder_name TEXT NOT NULL DEFAULT '', + elder_phone TEXT NOT NULL DEFAULT '', + contact_name TEXT NOT NULL DEFAULT '', + contact_phone TEXT NOT NULL DEFAULT '', + address_snapshot JSONB, + address_snapshot_json JSONB, + scheduled_at TIMESTAMPTZ, + appointment_time TIMESTAMPTZ, + remark TEXT NOT NULL DEFAULT '', + status TEXT NOT NULL DEFAULT 'ORDER_CREATED', + reject_reason TEXT NOT NULL DEFAULT '', + accepted_at TIMESTAMPTZ, + departed_at TIMESTAMPTZ, + checked_in_at TIMESTAMPTZ, + service_started_at TIMESTAMPTZ, + service_completed_at TIMESTAMPTZ, + acceptance_pending_at TIMESTAMPTZ, + accepted_by_family_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.ec_care_records ( + id TEXT PRIMARY KEY, + task_id UUID NOT NULL REFERENCES public.ec_care_tasks(id) ON DELETE CASCADE, + record_type TEXT NOT NULL DEFAULT 'service_record', + care_record_type TEXT, + created_by UUID, + started_at TIMESTAMPTZ, + finished_at TIMESTAMPTZ, + checked_in_at TIMESTAMPTZ, + checkin_time TIMESTAMPTZ, + latitude DOUBLE PRECISION, + longitude DOUBLE PRECISION, + location_text TEXT NOT NULL DEFAULT '', + duration_minutes INTEGER NOT NULL DEFAULT 0, + actual_duration_minutes INTEGER NOT NULL DEFAULT 0, + service_items_json JSONB NOT NULL DEFAULT '[]'::jsonb, + service_content_json JSONB NOT NULL DEFAULT '[]'::jsonb, + service_summary TEXT NOT NULL DEFAULT '', + process_note TEXT NOT NULL DEFAULT '', + summary TEXT NOT NULL DEFAULT '', + content TEXT NOT NULL DEFAULT '', + remark TEXT NOT NULL DEFAULT '', + elder_status TEXT NOT NULL DEFAULT '', + health_metrics_json JSONB NOT NULL DEFAULT '{}'::jsonb, + materials_used TEXT NOT NULL DEFAULT '', + abnormal_note TEXT NOT NULL DEFAULT '', + photos_json JSONB NOT NULL DEFAULT '[]'::jsonb, + staff_remark TEXT NOT NULL DEFAULT '', + family_confirmation_json JSONB NOT NULL DEFAULT '{}'::jsonb, + rating INTEGER, + tags_json JSONB NOT NULL DEFAULT '[]'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.hc_work_order_events ( + id TEXT PRIMARY KEY, + task_id UUID NOT NULL REFERENCES public.ec_care_tasks(id) ON DELETE CASCADE, + from_status TEXT, + to_status TEXT NOT NULL DEFAULT '', + actor_id UUID, + actor_role TEXT NOT NULL DEFAULT '', + action TEXT NOT NULL DEFAULT '', + remark TEXT NOT NULL DEFAULT '', + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.hc_work_order_exceptions ( + id TEXT PRIMARY KEY, + task_id UUID NOT NULL REFERENCES public.ec_care_tasks(id) ON DELETE CASCADE, + exception_type TEXT NOT NULL DEFAULT 'other', + description TEXT NOT NULL DEFAULT '', + occurred_at TIMESTAMPTZ, + location_text TEXT NOT NULL DEFAULT '', + images_json JSONB NOT NULL DEFAULT '[]'::jsonb, + need_platform_intervention BOOLEAN NOT NULL DEFAULT false, + request_cancel_order BOOLEAN NOT NULL DEFAULT false, + request_reschedule BOOLEAN NOT NULL DEFAULT false, + created_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE TABLE IF NOT EXISTS public.hc_evidence_files ( + id TEXT PRIMARY KEY, + task_id UUID NOT NULL REFERENCES public.ec_care_tasks(id) ON DELETE CASCADE, + care_record_id TEXT, + phase TEXT NOT NULL DEFAULT 'service', + file_type TEXT NOT NULL DEFAULT 'image', + storage_path TEXT NOT NULL DEFAULT '', + file_url TEXT NOT NULL DEFAULT '', + latitude DOUBLE PRECISION, + longitude DOUBLE PRECISION, + captured_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +CREATE INDEX IF NOT EXISTS idx_ec_service_requests_user_created + ON public.ec_service_requests(user_id, created_at DESC); + +CREATE INDEX IF NOT EXISTS idx_ec_care_tasks_user_created + ON public.ec_care_tasks(user_id, created_at DESC); + +CREATE INDEX IF NOT EXISTS idx_ec_care_tasks_assigned_status + ON public.ec_care_tasks(assigned_to, status, created_at DESC); + +CREATE INDEX IF NOT EXISTS idx_ec_care_records_task_created + ON public.ec_care_records(task_id, created_at DESC); + +CREATE INDEX IF NOT EXISTS idx_hc_work_order_events_task_created + ON public.hc_work_order_events(task_id, created_at DESC); + +CREATE INDEX IF NOT EXISTS idx_hc_work_order_exceptions_task_created + ON public.hc_work_order_exceptions(task_id, created_at DESC); + +CREATE INDEX IF NOT EXISTS idx_hc_evidence_files_task_created + ON public.hc_evidence_files(task_id, created_at DESC); + +CREATE OR REPLACE FUNCTION public.tg_ec_set_updated_at() +RETURNS trigger +LANGUAGE plpgsql +AS $$ +BEGIN + NEW.updated_at = now(); + RETURN NEW; +END; +$$; + +DROP TRIGGER IF EXISTS trg_ec_service_requests_updated_at ON public.ec_service_requests; +CREATE TRIGGER trg_ec_service_requests_updated_at +BEFORE UPDATE ON public.ec_service_requests +FOR EACH ROW +EXECUTE FUNCTION public.tg_ec_set_updated_at(); + +DROP TRIGGER IF EXISTS trg_ec_care_tasks_updated_at ON public.ec_care_tasks; +CREATE TRIGGER trg_ec_care_tasks_updated_at +BEFORE UPDATE ON public.ec_care_tasks +FOR EACH ROW +EXECUTE FUNCTION public.tg_ec_set_updated_at(); + +DROP TRIGGER IF EXISTS trg_ec_care_records_updated_at ON public.ec_care_records; +CREATE TRIGGER trg_ec_care_records_updated_at +BEFORE UPDATE ON public.ec_care_records +FOR EACH ROW +EXECUTE FUNCTION public.tg_ec_set_updated_at(); + +DROP TRIGGER IF EXISTS trg_hc_work_order_exceptions_updated_at ON public.hc_work_order_exceptions; +CREATE TRIGGER trg_hc_work_order_exceptions_updated_at +BEFORE UPDATE ON public.hc_work_order_exceptions +FOR EACH ROW +EXECUTE FUNCTION public.tg_ec_set_updated_at(); + +ALTER TABLE public.ec_service_requests ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.ec_care_tasks ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.ec_care_records ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.hc_work_order_events ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.hc_work_order_exceptions ENABLE ROW LEVEL SECURITY; +ALTER TABLE public.hc_evidence_files ENABLE ROW LEVEL SECURITY; + +DROP POLICY IF EXISTS ec_service_requests_owner_select ON public.ec_service_requests; +CREATE POLICY ec_service_requests_owner_select + ON public.ec_service_requests + FOR SELECT + TO authenticated + USING (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); + +DROP POLICY IF EXISTS ec_service_requests_owner_insert ON public.ec_service_requests; +CREATE POLICY ec_service_requests_owner_insert + ON public.ec_service_requests + FOR INSERT + TO authenticated + WITH CHECK (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); + +DROP POLICY IF EXISTS ec_service_requests_owner_update ON public.ec_service_requests; +CREATE POLICY ec_service_requests_owner_update + ON public.ec_service_requests + FOR UPDATE + TO authenticated + USING (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())) + WITH CHECK (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); + +DROP POLICY IF EXISTS ec_care_tasks_owner_or_staff_select ON public.ec_care_tasks; +CREATE POLICY ec_care_tasks_owner_or_staff_select + ON public.ec_care_tasks + FOR SELECT + TO authenticated + USING ( + user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ); + +DROP POLICY IF EXISTS ec_care_tasks_owner_insert ON public.ec_care_tasks; +CREATE POLICY ec_care_tasks_owner_insert + ON public.ec_care_tasks + FOR INSERT + TO authenticated + WITH CHECK (user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid())); + +DROP POLICY IF EXISTS ec_care_tasks_owner_or_staff_update ON public.ec_care_tasks; +CREATE POLICY ec_care_tasks_owner_or_staff_update + ON public.ec_care_tasks + FOR UPDATE + TO authenticated + USING ( + user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + WITH CHECK ( + user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ); + +DROP POLICY IF EXISTS ec_care_records_task_access ON public.ec_care_records; +CREATE POLICY ec_care_records_task_access + ON public.ec_care_records + FOR ALL + TO authenticated + USING ( + task_id IN ( + SELECT t.id + FROM public.ec_care_tasks t + WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + ) + WITH CHECK ( + task_id IN ( + SELECT t.id + FROM public.ec_care_tasks t + WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + ); + +DROP POLICY IF EXISTS hc_work_order_events_task_access ON public.hc_work_order_events; +CREATE POLICY hc_work_order_events_task_access + ON public.hc_work_order_events + FOR ALL + TO authenticated + USING ( + task_id IN ( + SELECT t.id + FROM public.ec_care_tasks t + WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + ) + WITH CHECK ( + task_id IN ( + SELECT t.id + FROM public.ec_care_tasks t + WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + ); + +DROP POLICY IF EXISTS hc_work_order_exceptions_task_access ON public.hc_work_order_exceptions; +CREATE POLICY hc_work_order_exceptions_task_access + ON public.hc_work_order_exceptions + FOR ALL + TO authenticated + USING ( + task_id IN ( + SELECT t.id + FROM public.ec_care_tasks t + WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + ) + WITH CHECK ( + task_id IN ( + SELECT t.id + FROM public.ec_care_tasks t + WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + ); + +DROP POLICY IF EXISTS hc_evidence_files_task_access ON public.hc_evidence_files; +CREATE POLICY hc_evidence_files_task_access + ON public.hc_evidence_files + FOR ALL + TO authenticated + USING ( + task_id IN ( + SELECT t.id + FROM public.ec_care_tasks t + WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + ) + WITH CHECK ( + task_id IN ( + SELECT t.id + FROM public.ec_care_tasks t + WHERE t.user_id IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + OR t.assigned_to IN (SELECT id FROM public.ak_users WHERE auth_id = auth.uid()) + ) + ); + +COMMIT; \ No newline at end of file diff --git a/pages.json b/pages.json index b534020b..b2ea96e6 100644 --- a/pages.json +++ b/pages.json @@ -17,9 +17,8 @@ { "path": "pages/user/login", "style": { - "navigationBarTitleText": "登录", - "navigationBarBackgroundColor": "#ffffff", - "navigationBarTextStyle": "black" + "navigationBarTitleText": "用户登录", + "navigationStyle": "custom" } }, { diff --git a/services/homeServiceService.uts b/services/homeServiceService.uts index 3aefd4de..95913f50 100644 --- a/services/homeServiceService.uts +++ b/services/homeServiceService.uts @@ -645,7 +645,16 @@ async function listWorkerTaskIds(): Promise> { return result } } - const legacyResponse = await supa.from('hss_service_orders').select('id').eq('current_staff_id', userId).order('created_at', { ascending: false }).execute() + let staffProfileId = '' + const staffResponse = await supa.from('ml_delivery_staff').select('id').eq('uid', userId).limit(1).execute() + if (staffResponse.error == null && staffResponse.data != null) { + const staffRows = staffResponse.data as Array + if (staffRows.length > 0) { + staffProfileId = readString(staffRows[0], 'id') + } + } + const legacyStaffId = staffProfileId != '' ? staffProfileId : userId + const legacyResponse = await supa.from('hss_service_orders').select('id').eq('current_staff_id', legacyStaffId).order('created_at', { ascending: false }).execute() if (legacyResponse.error != null || legacyResponse.data == null) { return [] as Array } diff --git a/services/serviceOrderService.uts b/services/serviceOrderService.uts index b7debd11..92b7eaa3 100644 --- a/services/serviceOrderService.uts +++ b/services/serviceOrderService.uts @@ -27,6 +27,8 @@ export type CreateServiceOrderParams = { remark: string } +const HOMECARE_DISPATCH_CANDIDATE_RPC = 'rpc_homecare_dispatch_candidate' + function nowText(): string { return new Date().toISOString().replace('T', ' ').substring(0, 19) } @@ -254,36 +256,16 @@ function getStaffPriority(staff: any): number { } async function getAutoAssignableStaff(): Promise { - const staffResponse = await supa - .from('ml_delivery_staff') - .select('id, uid, station_id, status, deleted_at, is_active, online_status, updated_at, created_at') - .eq('status', 1) - .eq('online_status', 'online') - .execute() - if (staffResponse.error != null || staffResponse.data == null) { + try { + const rpcResponse: any = await supa.rpc(HOMECARE_DISPATCH_CANDIDATE_RPC, {} as any) + if (rpcResponse == null || rpcResponse.error != null || rpcResponse.data == null) { + return null + } + return rpcResponse.data + } catch (error) { + console.warn('getAutoAssignableStaff rpc failed', error) return null } - const rawStaffList = staffResponse.data as Array - let selected: any = null - let bestScore = -1 - let bestTime = '' - for (let i = 0; i < rawStaffList.length; i++) { - const staff = rawStaffList[i] - if (!isStaffActive(staff)) { - continue - } - if (readString(staff, 'uid') == '') { - continue - } - const score = getStaffPriority(staff) - const timeMark = readFirstString(staff, ['updated_at', 'created_at']) - if (selected == null || score > bestScore || (score == bestScore && timeMark > bestTime)) { - selected = staff - bestScore = score - bestTime = timeMark - } - } - return selected } function buildEcServiceRequestPayload(params: CreateServiceOrderParams, userId: string, requestId: string, createdAt: string, appointmentTime: string | null, useAddressSnapshot: boolean): any { diff --git a/报错信息.txt b/报错信息.txt index 7efaac04..1c9a15f6 100644 --- a/报错信息.txt +++ b/报错信息.txt @@ -1,389 +1,79 @@ +[自动热重载] 已开启代码文件保存后自动热重载 mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ak_users filter: auth_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ak_users?select=*&auth_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) mp.esm.js:529 Profile Load Result: AkReqResponse {status: 200, data: Array(1), headers: Proxy, error: null, total: 1, …} mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_catalog filter: status=eq.1&deleted_at=is.null mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_catalog?select=id%2C%20name%2C%20category%2C%20price%2C%20duration_text%2C%20summary%2C%20tags_json%2C%20suitable_for%2C%20sort_no&order=sort_no.asc&status=eq.1&deleted_at=is.null -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_service_requests filter: null mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/ec_service_requests -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: return=representation -uni.api.esm.js:1042 POST http://119.146.131.237:9126/rest/v1/ec_service_requests 400 (Bad Request)(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ uni.api.esm.js:1042 -invokeApi @ uni.api.esm.js:330 -promiseApi @ uni.api.esm.js:889 -(anonymous) @ ak-req.uts:214 -doOnce @ ak-req.uts:213 -_loop$ @ ak-req.uts:328 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -s @ regeneratorRuntime.js?forceSync=true:1 -_ @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -request @ ak-req.uts:148 -_callee19$ @ aksupa.uts:1290 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -requestWithAutoRefresh @ aksupa.uts:1289 -_callee13$ @ aksupa.uts:1109 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -insert @ aksupa.uts:1084 -_callee$ @ aksupa.uts:469 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -execute @ aksupa.uts:369 -_callee5$ @ serviceOrderService.uts:754 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -tryCreateCareTask @ serviceOrderService.uts:742 -_callee7$ @ serviceOrderService.uts:874 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -createServiceOrder @ serviceOrderService.uts:873 -_callee4$ @ homeServiceService.uts:440 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -createHomeServiceApplication @ homeServiceService.uts:430 -_callee3$ @ service-detail.uvue:860 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -submitBooking @ service-detail.uvue:813 -callWithErrorHandling @ vue.runtime.esm.js:1356 -callWithAsyncErrorHandling @ vue.runtime.esm.js:1363 -invoke @ vue.runtime.esm.js:6223 -setTimeout (async) -invoker @ vue.runtime.esm.js:6232 -Show 28 more frames -mp.esm.js:529 [ak-req] HTTP error response(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:304 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] status: 400(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:305 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] url: http://119.146.131.237:9126/rest/v1/ec_service_requests(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:306 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] body: {"code":"PGRST204","details":null,"hint":null,"message":"Could not find the 'address_snapshot' column of 'ec_service_requests' in the schema cache"}(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:307 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_service_requests filter: null -mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/ec_service_requests -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: return=representation -uni.api.esm.js:1042 POST http://119.146.131.237:9126/rest/v1/ec_service_requests 400 (Bad Request)(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ uni.api.esm.js:1042 -invokeApi @ uni.api.esm.js:330 -promiseApi @ uni.api.esm.js:889 -(anonymous) @ ak-req.uts:214 -doOnce @ ak-req.uts:213 -_loop$ @ ak-req.uts:328 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -s @ regeneratorRuntime.js?forceSync=true:1 -_ @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -request @ ak-req.uts:148 -_callee19$ @ aksupa.uts:1290 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -requestWithAutoRefresh @ aksupa.uts:1289 -_callee13$ @ aksupa.uts:1109 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -insert @ aksupa.uts:1084 -_callee$ @ aksupa.uts:469 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -execute @ aksupa.uts:369 -_callee5$ @ serviceOrderService.uts:758 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -tryCreateCareTask @ serviceOrderService.uts:742 -_callee7$ @ serviceOrderService.uts:874 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -createServiceOrder @ serviceOrderService.uts:873 -_callee4$ @ homeServiceService.uts:440 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -createHomeServiceApplication @ homeServiceService.uts:430 -_callee3$ @ service-detail.uvue:860 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -submitBooking @ service-detail.uvue:813 -callWithErrorHandling @ vue.runtime.esm.js:1356 -callWithAsyncErrorHandling @ vue.runtime.esm.js:1363 -invoke @ vue.runtime.esm.js:6223 -setTimeout (async) -invoker @ vue.runtime.esm.js:6232 -mp.esm.js:529 [ak-req] HTTP error response(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:304 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] status: 400(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:305 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] url: http://119.146.131.237:9126/rest/v1/ec_service_requests(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:306 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] body: {"code":"PGRST204","details":null,"hint":null,"message":"Could not find the 'address_snapshot_json' column of 'ec_service_requests' in the schema cache"}(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:307 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_service_requests filter: null -mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/ec_service_requests -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: return=representation -uni.api.esm.js:1042 POST http://119.146.131.237:9126/rest/v1/ec_service_requests 400 (Bad Request)(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ uni.api.esm.js:1042 -invokeApi @ uni.api.esm.js:330 -promiseApi @ uni.api.esm.js:889 -(anonymous) @ ak-req.uts:214 -doOnce @ ak-req.uts:213 -_loop$ @ ak-req.uts:328 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -s @ regeneratorRuntime.js?forceSync=true:1 -_ @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -request @ ak-req.uts:148 -_callee19$ @ aksupa.uts:1290 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -requestWithAutoRefresh @ aksupa.uts:1289 -_callee13$ @ aksupa.uts:1109 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -insert @ aksupa.uts:1084 -_callee$ @ aksupa.uts:469 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -execute @ aksupa.uts:369 -_callee5$ @ serviceOrderService.uts:763 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -tryCreateCareTask @ serviceOrderService.uts:742 -_callee7$ @ serviceOrderService.uts:874 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -createServiceOrder @ serviceOrderService.uts:873 -_callee4$ @ homeServiceService.uts:440 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -createHomeServiceApplication @ homeServiceService.uts:430 -_callee3$ @ service-detail.uvue:860 -s @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -(anonymous) @ regeneratorRuntime.js?forceSync=true:1 -fulfilled @ uni.mp.esm.js:1134 -Promise.then (async) -step @ uni.mp.esm.js:1134 -(anonymous) @ uni.mp.esm.js:1134 -__awaiter @ uni.mp.esm.js:1134 -submitBooking @ service-detail.uvue:813 -callWithErrorHandling @ vue.runtime.esm.js:1356 -callWithAsyncErrorHandling @ vue.runtime.esm.js:1363 -invoke @ vue.runtime.esm.js:6223 -setTimeout (async) -invoker @ vue.runtime.esm.js:6232 -mp.esm.js:529 [ak-req] HTTP error response(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:304 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] status: 400(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:305 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] url: http://119.146.131.237:9126/rest/v1/ec_service_requests(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:306 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [ak-req] body: {"code":"PGRST204","details":null,"hint":null,"message":"Could not find the 'contact_name' column of 'ec_service_requests' in the schema cache"}(env: Windows,mp,1.06.2504030; lib: 3.16.0) -(anonymous) @ mp.esm.js:529 -__f__ @ uni.api.esm.js:590 -success @ ak-req.uts:307 -(anonymous) @ uni.api.esm.js:946 -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_orders filter: null -mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/hss_service_orders -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: return=representation -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_order_status_logs filter: null -mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/hss_service_order_status_logs -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: return=representation -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_delivery_staff filter: status=eq.1 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_delivery_staff?select=id%2C%20uid%2C%20station_id%2C%20nickname%2C%20phone%2C%20status%2C%20deleted_at%2C%20is_active%2C%20online_status%2C%20updated_at%2C%20created_at&status=eq.1 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_orders filter: id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_orders?select=*&limit=1&id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: count=exact -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_order_status_logs filter: order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_order_status_logs?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_reviews filter: order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_reviews?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_execution_records filter: order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_execution_records?select=*&limit=1&order=created_at.desc&order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: count=exact -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_evidence_files filter: order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_evidence_files?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: return=representation +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_care_tasks filter: null +mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/ec_care_tasks +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: return=representation +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hc_work_order_events filter: null +mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/hc_work_order_events +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: return=representation +mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/rpc/rpc_homecare_dispatch_candidate +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_care_tasks filter: id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] PATCH http://119.146.131.237:9126/rest/v1/ec_care_tasks?id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: return=representation +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hc_work_order_events filter: null +mp.esm.js:529 [ak-req] POST http://119.146.131.237:9126/rest/v1/hc_work_order_events +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: return=representation +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_care_tasks filter: id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ec_care_tasks?select=*&limit=1&id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: count=exact +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hc_work_order_events filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hc_work_order_events?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_care_records filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ec_care_records?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hc_evidence_files filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hc_evidence_files?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_delivery_staff filter: uid=eq.67251404-ad43-4118-a6d4-74ca16351427 +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_delivery_staff?select=nickname%2C%20phone&limit=1&uid=eq.67251404-ad43-4118-a6d4-74ca16351427 +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: count=exact [自动热重载] 已开启代码文件保存后自动热重载 2mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ak_users filter: auth_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ak_users?select=*&auth_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ak_users?select=*&auth_id=eq.b653fded-7d5e-4950-aa0d-725595543e3c -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) mp.esm.js:529 Profile Load Result: AkReqResponse {status: 200, data: Array(1), headers: Proxy, error: null, total: 1, …} -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_orders filter: id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_orders?select=*&limit=1&id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: count=exact -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_order_status_logs filter: order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_order_status_logs?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_care_tasks filter: id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ec_care_tasks?select=*&limit=1&id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: count=exact mp.esm.js:529 Profile Load Result: AkReqResponse {status: 200, data: Array(1), headers: Proxy, error: null, total: 1, …} -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_orders filter: id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_orders?select=*&limit=1&id=eq.so-1779788538646-23864 -mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: count=exact -mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hss_service_reviews filter: order_id=eq.so-1779788538646-23864 -​ [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_reviews?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -​ [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) -​ [AkSupaQueryBuilder] execute - 表: hss_service_execution_records filter: order_id=eq.so-1779788538646-23864 -​ [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_execution_records?select=*&limit=1&order=created_at.desc&order_id=eq.so-1779788538646-23864 -​ [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: count=exact -​ [AkSupaQueryBuilder] execute - 表: hss_service_evidence_files filter: order_id=eq.so-1779788538646-23864 -​ [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_evidence_files?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -​ [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_care_tasks filter: id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ec_care_tasks?select=*&limit=1&id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: count=exact +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hc_work_order_events filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hc_work_order_events?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hc_work_order_events filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hc_work_order_events?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_care_records filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ec_care_records?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ec_care_records filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ec_care_records?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hc_evidence_files filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hc_evidence_files?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_delivery_staff filter: uid=eq.67251404-ad43-4118-a6d4-74ca16351427 +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_delivery_staff?select=nickname%2C%20phone&limit=1&uid=eq.67251404-ad43-4118-a6d4-74ca16351427 +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: count=exact [pages/mall/consumer/home-service/order-detail] 提示: text 组件包含了长文本,可以考虑增加 user-select 属性,方便用户复制。 -​ [AkSupaQueryBuilder] execute - 表: hss_service_order_status_logs filter: order_id=eq.so-1779788538646-23864 -​ [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_order_status_logs?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -​ [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) -​ [AkSupaQueryBuilder] execute - 表: hss_service_reviews filter: order_id=eq.so-1779788538646-23864 -​ [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_reviews?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -​ [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) -​ [AkSupaQueryBuilder] execute - 表: hss_service_execution_records filter: order_id=eq.so-1779788538646-23864 -​ [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_execution_records?select=*&limit=1&order=created_at.desc&order_id=eq.so-1779788538646-23864 -​ [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: count=exact -​ [AkSupaQueryBuilder] execute - 表: hss_service_evidence_files filter: order_id=eq.so-1779788538646-23864 -​ [ak-req] GET http://119.146.131.237:9126/rest/v1/hss_service_evidence_files?select=*&order=created_at.desc&order_id=eq.so-1779788538646-23864 -​ [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...2Rsk | auth-mode: pre-set | prefer: (none) \ No newline at end of file +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: hc_evidence_files filter: task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/hc_evidence_files?select=*&order=created_at.desc&task_id=eq.56df7ffd-c64a-4455-b674-e8cee34f611b +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: (none) +mp.esm.js:529 [AkSupaQueryBuilder] execute - 表: ml_delivery_staff filter: uid=eq.67251404-ad43-4118-a6d4-74ca16351427 +mp.esm.js:529 [ak-req] GET http://119.146.131.237:9126/rest/v1/ml_delivery_staff?select=nickname%2C%20phone&limit=1&uid=eq.67251404-ad43-4118-a6d4-74ca16351427 +mp.esm.js:529 [ak-req] apikey: eyJhbG...7890 | Authorization: Bearer eyJhbG...qSAI | auth-mode: pre-set | prefer: count=exact \ No newline at end of file