-- Data warehouse schema for the entertainment chain (ODS -> DWD -> DWS)
-- ASCII only to keep cross-platform friendly.

DROP SCHEMA IF EXISTS billiards_ods CASCADE;
CREATE SCHEMA IF NOT EXISTS billiards_ods;
CREATE SCHEMA IF NOT EXISTS billiards_dwd;
CREATE SCHEMA IF NOT EXISTS billiards_dws;

-- ---------- ODS (raw, lightly typed) ----------
-- Each ODS table keeps the source payload for replay/debug while exposing key fields.

CREATE TABLE IF NOT EXISTS billiards_ods.member_profiles (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    member_id           BIGINT          NOT NULL,
    system_member_id    BIGINT,
    register_site_id    BIGINT,
    site_name           TEXT,
    member_name         TEXT,
    nickname            TEXT,
    mobile              TEXT,
    gender              TEXT,
    birthday            DATE,
    register_time       TIMESTAMPTZ,
    member_type_id      BIGINT,
    member_type_name    TEXT,
    member_card_grade_code TEXT,
    status              TEXT,
    user_status         TEXT,
    balance             NUMERIC(18,2),
    points              NUMERIC(18,2),
    growth_value        NUMERIC(18,2),
    last_visit_time     TIMESTAMPTZ,
    wechat_id           TEXT,
    alipay_id           TEXT,
    member_card_no      TEXT,
    referrer_member_id  BIGINT,
    remarks             TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, member_id)
);
COMMENT ON TABLE billiards_ods.member_profiles IS '鏉ユ簮锛氫細鍛樻。妗?json -> data.tenantMemberInfos锛涗竴鏉¤褰曚唬琛ㄢ€滅鎴峰唴涓€寮犱細鍛樺崱璐︽埛鈥?;
COMMENT ON COLUMN billiards_ods.member_profiles.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.member_profiles.site_id IS '浠?register_site_id 琛ュ叆锛涙敞鍐岄棬搴桰D锛堟湰鎵逛负鍗曞簵锛?;
COMMENT ON COLUMN billiards_ods.member_profiles.member_id IS '婧愬瓧娈?id锛涚鎴峰唴浼氬憳璐︽埛涓婚敭锛堝搴斿崱璐︽埛/妗ｆID锛?;
COMMENT ON COLUMN billiards_ods.member_profiles.system_member_id IS '婧愬瓧娈?system_member_id锛涘叏骞冲彴浼氬憳ID锛屽彲璺ㄥ簵鑱氬悎鍚屼竴浜?;
COMMENT ON COLUMN billiards_ods.member_profiles.register_site_id IS '婧愬瓧娈?register_site_id锛涘姙鍗?娉ㄥ唽闂ㄥ簵ID';
COMMENT ON COLUMN billiards_ods.member_profiles.site_name IS '婧愬瓧娈?site_name锛涘姙鍗￠棬搴楀悕绉板揩鐓?;
COMMENT ON COLUMN billiards_ods.member_profiles.member_name IS '婧愭暟鎹湭鎻愪緵锛涢鐣欎細鍛樺疄鍚?;
COMMENT ON COLUMN billiards_ods.member_profiles.nickname IS '婧愬瓧娈?nickname锛涗細鍛樻樉绀哄悕/鏄电О';
COMMENT ON COLUMN billiards_ods.member_profiles.mobile IS '婧愬瓧娈?mobile锛涗細鍛樻墜鏈哄彿';
COMMENT ON COLUMN billiards_ods.member_profiles.gender IS '婧愭暟鎹湭鎻愪緵锛涢鐣欐€у埆';
COMMENT ON COLUMN billiards_ods.member_profiles.birthday IS '婧愭暟鎹湭鎻愪緵锛涢鐣欑敓鏃?;
COMMENT ON COLUMN billiards_ods.member_profiles.register_time IS '婧愬瓧娈?create_time锛涜处鎴峰垱寤烘椂闂?;
COMMENT ON COLUMN billiards_ods.member_profiles.member_type_id IS '婧愭暟鎹湭鎻愪緵锛涢鐣欎細鍛樼被鍨?绛夌骇ID';
COMMENT ON COLUMN billiards_ods.member_profiles.member_type_name IS '婧愭暟鎹湭鎻愪緵锛涢鐣欎細鍛樼被鍨?绛夌骇鍚嶇О';
COMMENT ON COLUMN billiards_ods.member_profiles.member_card_grade_code IS '婧愬瓧娈?member_card_grade_code锛涘崱绉?绛夌骇缂栫爜锛堝偍鍊煎崱/鍙拌垂鍗?鏈堝崱/娲诲姩鍒哥瓑锛?;
COMMENT ON COLUMN billiards_ods.member_profiles.status IS '婧愬瓧娈?status锛涘崱璐︽埛鐘舵€侊紙1姝ｅ父锛屽叾浠栦负鍋滅敤/浣滃簾锛?;
COMMENT ON COLUMN billiards_ods.member_profiles.user_status IS '婧愬瓧娈?user_status锛涚敤鎴峰眰鐘舵€侊紙1姝ｅ父锛?绂佺敤绛夛級';
COMMENT ON COLUMN billiards_ods.member_profiles.balance IS '婧愭暟鎹湭鎻愪緵锛涢鐣欎綑棰濆揩鐓э紙瀹為檯浣欓鍦ㄥ崱琛級';
COMMENT ON COLUMN billiards_ods.member_profiles.points IS '婧愬瓧娈?point锛涘綋鍓嶇Н鍒嗕綑棰濓紙鏈壒涓?锛?;
COMMENT ON COLUMN billiards_ods.member_profiles.growth_value IS '婧愬瓧娈?growth_value锛涙垚闀垮€?缁忛獙鍊硷紙鏈壒涓?锛?;
COMMENT ON COLUMN billiards_ods.member_profiles.last_visit_time IS '婧愭暟鎹湭鎻愪緵锛涢鐣欐渶杩戝埌搴楁椂闂?;
COMMENT ON COLUMN billiards_ods.member_profiles.wechat_id IS '婧愭暟鎹湭鎻愪緵锛涢鐣欏井淇D';
COMMENT ON COLUMN billiards_ods.member_profiles.alipay_id IS '婧愭暟鎹湭鎻愪緵锛涢鐣欐敮浠樺疂ID';
COMMENT ON COLUMN billiards_ods.member_profiles.member_card_no IS '婧愭暟鎹湭鎻愪緵锛涢鐣欏疄浣撳崱鍙?;
COMMENT ON COLUMN billiards_ods.member_profiles.referrer_member_id IS '婧愬瓧娈?referrer_member_id锛涙帹鑽愪汉浼氬憳ID锛?琛ㄧず鏃狅級';
COMMENT ON COLUMN billiards_ods.member_profiles.remarks IS '婧愭暟鎹湭鎻愪緵锛涢鐣欏娉?;
COMMENT ON COLUMN billiards_ods.member_profiles.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.member_profiles.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.member_profiles.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.member_profiles.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.member_stored_value_cards (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    card_id             BIGINT          NOT NULL,
    member_id           BIGINT,
    tenant_member_id    BIGINT,
    card_type_id        BIGINT,
    card_type_name      TEXT,
    card_no             TEXT,
    card_physics_type   TEXT,
    card_balance        NUMERIC(18,2),
    denomination        NUMERIC(18,2),
    discount_rate       NUMERIC(8,4),
    table_discount      NUMERIC(18,2),
    goods_discount      NUMERIC(18,2),
    assistant_discount  NUMERIC(18,2),
    assistant_reward_discount NUMERIC(18,2),
    valid_start_date    DATE,
    valid_end_date      DATE,
    disable_start_date  DATE,
    disable_end_date    DATE,
    last_consume_time   TIMESTAMPTZ,
    status              TEXT,
    is_delete           BOOLEAN,
    activate_time       TIMESTAMPTZ,
    deactivate_time     TIMESTAMPTZ,
    register_site_id    BIGINT,
    issuer_id           BIGINT,
    issuer_name         TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, card_id)
);
COMMENT ON TABLE billiards_ods.member_stored_value_cards IS '鏉ユ簮锛氬偍鍊煎崱鍒楄〃.json -> data.tenantMemberCards锛涘寘鍚偍鍊煎崱/鍙拌垂鍗?鏈堝崱/娲诲姩鍒哥瓑浼氬憳鍗″畾涔変笌浣欓';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.site_id IS '婧愬瓧娈?register_site_id锛涘紑鍗￠棬搴桰D';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.card_id IS '婧愬瓧娈?id锛涗細鍛樺崱璐︽埛ID';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.member_id IS '婧愬瓧娈?system_member_id锛涘钩鍙颁細鍛業D锛堟湭缁戝畾鏃朵负0锛?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.tenant_member_id IS '婧愬瓧娈?tenant_member_id锛涚鎴峰唴浼氬憳ID';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.card_type_id IS '婧愬瓧娈?card_type_id锛涘崱绉岻D';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.card_type_name IS '婧愬瓧娈?member_card_type_name锛涘崱绉嶅悕绉?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.card_no IS '婧愬瓧娈?card_no锛涘疄浣?铏氭嫙鍗″彿锛堟湰鎵逛负绌猴級';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.card_physics_type IS '婧愬瓧娈?card_physics_type锛涚墿鐞嗗崱绫诲瀷鏋氫妇锛堟湰鎵逛负1锛?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.card_balance IS '婧愬瓧娈?balance锛涘綋鍓嶅崱鍐呬綑棰?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.denomination IS '婧愬瓧娈?denomination锛涢潰棰?鍒濆鍌ㄥ€奸搴?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.discount_rate IS '婧愭暟鎹湭鎷嗗嚭缁熶竴鎶樻墸锛涘悇鍦烘櫙鎶樻墸瑙?table/goods/assistant 瀛楁';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.table_discount IS '婧愬瓧娈?table_discount锛涘彴璐规姌鎵ｏ紝10琛ㄧず涓嶆墦鎶?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.goods_discount IS '婧愬瓧娈?goods_discount锛涘晢鍝佹姌鎵?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.assistant_discount IS '婧愬瓧娈?assistant_discount锛涘姪鏁欐湇鍔℃姌鎵?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.assistant_reward_discount IS '婧愬瓧娈?assistant_reward_discount锛涘姪鏁欏鍔遍噾鎶樻墸';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.valid_start_date IS '婧愬瓧娈?start_time锛涘崱鏈夋晥鏈熻捣';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.valid_end_date IS '婧愬瓧娈?end_time锛涘崱鏈夋晥鏈熸';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.disable_start_date IS '婧愬瓧娈?disable_start_time锛涘仠鐢ㄥ紑濮嬫椂闂达紙鏈壒榛樿0001-01-01锛?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.disable_end_date IS '婧愬瓧娈?disable_end_time锛涘仠鐢ㄧ粨鏉熸椂闂?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.last_consume_time IS '婧愬瓧娈?last_consume_time锛涙渶杩戞秷璐规椂闂?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.status IS '婧愬瓧娈?status锛涘崱鐘舵€侊紙1姝ｅ父锛?杩囨湡/鍋滅敤绛夛級';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.is_delete IS '婧愬瓧娈?is_delete锛涢€昏緫鍒犻櫎鏍囪';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.activate_time IS '婧愬瓧娈?create_time锛涘紑鍗℃椂闂?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.deactivate_time IS '婧愭暟鎹湭鎻愪緵锛涢鐣欐敞閿€/鍋滅敤鏃堕棿';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.register_site_id IS '婧愬瓧娈?register_site_id锛涘紑鍗￠棬搴桰D';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.issuer_id IS '婧愭暟鎹湭鎻愪緵锛涢鐣欏紑鍗℃搷浣滀汉ID';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.issuer_name IS '婧愭暟鎹湭鎻愪緵锛涢鐣欏紑鍗℃搷浣滀汉鍚嶇О';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.member_stored_value_cards.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.member_balance_changes (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    change_id           BIGINT          NOT NULL,
    member_id           BIGINT,
    tenant_member_id    BIGINT,
    tenant_member_card_id BIGINT,
    member_name         TEXT,
    member_mobile       TEXT,
    change_amount       NUMERIC(18,2),
    balance_before      NUMERIC(18,2),
    balance_after       NUMERIC(18,2),
    change_type         INT,
    payment_method      INT,
    refund_amount       NUMERIC(18,2),
    relate_id           BIGINT,
    register_site_id    BIGINT,
    register_site_name  TEXT,
    pay_site_name       TEXT,
    remark              TEXT,
    operator_id         BIGINT,
    operator_name       TEXT,
    change_time         TIMESTAMPTZ,
    is_deleted          BOOLEAN         DEFAULT FALSE,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, change_id)
);
COMMENT ON TABLE billiards_ods.goods_stock_movements IS 'Source: 搴撳瓨鍙樺寲璁板綍1.json -> data.queryDeliveryRecordsList';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.tenant_id IS 'source: queryDeliveryRecordsList.tenantId; tenant/brand id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.site_id IS 'source: queryDeliveryRecordsList.siteId; store id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.change_id IS 'source: queryDeliveryRecordsList.siteGoodsStockId; stock change record id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.site_goods_id IS 'source: queryDeliveryRecordsList.siteGoodsId; site goods id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.goods_id IS 'not present in export; reserved for tenant goods id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.stock_type IS 'source: queryDeliveryRecordsList.stockType; stock change type';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.change_amount IS 'source: queryDeliveryRecordsList.changeNum; change amount (primary unit)';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.before_stock IS 'source: queryDeliveryRecordsList.startNum; stock before change';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.after_stock IS 'source: queryDeliveryRecordsList.endNum; stock after change';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.change_amount_alt IS 'source: queryDeliveryRecordsList.changeNumA; alt unit change amount';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.before_stock_alt IS 'source: queryDeliveryRecordsList.startNumA; alt unit stock before';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.after_stock_alt IS 'source: queryDeliveryRecordsList.endNumA; alt unit stock after';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.change_type IS 'source: queryDeliveryRecordsList.stockType; stock change type enum';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.relate_id IS 'not present in export; reserved for related business id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.unit IS 'source: queryDeliveryRecordsList.unit; unit name';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.price IS 'source: queryDeliveryRecordsList.price; unit price';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.goods_category_id IS 'source: queryDeliveryRecordsList.goodsCategoryId; category id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.goods_second_category_id IS 'source: queryDeliveryRecordsList.goodsSecondCategoryId; second category id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.remark IS 'source: queryDeliveryRecordsList.remark; remark';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.operator_id IS 'not present in export; reserved for operator id';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.operator_name IS 'source: queryDeliveryRecordsList.operatorName; operator name';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.change_time IS 'source: queryDeliveryRecordsList.createTime; change time';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.goods_stock_movements.payload IS 'raw JSON payload for replay/debug';
COMMENT ON TABLE billiards_ods.member_balance_changes IS '鏉ユ簮锛氫綑棰濆彉鏇磋褰?json -> data.tenantMemberCardLogs锛涜褰曟瘡娆″崱浣欓澧炲噺';
COMMENT ON COLUMN billiards_ods.member_balance_changes.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.member_balance_changes.site_id IS '婧愬瓧娈?site_id锛涘彂鐢熷彉鍔ㄧ殑闂ㄥ簵ID锛?琛ㄧず骞冲彴/璺ㄥ簵璋冩暣锛?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.change_id IS '婧愬瓧娈?id锛涗綑棰濆彉鏇磋褰曚富閿?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.member_id IS '婧愬瓧娈?system_member_id锛涘钩鍙颁細鍛業D';
COMMENT ON COLUMN billiards_ods.member_balance_changes.tenant_member_id IS '婧愬瓧娈?tenant_member_id锛涚鎴峰唴浼氬憳ID';
COMMENT ON COLUMN billiards_ods.member_balance_changes.tenant_member_card_id IS '婧愬瓧娈?tenant_member_card_id锛涗細鍛樺崱璐︽埛ID';
COMMENT ON COLUMN billiards_ods.member_balance_changes.member_name IS '婧愬瓧娈?memberName锛涗細鍛樺鍚嶅揩鐓?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.member_mobile IS '婧愬瓧娈?memberMobile锛涗細鍛樻墜鏈哄彿蹇収';
COMMENT ON COLUMN billiards_ods.member_balance_changes.change_amount IS '婧愬瓧娈?account_data锛涙湰娆″彉鍔ㄩ噾棰濓紝姝ｅ姞璐熷噺';
COMMENT ON COLUMN billiards_ods.member_balance_changes.balance_before IS '婧愬瓧娈?before锛涘彉鍔ㄥ墠浣欓';
COMMENT ON COLUMN billiards_ods.member_balance_changes.balance_after IS '婧愬瓧娈?after锛涘彉鍔ㄥ悗浣欓';
COMMENT ON COLUMN billiards_ods.member_balance_changes.change_type IS '婧愬瓧娈?from_type锛涘彉鍔ㄦ潵婧愮被鍨嬫灇涓?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.payment_method IS '婧愬瓧娈?payment_method锛涙敮浠樻柟寮忔灇涓?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.refund_amount IS '婧愬瓧娈?refund_amount锛涢€€娆鹃噾棰濓紙鏍锋湰涓?锛?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.relate_id IS '婧愬瓧娈?relate_id锛涘叧鑱斾笟鍔D锛堝厖鍊?缁撶畻/娲诲姩绛夛級';
COMMENT ON COLUMN billiards_ods.member_balance_changes.register_site_id IS '婧愬瓧娈?register_site_id锛涘姙鍗￠棬搴桰D';
COMMENT ON COLUMN billiards_ods.member_balance_changes.register_site_name IS '婧愬瓧娈?registerSiteName锛涘姙鍗￠棬搴楀悕绉?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.pay_site_name IS '婧愬瓧娈?paySiteName锛涙湰娆″彂鐢熼棬搴楀悕绉?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.remark IS '婧愬瓧娈?remark锛涘娉?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.operator_id IS '婧愬瓧娈?operator_id锛涙搷浣滃憳ID';
COMMENT ON COLUMN billiards_ods.member_balance_changes.operator_name IS '婧愬瓧娈?operator_name锛涙搷浣滃憳鍚嶇О';
COMMENT ON COLUMN billiards_ods.member_balance_changes.change_time IS '婧愬瓧娈?create_time锛涘彉鍔ㄦ椂闂?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.is_deleted IS '婧愬瓧娈?is_delete锛涢€昏緫鍒犻櫎鏍囪';
COMMENT ON COLUMN billiards_ods.member_balance_changes.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.member_balance_changes.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.member_balance_changes.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.member_balance_changes.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_recharge_record (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    recharge_id         BIGINT          NOT NULL,
    member_id           BIGINT,
    recharge_amount     NUMERIC(18,2),
    gift_amount         NUMERIC(18,2),
    pay_method          INT,
    pay_trade_no        TEXT,
    order_trade_no      TEXT,
    recharge_time       TIMESTAMPTZ,
    status              TEXT,
    operator_id         BIGINT,
    operator_name       TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, recharge_id)
);
COMMENT ON TABLE billiards_ods.ods_recharge_record IS '鏉ユ簮锛氬厖鍊艰褰?json -> data.settleList[].settleList锛涙部鐢ㄧ粨绠楀崟妯″瀷鐨勫厖鍊兼祦姘?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.tenant_id IS '婧愬瓧娈?tenantId锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.ods_recharge_record.site_id IS '婧愬瓧娈?siteId锛涘厖鍊煎彂鐢熼棬搴?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.recharge_id IS '婧愬瓧娈?id锛涘厖鍊艰褰旾D';
COMMENT ON COLUMN billiards_ods.ods_recharge_record.member_id IS '婧愬瓧娈?memberId锛涗細鍛業D锛堝彲涓?琛ㄧず鏈粦浼氬憳锛?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.recharge_amount IS '婧愬瓧娈?payAmount锛涙湰娆″厖鍊兼敮浠橀噾棰?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.gift_amount IS '婧愬瓧娈?giftCardAmount锛涜禒閫侀噾棰濓紙鏍锋湰涓?锛?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.pay_method IS '婧愬瓧娈?paymentMethod锛涙敮浠樻柟寮忔灇涓?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.pay_trade_no IS '婧愭暟鎹湭缁欙紱棰勭暀澶栭儴浜ゆ槗鍙?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.order_trade_no IS '婧愬瓧娈?settleRelateId 鍙槧灏勶紱鍏宠仈涓氬姟鍗曞彿';
COMMENT ON COLUMN billiards_ods.ods_recharge_record.recharge_time IS '婧愬瓧娈?payTime锛涙敮浠樻椂闂?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.status IS '婧愬瓧娈?settleStatus锛涘厖鍊肩姸鎬侊紙2鎴愬姛绛夛級';
COMMENT ON COLUMN billiards_ods.ods_recharge_record.operator_id IS '婧愬瓧娈?operatorId锛涙搷浣滃憳ID';
COMMENT ON COLUMN billiards_ods.ods_recharge_record.operator_name IS '婧愬瓧娈?operatorName锛涙搷浣滃憳鍚嶇О';
COMMENT ON COLUMN billiards_ods.ods_recharge_record.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.ods_recharge_record.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.ods_recharge_record.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_recharge_record.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_product (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    goods_id            BIGINT          NOT NULL,
    goods_name          TEXT,
    goods_code          TEXT,
    category_id         BIGINT,
    category_name       TEXT,
    goods_second_category_id BIGINT,
    unit                TEXT,
    price               NUMERIC(18,2),
    cost_price          NUMERIC(18,2),
    market_price        NUMERIC(18,2),
    goods_state         TEXT,
    goods_cover         TEXT,
    goods_bar_code      TEXT,
    able_discount       BOOLEAN,
    is_delete           BOOLEAN,
    status              TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, goods_id)
);
COMMENT ON TABLE billiards_ods.ods_product IS '鏉ユ簮锛氬晢鍝佹。妗?json -> data.tenantGoodsList锛涚鎴风骇鍟嗗搧鍩虹淇℃伅';
COMMENT ON COLUMN billiards_ods.ods_product.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.ods_product.site_id IS '婧愭暟鎹湭缁欙紱鍟嗗搧涓虹鎴风骇锛屾寜閲囬泦涓婁笅鏂囪祴鍊?;
COMMENT ON COLUMN billiards_ods.ods_product.goods_id IS '婧愬瓧娈?id锛涚鎴峰晢鍝両D';
COMMENT ON COLUMN billiards_ods.ods_product.goods_name IS '婧愬瓧娈?goods_name锛涘晢鍝佸悕绉?;
COMMENT ON COLUMN billiards_ods.ods_product.goods_code IS '婧愬瓧娈?commodityCode/commodity_code锛涘唴閮ㄧ紪鐮?鏉＄爜';
COMMENT ON COLUMN billiards_ods.ods_product.category_id IS '婧愬瓧娈?goods_category_id锛涗竴绾у垎绫籌D';
COMMENT ON COLUMN billiards_ods.ods_product.category_name IS '婧愬瓧娈?categoryName锛涗竴绾у垎绫诲悕绉?;
COMMENT ON COLUMN billiards_ods.ods_product.goods_second_category_id IS '婧愬瓧娈?goods_second_category_id锛涗簩绾у垎绫籌D';
COMMENT ON COLUMN billiards_ods.ods_product.unit IS '婧愬瓧娈?unit锛涜閲忓崟浣?;
COMMENT ON COLUMN billiards_ods.ods_product.price IS '婧愭暟鎹湭鐩存帴缁欙紱鍙寜 market_price/min_discount_price 琛ュ厖';
COMMENT ON COLUMN billiards_ods.ods_product.cost_price IS '婧愬瓧娈?cost_price锛涙垚鏈环';
COMMENT ON COLUMN billiards_ods.ods_product.market_price IS '婧愬瓧娈?market_price锛涘競鍦?鏍囦环';
COMMENT ON COLUMN billiards_ods.ods_product.goods_state IS '婧愬瓧娈?goods_state锛涗笂鏋剁姸鎬?;
COMMENT ON COLUMN billiards_ods.ods_product.goods_cover IS '婧愬瓧娈?goods_cover锛涘晢鍝佸浘鐗嘦RL';
COMMENT ON COLUMN billiards_ods.ods_product.goods_bar_code IS '婧愬瓧娈?goods_bar_code锛涙潯鐮?;
COMMENT ON COLUMN billiards_ods.ods_product.able_discount IS '婧愬瓧娈?able_discount锛涙槸鍚﹀彲鎵撴姌';
COMMENT ON COLUMN billiards_ods.ods_product.is_delete IS '婧愬瓧娈?is_delete锛涢€昏緫鍒犻櫎鏍囪';
COMMENT ON COLUMN billiards_ods.ods_product.status IS '涓?goods_state 瀵归綈鐨勯€氱敤鐘舵€?;
COMMENT ON COLUMN billiards_ods.ods_product.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.ods_product.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.ods_product.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_product.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_store_product (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    site_goods_id       BIGINT          NOT NULL,
    goods_id            BIGINT,
    goods_name          TEXT,
    category_id         BIGINT,
    category_name       TEXT,
    unit                TEXT,
    sale_price          NUMERIC(18,2),
    cost_price          NUMERIC(18,2),
    sale_num            NUMERIC(18,2),
    stock_a             NUMERIC(18,2),
    stock               NUMERIC(18,2),
    provisional_total_cost NUMERIC(18,2),
    total_purchase_cost NUMERIC(18,2),
    batch_stock_quantity NUMERIC(18,2),
    goods_state         TEXT,
    status              TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, site_goods_id)
);
COMMENT ON TABLE billiards_ods.ods_store_product IS '鏉ユ簮锛氶棬搴楀晢鍝佹。妗?.json -> data.orderGoodsList锛涢棬搴楃淮搴﹀晢鍝佽鍥?;
COMMENT ON COLUMN billiards_ods.ods_store_product.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.ods_store_product.site_id IS '婧愬瓧娈?site_id锛涢棬搴桰D';
COMMENT ON COLUMN billiards_ods.ods_store_product.site_goods_id IS '婧愬瓧娈?id锛涢棬搴楀晢鍝両D';
COMMENT ON COLUMN billiards_ods.ods_store_product.goods_id IS '婧愬瓧娈?tenant_goods_id锛涚鎴风骇鍟嗗搧ID';
COMMENT ON COLUMN billiards_ods.ods_store_product.goods_name IS '婧愬瓧娈?goods_name锛涘晢鍝佸悕绉?;
COMMENT ON COLUMN billiards_ods.ods_store_product.category_id IS '婧愬瓧娈?goods_category_id锛涘垎绫籌D';
COMMENT ON COLUMN billiards_ods.ods_store_product.category_name IS '婧愬瓧娈?twoCategoryName/oneCategoryName锛涘垎绫诲悕绉板揩鐓?;
COMMENT ON COLUMN billiards_ods.ods_store_product.unit IS '婧愬瓧娈?unit锛涜閲忓崟浣?;
COMMENT ON COLUMN billiards_ods.ods_store_product.sale_price IS '婧愬瓧娈?sale_price锛涢攢鍞环';
COMMENT ON COLUMN billiards_ods.ods_store_product.cost_price IS '婧愬瓧娈?cost_price锛涙垚鏈环';
COMMENT ON COLUMN billiards_ods.ods_store_product.sale_num IS '婧愬瓧娈?sale_num锛涚疮璁￠攢閲?;
COMMENT ON COLUMN billiards_ods.ods_store_product.stock_a IS '婧愬瓧娈?stock_A锛涘鐢ㄥ簱瀛樻暟閲?;
COMMENT ON COLUMN billiards_ods.ods_store_product.stock IS '婧愬瓧娈?stock锛涘綋鍓嶅簱瀛?;
COMMENT ON COLUMN billiards_ods.ods_store_product.provisional_total_cost IS '婧愬瓧娈?provisional_total_cost锛涙殏浼板簱瀛樻垚鏈?;
COMMENT ON COLUMN billiards_ods.ods_store_product.total_purchase_cost IS '婧愬瓧娈?total_purchase_cost锛涜繘璐ф€绘垚鏈?;
COMMENT ON COLUMN billiards_ods.ods_store_product.batch_stock_quantity IS '婧愬瓧娈?batch_stock_quantity锛涙壒娆″簱瀛樻暟閲?;
COMMENT ON COLUMN billiards_ods.ods_store_product.goods_state IS '婧愬瓧娈?goods_state锛涘晢鍝佺姸鎬?;
COMMENT ON COLUMN billiards_ods.ods_store_product.status IS '婧愬瓧娈?enable_status锛涗笂涓嬫灦鐘舵€?;
COMMENT ON COLUMN billiards_ods.ods_store_product.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.ods_store_product.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.ods_store_product.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_store_product.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_store_sale_item (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    sale_item_id        BIGINT          NOT NULL,
    order_goods_id      BIGINT,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    site_goods_id       BIGINT,
    goods_id            BIGINT,
    goods_name          TEXT,
    category_id         BIGINT,
    quantity            NUMERIC(18,4),
    unit_price          NUMERIC(18,2),
    original_amount     NUMERIC(18,2),
    discount_amount     NUMERIC(18,2),
    final_amount        NUMERIC(18,2),
    is_gift             BOOLEAN         DEFAULT FALSE,
    sale_time           TIMESTAMPTZ,
    member_id           BIGINT,
    salesman_id         BIGINT,
    operator_id         BIGINT,
    is_refunded         BOOLEAN         DEFAULT FALSE,
    discount_price      NUMERIC(18,2),
    cost_money          NUMERIC(18,2),
    coupon_deduct_amount NUMERIC(18,2),
    member_discount_amount NUMERIC(18,2),
    point_discount_money NUMERIC(18,2),
    point_discount_cost NUMERIC(18,2),
    sales_type          TEXT,
    goods_remark        TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, sale_item_id)
);
COMMENT ON TABLE billiards_ods.ods_store_sale_item IS '鏉ユ簮锛氶棬搴楀晢鍝侀攢鍞褰?json -> data.orderGoodsLedgers锛涘晢鍝佹槑缁嗘祦姘?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.site_id IS '婧愬瓧娈?site_id锛涢棬搴桰D';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.sale_item_id IS '婧愬瓧娈?id锛涘晢鍝佹槑缁嗘祦姘碔D';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.order_goods_id IS '婧愬瓧娈?order_goods_id锛涗笂娓歌鍗曞晢鍝両D';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.order_trade_no IS '婧愬瓧娈?order_trade_no锛涗笟鍔¤鍗曞彿';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.order_settle_id IS '婧愬瓧娈?order_settle_id锛涚粨绠楀崟ID';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.site_goods_id IS '婧愬瓧娈?site_goods_id锛涢棬搴楀晢鍝両D';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.goods_id IS '婧愬瓧娈?tenant_goods_id锛涚鎴风骇鍟嗗搧ID';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.goods_name IS '婧愬瓧娈?ledger_name锛涘晢鍝佸悕绉板揩鐓?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.category_id IS '婧愬瓧娈?tenant_goods_category_id锛涘晢鍝佸垎绫籌D';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.quantity IS '婧愬瓧娈?ledger_count锛涢攢鍞暟閲?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.unit_price IS '婧愬瓧娈?ledger_unit_price锛涚粨绠楀崟浠?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.original_amount IS '婧愬瓧娈?ledger_amount锛涘師濮嬮噾棰濓紙鏈墸鍑忥級';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.discount_amount IS '婧愬瓧娈?discount_money锛涙姌鎵ｉ噾棰?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.final_amount IS '婧愬瓧娈?real_goods_money锛涙姌鍚庡疄鏀堕噾棰?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.is_gift IS '鐢?sales_type/ledger_status 鎺ㄥ锛涜禒閫?閫€璐у満鏅爣璁?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.sale_time IS '婧愬瓧娈?create_time锛涢攢鍞椂闂?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.member_id IS '婧愭暟鎹湭缁欙紱棰勭暀浼氬憳ID';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.salesman_id IS '婧愬瓧娈?salesman_user_id锛涢攢鍞憳ID';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.operator_id IS '婧愬瓧娈?operator_id锛涙敹閾?鎿嶄綔鍛業D';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.is_refunded IS '鐢?returns_number>0 鎴?ledger_status 鍒ゆ柇锛涢€€璐ф爣璁?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.discount_price IS '婧愬瓧娈?discount_price锛涙姌鍚庡崟浠凤紙濡傛湁锛?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.cost_money IS '婧愬瓧娈?cost_money锛涙垚鏈噾棰?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.coupon_deduct_amount IS '婧愬瓧娈?coupon_deduct_money + option_coupon_deduct_money锛涘埜鎶垫墸閲戦';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.member_discount_amount IS '婧愬瓧娈?member_discount_amount锛涗細鍛樻姌鎵ｉ噾棰?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.point_discount_money IS '婧愬瓧娈?point_discount_money锛涚Н鍒嗘姷鎵ｉ噾棰?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.point_discount_cost IS '婧愬瓧娈?point_discount_money_cost锛涚Н鍒嗘姷鎵ｆ垚鏈?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.sales_type IS '婧愬瓧娈?sales_type锛涢攢鍞被鍨嬫灇涓?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.goods_remark IS '婧愬瓧娈?goods_remark锛涘晢鍝佸娉?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_store_sale_item.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_table_info (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    table_id            BIGINT          NOT NULL,
    table_code          TEXT,
    table_name          TEXT,
    table_type          TEXT,
    area_name           TEXT,
    site_table_area_id  BIGINT,
    tenant_table_area_id BIGINT,
    table_price         NUMERIC(18,2),
    table_status        TEXT,
    audit_status        INT,
    show_status         INT,
    light_status        INT,
    virtual_table       BOOLEAN,
    is_rest_area        BOOLEAN,
    charge_free         BOOLEAN,
    table_cloth_use_time INT,
    table_cloth_use_cycle INT,
    status              TEXT,
    created_time        TIMESTAMPTZ,
    updated_time        TIMESTAMPTZ,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, table_id)
);
COMMENT ON TABLE billiards_ods.ods_table_info IS '鏉ユ簮锛氬彴妗屽垪琛?json -> data.siteTables锛涢棬搴楃悆妗?鍖呭帰妗ｆ';
COMMENT ON COLUMN billiards_ods.ods_table_info.tenant_id IS '婧愭暟鎹湭缁欙紱鎸夐噰闆嗕笂涓嬫枃璁剧疆';
COMMENT ON COLUMN billiards_ods.ods_table_info.site_id IS '婧愬瓧娈?site_id锛涢棬搴桰D';
COMMENT ON COLUMN billiards_ods.ods_table_info.table_id IS '婧愬瓧娈?id锛涚悆妗?鍖呭帰ID';
COMMENT ON COLUMN billiards_ods.ods_table_info.table_code IS '婧愭暟鎹湭缁欙紱棰勭暀鍐呴儴缂栧彿';
COMMENT ON COLUMN billiards_ods.ods_table_info.table_name IS '婧愬瓧娈?table_name锛涙鍙?鍚嶇О';
COMMENT ON COLUMN billiards_ods.ods_table_info.table_type IS '婧愭暟鎹湭缁欙紱棰勭暀妗屽瀷';
COMMENT ON COLUMN billiards_ods.ods_table_info.area_name IS '婧愬瓧娈?areaName锛涙墍灞炲尯鍩熷悕绉?;
COMMENT ON COLUMN billiards_ods.ods_table_info.site_table_area_id IS '婧愬瓧娈?site_table_area_id锛涢棬搴楀尯鍩烮D';
COMMENT ON COLUMN billiards_ods.ods_table_info.tenant_table_area_id IS '婧愭暟鎹湭缁欙紱棰勭暀绉熸埛绾у尯鍩烮D';
COMMENT ON COLUMN billiards_ods.ods_table_info.table_price IS '婧愬瓧娈?table_price锛涘熀纭€鍙拌垂鍗曚环';
COMMENT ON COLUMN billiards_ods.ods_table_info.table_status IS '婧愬瓧娈?table_status锛涘綋鍓嶆鐘舵€?;
COMMENT ON COLUMN billiards_ods.ods_table_info.audit_status IS '婧愬瓧娈?audit_status锛涘鏍哥姸鎬?;
COMMENT ON COLUMN billiards_ods.ods_table_info.show_status IS '婧愬瓧娈?show_status锛涙樉绀虹姸鎬?;
COMMENT ON COLUMN billiards_ods.ods_table_info.light_status IS '婧愬瓧娈?light_status锛涚伅鎺х姸鎬?;
COMMENT ON COLUMN billiards_ods.ods_table_info.virtual_table IS '婧愬瓧娈?virtual_table锛涙槸鍚﹁櫄鎷熸';
COMMENT ON COLUMN billiards_ods.ods_table_info.is_rest_area IS '婧愬瓧娈?is_rest_area锛涙槸鍚︿紤鎭尯';
COMMENT ON COLUMN billiards_ods.ods_table_info.charge_free IS '婧愬瓧娈?charge_free锛涙槸鍚﹀厤璐?;
COMMENT ON COLUMN billiards_ods.ods_table_info.table_cloth_use_time IS '婧愬瓧娈?table_cloth_use_time锛涘彴鍛㈠凡鐢ㄦ椂闀匡紙绉掞級';
COMMENT ON COLUMN billiards_ods.ods_table_info.table_cloth_use_cycle IS '婧愬瓧娈?table_cloth_use_Cycle锛涘彴鍛㈡洿鎹㈠懆鏈熼槇鍊硷紙绉掞級';
COMMENT ON COLUMN billiards_ods.ods_table_info.status IS '閫氱敤鐘舵€侊紱鍙粨鍚?table_status/show_status 浣跨敤';
COMMENT ON COLUMN billiards_ods.ods_table_info.created_time IS '婧愬瓧娈?create_time锛涘垱寤烘椂闂?;
COMMENT ON COLUMN billiards_ods.ods_table_info.updated_time IS '婧愭暟鎹湭缁欙紱棰勭暀鏇存柊鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_table_info.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.ods_table_info.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.ods_table_info.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_table_info.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.table_fee_transactions_log (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    ledger_id           BIGINT          NOT NULL,
    table_id            BIGINT,
    table_name          TEXT,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    start_time          TIMESTAMPTZ,
    end_time            TIMESTAMPTZ,
    duration_seconds    INT,
    billing_unit_price  NUMERIC(18,4),
    billing_count       NUMERIC(18,4),
    original_table_fee  NUMERIC(18,2),
    member_discount_amount NUMERIC(18,2),
    coupon_discount_amount NUMERIC(18,2),
    manual_discount_amount NUMERIC(18,2),
    service_fee         NUMERIC(18,2),
    final_table_fee     NUMERIC(18,2),
    member_id           BIGINT,
    operator_id         BIGINT,
    salesman_id         BIGINT,
    is_canceled         BOOLEAN         DEFAULT FALSE,
    cancel_time         TIMESTAMPTZ,
    site_table_area_id  BIGINT,
    tenant_table_area_id BIGINT,
    site_table_area_name TEXT,
    is_single_order     BOOLEAN,
    used_card_amount    NUMERIC(18,2),
    adjust_amount       NUMERIC(18,2),
    coupon_promotion_amount NUMERIC(18,2),
    service_money       NUMERIC(18,2),
    mgmt_fee            NUMERIC(18,2),
    fee_total           NUMERIC(18,2),
    real_table_use_seconds INT,
    last_use_time       TIMESTAMPTZ,
    ledger_start_time   TIMESTAMPTZ,
    ledger_end_time     TIMESTAMPTZ,
    ledger_status       INT,
    start_use_time      TIMESTAMPTZ,
    add_clock_seconds   INT,
    status              TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, ledger_id)
);
COMMENT ON TABLE billiards_ods.ods_assistant_service_log IS 'Source: 鍔╂暀娴佹按.json -> data.orderAssistantDetails';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.tenant_id IS 'source: orderAssistantDetails.tenant_id; tenant/brand id';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.site_id IS 'source: orderAssistantDetails.site_id; store id';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.ledger_id IS 'source: orderAssistantDetails.id; assistant service ledger id';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.assistant_id IS 'source: orderAssistantDetails.site_assistant_id; assistant id';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.assistant_name IS 'source: orderAssistantDetails.assistantName; assistant name';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.service_type IS 'source: orderAssistantDetails.order_assistant_type; service type code';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.order_trade_no IS 'source: orderAssistantDetails.order_trade_no; business order number';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.order_settle_id IS 'source: orderAssistantDetails.order_settle_id; settlement id';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.start_time IS 'source: orderAssistantDetails.start_use_time; service start time';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.end_time IS 'source: orderAssistantDetails.ledger_end_time; service end time';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.duration_seconds IS 'source: orderAssistantDetails.real_use_seconds; actual service seconds';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.original_fee IS 'source: orderAssistantDetails.ledger_amount; amount before discounts';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.member_discount_amount IS 'source: orderAssistantDetails.member_discount_amount; member discount amount';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.manual_discount_amount IS 'source: orderAssistantDetails.manual_discount_amount; manual discount';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.coupon_discount_amount IS 'source: orderAssistantDetails.coupon_deduct_money; coupon deduction';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.final_fee IS 'source: orderAssistantDetails.service_money; final assistant fee';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.member_id IS 'source: orderAssistantDetails.system_member_id; member id';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.operator_id IS 'source: orderAssistantDetails.operator_id; operator id';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.salesman_id IS 'source: orderAssistantDetails.salesman_user_id; salesperson id';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.is_canceled IS 'derived; set when is_delete/is_trash indicates cancellation';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.cancel_time IS 'not present in export; reserved for cancel time';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.skill_grade IS 'source: orderAssistantDetails.skill_grade; skill grade score';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.service_grade IS 'source: orderAssistantDetails.service_grade; service grade score';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.composite_grade IS 'source: orderAssistantDetails.composite_grade; composite score';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.overall_score IS 'source: orderAssistantDetails.sum_grade; overall score sum';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.status IS 'source: orderAssistantDetails.ledger_status/grade_status; status flag';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.ods_assistant_service_log.payload IS 'raw JSON payload for replay/debug';
COMMENT ON TABLE billiards_ods.table_fee_transactions_log IS '鏉ユ簮锛氬彴璐规祦姘?json -> data.siteTableUseDetailsList锛涘彴璐硅璐规祦姘?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.site_id IS '婧愬瓧娈?site_id锛涢棬搴桰D';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.ledger_id IS '婧愬瓧娈?id锛涘彴璐硅处鍗旾D';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.table_id IS '婧愬瓧娈?site_table_id锛涚悆妗孖D';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.table_name IS '婧愬瓧娈?ledger_name锛涙鍚嶅揩鐓?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.order_trade_no IS '婧愬瓧娈?order_trade_no锛涗笟鍔¤鍗曞彿';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.order_settle_id IS '婧愬瓧娈?order_settle_id锛涚粨绠楀崟ID';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.start_time IS '婧愬瓧娈?ledger_start_time锛涜璐瑰紑濮嬫椂闂?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.end_time IS '婧愬瓧娈?ledger_end_time锛涜璐圭粨鏉熸椂闂?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.duration_seconds IS '婧愬瓧娈?ledger_count锛涜璐规椂闀?鏁伴噺锛堢锛?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.billing_unit_price IS '婧愬瓧娈?ledger_unit_price锛涜璐瑰崟浠?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.billing_count IS '婧愬瓧娈?ledger_count锛涜璐规暟閲?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.original_table_fee IS '婧愬瓧娈?ledger_amount锛涙姌鎵ｅ墠閲戦';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.member_discount_amount IS '婧愬瓧娈?member_discount_amount锛涗細鍛樻姌鎵?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.coupon_discount_amount IS '婧愬瓧娈?coupon_promotion_amount锛涘埜鎶垫墸';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.manual_discount_amount IS '婧愬瓧娈?adjust_amount锛涙墜宸ヤ紭鎯?璋冩暣';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.service_fee IS '婧愬瓧娈?service_money锛涙湇鍔¤垂';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.final_table_fee IS '婧愬瓧娈?real_table_charge_money锛涙渶缁堝彴璐?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.member_id IS '婧愬瓧娈?member_id锛涗細鍛業D';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.operator_id IS '婧愬瓧娈?operator_id锛涙搷浣滃憳ID';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.salesman_id IS '婧愬瓧娈?salesman_user_id锛涢攢鍞憳ID';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.is_canceled IS '鐢?ledger_status/is_delete 鎺ㄥ锛涙槸鍚﹀彇娑?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.cancel_time IS '婧愭暟鎹湭缁欙紱棰勭暀鍙栨秷鏃堕棿';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.site_table_area_id IS '婧愬瓧娈?site_table_area_id锛涢棬搴楀尯鍩烮D';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.tenant_table_area_id IS '婧愬瓧娈?tenant_table_area_id锛涚鎴峰尯鍩烮D';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.site_table_area_name IS '婧愬瓧娈?site_table_area_name锛涘尯鍩熷悕绉?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.is_single_order IS '婧愬瓧娈?is_single_order锛涘崟鍙?骞跺崟鏍囪';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.used_card_amount IS '婧愬瓧娈?used_card_amount锛涘崱鏀粯閲戦';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.adjust_amount IS '婧愬瓧娈?adjust_amount锛涜皟鏁撮噾棰?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.coupon_promotion_amount IS '婧愬瓧娈?coupon_promotion_amount锛涗紭鎯犲埜淇冮攢閲戦';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.service_money IS '婧愬瓧娈?service_money锛涙湇鍔¤垂閲戦';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.mgmt_fee IS '婧愬瓧娈?mgmt_fee锛涚鐞嗚垂';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.fee_total IS '婧愬瓧娈?fee_total锛涙€昏垂鐢紙鍚績閿€鍚庯級';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.real_table_use_seconds IS '婧愬瓧娈?real_table_use_seconds锛涘疄闄呬娇鐢ㄧ鏁?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.last_use_time IS '婧愬瓧娈?last_use_time锛涙渶杩戜娇鐢ㄧ粨鏉熸椂闂?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.ledger_start_time IS '婧愬瓧娈?ledger_start_time锛涜璐瑰紑濮?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.ledger_end_time IS '婧愬瓧娈?ledger_end_time锛涜璐圭粨鏉?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.ledger_status IS '婧愬瓧娈?ledger_status锛涜处鍗曠姸鎬?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.start_use_time IS '婧愬瓧娈?start_use_time锛涘疄闄呭紑濮嬬敤鍙版椂闂?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.add_clock_seconds IS '婧愬瓧娈?add_clock_seconds锛涢澶栬鏃剁鏁?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.status IS '閫氱敤鐘舵€侊紱缁撳悎 ledger_status/is_delete 鍒ゅ畾';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.table_fee_transactions_log.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_table_fee_adjust (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    adjust_id           BIGINT          NOT NULL,
    ledger_id           BIGINT,
    order_trade_no      TEXT,
    discount_amount     NUMERIC(18,2),
    reason              TEXT,
    operator_id         BIGINT,
    operator_name       TEXT,
    created_at          TIMESTAMPTZ,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, adjust_id)
);
COMMENT ON TABLE billiards_ods.ods_table_fee_adjust IS '鏉ユ簮锛氬彴璐规墦鎶?json -> data.taiFeeAdjustInfos锛涘彴璐规墜宸ユ姌鎵?璋冩暣璁板綍';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.site_id IS '婧愬瓧娈?site_id锛涢棬搴桰D';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.adjust_id IS '婧愬瓧娈?id锛涘彴璐规姌鎵ｈ褰旾D';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.ledger_id IS '婧愬瓧娈?order_settle_id锛涘叧鑱斿彴璐硅处鍗?缁撶畻ID';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.order_trade_no IS '婧愬瓧娈?order_trade_no锛涗笟鍔¤鍗曞彿';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.discount_amount IS '婧愬瓧娈?ledger_amount锛涙姌鎵ｉ噾棰?;
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.reason IS '婧愬瓧娈?adjust_type/applicant_* 缁勫悎锛涜皟鏁村師鍥狅紙鏄庣粏闇€缁撳悎 payload锛?;
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.operator_id IS '婧愬瓧娈?operator_id锛涙搷浣滃憳ID';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.operator_name IS '婧愬瓧娈?operator_name锛涙搷浣滃憳鍚嶇О';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.created_at IS '婧愬瓧娈?create_time锛涘垱寤烘椂闂?;
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_table_fee_adjust.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.assistant_accounts_master (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    assistant_id        BIGINT          NOT NULL,
    assistant_name      TEXT,
    mobile              TEXT,
    assistant_no        INT,
    team_id             BIGINT,
    team_name           TEXT,
    group_id            BIGINT,
    group_name          TEXT,
    job_num             TEXT,
    entry_type          TEXT,
    leave_status        TEXT,
    assistant_status    TEXT,
    allow_cx            BOOLEAN,
    status              TEXT,
    hired_date          DATE,
    left_date           DATE,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, assistant_id)
);
COMMENT ON TABLE billiards_ods.assistant_accounts_master IS 'Source: 鍔╂暀璐﹀彿.json -> data.assistantInfos';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.tenant_id IS 'source: assistantInfos.tenant_id; tenant/brand id';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.site_id IS 'source: assistantInfos.site_id; store id';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.assistant_id IS 'source: assistantInfos.id; assistant account id';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.assistant_name IS 'source: assistantInfos.real_name; assistant real name';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.mobile IS 'source: assistantInfos.mobile; phone number';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.assistant_no IS 'source: assistantInfos.assistant_no; assistant code';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.team_id IS 'source: assistantInfos.team_id; team id';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.team_name IS 'source: assistantInfos.team_name; team name';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.group_id IS 'source: assistantInfos.group_id; group id';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.group_name IS 'source: assistantInfos.group_name; group name';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.job_num IS 'source: assistantInfos.job_num; job number';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.entry_type IS 'source: assistantInfos.entry_type; entry type enum';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.leave_status IS 'source: assistantInfos.leave_status; on-duty/off-duty status';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.assistant_status IS 'source: assistantInfos.assistant_status; account enable flag';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.allow_cx IS 'source: assistantInfos.allow_cx; allow promotion/discount flag';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.status IS 'source: assistantInfos.show_status/work_status; general status flag';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.hired_date IS 'source: assistantInfos.entry_time; hire date';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.left_date IS 'source: assistantInfos.resign_time; resign date';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.assistant_accounts_master.payload IS 'raw JSON payload for replay/debug';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_assistant_service_log (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    ledger_id           BIGINT          NOT NULL,
    assistant_id        BIGINT,
    assistant_name      TEXT,
    service_type        TEXT,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    start_time          TIMESTAMPTZ,
    end_time            TIMESTAMPTZ,
    duration_seconds    INT,
    original_fee        NUMERIC(18,2),
    member_discount_amount NUMERIC(18,2),
    manual_discount_amount NUMERIC(18,2),
    coupon_discount_amount NUMERIC(18,2),
    final_fee           NUMERIC(18,2),
    member_id           BIGINT,
    operator_id         BIGINT,
    salesman_id         BIGINT,
    is_canceled         BOOLEAN         DEFAULT FALSE,
    cancel_time         TIMESTAMPTZ,
    skill_grade         NUMERIC(10,2),
    service_grade       NUMERIC(10,2),
    composite_grade     NUMERIC(10,2),
    overall_score       NUMERIC(10,2),
    status              TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, ledger_id)
);

CREATE TABLE IF NOT EXISTS billiards_ods.ods_assistant_cancel_log (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    cancel_id           BIGINT          NOT NULL,
    ledger_id           BIGINT,
    assistant_id        BIGINT,
    order_trade_no      TEXT,
    table_id            BIGINT,
    table_area_id       BIGINT,
    table_area_name     TEXT,
    table_name          TEXT,
    assistant_on        INT,
    pd_charge_minutes   INT,
    assistant_abolish_amount NUMERIC(18,2),
    reason              TEXT,
    cancel_time         TIMESTAMPTZ,
    operator_id         BIGINT,
    operator_name       TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, cancel_id)
);
COMMENT ON TABLE billiards_ods.ods_assistant_cancel_log IS 'Source: 鍔╂暀搴熼櫎.json -> data.abolitionAssistants';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.tenant_id IS 'ingest context; tenant id not explicitly exported in abolitionAssistants';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.site_id IS 'source: abolitionAssistants.siteId; store id';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.cancel_id IS 'source: abolitionAssistants.id; abolish record id';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.ledger_id IS 'not present; reserved for related ledger id';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.assistant_id IS 'not present in export; assistant name only in payload';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.order_trade_no IS 'not present in export; reserved for order number';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.table_id IS 'source: abolitionAssistants.tableId; table id';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.table_area_id IS 'source: abolitionAssistants.tableAreaId; table area id';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.table_area_name IS 'source: abolitionAssistants.tableArea; table area name';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.table_name IS 'source: abolitionAssistants.tableName; table name';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.assistant_on IS 'source: abolitionAssistants.assistantOn; number of assistants on duty';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.pd_charge_minutes IS 'source: abolitionAssistants.pdChargeMinutes; charged minutes';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.assistant_abolish_amount IS 'source: abolitionAssistants.assistantAbolishAmount; abolish amount';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.reason IS 'source: abolitionAssistants.trashReason; abolish reason';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.cancel_time IS 'source: abolitionAssistants.createTime; abolish time';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.operator_id IS 'not present in export; reserved for operator id';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.operator_name IS 'not present in export; reserved for operator name';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.ods_assistant_cancel_log.payload IS 'raw JSON payload for replay/debug';

CREATE TABLE IF NOT EXISTS billiards_ods.group_buy_packages (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    package_id          BIGINT          NOT NULL,
    package_name        TEXT,
    table_area_id       BIGINT,
    table_area_name     TEXT,
    platform_code       TEXT,
    status              TEXT,
    face_price          NUMERIC(18,2),
    settle_price        NUMERIC(18,2),
    selling_price       NUMERIC(18,2),
    duration            INT,
    valid_from          DATE,
    valid_to            DATE,
    start_time          TIMESTAMPTZ,
    end_time            TIMESTAMPTZ,
    is_enabled          BOOLEAN,
    is_delete           BOOLEAN,
    package_type        TEXT,
    usable_count        INT,
    creator_name        TEXT,
    tenant_table_area_id BIGINT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, package_id)
);
COMMENT ON TABLE billiards_ods.group_buy_packages IS '鏉ユ簮锛氬洟璐椁?json -> data.packageCouponList锛涘洟璐?骞冲彴鍒稿畾涔?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.group_buy_packages.site_id IS '婧愬瓧娈?site_id锛涢棬搴桰D';
COMMENT ON COLUMN billiards_ods.group_buy_packages.package_id IS '婧愬瓧娈?id/package_id锛涘椁愬畾涔塈D';
COMMENT ON COLUMN billiards_ods.group_buy_packages.package_name IS '婧愬瓧娈?package_name锛涘椁愬悕绉?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.table_area_id IS '婧愬瓧娈?table_area_id锛涢€傜敤鍖哄煙ID';
COMMENT ON COLUMN billiards_ods.group_buy_packages.table_area_name IS '婧愬瓧娈?table_area_name锛涢€傜敤鍖哄煙鍚嶇О';
COMMENT ON COLUMN billiards_ods.group_buy_packages.platform_code IS '婧愬瓧娈?groupon_type/system_group_type锛涘钩鍙?鍒告笭閬撶爜';
COMMENT ON COLUMN billiards_ods.group_buy_packages.status IS '婧愬瓧娈?effective_status锛涚敓鏁堢姸鎬?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.face_price IS '婧愬瓧娈?coupon_money锛涘埜闈㈤';
COMMENT ON COLUMN billiards_ods.group_buy_packages.settle_price IS '婧愭暟鎹湭鍗曞垪锛涢鐣欑粨绠椾环';
COMMENT ON COLUMN billiards_ods.group_buy_packages.selling_price IS '婧愬瓧娈?selling_price锛涘敭鍗栦环';
COMMENT ON COLUMN billiards_ods.group_buy_packages.duration IS '婧愬瓧娈?duration锛涙椂闀匡紙绉掞級';
COMMENT ON COLUMN billiards_ods.group_buy_packages.valid_from IS '婧愬瓧娈?start_time锛涙湁鏁堟湡寮€濮?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.valid_to IS '婧愬瓧娈?end_time锛涙湁鏁堟湡缁撴潫';
COMMENT ON COLUMN billiards_ods.group_buy_packages.start_time IS '婧愬瓧娈?start_clock/add_start_clock锛涙瘡鏃ョ敓鏁堣捣鐐?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.end_time IS '婧愬瓧娈?end_clock/add_end_clock锛涙瘡鏃ュけ鏁堢粓鐐?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.is_enabled IS '婧愬瓧娈?is_enabled锛涙槸鍚﹀惎鐢?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.is_delete IS '婧愬瓧娈?is_delete锛涢€昏緫鍒犻櫎鏍囪';
COMMENT ON COLUMN billiards_ods.group_buy_packages.package_type IS '婧愬瓧娈?type/system_group_type/group_type锛涘椁愮被鍨?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.usable_count IS '婧愬瓧娈?usable_count锛涘彲鐢ㄦ鏁?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.creator_name IS '婧愬瓧娈?creator_name锛涘垱寤轰汉';
COMMENT ON COLUMN billiards_ods.group_buy_packages.tenant_table_area_id IS '婧愬瓧娈?tenant_table_area_id锛涚鎴风骇鍖哄煙ID';
COMMENT ON COLUMN billiards_ods.group_buy_packages.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.group_buy_packages.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.group_buy_packages.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.group_buy_packages.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.group_buy_packages_log (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    usage_id            BIGINT          NOT NULL,
    package_id          BIGINT,
    coupon_id           BIGINT,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    member_id           BIGINT,
    status              TEXT,
    used_time           TIMESTAMPTZ,
    deduct_amount       NUMERIC(18,2),
    settle_price        NUMERIC(18,2),
    coupon_code         TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, usage_id)
);
COMMENT ON TABLE billiards_ods.group_buy_packages_log IS '鏉ユ簮锛氬洟璐椁愭祦姘?json -> data.siteTableUseDetailsList锛涘洟璐埜鏍搁攢娴佹按';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.site_id IS '婧愬瓧娈?site_id锛涢棬搴桰D';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.usage_id IS '婧愬瓧娈?id锛涙牳閿€娴佹按ID';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.package_id IS '婧愬瓧娈?promotion_coupon_id锛涘椁?鍒窱D';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.coupon_id IS '婧愬瓧娈?order_coupon_id/coupon_origin_id锛涘埜瀹炰緥ID';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.order_trade_no IS '婧愬瓧娈?order_trade_no锛涗笟鍔¤鍗曞彿';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.order_settle_id IS '婧愬瓧娈?order_settle_id锛涚粨绠楀崟ID';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.member_id IS '婧愭暟鎹湭缁欙紱棰勭暀浼氬憳ID';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.status IS '婧愬瓧娈?ledger_status锛涙祦姘寸姸鎬?;
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.used_time IS '婧愬瓧娈?create_time锛涙牳閿€鏃堕棿';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.deduct_amount IS '婧愬瓧娈?coupon_money锛涘埜鎶垫墸閲戦';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.settle_price IS '婧愬瓧娈?ledger_amount锛涙湰鏉℃槑缁嗙粨绠楅噾棰?;
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.coupon_code IS '婧愬瓧娈?coupon_code锛涘埜鐮?;
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.group_buy_packages_log.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_platform_coupon_log (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    coupon_id           BIGINT          NOT NULL,
    platform_code       TEXT,
    verify_code         TEXT,
    coupon_code         TEXT,
    coupon_channel      TEXT,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    member_id           BIGINT,
    status              TEXT,
    used_time           TIMESTAMPTZ,
    deduct_amount       NUMERIC(18,2),
    settle_price        NUMERIC(18,2),
    coupon_money        NUMERIC(18,2),
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, coupon_id)
);
COMMENT ON TABLE billiards_ods.ods_platform_coupon_log IS '鏉ユ簮锛氬钩鍙伴獙鍒歌褰?json锛涘钩鍙板埜鏍搁攢璁板綍';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.tenant_id IS '婧愬瓧娈?tenant_id锛涚鎴?鍝佺墝ID';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.site_id IS '婧愬瓧娈?site_id锛涢棬搴桰D';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.coupon_id IS '婧愬瓧娈?id锛涙牳閿€璁板綍ID';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.platform_code IS '婧愬瓧娈?groupon_type锛涘埜骞冲彴/娓犻亾缂栫爜';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.verify_code IS '婧愬瓧娈?verify_id锛涙牳閿€娴佹按鍙?;
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.coupon_code IS '婧愬瓧娈?coupon_code锛涘埜鐮?;
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.coupon_channel IS '婧愬瓧娈?coupon_channel锛涢攢鍞笭閬?;
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.order_trade_no IS '婧愬瓧娈?site_order_id锛涘叧鑱旇鍗曞彿';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.order_settle_id IS '婧愭暟鎹湭缁欙紱棰勭暀缁撶畻ID';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.member_id IS '婧愭暟鎹湭缁欙紱棰勭暀浼氬憳ID';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.status IS '婧愬瓧娈?use_status锛涙牳閿€鐘舵€?;
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.used_time IS '婧愬瓧娈?consume_time锛涙牳閿€鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.deduct_amount IS '婧愬瓧娈?coupon_money锛涙姷鎵ｉ噾棰?;
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.settle_price IS '婧愬瓧娈?sale_price锛涚粨绠?閿€鍞环';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.coupon_money IS '婧愬瓧娈?coupon_money锛涘埜闈㈤';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.source_file IS '閲囬泦鍏冩暟鎹細婧愭枃浠惰矾寰?;
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.source_endpoint IS '閲囬泦鍏冩暟鎹細鏉ユ簮鎺ュ彛/绔偣';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.fetched_at IS '閲囬泦鍏冩暟鎹細鍏ユ箹鏃堕棿';
COMMENT ON COLUMN billiards_ods.ods_platform_coupon_log.payload IS '鍘熷JSON鍏ㄩ噺瀛樻。锛屼究浜庨噸鏀?瀹¤';

CREATE TABLE IF NOT EXISTS billiards_ods.goods_stock_movements (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    change_id           BIGINT          NOT NULL,
    site_goods_id       BIGINT,
    goods_id            BIGINT,
    stock_type          TEXT,
    change_amount       NUMERIC(18,2),
    before_stock        NUMERIC(18,2),
    after_stock         NUMERIC(18,2),
    change_amount_alt   NUMERIC(18,2),
    before_stock_alt    NUMERIC(18,2),
    after_stock_alt     NUMERIC(18,2),
    change_type         TEXT,
    relate_id           BIGINT,
    unit                TEXT,
    price               NUMERIC(18,2),
    goods_category_id   BIGINT,
    goods_second_category_id BIGINT,
    remark              TEXT,
    operator_id         BIGINT,
    operator_name       TEXT,
    change_time         TIMESTAMPTZ,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, change_id)
);

CREATE TABLE IF NOT EXISTS billiards_ods.ods_inventory_stock (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    site_goods_id       BIGINT          NOT NULL,
    goods_id            BIGINT,
    goods_name          TEXT,
    goods_unit          TEXT,
    goods_category_id   BIGINT,
    goods_second_category_id BIGINT,
    range_start_stock   NUMERIC(18,2),
    range_end_stock     NUMERIC(18,2),
    range_in            NUMERIC(18,2),
    range_out           NUMERIC(18,2),
    range_inventory     NUMERIC(18,2),
    range_sale          NUMERIC(18,2),
    range_sale_money    NUMERIC(18,2),
    current_stock       NUMERIC(18,2),
    cost_price          NUMERIC(18,2),
    category_name       TEXT,
    snapshot_key        TEXT            NOT NULL DEFAULT 'default',
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, site_goods_id, snapshot_key)
);
COMMENT ON TABLE billiards_ods.ods_inventory_stock IS 'Source: 搴撳瓨姹囨€?json';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.tenant_id IS 'ingest context; tenant id not explicitly exported';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.site_id IS 'ingest context; site id not explicitly exported';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.site_goods_id IS 'source: 搴撳瓨姹囨€?siteGoodsId; site goods id';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.goods_id IS 'not present in export; reserved for tenant goods id';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.goods_name IS 'source: 搴撳瓨姹囨€?goodsName; goods name';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.goods_unit IS 'source: 搴撳瓨姹囨€?goodsUnit; unit';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.goods_category_id IS 'source: 搴撳瓨姹囨€?goodsCategoryId; category id';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.goods_second_category_id IS 'source: 搴撳瓨姹囨€?goodsCategorySecondId; second category id';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.range_start_stock IS 'source: 搴撳瓨姹囨€?rangeStartStock; period opening stock';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.range_end_stock IS 'source: 搴撳瓨姹囨€?rangeEndStock; period closing stock';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.range_in IS 'source: 搴撳瓨姹囨€?rangeIn; inbound qty during period';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.range_out IS 'source: 搴撳瓨姹囨€?rangeOut; outbound qty during period';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.range_inventory IS 'source: 搴撳瓨姹囨€?rangeInventory; inventory adjustments during period';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.range_sale IS 'source: 搴撳瓨姹囨€?rangeSale; sales qty during period';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.range_sale_money IS 'source: 搴撳瓨姹囨€?rangeSaleMoney; sales amount during period';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.current_stock IS 'source: 搴撳瓨姹囨€?currentStock; current stock';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.cost_price IS 'not present in export; reserved for cost price';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.category_name IS 'source: 搴撳瓨姹囨€?categoryName; category name';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.snapshot_key IS 'snapshot label (default ''default'') for periodic loads';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.ods_inventory_stock.payload IS 'raw JSON payload for replay/debug';

CREATE TABLE IF NOT EXISTS billiards_ods.settlement_records (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    order_settle_id     BIGINT          NOT NULL,
    settle_relate_id    BIGINT,
    settle_name         TEXT,
    settle_type         INT,
    settle_status       INT,
    member_id           BIGINT,
    member_name         TEXT,
    member_phone        TEXT,
    table_id            BIGINT,
    consume_money       NUMERIC(18,2),
    table_charge_money  NUMERIC(18,2),
    goods_money         NUMERIC(18,2),
    service_money       NUMERIC(18,2),
    assistant_pd_money  NUMERIC(18,2),
    assistant_cx_money  NUMERIC(18,2),
    pay_amount          NUMERIC(18,2),
    cash_amount         NUMERIC(18,2),
    online_amount       NUMERIC(18,2),
    point_amount        NUMERIC(18,2),
    coupon_amount       NUMERIC(18,2),
    card_amount         NUMERIC(18,2),
    balance_amount      NUMERIC(18,2),
    refund_amount       NUMERIC(18,2),
    prepay_money        NUMERIC(18,2),
    adjust_amount       NUMERIC(18,2),
    rounding_amount     NUMERIC(18,2),
    member_discount_amount NUMERIC(18,2),
    coupon_sale_amount  NUMERIC(18,2),
    goods_promotion_money NUMERIC(18,2),
    assistant_promotion_money NUMERIC(18,2),
    point_discount_price NUMERIC(18,2),
    point_discount_cost NUMERIC(18,2),
    real_goods_money    NUMERIC(18,2),
    assistant_manual_discount NUMERIC(18,2),
    all_coupon_discount NUMERIC(18,2),
    is_use_coupon       BOOLEAN,
    is_use_discount     BOOLEAN,
    is_activity         BOOLEAN,
    is_bind_member      BOOLEAN,
    is_first            BOOLEAN,
    recharge_card_amount NUMERIC(18,2),
    gift_card_amount    NUMERIC(18,2),
    payment_method      INT,
    create_time         TIMESTAMPTZ,
    pay_time            TIMESTAMPTZ,
    revoke_order_id     BIGINT,
    revoke_order_name   TEXT,
    revoke_time         TIMESTAMPTZ,
    can_be_revoked      BOOLEAN,
    serial_number       TEXT,
    sales_man_name      TEXT,
    sales_man_user_id   BIGINT,
    order_remark        TEXT,
    operator_id         BIGINT,
    operator_name       TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, order_settle_id)
);
COMMENT ON TABLE billiards_ods.settlement_records IS 'Source: 缁撹处璁板綍.json -> data.settleList[].settleList';
COMMENT ON COLUMN billiards_ods.settlement_records.tenant_id IS 'source: settleList.tenantId; tenant/brand id';
COMMENT ON COLUMN billiards_ods.settlement_records.site_id IS 'source: settleList.siteId; store id';
COMMENT ON COLUMN billiards_ods.settlement_records.order_settle_id IS 'source: settleList.id; settlement record id';
COMMENT ON COLUMN billiards_ods.settlement_records.settle_relate_id IS 'source: settleList.settleRelateId; related business/order id';
COMMENT ON COLUMN billiards_ods.settlement_records.settle_name IS 'source: settleList.settleName; settlement name';
COMMENT ON COLUMN billiards_ods.settlement_records.settle_type IS 'source: settleList.settleType; settlement type enum';
COMMENT ON COLUMN billiards_ods.settlement_records.settle_status IS 'source: settleList.settleStatus; settlement status';
COMMENT ON COLUMN billiards_ods.settlement_records.member_id IS 'source: settleList.memberId; member id';
COMMENT ON COLUMN billiards_ods.settlement_records.member_name IS 'source: settleList.memberName; member name';
COMMENT ON COLUMN billiards_ods.settlement_records.member_phone IS 'source: settleList.memberPhone; member phone';
COMMENT ON COLUMN billiards_ods.settlement_records.table_id IS 'source: settleList.tableId; table id';
COMMENT ON COLUMN billiards_ods.settlement_records.consume_money IS 'source: settleList.consumeMoney; total consumption amount';
COMMENT ON COLUMN billiards_ods.settlement_records.table_charge_money IS 'source: settleList.tableChargeMoney; table fee amount';
COMMENT ON COLUMN billiards_ods.settlement_records.goods_money IS 'source: settleList.goodsMoney; goods amount';
COMMENT ON COLUMN billiards_ods.settlement_records.service_money IS 'source: settleList.serviceMoney; service amount';
COMMENT ON COLUMN billiards_ods.settlement_records.assistant_pd_money IS 'source: settleList.assistantPdMoney; assistant accompany amount';
COMMENT ON COLUMN billiards_ods.settlement_records.assistant_cx_money IS 'source: settleList.assistantCxMoney; assistant promotion amount';
COMMENT ON COLUMN billiards_ods.settlement_records.pay_amount IS 'source: settleList.payAmount; payment amount (settled)';
COMMENT ON COLUMN billiards_ods.settlement_records.cash_amount IS 'source: settleList.cashAmount; cash payment amount';
COMMENT ON COLUMN billiards_ods.settlement_records.online_amount IS 'source: settleList.onlineAmount; online payment amount';
COMMENT ON COLUMN billiards_ods.settlement_records.point_amount IS 'source: settleList.pointAmount; amount credited to card/points';
COMMENT ON COLUMN billiards_ods.settlement_records.coupon_amount IS 'source: settleList.couponAmount; coupon deduction';
COMMENT ON COLUMN billiards_ods.settlement_records.card_amount IS 'source: settleList.cardAmount; member card amount';
COMMENT ON COLUMN billiards_ods.settlement_records.balance_amount IS 'source: settleList.balanceAmount; balance payment amount';
COMMENT ON COLUMN billiards_ods.settlement_records.refund_amount IS 'source: settleList.refundAmount; refund amount on this settlement';
COMMENT ON COLUMN billiards_ods.settlement_records.prepay_money IS 'source: settleList.prepayMoney; prepayment amount';
COMMENT ON COLUMN billiards_ods.settlement_records.adjust_amount IS 'source: settleList.adjustAmount; adjustment amount';
COMMENT ON COLUMN billiards_ods.settlement_records.rounding_amount IS 'source: settleList.roundingAmount; rounding write-off amount';
COMMENT ON COLUMN billiards_ods.settlement_records.member_discount_amount IS 'source: settleList.memberDiscountAmount; member discount amount';
COMMENT ON COLUMN billiards_ods.settlement_records.coupon_sale_amount IS 'source: settleList.couponSaleAmount; coupon sale amount';
COMMENT ON COLUMN billiards_ods.settlement_records.goods_promotion_money IS 'source: settleList.goodsPromotionMoney; goods promotion deduction';
COMMENT ON COLUMN billiards_ods.settlement_records.assistant_promotion_money IS 'source: settleList.assistantPromotionMoney; assistant promotion deduction';
COMMENT ON COLUMN billiards_ods.settlement_records.point_discount_price IS 'source: settleList.pointDiscountPrice; points discount amount';
COMMENT ON COLUMN billiards_ods.settlement_records.point_discount_cost IS 'source: settleList.pointDiscountCost; cost of points discount';
COMMENT ON COLUMN billiards_ods.settlement_records.real_goods_money IS 'source: settleList.realGoodsMoney; real goods amount after discount';
COMMENT ON COLUMN billiards_ods.settlement_records.assistant_manual_discount IS 'source: settleList.assistantManualDiscount; manual assistant discount';
COMMENT ON COLUMN billiards_ods.settlement_records.all_coupon_discount IS 'source: settleList.allCouponDiscount; total coupon discount';
COMMENT ON COLUMN billiards_ods.settlement_records.is_use_coupon IS 'source: settleList.isUseCoupon; whether coupon used';
COMMENT ON COLUMN billiards_ods.settlement_records.is_use_discount IS 'source: settleList.isUseDiscount; whether discount used';
COMMENT ON COLUMN billiards_ods.settlement_records.is_activity IS 'source: settleList.isActivity; whether activity applied';
COMMENT ON COLUMN billiards_ods.settlement_records.is_bind_member IS 'source: settleList.isBindMember; whether member bound';
COMMENT ON COLUMN billiards_ods.settlement_records.is_first IS 'source: settleList.isFirst; whether first order flag';
COMMENT ON COLUMN billiards_ods.settlement_records.recharge_card_amount IS 'source: settleList.rechargeCardAmount; recharge card amount';
COMMENT ON COLUMN billiards_ods.settlement_records.gift_card_amount IS 'source: settleList.giftCardAmount; gift card amount';
COMMENT ON COLUMN billiards_ods.settlement_records.payment_method IS 'source: settleList.paymentMethod; payment method code';
COMMENT ON COLUMN billiards_ods.settlement_records.create_time IS 'source: settleList.createTime; creation time';
COMMENT ON COLUMN billiards_ods.settlement_records.pay_time IS 'source: settleList.payTime; pay time';
COMMENT ON COLUMN billiards_ods.settlement_records.revoke_order_id IS 'source: settleList.revokeOrderId; revoke order id';
COMMENT ON COLUMN billiards_ods.settlement_records.revoke_order_name IS 'source: settleList.revokeOrderName; revoke order name';
COMMENT ON COLUMN billiards_ods.settlement_records.revoke_time IS 'source: settleList.revokeTime; revoke time';
COMMENT ON COLUMN billiards_ods.settlement_records.can_be_revoked IS 'source: settleList.canBeRevoked; flag whether revocable';
COMMENT ON COLUMN billiards_ods.settlement_records.serial_number IS 'source: settleList.serialNumber; receipt serial number';
COMMENT ON COLUMN billiards_ods.settlement_records.sales_man_name IS 'source: settleList.salesManName; sales person name';
COMMENT ON COLUMN billiards_ods.settlement_records.sales_man_user_id IS 'source: settleList.salesManUserId; sales person user id';
COMMENT ON COLUMN billiards_ods.settlement_records.order_remark IS 'source: settleList.orderRemark; order remark';
COMMENT ON COLUMN billiards_ods.settlement_records.operator_id IS 'source: settleList.operatorId; operator id';
COMMENT ON COLUMN billiards_ods.settlement_records.operator_name IS 'source: settleList.operatorName; operator name';
COMMENT ON COLUMN billiards_ods.settlement_records.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.settlement_records.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.settlement_records.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.settlement_records.payload IS 'raw JSON payload for replay/debug';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_goods_category (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    category_id         BIGINT          NOT NULL,
    category_name       TEXT,
    parent_id           BIGINT,
    level_no            INT,
    status              TEXT,
    remark              TEXT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, category_id)
);
COMMENT ON TABLE billiards_ods.ods_goods_category IS 'Source: 搴撳瓨鍙樺寲璁板綍2.json -> data.goodsCategoryList';
COMMENT ON COLUMN billiards_ods.ods_goods_category.tenant_id IS 'source: goodsCategoryList.tenant_id; tenant/brand id';
COMMENT ON COLUMN billiards_ods.ods_goods_category.site_id IS 'ingest context; site id not explicitly exported';
COMMENT ON COLUMN billiards_ods.ods_goods_category.category_id IS 'source: goodsCategoryList.id; category id';
COMMENT ON COLUMN billiards_ods.ods_goods_category.category_name IS 'source: goodsCategoryList.category_name; category name';
COMMENT ON COLUMN billiards_ods.ods_goods_category.parent_id IS 'source: goodsCategoryList.pid; parent category id';
COMMENT ON COLUMN billiards_ods.ods_goods_category.level_no IS 'not present; reserved for category level';
COMMENT ON COLUMN billiards_ods.ods_goods_category.status IS 'source: goodsCategoryList.is_warehousing/open_salesman; category status flags';
COMMENT ON COLUMN billiards_ods.ods_goods_category.remark IS 'source: goodsCategoryList.alias_name/business_name; remark/alias';
COMMENT ON COLUMN billiards_ods.ods_goods_category.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.ods_goods_category.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.ods_goods_category.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.ods_goods_category.payload IS 'raw JSON payload for replay/debug';

CREATE TABLE IF NOT EXISTS billiards_ods.payment_transactions (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    pay_id              BIGINT          NOT NULL,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    member_id           BIGINT,
    pay_method_code     TEXT,
    pay_method_name     TEXT,
    pay_status          INT,
    pay_amount          NUMERIC(18,2),
    pay_time            TIMESTAMPTZ,
    online_pay_channel  TEXT,
    transaction_id      TEXT,
    operator_id         BIGINT,
    remark              TEXT,
    relate_type         TEXT,
    relate_id           BIGINT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, pay_id)
);
COMMENT ON TABLE billiards_ods.payment_transactions IS 'Source: 鏀粯璁板綍.json';
COMMENT ON COLUMN billiards_ods.payment_transactions.tenant_id IS 'ingest context; tenant id not explicitly exported';
COMMENT ON COLUMN billiards_ods.payment_transactions.site_id IS 'source: 鏀粯璁板綍.site_id; store id';
COMMENT ON COLUMN billiards_ods.payment_transactions.pay_id IS 'source: 鏀粯璁板綍.id; payment record id';
COMMENT ON COLUMN billiards_ods.payment_transactions.order_trade_no IS 'not present in export; reserved for business order number';
COMMENT ON COLUMN billiards_ods.payment_transactions.order_settle_id IS 'not present; reserved for settlement id';
COMMENT ON COLUMN billiards_ods.payment_transactions.member_id IS 'not present; reserved for member id';
COMMENT ON COLUMN billiards_ods.payment_transactions.pay_method_code IS 'source: 鏀粯璁板綍.payment_method; payment method enum';
COMMENT ON COLUMN billiards_ods.payment_transactions.pay_method_name IS 'not present; reserved for payment method name';
COMMENT ON COLUMN billiards_ods.payment_transactions.pay_status IS 'source: 鏀粯璁板綍.pay_status; payment status';
COMMENT ON COLUMN billiards_ods.payment_transactions.pay_amount IS 'source: 鏀粯璁板綍.pay_amount; paid amount';
COMMENT ON COLUMN billiards_ods.payment_transactions.pay_time IS 'source: 鏀粯璁板綍.pay_time; payment time';
COMMENT ON COLUMN billiards_ods.payment_transactions.online_pay_channel IS 'source: 鏀粯璁板綍.online_pay_channel; online channel code';
COMMENT ON COLUMN billiards_ods.payment_transactions.transaction_id IS 'not present in export; reserved for channel transaction id';
COMMENT ON COLUMN billiards_ods.payment_transactions.operator_id IS 'not present; reserved for operator id';
COMMENT ON COLUMN billiards_ods.payment_transactions.remark IS 'not present; reserved for remark';
COMMENT ON COLUMN billiards_ods.payment_transactions.relate_type IS 'source: 鏀粯璁板綍.relate_type; related business type';
COMMENT ON COLUMN billiards_ods.payment_transactions.relate_id IS 'source: 鏀粯璁板綍.relate_id; related business id';
COMMENT ON COLUMN billiards_ods.payment_transactions.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.payment_transactions.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.payment_transactions.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.payment_transactions.payload IS 'raw JSON payload for replay/debug';

CREATE TABLE IF NOT EXISTS billiards_ods.refund_transactions (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    refund_id           BIGINT          NOT NULL,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    member_id           BIGINT,
    pay_sn              TEXT,
    pay_amount          NUMERIC(18,2),
    pay_status          INT,
    is_revoke           BOOLEAN,
    is_delete           BOOLEAN,
    online_pay_channel  TEXT,
    pay_method_code     TEXT,
    refund_amount       NUMERIC(18,2),
    refund_time         TIMESTAMPTZ,
    action_type         INT,
    pay_terminal        INT,
    pay_config_id       BIGINT,
    cashier_point_id    BIGINT,
    operator_id         BIGINT,
    member_card_id      BIGINT,
    balance_frozen_amount NUMERIC(18,2),
    card_frozen_amount  NUMERIC(18,2),
    round_amount        NUMERIC(18,2),
    online_pay_type     INT,
    channel_payer_id    TEXT,
    channel_pay_no      TEXT,
    check_status        INT,
    channel_fee         NUMERIC(18,2),
    relate_type         TEXT,
    relate_id           BIGINT,
    status              TEXT,
    reason              TEXT,
    related_payment_id  BIGINT,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, refund_id)
);
COMMENT ON TABLE billiards_ods.refund_transactions IS 'Source: 閫€娆捐褰?json';
COMMENT ON COLUMN billiards_ods.refund_transactions.tenant_id IS 'source: 閫€娆捐褰?tenant_id; tenant/brand id';
COMMENT ON COLUMN billiards_ods.refund_transactions.site_id IS 'source: 閫€娆捐褰?site_id; store id';
COMMENT ON COLUMN billiards_ods.refund_transactions.refund_id IS 'source: 閫€娆捐褰?id; refund record id';
COMMENT ON COLUMN billiards_ods.refund_transactions.order_trade_no IS 'not present in export; reserved for order number';
COMMENT ON COLUMN billiards_ods.refund_transactions.order_settle_id IS 'not present; reserved for settlement id';
COMMENT ON COLUMN billiards_ods.refund_transactions.member_id IS 'source: 閫€娆捐褰?member_id; member id';
COMMENT ON COLUMN billiards_ods.refund_transactions.pay_sn IS 'source: 閫€娆捐褰?pay_sn; payment serial number';
COMMENT ON COLUMN billiards_ods.refund_transactions.pay_amount IS 'source: 閫€娆捐褰?pay_amount; paid amount';
COMMENT ON COLUMN billiards_ods.refund_transactions.pay_status IS 'source: 閫€娆捐褰?pay_status; status code';
COMMENT ON COLUMN billiards_ods.refund_transactions.is_revoke IS 'source: 閫€娆捐褰?is_revoke; revoke flag';
COMMENT ON COLUMN billiards_ods.refund_transactions.is_delete IS 'source: 閫€娆捐褰?is_delete; logical delete flag';
COMMENT ON COLUMN billiards_ods.refund_transactions.online_pay_channel IS 'source: 閫€娆捐褰?online_pay_channel; online pay channel';
COMMENT ON COLUMN billiards_ods.refund_transactions.pay_method_code IS 'source: 閫€娆捐褰?payment_method; payment method enum';
COMMENT ON COLUMN billiards_ods.refund_transactions.refund_amount IS 'source: 閫€娆捐褰?refund_amount; refund amount';
COMMENT ON COLUMN billiards_ods.refund_transactions.refund_time IS 'source: 閫€娆捐褰?pay_time; refund time';
COMMENT ON COLUMN billiards_ods.refund_transactions.action_type IS 'source: 閫€娆捐褰?action_type; action type';
COMMENT ON COLUMN billiards_ods.refund_transactions.pay_terminal IS 'source: 閫€娆捐褰?pay_terminal; pay terminal';
COMMENT ON COLUMN billiards_ods.refund_transactions.pay_config_id IS 'source: 閫€娆捐褰?pay_config_id; pay config id';
COMMENT ON COLUMN billiards_ods.refund_transactions.cashier_point_id IS 'source: 閫€娆捐褰?cashier_point_id; cashier point id';
COMMENT ON COLUMN billiards_ods.refund_transactions.operator_id IS 'source: 閫€娆捐褰?operator_id; operator id';
COMMENT ON COLUMN billiards_ods.refund_transactions.member_card_id IS 'source: 閫€娆捐褰?member_card_id; member card id';
COMMENT ON COLUMN billiards_ods.refund_transactions.balance_frozen_amount IS 'source: 閫€娆捐褰?balance_frozen_amount; frozen balance amount';
COMMENT ON COLUMN billiards_ods.refund_transactions.card_frozen_amount IS 'source: 閫€娆捐褰?card_frozen_amount; frozen card amount';
COMMENT ON COLUMN billiards_ods.refund_transactions.round_amount IS 'source: 閫€娆捐褰?round_amount; rounding/write-off amount';
COMMENT ON COLUMN billiards_ods.refund_transactions.online_pay_type IS 'source: 閫€娆捐褰?online_pay_type; online refund type';
COMMENT ON COLUMN billiards_ods.refund_transactions.channel_payer_id IS 'source: 閫€娆捐褰?channel_payer_id; channel payer id';
COMMENT ON COLUMN billiards_ods.refund_transactions.channel_pay_no IS 'source: 閫€娆捐褰?channel_pay_no; channel transaction id';
COMMENT ON COLUMN billiards_ods.refund_transactions.check_status IS 'source: 閫€娆捐褰?check_status; audit status';
COMMENT ON COLUMN billiards_ods.refund_transactions.channel_fee IS 'source: 閫€娆捐褰?channel_fee; channel fee';
COMMENT ON COLUMN billiards_ods.refund_transactions.relate_type IS 'source: 閫€娆捐褰?relate_type; related business type';
COMMENT ON COLUMN billiards_ods.refund_transactions.relate_id IS 'source: 閫€娆捐褰?relate_id; related business id';
COMMENT ON COLUMN billiards_ods.refund_transactions.status IS 'alias for pay_status/is_delete to track row status';
COMMENT ON COLUMN billiards_ods.refund_transactions.reason IS 'not present in export; reserved for refund reason';
COMMENT ON COLUMN billiards_ods.refund_transactions.related_payment_id IS 'not present; reserved for linking to original payment';
COMMENT ON COLUMN billiards_ods.refund_transactions.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.refund_transactions.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.refund_transactions.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.refund_transactions.payload IS 'raw JSON payload for replay/debug';

CREATE TABLE IF NOT EXISTS billiards_ods.ods_order_receipt_detail (
    tenant_id           BIGINT,
    site_id             BIGINT          NOT NULL,
    order_settle_id     BIGINT          NOT NULL,
    order_trade_no      TEXT,
    order_settle_number TEXT,
    settle_type         INT,
    receipt_no          TEXT,
    receipt_time        TIMESTAMPTZ,
    total_amount        NUMERIC(18,2),
    discount_amount     NUMERIC(18,2),
    final_amount        NUMERIC(18,2),
    actual_payment      NUMERIC(18,2),
    ledger_amount       NUMERIC(18,2),
    member_offer_amount NUMERIC(18,2),
    delivery_fee        NUMERIC(18,2),
    adjust_amount       NUMERIC(18,2),
    payment_method      INT,
    pay_time            TIMESTAMPTZ,
    member_id           BIGINT,
    order_remark        TEXT,
    cashier_name        TEXT,
    ticket_remark       TEXT,
    ticket_custom_content TEXT,
    voucher_money       NUMERIC(18,2),
    reward_name         TEXT,
    consume_money       NUMERIC(18,2),
    refund_amount       NUMERIC(18,2),
    balance_amount      NUMERIC(18,2),
    coupon_amount       NUMERIC(18,2),
    member_deduct_amount NUMERIC(18,2),
    prepay_money        NUMERIC(18,2),
    delivery_address    TEXT,
    snapshot_raw        JSONB,
    member_snapshot     JSONB,
    source_file         TEXT,
    source_endpoint     TEXT,
    fetched_at          TIMESTAMPTZ     DEFAULT now(),
    payload             JSONB           NOT NULL,
    PRIMARY KEY (site_id, order_settle_id)
);
COMMENT ON TABLE billiards_ods.ods_order_receipt_detail IS 'Source: 灏忕エ璇︽儏.json -> data.data (order detail)';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.order_settle_id IS 'source: orderSettleId; settlement id';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.order_trade_no IS 'not present in export; reserved for order number';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.order_settle_number IS 'source: orderSettleNumber; ticket/settlement number';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.settle_type IS 'source: settleType; settlement type enum';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.receipt_no IS 'not present in export; reserved for printed receipt number';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.receipt_time IS 'source: payTime; receipt/pay time';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.total_amount IS 'source: ledgerAmount; total amount before discounts';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.discount_amount IS 'derived from couponAmount/memberOfferAmount/pointDiscountPrice in payload; total discounts';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.final_amount IS 'source: consumeMoney; amount after discounts before payment';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.actual_payment IS 'source: actualPayment; amount actually paid';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.ledger_amount IS 'source: ledgerAmount; aggregated ledger amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.member_offer_amount IS 'source: memberOfferAmount; member discount amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.delivery_fee IS 'source: deliveryFee; delivery fee';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.adjust_amount IS 'source: adjustAmount; manual adjustment';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.payment_method IS 'source: paymentMethod; payment method code';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.pay_time IS 'source: payTime; payment time';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.member_id IS 'not present; memberProfile lacks id (only name/phone/point)';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.order_remark IS 'source: orderRemark; order remark';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.cashier_name IS 'source: cashierName; cashier name';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.ticket_remark IS 'source: ticketRemark; ticket remark';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.ticket_custom_content IS 'source: ticketCustomContent; custom print content';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.voucher_money IS 'source: voucherMoney; voucher amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.reward_name IS 'source: rewardName; reward description';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.consume_money IS 'source: consumeMoney; consumption amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.refund_amount IS 'source: refundAmount; refund amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.balance_amount IS 'source: balanceAmount or payMemberBalance; balance payment amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.coupon_amount IS 'source: couponAmount; coupon deduction amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.member_deduct_amount IS 'source: memberDeductAmount; member deduction amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.prepay_money IS 'source: prepayMoney; prepaid amount';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.delivery_address IS 'source: deliveryAddress; delivery address';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.snapshot_raw IS 'source: orderItem (table/goods/coupon ledgers); full nested detail JSON';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.member_snapshot IS 'source: memberProfile; member snapshot JSON';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.source_file IS 'ingest metadata: source file path';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.source_endpoint IS 'ingest metadata: API or endpoint name';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.fetched_at IS 'ingest metadata: load timestamp';
COMMENT ON COLUMN billiards_ods.ods_order_receipt_detail.payload IS 'raw JSON payload for replay/debug';

-- ---------- DWD Dimensions ----------

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_tenant (
    tenant_id       BIGINT PRIMARY KEY,
    tenant_name     TEXT,
    short_name      TEXT,
    status          TEXT,
    created_at      TIMESTAMPTZ DEFAULT now(),
    updated_at      TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_site (
    site_id         BIGINT PRIMARY KEY,
    tenant_id       BIGINT,
    site_code       TEXT,
    site_name       TEXT,
    city            TEXT,
    region          TEXT,
    status          TEXT,
    open_date       DATE,
    close_date      DATE,
    created_at      TIMESTAMPTZ DEFAULT now(),
    updated_at      TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_member_card_type (
    card_type_id    BIGINT PRIMARY KEY,
    card_type_name  TEXT,
    discount_rate   NUMERIC(8,4),
    description     TEXT,
    remark          TEXT
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_product_category (
    category_id     BIGINT PRIMARY KEY,
    category_name   TEXT,
    parent_id       BIGINT,
    level_no        INT,
    status          TEXT,
    remark          TEXT
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_product (
    goods_id        BIGINT PRIMARY KEY,
    goods_name      TEXT,
    goods_code      TEXT,
    category_id     BIGINT REFERENCES billiards_dwd.dim_product_category (category_id),
    category_name   TEXT,
    unit            TEXT,
    default_price   NUMERIC(18,2),
    status          TEXT,
    updated_at      TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_table (
    table_id        BIGINT PRIMARY KEY,
    site_id         BIGINT,
    table_code      TEXT,
    table_name      TEXT,
    table_type      TEXT,
    area_name       TEXT,
    status          TEXT,
    created_time    TIMESTAMPTZ,
    updated_time    TIMESTAMPTZ
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_assistant_team (
    team_id         BIGINT PRIMARY KEY,
    team_name       TEXT,
    remark          TEXT,
    updated_at      TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_assistant (
    assistant_id    BIGINT PRIMARY KEY,
    assistant_name  TEXT,
    mobile          TEXT,
    team_id         BIGINT REFERENCES billiards_dwd.dim_assistant_team (team_id),
    status          TEXT,
    hired_date      DATE,
    left_date       DATE,
    updated_at      TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_pay_method (
    pay_method_code TEXT PRIMARY KEY,
    pay_method_name TEXT,
    is_stored_value BOOLEAN DEFAULT FALSE,
    status          TEXT,
    updated_at      TIMESTAMPTZ DEFAULT now()
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_order_assist_type (
    assist_type_code TEXT PRIMARY KEY,
    assist_type_name TEXT,
    description      TEXT
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_coupon_platform (
    platform_code   TEXT PRIMARY KEY,
    platform_name   TEXT,
    description     TEXT
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_date (
    date_key        DATE PRIMARY KEY,
    year_no         INT,
    month_no        INT,
    day_no          INT,
    week_no         INT,
    day_of_week     INT,
    month_name      TEXT
);

CREATE TABLE IF NOT EXISTS billiards_dwd.dim_member (
    site_id                 BIGINT,
    member_id               BIGINT,
    tenant_id               BIGINT,
    member_name             TEXT,
    nickname                TEXT,
    gender                  TEXT,
    birthday                DATE,
    mobile                  TEXT,
    member_type_id          BIGINT REFERENCES billiards_dwd.dim_member_card_type (card_type_id),
    member_type_name        TEXT,
    status                  TEXT,
    register_time           TIMESTAMPTZ,
    valid_from              DATE,
    valid_to                DATE,
    last_visit_time         TIMESTAMPTZ,
    balance                 NUMERIC(18,2),
    total_recharge_amount   NUMERIC(18,2),
    total_consumed_amount   NUMERIC(18,2),
    wechat_id               TEXT,
    alipay_id               TEXT,
    remark                  TEXT,
    updated_at              TIMESTAMPTZ DEFAULT now(),
    PRIMARY KEY (site_id, member_id)
);

-- ---------- DWD Facts ----------

CREATE TABLE IF NOT EXISTS billiards_dwd.fact_sale_item (
    site_id             BIGINT          NOT NULL,
    sale_item_id        BIGINT          NOT NULL,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    member_id           BIGINT,
    goods_id            BIGINT REFERENCES billiards_dwd.dim_product (goods_id),
    category_id         BIGINT REFERENCES billiards_dwd.dim_product_category (category_id),
    quantity            NUMERIC(18,4),
    original_amount     NUMERIC(18,2),
    discount_amount     NUMERIC(18,2),
    final_amount        NUMERIC(18,2),
    is_gift             BOOLEAN         DEFAULT FALSE,
    sale_time           TIMESTAMPTZ,
    created_at          TIMESTAMPTZ     DEFAULT now(),
    updated_at          TIMESTAMPTZ     DEFAULT now(),
    PRIMARY KEY (site_id, sale_item_id)
);

CREATE TABLE IF NOT EXISTS billiards_dwd.fact_table_usage (
    site_id             BIGINT          NOT NULL,
    ledger_id           BIGINT          NOT NULL,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    table_id            BIGINT REFERENCES billiards_dwd.dim_table (table_id),
    member_id           BIGINT,
    start_time          TIMESTAMPTZ,
    end_time            TIMESTAMPTZ,
    duration_minutes    INT,
    original_table_fee  NUMERIC(18,2),
    member_discount_amount NUMERIC(18,2),
    manual_discount_amount NUMERIC(18,2),
    final_table_fee     NUMERIC(18,2),
    is_canceled         BOOLEAN         DEFAULT FALSE,
    cancel_time         TIMESTAMPTZ,
    created_at          TIMESTAMPTZ     DEFAULT now(),
    updated_at          TIMESTAMPTZ     DEFAULT now(),
    PRIMARY KEY (site_id, ledger_id)
);

CREATE TABLE IF NOT EXISTS billiards_dwd.fact_assistant_service (
    site_id             BIGINT          NOT NULL,
    ledger_id           BIGINT          NOT NULL,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    assistant_id        BIGINT REFERENCES billiards_dwd.dim_assistant (assistant_id),
    assist_type_code    TEXT REFERENCES billiards_dwd.dim_order_assist_type (assist_type_code),
    member_id           BIGINT,
    start_time          TIMESTAMPTZ,
    end_time            TIMESTAMPTZ,
    duration_minutes    INT,
    original_fee        NUMERIC(18,2),
    member_discount_amount NUMERIC(18,2),
    manual_discount_amount NUMERIC(18,2),
    final_fee           NUMERIC(18,2),
    is_canceled         BOOLEAN         DEFAULT FALSE,
    cancel_time         TIMESTAMPTZ,
    created_at          TIMESTAMPTZ     DEFAULT now(),
    updated_at          TIMESTAMPTZ     DEFAULT now(),
    PRIMARY KEY (site_id, ledger_id)
);

CREATE TABLE IF NOT EXISTS billiards_dwd.fact_coupon_usage (
    site_id             BIGINT          NOT NULL,
    coupon_id           BIGINT          NOT NULL,
    package_id          BIGINT,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    member_id           BIGINT,
    platform_code       TEXT REFERENCES billiards_dwd.dim_coupon_platform (platform_code),
    status              TEXT,
    deduct_amount       NUMERIC(18,2),
    settle_price        NUMERIC(18,2),
    used_time           TIMESTAMPTZ,
    created_at          TIMESTAMPTZ     DEFAULT now(),
    updated_at          TIMESTAMPTZ     DEFAULT now(),
    PRIMARY KEY (site_id, coupon_id)
);

CREATE TABLE IF NOT EXISTS billiards_dwd.fact_payment (
    site_id             BIGINT          NOT NULL,
    pay_id              BIGINT          NOT NULL,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    member_id           BIGINT,
    pay_method_code     TEXT REFERENCES billiards_dwd.dim_pay_method (pay_method_code),
    pay_amount          NUMERIC(18,2),
    pay_time            TIMESTAMPTZ,
    relate_type         TEXT,
    relate_id           BIGINT,
    created_at          TIMESTAMPTZ     DEFAULT now(),
    updated_at          TIMESTAMPTZ     DEFAULT now(),
    PRIMARY KEY (site_id, pay_id)
);

CREATE TABLE IF NOT EXISTS billiards_dwd.fact_refund (
    site_id             BIGINT          NOT NULL,
    refund_id           BIGINT          NOT NULL,
    order_trade_no      TEXT,
    order_settle_id     BIGINT,
    member_id           BIGINT,
    pay_method_code     TEXT REFERENCES billiards_dwd.dim_pay_method (pay_method_code),
    refund_amount       NUMERIC(18,2),
    refund_time         TIMESTAMPTZ,
    status              TEXT,
    created_at          TIMESTAMPTZ     DEFAULT now(),
    updated_at          TIMESTAMPTZ     DEFAULT now(),
    PRIMARY KEY (site_id, refund_id)
);

CREATE TABLE IF NOT EXISTS billiards_dwd.fact_balance_change (
    site_id             BIGINT          NOT NULL,
    change_id           BIGINT          NOT NULL,
    member_id           BIGINT,
    change_type         INT,
    relate_type         TEXT,
    relate_id           BIGINT,
    pay_method_code     TEXT REFERENCES billiards_dwd.dim_pay_method (pay_method_code),
    change_amount       NUMERIC(18,2),
    balance_before      NUMERIC(18,2),
    balance_after       NUMERIC(18,2),
    change_time         TIMESTAMPTZ,
    created_at          TIMESTAMPTZ     DEFAULT now(),
    updated_at          TIMESTAMPTZ     DEFAULT now(),
    PRIMARY KEY (site_id, change_id)
);

-- ---------- DWS (serving layers) ----------

CREATE TABLE IF NOT EXISTS billiards_dws.dws_order_summary (
    site_id                         BIGINT          NOT NULL,
    order_settle_id                 BIGINT          NOT NULL,
    order_trade_no                  TEXT,
    order_date                      DATE,
    tenant_id                       BIGINT,
    member_id                       BIGINT,
    member_flag                     BOOLEAN         DEFAULT FALSE,
    recharge_order_flag             BOOLEAN         DEFAULT FALSE,
    item_count                      INT,
    total_item_quantity             NUMERIC(18,4),
    table_fee_amount                NUMERIC(18,2),
    assistant_service_amount        NUMERIC(18,2),
    goods_amount                    NUMERIC(18,2),
    group_amount                    NUMERIC(18,2),
    total_coupon_deduction          NUMERIC(18,2),
    member_discount_amount          NUMERIC(18,2),
    manual_discount_amount          NUMERIC(18,2),
    order_original_amount           NUMERIC(18,2),
    order_final_amount              NUMERIC(18,2),
    stored_card_deduct              NUMERIC(18,2),
    external_paid_amount            NUMERIC(18,2),
    total_paid_amount               NUMERIC(18,2),
    book_table_flow                 NUMERIC(18,2),
    book_assistant_flow             NUMERIC(18,2),
    book_goods_flow                 NUMERIC(18,2),
    book_group_flow                 NUMERIC(18,2),
    book_order_flow                 NUMERIC(18,2),
    order_effective_consume_cash    NUMERIC(18,2),
    order_effective_recharge_cash   NUMERIC(18,2),
    order_effective_flow            NUMERIC(18,2),
    refund_amount                   NUMERIC(18,2),
    net_income                      NUMERIC(18,2),
    created_at                      TIMESTAMPTZ     DEFAULT now(),
    updated_at                      TIMESTAMPTZ     DEFAULT now(),
    PRIMARY KEY (site_id, order_settle_id)
);

-- ---------- etl_admin (scheduler, cursor, run tracking) ----------
CREATE SCHEMA IF NOT EXISTS etl_admin;

CREATE TABLE IF NOT EXISTS etl_admin.etl_task (
    task_id                 BIGSERIAL PRIMARY KEY,
    task_code               TEXT            NOT NULL,
    store_id                BIGINT          NOT NULL,
    enabled                 BOOLEAN         DEFAULT TRUE,
    cursor_field            TEXT,
    window_minutes_default  INT             DEFAULT 30,
    overlap_seconds         INT             DEFAULT 120,
    page_size               INT             DEFAULT 200,
    retry_max               INT             DEFAULT 3,
    params                  JSONB           DEFAULT '{}'::jsonb,
    created_at              TIMESTAMPTZ     DEFAULT now(),
    updated_at              TIMESTAMPTZ     DEFAULT now(),
    UNIQUE (task_code, store_id)
);

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,
    last_run_id BIGINT,
    extra       JSONB       DEFAULT '{}'::jsonb,
    created_at  TIMESTAMPTZ DEFAULT now(),
    updated_at  TIMESTAMPTZ DEFAULT now(),
    UNIQUE (task_id, store_id)
);

CREATE TABLE IF NOT EXISTS etl_admin.etl_run (
    run_id          BIGSERIAL PRIMARY KEY,
    run_uuid        TEXT            NOT NULL,
    task_id         BIGINT          NOT NULL REFERENCES etl_admin.etl_task(task_id) ON DELETE CASCADE,
    store_id        BIGINT          NOT NULL,
    status          TEXT            NOT NULL,
    started_at      TIMESTAMPTZ     DEFAULT now(),
    ended_at        TIMESTAMPTZ,
    window_start    TIMESTAMPTZ,
    window_end      TIMESTAMPTZ,
    window_minutes  INT,
    overlap_seconds INT,
    fetched_count   INT             DEFAULT 0,
    loaded_count    INT             DEFAULT 0,
    updated_count   INT             DEFAULT 0,
    skipped_count   INT             DEFAULT 0,
    error_count     INT             DEFAULT 0,
    unknown_fields  INT             DEFAULT 0,
    export_dir      TEXT,
    log_path        TEXT,
    request_params  JSONB           DEFAULT '{}'::jsonb,
    manifest        JSONB           DEFAULT '{}'::jsonb,
    error_message   TEXT,
    extra           JSONB           DEFAULT '{}'::jsonb
);

-- Default task registry seed (idempotent)
INSERT INTO etl_admin.etl_task (task_code, store_id, enabled)
VALUES
    ('PRODUCTS', 2790685415443269, TRUE),
    ('TABLES', 2790685415443269, TRUE),
    ('MEMBERS', 2790685415443269, TRUE),
    ('ASSISTANTS', 2790685415443269, TRUE),
    ('PACKAGES_DEF', 2790685415443269, TRUE),
    ('ORDERS', 2790685415443269, TRUE),
    ('PAYMENTS', 2790685415443269, TRUE),
    ('REFUNDS', 2790685415443269, TRUE),
    ('COUPON_USAGE', 2790685415443269, TRUE),
    ('INVENTORY_CHANGE', 2790685415443269, TRUE),
    ('TOPUPS', 2790685415443269, TRUE),
    ('TABLE_DISCOUNT', 2790685415443269, TRUE),
    ('ASSISTANT_ABOLISH', 2790685415443269, TRUE),
    ('LEDGER', 2790685415443269, TRUE),
    ('TICKET_DWD', 2790685415443269, TRUE),
    ('PAYMENTS_DWD', 2790685415443269, TRUE),
    ('MEMBERS_DWD', 2790685415443269, TRUE),
    ('MANUAL_INGEST', 2790685415443269, TRUE),
    ('settlement_records', 2790685415443269, TRUE),
    ('table_fee_transactions', 2790685415443269, TRUE),
    ('assistant_service_records', 2790685415443269, TRUE),
    ('assistant_cancellation_records', 2790685415443269, TRUE),
    ('ODS_GOODS_LEDGER', 2790685415443269, TRUE),
    ('ODS_PAYMENT', 2790685415443269, TRUE),
    ('ODS_REFUND', 2790685415443269, TRUE),
    ('platform_coupon_redemption_records', 2790685415443269, TRUE),
    ('ODS_MEMBER', 2790685415443269, TRUE),
    ('member_stored_value_cards', 2790685415443269, TRUE),
    ('ODS_PACKAGE', 2790685415443269, TRUE),
    ('ODS_INVENTORY_STOCK', 2790685415443269, TRUE),
    ('goods_stock_movements', 2790685415443269, TRUE)
ON CONFLICT (task_code, store_id) DO NOTHING;

