BEGIN; CREATE TABLE IF NOT EXISTS public.hss_service_packages ( id TEXT PRIMARY KEY, service_id TEXT NOT NULL REFERENCES public.hss_service_catalog(id) ON DELETE RESTRICT, package_name TEXT NOT NULL, package_desc TEXT NOT NULL DEFAULT '', duration_minutes INTEGER NOT NULL DEFAULT 0, duration_text TEXT NOT NULL DEFAULT '', price NUMERIC(10, 2) NOT NULL DEFAULT 0, list_price NUMERIC(10, 2) NOT NULL DEFAULT 0, is_default BOOLEAN NOT NULL DEFAULT false, sort_no INTEGER NOT NULL DEFAULT 0, status SMALLINT NOT NULL DEFAULT 1, effective_at TIMESTAMPTZ, expires_at TIMESTAMPTZ, data_source TEXT NOT NULL DEFAULT 'manual', seed_batch_no TEXT NOT NULL DEFAULT '', remark TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), deleted_at TIMESTAMPTZ ); CREATE INDEX IF NOT EXISTS idx_hss_service_packages_service_status_sort ON public.hss_service_packages(service_id, status, sort_no) WHERE deleted_at IS NULL; CREATE INDEX IF NOT EXISTS idx_hss_service_packages_effective_window ON public.hss_service_packages(effective_at, expires_at) WHERE deleted_at IS NULL; CREATE OR REPLACE FUNCTION public.tg_hss_service_packages_updated_at() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN NEW.updated_at = now(); RETURN NEW; END; $$; DROP TRIGGER IF EXISTS trg_hss_service_packages_updated_at ON public.hss_service_packages; CREATE TRIGGER trg_hss_service_packages_updated_at BEFORE UPDATE ON public.hss_service_packages FOR EACH ROW EXECUTE FUNCTION public.tg_hss_service_packages_updated_at(); ALTER TABLE public.hss_service_packages ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS hss_service_packages_public_select_active ON public.hss_service_packages; CREATE POLICY hss_service_packages_public_select_active ON public.hss_service_packages FOR SELECT USING ( deleted_at IS NULL AND status = 1 AND (effective_at IS NULL OR effective_at <= now()) AND (expires_at IS NULL OR expires_at > now()) ); ALTER TABLE public.hss_service_orders ADD COLUMN IF NOT EXISTS service_package_id TEXT REFERENCES public.hss_service_packages(id) ON DELETE SET NULL, ADD COLUMN IF NOT EXISTS pricing_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb, ADD COLUMN IF NOT EXISTS original_amount NUMERIC(10, 2) NOT NULL DEFAULT 0, ADD COLUMN IF NOT EXISTS payable_amount NUMERIC(10, 2) NOT NULL DEFAULT 0, ADD COLUMN IF NOT EXISTS total_amount NUMERIC(10, 2) NOT NULL DEFAULT 0; CREATE INDEX IF NOT EXISTS idx_hss_service_orders_service_package_id ON public.hss_service_orders(service_package_id) WHERE deleted_at IS NULL; UPDATE public.hss_service_orders SET pricing_snapshot_json = CASE WHEN pricing_snapshot_json = '{}'::jsonb THEN jsonb_build_object( 'service_id', service_id, 'service_name', service_name, 'package_id', NULL, 'package_name', service_name, 'price', COALESCE((service_snapshot_json ->> 'price')::NUMERIC, 0), 'data_source', 'legacy_service_snapshot', 'remark', '历史订单由 service_snapshot_json.price 回填' ) ELSE pricing_snapshot_json END, original_amount = CASE WHEN original_amount <= 0 THEN COALESCE((service_snapshot_json ->> 'price')::NUMERIC, 0) ELSE original_amount END, payable_amount = CASE WHEN payable_amount <= 0 THEN COALESCE((service_snapshot_json ->> 'price')::NUMERIC, 0) ELSE payable_amount END, total_amount = CASE WHEN total_amount <= 0 THEN COALESCE((service_snapshot_json ->> 'price')::NUMERIC, 0) ELSE total_amount END, updated_at = now() WHERE deleted_at IS NULL AND ( pricing_snapshot_json = '{}'::jsonb OR original_amount <= 0 OR payable_amount <= 0 OR total_amount <= 0 ); COMMENT ON TABLE public.hss_service_packages IS '居家服务正式套餐/报价表'; COMMENT ON COLUMN public.hss_service_packages.data_source IS 'manual/dev_seed/prod_import 等数据来源标识'; COMMENT ON COLUMN public.hss_service_packages.seed_batch_no IS '测试种子批次号,便于上线前清理'; COMMENT ON COLUMN public.hss_service_orders.service_package_id IS '下单时冻结的正式套餐 ID'; COMMENT ON COLUMN public.hss_service_orders.pricing_snapshot_json IS '订单创建时冻结的套餐/报价快照'; COMMENT ON COLUMN public.hss_service_orders.payable_amount IS '订单应付金额快照'; COMMENT ON COLUMN public.hss_service_orders.total_amount IS '服务订单总金额快照'; COMMIT;