From 83be5b3c66c2516af30ae24d40c36724e0b15e3f Mon Sep 17 00:00:00 2001 From: huangzhenbao <17818024429@163.com> Date: Thu, 28 May 2026 15:16:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=8E=A5=E5=8D=95=E5=90=8E?= =?UTF-8?q?=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ges.consumer.2026-05-28T00-40-52-431Z.json | 464 ++++++++++++++++++ ...8_add_elder_age_gender_and_fix_address.sql | 223 +++++++++ .../20260528_diagnose_delivery_rpc.sql | 267 ++++++++++ ...260528_quick_restore_delivery_function.sql | 216 ++++++++ .../20260528_readd_age_gender_columns.sql | 18 + ...528_rollback_delivery_build_order_json.sql | 208 ++++++++ pages/mall/consumer/address-list.uvue | 29 +- .../consumer/home-service/service-detail.uvue | 3 +- services/homeServiceService.uts | 4 +- services/serviceOrderService.uts | 16 + types/home-service.uts | 1 + types/service-order.uts | 2 + 报错信息.txt | 79 --- 13 files changed, 1437 insertions(+), 93 deletions(-) create mode 100644 .pages-backup/pages.consumer.2026-05-28T00-40-52-431Z.json create mode 100644 mall_sql/migrations/20260528_add_elder_age_gender_and_fix_address.sql create mode 100644 mall_sql/migrations/20260528_diagnose_delivery_rpc.sql create mode 100644 mall_sql/migrations/20260528_quick_restore_delivery_function.sql create mode 100644 mall_sql/migrations/20260528_readd_age_gender_columns.sql create mode 100644 mall_sql/migrations/20260528_rollback_delivery_build_order_json.sql diff --git a/.pages-backup/pages.consumer.2026-05-28T00-40-52-431Z.json b/.pages-backup/pages.consumer.2026-05-28T00-40-52-431Z.json new file mode 100644 index 00000000..b2ea96e6 --- /dev/null +++ b/.pages-backup/pages.consumer.2026-05-28T00-40-52-431Z.json @@ -0,0 +1,464 @@ +{ + "pages": [ + { + "path": "pages/main/index", + "style": { + "navigationBarTitleText": "首页", + "navigationStyle": "custom", + "enablePullDownRefresh": false + } + }, + { + "path": "pages/user/boot", + "style": { + "navigationBarTitleText": "" + } + }, + { + "path": "pages/user/login", + "style": { + "navigationBarTitleText": "用户登录", + "navigationStyle": "custom" + } + }, + { + "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/mall_sql/migrations/20260528_add_elder_age_gender_and_fix_address.sql b/mall_sql/migrations/20260528_add_elder_age_gender_and_fix_address.sql new file mode 100644 index 00000000..ad978a45 --- /dev/null +++ b/mall_sql/migrations/20260528_add_elder_age_gender_and_fix_address.sql @@ -0,0 +1,223 @@ +BEGIN; + +-- 1. 补充 ec_service_requests / ec_care_tasks / hss_service_orders 缺少的 age / gender 字段 +ALTER TABLE public.ec_service_requests + ADD COLUMN IF NOT EXISTS elder_age INTEGER NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS elder_gender TEXT NOT NULL DEFAULT ''; + +ALTER TABLE public.ec_care_tasks + ADD COLUMN IF NOT EXISTS elder_age INTEGER NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS elder_gender TEXT NOT NULL DEFAULT ''; + +ALTER TABLE public.hss_service_orders + ADD COLUMN IF NOT EXISTS recipient_age INTEGER NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS recipient_gender TEXT NOT NULL DEFAULT ''; + +-- 2. 修复 delivery_build_order_json:地址 snapshot 增加 address_snapshot fallback, elderGender / elderAge 从原始行读取 +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', p_raw -> 'address_snapshot', '{}'::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', COALESCE(NULLIF(p_raw ->> 'elder_gender', ''), p_raw ->> 'recipient_gender', ''), + 'elderAge', COALESCE((p_raw ->> 'elder_age')::INTEGER, (p_raw ->> 'recipient_age')::INTEGER, 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; +$$; + +COMMIT; diff --git a/mall_sql/migrations/20260528_diagnose_delivery_rpc.sql b/mall_sql/migrations/20260528_diagnose_delivery_rpc.sql new file mode 100644 index 00000000..2a7d5a0e --- /dev/null +++ b/mall_sql/migrations/20260528_diagnose_delivery_rpc.sql @@ -0,0 +1,267 @@ +-- ============================================ +-- 诊断脚本:排查 delivery RPC 超时问题 +-- 请在 Supabase SQL Editor 中逐条执行 +-- ============================================ + +-- 诊断1:确认 delivery_build_order_json 当前是 baseline 还是 patched 版本 +SELECT + proname, + CASE + WHEN prosrc LIKE '%p_raw -> ''address_snapshot''%' THEN 'PATCHED (含 address_snapshot fallback)' + WHEN prosrc LIKE '%''elderGender'', COALESCE(NULLIF(p_raw ->> ''elder_gender''%' THEN 'PATCHED (动态读取 gender/age)' + ELSE 'BASELINE (原始版本)' + END AS function_status, + md5(prosrc) AS function_md5, + LENGTH(prosrc) AS src_length +FROM pg_proc +WHERE proname = 'delivery_build_order_json'; + + +-- 诊断2:直接测试 delivery_build_order_json 是否能正常执行(不依赖任何表数据) +-- 如果这条语句能立即返回 JSONB 结果,说明函数本身没有问题 +SELECT delivery_build_order_json( + '{"id": "test123", "service_name": "测试服务", "status": "assigned", "address_snapshot_json": {"fullAddress": "测试地址"}}'::jsonb, + '[]'::jsonb, + '[]'::jsonb, + '[]'::jsonb, + NULL, + 'legacy' +) AS test_result; + + +-- 诊断3:查看 hss_service_orders 有多少条记录、有多少条已删除、有多少条分配给当前登录用户 +-- 注意:auth.uid() 在 SQL Editor 中为 NULL,所以 current_staff_id 匹配会返回空结果 +-- 这条只是确认表结构和数据量 +SELECT + COUNT(*) AS total_orders, + COUNT(*) FILTER (WHERE deleted_at IS NOT NULL) AS deleted_orders, + COUNT(*) FILTER (WHERE deleted_at IS NULL) AS active_orders, + COUNT(*) FILTER (WHERE current_staff_id IS NOT NULL) AS assigned_orders +FROM public.hss_service_orders; + + +-- 诊断4:查看 ec_care_tasks 有多少条记录(如果存在) +DO $$ +BEGIN + IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'ec_care_tasks') THEN + RAISE NOTICE 'ec_care_tasks exists, row count: %', (SELECT COUNT(*) FROM public.ec_care_tasks); + ELSE + RAISE NOTICE 'ec_care_tasks does NOT exist'; + END IF; +END $$; + + +-- 诊断5:强制刷新 PostgREST schema cache(如果怀疑是 schema 缓存问题) +-- 执行后等待 5-10 秒再测试前端 +NOTIFY pgrst, 'reload schema'; + + +-- 诊断6:如果诊断1显示函数是 PATCHED 版本,执行以下恢复语句(取消注释后运行) +-- 或者如果诊断2卡住/超时,说明 patched 版本有问题,也需要恢复 +/* +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; +$$; +*/ diff --git a/mall_sql/migrations/20260528_quick_restore_delivery_function.sql b/mall_sql/migrations/20260528_quick_restore_delivery_function.sql new file mode 100644 index 00000000..2c0435ba --- /dev/null +++ b/mall_sql/migrations/20260528_quick_restore_delivery_function.sql @@ -0,0 +1,216 @@ +-- ============================================ +-- 快速恢复:将 delivery_build_order_json 恢复为原始版本 +-- 请复制以下全部内容,在 Supabase SQL Editor 中执行 +-- ============================================ + +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', p_raw -> 'address_snapshot', '{}'::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', COALESCE(NULLIF(p_raw ->> 'elder_gender', ''), p_raw ->> 'recipient_gender', ''), + 'elderAge', COALESCE((p_raw ->> 'elder_age')::INTEGER, (p_raw ->> 'recipient_age')::INTEGER, 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) + ) || jsonb_build_object( + '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', '') + ) || jsonb_build_object( + '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) + ) || jsonb_build_object( + '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; +$$; + +-- 刷新 PostgREST schema cache(有时 schema 变更后需要刷新) +NOTIFY pgrst, 'reload schema'; diff --git a/mall_sql/migrations/20260528_readd_age_gender_columns.sql b/mall_sql/migrations/20260528_readd_age_gender_columns.sql new file mode 100644 index 00000000..a1b87056 --- /dev/null +++ b/mall_sql/migrations/20260528_readd_age_gender_columns.sql @@ -0,0 +1,18 @@ +-- ============================================ +-- 重新添加 age / gender 列(consumer 端提交预约需要) +-- 不影响 delivery 端链路 +-- ============================================ + +-- care 表 +ALTER TABLE public.ec_service_requests + ADD COLUMN IF NOT EXISTS elder_age INTEGER NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS elder_gender TEXT NOT NULL DEFAULT ''; + +ALTER TABLE public.ec_care_tasks + ADD COLUMN IF NOT EXISTS elder_age INTEGER NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS elder_gender TEXT NOT NULL DEFAULT ''; + +-- 旧订单表 +ALTER TABLE public.hss_service_orders + ADD COLUMN IF NOT EXISTS recipient_age INTEGER NOT NULL DEFAULT 0, + ADD COLUMN IF NOT EXISTS recipient_gender TEXT NOT NULL DEFAULT ''; diff --git a/mall_sql/migrations/20260528_rollback_delivery_build_order_json.sql b/mall_sql/migrations/20260528_rollback_delivery_build_order_json.sql new file mode 100644 index 00000000..ed7855b6 --- /dev/null +++ b/mall_sql/migrations/20260528_rollback_delivery_build_order_json.sql @@ -0,0 +1,208 @@ +BEGIN; +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; +$$; + +COMMIT; diff --git a/pages/mall/consumer/address-list.uvue b/pages/mall/consumer/address-list.uvue index 846f68b6..9a222800 100644 --- a/pages/mall/consumer/address-list.uvue +++ b/pages/mall/consumer/address-list.uvue @@ -37,6 +37,7 @@ import { ref, onMounted, getCurrentInstance } from 'vue' import { onShow, onLoad } from '@dcloudio/uni-app' import { supabaseService, type UserAddress as SupabaseUserAddress } from '@/utils/supabaseService.uts' +import { HomeServiceSelectedAddressType } from '@/types/home-service.uts' type Address = { id: string @@ -170,27 +171,31 @@ const editAddress = (id: string) => { const selectAddress = (item: Address) => { if (selectionMode.value) { - uni.$emit('addressSelected', { - id: item.id, + const fullAddressText = getFullAddress(item) + const selectedAddress = { addressId: item.id, userId: '', - recipient_name: item.name, + isDefault: item.isDefault, contactName: item.name, - phone: item.phone, contactPhone: item.phone, - province: item.province, - city: item.city, - district: item.district, - detail: item.detail, + phone: item.phone, + addressName: `${item.province}${item.city}${item.district}`, + locationName: `${item.province}${item.city}${item.district}`, addressDetail: item.detail, + locationAddress: `${item.province}${item.city}${item.district}`, houseNumber: item.detail, - fullAddress: getFullAddress(item), - remark: item.label ?? '', + doorNo: item.detail, + fullAddress: fullAddressText, latitude: item.latitude ?? 0, longitude: item.longitude ?? 0, + remark: item.label ?? '', coordinateType: item.coordinateType ?? 'gcj02', - is_default: item.isDefault - }) + createdAt: Date.now(), + updatedAt: Date.now() + } as HomeServiceSelectedAddressType + + uni.setStorageSync('hss_selected_service_address', selectedAddress) + uni.$emit('addressSelected', selectedAddress) uni.navigateBack() } else { editAddress(item.id) diff --git a/pages/mall/consumer/home-service/service-detail.uvue b/pages/mall/consumer/home-service/service-detail.uvue index 58044650..24ba786f 100644 --- a/pages/mall/consumer/home-service/service-detail.uvue +++ b/pages/mall/consumer/home-service/service-detail.uvue @@ -730,7 +730,7 @@ function selectAddress() { if (!ok) { return } - uni.navigateTo({ url: '/pages/address/address-list' }) + uni.navigateTo({ url: '/pages/mall/consumer/address-list?selectMode=true' }) }) } @@ -850,6 +850,7 @@ async function submitBooking() { applicantName: contactName.value, elderName: recipientName.value, age: parsedAge, + gender: recipientGender.value, phone: contactPhone.value, address: addressLineText.value, preferredTime: selectedTimeText.value, diff --git a/services/homeServiceService.uts b/services/homeServiceService.uts index 95913f50..587a7eb6 100644 --- a/services/homeServiceService.uts +++ b/services/homeServiceService.uts @@ -455,6 +455,8 @@ export async function createHomeServiceApplication(draft: HomeServiceApplication }, recipientName: draft.elderName, recipientPhone: draft.phone, + recipientAge: draft.age, + recipientGender: draft.gender, contactName: draft.applicantName, contactPhone: draft.phone, appointmentTime: draft.preferredTime, @@ -565,7 +567,7 @@ function mapOrderToCase(order: ServiceOrderType): HomeServiceCaseType { serviceTime: formatServiceAppointmentText(order.appointmentTime), applicantName: order.contactName, elderName: order.recipientName, - age: 0, + age: order.recipientAge, phone: order.contactPhone, address: order.addressSnapshot.fullAddress, summary: order.remark, diff --git a/services/serviceOrderService.uts b/services/serviceOrderService.uts index 92b7eaa3..71676a44 100644 --- a/services/serviceOrderService.uts +++ b/services/serviceOrderService.uts @@ -21,6 +21,8 @@ export type CreateServiceOrderParams = { address: ServiceOrderAddressSnapshotType recipientName: string recipientPhone: string + recipientAge: number + recipientGender: string contactName: string contactPhone: string appointmentTime: string @@ -277,6 +279,8 @@ function buildEcServiceRequestPayload(params: CreateServiceOrderParams, userId: service_category: params.service.category, elder_name: params.recipientName, elder_phone: params.recipientPhone, + elder_age: params.recipientAge, + elder_gender: params.recipientGender, contact_name: params.contactName, contact_phone: params.contactPhone, scheduled_at: appointmentTime, @@ -302,6 +306,8 @@ function buildEcServiceRequestPayloadWithoutAddress(params: CreateServiceOrderPa service_category: params.service.category, elder_name: params.recipientName, elder_phone: params.recipientPhone, + elder_age: params.recipientAge, + elder_gender: params.recipientGender, contact_name: params.contactName, contact_phone: params.contactPhone, scheduled_at: appointmentTime, @@ -324,6 +330,8 @@ function buildEcCareTaskPayload(params: CreateServiceOrderParams, userId: string service_snapshot_json: params.service as any, elder_name: params.recipientName, elder_phone: params.recipientPhone, + elder_age: params.recipientAge, + elder_gender: params.recipientGender, contact_name: params.contactName, contact_phone: params.contactPhone, scheduled_at: appointmentTime, @@ -352,6 +360,8 @@ function buildEcCareTaskPayloadWithoutAddress(params: CreateServiceOrderParams, service_snapshot_json: params.service as any, elder_name: params.recipientName, elder_phone: params.recipientPhone, + elder_age: params.recipientAge, + elder_gender: params.recipientGender, contact_name: params.contactName, contact_phone: params.contactPhone, scheduled_at: appointmentTime, @@ -462,6 +472,8 @@ function parseServiceOrder(item: any, logs: Array, }, recipientName: readString(item, 'recipient_name'), recipientPhone: readString(item, 'recipient_phone'), + recipientAge: readFirstNumber(item, ['recipient_age', 'elder_age']), + recipientGender: readFirstString(item, ['recipient_gender', 'elder_gender']), contactName: readString(item, 'contact_name'), contactPhone: readString(item, 'contact_phone'), appointmentTime: readString(item, 'appointment_time'), @@ -586,6 +598,8 @@ function mapCareTaskRowToLegacyOrderRow(item: any): any { address_snapshot_json: addressSnapshotValue != null ? addressSnapshotValue : JSON.parse('{}'), recipient_name: readFirstString(item, ['elder_name', 'recipient_name']), recipient_phone: readFirstString(item, ['elder_phone', 'recipient_phone']), + recipient_age: readFirstNumber(item, ['elder_age', 'recipient_age']), + recipient_gender: readFirstString(item, ['elder_gender', 'recipient_gender']), contact_name: readString(item, 'contact_name'), contact_phone: readString(item, 'contact_phone'), appointment_time: readFirstString(item, ['scheduled_at', 'appointment_time']), @@ -907,6 +921,8 @@ export async function createServiceOrder(params: CreateServiceOrderParams): Prom address_snapshot_json: params.address as any, recipient_name: params.recipientName, recipient_phone: params.recipientPhone, + recipient_age: params.recipientAge, + recipient_gender: params.recipientGender, contact_name: params.contactName, contact_phone: params.contactPhone, appointment_time: appointmentTime, diff --git a/types/home-service.uts b/types/home-service.uts index da811dba..0216d9d1 100644 --- a/types/home-service.uts +++ b/types/home-service.uts @@ -92,6 +92,7 @@ export type HomeServiceApplicationDraftType = { applicantName: string elderName: string age: number + gender: string phone: string address: string preferredTime: string diff --git a/types/service-order.uts b/types/service-order.uts index 308769d0..16d4f39f 100644 --- a/types/service-order.uts +++ b/types/service-order.uts @@ -124,6 +124,8 @@ export type ServiceOrderType = { addressSnapshot: ServiceOrderAddressSnapshotType recipientName: string recipientPhone: string + recipientAge: number + recipientGender: string contactName: string contactPhone: string appointmentTime: string diff --git a/报错信息.txt b/报错信息.txt index 1c9a15f6..e69de29b 100644 --- a/报错信息.txt +++ b/报错信息.txt @@ -1,79 +0,0 @@ -[自动热重载] 已开启代码文件保存后自动热重载 -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...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...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...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...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...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 - 表: 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 - 表: 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 属性,方便用户复制。 -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