diff --git a/etl_billiards/schema_v2.sql b/etl_billiards/schema_v2.sql new file mode 100644 index 0000000..054aa1c --- /dev/null +++ b/etl_billiards/schema_v2.sql @@ -0,0 +1,1104 @@ +-- -*- coding: utf-8 -*- +-- Feiqiu-ETL schema (JSON-first alignment) + +CREATE SCHEMA IF NOT EXISTS billiards; +CREATE SCHEMA IF NOT EXISTS billiards_ods; +CREATE SCHEMA IF NOT EXISTS etl_admin; + +COMMENT ON SCHEMA billiards IS '门店业务数据 Schema,存放维度/事实层(与 JSON 字段对应)'; +COMMENT ON SCHEMA etl_admin IS 'ETL 调度、游标与运行记录 Schema'; + +-- ========================= +-- Billiards ODS tables +-- ========================= + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_order_settle ( + store_id bigint NOT NULL, + order_settle_id bigint NOT NULL, + order_trade_no bigint, + page_no integer, + page_size integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, order_settle_id) +); + +COMMENT ON TABLE billiards_ods.ods_order_settle IS '订单/结算 ODS(/order/list、ticket 接口原始 JSON)'; +COMMENT ON COLUMN billiards_ods.ods_order_settle.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards_ods.ods_order_settle.order_settle_id IS '结算单 ID (order_settle_id)'; +COMMENT ON COLUMN billiards_ods.ods_order_settle.order_trade_no IS '订单交易号 (order_trade_no)'; +COMMENT ON COLUMN billiards_ods.ods_order_settle.page_no IS '来源分页序号'; +COMMENT ON COLUMN billiards_ods.ods_order_settle.page_size IS '来源分页大小'; +COMMENT ON COLUMN billiards_ods.ods_order_settle.source_file IS '离线导出文件名/路径'; +COMMENT ON COLUMN billiards_ods.ods_order_settle.fetched_at IS '入 ODS 时间'; +COMMENT ON COLUMN billiards_ods.ods_order_settle.payload IS '原始 JSON 负载'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_table_use_detail ( + store_id bigint NOT NULL, + ledger_id bigint NOT NULL, + order_trade_no bigint, + order_settle_id bigint, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, ledger_id) +); + +COMMENT ON TABLE billiards_ods.ods_table_use_detail IS '台费/开台流水 ODS(siteTableUseDetailsList 等接口)'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_assistant_ledger ( + store_id bigint NOT NULL, + ledger_id bigint NOT NULL, + order_trade_no bigint, + order_settle_id bigint, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, ledger_id) +); + +COMMENT ON TABLE billiards_ods.ods_assistant_ledger IS '助教流水 ODS(orderAssistantDetails 等接口)'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_assistant_abolish ( + store_id bigint NOT NULL, + abolish_id bigint NOT NULL, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, abolish_id) +); + +COMMENT ON TABLE billiards_ods.ods_assistant_abolish IS '助教作废记录 ODS'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_goods_ledger ( + store_id bigint NOT NULL, + order_goods_id bigint NOT NULL, + order_trade_no bigint, + order_settle_id bigint, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, order_goods_id) +); + +COMMENT ON TABLE billiards_ods.ods_goods_ledger IS '商品销售流水 ODS(orderGoodsLedgers/orderGoodsList)'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_payment ( + store_id bigint NOT NULL, + pay_id bigint NOT NULL, + relate_type varchar(50), + relate_id bigint, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, pay_id) +); + +COMMENT ON TABLE billiards_ods.ods_payment IS '支付流水 ODS(payRecord/payList)'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_refund ( + store_id bigint NOT NULL, + refund_id bigint NOT NULL, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, refund_id) +); + +COMMENT ON TABLE billiards_ods.ods_refund IS '退款流水 ODS'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_coupon_verify ( + store_id bigint NOT NULL, + coupon_id bigint NOT NULL, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, coupon_id) +); + +COMMENT ON TABLE billiards_ods.ods_coupon_verify IS '团购/验券流水 ODS'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_member ( + store_id bigint NOT NULL, + member_id bigint NOT NULL, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, member_id) +); + +COMMENT ON TABLE billiards_ods.ods_member IS '会员档案 ODS'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_member_card ( + store_id bigint NOT NULL, + card_id bigint NOT NULL, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, card_id) +); + +COMMENT ON TABLE billiards_ods.ods_member_card IS '储值卡/会员卡 ODS'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_package_coupon ( + store_id bigint NOT NULL, + package_id bigint NOT NULL, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, package_id) +); + +COMMENT ON TABLE billiards_ods.ods_package_coupon IS '团购套餐定义 ODS'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_inventory_stock ( + store_id bigint NOT NULL, + site_goods_id bigint NOT NULL, + snapshot_key varchar(100) NOT NULL DEFAULT 'default', + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, site_goods_id, snapshot_key) +); + +COMMENT ON TABLE billiards_ods.ods_inventory_stock IS '库存汇总 ODS(inventorySummary 等接口)'; + +CREATE TABLE IF NOT EXISTS billiards_ods.ods_inventory_change ( + store_id bigint NOT NULL, + change_id bigint NOT NULL, + page_no integer, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, change_id) +); + +COMMENT ON TABLE billiards_ods.ods_inventory_change IS '库存变动记录 ODS'; + +-- ========================= +-- Billiards dimension tables +-- ========================= + +CREATE TABLE IF NOT EXISTS billiards.dim_store ( + store_id bigint PRIMARY KEY, + store_name varchar(200), + tenant_id bigint, + region_code varchar(30), + address varchar(500), + contact_name varchar(100), + contact_phone varchar(30), + created_time timestamptz, + updated_time timestamptz, + remark text, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb +); + +COMMENT ON TABLE billiards.dim_store IS '门店/场馆维度(siteProfile 信息快照)'; +COMMENT ON COLUMN billiards.dim_store.store_id IS '门店 ID,主键(API storeId)'; +COMMENT ON COLUMN billiards.dim_store.store_name IS '门店名称 (shop_name)'; +COMMENT ON COLUMN billiards.dim_store.tenant_id IS '租户 ID (tenant_id)'; +COMMENT ON COLUMN billiards.dim_store.region_code IS '区域编码/城市代码 (tenant_site_region_id)'; +COMMENT ON COLUMN billiards.dim_store.address IS '完整地址 (full_address/address)'; +COMMENT ON COLUMN billiards.dim_store.contact_name IS '联系人(可扩展)'; +COMMENT ON COLUMN billiards.dim_store.contact_phone IS '联系电话 (business_tel)'; +COMMENT ON COLUMN billiards.dim_store.created_time IS '创建时间'; +COMMENT ON COLUMN billiards.dim_store.updated_time IS '更新时间'; +COMMENT ON COLUMN billiards.dim_store.remark IS '备注'; +COMMENT ON COLUMN billiards.dim_store.raw_data IS '原始 JSON 快照'; + +CREATE TABLE IF NOT EXISTS billiards.dim_assistant ( + store_id bigint NOT NULL, + assistant_id bigint NOT NULL, + assistant_no varchar(64), + nickname varchar(100), + real_name varchar(100), + gender varchar(20), + mobile varchar(30), + level varchar(50), + team_id bigint, + team_name varchar(100), + assistant_status varchar(30), + work_status varchar(30), + entry_time timestamptz, + resign_time timestamptz, + start_time timestamptz, + end_time timestamptz, + create_time timestamptz, + update_time timestamptz, + system_role_id bigint, + online_status varchar(30), + allow_cx integer, + charge_way varchar(30), + pd_unit_price numeric(14,2), + cx_unit_price numeric(14,2), + is_guaranteed integer, + is_team_leader integer, + serial_number varchar(64), + show_sort integer, + is_delete integer, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, assistant_id) +); + +COMMENT ON TABLE billiards.dim_assistant IS '助教/技师维度(Assistant/List)'; +COMMENT ON COLUMN billiards.dim_assistant.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.dim_assistant.assistant_id IS '助教 ID (id)'; +COMMENT ON COLUMN billiards.dim_assistant.assistant_no IS '助教编号 (assistant_no)'; +COMMENT ON COLUMN billiards.dim_assistant.nickname IS '昵称 (nickname)'; +COMMENT ON COLUMN billiards.dim_assistant.real_name IS '真实姓名 (real_name)'; +COMMENT ON COLUMN billiards.dim_assistant.gender IS '性别'; +COMMENT ON COLUMN billiards.dim_assistant.mobile IS '手机号'; +COMMENT ON COLUMN billiards.dim_assistant.level IS '等级/段位 (level)'; +COMMENT ON COLUMN billiards.dim_assistant.team_id IS '团队 ID (team_id)'; +COMMENT ON COLUMN billiards.dim_assistant.team_name IS '团队名称 (team_name)'; +COMMENT ON COLUMN billiards.dim_assistant.assistant_status IS '账号状态 (assistant_status)'; +COMMENT ON COLUMN billiards.dim_assistant.work_status IS '工作状态 (work_status)'; +COMMENT ON COLUMN billiards.dim_assistant.entry_time IS '入职时间 (entry_time)'; +COMMENT ON COLUMN billiards.dim_assistant.resign_time IS '离职时间 (resign_time)'; +COMMENT ON COLUMN billiards.dim_assistant.start_time IS '可服务开始时间 (start_time)'; +COMMENT ON COLUMN billiards.dim_assistant.end_time IS '可服务结束时间 (end_time)'; +COMMENT ON COLUMN billiards.dim_assistant.create_time IS '创建时间 (create_time)'; +COMMENT ON COLUMN billiards.dim_assistant.update_time IS '最近更新时间 (update_time)'; +COMMENT ON COLUMN billiards.dim_assistant.system_role_id IS '系统角色 ID'; +COMMENT ON COLUMN billiards.dim_assistant.online_status IS '在线/离线状态'; +COMMENT ON COLUMN billiards.dim_assistant.allow_cx IS '是否允许撤销/重开'; +COMMENT ON COLUMN billiards.dim_assistant.charge_way IS '计费方式 (charge_way)'; +COMMENT ON COLUMN billiards.dim_assistant.pd_unit_price IS '陪打单价 (pd_unit_price)'; +COMMENT ON COLUMN billiards.dim_assistant.cx_unit_price IS '拆洗/其它单价 (cx_unit_price)'; +COMMENT ON COLUMN billiards.dim_assistant.is_guaranteed IS '是否保底'; +COMMENT ON COLUMN billiards.dim_assistant.is_team_leader IS '是否队长'; +COMMENT ON COLUMN billiards.dim_assistant.serial_number IS '序列号/展示码 (serial_number)'; +COMMENT ON COLUMN billiards.dim_assistant.show_sort IS '展示排序 (show_sort)'; +COMMENT ON COLUMN billiards.dim_assistant.is_delete IS '删除标记'; +COMMENT ON COLUMN billiards.dim_assistant.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.dim_assistant.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.dim_member ( + store_id bigint NOT NULL, + member_id bigint NOT NULL, + member_name varchar(100), + phone varchar(30), + balance numeric(18,4), + status varchar(30), + register_time timestamptz, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, member_id) +); + +COMMENT ON TABLE billiards.dim_member IS '会员维度(MemberProfile/GetTenantMemberList)'; +COMMENT ON COLUMN billiards.dim_member.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.dim_member.member_id IS '会员 ID (memberId)'; +COMMENT ON COLUMN billiards.dim_member.member_name IS '会员名称 (memberName)'; +COMMENT ON COLUMN billiards.dim_member.phone IS '手机号 (phone)'; +COMMENT ON COLUMN billiards.dim_member.balance IS '储值余额 (balance)'; +COMMENT ON COLUMN billiards.dim_member.status IS '会员状态 (status)'; +COMMENT ON COLUMN billiards.dim_member.register_time IS '注册时间 (registerTime)'; +COMMENT ON COLUMN billiards.dim_member.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.dim_member.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.dim_package_coupon ( + store_id bigint NOT NULL, + package_id bigint NOT NULL, + package_code varchar(100), + package_name varchar(200), + table_area_id bigint, + table_area_name varchar(100), + selling_price numeric(14,2), + duration_seconds integer, + start_time timestamptz, + end_time timestamptz, + type varchar(50), + is_enabled integer, + is_delete integer, + usable_count integer, + creator_name varchar(100), + date_type varchar(50), + group_type varchar(50), + coupon_money numeric(14,2), + area_tag_type varchar(50), + system_group_type varchar(50), + card_type_ids text, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, package_id) +); + +COMMENT ON TABLE billiards.dim_package_coupon IS '团购/套餐定义维度(Package/List)'; +COMMENT ON COLUMN billiards.dim_package_coupon.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.dim_package_coupon.package_id IS '套餐主键 (id)'; +COMMENT ON COLUMN billiards.dim_package_coupon.package_code IS '套餐编码 (package_id 或自定义编码)'; +COMMENT ON COLUMN billiards.dim_package_coupon.package_name IS '套餐名称 (package_name)'; +COMMENT ON COLUMN billiards.dim_package_coupon.table_area_id IS '适用台桌区域 ID'; +COMMENT ON COLUMN billiards.dim_package_coupon.table_area_name IS '适用台桌区域名称'; +COMMENT ON COLUMN billiards.dim_package_coupon.selling_price IS '售卖价格 (selling_price)'; +COMMENT ON COLUMN billiards.dim_package_coupon.duration_seconds IS '可用时长(秒)(duration)'; +COMMENT ON COLUMN billiards.dim_package_coupon.start_time IS '可用时间起'; +COMMENT ON COLUMN billiards.dim_package_coupon.end_time IS '可用时间止'; +COMMENT ON COLUMN billiards.dim_package_coupon.type IS '套餐类型 (type/group_type)'; +COMMENT ON COLUMN billiards.dim_package_coupon.is_enabled IS '是否启用 (is_enabled)'; +COMMENT ON COLUMN billiards.dim_package_coupon.is_delete IS '删除标记 (is_delete)'; +COMMENT ON COLUMN billiards.dim_package_coupon.usable_count IS '可使用次数 (usable_count)'; +COMMENT ON COLUMN billiards.dim_package_coupon.creator_name IS '创建人 (creator_name)'; +COMMENT ON COLUMN billiards.dim_package_coupon.date_type IS '日期限制 (date_type)'; +COMMENT ON COLUMN billiards.dim_package_coupon.group_type IS '渠道/团购类型 (group_type)'; +COMMENT ON COLUMN billiards.dim_package_coupon.coupon_money IS '券面额 (coupon_money)'; +COMMENT ON COLUMN billiards.dim_package_coupon.area_tag_type IS '区域标签类型'; +COMMENT ON COLUMN billiards.dim_package_coupon.system_group_type IS '系统套餐类型 (system_group_type)'; +COMMENT ON COLUMN billiards.dim_package_coupon.card_type_ids IS '适用会员卡类型列表 (card_type_ids)'; +COMMENT ON COLUMN billiards.dim_package_coupon.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.dim_package_coupon.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.dim_product ( + store_id bigint NOT NULL, + product_id bigint NOT NULL, + site_product_id bigint, + product_name varchar(200) NOT NULL, + category_id bigint, + category_name varchar(100), + second_category_id bigint, + unit varchar(20), + cost_price numeric(14,4), + sale_price numeric(14,4), + allow_discount boolean, + status varchar(30), + supplier_id bigint, + barcode varchar(128), + is_combo boolean, + created_time timestamptz, + updated_time timestamptz, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, product_id) +); + +COMMENT ON TABLE billiards.dim_product IS '商品维度(TenantGoods/QueryTenantGoods)'; +COMMENT ON COLUMN billiards.dim_product.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.dim_product.product_id IS '商品 ID (siteGoodsId/tenantGoodsId)'; +COMMENT ON COLUMN billiards.dim_product.site_product_id IS '站点/门店商品 ID (siteGoodsId)'; +COMMENT ON COLUMN billiards.dim_product.product_name IS '商品名称 (goodsName/productName)'; +COMMENT ON COLUMN billiards.dim_product.category_id IS '一级分类 ID (goodsCategoryId)'; +COMMENT ON COLUMN billiards.dim_product.category_name IS '一级分类名称 (categoryName)'; +COMMENT ON COLUMN billiards.dim_product.second_category_id IS '二级分类 ID (goodsSecondCategoryId)'; +COMMENT ON COLUMN billiards.dim_product.unit IS '计量单位 (goodsUnit)'; +COMMENT ON COLUMN billiards.dim_product.cost_price IS '成本价 (costPrice)'; +COMMENT ON COLUMN billiards.dim_product.sale_price IS '销售价 (goodsPrice/salePrice)'; +COMMENT ON COLUMN billiards.dim_product.allow_discount IS '是否允许折扣 (able_discount)'; +COMMENT ON COLUMN billiards.dim_product.status IS '商品状态 (goodsState/status)'; +COMMENT ON COLUMN billiards.dim_product.supplier_id IS '供应商 ID'; +COMMENT ON COLUMN billiards.dim_product.barcode IS '条码 (barcode)'; +COMMENT ON COLUMN billiards.dim_product.is_combo IS '是否组合/套餐商品 (isCombo)'; +COMMENT ON COLUMN billiards.dim_product.created_time IS '创建时间 (createTime)'; +COMMENT ON COLUMN billiards.dim_product.updated_time IS '更新时间 (updateTime)'; +COMMENT ON COLUMN billiards.dim_product.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.dim_product.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.dim_product_price_scd ( + product_scd_id bigserial PRIMARY KEY, + store_id bigint NOT NULL, + product_id bigint NOT NULL, + product_name varchar(200), + category_id bigint, + category_name varchar(100), + second_category_id bigint, + cost_price numeric(14,4), + sale_price numeric(14,4), + allow_discount boolean, + status varchar(30), + valid_from timestamptz NOT NULL DEFAULT now(), + valid_to timestamptz, + is_current boolean NOT NULL DEFAULT true, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + CONSTRAINT fk_dpps_product FOREIGN KEY (store_id, product_id) + REFERENCES billiards.dim_product(store_id, product_id) ON DELETE CASCADE, + CONSTRAINT ck_dpps_range CHECK ( + valid_from < COALESCE(valid_to, '9999-12-31 00:00:00+00'::timestamptz) + ), + CONSTRAINT uq_dpps_current UNIQUE (store_id, product_id) WHERE (is_current) +); + +COMMENT ON TABLE billiards.dim_product_price_scd IS '商品维度价格 SCD(记录价格/分类变动历史)'; +COMMENT ON COLUMN billiards.dim_product_price_scd.product_scd_id IS '价格 SCD 主键'; +COMMENT ON COLUMN billiards.dim_product_price_scd.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.dim_product_price_scd.product_id IS '商品 ID'; +COMMENT ON COLUMN billiards.dim_product_price_scd.product_name IS '商品名称(冗余,方便简单查询)'; +COMMENT ON COLUMN billiards.dim_product_price_scd.category_id IS '一级分类 ID'; +COMMENT ON COLUMN billiards.dim_product_price_scd.category_name IS '一级分类名称'; +COMMENT ON COLUMN billiards.dim_product_price_scd.second_category_id IS '二级分类 ID'; +COMMENT ON COLUMN billiards.dim_product_price_scd.cost_price IS '成本价'; +COMMENT ON COLUMN billiards.dim_product_price_scd.sale_price IS '销售价'; +COMMENT ON COLUMN billiards.dim_product_price_scd.allow_discount IS '是否允许折扣'; +COMMENT ON COLUMN billiards.dim_product_price_scd.status IS '商品状态 (上/下架)'; +COMMENT ON COLUMN billiards.dim_product_price_scd.valid_from IS '生效时间'; +COMMENT ON COLUMN billiards.dim_product_price_scd.valid_to IS '失效时间,NULL 表示当前版本'; +COMMENT ON COLUMN billiards.dim_product_price_scd.is_current IS '当前有效版本标记'; +COMMENT ON COLUMN billiards.dim_product_price_scd.raw_data IS '原始 JSON 快照'; + +CREATE VIEW IF NOT EXISTS billiards.dim_product_price_current AS +SELECT product_scd_id, + store_id, + product_id, + product_name, + category_id, + category_name, + second_category_id, + cost_price, + sale_price, + allow_discount, + status, + valid_from, + valid_to, + raw_data + FROM billiards.dim_product_price_scd + WHERE is_current; + +COMMENT ON VIEW billiards.dim_product_price_current IS '商品价格 SCD 当前有效版本视图'; +COMMENT ON COLUMN billiards.dim_product_price_current.product_scd_id IS '价格 SCD 主键'; +COMMENT ON COLUMN billiards.dim_product_price_current.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.dim_product_price_current.product_id IS '商品 ID'; +COMMENT ON COLUMN billiards.dim_product_price_current.product_name IS '商品名称'; +COMMENT ON COLUMN billiards.dim_product_price_current.category_id IS '一级分类 ID'; +COMMENT ON COLUMN billiards.dim_product_price_current.category_name IS '一级分类名称'; +COMMENT ON COLUMN billiards.dim_product_price_current.second_category_id IS '二级分类 ID'; +COMMENT ON COLUMN billiards.dim_product_price_current.cost_price IS '成本价'; +COMMENT ON COLUMN billiards.dim_product_price_current.sale_price IS '销售价'; +COMMENT ON COLUMN billiards.dim_product_price_current.allow_discount IS '是否允许折扣'; +COMMENT ON COLUMN billiards.dim_product_price_current.status IS '商品状态'; +COMMENT ON COLUMN billiards.dim_product_price_current.valid_from IS '生效时间'; +COMMENT ON COLUMN billiards.dim_product_price_current.valid_to IS '失效时间'; +COMMENT ON COLUMN billiards.dim_product_price_current.raw_data IS '原始 JSON'; + +CREATE TABLE IF NOT EXISTS billiards.dim_table ( + store_id bigint NOT NULL, + table_id bigint NOT NULL, + site_id bigint, + area_id bigint, + area_name varchar(100), + table_name varchar(100) NOT NULL, + table_price numeric(14,4), + table_status varchar(30), + table_status_name varchar(50), + light_status integer, + is_rest_area integer, + show_status integer, + virtual_table integer, + charge_free integer, + only_allow_groupon integer, + is_online_reservation integer, + created_time timestamptz, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, table_id) +); + +COMMENT ON TABLE billiards.dim_table IS '台桌档案维度(Table/GetSiteTables)'; +COMMENT ON COLUMN billiards.dim_table.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.dim_table.table_id IS '台桌 ID (id)'; +COMMENT ON COLUMN billiards.dim_table.site_id IS '场馆/站点 ID (site_id)'; +COMMENT ON COLUMN billiards.dim_table.area_id IS '区域 ID (site_table_area_id)'; +COMMENT ON COLUMN billiards.dim_table.area_name IS '区域名称 (areaName)'; +COMMENT ON COLUMN billiards.dim_table.table_name IS '台桌名称 (table_name)'; +COMMENT ON COLUMN billiards.dim_table.table_price IS '台费单价 (table_price)'; +COMMENT ON COLUMN billiards.dim_table.table_status IS '台桌状态代码 (table_status)'; +COMMENT ON COLUMN billiards.dim_table.table_status_name IS '台桌状态名称 (tableStatusName)'; +COMMENT ON COLUMN billiards.dim_table.light_status IS '灯光状态 (light_status)'; +COMMENT ON COLUMN billiards.dim_table.is_rest_area IS '是否休息区 (is_rest_area)'; +COMMENT ON COLUMN billiards.dim_table.show_status IS '是否展示 (show_status)'; +COMMENT ON COLUMN billiards.dim_table.virtual_table IS '是否虚拟台 (virtual_table)'; +COMMENT ON COLUMN billiards.dim_table.charge_free IS '是否免收费 (charge_free)'; +COMMENT ON COLUMN billiards.dim_table.only_allow_groupon IS '是否仅限团购 (only_allow_groupon)'; +COMMENT ON COLUMN billiards.dim_table.is_online_reservation IS '是否可线上预约 (is_online_reservation)'; +COMMENT ON COLUMN billiards.dim_table.created_time IS '创建时间 (create_time)'; +COMMENT ON COLUMN billiards.dim_table.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.dim_table.updated_at IS 'ETL 更新时间'; + +-- ========================= +-- Billiards fact tables +-- ========================= + +CREATE TABLE IF NOT EXISTS billiards.fact_assistant_abolish ( + store_id bigint NOT NULL, + abolish_id bigint NOT NULL, + table_id bigint, + table_name varchar(100), + table_area_id bigint, + table_area varchar(100), + assistant_no varchar(64), + assistant_name varchar(100), + charge_minutes integer, + abolish_amount numeric(14,4), + create_time timestamptz, + trash_reason text, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, abolish_id) +); + +COMMENT ON TABLE billiards.fact_assistant_abolish IS '助教作废/撤单流水(Assistant/AbolishList)'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.abolish_id IS '作废记录 ID (id)'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.table_id IS '台桌 ID (tableId)'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.table_name IS '台桌名称'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.table_area_id IS '台桌区域 ID'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.table_area IS '台桌区域名称'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.assistant_no IS '助教编号 (assistantOn)'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.assistant_name IS '助教姓名'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.charge_minutes IS '计费分钟 (pdChargeMinutes)'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.abolish_amount IS '作废金额 (assistantAbolishAmount)'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.create_time IS '创建时间 (createTime)'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.trash_reason IS '作废原因 (trashReason)'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_assistant_abolish.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.fact_assistant_ledger ( + store_id bigint NOT NULL, + ledger_id bigint NOT NULL, + assistant_no varchar(64), + assistant_name varchar(100), + nickname varchar(100), + level_name varchar(50), + table_name varchar(100), + ledger_unit_price numeric(14,4), + ledger_count numeric(14,4), + ledger_amount numeric(14,4), + projected_income numeric(14,4), + service_money numeric(14,4), + member_discount_amount numeric(14,4), + manual_discount_amount numeric(14,4), + coupon_deduct_money numeric(14,4), + order_trade_no bigint, + order_settle_id bigint, + operator_id bigint, + operator_name varchar(100), + assistant_team_id bigint, + assistant_level varchar(50), + site_table_id bigint, + order_assistant_id bigint, + site_assistant_id bigint, + user_id bigint, + ledger_start_time timestamptz, + ledger_end_time timestamptz, + start_use_time timestamptz, + last_use_time timestamptz, + income_seconds integer, + real_use_seconds integer, + is_trash integer, + trash_reason text, + is_confirm integer, + ledger_status varchar(30), + create_time timestamptz, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, ledger_id) +); + +COMMENT ON TABLE billiards.fact_assistant_ledger IS '助教陪打计费流水(Assistant/LedgerList)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_id IS '流水 ID (id)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.assistant_no IS '助教编号'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.assistant_name IS '助教姓名'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.nickname IS '昵称'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.level_name IS '等级名称'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.table_name IS '服务台桌名称'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_unit_price IS '计费单价 (ledger_unit_price)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_count IS '计费数量 (ledger_count)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_amount IS '计费金额 (ledger_amount)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.projected_income IS '预计收益 (projected_income)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.service_money IS '服务费 (service_money)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.member_discount_amount IS '会员折扣金额'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.manual_discount_amount IS '手动折扣金额'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.coupon_deduct_money IS '券抵扣金额'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.order_trade_no IS '订单交易号 (order_trade_no)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.order_settle_id IS '结算单 ID (order_settle_id)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.operator_id IS '操作人 ID'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.operator_name IS '操作人姓名'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.assistant_team_id IS '助教团队 ID'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.assistant_level IS '助教等级'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.site_table_id IS '台桌 ID'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.order_assistant_id IS '订单内助教 ID'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.site_assistant_id IS '场馆助教 ID'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.user_id IS '会员/顾客 ID'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_start_time IS '计费开始时间'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_end_time IS '计费结束时间'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.start_use_time IS '开始使用时间'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.last_use_time IS '最后使用时间'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.income_seconds IS '计费秒数 (income_seconds)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.real_use_seconds IS '实际使用秒数 (real_use_seconds)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.is_trash IS '是否作废 (is_trash)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.trash_reason IS '作废原因 (trash_reason)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.is_confirm IS '是否确认 (is_confirm)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_status IS '状态 (ledger_status)'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.create_time IS '创建时间'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_assistant_ledger.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.fact_inventory_change ( + store_id bigint NOT NULL, + change_id bigint NOT NULL, + site_goods_id bigint, + stock_type varchar(50), + goods_name varchar(200), + change_time timestamptz, + start_qty numeric(18,4), + end_qty numeric(18,4), + change_qty numeric(18,4), + unit varchar(20), + price numeric(14,4), + operator_name varchar(100), + remark text, + goods_category_id bigint, + goods_second_category_id bigint, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, change_id) +); + +COMMENT ON TABLE billiards.fact_inventory_change IS '库存变更流水(Inventory/ChangeList)'; +COMMENT ON COLUMN billiards.fact_inventory_change.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_inventory_change.change_id IS '库存变动 ID (siteGoodsStockId)'; +COMMENT ON COLUMN billiards.fact_inventory_change.site_goods_id IS '门店商品 ID (siteGoodsId)'; +COMMENT ON COLUMN billiards.fact_inventory_change.stock_type IS '变动类型 (stockType)'; +COMMENT ON COLUMN billiards.fact_inventory_change.goods_name IS '商品名称 (goodsName)'; +COMMENT ON COLUMN billiards.fact_inventory_change.change_time IS '变动时间 (createTime)'; +COMMENT ON COLUMN billiards.fact_inventory_change.start_qty IS '变动前数量 (startNum)'; +COMMENT ON COLUMN billiards.fact_inventory_change.end_qty IS '变动后数量 (endNum)'; +COMMENT ON COLUMN billiards.fact_inventory_change.change_qty IS '变化数量 (changeNum)'; +COMMENT ON COLUMN billiards.fact_inventory_change.unit IS '单位 (unit)'; +COMMENT ON COLUMN billiards.fact_inventory_change.price IS '单价 (price)'; +COMMENT ON COLUMN billiards.fact_inventory_change.operator_name IS '操作人 (operatorName)'; +COMMENT ON COLUMN billiards.fact_inventory_change.remark IS '备注 (remark)'; +COMMENT ON COLUMN billiards.fact_inventory_change.goods_category_id IS '一级分类 ID'; +COMMENT ON COLUMN billiards.fact_inventory_change.goods_second_category_id IS '二级分类 ID'; +COMMENT ON COLUMN billiards.fact_inventory_change.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_inventory_change.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.fact_order ( + store_id bigint NOT NULL, + order_id bigint NOT NULL, + order_no varchar(64), + member_id bigint, + table_id bigint, + order_time timestamptz, + end_time timestamptz, + total_amount numeric(14,4), + discount_amount numeric(14,4), + final_amount numeric(14,4), + pay_status varchar(30), + order_status varchar(30), + remark text, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, order_id) +); + +COMMENT ON TABLE billiards.fact_order IS '订单事实表(/order/list 返回的结账记录)'; +COMMENT ON COLUMN billiards.fact_order.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_order.order_id IS '订单 ID (orderId)'; +COMMENT ON COLUMN billiards.fact_order.order_no IS '订单编号 (orderNo)'; +COMMENT ON COLUMN billiards.fact_order.member_id IS '会员 ID (memberId)'; +COMMENT ON COLUMN billiards.fact_order.table_id IS '台桌 ID (tableId)'; +COMMENT ON COLUMN billiards.fact_order.order_time IS '开单时间/开始时间 (orderTime)'; +COMMENT ON COLUMN billiards.fact_order.end_time IS '结账时间 (endTime)'; +COMMENT ON COLUMN billiards.fact_order.total_amount IS '订单总金额 (totalAmount)'; +COMMENT ON COLUMN billiards.fact_order.discount_amount IS '折扣金额 (discountAmount)'; +COMMENT ON COLUMN billiards.fact_order.final_amount IS '应付金额 (finalAmount)'; +COMMENT ON COLUMN billiards.fact_order.pay_status IS '支付状态 (payStatus)'; +COMMENT ON COLUMN billiards.fact_order.order_status IS '订单状态 (orderStatus)'; +COMMENT ON COLUMN billiards.fact_order.remark IS '备注 (remark)'; +COMMENT ON COLUMN billiards.fact_order.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_order.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.fact_payment ( + store_id bigint NOT NULL, + pay_id bigint NOT NULL, + order_id bigint, + order_settle_id bigint, + relate_type varchar(50), + relate_id bigint, + site_id bigint, + tenant_id bigint, + pay_time timestamptz, + create_time timestamptz, + pay_amount numeric(14,4), + pay_type varchar(50), + payment_method varchar(50), + online_pay_channel varchar(50), + pay_status varchar(30), + pay_terminal varchar(50), + remark text, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, pay_id) +); + +COMMENT ON TABLE billiards.fact_payment IS '支付流水(pay/records)'; +COMMENT ON COLUMN billiards.fact_payment.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_payment.pay_id IS '支付记录 ID (payId)'; +COMMENT ON COLUMN billiards.fact_payment.order_id IS '旧版订单 ID (orderId),保留兼容'; +COMMENT ON COLUMN billiards.fact_payment.order_settle_id IS '结算单 ID (relate_id 当 relate_type=2)'; +COMMENT ON COLUMN billiards.fact_payment.relate_type IS '关联类型 (relate_type)'; +COMMENT ON COLUMN billiards.fact_payment.relate_id IS '关联 ID (relate_id)'; +COMMENT ON COLUMN billiards.fact_payment.site_id IS '门店 ID(冗余自 payload)'; +COMMENT ON COLUMN billiards.fact_payment.tenant_id IS '租户 ID'; +COMMENT ON COLUMN billiards.fact_payment.pay_time IS '支付时间 (payTime)'; +COMMENT ON COLUMN billiards.fact_payment.create_time IS '创建时间 (create_time)'; +COMMENT ON COLUMN billiards.fact_payment.pay_amount IS '支付金额 (payAmount)'; +COMMENT ON COLUMN billiards.fact_payment.pay_type IS '支付渠道 (payType)'; +COMMENT ON COLUMN billiards.fact_payment.payment_method IS '支付方式 (payment_method)'; +COMMENT ON COLUMN billiards.fact_payment.online_pay_channel IS '线上支付渠道 (online_pay_channel)'; +COMMENT ON COLUMN billiards.fact_payment.pay_status IS '支付状态 (payStatus)'; +COMMENT ON COLUMN billiards.fact_payment.pay_terminal IS '支付终端 (pay_terminal)'; +COMMENT ON COLUMN billiards.fact_payment.remark IS '备注'; +COMMENT ON COLUMN billiards.fact_payment.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_payment.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.fact_refund ( + store_id bigint NOT NULL, + refund_id bigint NOT NULL, + site_id bigint, + tenant_id bigint, + pay_amount numeric(14,4), + pay_status varchar(30), + pay_time timestamptz, + create_time timestamptz, + relate_type varchar(50), + relate_id bigint, + payment_method varchar(50), + refund_amount numeric(14,4), + action_type varchar(30), + pay_terminal varchar(50), + operator_id bigint, + channel_pay_no varchar(100), + channel_fee numeric(14,4), + is_delete integer, + member_id bigint, + member_card_id bigint, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, refund_id) +); + +COMMENT ON TABLE billiards.fact_refund IS '退款流水(Pay/RefundList)'; +COMMENT ON COLUMN billiards.fact_refund.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_refund.refund_id IS '退款记录 ID (id)'; +COMMENT ON COLUMN billiards.fact_refund.site_id IS '场地 ID (site_id)'; +COMMENT ON COLUMN billiards.fact_refund.tenant_id IS '租户 ID (tenant_id)'; +COMMENT ON COLUMN billiards.fact_refund.pay_amount IS '原支付金额 (pay_amount)'; +COMMENT ON COLUMN billiards.fact_refund.pay_status IS '支付状态 (pay_status)'; +COMMENT ON COLUMN billiards.fact_refund.pay_time IS '支付时间 (pay_time)'; +COMMENT ON COLUMN billiards.fact_refund.create_time IS '创建时间 (create_time)'; +COMMENT ON COLUMN billiards.fact_refund.relate_type IS '关联对象类型 (relate_type)'; +COMMENT ON COLUMN billiards.fact_refund.relate_id IS '关联对象 ID (relate_id)'; +COMMENT ON COLUMN billiards.fact_refund.payment_method IS '支付方式 (payment_method)'; +COMMENT ON COLUMN billiards.fact_refund.refund_amount IS '退款金额 (refund_amount)'; +COMMENT ON COLUMN billiards.fact_refund.action_type IS '操作类型 (action_type)'; +COMMENT ON COLUMN billiards.fact_refund.pay_terminal IS '终端类型 (pay_terminal)'; +COMMENT ON COLUMN billiards.fact_refund.operator_id IS '操作人 ID (operator_id)'; +COMMENT ON COLUMN billiards.fact_refund.channel_pay_no IS '渠道支付单号 (channel_pay_no)'; +COMMENT ON COLUMN billiards.fact_refund.channel_fee IS '渠道手续费 (channel_fee)'; +COMMENT ON COLUMN billiards.fact_refund.is_delete IS '删除标记 (is_delete)'; +COMMENT ON COLUMN billiards.fact_refund.member_id IS '会员 ID (member_id)'; +COMMENT ON COLUMN billiards.fact_refund.member_card_id IS '会员卡 ID (member_card_id)'; +COMMENT ON COLUMN billiards.fact_refund.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_refund.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.fact_table_discount ( + store_id bigint NOT NULL, + discount_id bigint NOT NULL, + adjust_type varchar(50), + applicant_id bigint, + applicant_name varchar(100), + operator_id bigint, + operator_name varchar(100), + ledger_amount numeric(14,4), + ledger_count numeric(14,4), + ledger_name varchar(100), + ledger_status varchar(30), + order_settle_id bigint, + order_trade_no bigint, + site_table_id bigint, + table_area_id bigint, + table_area_name varchar(100), + create_time timestamptz, + is_delete integer, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, discount_id) +); + +COMMENT ON TABLE billiards.fact_table_discount IS '台费调价/折扣记录(Table/AdjustList)'; +COMMENT ON COLUMN billiards.fact_table_discount.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_table_discount.discount_id IS '调价记录 ID (id)'; +COMMENT ON COLUMN billiards.fact_table_discount.adjust_type IS '调价类型 (adjust_type/adjustType)'; +COMMENT ON COLUMN billiards.fact_table_discount.applicant_id IS '申请人 ID (applicant_id)'; +COMMENT ON COLUMN billiards.fact_table_discount.applicant_name IS '申请人姓名 (applicant_name)'; +COMMENT ON COLUMN billiards.fact_table_discount.operator_id IS '操作人 ID (operator_id)'; +COMMENT ON COLUMN billiards.fact_table_discount.operator_name IS '操作人姓名 (operator_name)'; +COMMENT ON COLUMN billiards.fact_table_discount.ledger_amount IS '台费金额调整 (ledger_amount)'; +COMMENT ON COLUMN billiards.fact_table_discount.ledger_count IS '台费数量调整 (ledger_count)'; +COMMENT ON COLUMN billiards.fact_table_discount.ledger_name IS '科目名称 (ledger_name)'; +COMMENT ON COLUMN billiards.fact_table_discount.ledger_status IS '记录状态 (ledger_status)'; +COMMENT ON COLUMN billiards.fact_table_discount.order_settle_id IS '结算单 ID (order_settle_id)'; +COMMENT ON COLUMN billiards.fact_table_discount.order_trade_no IS '订单号 (order_trade_no)'; +COMMENT ON COLUMN billiards.fact_table_discount.site_table_id IS '台桌 ID (site_table_id)'; +COMMENT ON COLUMN billiards.fact_table_discount.table_area_id IS '台桌区域 ID'; +COMMENT ON COLUMN billiards.fact_table_discount.table_area_name IS '台桌区域名称'; +COMMENT ON COLUMN billiards.fact_table_discount.create_time IS '创建时间 (create_time)'; +COMMENT ON COLUMN billiards.fact_table_discount.is_delete IS '删除标记 (is_delete)'; +COMMENT ON COLUMN billiards.fact_table_discount.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_table_discount.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.fact_topup ( + store_id bigint NOT NULL, + topup_id bigint NOT NULL, + member_id bigint, + member_name varchar(100), + member_phone varchar(30), + card_id bigint, + card_type_name varchar(100), + pay_amount numeric(14,4), + consume_money numeric(14,4), + settle_status varchar(30), + settle_type varchar(30), + settle_name varchar(100), + settle_relate_id bigint, + pay_time timestamptz, + create_time timestamptz, + operator_id bigint, + operator_name varchar(100), + payment_method varchar(50), + refund_amount numeric(14,4), + cash_amount numeric(14,4), + card_amount numeric(14,4), + balance_amount numeric(14,4), + online_amount numeric(14,4), + rounding_amount numeric(14,4), + adjust_amount numeric(14,4), + goods_money numeric(14,4), + table_charge_money numeric(14,4), + service_money numeric(14,4), + coupon_amount numeric(14,4), + order_remark text, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, topup_id) +); + +COMMENT ON TABLE billiards.fact_topup IS '储值充值结算流水(Topup/SettleList)'; +COMMENT ON COLUMN billiards.fact_topup.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_topup.topup_id IS '充值记录 ID (id)'; +COMMENT ON COLUMN billiards.fact_topup.member_id IS '会员 ID (memberId)'; +COMMENT ON COLUMN billiards.fact_topup.member_name IS '会员姓名 (memberName)'; +COMMENT ON COLUMN billiards.fact_topup.member_phone IS '会员手机号 (memberPhone)'; +COMMENT ON COLUMN billiards.fact_topup.card_id IS '会员卡 ID (tenantMemberCardId)'; +COMMENT ON COLUMN billiards.fact_topup.card_type_name IS '会员卡类型名称 (memberCardTypeName)'; +COMMENT ON COLUMN billiards.fact_topup.pay_amount IS '支付金额 (payAmount)'; +COMMENT ON COLUMN billiards.fact_topup.consume_money IS '可消费金额/储值 (consumeMoney)'; +COMMENT ON COLUMN billiards.fact_topup.settle_status IS '结算状态 (settleStatus)'; +COMMENT ON COLUMN billiards.fact_topup.settle_type IS '结算方式 (settleType)'; +COMMENT ON COLUMN billiards.fact_topup.settle_name IS '结算名称 (settleName)'; +COMMENT ON COLUMN billiards.fact_topup.settle_relate_id IS '结算关联 ID (settleRelateId)'; +COMMENT ON COLUMN billiards.fact_topup.pay_time IS '支付时间 (payTime)'; +COMMENT ON COLUMN billiards.fact_topup.create_time IS '创建时间 (createTime)'; +COMMENT ON COLUMN billiards.fact_topup.operator_id IS '操作人 ID (operatorId)'; +COMMENT ON COLUMN billiards.fact_topup.operator_name IS '操作人姓名 (operatorName)'; +COMMENT ON COLUMN billiards.fact_topup.payment_method IS '支付方式 (paymentMethod)'; +COMMENT ON COLUMN billiards.fact_topup.refund_amount IS '退款金额 (refundAmount)'; +COMMENT ON COLUMN billiards.fact_topup.cash_amount IS '现金金额 (cashAmount)'; +COMMENT ON COLUMN billiards.fact_topup.card_amount IS '银行卡金额 (cardAmount)'; +COMMENT ON COLUMN billiards.fact_topup.balance_amount IS '余额抵扣金额 (balanceAmount)'; +COMMENT ON COLUMN billiards.fact_topup.online_amount IS '在线支付金额 (onlineAmount)'; +COMMENT ON COLUMN billiards.fact_topup.rounding_amount IS '抹零金额 (roundingAmount)'; +COMMENT ON COLUMN billiards.fact_topup.adjust_amount IS '调整金额 (adjustAmount)'; +COMMENT ON COLUMN billiards.fact_topup.goods_money IS '商品金额 (goodsMoney)'; +COMMENT ON COLUMN billiards.fact_topup.table_charge_money IS '台费金额 (tableChargeMoney)'; +COMMENT ON COLUMN billiards.fact_topup.service_money IS '服务费 (serviceMoney)'; +COMMENT ON COLUMN billiards.fact_topup.coupon_amount IS '券抵扣金额 (couponAmount)'; +COMMENT ON COLUMN billiards.fact_topup.order_remark IS '备注 (orderRemark)'; +COMMENT ON COLUMN billiards.fact_topup.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_topup.updated_at IS 'ETL 更新时间'; + +CREATE TABLE IF NOT EXISTS billiards.fact_coupon_usage ( + store_id bigint NOT NULL, + usage_id bigint NOT NULL, + coupon_code varchar(100), + coupon_channel varchar(50), + coupon_name varchar(200), + sale_price numeric(14,4), + coupon_money numeric(14,4), + coupon_free_time integer, + use_status varchar(30), + create_time timestamptz, + consume_time timestamptz, + operator_id bigint, + operator_name varchar(100), + table_id bigint, + site_order_id bigint, + group_package_id bigint, + coupon_remark text, + deal_id varchar(100), + certificate_id varchar(100), + verify_id varchar(100), + is_delete integer, + raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, + updated_at timestamptz NOT NULL DEFAULT now(), + PRIMARY KEY (store_id, usage_id) +); + +COMMENT ON TABLE billiards.fact_coupon_usage IS '平台券验券/核销流水(Coupon/UsageList)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.store_id IS '门店 ID'; +COMMENT ON COLUMN billiards.fact_coupon_usage.usage_id IS '核销记录 ID (id)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_code IS '券码 (coupon_code)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_channel IS '券渠道 (coupon_channel)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_name IS '券名称 (coupon_name)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.sale_price IS '售卖价 (sale_price)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_money IS '券面额 (coupon_money)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_free_time IS '赠送/免单时长 (coupon_free_time)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.use_status IS '核销状态 (use_status)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.create_time IS '创建时间 (create_time)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.consume_time IS '核销时间 (consume_time)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.operator_id IS '操作人 ID (operator_id)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.operator_name IS '操作人姓名 (operator_name)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.table_id IS '台桌 ID (table_id)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.site_order_id IS '场馆订单 ID (site_order_id)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.group_package_id IS '套餐 ID (group_package_id)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_remark IS '备注 (coupon_remark)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.deal_id IS '团购/渠道 deal_id'; +COMMENT ON COLUMN billiards.fact_coupon_usage.certificate_id IS '凭证号 (certificate_id)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.verify_id IS '核销流水号 (verify_id)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.is_delete IS '删除标记 (is_delete)'; +COMMENT ON COLUMN billiards.fact_coupon_usage.raw_data IS '原始 JSON'; +COMMENT ON COLUMN billiards.fact_coupon_usage.updated_at IS 'ETL 更新时间'; + +-- ========================= +-- ETL admin tables +-- ========================= + +CREATE TYPE IF NOT EXISTS etl_admin.run_status_enum AS ENUM ('SUCC', 'FAIL', 'PARTIAL'); + +COMMENT ON TYPE etl_admin.run_status_enum IS 'ETL 运行状态枚举(成功/失败/部分成功)'; + +CREATE TABLE IF NOT EXISTS etl_admin.etl_task ( + task_id bigserial PRIMARY KEY, + store_id bigint NOT NULL, + task_code varchar(50) NOT NULL, + description text, + enabled boolean NOT NULL DEFAULT true, + cursor_field varchar(100), + window_minutes_default integer DEFAULT 60, + overlap_seconds integer DEFAULT 120, + page_size integer DEFAULT 200, + retry_max integer DEFAULT 3, + params jsonb NOT NULL DEFAULT '{}'::jsonb, + created_at timestamptz NOT NULL DEFAULT now(), + updated_at timestamptz NOT NULL DEFAULT now(), + UNIQUE (store_id, task_code) +); + +COMMENT ON TABLE etl_admin.etl_task IS 'ETL 任务配置(启停、窗口、分页参数等)'; +COMMENT ON COLUMN etl_admin.etl_task.task_id IS '任务 ID(自增主键)'; +COMMENT ON COLUMN etl_admin.etl_task.store_id IS '门店 ID(任务所属门店)'; +COMMENT ON COLUMN etl_admin.etl_task.task_code IS '任务代码(如 PRODUCTS、ORDERS)'; +COMMENT ON COLUMN etl_admin.etl_task.description IS '任务描述'; +COMMENT ON COLUMN etl_admin.etl_task.enabled IS '是否启用'; +COMMENT ON COLUMN etl_admin.etl_task.cursor_field IS '游标使用的字段(如时间或 ID)'; +COMMENT ON COLUMN etl_admin.etl_task.window_minutes_default IS '默认抓取窗口(分钟)'; +COMMENT ON COLUMN etl_admin.etl_task.overlap_seconds IS '窗口重叠秒数,用于防重'; +COMMENT ON COLUMN etl_admin.etl_task.page_size IS 'API 分页大小'; +COMMENT ON COLUMN etl_admin.etl_task.retry_max IS 'API 最大重试次数'; +COMMENT ON COLUMN etl_admin.etl_task.params IS '额外参数 (JSON)'; +COMMENT ON COLUMN etl_admin.etl_task.created_at IS '创建时间'; +COMMENT ON COLUMN etl_admin.etl_task.updated_at IS '更新时间'; + +CREATE TABLE IF NOT EXISTS etl_admin.etl_cursor ( + cursor_id bigserial PRIMARY KEY, + task_id bigint NOT NULL REFERENCES etl_admin.etl_task(task_id) ON DELETE CASCADE, + store_id bigint NOT NULL, + last_start timestamptz, + last_end timestamptz, + last_id bigint, + extra jsonb NOT NULL DEFAULT '{}'::jsonb, + last_run_id bigint, + updated_at timestamptz NOT NULL DEFAULT now(), + UNIQUE (task_id, store_id) +); + +COMMENT ON TABLE etl_admin.etl_cursor IS 'ETL 游标存储(记录每个任务的上次窗口与 ID)'; +COMMENT ON COLUMN etl_admin.etl_cursor.cursor_id IS '游标主键'; +COMMENT ON COLUMN etl_admin.etl_cursor.task_id IS '关联任务 ID'; +COMMENT ON COLUMN etl_admin.etl_cursor.store_id IS '门店 ID'; +COMMENT ON COLUMN etl_admin.etl_cursor.last_start IS '上次窗口起始时间'; +COMMENT ON COLUMN etl_admin.etl_cursor.last_end IS '上次窗口结束时间'; +COMMENT ON COLUMN etl_admin.etl_cursor.last_id IS '上次处理的 ID(适用于基于 ID 的翻页)'; +COMMENT ON COLUMN etl_admin.etl_cursor.extra IS '额外游标信息 (JSON)'; +COMMENT ON COLUMN etl_admin.etl_cursor.last_run_id IS '最后一次运行 run_id'; +COMMENT ON COLUMN etl_admin.etl_cursor.updated_at IS '更新时间'; + +CREATE TABLE IF NOT EXISTS etl_admin.etl_run ( + run_id bigserial PRIMARY KEY, + run_uuid varchar(64) NOT NULL, + task_id bigint NOT NULL REFERENCES etl_admin.etl_task(task_id) ON DELETE CASCADE, + store_id bigint NOT NULL, + status etl_admin.run_status_enum NOT NULL DEFAULT 'SUCC', + started_at timestamptz NOT NULL DEFAULT now(), + ended_at timestamptz, + window_start timestamptz, + window_end timestamptz, + window_minutes integer, + overlap_seconds integer, + fetched_count integer DEFAULT 0, + loaded_count integer DEFAULT 0, + updated_count integer DEFAULT 0, + skipped_count integer DEFAULT 0, + error_count integer DEFAULT 0, + unknown_fields integer DEFAULT 0, + export_dir text, + log_path text, + request_params jsonb NOT NULL DEFAULT '{}'::jsonb, + manifest jsonb NOT NULL DEFAULT '{}'::jsonb, + error_message text, + extra jsonb NOT NULL DEFAULT '{}'::jsonb +); + +CREATE INDEX IF NOT EXISTS idx_etl_run_task ON etl_admin.etl_run(task_id, started_at DESC); +CREATE INDEX IF NOT EXISTS idx_etl_run_status ON etl_admin.etl_run(status); + +COMMENT ON TABLE etl_admin.etl_run IS 'ETL 运行记录(针对每次任务执行)'; +COMMENT ON COLUMN etl_admin.etl_run.run_id IS '运行记录 ID'; +COMMENT ON COLUMN etl_admin.etl_run.run_uuid IS '运行批次 UUID'; +COMMENT ON COLUMN etl_admin.etl_run.task_id IS '任务 ID'; +COMMENT ON COLUMN etl_admin.etl_run.store_id IS '门店 ID'; +COMMENT ON COLUMN etl_admin.etl_run.status IS '运行状态 (SUCC/FAIL/PARTIAL)'; +COMMENT ON COLUMN etl_admin.etl_run.started_at IS '开始时间'; +COMMENT ON COLUMN etl_admin.etl_run.ended_at IS '结束时间'; +COMMENT ON COLUMN etl_admin.etl_run.window_start IS '窗口起始时间'; +COMMENT ON COLUMN etl_admin.etl_run.window_end IS '窗口结束时间'; +COMMENT ON COLUMN etl_admin.etl_run.window_minutes IS '窗口长度(分钟)'; +COMMENT ON COLUMN etl_admin.etl_run.overlap_seconds IS '重叠秒数'; +COMMENT ON COLUMN etl_admin.etl_run.fetched_count IS '抓取条数'; +COMMENT ON COLUMN etl_admin.etl_run.loaded_count IS '插入条数'; +COMMENT ON COLUMN etl_admin.etl_run.updated_count IS '更新条数'; +COMMENT ON COLUMN etl_admin.etl_run.skipped_count IS '跳过条数'; +COMMENT ON COLUMN etl_admin.etl_run.error_count IS '错误条数'; +COMMENT ON COLUMN etl_admin.etl_run.unknown_fields IS '未知字段次数'; +COMMENT ON COLUMN etl_admin.etl_run.export_dir IS '导出目录'; +COMMENT ON COLUMN etl_admin.etl_run.log_path IS '日志文件路径'; +COMMENT ON COLUMN etl_admin.etl_run.request_params IS '本次请求参数 (JSON)'; +COMMENT ON COLUMN etl_admin.etl_run.manifest IS '导出 manifest (JSON)'; +COMMENT ON COLUMN etl_admin.etl_run.error_message IS '错误信息'; +COMMENT ON COLUMN etl_admin.etl_run.extra IS '额外信息 (JSON)'; + +