diff --git a/etl_billiards/.env b/etl_billiards/.env index bad1af6..aec2c11 100644 --- a/etl_billiards/.env +++ b/etl_billiards/.env @@ -22,6 +22,7 @@ STORE_ID=2790685415443269 # 路径配置 EXPORT_ROOT=r"D:\LLZQ\DB\export", LOG_ROOT=r"D:\LLZQ\DB\logs", +PGCLIENTENCODING=utf8 # ETL配置 OVERLAP_SECONDS=120 # 为了防止边界遗漏,会往前“回拨”一点的冗余秒数 diff --git a/etl_billiards/.env.example b/etl_billiards/.env.example index 86a094d..cb8218f 100644 --- a/etl_billiards/.env.example +++ b/etl_billiards/.env.example @@ -36,8 +36,6 @@ LOG_UNKNOWN_FIELDS=true HASH_ALGO=sha1 STRICT_NUMERIC=true ROUND_MONEY_SCALE=2 -<<<<<<< HEAD -======= # 测试/离线模式 TEST_MODE=ONLINE @@ -46,4 +44,3 @@ TEST_JSON_TEMP_DIR=/tmp/etl_billiards_json_tmp # 测试数据库(可选:若设置则单元测试连入此 DSN) TEST_DB_DSN= ->>>>>>> main diff --git a/etl_billiards/LLZQ-test-1.sql b/etl_billiards/LLZQ-test-1.sql new file mode 100644 index 0000000..281a4d6 --- /dev/null +++ b/etl_billiards/LLZQ-test-1.sql @@ -0,0 +1,5815 @@ +-- +-- PostgreSQL database dump +-- + +\restrict p8JD9GasGaebtFp8BKgzK7l8DwZ7APkKsTnZV2aOY6thmYoUHLF5Lz8l8pc1Wad + +-- Dumped from database version 18.0 +-- Dumped by pg_dump version 18.0 + +-- Started on 2025-11-19 07:06:32 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET transaction_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- TOC entry 8 (class 2615 OID 21923) +-- Name: XCX; Type: SCHEMA; Schema: -; Owner: postgres +-- + +CREATE SCHEMA "XCX"; + + +ALTER SCHEMA "XCX" OWNER TO postgres; + +-- +-- TOC entry 6 (class 2615 OID 20228) +-- Name: billiards; Type: SCHEMA; Schema: -; Owner: postgres +-- + +CREATE SCHEMA billiards; + + +ALTER SCHEMA billiards OWNER TO postgres; + +-- +-- TOC entry 7 (class 2615 OID 20229) +-- Name: etl_admin; Type: SCHEMA; Schema: -; Owner: postgres +-- + +CREATE SCHEMA etl_admin; + + +ALTER SCHEMA etl_admin OWNER TO postgres; + +-- +-- TOC entry 5605 (class 0 OID 0) +-- Dependencies: 7 +-- Name: SCHEMA etl_admin; Type: COMMENT; Schema: -; Owner: postgres +-- + +COMMENT ON SCHEMA etl_admin IS 'ETL 元数据与运行控制专用 Schema,区分于业务表 billiards'; + + +-- +-- TOC entry 2 (class 3079 OID 20230) +-- Name: btree_gist; Type: EXTENSION; Schema: -; Owner: - +-- + +CREATE EXTENSION IF NOT EXISTS btree_gist WITH SCHEMA billiards; + + +-- +-- TOC entry 5606 (class 0 OID 0) +-- Dependencies: 2 +-- Name: EXTENSION btree_gist; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION btree_gist IS 'support for indexing common datatypes in GiST'; + + +-- +-- TOC entry 1109 (class 1247 OID 20995) +-- Name: run_status_enum; Type: TYPE; Schema: etl_admin; Owner: postgres +-- + +CREATE TYPE etl_admin.run_status_enum AS ENUM ( + 'SUCC', + 'FAIL', + 'PARTIAL' +); + + +ALTER TYPE etl_admin.run_status_enum OWNER TO postgres; + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- TOC entry 222 (class 1259 OID 21001) +-- Name: dim_assistant; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_assistant ( + store_id bigint NOT NULL, + assistant_id bigint NOT NULL, + assistant_name character varying(100), + nickname character varying(100), + level_name character varying(50), + skill_name character varying(100), + team_id bigint, + status character varying(20), + entry_date date, + leave_date date, + raw_data jsonb +); + + +ALTER TABLE billiards.dim_assistant OWNER TO postgres; + +-- +-- TOC entry 5819 (class 0 OID 0) +-- Dependencies: 222 +-- Name: TABLE dim_assistant; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_assistant IS '助教维度(账号/基础资料)'; + + +-- +-- TOC entry 5820 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.store_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.store_id IS '门店ID'; + + +-- +-- TOC entry 5821 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.assistant_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.assistant_id IS '助教ID(上游 siteAssistantId)'; + + +-- +-- TOC entry 5822 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.assistant_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.assistant_name IS '姓名'; + + +-- +-- TOC entry 5823 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.nickname; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.nickname IS '昵称'; + + +-- +-- TOC entry 5824 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.level_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.level_name IS '当前等级名(历史变更放 SCD2)'; + + +-- +-- TOC entry 5825 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.skill_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.skill_name IS '技能标签(花式/九球等)'; + + +-- +-- TOC entry 5826 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.team_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.team_id IS '团队/班组ID'; + + +-- +-- TOC entry 5827 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.status; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.status IS '状态(在职/离职)'; + + +-- +-- TOC entry 5828 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.entry_date; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.entry_date IS '入职日期'; + + +-- +-- TOC entry 5829 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.leave_date; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.leave_date IS '离职日期'; + + +-- +-- TOC entry 5830 (class 0 OID 0) +-- Dependencies: 222 +-- Name: COLUMN dim_assistant.raw_data; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_assistant.raw_data IS '原始JSON快照'; + + +-- +-- TOC entry 223 (class 1259 OID 21008) +-- Name: dim_assistant_scd; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_assistant_scd ( + assistant_scd_id bigint NOT NULL, + store_id bigint NOT NULL, + assistant_id bigint NOT NULL, + assistant_name character varying(100), + level_name character varying(50), + skill_name character varying(100), + team_id bigint, + status character varying(20), + valid_from timestamp with time zone NOT NULL, + valid_to timestamp with time zone DEFAULT '10000-01-01 07:59:59+08'::timestamp with time zone NOT NULL, + is_current boolean DEFAULT true NOT NULL, + raw_data jsonb, + CONSTRAINT ck_das_range CHECK ((valid_from < valid_to)) +); + + +ALTER TABLE billiards.dim_assistant_scd OWNER TO postgres; + +-- +-- TOC entry 5832 (class 0 OID 0) +-- Dependencies: 223 +-- Name: TABLE dim_assistant_scd; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_assistant_scd IS '助教维度 SCD2:等级/团队等历史版本'; + + +-- +-- TOC entry 224 (class 1259 OID 21022) +-- Name: dim_assistant_current; Type: VIEW; Schema: billiards; Owner: postgres +-- + +CREATE VIEW billiards.dim_assistant_current AS + SELECT assistant_scd_id, + store_id, + assistant_id, + assistant_name, + level_name, + skill_name, + team_id, + status, + valid_from, + valid_to, + is_current, + raw_data + FROM billiards.dim_assistant_scd + WHERE (is_current = true); + + +ALTER VIEW billiards.dim_assistant_current OWNER TO postgres; + +-- +-- TOC entry 225 (class 1259 OID 21026) +-- Name: dim_assistant_scd_assistant_scd_id_seq; Type: SEQUENCE; Schema: billiards; Owner: postgres +-- + +CREATE SEQUENCE billiards.dim_assistant_scd_assistant_scd_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE billiards.dim_assistant_scd_assistant_scd_id_seq OWNER TO postgres; + +-- +-- TOC entry 5835 (class 0 OID 0) +-- Dependencies: 225 +-- Name: dim_assistant_scd_assistant_scd_id_seq; Type: SEQUENCE OWNED BY; Schema: billiards; Owner: postgres +-- + +ALTER SEQUENCE billiards.dim_assistant_scd_assistant_scd_id_seq OWNED BY billiards.dim_assistant_scd.assistant_scd_id; + + +-- +-- TOC entry 226 (class 1259 OID 21027) +-- Name: dim_member; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_member ( + store_id bigint NOT NULL, + member_id bigint NOT NULL, + member_name character varying(100), + mobile character varying(30), + register_time timestamp with time zone, + member_level_code character varying(50), + member_level_name character varying(100), + status character varying(20), + referrer_id bigint, + birth_date date, + gender character varying(10), + point_balance numeric(12,2), + raw_data jsonb +); + + +ALTER TABLE billiards.dim_member OWNER TO postgres; + +-- +-- TOC entry 5837 (class 0 OID 0) +-- Dependencies: 226 +-- Name: TABLE dim_member; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_member IS '会员维度;等级历史在 SCD2 保留'; + + +-- +-- TOC entry 5838 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.store_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.store_id IS '门店ID'; + + +-- +-- TOC entry 5839 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.member_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.member_id IS '会员ID(tenantMemberId/systemMemberId)'; + + +-- +-- TOC entry 5840 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.member_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.member_name IS '会员姓名'; + + +-- +-- TOC entry 5841 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.mobile; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.mobile IS '手机号'; + + +-- +-- TOC entry 5842 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.register_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.register_time IS '注册时间'; + + +-- +-- TOC entry 5843 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.member_level_code; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.member_level_code IS '会员等级代码'; + + +-- +-- TOC entry 5844 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.member_level_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.member_level_name IS '会员等级名称'; + + +-- +-- TOC entry 5845 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.status; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.status IS '状态'; + + +-- +-- TOC entry 5846 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.referrer_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.referrer_id IS '推荐人会员ID'; + + +-- +-- TOC entry 5847 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.birth_date; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.birth_date IS '生日'; + + +-- +-- TOC entry 5848 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.gender; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.gender IS '性别'; + + +-- +-- TOC entry 5849 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.point_balance; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.point_balance IS '积分余额(如有积分)'; + + +-- +-- TOC entry 5850 (class 0 OID 0) +-- Dependencies: 226 +-- Name: COLUMN dim_member.raw_data; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_member.raw_data IS '原始JSON快照'; + + +-- +-- TOC entry 227 (class 1259 OID 21034) +-- Name: dim_member_scd; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_member_scd ( + member_scd_id bigint NOT NULL, + store_id bigint NOT NULL, + member_id bigint NOT NULL, + member_level_code character varying(50), + member_level_name character varying(100), + member_name character varying(100), + mobile character varying(30), + status character varying(20), + valid_from timestamp with time zone NOT NULL, + valid_to timestamp with time zone DEFAULT '10000-01-01 07:59:59+08'::timestamp with time zone NOT NULL, + is_current boolean DEFAULT true NOT NULL, + raw_data jsonb, + CONSTRAINT ck_dms_range CHECK ((valid_from < valid_to)) +); + + +ALTER TABLE billiards.dim_member_scd OWNER TO postgres; + +-- +-- TOC entry 5852 (class 0 OID 0) +-- Dependencies: 227 +-- Name: TABLE dim_member_scd; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_member_scd IS '会员维度 SCD2:保留等级等属性的历史版本'; + + +-- +-- TOC entry 228 (class 1259 OID 21048) +-- Name: dim_member_current; Type: VIEW; Schema: billiards; Owner: postgres +-- + +CREATE VIEW billiards.dim_member_current AS + SELECT member_scd_id, + store_id, + member_id, + member_level_code, + member_level_name, + member_name, + mobile, + status, + valid_from, + valid_to, + is_current, + raw_data + FROM billiards.dim_member_scd + WHERE (is_current = true); + + +ALTER VIEW billiards.dim_member_current OWNER TO postgres; + +-- +-- TOC entry 229 (class 1259 OID 21052) +-- Name: dim_member_scd_member_scd_id_seq; Type: SEQUENCE; Schema: billiards; Owner: postgres +-- + +CREATE SEQUENCE billiards.dim_member_scd_member_scd_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE billiards.dim_member_scd_member_scd_id_seq OWNER TO postgres; + +-- +-- TOC entry 5855 (class 0 OID 0) +-- Dependencies: 229 +-- Name: dim_member_scd_member_scd_id_seq; Type: SEQUENCE OWNED BY; Schema: billiards; Owner: postgres +-- + +ALTER SEQUENCE billiards.dim_member_scd_member_scd_id_seq OWNED BY billiards.dim_member_scd.member_scd_id; + + +-- +-- TOC entry 230 (class 1259 OID 21053) +-- Name: dim_package; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_package ( + store_id bigint NOT NULL, + package_id bigint NOT NULL, + package_name character varying(200), + content_desc character varying(500), + sale_price numeric(12,2), + valid_from date, + valid_to date, + status character varying(20), + raw_data jsonb +); + + +ALTER TABLE billiards.dim_package OWNER TO postgres; + +-- +-- TOC entry 5857 (class 0 OID 0) +-- Dependencies: 230 +-- Name: TABLE dim_package; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_package IS '套餐/团购定义'; + + +-- +-- TOC entry 5858 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.store_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.store_id IS '门店ID'; + + +-- +-- TOC entry 5859 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.package_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.package_id IS '套餐ID'; + + +-- +-- TOC entry 5860 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.package_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.package_name IS '套餐名称'; + + +-- +-- TOC entry 5861 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.content_desc; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.content_desc IS '包含内容/说明'; + + +-- +-- TOC entry 5862 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.sale_price; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.sale_price IS '标价'; + + +-- +-- TOC entry 5863 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.valid_from; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.valid_from IS '有效期起'; + + +-- +-- TOC entry 5864 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.valid_to; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.valid_to IS '有效期止'; + + +-- +-- TOC entry 5865 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.status; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.status IS '状态'; + + +-- +-- TOC entry 5866 (class 0 OID 0) +-- Dependencies: 230 +-- Name: COLUMN dim_package.raw_data; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_package.raw_data IS '原始JSON快照'; + + +-- +-- TOC entry 231 (class 1259 OID 21060) +-- Name: dim_product; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_product ( + store_id bigint NOT NULL, + product_id bigint NOT NULL, + site_product_id bigint, + product_name character varying(200) NOT NULL, + category_id bigint, + category_name character varying(100), + second_category_id bigint, + unit character varying(20), + cost_price numeric(12,2), + sale_price numeric(12,2), + allow_discount boolean, + status character varying(20), + supplier_id bigint, + barcode character varying(64), + is_combo boolean, + created_time timestamp with time zone, + updated_time timestamp with time zone, + raw_data jsonb +); + + +ALTER TABLE billiards.dim_product OWNER TO postgres; + +-- +-- TOC entry 5868 (class 0 OID 0) +-- Dependencies: 231 +-- Name: TABLE dim_product; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_product IS '商品维度(含门店价/状态等)'; + + +-- +-- TOC entry 5869 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.store_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.store_id IS '门店ID'; + + +-- +-- TOC entry 5870 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.product_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.product_id IS '商品ID(tenantGoodsId或统一ID)'; + + +-- +-- TOC entry 5871 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.site_product_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.site_product_id IS '门店商品ID(siteGoodsId)'; + + +-- +-- TOC entry 5872 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.product_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.product_name IS '商品名称'; + + +-- +-- TOC entry 5873 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.category_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.category_id IS '一级类目ID'; + + +-- +-- TOC entry 5874 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.category_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.category_name IS '一级类目名称'; + + +-- +-- TOC entry 5875 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.second_category_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.second_category_id IS '二级类目ID'; + + +-- +-- TOC entry 5876 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.unit; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.unit IS '计量单位(瓶/盒/份)'; + + +-- +-- TOC entry 5877 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.cost_price; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.cost_price IS '成本价(用于毛利分析)'; + + +-- +-- TOC entry 5878 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.sale_price; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.sale_price IS '标准售价/门店售价'; + + +-- +-- TOC entry 5879 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.allow_discount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.allow_discount IS '是否允许打折'; + + +-- +-- TOC entry 5880 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.status; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.status IS '状态(上/下架)'; + + +-- +-- TOC entry 5881 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.supplier_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.supplier_id IS '供应商ID(如有)'; + + +-- +-- TOC entry 5882 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.barcode; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.barcode IS '条码'; + + +-- +-- TOC entry 5883 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.is_combo; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.is_combo IS '是否组合/套餐商品'; + + +-- +-- TOC entry 5884 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.created_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.created_time IS '创建时间'; + + +-- +-- TOC entry 5885 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.updated_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.updated_time IS '最近更新时间'; + + +-- +-- TOC entry 5886 (class 0 OID 0) +-- Dependencies: 231 +-- Name: COLUMN dim_product.raw_data; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_product.raw_data IS '原始JSON快照'; + + +-- +-- TOC entry 232 (class 1259 OID 21068) +-- Name: dim_product_price_scd; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_product_price_scd ( + product_scd_id bigint NOT NULL, + store_id bigint NOT NULL, + product_id bigint NOT NULL, + product_name character varying(200), + category_id bigint, + category_name character varying(100), + second_category_id bigint, + cost_price numeric(12,2), + sale_price numeric(12,2), + allow_discount boolean, + status character varying(20), + valid_from timestamp with time zone NOT NULL, + valid_to timestamp with time zone DEFAULT '10000-01-01 07:59:59+08'::timestamp with time zone NOT NULL, + is_current boolean DEFAULT true NOT NULL, + raw_data jsonb, + CONSTRAINT ck_dpps_range CHECK ((valid_from < valid_to)) +); + + +ALTER TABLE billiards.dim_product_price_scd OWNER TO postgres; + +-- +-- TOC entry 5888 (class 0 OID 0) +-- Dependencies: 232 +-- Name: TABLE dim_product_price_scd; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_product_price_scd IS '商品维度 SCD2:价格/分类等历史版本'; + + +-- +-- TOC entry 233 (class 1259 OID 21082) +-- Name: dim_product_price_current; Type: VIEW; Schema: billiards; Owner: postgres +-- + +CREATE VIEW billiards.dim_product_price_current AS + SELECT product_scd_id, + store_id, + product_id, + product_name, + category_id, + category_name, + second_category_id, + cost_price, + sale_price, + allow_discount, + status, + valid_from, + valid_to, + is_current, + raw_data + FROM billiards.dim_product_price_scd + WHERE (is_current = true); + + +ALTER VIEW billiards.dim_product_price_current OWNER TO postgres; + +-- +-- TOC entry 234 (class 1259 OID 21086) +-- Name: dim_product_price_scd_product_scd_id_seq; Type: SEQUENCE; Schema: billiards; Owner: postgres +-- + +CREATE SEQUENCE billiards.dim_product_price_scd_product_scd_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE billiards.dim_product_price_scd_product_scd_id_seq OWNER TO postgres; + +-- +-- TOC entry 5891 (class 0 OID 0) +-- Dependencies: 234 +-- Name: dim_product_price_scd_product_scd_id_seq; Type: SEQUENCE OWNED BY; Schema: billiards; Owner: postgres +-- + +ALTER SEQUENCE billiards.dim_product_price_scd_product_scd_id_seq OWNED BY billiards.dim_product_price_scd.product_scd_id; + + +-- +-- TOC entry 235 (class 1259 OID 21087) +-- Name: dim_store; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_store ( + store_id bigint NOT NULL, + store_name character varying(100) NOT NULL, + tenant_id bigint, + created_time timestamp with time zone, + updated_time timestamp with time zone, + remark character varying(255) +); + + +ALTER TABLE billiards.dim_store OWNER TO postgres; + +-- +-- TOC entry 5893 (class 0 OID 0) +-- Dependencies: 235 +-- Name: TABLE dim_store; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_store IS '门店维度;预留多门店扩展'; + + +-- +-- TOC entry 5894 (class 0 OID 0) +-- Dependencies: 235 +-- Name: COLUMN dim_store.store_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_store.store_id IS '门店ID(上游 siteId)'; + + +-- +-- TOC entry 5895 (class 0 OID 0) +-- Dependencies: 235 +-- Name: COLUMN dim_store.store_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_store.store_name IS '门店名称'; + + +-- +-- TOC entry 5896 (class 0 OID 0) +-- Dependencies: 235 +-- Name: COLUMN dim_store.tenant_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_store.tenant_id IS '租户ID(平台商户)'; + + +-- +-- TOC entry 5897 (class 0 OID 0) +-- Dependencies: 235 +-- Name: COLUMN dim_store.created_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_store.created_time IS '创建时间'; + + +-- +-- TOC entry 5898 (class 0 OID 0) +-- Dependencies: 235 +-- Name: COLUMN dim_store.updated_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_store.updated_time IS '最近更新时间'; + + +-- +-- TOC entry 5899 (class 0 OID 0) +-- Dependencies: 235 +-- Name: COLUMN dim_store.remark; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_store.remark IS '备注'; + + +-- +-- TOC entry 236 (class 1259 OID 21092) +-- Name: dim_table; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.dim_table ( + store_id bigint NOT NULL, + table_id bigint NOT NULL, + table_name character varying(100) NOT NULL, + table_area_id bigint, + table_area_name character varying(100), + is_vip boolean, + charge_free boolean, + status character varying(20), + created_time timestamp with time zone, + last_used_time timestamp with time zone, + raw_data jsonb +); + + +ALTER TABLE billiards.dim_table OWNER TO postgres; + +-- +-- TOC entry 5901 (class 0 OID 0) +-- Dependencies: 236 +-- Name: TABLE dim_table; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.dim_table IS '球台维度'; + + +-- +-- TOC entry 5902 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.store_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.store_id IS '门店ID'; + + +-- +-- TOC entry 5903 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.table_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.table_id IS '球台ID'; + + +-- +-- TOC entry 5904 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.table_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.table_name IS '球台名称/编号'; + + +-- +-- TOC entry 5905 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.table_area_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.table_area_id IS '区域ID(大厅/包厢等)'; + + +-- +-- TOC entry 5906 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.table_area_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.table_area_name IS '区域名称'; + + +-- +-- TOC entry 5907 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.is_vip; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.is_vip IS '是否VIP专用'; + + +-- +-- TOC entry 5908 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.charge_free; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.charge_free IS '是否免台费'; + + +-- +-- TOC entry 5909 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.status; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.status IS '状态(启用/停用等)'; + + +-- +-- TOC entry 5910 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.created_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.created_time IS '创建时间'; + + +-- +-- TOC entry 5911 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.last_used_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.last_used_time IS '最近使用时间'; + + +-- +-- TOC entry 5912 (class 0 OID 0) +-- Dependencies: 236 +-- Name: COLUMN dim_table.raw_data; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.dim_table.raw_data IS '原始JSON快照'; + + +-- +-- TOC entry 237 (class 1259 OID 21100) +-- Name: fact_assistant_abolish; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_assistant_abolish ( + store_id bigint NOT NULL, + abolish_id bigint NOT NULL, + order_id bigint, + table_id bigint, + table_area_name character varying(100), + assistant_id bigint, + assistant_name character varying(100), + start_time timestamp with time zone, + end_time timestamp with time zone, + pd_charge_minutes integer, + assistant_abolish_amount numeric(14,2), + trash_reason character varying(255), + create_time timestamp with time zone +); + + +ALTER TABLE billiards.fact_assistant_abolish OWNER TO postgres; + +-- +-- TOC entry 5914 (class 0 OID 0) +-- Dependencies: 237 +-- Name: TABLE fact_assistant_abolish; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_assistant_abolish IS '助教服务作废/取消记录'; + + +-- +-- TOC entry 238 (class 1259 OID 21105) +-- Name: fact_inventory_change; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_inventory_change ( + store_id bigint NOT NULL, + change_id bigint NOT NULL, + product_id bigint, + change_type character varying(50), + change_quantity numeric(12,2), + start_stock numeric(12,2), + end_stock numeric(12,2), + unit character varying(20), + price numeric(12,2), + operator_name character varying(100), + change_time timestamp with time zone, + remark character varying(255) +); + + +ALTER TABLE billiards.fact_inventory_change OWNER TO postgres; + +-- +-- TOC entry 5916 (class 0 OID 0) +-- Dependencies: 238 +-- Name: TABLE fact_inventory_change; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_inventory_change IS '库存变动流水(进/销/退/盘点)'; + + +-- +-- TOC entry 239 (class 1259 OID 21110) +-- Name: fact_ledger_entry; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_ledger_entry ( + store_id bigint NOT NULL, + entry_id bigint NOT NULL, + entry_time timestamp with time zone, + transaction_type character varying(50), + transaction_id character varying(100), + account_debit character varying(100), + account_credit character varying(100), + amount numeric(14,2), + memo character varying(255), + CONSTRAINT ck_fle_amount_pos CHECK ((COALESCE(amount, (0)::numeric) >= (0)::numeric)) +); + + +ALTER TABLE billiards.fact_ledger_entry OWNER TO postgres; + +-- +-- TOC entry 5918 (class 0 OID 0) +-- Dependencies: 239 +-- Name: TABLE fact_ledger_entry; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_ledger_entry IS '双分录总账(资金与收入科目平衡核对)'; + + +-- +-- TOC entry 240 (class 1259 OID 21118) +-- Name: fact_order; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_order ( + store_id bigint NOT NULL, + order_id bigint NOT NULL, + order_no character varying(64), + member_id bigint, + open_table_time timestamp with time zone, + close_time timestamp with time zone, + total_amount numeric(14,2), + payable_amount numeric(14,2), + discount_amount numeric(14,2), + pay_amount numeric(14,2), + pay_status character varying(20), + payment_method character varying(50), + assistant_count integer, + table_time_minutes integer, + salesman_id bigint, + salesman_name character varying(100), + created_time timestamp with time zone, + table_id bigint +); + + +ALTER TABLE billiards.fact_order OWNER TO postgres; + +-- +-- TOC entry 5920 (class 0 OID 0) +-- Dependencies: 240 +-- Name: TABLE fact_order; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_order IS '订单/结账事实(一次离场结账为一单)'; + + +-- +-- TOC entry 5921 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.store_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.store_id IS '门店ID'; + + +-- +-- TOC entry 5922 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.order_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.order_id IS '订单ID(orderSettleId)'; + + +-- +-- TOC entry 5923 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.order_no; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.order_no IS '交易号(orderTradeNo)'; + + +-- +-- TOC entry 5924 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.member_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.member_id IS '会员ID(如有)'; + + +-- +-- TOC entry 5925 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.open_table_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.open_table_time IS '开台时间'; + + +-- +-- TOC entry 5926 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.close_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.close_time IS '结账时间'; + + +-- +-- TOC entry 5927 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.total_amount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.total_amount IS '订单总金额(含所有项目)'; + + +-- +-- TOC entry 5928 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.payable_amount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.payable_amount IS '应收金额(折前)'; + + +-- +-- TOC entry 5929 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.discount_amount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.discount_amount IS '总优惠金额(会员/手工/券)'; + + +-- +-- TOC entry 5930 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.pay_amount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.pay_amount IS '实收金额'; + + +-- +-- TOC entry 5931 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.pay_status; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.pay_status IS '支付状态'; + + +-- +-- TOC entry 5932 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.payment_method; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.payment_method IS '主支付方式(聚合口径)'; + + +-- +-- TOC entry 5933 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.assistant_count; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.assistant_count IS '助教服务次数/人数估计'; + + +-- +-- TOC entry 5934 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.table_time_minutes; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.table_time_minutes IS '台费累计分钟'; + + +-- +-- TOC entry 5935 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.salesman_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.salesman_id IS '开单/营业员ID'; + + +-- +-- TOC entry 5936 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.salesman_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.salesman_name IS '开单/营业员姓名'; + + +-- +-- TOC entry 5937 (class 0 OID 0) +-- Dependencies: 240 +-- Name: COLUMN fact_order.created_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order.created_time IS '订单创建时间/入库时间'; + + +-- +-- TOC entry 241 (class 1259 OID 21124) +-- Name: fact_order_detail; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_order_detail ( + store_id bigint NOT NULL, + order_id bigint NOT NULL, + detail_id bigint NOT NULL, + detail_type character varying(20) NOT NULL, + item_id bigint, + item_name character varying(200), + quantity numeric(12,2), + unit_price numeric(12,2), + amount numeric(14,2), + original_amount numeric(14,2), + discount numeric(14,2), + start_time timestamp with time zone, + end_time timestamp with time zone, + duration_seconds integer, + assistant_id bigint, + assistant_level character varying(50), + table_id bigint, + table_area character varying(100), + product_id bigint, + salesman_id bigint, + salesman_name character varying(100), + notes character varying(255), + CONSTRAINT ck_fod_amount_pos CHECK (((COALESCE(amount, (0)::numeric) >= (0)::numeric) AND (COALESCE(original_amount, (0)::numeric) >= (0)::numeric) AND (COALESCE(discount, (0)::numeric) >= (0)::numeric))) +); + + +ALTER TABLE billiards.fact_order_detail OWNER TO postgres; + +-- +-- TOC entry 5939 (class 0 OID 0) +-- Dependencies: 241 +-- Name: TABLE fact_order_detail; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_order_detail IS '订单明细(台费/助教费/商品)'; + + +-- +-- TOC entry 5940 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.detail_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.detail_id IS '明细ID(上游明细主键或生成ID)'; + + +-- +-- TOC entry 5941 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.detail_type; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.detail_type IS '明细类别:TABLE/ASSISTANT/PRODUCT/OTHER'; + + +-- +-- TOC entry 5942 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.item_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.item_id IS '项目ID(商品/助教/球台)'; + + +-- +-- TOC entry 5943 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.item_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.item_name IS '项目名称(商品名/助教名/球台号)'; + + +-- +-- TOC entry 5944 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.quantity; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.quantity IS '数量(商品件数或时长转化)'; + + +-- +-- TOC entry 5945 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.unit_price; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.unit_price IS '单价'; + + +-- +-- TOC entry 5946 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.amount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.amount IS '折后金额'; + + +-- +-- TOC entry 5947 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.original_amount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.original_amount IS '原价金额'; + + +-- +-- TOC entry 5948 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.discount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.discount IS '折扣金额'; + + +-- +-- TOC entry 5949 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.start_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.start_time IS '开始时间(按时计费使用)'; + + +-- +-- TOC entry 5950 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.end_time; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.end_time IS '结束时间'; + + +-- +-- TOC entry 5951 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.duration_seconds; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.duration_seconds IS '服务/台费时长(秒)'; + + +-- +-- TOC entry 5952 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.assistant_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.assistant_id IS '助教ID(助教费)'; + + +-- +-- TOC entry 5953 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.assistant_level; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.assistant_level IS '服务当时助教等级'; + + +-- +-- TOC entry 5954 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.table_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.table_id IS '球台ID(台费)'; + + +-- +-- TOC entry 5955 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.table_area; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.table_area IS '球台区域名'; + + +-- +-- TOC entry 5956 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.product_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.product_id IS '商品ID(商品明细)'; + + +-- +-- TOC entry 5957 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.salesman_id; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.salesman_id IS '销售员ID'; + + +-- +-- TOC entry 5958 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.salesman_name; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.salesman_name IS '销售员姓名'; + + +-- +-- TOC entry 5959 (class 0 OID 0) +-- Dependencies: 241 +-- Name: COLUMN fact_order_detail.notes; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_order_detail.notes IS '备注(选项/退换信息等)'; + + +-- +-- TOC entry 242 (class 1259 OID 21134) +-- Name: fact_package_usage; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_package_usage ( + store_id bigint NOT NULL, + usage_id bigint NOT NULL, + package_id bigint, + order_id bigint, + member_id bigint, + redeem_time timestamp with time zone +); + + +ALTER TABLE billiards.fact_package_usage OWNER TO postgres; + +-- +-- TOC entry 5961 (class 0 OID 0) +-- Dependencies: 242 +-- Name: TABLE fact_package_usage; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_package_usage IS '套餐/团购核销流水'; + + +-- +-- TOC entry 243 (class 1259 OID 21139) +-- Name: fact_payment; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_payment ( + store_id bigint NOT NULL, + pay_id bigint NOT NULL, + order_id bigint, + pay_trade_no character varying(100), + pay_amount numeric(14,2), + pay_method character varying(50), + pay_time timestamp with time zone, + pay_status character varying(20), + operator_id bigint, + create_time timestamp with time zone, + CONSTRAINT ck_fp_amount_pos CHECK ((pay_amount >= (0)::numeric)) +); + + +ALTER TABLE billiards.fact_payment OWNER TO postgres; + +-- +-- TOC entry 5963 (class 0 OID 0) +-- Dependencies: 243 +-- Name: TABLE fact_payment; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_payment IS '支付流水(可能一单多次支付)'; + + +-- +-- TOC entry 244 (class 1259 OID 21145) +-- Name: fact_refund; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_refund ( + store_id bigint NOT NULL, + refund_id bigint NOT NULL, + order_id bigint, + pay_id bigint, + refund_amount numeric(14,2), + refund_time timestamp with time zone, + refund_method character varying(50), + operator_id bigint, + reason character varying(255), + CONSTRAINT ck_fr_amount_pos CHECK ((refund_amount >= (0)::numeric)) +); + + +ALTER TABLE billiards.fact_refund OWNER TO postgres; + +-- +-- TOC entry 5965 (class 0 OID 0) +-- Dependencies: 244 +-- Name: TABLE fact_refund; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_refund IS '退款流水'; + + +-- +-- TOC entry 245 (class 1259 OID 21151) +-- Name: fact_table_discount; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_table_discount ( + store_id bigint NOT NULL, + discount_id bigint NOT NULL, + order_id bigint, + table_id bigint, + applicant_id bigint, + applicant_name character varying(100), + operator_id bigint, + operator_name character varying(100), + adjust_type character varying(50), + ledger_count numeric(12,2), + ledger_amount numeric(14,2), + create_time timestamp with time zone, + reason character varying(255) +); + + +ALTER TABLE billiards.fact_table_discount OWNER TO postgres; + +-- +-- TOC entry 5967 (class 0 OID 0) +-- Dependencies: 245 +-- Name: TABLE fact_table_discount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_table_discount IS '台费打折/减免记录'; + + +-- +-- TOC entry 5968 (class 0 OID 0) +-- Dependencies: 245 +-- Name: COLUMN fact_table_discount.ledger_count; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_table_discount.ledger_count IS '减免费用数量(如分钟)'; + + +-- +-- TOC entry 5969 (class 0 OID 0) +-- Dependencies: 245 +-- Name: COLUMN fact_table_discount.ledger_amount; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON COLUMN billiards.fact_table_discount.ledger_amount IS '减免金额'; + + +-- +-- TOC entry 246 (class 1259 OID 21158) +-- Name: fact_topup; Type: TABLE; Schema: billiards; Owner: postgres +-- + +CREATE TABLE billiards.fact_topup ( + store_id bigint NOT NULL, + topup_id bigint NOT NULL, + member_id bigint, + order_id bigint, + amount_recharge numeric(14,2), + amount_bonus numeric(14,2), + pay_method character varying(50), + pay_time timestamp with time zone, + operator_id bigint, + remark character varying(255), + CONSTRAINT ck_ft_amount_pos CHECK (((COALESCE(amount_recharge, (0)::numeric) >= (0)::numeric) AND (COALESCE(amount_bonus, (0)::numeric) >= (0)::numeric))) +); + + +ALTER TABLE billiards.fact_topup OWNER TO postgres; + +-- +-- TOC entry 5971 (class 0 OID 0) +-- Dependencies: 246 +-- Name: TABLE fact_topup; Type: COMMENT; Schema: billiards; Owner: postgres +-- + +COMMENT ON TABLE billiards.fact_topup IS '会员充值记录(含赠送)'; + + +-- +-- TOC entry 247 (class 1259 OID 21164) +-- Name: etl_cursor; Type: TABLE; Schema: etl_admin; Owner: postgres +-- + +CREATE TABLE etl_admin.etl_cursor ( + cursor_id bigint NOT NULL, + task_id bigint NOT NULL, + store_id bigint NOT NULL, + last_start timestamp with time zone, + last_end timestamp with time zone, + last_id bigint, + extra jsonb DEFAULT '{}'::jsonb NOT NULL, + last_run_id bigint, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + lock_token uuid, + lock_at timestamp with time zone, + CONSTRAINT ck_cursor_range CHECK (((last_start IS NULL) OR (last_end IS NULL) OR (last_start <= last_end))) +); + + +ALTER TABLE etl_admin.etl_cursor OWNER TO postgres; + +-- +-- TOC entry 5973 (class 0 OID 0) +-- Dependencies: 247 +-- Name: TABLE etl_cursor; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON TABLE etl_admin.etl_cursor IS 'ETL 任务时间/ID 水位;支撑增量抓取的断点续传'; + + +-- +-- TOC entry 5974 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.task_id; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.task_id IS '关联 etl_task.task_id'; + + +-- +-- TOC entry 5975 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.store_id; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.store_id IS '门店ID(与任务维度一致,便于过滤)'; + + +-- +-- TOC entry 5976 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.last_start; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.last_start IS '上次成功抓取窗口起点(含)'; + + +-- +-- TOC entry 5977 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.last_end; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.last_end IS '上次成功抓取窗口终点(含)'; + + +-- +-- TOC entry 5978 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.last_id; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.last_id IS '上次成功抓取到的最大ID(如接口支持按ID增量)'; + + +-- +-- TOC entry 5979 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.extra; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.extra IS '附加游标数据(JSON):如页码、标志位等'; + + +-- +-- TOC entry 5980 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.last_run_id; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.last_run_id IS '最近一次成功运行的 etl_run.run_id'; + + +-- +-- TOC entry 5981 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.updated_at; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.updated_at IS '最近更新时间'; + + +-- +-- TOC entry 5982 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.lock_token; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.lock_token IS '并发抢占锁的 token,防重入'; + + +-- +-- TOC entry 5983 (class 0 OID 0) +-- Dependencies: 247 +-- Name: COLUMN etl_cursor.lock_at; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_cursor.lock_at IS '加锁时间戳'; + + +-- +-- TOC entry 248 (class 1259 OID 21177) +-- Name: etl_cursor_cursor_id_seq; Type: SEQUENCE; Schema: etl_admin; Owner: postgres +-- + +CREATE SEQUENCE etl_admin.etl_cursor_cursor_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE etl_admin.etl_cursor_cursor_id_seq OWNER TO postgres; + +-- +-- TOC entry 5985 (class 0 OID 0) +-- Dependencies: 248 +-- Name: etl_cursor_cursor_id_seq; Type: SEQUENCE OWNED BY; Schema: etl_admin; Owner: postgres +-- + +ALTER SEQUENCE etl_admin.etl_cursor_cursor_id_seq OWNED BY etl_admin.etl_cursor.cursor_id; + + +-- +-- TOC entry 249 (class 1259 OID 21178) +-- Name: etl_run; Type: TABLE; Schema: etl_admin; Owner: postgres +-- + +CREATE TABLE etl_admin.etl_run ( + run_id bigint NOT NULL, + run_uuid uuid DEFAULT gen_random_uuid() NOT NULL, + task_id bigint NOT NULL, + store_id bigint NOT NULL, + status etl_admin.run_status_enum NOT NULL, + started_at timestamp with time zone DEFAULT now() NOT NULL, + ended_at timestamp with time zone, + window_start timestamp with time zone, + window_end timestamp with time zone, + window_minutes integer, + overlap_seconds integer, + fetched_count integer DEFAULT 0 NOT NULL, + loaded_count integer DEFAULT 0 NOT NULL, + updated_count integer DEFAULT 0 NOT NULL, + skipped_count integer DEFAULT 0 NOT NULL, + error_count integer DEFAULT 0 NOT NULL, + unknown_fields integer DEFAULT 0 NOT NULL, + export_dir text, + log_path text, + request_params jsonb, + manifest jsonb, + error_message text, + extra jsonb DEFAULT '{}'::jsonb NOT NULL, + created_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT ck_run_counts CHECK (((fetched_count >= 0) AND (loaded_count >= 0) AND (updated_count >= 0) AND (skipped_count >= 0) AND (error_count >= 0) AND (unknown_fields >= 0))), + CONSTRAINT ck_run_time CHECK (((ended_at IS NULL) OR (ended_at >= started_at))) +); + + +ALTER TABLE etl_admin.etl_run OWNER TO postgres; + +-- +-- TOC entry 5987 (class 0 OID 0) +-- Dependencies: 249 +-- Name: TABLE etl_run; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON TABLE etl_admin.etl_run IS 'ETL 运行流水;记录每次任务运行的窗口、计数、导出与日志位置'; + + +-- +-- TOC entry 5988 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.run_uuid; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.run_uuid IS '运行 UUID;建议与导出目录名中的 run_id 对齐'; + + +-- +-- TOC entry 5989 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.task_id; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.task_id IS '关联 etl_task.task_id'; + + +-- +-- TOC entry 5990 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.store_id; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.store_id IS '门店ID(与任务维度一致)'; + + +-- +-- TOC entry 5991 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.status; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.status IS '运行状态:SUCC/FAIL/PARTIAL'; + + +-- +-- TOC entry 5992 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.started_at; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.started_at IS '开始时间'; + + +-- +-- TOC entry 5993 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.ended_at; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.ended_at IS '结束时间'; + + +-- +-- TOC entry 5994 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.window_start; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.window_start IS '此次抓取窗口起点(请求侧)'; + + +-- +-- TOC entry 5995 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.window_end; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.window_end IS '此次抓取窗口终点(请求侧)'; + + +-- +-- TOC entry 5996 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.window_minutes; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.window_minutes IS '窗口大小(分钟)'; + + +-- +-- TOC entry 5997 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.overlap_seconds; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.overlap_seconds IS '窗口冗余(秒)'; + + +-- +-- TOC entry 5998 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.fetched_count; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.fetched_count IS '拉取条数(原始响应合计)'; + + +-- +-- TOC entry 5999 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.loaded_count; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.loaded_count IS '入库新增数'; + + +-- +-- TOC entry 6000 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.updated_count; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.updated_count IS '入库更新数'; + + +-- +-- TOC entry 6001 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.skipped_count; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.skipped_count IS '因幂等已存在而跳过的条数'; + + +-- +-- TOC entry 6002 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.error_count; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.error_count IS '处理失败条数'; + + +-- +-- TOC entry 6003 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.unknown_fields; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.unknown_fields IS '清洗过程中识别为未知的字段数量'; + + +-- +-- TOC entry 6004 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.export_dir; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.export_dir IS '导出目录绝对路径(与 manifest.json 配对)'; + + +-- +-- TOC entry 6005 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.log_path; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.log_path IS '日志文件路径'; + + +-- +-- TOC entry 6006 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.request_params; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.request_params IS '关键请求参数(JSON);不含敏感 token'; + + +-- +-- TOC entry 6007 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.manifest; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.manifest IS 'manifest.json 内容冗余(JSON)'; + + +-- +-- TOC entry 6008 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.error_message; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.error_message IS '错误摘要信息'; + + +-- +-- TOC entry 6009 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.extra; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.extra IS '其他补充键值对(JSON)'; + + +-- +-- TOC entry 6010 (class 0 OID 0) +-- Dependencies: 249 +-- Name: COLUMN etl_run.created_at; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_run.created_at IS '记录创建时间'; + + +-- +-- TOC entry 250 (class 1259 OID 21209) +-- Name: etl_run_run_id_seq; Type: SEQUENCE; Schema: etl_admin; Owner: postgres +-- + +CREATE SEQUENCE etl_admin.etl_run_run_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE etl_admin.etl_run_run_id_seq OWNER TO postgres; + +-- +-- TOC entry 6012 (class 0 OID 0) +-- Dependencies: 250 +-- Name: etl_run_run_id_seq; Type: SEQUENCE OWNED BY; Schema: etl_admin; Owner: postgres +-- + +ALTER SEQUENCE etl_admin.etl_run_run_id_seq OWNED BY etl_admin.etl_run.run_id; + + +-- +-- TOC entry 251 (class 1259 OID 21210) +-- Name: etl_task; Type: TABLE; Schema: etl_admin; Owner: postgres +-- + +CREATE TABLE etl_admin.etl_task ( + task_id bigint NOT NULL, + task_code character varying(64) NOT NULL, + store_id bigint NOT NULL, + enabled boolean DEFAULT true NOT NULL, + cursor_field character varying(64), + cursor_field_fallback character varying(64), + window_minutes_default integer DEFAULT 180 NOT NULL, + overlap_seconds integer DEFAULT 120 NOT NULL, + page_size integer DEFAULT 200 NOT NULL, + retry_max integer DEFAULT 3 NOT NULL, + params jsonb DEFAULT '{}'::jsonb NOT NULL, + description character varying(255), + created_at timestamp with time zone DEFAULT now() NOT NULL, + updated_at timestamp with time zone DEFAULT now() NOT NULL, + CONSTRAINT ck_task_overlap CHECK ((overlap_seconds >= 0)), + CONSTRAINT ck_task_pagesize CHECK ((page_size > 0)), + CONSTRAINT ck_task_retry CHECK ((retry_max >= 0)), + CONSTRAINT ck_task_window CHECK ((window_minutes_default > 0)) +); + + +ALTER TABLE etl_admin.etl_task OWNER TO postgres; + +-- +-- TOC entry 6014 (class 0 OID 0) +-- Dependencies: 251 +-- Name: TABLE etl_task; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON TABLE etl_admin.etl_task IS 'ETL 任务注册与默认配置;按 task_code + store_id 唯一'; + + +-- +-- TOC entry 6015 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.task_code; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.task_code IS '任务代码:ORDERS / PAYMENTS / REFUNDS / INVENTORY_CHANGE / COUPON_USAGE 等'; + + +-- +-- TOC entry 6016 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.store_id; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.store_id IS '门店ID(与业务数据同维度隔离)'; + + +-- +-- TOC entry 6017 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.enabled; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.enabled IS '任务是否启用'; + + +-- +-- TOC entry 6018 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.cursor_field; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.cursor_field IS '主游标字段名(上游返回的时间字段名)'; + + +-- +-- TOC entry 6019 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.cursor_field_fallback; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.cursor_field_fallback IS '备选游标字段名(主字段缺失时回退)'; + + +-- +-- TOC entry 6020 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.window_minutes_default; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.window_minutes_default IS '默认拉取窗口(分钟),闲忙时可由外部调度覆盖'; + + +-- +-- TOC entry 6021 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.overlap_seconds; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.overlap_seconds IS '窗口冗余秒数,防止边界遗漏'; + + +-- +-- TOC entry 6022 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.page_size; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.page_size IS '上游接口分页大小'; + + +-- +-- TOC entry 6023 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.retry_max; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.retry_max IS '失败重试次数上限'; + + +-- +-- TOC entry 6024 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.params; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.params IS '任务级自定义参数(JSON)'; + + +-- +-- TOC entry 6025 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.description; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.description IS '任务描述'; + + +-- +-- TOC entry 6026 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.created_at; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.created_at IS '创建时间'; + + +-- +-- TOC entry 6027 (class 0 OID 0) +-- Dependencies: 251 +-- Name: COLUMN etl_task.updated_at; Type: COMMENT; Schema: etl_admin; Owner: postgres +-- + +COMMENT ON COLUMN etl_admin.etl_task.updated_at IS '最近更新时间'; + + +-- +-- TOC entry 252 (class 1259 OID 21238) +-- Name: etl_task_task_id_seq; Type: SEQUENCE; Schema: etl_admin; Owner: postgres +-- + +CREATE SEQUENCE etl_admin.etl_task_task_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER SEQUENCE etl_admin.etl_task_task_id_seq OWNER TO postgres; + +-- +-- TOC entry 6029 (class 0 OID 0) +-- Dependencies: 252 +-- Name: etl_task_task_id_seq; Type: SEQUENCE OWNED BY; Schema: etl_admin; Owner: postgres +-- + +ALTER SEQUENCE etl_admin.etl_task_task_id_seq OWNED BY etl_admin.etl_task.task_id; + + +-- +-- TOC entry 5251 (class 2604 OID 21378) +-- Name: dim_assistant_scd assistant_scd_id; Type: DEFAULT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_assistant_scd ALTER COLUMN assistant_scd_id SET DEFAULT nextval('billiards.dim_assistant_scd_assistant_scd_id_seq'::regclass); + + +-- +-- TOC entry 5254 (class 2604 OID 21379) +-- Name: dim_member_scd member_scd_id; Type: DEFAULT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_member_scd ALTER COLUMN member_scd_id SET DEFAULT nextval('billiards.dim_member_scd_member_scd_id_seq'::regclass); + + +-- +-- TOC entry 5257 (class 2604 OID 21380) +-- Name: dim_product_price_scd product_scd_id; Type: DEFAULT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_product_price_scd ALTER COLUMN product_scd_id SET DEFAULT nextval('billiards.dim_product_price_scd_product_scd_id_seq'::regclass); + + +-- +-- TOC entry 5260 (class 2604 OID 21381) +-- Name: etl_cursor cursor_id; Type: DEFAULT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_cursor ALTER COLUMN cursor_id SET DEFAULT nextval('etl_admin.etl_cursor_cursor_id_seq'::regclass); + + +-- +-- TOC entry 5263 (class 2604 OID 21382) +-- Name: etl_run run_id; Type: DEFAULT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_run ALTER COLUMN run_id SET DEFAULT nextval('etl_admin.etl_run_run_id_seq'::regclass); + + +-- +-- TOC entry 5274 (class 2604 OID 21383) +-- Name: etl_task task_id; Type: DEFAULT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_task ALTER COLUMN task_id SET DEFAULT nextval('etl_admin.etl_task_task_id_seq'::regclass); + + +-- +-- TOC entry 5571 (class 0 OID 21001) +-- Dependencies: 222 +-- Data for Name: dim_assistant; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_assistant (store_id, assistant_id, assistant_name, nickname, level_name, skill_name, team_id, status, entry_date, leave_date, raw_data) FROM stdin; +\. + + +-- +-- TOC entry 5572 (class 0 OID 21008) +-- Dependencies: 223 +-- Data for Name: dim_assistant_scd; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_assistant_scd (assistant_scd_id, store_id, assistant_id, assistant_name, level_name, skill_name, team_id, status, valid_from, valid_to, is_current, raw_data) FROM stdin; +\. + + +-- +-- TOC entry 5574 (class 0 OID 21027) +-- Dependencies: 226 +-- Data for Name: dim_member; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_member (store_id, member_id, member_name, mobile, register_time, member_level_code, member_level_name, status, referrer_id, birth_date, gender, point_balance, raw_data) FROM stdin; +\. + + +-- +-- TOC entry 5575 (class 0 OID 21034) +-- Dependencies: 227 +-- Data for Name: dim_member_scd; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_member_scd (member_scd_id, store_id, member_id, member_level_code, member_level_name, member_name, mobile, status, valid_from, valid_to, is_current, raw_data) FROM stdin; +\. + + +-- +-- TOC entry 5577 (class 0 OID 21053) +-- Dependencies: 230 +-- Data for Name: dim_package; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_package (store_id, package_id, package_name, content_desc, sale_price, valid_from, valid_to, status, raw_data) FROM stdin; +\. + + +-- +-- TOC entry 5578 (class 0 OID 21060) +-- Dependencies: 231 +-- Data for Name: dim_product; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_product (store_id, product_id, site_product_id, product_name, category_id, category_name, second_category_id, unit, cost_price, sale_price, allow_discount, status, supplier_id, barcode, is_combo, created_time, updated_time, raw_data) FROM stdin; +\. + + +-- +-- TOC entry 5579 (class 0 OID 21068) +-- Dependencies: 232 +-- Data for Name: dim_product_price_scd; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_product_price_scd (product_scd_id, store_id, product_id, product_name, category_id, category_name, second_category_id, cost_price, sale_price, allow_discount, status, valid_from, valid_to, is_current, raw_data) FROM stdin; +\. + + +-- +-- TOC entry 5581 (class 0 OID 21087) +-- Dependencies: 235 +-- Data for Name: dim_store; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_store (store_id, store_name, tenant_id, created_time, updated_time, remark) FROM stdin; +\. + + +-- +-- TOC entry 5582 (class 0 OID 21092) +-- Dependencies: 236 +-- Data for Name: dim_table; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.dim_table (store_id, table_id, table_name, table_area_id, table_area_name, is_vip, charge_free, status, created_time, last_used_time, raw_data) FROM stdin; +\. + + +-- +-- TOC entry 5583 (class 0 OID 21100) +-- Dependencies: 237 +-- Data for Name: fact_assistant_abolish; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_assistant_abolish (store_id, abolish_id, order_id, table_id, table_area_name, assistant_id, assistant_name, start_time, end_time, pd_charge_minutes, assistant_abolish_amount, trash_reason, create_time) FROM stdin; +\. + + +-- +-- TOC entry 5584 (class 0 OID 21105) +-- Dependencies: 238 +-- Data for Name: fact_inventory_change; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_inventory_change (store_id, change_id, product_id, change_type, change_quantity, start_stock, end_stock, unit, price, operator_name, change_time, remark) FROM stdin; +\. + + +-- +-- TOC entry 5585 (class 0 OID 21110) +-- Dependencies: 239 +-- Data for Name: fact_ledger_entry; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_ledger_entry (store_id, entry_id, entry_time, transaction_type, transaction_id, account_debit, account_credit, amount, memo) FROM stdin; +\. + + +-- +-- TOC entry 5586 (class 0 OID 21118) +-- Dependencies: 240 +-- Data for Name: fact_order; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_order (store_id, order_id, order_no, member_id, open_table_time, close_time, total_amount, payable_amount, discount_amount, pay_amount, pay_status, payment_method, assistant_count, table_time_minutes, salesman_id, salesman_name, created_time, table_id) FROM stdin; +\. + + +-- +-- TOC entry 5587 (class 0 OID 21124) +-- Dependencies: 241 +-- Data for Name: fact_order_detail; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_order_detail (store_id, order_id, detail_id, detail_type, item_id, item_name, quantity, unit_price, amount, original_amount, discount, start_time, end_time, duration_seconds, assistant_id, assistant_level, table_id, table_area, product_id, salesman_id, salesman_name, notes) FROM stdin; +\. + + +-- +-- TOC entry 5588 (class 0 OID 21134) +-- Dependencies: 242 +-- Data for Name: fact_package_usage; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_package_usage (store_id, usage_id, package_id, order_id, member_id, redeem_time) FROM stdin; +\. + + +-- +-- TOC entry 5589 (class 0 OID 21139) +-- Dependencies: 243 +-- Data for Name: fact_payment; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_payment (store_id, pay_id, order_id, pay_trade_no, pay_amount, pay_method, pay_time, pay_status, operator_id, create_time) FROM stdin; +\. + + +-- +-- TOC entry 5590 (class 0 OID 21145) +-- Dependencies: 244 +-- Data for Name: fact_refund; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_refund (store_id, refund_id, order_id, pay_id, refund_amount, refund_time, refund_method, operator_id, reason) FROM stdin; +\. + + +-- +-- TOC entry 5591 (class 0 OID 21151) +-- Dependencies: 245 +-- Data for Name: fact_table_discount; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_table_discount (store_id, discount_id, order_id, table_id, applicant_id, applicant_name, operator_id, operator_name, adjust_type, ledger_count, ledger_amount, create_time, reason) FROM stdin; +\. + + +-- +-- TOC entry 5592 (class 0 OID 21158) +-- Dependencies: 246 +-- Data for Name: fact_topup; Type: TABLE DATA; Schema: billiards; Owner: postgres +-- + +COPY billiards.fact_topup (store_id, topup_id, member_id, order_id, amount_recharge, amount_bonus, pay_method, pay_time, operator_id, remark) FROM stdin; +\. + + +-- +-- TOC entry 5593 (class 0 OID 21164) +-- Dependencies: 247 +-- Data for Name: etl_cursor; Type: TABLE DATA; Schema: etl_admin; Owner: postgres +-- + +COPY etl_admin.etl_cursor (cursor_id, task_id, store_id, last_start, last_end, last_id, extra, last_run_id, updated_at, lock_token, lock_at) FROM stdin; +1 29 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +2 44 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +3 45 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +4 46 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +5 47 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +6 48 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +7 49 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +8 50 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +9 51 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +10 52 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +11 53 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +12 54 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +13 55 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +14 56 2790685415443269 \N \N \N {} \N 2025-11-11 10:09:34.540566+08 \N \N +\. + + +-- +-- TOC entry 5595 (class 0 OID 21178) +-- Dependencies: 249 +-- Data for Name: etl_run; Type: TABLE DATA; Schema: etl_admin; Owner: postgres +-- + +COPY etl_admin.etl_run (run_id, run_uuid, task_id, store_id, status, started_at, ended_at, window_start, window_end, window_minutes, overlap_seconds, fetched_count, loaded_count, updated_count, skipped_count, error_count, unknown_fields, export_dir, log_path, request_params, manifest, error_message, extra, created_at) FROM stdin; +\. + + +-- +-- TOC entry 5597 (class 0 OID 21210) +-- Dependencies: 251 +-- Data for Name: etl_task; Type: TABLE DATA; Schema: etl_admin; Owner: postgres +-- + +COPY etl_admin.etl_task (task_id, task_code, store_id, enabled, cursor_field, cursor_field_fallback, window_minutes_default, overlap_seconds, page_size, retry_max, params, description, created_at, updated_at) FROM stdin; +44 ORDERS 2790685415443269 t payTime closeTime 180 180 200 3 {"endpoint_list": "/order/settle/list", "endpoint_detail": "/order/settle/detail"} 订单/结账与明细(台费/助教/商品) 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +45 PAYMENTS 2790685415443269 t payTime createTime 180 180 200 3 {"endpoint": "/pay/records"} 支付流水 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +46 REFUNDS 2790685415443269 t refundTime payTime 180 180 200 3 {"endpoint": "/refund/records"} 退款流水 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +47 INVENTORY_CHANGE 2790685415443269 t createTime \N 180 180 200 3 {"endpoint": "/inventory/change", "list_key": "queryDeliveryRecordsList"} 库存变动流水(进/销/退/盘点) 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +48 COUPON_USAGE 2790685415443269 t consumeTime redeemTime 180 180 200 3 {"endpoint": "/coupon/usage"} 套餐/团购核销流水 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +49 MEMBERS 2790685415443269 t updateTime registerTime 180 180 500 3 {"endpoint": "/MemberProfile/GetTenantMemberList"} 会员档案(含等级等属性,SCD2) 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +50 ASSISTANTS 2790685415443269 t updateTime entryTime 180 180 500 3 {"endpoint": "/PersonnelManagement/SearchAssistantInfo"} 助教档案(等级/团队等属性,SCD2) 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +51 PRODUCTS 2790685415443269 t updateTime createTime 180 180 500 3 {"endpoint": "/TenantGoods/QueryTenantGoods"} 商品档案(含分类/价格属性,SCD2) 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +52 TABLES 2790685415443269 t lastUseTime createTime 180 180 200 3 {"endpoint": "/Table/GetSiteTables"} 球台档案(名称/区域/状态) 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +53 PACKAGES_DEF 2790685415443269 t updateTime validFrom 180 180 200 3 {"endpoint": "/PackageCoupon/QueryPackageCouponList"} 套餐/团购定义 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +54 TOPUPS 2790685415443269 t payTime createTime 180 180 200 3 {"endpoint": "/Site/GetRechargeSettleList"} 会员充值记录(含赠送) 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +55 TABLE_DISCOUNT 2790685415443269 t createTime \N 180 180 200 3 {"endpoint": "/Site/GetTaiFeeAdjustList"} 台费打折/减免记录 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +56 ASSISTANT_ABOLISH 2790685415443269 t createTime endTime 180 180 200 3 {"endpoint": "/AssistantPerformance/GetAbolitionAssistant"} 助教服务作废/取消记录 2025-11-11 10:09:34.540566+08 2025-11-11 10:09:34.540566+08 +29 LEDGER 2790685415443269 t \N \N 180 0 1 0 {} 由事实表派生的双分录总账 2025-11-11 09:53:06.423242+08 2025-11-11 10:09:34.540566+08 +\. + + +-- +-- TOC entry 6031 (class 0 OID 0) +-- Dependencies: 225 +-- Name: dim_assistant_scd_assistant_scd_id_seq; Type: SEQUENCE SET; Schema: billiards; Owner: postgres +-- + +SELECT pg_catalog.setval('billiards.dim_assistant_scd_assistant_scd_id_seq', 1, false); + + +-- +-- TOC entry 6032 (class 0 OID 0) +-- Dependencies: 229 +-- Name: dim_member_scd_member_scd_id_seq; Type: SEQUENCE SET; Schema: billiards; Owner: postgres +-- + +SELECT pg_catalog.setval('billiards.dim_member_scd_member_scd_id_seq', 1, false); + + +-- +-- TOC entry 6033 (class 0 OID 0) +-- Dependencies: 234 +-- Name: dim_product_price_scd_product_scd_id_seq; Type: SEQUENCE SET; Schema: billiards; Owner: postgres +-- + +SELECT pg_catalog.setval('billiards.dim_product_price_scd_product_scd_id_seq', 1, false); + + +-- +-- TOC entry 6034 (class 0 OID 0) +-- Dependencies: 248 +-- Name: etl_cursor_cursor_id_seq; Type: SEQUENCE SET; Schema: etl_admin; Owner: postgres +-- + +SELECT pg_catalog.setval('etl_admin.etl_cursor_cursor_id_seq', 15, true); + + +-- +-- TOC entry 6035 (class 0 OID 0) +-- Dependencies: 250 +-- Name: etl_run_run_id_seq; Type: SEQUENCE SET; Schema: etl_admin; Owner: postgres +-- + +SELECT pg_catalog.setval('etl_admin.etl_run_run_id_seq', 1, false); + + +-- +-- TOC entry 6036 (class 0 OID 0) +-- Dependencies: 252 +-- Name: etl_task_task_id_seq; Type: SEQUENCE SET; Schema: etl_admin; Owner: postgres +-- + +SELECT pg_catalog.setval('etl_admin.etl_task_task_id_seq', 57, true); + + +-- +-- TOC entry 5299 (class 2606 OID 21388) +-- Name: dim_assistant dim_assistant_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_assistant + ADD CONSTRAINT dim_assistant_pkey PRIMARY KEY (store_id, assistant_id); + + +-- +-- TOC entry 5301 (class 2606 OID 21390) +-- Name: dim_assistant_scd dim_assistant_scd_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_assistant_scd + ADD CONSTRAINT dim_assistant_scd_pkey PRIMARY KEY (assistant_scd_id); + + +-- +-- TOC entry 5306 (class 2606 OID 21392) +-- Name: dim_member dim_member_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_member + ADD CONSTRAINT dim_member_pkey PRIMARY KEY (store_id, member_id); + + +-- +-- TOC entry 5309 (class 2606 OID 21394) +-- Name: dim_member_scd dim_member_scd_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_member_scd + ADD CONSTRAINT dim_member_scd_pkey PRIMARY KEY (member_scd_id); + + +-- +-- TOC entry 5314 (class 2606 OID 21396) +-- Name: dim_package dim_package_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_package + ADD CONSTRAINT dim_package_pkey PRIMARY KEY (store_id, package_id); + + +-- +-- TOC entry 5316 (class 2606 OID 21398) +-- Name: dim_product dim_product_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_product + ADD CONSTRAINT dim_product_pkey PRIMARY KEY (store_id, product_id); + + +-- +-- TOC entry 5319 (class 2606 OID 21400) +-- Name: dim_product_price_scd dim_product_price_scd_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_product_price_scd + ADD CONSTRAINT dim_product_price_scd_pkey PRIMARY KEY (product_scd_id); + + +-- +-- TOC entry 5324 (class 2606 OID 21402) +-- Name: dim_store dim_store_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_store + ADD CONSTRAINT dim_store_pkey PRIMARY KEY (store_id); + + +-- +-- TOC entry 5326 (class 2606 OID 21404) +-- Name: dim_table dim_table_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_table + ADD CONSTRAINT dim_table_pkey PRIMARY KEY (store_id, table_id); + + +-- +-- TOC entry 5303 (class 2606 OID 21406) +-- Name: dim_assistant_scd ex_assistant_scd_no_overlap; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_assistant_scd + ADD CONSTRAINT ex_assistant_scd_no_overlap EXCLUDE USING gist (store_id WITH =, assistant_id WITH =, tstzrange(valid_from, COALESCE(valid_to, 'infinity'::timestamp with time zone), '[)'::text) WITH &&) DEFERRABLE; + + +-- +-- TOC entry 5311 (class 2606 OID 21409) +-- Name: dim_member_scd ex_member_scd_no_overlap; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_member_scd + ADD CONSTRAINT ex_member_scd_no_overlap EXCLUDE USING gist (store_id WITH =, member_id WITH =, tstzrange(valid_from, COALESCE(valid_to, 'infinity'::timestamp with time zone), '[)'::text) WITH &&) DEFERRABLE; + + +-- +-- TOC entry 5321 (class 2606 OID 21412) +-- Name: dim_product_price_scd ex_product_scd_no_overlap; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_product_price_scd + ADD CONSTRAINT ex_product_scd_no_overlap EXCLUDE USING gist (store_id WITH =, product_id WITH =, tstzrange(valid_from, COALESCE(valid_to, 'infinity'::timestamp with time zone), '[)'::text) WITH &&) DEFERRABLE; + + +-- +-- TOC entry 5328 (class 2606 OID 21415) +-- Name: fact_assistant_abolish fact_assistant_abolish_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_assistant_abolish + ADD CONSTRAINT fact_assistant_abolish_pkey PRIMARY KEY (store_id, abolish_id); + + +-- +-- TOC entry 5331 (class 2606 OID 21417) +-- Name: fact_inventory_change fact_inventory_change_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_inventory_change + ADD CONSTRAINT fact_inventory_change_pkey PRIMARY KEY (store_id, change_id); + + +-- +-- TOC entry 5335 (class 2606 OID 21419) +-- Name: fact_ledger_entry fact_ledger_entry_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_ledger_entry + ADD CONSTRAINT fact_ledger_entry_pkey PRIMARY KEY (store_id, entry_id); + + +-- +-- TOC entry 5342 (class 2606 OID 21421) +-- Name: fact_order_detail fact_order_detail_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_order_detail + ADD CONSTRAINT fact_order_detail_pkey PRIMARY KEY (store_id, order_id, detail_id); + + +-- +-- TOC entry 5337 (class 2606 OID 21423) +-- Name: fact_order fact_order_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_order + ADD CONSTRAINT fact_order_pkey PRIMARY KEY (store_id, order_id); + + +-- +-- TOC entry 5348 (class 2606 OID 21425) +-- Name: fact_package_usage fact_package_usage_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_package_usage + ADD CONSTRAINT fact_package_usage_pkey PRIMARY KEY (store_id, usage_id); + + +-- +-- TOC entry 5351 (class 2606 OID 21427) +-- Name: fact_payment fact_payment_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_payment + ADD CONSTRAINT fact_payment_pkey PRIMARY KEY (store_id, pay_id); + + +-- +-- TOC entry 5355 (class 2606 OID 21429) +-- Name: fact_refund fact_refund_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_refund + ADD CONSTRAINT fact_refund_pkey PRIMARY KEY (store_id, refund_id); + + +-- +-- TOC entry 5359 (class 2606 OID 21431) +-- Name: fact_table_discount fact_table_discount_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_table_discount + ADD CONSTRAINT fact_table_discount_pkey PRIMARY KEY (store_id, discount_id); + + +-- +-- TOC entry 5362 (class 2606 OID 21433) +-- Name: fact_topup fact_topup_pkey; Type: CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_topup + ADD CONSTRAINT fact_topup_pkey PRIMARY KEY (store_id, topup_id); + + +-- +-- TOC entry 5365 (class 2606 OID 21435) +-- Name: etl_cursor etl_cursor_pkey; Type: CONSTRAINT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_cursor + ADD CONSTRAINT etl_cursor_pkey PRIMARY KEY (cursor_id); + + +-- +-- TOC entry 5371 (class 2606 OID 21437) +-- Name: etl_run etl_run_pkey; Type: CONSTRAINT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_run + ADD CONSTRAINT etl_run_pkey PRIMARY KEY (run_id); + + +-- +-- TOC entry 5377 (class 2606 OID 21439) +-- Name: etl_task etl_task_pkey; Type: CONSTRAINT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_task + ADD CONSTRAINT etl_task_pkey PRIMARY KEY (task_id); + + +-- +-- TOC entry 5369 (class 2606 OID 21441) +-- Name: etl_cursor ux_cursor_task; Type: CONSTRAINT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_cursor + ADD CONSTRAINT ux_cursor_task UNIQUE (task_id, store_id); + + +-- +-- TOC entry 5380 (class 2606 OID 21443) +-- Name: etl_task ux_task_store; Type: CONSTRAINT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_task + ADD CONSTRAINT ux_task_store UNIQUE (task_code, store_id); + + +-- +-- TOC entry 5329 (class 1259 OID 21482) +-- Name: ix_faa_order; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_faa_order ON billiards.fact_assistant_abolish USING btree (store_id, order_id); + + +-- +-- TOC entry 5332 (class 1259 OID 21483) +-- Name: ix_fic_change_time; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_fic_change_time ON billiards.fact_inventory_change USING btree (store_id, change_time); + + +-- +-- TOC entry 5343 (class 1259 OID 21484) +-- Name: ix_fod_assistant; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_fod_assistant ON billiards.fact_order_detail USING btree (store_id, assistant_id); + + +-- +-- TOC entry 5344 (class 1259 OID 21485) +-- Name: ix_fod_order; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_fod_order ON billiards.fact_order_detail USING btree (store_id, order_id); + + +-- +-- TOC entry 5345 (class 1259 OID 21486) +-- Name: ix_fod_product; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_fod_product ON billiards.fact_order_detail USING btree (store_id, product_id); + + +-- +-- TOC entry 5346 (class 1259 OID 21487) +-- Name: ix_fod_table; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_fod_table ON billiards.fact_order_detail USING btree (store_id, table_id); + + +-- +-- TOC entry 5349 (class 1259 OID 21488) +-- Name: ix_fpu_member; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_fpu_member ON billiards.fact_package_usage USING btree (store_id, member_id); + + +-- +-- TOC entry 5360 (class 1259 OID 21489) +-- Name: ix_ftd_order; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_ftd_order ON billiards.fact_table_discount USING btree (store_id, order_id); + + +-- +-- TOC entry 5333 (class 1259 OID 21490) +-- Name: ix_invchg_product; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_invchg_product ON billiards.fact_inventory_change USING btree (store_id, product_id); + + +-- +-- TOC entry 5307 (class 1259 OID 21491) +-- Name: ix_member_mobile; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_member_mobile ON billiards.dim_member USING btree (store_id, mobile); + + +-- +-- TOC entry 5338 (class 1259 OID 21492) +-- Name: ix_order_close_ts; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_order_close_ts ON billiards.fact_order USING btree (store_id, close_time DESC); + + +-- +-- TOC entry 5339 (class 1259 OID 21493) +-- Name: ix_order_member; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_order_member ON billiards.fact_order USING btree (store_id, member_id); + + +-- +-- TOC entry 5352 (class 1259 OID 21494) +-- Name: ix_pay_order; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_pay_order ON billiards.fact_payment USING btree (store_id, order_id); + + +-- +-- TOC entry 5317 (class 1259 OID 21495) +-- Name: ix_product_barcode; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_product_barcode ON billiards.dim_product USING btree (store_id, barcode); + + +-- +-- TOC entry 5356 (class 1259 OID 21496) +-- Name: ix_refund_order; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_refund_order ON billiards.fact_refund USING btree (store_id, order_id); + + +-- +-- TOC entry 5357 (class 1259 OID 21497) +-- Name: ix_refund_pay; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_refund_pay ON billiards.fact_refund USING btree (store_id, pay_id); + + +-- +-- TOC entry 5363 (class 1259 OID 21498) +-- Name: ix_topup_member; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE INDEX ix_topup_member ON billiards.fact_topup USING btree (store_id, member_id); + + +-- +-- TOC entry 5304 (class 1259 OID 21499) +-- Name: ux_assistant_scd_current; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE UNIQUE INDEX ux_assistant_scd_current ON billiards.dim_assistant_scd USING btree (store_id, assistant_id) WHERE (is_current = true); + + +-- +-- TOC entry 5312 (class 1259 OID 21500) +-- Name: ux_member_scd_current; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE UNIQUE INDEX ux_member_scd_current ON billiards.dim_member_scd USING btree (store_id, member_id) WHERE (is_current = true); + + +-- +-- TOC entry 5340 (class 1259 OID 21501) +-- Name: ux_order_no; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE UNIQUE INDEX ux_order_no ON billiards.fact_order USING btree (store_id, order_no) WHERE (order_no IS NOT NULL); + + +-- +-- TOC entry 5353 (class 1259 OID 21502) +-- Name: ux_pay_trade_no_norm; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE UNIQUE INDEX ux_pay_trade_no_norm ON billiards.fact_payment USING btree (store_id, lower(regexp_replace((pay_trade_no)::text, '\s+'::text, ''::text, 'g'::text))) WHERE (pay_trade_no IS NOT NULL); + + +-- +-- TOC entry 5322 (class 1259 OID 21503) +-- Name: ux_product_scd_current; Type: INDEX; Schema: billiards; Owner: postgres +-- + +CREATE UNIQUE INDEX ux_product_scd_current ON billiards.dim_product_price_scd USING btree (store_id, product_id) WHERE (is_current = true); + + +-- +-- TOC entry 5366 (class 1259 OID 21504) +-- Name: ix_cursor_store; Type: INDEX; Schema: etl_admin; Owner: postgres +-- + +CREATE INDEX ix_cursor_store ON etl_admin.etl_cursor USING btree (store_id); + + +-- +-- TOC entry 5367 (class 1259 OID 21505) +-- Name: ix_cursor_updated; Type: INDEX; Schema: etl_admin; Owner: postgres +-- + +CREATE INDEX ix_cursor_updated ON etl_admin.etl_cursor USING btree (updated_at DESC); + + +-- +-- TOC entry 5372 (class 1259 OID 21506) +-- Name: ix_run_status_time; Type: INDEX; Schema: etl_admin; Owner: postgres +-- + +CREATE INDEX ix_run_status_time ON etl_admin.etl_run USING btree (status, started_at DESC); + + +-- +-- TOC entry 5373 (class 1259 OID 21507) +-- Name: ix_run_store_time; Type: INDEX; Schema: etl_admin; Owner: postgres +-- + +CREATE INDEX ix_run_store_time ON etl_admin.etl_run USING btree (store_id, started_at DESC); + + +-- +-- TOC entry 5374 (class 1259 OID 21508) +-- Name: ix_run_task_time; Type: INDEX; Schema: etl_admin; Owner: postgres +-- + +CREATE INDEX ix_run_task_time ON etl_admin.etl_run USING btree (task_id, started_at DESC); + + +-- +-- TOC entry 5375 (class 1259 OID 21509) +-- Name: ix_run_window_end; Type: INDEX; Schema: etl_admin; Owner: postgres +-- + +CREATE INDEX ix_run_window_end ON etl_admin.etl_run USING btree (window_end DESC); + + +-- +-- TOC entry 5378 (class 1259 OID 21510) +-- Name: ix_task_store_enabled; Type: INDEX; Schema: etl_admin; Owner: postgres +-- + +CREATE INDEX ix_task_store_enabled ON etl_admin.etl_task USING btree (store_id, enabled); + + +-- +-- TOC entry 5381 (class 2606 OID 21511) +-- Name: dim_assistant_scd fk_das_assistant; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_assistant_scd + ADD CONSTRAINT fk_das_assistant FOREIGN KEY (store_id, assistant_id) REFERENCES billiards.dim_assistant(store_id, assistant_id); + + +-- +-- TOC entry 5382 (class 2606 OID 21516) +-- Name: dim_assistant_scd fk_das_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_assistant_scd + ADD CONSTRAINT fk_das_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id); + + +-- +-- TOC entry 5383 (class 2606 OID 21526) +-- Name: dim_member fk_dim_member_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_member + ADD CONSTRAINT fk_dim_member_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5386 (class 2606 OID 21531) +-- Name: dim_package fk_dim_package_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_package + ADD CONSTRAINT fk_dim_package_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5387 (class 2606 OID 21536) +-- Name: dim_product fk_dim_product_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_product + ADD CONSTRAINT fk_dim_product_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5390 (class 2606 OID 21541) +-- Name: dim_table fk_dim_table_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_table + ADD CONSTRAINT fk_dim_table_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5384 (class 2606 OID 21546) +-- Name: dim_member_scd fk_dms_member; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_member_scd + ADD CONSTRAINT fk_dms_member FOREIGN KEY (store_id, member_id) REFERENCES billiards.dim_member(store_id, member_id); + + +-- +-- TOC entry 5385 (class 2606 OID 21551) +-- Name: dim_member_scd fk_dms_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_member_scd + ADD CONSTRAINT fk_dms_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id); + + +-- +-- TOC entry 5388 (class 2606 OID 21556) +-- Name: dim_product_price_scd fk_dpps_product; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_product_price_scd + ADD CONSTRAINT fk_dpps_product FOREIGN KEY (store_id, product_id) REFERENCES billiards.dim_product(store_id, product_id); + + +-- +-- TOC entry 5389 (class 2606 OID 21561) +-- Name: dim_product_price_scd fk_dpps_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.dim_product_price_scd + ADD CONSTRAINT fk_dpps_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id); + + +-- +-- TOC entry 5391 (class 2606 OID 21566) +-- Name: fact_assistant_abolish fk_faa_assistant; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_assistant_abolish + ADD CONSTRAINT fk_faa_assistant FOREIGN KEY (store_id, assistant_id) REFERENCES billiards.dim_assistant(store_id, assistant_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5392 (class 2606 OID 21571) +-- Name: fact_assistant_abolish fk_faa_order; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_assistant_abolish + ADD CONSTRAINT fk_faa_order FOREIGN KEY (store_id, order_id) REFERENCES billiards.fact_order(store_id, order_id) ON DELETE CASCADE; + + +-- +-- TOC entry 5393 (class 2606 OID 21576) +-- Name: fact_assistant_abolish fk_faa_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_assistant_abolish + ADD CONSTRAINT fk_faa_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5394 (class 2606 OID 21581) +-- Name: fact_assistant_abolish fk_faa_table; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_assistant_abolish + ADD CONSTRAINT fk_faa_table FOREIGN KEY (store_id, table_id) REFERENCES billiards.dim_table(store_id, table_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5398 (class 2606 OID 21586) +-- Name: fact_order fk_fact_order_member; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_order + ADD CONSTRAINT fk_fact_order_member FOREIGN KEY (store_id, member_id) REFERENCES billiards.dim_member(store_id, member_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5399 (class 2606 OID 21591) +-- Name: fact_order fk_fact_order_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_order + ADD CONSTRAINT fk_fact_order_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5395 (class 2606 OID 21596) +-- Name: fact_inventory_change fk_fic_product; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_inventory_change + ADD CONSTRAINT fk_fic_product FOREIGN KEY (store_id, product_id) REFERENCES billiards.dim_product(store_id, product_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5396 (class 2606 OID 21601) +-- Name: fact_inventory_change fk_fic_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_inventory_change + ADD CONSTRAINT fk_fic_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5397 (class 2606 OID 21606) +-- Name: fact_ledger_entry fk_fle_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_ledger_entry + ADD CONSTRAINT fk_fle_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5400 (class 2606 OID 21611) +-- Name: fact_order_detail fk_fod_assistant; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_order_detail + ADD CONSTRAINT fk_fod_assistant FOREIGN KEY (store_id, assistant_id) REFERENCES billiards.dim_assistant(store_id, assistant_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5401 (class 2606 OID 21616) +-- Name: fact_order_detail fk_fod_order; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_order_detail + ADD CONSTRAINT fk_fod_order FOREIGN KEY (store_id, order_id) REFERENCES billiards.fact_order(store_id, order_id) ON DELETE CASCADE; + + +-- +-- TOC entry 5402 (class 2606 OID 21621) +-- Name: fact_order_detail fk_fod_product; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_order_detail + ADD CONSTRAINT fk_fod_product FOREIGN KEY (store_id, product_id) REFERENCES billiards.dim_product(store_id, product_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5403 (class 2606 OID 21626) +-- Name: fact_order_detail fk_fod_table; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_order_detail + ADD CONSTRAINT fk_fod_table FOREIGN KEY (store_id, table_id) REFERENCES billiards.dim_table(store_id, table_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5408 (class 2606 OID 21631) +-- Name: fact_payment fk_fp_order; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_payment + ADD CONSTRAINT fk_fp_order FOREIGN KEY (store_id, order_id) REFERENCES billiards.fact_order(store_id, order_id) ON DELETE CASCADE; + + +-- +-- TOC entry 5409 (class 2606 OID 21636) +-- Name: fact_payment fk_fp_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_payment + ADD CONSTRAINT fk_fp_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5404 (class 2606 OID 21641) +-- Name: fact_package_usage fk_fpu_member; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_package_usage + ADD CONSTRAINT fk_fpu_member FOREIGN KEY (store_id, member_id) REFERENCES billiards.dim_member(store_id, member_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5405 (class 2606 OID 21646) +-- Name: fact_package_usage fk_fpu_order; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_package_usage + ADD CONSTRAINT fk_fpu_order FOREIGN KEY (store_id, order_id) REFERENCES billiards.fact_order(store_id, order_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5406 (class 2606 OID 21651) +-- Name: fact_package_usage fk_fpu_package; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_package_usage + ADD CONSTRAINT fk_fpu_package FOREIGN KEY (store_id, package_id) REFERENCES billiards.dim_package(store_id, package_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5407 (class 2606 OID 21656) +-- Name: fact_package_usage fk_fpu_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_package_usage + ADD CONSTRAINT fk_fpu_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5410 (class 2606 OID 21661) +-- Name: fact_refund fk_fr_order; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_refund + ADD CONSTRAINT fk_fr_order FOREIGN KEY (store_id, order_id) REFERENCES billiards.fact_order(store_id, order_id) ON DELETE CASCADE; + + +-- +-- TOC entry 5411 (class 2606 OID 21666) +-- Name: fact_refund fk_fr_pay; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_refund + ADD CONSTRAINT fk_fr_pay FOREIGN KEY (store_id, pay_id) REFERENCES billiards.fact_payment(store_id, pay_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5412 (class 2606 OID 21671) +-- Name: fact_refund fk_fr_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_refund + ADD CONSTRAINT fk_fr_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5416 (class 2606 OID 21676) +-- Name: fact_topup fk_ft_member; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_topup + ADD CONSTRAINT fk_ft_member FOREIGN KEY (store_id, member_id) REFERENCES billiards.dim_member(store_id, member_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5417 (class 2606 OID 21681) +-- Name: fact_topup fk_ft_order; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_topup + ADD CONSTRAINT fk_ft_order FOREIGN KEY (store_id, order_id) REFERENCES billiards.fact_order(store_id, order_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5418 (class 2606 OID 21686) +-- Name: fact_topup fk_ft_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_topup + ADD CONSTRAINT fk_ft_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5413 (class 2606 OID 21691) +-- Name: fact_table_discount fk_ftd_order; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_table_discount + ADD CONSTRAINT fk_ftd_order FOREIGN KEY (store_id, order_id) REFERENCES billiards.fact_order(store_id, order_id) ON DELETE CASCADE; + + +-- +-- TOC entry 5414 (class 2606 OID 21696) +-- Name: fact_table_discount fk_ftd_store; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_table_discount + ADD CONSTRAINT fk_ftd_store FOREIGN KEY (store_id) REFERENCES billiards.dim_store(store_id) ON DELETE RESTRICT; + + +-- +-- TOC entry 5415 (class 2606 OID 21701) +-- Name: fact_table_discount fk_ftd_table; Type: FK CONSTRAINT; Schema: billiards; Owner: postgres +-- + +ALTER TABLE ONLY billiards.fact_table_discount + ADD CONSTRAINT fk_ftd_table FOREIGN KEY (store_id, table_id) REFERENCES billiards.dim_table(store_id, table_id) ON DELETE SET NULL; + + +-- +-- TOC entry 5419 (class 2606 OID 21706) +-- Name: etl_cursor etl_cursor_task_id_fkey; Type: FK CONSTRAINT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_cursor + ADD CONSTRAINT etl_cursor_task_id_fkey FOREIGN KEY (task_id) REFERENCES etl_admin.etl_task(task_id) ON DELETE CASCADE; + + +-- +-- TOC entry 5420 (class 2606 OID 21711) +-- Name: etl_run etl_run_task_id_fkey; Type: FK CONSTRAINT; Schema: etl_admin; Owner: postgres +-- + +ALTER TABLE ONLY etl_admin.etl_run + ADD CONSTRAINT etl_run_task_id_fkey FOREIGN KEY (task_id) REFERENCES etl_admin.etl_task(task_id) ON DELETE CASCADE; + + +-- +-- TOC entry 5604 (class 0 OID 0) +-- Dependencies: 8 +-- Name: SCHEMA "XCX"; Type: ACL; Schema: -; Owner: postgres +-- + +GRANT ALL ON SCHEMA "XCX" TO "local-Python" WITH GRANT OPTION; + + +-- +-- TOC entry 5607 (class 0 OID 0) +-- Dependencies: 395 +-- Name: FUNCTION gbtreekey16_in(cstring); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey16_in(cstring) TO "local-Python"; + + +-- +-- TOC entry 5608 (class 0 OID 0) +-- Dependencies: 391 +-- Name: FUNCTION gbtreekey16_out(billiards.gbtreekey16); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey16_out(billiards.gbtreekey16) TO "local-Python"; + + +-- +-- TOC entry 5609 (class 0 OID 0) +-- Dependencies: 380 +-- Name: FUNCTION gbtreekey2_in(cstring); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey2_in(cstring) TO "local-Python"; + + +-- +-- TOC entry 5610 (class 0 OID 0) +-- Dependencies: 308 +-- Name: FUNCTION gbtreekey2_out(billiards.gbtreekey2); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey2_out(billiards.gbtreekey2) TO "local-Python"; + + +-- +-- TOC entry 5611 (class 0 OID 0) +-- Dependencies: 309 +-- Name: FUNCTION gbtreekey32_in(cstring); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey32_in(cstring) TO "local-Python"; + + +-- +-- TOC entry 5612 (class 0 OID 0) +-- Dependencies: 416 +-- Name: FUNCTION gbtreekey32_out(billiards.gbtreekey32); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey32_out(billiards.gbtreekey32) TO "local-Python"; + + +-- +-- TOC entry 5613 (class 0 OID 0) +-- Dependencies: 342 +-- Name: FUNCTION gbtreekey4_in(cstring); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey4_in(cstring) TO "local-Python"; + + +-- +-- TOC entry 5614 (class 0 OID 0) +-- Dependencies: 455 +-- Name: FUNCTION gbtreekey4_out(billiards.gbtreekey4); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey4_out(billiards.gbtreekey4) TO "local-Python"; + + +-- +-- TOC entry 5615 (class 0 OID 0) +-- Dependencies: 464 +-- Name: FUNCTION gbtreekey8_in(cstring); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey8_in(cstring) TO "local-Python"; + + +-- +-- TOC entry 5616 (class 0 OID 0) +-- Dependencies: 382 +-- Name: FUNCTION gbtreekey8_out(billiards.gbtreekey8); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey8_out(billiards.gbtreekey8) TO "local-Python"; + + +-- +-- TOC entry 5617 (class 0 OID 0) +-- Dependencies: 262 +-- Name: FUNCTION gbtreekey_var_in(cstring); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey_var_in(cstring) TO "local-Python"; + + +-- +-- TOC entry 5618 (class 0 OID 0) +-- Dependencies: 407 +-- Name: FUNCTION gbtreekey_var_out(billiards.gbtreekey_var); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbtreekey_var_out(billiards.gbtreekey_var) TO "local-Python"; + + +-- +-- TOC entry 5619 (class 0 OID 0) +-- Dependencies: 450 +-- Name: FUNCTION cash_dist(money, money); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.cash_dist(money, money) TO "local-Python"; + + +-- +-- TOC entry 5620 (class 0 OID 0) +-- Dependencies: 266 +-- Name: FUNCTION date_dist(date, date); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.date_dist(date, date) TO "local-Python"; + + +-- +-- TOC entry 5621 (class 0 OID 0) +-- Dependencies: 304 +-- Name: FUNCTION float4_dist(real, real); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.float4_dist(real, real) TO "local-Python"; + + +-- +-- TOC entry 5622 (class 0 OID 0) +-- Dependencies: 412 +-- Name: FUNCTION float8_dist(double precision, double precision); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.float8_dist(double precision, double precision) TO "local-Python"; + + +-- +-- TOC entry 5623 (class 0 OID 0) +-- Dependencies: 424 +-- Name: FUNCTION gbt_bit_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bit_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5624 (class 0 OID 0) +-- Dependencies: 457 +-- Name: FUNCTION gbt_bit_consistent(internal, bit, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bit_consistent(internal, bit, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5625 (class 0 OID 0) +-- Dependencies: 435 +-- Name: FUNCTION gbt_bit_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bit_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5626 (class 0 OID 0) +-- Dependencies: 273 +-- Name: FUNCTION gbt_bit_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bit_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5627 (class 0 OID 0) +-- Dependencies: 475 +-- Name: FUNCTION gbt_bit_same(billiards.gbtreekey_var, billiards.gbtreekey_var, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bit_same(billiards.gbtreekey_var, billiards.gbtreekey_var, internal) TO "local-Python"; + + +-- +-- TOC entry 5628 (class 0 OID 0) +-- Dependencies: 288 +-- Name: FUNCTION gbt_bit_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bit_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5629 (class 0 OID 0) +-- Dependencies: 448 +-- Name: FUNCTION gbt_bit_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bit_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5630 (class 0 OID 0) +-- Dependencies: 397 +-- Name: FUNCTION gbt_bool_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bool_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5631 (class 0 OID 0) +-- Dependencies: 422 +-- Name: FUNCTION gbt_bool_consistent(internal, boolean, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bool_consistent(internal, boolean, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5632 (class 0 OID 0) +-- Dependencies: 256 +-- Name: FUNCTION gbt_bool_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bool_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5633 (class 0 OID 0) +-- Dependencies: 319 +-- Name: FUNCTION gbt_bool_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bool_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5634 (class 0 OID 0) +-- Dependencies: 411 +-- Name: FUNCTION gbt_bool_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bool_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5635 (class 0 OID 0) +-- Dependencies: 453 +-- Name: FUNCTION gbt_bool_same(billiards.gbtreekey2, billiards.gbtreekey2, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bool_same(billiards.gbtreekey2, billiards.gbtreekey2, internal) TO "local-Python"; + + +-- +-- TOC entry 5636 (class 0 OID 0) +-- Dependencies: 290 +-- Name: FUNCTION gbt_bool_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bool_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5637 (class 0 OID 0) +-- Dependencies: 257 +-- Name: FUNCTION gbt_bool_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bool_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5638 (class 0 OID 0) +-- Dependencies: 396 +-- Name: FUNCTION gbt_bpchar_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bpchar_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5639 (class 0 OID 0) +-- Dependencies: 466 +-- Name: FUNCTION gbt_bpchar_consistent(internal, character, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bpchar_consistent(internal, character, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5640 (class 0 OID 0) +-- Dependencies: 332 +-- Name: FUNCTION gbt_bpchar_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bpchar_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5641 (class 0 OID 0) +-- Dependencies: 399 +-- Name: FUNCTION gbt_bytea_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bytea_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5642 (class 0 OID 0) +-- Dependencies: 302 +-- Name: FUNCTION gbt_bytea_consistent(internal, bytea, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bytea_consistent(internal, bytea, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5643 (class 0 OID 0) +-- Dependencies: 458 +-- Name: FUNCTION gbt_bytea_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bytea_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5644 (class 0 OID 0) +-- Dependencies: 404 +-- Name: FUNCTION gbt_bytea_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bytea_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5645 (class 0 OID 0) +-- Dependencies: 426 +-- Name: FUNCTION gbt_bytea_same(billiards.gbtreekey_var, billiards.gbtreekey_var, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bytea_same(billiards.gbtreekey_var, billiards.gbtreekey_var, internal) TO "local-Python"; + + +-- +-- TOC entry 5646 (class 0 OID 0) +-- Dependencies: 398 +-- Name: FUNCTION gbt_bytea_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bytea_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5647 (class 0 OID 0) +-- Dependencies: 313 +-- Name: FUNCTION gbt_bytea_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_bytea_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5648 (class 0 OID 0) +-- Dependencies: 421 +-- Name: FUNCTION gbt_cash_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5649 (class 0 OID 0) +-- Dependencies: 414 +-- Name: FUNCTION gbt_cash_consistent(internal, money, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_consistent(internal, money, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5650 (class 0 OID 0) +-- Dependencies: 324 +-- Name: FUNCTION gbt_cash_distance(internal, money, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_distance(internal, money, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5651 (class 0 OID 0) +-- Dependencies: 297 +-- Name: FUNCTION gbt_cash_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5652 (class 0 OID 0) +-- Dependencies: 255 +-- Name: FUNCTION gbt_cash_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5653 (class 0 OID 0) +-- Dependencies: 296 +-- Name: FUNCTION gbt_cash_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5654 (class 0 OID 0) +-- Dependencies: 442 +-- Name: FUNCTION gbt_cash_same(billiards.gbtreekey16, billiards.gbtreekey16, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_same(billiards.gbtreekey16, billiards.gbtreekey16, internal) TO "local-Python"; + + +-- +-- TOC entry 5655 (class 0 OID 0) +-- Dependencies: 293 +-- Name: FUNCTION gbt_cash_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5656 (class 0 OID 0) +-- Dependencies: 271 +-- Name: FUNCTION gbt_cash_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_cash_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5657 (class 0 OID 0) +-- Dependencies: 260 +-- Name: FUNCTION gbt_date_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5658 (class 0 OID 0) +-- Dependencies: 400 +-- Name: FUNCTION gbt_date_consistent(internal, date, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_consistent(internal, date, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5659 (class 0 OID 0) +-- Dependencies: 374 +-- Name: FUNCTION gbt_date_distance(internal, date, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_distance(internal, date, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5660 (class 0 OID 0) +-- Dependencies: 368 +-- Name: FUNCTION gbt_date_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5661 (class 0 OID 0) +-- Dependencies: 378 +-- Name: FUNCTION gbt_date_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5662 (class 0 OID 0) +-- Dependencies: 410 +-- Name: FUNCTION gbt_date_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5663 (class 0 OID 0) +-- Dependencies: 389 +-- Name: FUNCTION gbt_date_same(billiards.gbtreekey8, billiards.gbtreekey8, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_same(billiards.gbtreekey8, billiards.gbtreekey8, internal) TO "local-Python"; + + +-- +-- TOC entry 5664 (class 0 OID 0) +-- Dependencies: 330 +-- Name: FUNCTION gbt_date_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5665 (class 0 OID 0) +-- Dependencies: 409 +-- Name: FUNCTION gbt_date_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_date_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5666 (class 0 OID 0) +-- Dependencies: 387 +-- Name: FUNCTION gbt_decompress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_decompress(internal) TO "local-Python"; + + +-- +-- TOC entry 5667 (class 0 OID 0) +-- Dependencies: 467 +-- Name: FUNCTION gbt_enum_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_enum_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5668 (class 0 OID 0) +-- Dependencies: 367 +-- Name: FUNCTION gbt_enum_consistent(internal, anyenum, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_enum_consistent(internal, anyenum, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5669 (class 0 OID 0) +-- Dependencies: 340 +-- Name: FUNCTION gbt_enum_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_enum_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5670 (class 0 OID 0) +-- Dependencies: 440 +-- Name: FUNCTION gbt_enum_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_enum_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5671 (class 0 OID 0) +-- Dependencies: 451 +-- Name: FUNCTION gbt_enum_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_enum_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5672 (class 0 OID 0) +-- Dependencies: 363 +-- Name: FUNCTION gbt_enum_same(billiards.gbtreekey8, billiards.gbtreekey8, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_enum_same(billiards.gbtreekey8, billiards.gbtreekey8, internal) TO "local-Python"; + + +-- +-- TOC entry 5673 (class 0 OID 0) +-- Dependencies: 472 +-- Name: FUNCTION gbt_enum_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_enum_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5674 (class 0 OID 0) +-- Dependencies: 376 +-- Name: FUNCTION gbt_enum_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_enum_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5675 (class 0 OID 0) +-- Dependencies: 388 +-- Name: FUNCTION gbt_float4_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5676 (class 0 OID 0) +-- Dependencies: 268 +-- Name: FUNCTION gbt_float4_consistent(internal, real, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_consistent(internal, real, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5677 (class 0 OID 0) +-- Dependencies: 471 +-- Name: FUNCTION gbt_float4_distance(internal, real, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_distance(internal, real, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5678 (class 0 OID 0) +-- Dependencies: 277 +-- Name: FUNCTION gbt_float4_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5679 (class 0 OID 0) +-- Dependencies: 413 +-- Name: FUNCTION gbt_float4_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5680 (class 0 OID 0) +-- Dependencies: 364 +-- Name: FUNCTION gbt_float4_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5681 (class 0 OID 0) +-- Dependencies: 352 +-- Name: FUNCTION gbt_float4_same(billiards.gbtreekey8, billiards.gbtreekey8, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_same(billiards.gbtreekey8, billiards.gbtreekey8, internal) TO "local-Python"; + + +-- +-- TOC entry 5682 (class 0 OID 0) +-- Dependencies: 381 +-- Name: FUNCTION gbt_float4_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5683 (class 0 OID 0) +-- Dependencies: 307 +-- Name: FUNCTION gbt_float4_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float4_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5684 (class 0 OID 0) +-- Dependencies: 278 +-- Name: FUNCTION gbt_float8_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5685 (class 0 OID 0) +-- Dependencies: 284 +-- Name: FUNCTION gbt_float8_consistent(internal, double precision, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_consistent(internal, double precision, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5686 (class 0 OID 0) +-- Dependencies: 315 +-- Name: FUNCTION gbt_float8_distance(internal, double precision, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_distance(internal, double precision, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5687 (class 0 OID 0) +-- Dependencies: 334 +-- Name: FUNCTION gbt_float8_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5688 (class 0 OID 0) +-- Dependencies: 415 +-- Name: FUNCTION gbt_float8_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5689 (class 0 OID 0) +-- Dependencies: 463 +-- Name: FUNCTION gbt_float8_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5690 (class 0 OID 0) +-- Dependencies: 474 +-- Name: FUNCTION gbt_float8_same(billiards.gbtreekey16, billiards.gbtreekey16, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_same(billiards.gbtreekey16, billiards.gbtreekey16, internal) TO "local-Python"; + + +-- +-- TOC entry 5691 (class 0 OID 0) +-- Dependencies: 437 +-- Name: FUNCTION gbt_float8_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5692 (class 0 OID 0) +-- Dependencies: 465 +-- Name: FUNCTION gbt_float8_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_float8_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5693 (class 0 OID 0) +-- Dependencies: 384 +-- Name: FUNCTION gbt_inet_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_inet_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5694 (class 0 OID 0) +-- Dependencies: 357 +-- Name: FUNCTION gbt_inet_consistent(internal, inet, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_inet_consistent(internal, inet, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5695 (class 0 OID 0) +-- Dependencies: 306 +-- Name: FUNCTION gbt_inet_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_inet_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5696 (class 0 OID 0) +-- Dependencies: 417 +-- Name: FUNCTION gbt_inet_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_inet_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5697 (class 0 OID 0) +-- Dependencies: 401 +-- Name: FUNCTION gbt_inet_same(billiards.gbtreekey16, billiards.gbtreekey16, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_inet_same(billiards.gbtreekey16, billiards.gbtreekey16, internal) TO "local-Python"; + + +-- +-- TOC entry 5698 (class 0 OID 0) +-- Dependencies: 428 +-- Name: FUNCTION gbt_inet_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_inet_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5699 (class 0 OID 0) +-- Dependencies: 318 +-- Name: FUNCTION gbt_inet_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_inet_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5700 (class 0 OID 0) +-- Dependencies: 283 +-- Name: FUNCTION gbt_int2_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5701 (class 0 OID 0) +-- Dependencies: 393 +-- Name: FUNCTION gbt_int2_consistent(internal, smallint, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_consistent(internal, smallint, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5702 (class 0 OID 0) +-- Dependencies: 314 +-- Name: FUNCTION gbt_int2_distance(internal, smallint, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_distance(internal, smallint, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5703 (class 0 OID 0) +-- Dependencies: 358 +-- Name: FUNCTION gbt_int2_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5704 (class 0 OID 0) +-- Dependencies: 452 +-- Name: FUNCTION gbt_int2_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5705 (class 0 OID 0) +-- Dependencies: 405 +-- Name: FUNCTION gbt_int2_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5706 (class 0 OID 0) +-- Dependencies: 259 +-- Name: FUNCTION gbt_int2_same(billiards.gbtreekey4, billiards.gbtreekey4, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_same(billiards.gbtreekey4, billiards.gbtreekey4, internal) TO "local-Python"; + + +-- +-- TOC entry 5707 (class 0 OID 0) +-- Dependencies: 350 +-- Name: FUNCTION gbt_int2_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5708 (class 0 OID 0) +-- Dependencies: 267 +-- Name: FUNCTION gbt_int2_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int2_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5709 (class 0 OID 0) +-- Dependencies: 333 +-- Name: FUNCTION gbt_int4_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5710 (class 0 OID 0) +-- Dependencies: 253 +-- Name: FUNCTION gbt_int4_consistent(internal, integer, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_consistent(internal, integer, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5711 (class 0 OID 0) +-- Dependencies: 373 +-- Name: FUNCTION gbt_int4_distance(internal, integer, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_distance(internal, integer, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5712 (class 0 OID 0) +-- Dependencies: 359 +-- Name: FUNCTION gbt_int4_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5713 (class 0 OID 0) +-- Dependencies: 265 +-- Name: FUNCTION gbt_int4_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5714 (class 0 OID 0) +-- Dependencies: 275 +-- Name: FUNCTION gbt_int4_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5715 (class 0 OID 0) +-- Dependencies: 390 +-- Name: FUNCTION gbt_int4_same(billiards.gbtreekey8, billiards.gbtreekey8, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_same(billiards.gbtreekey8, billiards.gbtreekey8, internal) TO "local-Python"; + + +-- +-- TOC entry 5716 (class 0 OID 0) +-- Dependencies: 383 +-- Name: FUNCTION gbt_int4_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5717 (class 0 OID 0) +-- Dependencies: 473 +-- Name: FUNCTION gbt_int4_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int4_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5718 (class 0 OID 0) +-- Dependencies: 429 +-- Name: FUNCTION gbt_int8_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5719 (class 0 OID 0) +-- Dependencies: 276 +-- Name: FUNCTION gbt_int8_consistent(internal, bigint, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_consistent(internal, bigint, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5720 (class 0 OID 0) +-- Dependencies: 337 +-- Name: FUNCTION gbt_int8_distance(internal, bigint, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_distance(internal, bigint, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5721 (class 0 OID 0) +-- Dependencies: 377 +-- Name: FUNCTION gbt_int8_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5722 (class 0 OID 0) +-- Dependencies: 280 +-- Name: FUNCTION gbt_int8_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5723 (class 0 OID 0) +-- Dependencies: 325 +-- Name: FUNCTION gbt_int8_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5724 (class 0 OID 0) +-- Dependencies: 379 +-- Name: FUNCTION gbt_int8_same(billiards.gbtreekey16, billiards.gbtreekey16, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_same(billiards.gbtreekey16, billiards.gbtreekey16, internal) TO "local-Python"; + + +-- +-- TOC entry 5725 (class 0 OID 0) +-- Dependencies: 432 +-- Name: FUNCTION gbt_int8_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5726 (class 0 OID 0) +-- Dependencies: 274 +-- Name: FUNCTION gbt_int8_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_int8_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5727 (class 0 OID 0) +-- Dependencies: 289 +-- Name: FUNCTION gbt_intv_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5728 (class 0 OID 0) +-- Dependencies: 305 +-- Name: FUNCTION gbt_intv_consistent(internal, interval, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_consistent(internal, interval, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5729 (class 0 OID 0) +-- Dependencies: 320 +-- Name: FUNCTION gbt_intv_decompress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_decompress(internal) TO "local-Python"; + + +-- +-- TOC entry 5730 (class 0 OID 0) +-- Dependencies: 346 +-- Name: FUNCTION gbt_intv_distance(internal, interval, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_distance(internal, interval, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5731 (class 0 OID 0) +-- Dependencies: 341 +-- Name: FUNCTION gbt_intv_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5732 (class 0 OID 0) +-- Dependencies: 355 +-- Name: FUNCTION gbt_intv_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5733 (class 0 OID 0) +-- Dependencies: 281 +-- Name: FUNCTION gbt_intv_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5734 (class 0 OID 0) +-- Dependencies: 354 +-- Name: FUNCTION gbt_intv_same(billiards.gbtreekey32, billiards.gbtreekey32, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_same(billiards.gbtreekey32, billiards.gbtreekey32, internal) TO "local-Python"; + + +-- +-- TOC entry 5735 (class 0 OID 0) +-- Dependencies: 436 +-- Name: FUNCTION gbt_intv_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5736 (class 0 OID 0) +-- Dependencies: 298 +-- Name: FUNCTION gbt_intv_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_intv_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5737 (class 0 OID 0) +-- Dependencies: 439 +-- Name: FUNCTION gbt_macad8_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad8_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5738 (class 0 OID 0) +-- Dependencies: 339 +-- Name: FUNCTION gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad8_consistent(internal, macaddr8, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5739 (class 0 OID 0) +-- Dependencies: 434 +-- Name: FUNCTION gbt_macad8_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad8_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5740 (class 0 OID 0) +-- Dependencies: 312 +-- Name: FUNCTION gbt_macad8_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad8_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5741 (class 0 OID 0) +-- Dependencies: 294 +-- Name: FUNCTION gbt_macad8_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad8_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5742 (class 0 OID 0) +-- Dependencies: 372 +-- Name: FUNCTION gbt_macad8_same(billiards.gbtreekey16, billiards.gbtreekey16, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad8_same(billiards.gbtreekey16, billiards.gbtreekey16, internal) TO "local-Python"; + + +-- +-- TOC entry 5743 (class 0 OID 0) +-- Dependencies: 371 +-- Name: FUNCTION gbt_macad8_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad8_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5744 (class 0 OID 0) +-- Dependencies: 258 +-- Name: FUNCTION gbt_macad8_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad8_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5745 (class 0 OID 0) +-- Dependencies: 419 +-- Name: FUNCTION gbt_macad_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5746 (class 0 OID 0) +-- Dependencies: 454 +-- Name: FUNCTION gbt_macad_consistent(internal, macaddr, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad_consistent(internal, macaddr, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5747 (class 0 OID 0) +-- Dependencies: 438 +-- Name: FUNCTION gbt_macad_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5748 (class 0 OID 0) +-- Dependencies: 287 +-- Name: FUNCTION gbt_macad_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5749 (class 0 OID 0) +-- Dependencies: 406 +-- Name: FUNCTION gbt_macad_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5750 (class 0 OID 0) +-- Dependencies: 362 +-- Name: FUNCTION gbt_macad_same(billiards.gbtreekey16, billiards.gbtreekey16, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad_same(billiards.gbtreekey16, billiards.gbtreekey16, internal) TO "local-Python"; + + +-- +-- TOC entry 5751 (class 0 OID 0) +-- Dependencies: 360 +-- Name: FUNCTION gbt_macad_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macad_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5752 (class 0 OID 0) +-- Dependencies: 254 +-- Name: FUNCTION gbt_macaddr_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_macaddr_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5753 (class 0 OID 0) +-- Dependencies: 369 +-- Name: FUNCTION gbt_numeric_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_numeric_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5754 (class 0 OID 0) +-- Dependencies: 292 +-- Name: FUNCTION gbt_numeric_consistent(internal, numeric, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_numeric_consistent(internal, numeric, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5755 (class 0 OID 0) +-- Dependencies: 310 +-- Name: FUNCTION gbt_numeric_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_numeric_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5756 (class 0 OID 0) +-- Dependencies: 461 +-- Name: FUNCTION gbt_numeric_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_numeric_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5757 (class 0 OID 0) +-- Dependencies: 430 +-- Name: FUNCTION gbt_numeric_same(billiards.gbtreekey_var, billiards.gbtreekey_var, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_numeric_same(billiards.gbtreekey_var, billiards.gbtreekey_var, internal) TO "local-Python"; + + +-- +-- TOC entry 5758 (class 0 OID 0) +-- Dependencies: 433 +-- Name: FUNCTION gbt_numeric_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_numeric_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5759 (class 0 OID 0) +-- Dependencies: 431 +-- Name: FUNCTION gbt_numeric_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_numeric_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5760 (class 0 OID 0) +-- Dependencies: 323 +-- Name: FUNCTION gbt_oid_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5761 (class 0 OID 0) +-- Dependencies: 370 +-- Name: FUNCTION gbt_oid_consistent(internal, oid, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_consistent(internal, oid, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5762 (class 0 OID 0) +-- Dependencies: 459 +-- Name: FUNCTION gbt_oid_distance(internal, oid, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_distance(internal, oid, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5763 (class 0 OID 0) +-- Dependencies: 356 +-- Name: FUNCTION gbt_oid_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5764 (class 0 OID 0) +-- Dependencies: 282 +-- Name: FUNCTION gbt_oid_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5765 (class 0 OID 0) +-- Dependencies: 303 +-- Name: FUNCTION gbt_oid_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5766 (class 0 OID 0) +-- Dependencies: 449 +-- Name: FUNCTION gbt_oid_same(billiards.gbtreekey8, billiards.gbtreekey8, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_same(billiards.gbtreekey8, billiards.gbtreekey8, internal) TO "local-Python"; + + +-- +-- TOC entry 5767 (class 0 OID 0) +-- Dependencies: 347 +-- Name: FUNCTION gbt_oid_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5768 (class 0 OID 0) +-- Dependencies: 365 +-- Name: FUNCTION gbt_oid_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_oid_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5769 (class 0 OID 0) +-- Dependencies: 285 +-- Name: FUNCTION gbt_text_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_text_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5770 (class 0 OID 0) +-- Dependencies: 261 +-- Name: FUNCTION gbt_text_consistent(internal, text, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_text_consistent(internal, text, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5771 (class 0 OID 0) +-- Dependencies: 348 +-- Name: FUNCTION gbt_text_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_text_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5772 (class 0 OID 0) +-- Dependencies: 322 +-- Name: FUNCTION gbt_text_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_text_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5773 (class 0 OID 0) +-- Dependencies: 402 +-- Name: FUNCTION gbt_text_same(billiards.gbtreekey_var, billiards.gbtreekey_var, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_text_same(billiards.gbtreekey_var, billiards.gbtreekey_var, internal) TO "local-Python"; + + +-- +-- TOC entry 5774 (class 0 OID 0) +-- Dependencies: 301 +-- Name: FUNCTION gbt_text_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_text_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5775 (class 0 OID 0) +-- Dependencies: 295 +-- Name: FUNCTION gbt_text_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_text_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5776 (class 0 OID 0) +-- Dependencies: 375 +-- Name: FUNCTION gbt_time_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5777 (class 0 OID 0) +-- Dependencies: 321 +-- Name: FUNCTION gbt_time_consistent(internal, time without time zone, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_consistent(internal, time without time zone, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5778 (class 0 OID 0) +-- Dependencies: 403 +-- Name: FUNCTION gbt_time_distance(internal, time without time zone, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_distance(internal, time without time zone, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5779 (class 0 OID 0) +-- Dependencies: 423 +-- Name: FUNCTION gbt_time_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5780 (class 0 OID 0) +-- Dependencies: 326 +-- Name: FUNCTION gbt_time_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5781 (class 0 OID 0) +-- Dependencies: 446 +-- Name: FUNCTION gbt_time_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5782 (class 0 OID 0) +-- Dependencies: 299 +-- Name: FUNCTION gbt_time_same(billiards.gbtreekey16, billiards.gbtreekey16, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_same(billiards.gbtreekey16, billiards.gbtreekey16, internal) TO "local-Python"; + + +-- +-- TOC entry 5783 (class 0 OID 0) +-- Dependencies: 328 +-- Name: FUNCTION gbt_time_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5784 (class 0 OID 0) +-- Dependencies: 443 +-- Name: FUNCTION gbt_time_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_time_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5785 (class 0 OID 0) +-- Dependencies: 394 +-- Name: FUNCTION gbt_timetz_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_timetz_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5786 (class 0 OID 0) +-- Dependencies: 444 +-- Name: FUNCTION gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_timetz_consistent(internal, time with time zone, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5787 (class 0 OID 0) +-- Dependencies: 336 +-- Name: FUNCTION gbt_ts_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5788 (class 0 OID 0) +-- Dependencies: 462 +-- Name: FUNCTION gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_consistent(internal, timestamp without time zone, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5789 (class 0 OID 0) +-- Dependencies: 338 +-- Name: FUNCTION gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_distance(internal, timestamp without time zone, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5790 (class 0 OID 0) +-- Dependencies: 418 +-- Name: FUNCTION gbt_ts_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5791 (class 0 OID 0) +-- Dependencies: 345 +-- Name: FUNCTION gbt_ts_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5792 (class 0 OID 0) +-- Dependencies: 351 +-- Name: FUNCTION gbt_ts_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5793 (class 0 OID 0) +-- Dependencies: 327 +-- Name: FUNCTION gbt_ts_same(billiards.gbtreekey16, billiards.gbtreekey16, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_same(billiards.gbtreekey16, billiards.gbtreekey16, internal) TO "local-Python"; + + +-- +-- TOC entry 5794 (class 0 OID 0) +-- Dependencies: 353 +-- Name: FUNCTION gbt_ts_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5795 (class 0 OID 0) +-- Dependencies: 441 +-- Name: FUNCTION gbt_ts_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_ts_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5796 (class 0 OID 0) +-- Dependencies: 311 +-- Name: FUNCTION gbt_tstz_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_tstz_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5797 (class 0 OID 0) +-- Dependencies: 317 +-- Name: FUNCTION gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_tstz_consistent(internal, timestamp with time zone, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5798 (class 0 OID 0) +-- Dependencies: 269 +-- Name: FUNCTION gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_tstz_distance(internal, timestamp with time zone, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5799 (class 0 OID 0) +-- Dependencies: 270 +-- Name: FUNCTION gbt_uuid_compress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_uuid_compress(internal) TO "local-Python"; + + +-- +-- TOC entry 5800 (class 0 OID 0) +-- Dependencies: 343 +-- Name: FUNCTION gbt_uuid_consistent(internal, uuid, smallint, oid, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_uuid_consistent(internal, uuid, smallint, oid, internal) TO "local-Python"; + + +-- +-- TOC entry 5801 (class 0 OID 0) +-- Dependencies: 468 +-- Name: FUNCTION gbt_uuid_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_uuid_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5802 (class 0 OID 0) +-- Dependencies: 300 +-- Name: FUNCTION gbt_uuid_penalty(internal, internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_uuid_penalty(internal, internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5803 (class 0 OID 0) +-- Dependencies: 331 +-- Name: FUNCTION gbt_uuid_picksplit(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_uuid_picksplit(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5804 (class 0 OID 0) +-- Dependencies: 291 +-- Name: FUNCTION gbt_uuid_same(billiards.gbtreekey32, billiards.gbtreekey32, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_uuid_same(billiards.gbtreekey32, billiards.gbtreekey32, internal) TO "local-Python"; + + +-- +-- TOC entry 5805 (class 0 OID 0) +-- Dependencies: 316 +-- Name: FUNCTION gbt_uuid_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_uuid_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5806 (class 0 OID 0) +-- Dependencies: 361 +-- Name: FUNCTION gbt_uuid_union(internal, internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_uuid_union(internal, internal) TO "local-Python"; + + +-- +-- TOC entry 5807 (class 0 OID 0) +-- Dependencies: 408 +-- Name: FUNCTION gbt_var_decompress(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_var_decompress(internal) TO "local-Python"; + + +-- +-- TOC entry 5808 (class 0 OID 0) +-- Dependencies: 349 +-- Name: FUNCTION gbt_var_fetch(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_var_fetch(internal) TO "local-Python"; + + +-- +-- TOC entry 5809 (class 0 OID 0) +-- Dependencies: 427 +-- Name: FUNCTION gbt_varbit_sortsupport(internal); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gbt_varbit_sortsupport(internal) TO "local-Python"; + + +-- +-- TOC entry 5810 (class 0 OID 0) +-- Dependencies: 329 +-- Name: FUNCTION gist_translate_cmptype_btree(integer); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.gist_translate_cmptype_btree(integer) TO "local-Python"; + + +-- +-- TOC entry 5811 (class 0 OID 0) +-- Dependencies: 386 +-- Name: FUNCTION int2_dist(smallint, smallint); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.int2_dist(smallint, smallint) TO "local-Python"; + + +-- +-- TOC entry 5812 (class 0 OID 0) +-- Dependencies: 385 +-- Name: FUNCTION int4_dist(integer, integer); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.int4_dist(integer, integer) TO "local-Python"; + + +-- +-- TOC entry 5813 (class 0 OID 0) +-- Dependencies: 420 +-- Name: FUNCTION int8_dist(bigint, bigint); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.int8_dist(bigint, bigint) TO "local-Python"; + + +-- +-- TOC entry 5814 (class 0 OID 0) +-- Dependencies: 279 +-- Name: FUNCTION interval_dist(interval, interval); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.interval_dist(interval, interval) TO "local-Python"; + + +-- +-- TOC entry 5815 (class 0 OID 0) +-- Dependencies: 286 +-- Name: FUNCTION oid_dist(oid, oid); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.oid_dist(oid, oid) TO "local-Python"; + + +-- +-- TOC entry 5816 (class 0 OID 0) +-- Dependencies: 460 +-- Name: FUNCTION time_dist(time without time zone, time without time zone); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.time_dist(time without time zone, time without time zone) TO "local-Python"; + + +-- +-- TOC entry 5817 (class 0 OID 0) +-- Dependencies: 344 +-- Name: FUNCTION ts_dist(timestamp without time zone, timestamp without time zone); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.ts_dist(timestamp without time zone, timestamp without time zone) TO "local-Python"; + + +-- +-- TOC entry 5818 (class 0 OID 0) +-- Dependencies: 272 +-- Name: FUNCTION tstz_dist(timestamp with time zone, timestamp with time zone); Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON FUNCTION billiards.tstz_dist(timestamp with time zone, timestamp with time zone) TO "local-Python"; + + +-- +-- TOC entry 5831 (class 0 OID 0) +-- Dependencies: 222 +-- Name: TABLE dim_assistant; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_assistant TO "local-Python"; + + +-- +-- TOC entry 5833 (class 0 OID 0) +-- Dependencies: 223 +-- Name: TABLE dim_assistant_scd; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_assistant_scd TO "local-Python"; + + +-- +-- TOC entry 5834 (class 0 OID 0) +-- Dependencies: 224 +-- Name: TABLE dim_assistant_current; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_assistant_current TO "local-Python"; + + +-- +-- TOC entry 5836 (class 0 OID 0) +-- Dependencies: 225 +-- Name: SEQUENCE dim_assistant_scd_assistant_scd_id_seq; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON SEQUENCE billiards.dim_assistant_scd_assistant_scd_id_seq TO "local-Python"; + + +-- +-- TOC entry 5851 (class 0 OID 0) +-- Dependencies: 226 +-- Name: TABLE dim_member; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_member TO "local-Python"; + + +-- +-- TOC entry 5853 (class 0 OID 0) +-- Dependencies: 227 +-- Name: TABLE dim_member_scd; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_member_scd TO "local-Python"; + + +-- +-- TOC entry 5854 (class 0 OID 0) +-- Dependencies: 228 +-- Name: TABLE dim_member_current; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_member_current TO "local-Python"; + + +-- +-- TOC entry 5856 (class 0 OID 0) +-- Dependencies: 229 +-- Name: SEQUENCE dim_member_scd_member_scd_id_seq; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON SEQUENCE billiards.dim_member_scd_member_scd_id_seq TO "local-Python"; + + +-- +-- TOC entry 5867 (class 0 OID 0) +-- Dependencies: 230 +-- Name: TABLE dim_package; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_package TO "local-Python"; + + +-- +-- TOC entry 5887 (class 0 OID 0) +-- Dependencies: 231 +-- Name: TABLE dim_product; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_product TO "local-Python"; + + +-- +-- TOC entry 5889 (class 0 OID 0) +-- Dependencies: 232 +-- Name: TABLE dim_product_price_scd; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_product_price_scd TO "local-Python"; + + +-- +-- TOC entry 5890 (class 0 OID 0) +-- Dependencies: 233 +-- Name: TABLE dim_product_price_current; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_product_price_current TO "local-Python"; + + +-- +-- TOC entry 5892 (class 0 OID 0) +-- Dependencies: 234 +-- Name: SEQUENCE dim_product_price_scd_product_scd_id_seq; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT ALL ON SEQUENCE billiards.dim_product_price_scd_product_scd_id_seq TO "local-Python"; + + +-- +-- TOC entry 5900 (class 0 OID 0) +-- Dependencies: 235 +-- Name: TABLE dim_store; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_store TO "local-Python"; + + +-- +-- TOC entry 5913 (class 0 OID 0) +-- Dependencies: 236 +-- Name: TABLE dim_table; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.dim_table TO "local-Python"; + + +-- +-- TOC entry 5915 (class 0 OID 0) +-- Dependencies: 237 +-- Name: TABLE fact_assistant_abolish; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_assistant_abolish TO "local-Python"; + + +-- +-- TOC entry 5917 (class 0 OID 0) +-- Dependencies: 238 +-- Name: TABLE fact_inventory_change; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_inventory_change TO "local-Python"; + + +-- +-- TOC entry 5919 (class 0 OID 0) +-- Dependencies: 239 +-- Name: TABLE fact_ledger_entry; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_ledger_entry TO "local-Python"; + + +-- +-- TOC entry 5938 (class 0 OID 0) +-- Dependencies: 240 +-- Name: TABLE fact_order; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_order TO "local-Python"; + + +-- +-- TOC entry 5960 (class 0 OID 0) +-- Dependencies: 241 +-- Name: TABLE fact_order_detail; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_order_detail TO "local-Python"; + + +-- +-- TOC entry 5962 (class 0 OID 0) +-- Dependencies: 242 +-- Name: TABLE fact_package_usage; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_package_usage TO "local-Python"; + + +-- +-- TOC entry 5964 (class 0 OID 0) +-- Dependencies: 243 +-- Name: TABLE fact_payment; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_payment TO "local-Python"; + + +-- +-- TOC entry 5966 (class 0 OID 0) +-- Dependencies: 244 +-- Name: TABLE fact_refund; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_refund TO "local-Python"; + + +-- +-- TOC entry 5970 (class 0 OID 0) +-- Dependencies: 245 +-- Name: TABLE fact_table_discount; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_table_discount TO "local-Python"; + + +-- +-- TOC entry 5972 (class 0 OID 0) +-- Dependencies: 246 +-- Name: TABLE fact_topup; Type: ACL; Schema: billiards; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE billiards.fact_topup TO "local-Python"; + + +-- +-- TOC entry 5984 (class 0 OID 0) +-- Dependencies: 247 +-- Name: TABLE etl_cursor; Type: ACL; Schema: etl_admin; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE etl_admin.etl_cursor TO "local-Python"; + + +-- +-- TOC entry 5986 (class 0 OID 0) +-- Dependencies: 248 +-- Name: SEQUENCE etl_cursor_cursor_id_seq; Type: ACL; Schema: etl_admin; Owner: postgres +-- + +GRANT ALL ON SEQUENCE etl_admin.etl_cursor_cursor_id_seq TO "local-Python"; + + +-- +-- TOC entry 6011 (class 0 OID 0) +-- Dependencies: 249 +-- Name: TABLE etl_run; Type: ACL; Schema: etl_admin; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE etl_admin.etl_run TO "local-Python"; + + +-- +-- TOC entry 6013 (class 0 OID 0) +-- Dependencies: 250 +-- Name: SEQUENCE etl_run_run_id_seq; Type: ACL; Schema: etl_admin; Owner: postgres +-- + +GRANT ALL ON SEQUENCE etl_admin.etl_run_run_id_seq TO "local-Python"; + + +-- +-- TOC entry 6028 (class 0 OID 0) +-- Dependencies: 251 +-- Name: TABLE etl_task; Type: ACL; Schema: etl_admin; Owner: postgres +-- + +GRANT SELECT,INSERT,REFERENCES,DELETE,TRIGGER,TRUNCATE,UPDATE ON TABLE etl_admin.etl_task TO "local-Python"; + + +-- +-- TOC entry 6030 (class 0 OID 0) +-- Dependencies: 252 +-- Name: SEQUENCE etl_task_task_id_seq; Type: ACL; Schema: etl_admin; Owner: postgres +-- + +GRANT ALL ON SEQUENCE etl_admin.etl_task_task_id_seq TO "local-Python"; + + +-- Completed on 2025-11-19 07:06:32 + +-- +-- PostgreSQL database dump complete +-- + +\unrestrict p8JD9GasGaebtFp8BKgzK7l8DwZ7APkKsTnZV2aOY6thmYoUHLF5Lz8l8pc1Wad + diff --git a/etl_billiards/README.md b/etl_billiards/README.md index 47f35e1..725d3f3 100644 --- a/etl_billiards/README.md +++ b/etl_billiards/README.md @@ -219,6 +219,42 @@ python scripts/run_tests.py --preset offline_realdb python scripts/run_tests.py --list-presets # 查看或自定义 scripts/test_presets.py ``` +#### 3.3.2 脚本化测试组合(`run_tests.py` / `test_presets.py`) + +- `scripts/run_tests.py` 是 pytest 的统一入口:自动把项目根目录加入 `sys.path`,并提供 `--suite online/offline/integration`、`--tests`(自定义路径)、`--mode`、`--db-dsn`、`--json-archive`、`--json-temp`、`--keyword/-k`、`--pytest-args`、`--env KEY=VALUE` 等参数,可以像搭积木一样自由组合; +- `--preset foo` 会读取 `scripts/test_presets.py` 内 `PRESETS["foo"]` 的配置,并叠加到当前命令;`--list-presets` 与 `--dry-run` 可用来审阅或仅打印命令; +- 直接执行 `python scripts/test_presets.py` 可依次运行 `AUTO_RUN_PRESETS` 中列出的预置;传入 `--preset x --dry-run` 则只打印对应命令。 + +`test_presets.py` 充当“指令仓库”。每个预置都是一个字典,常用字段解释如下: + +| 字段 | 作用 | +| ---- | ---- | +| `suite` | 复用 `run_tests.py` 内置套件(online/offline/integration,可多选) | +| `tests` | 追加任意 pytest 路径,例如 `tests/unit/test_config.py` | +| `mode` | 覆盖 `TEST_MODE`(ONLINE / OFFLINE) | +| `db_dsn` | 覆盖 `TEST_DB_DSN`,用于连入真实测试库 | +| `json_archive` / `json_temp` | 配置离线 JSON 归档与临时目录 | +| `keyword` | 映射到 `pytest -k`,用于关键字过滤 | +| `pytest_args` | 附加 pytest 参数,例 `-vv --maxfail=1` | +| `env` | 额外环境变量列表,如 `["STORE_ID=123"]` | +| `preset_meta` | 说明性文字,便于描述场景 | + +示例:`offline_realdb` 预置会设置 `TEST_MODE=OFFLINE`、指定 `tests/testdata_json` 为归档目录,并通过 `db_dsn` 连到测试库。执行 `python scripts/run_tests.py --preset offline_realdb` 或 `python scripts/test_presets.py --preset offline_realdb` 即可复用该组合,保证本地、CI 与生产回放脚本一致。 + +#### 3.3.3 数据库连通性快速检查 + +`python scripts/test_db_connection.py` 提供最轻量的 PostgreSQL 连通性检测:默认使用 `TEST_DB_DSN`(也可传 `--dsn`),尝试连接并执行 `SELECT 1 AS ok`(可通过 `--query` 自定义)。典型用途: + +```bash +# 读取 .env/环境变量中的 TEST_DB_DSN +python scripts/test_db_connection.py + +# 临时指定 DSN,并检查任务配置表 +python scripts/test_db_connection.py --dsn postgresql://user:pwd@host:5432/LLZQ-test --query "SELECT count(*) FROM etl_admin.etl_task" +``` + +脚本返回 0 代表连接与查询成功;若返回非 0,可结合第 8 章“常见问题排查”的数据库章节(网络、防火墙、账号权限等)先定位问题,再运行完整 ETL。 + --- ## 4. 项目结构与文件说明 @@ -452,6 +488,19 @@ etl_billiards/ - API 请求失败时按配置进行重试,超过重试次数记录错误并终止该任务。 - 所有错误被记录到日志和运行追踪表,便于事后排查。 +### 5.6 ODS + DWD 双阶段策略(新增) + +为了支撑回溯/重放与后续 DWD 宽表构建,项目新增了 `billiards_ods` Schema 以及一组专门的 ODS 任务/Loader: + +- **ODS 表**:`billiards_ods.ods_order_settle`、`ods_table_use_detail`、`ods_assistant_ledger`、`ods_assistant_abolish`、`ods_goods_ledger`、`ods_payment`、`ods_refund`、`ods_coupon_verify`、`ods_member`、`ods_member_card`、`ods_package_coupon`、`ods_inventory_stock`、`ods_inventory_change`。每条记录都会保存 `store_id + 源主键 + payload JSON + fetched_at + source_endpoint` 等信息。 +- **通用 Loader**:`loaders/ods/generic.py::GenericODSLoader` 统一封装了 `INSERT ... ON CONFLICT ...` 与批量写入逻辑,调用方只需提供列名与主键列即可。 +- **ODS 任务**:`tasks/ods_tasks.py` 内通过 `OdsTaskSpec` 定义了一组任务(`ODS_ORDER_SETTLE`、`ODS_PAYMENT`、`ODS_ASSISTANT_LEDGER` 等),并在 `TaskRegistry` 中自动注册,可直接通过 `python -m cli.main --tasks ODS_ORDER_SETTLE,ODS_PAYMENT` 执行。 +- **双阶段链路**: + 1. 阶段 1(ODS):调用 API/离线归档 JSON,将原始记录写入 ODS 表,保留分页、抓取时间、来源文件等元数据。 + 2. 阶段 2(DWD/DIM):后续订单、支付、券等事实任务将改为从 ODS 读取 payload,经过解析/校验后写入 `billiards.fact_*`、`dim_*` 表,避免重复拉取上游接口。 + +> 新增的单元测试 `tests/unit/test_ods_tasks.py` 覆盖了 `ODS_ORDER_SETTLE`、`ODS_PAYMENT` 的入库路径,可作为扩展其他 ODS 任务的模板。 + --- ## 6. 迁移指南(从旧脚本到当前项目) diff --git a/etl_billiards/api/client.py b/etl_billiards/api/client.py index 1e45739..f03943f 100644 --- a/etl_billiards/api/client.py +++ b/etl_billiards/api/client.py @@ -54,42 +54,76 @@ class APIClient: resp.raise_for_status() return resp.json() + def iter_paginated( + self, + endpoint: str, + params: dict | None, + page_size: int = 200, + page_field: str = "pageIndex", + size_field: str = "pageSize", + data_path: tuple = ("data",), + list_key: str | None = None, + ): + """分页迭代器:逐页拉取数据并产出 (page_no, records, request_params, raw_response)。""" + base_params = dict(params or {}) + page = 1 + + while True: + page_params = dict(base_params) + page_params[page_field] = page + page_params[size_field] = page_size + + payload = self.get(endpoint, page_params) + records = self._extract_list(payload, data_path, list_key) + + yield page, records, page_params, payload + + if len(records) < page_size: + break + + if len(records) == 0: + break + + page += 1 + def get_paginated(self, endpoint: str, params: dict, page_size: int = 200, page_field: str = "pageIndex", size_field: str = "pageSize", data_path: tuple = ("data",), list_key: str = None) -> tuple: - """分页获取数据""" + """分页获取数据并将所有记录汇总在一个列表中。""" records, pages_meta = [], [] - page = 1 - - while True: - p = dict(params) - p[page_field] = page - p[size_field] = page_size - - obj = self.get(endpoint, p) - - # 解析数据路径 - cur = obj - for k in data_path: - if isinstance(cur, dict) and k in cur: - cur = cur[k] - - if list_key: - cur = (cur or {}).get(list_key, []) - - if not isinstance(cur, list): - cur = [] - - records.extend(cur) - - if len(cur) == 0: - break - - pages_meta.append({"page": page, "request": p, "response": obj}) - - if len(cur) < page_size: - break - - page += 1 - + + for page_no, page_records, request_params, response in self.iter_paginated( + endpoint=endpoint, + params=params, + page_size=page_size, + page_field=page_field, + size_field=size_field, + data_path=data_path, + list_key=list_key, + ): + records.extend(page_records) + pages_meta.append( + {"page": page_no, "request": request_params, "response": response} + ) + return records, pages_meta + + @staticmethod + def _extract_list(payload: dict, data_path: tuple, list_key: str | None): + """辅助函数:根据 data_path/list_key 提取列表结构。""" + cur = payload + for key in data_path: + if isinstance(cur, dict): + cur = cur.get(key) + else: + cur = None + if cur is None: + break + + if list_key and isinstance(cur, dict): + cur = cur.get(list_key) + + if not isinstance(cur, list): + cur = [] + + return cur diff --git a/etl_billiards/database/operations.py b/etl_billiards/database/operations.py index 6d54069..ec48c0d 100644 --- a/etl_billiards/database/operations.py +++ b/etl_billiards/database/operations.py @@ -7,6 +7,7 @@ class DatabaseOperations: """数据库批量操作封装""" def __init__(self, connection): + self._connection = connection self.conn = connection.conn def batch_execute(self, sql: str, rows: list, page_size: int = 1000): @@ -75,3 +76,24 @@ class DatabaseOperations: if isinstance(rec, dict): return bool(rec.get("inserted")) return False + + # --- pass-through helpers ------------------------------------------------- + def commit(self): + """提交事务(委托给底层连接)""" + self._connection.commit() + + def rollback(self): + """回滚事务(委托给底层连接)""" + self._connection.rollback() + + def query(self, sql: str, args=None): + """执行查询并返回结果""" + return self._connection.query(sql, args) + + def execute(self, sql: str, args=None): + """执行任意 SQL""" + self._connection.execute(sql, args) + + def cursor(self): + """暴露原生 cursor,供特殊操作使用""" + return self.conn.cursor() diff --git a/etl_billiards/loaders/facts/payment.py b/etl_billiards/loaders/facts/payment.py index 54e26a3..2a04b07 100644 --- a/etl_billiards/loaders/facts/payment.py +++ b/etl_billiards/loaders/facts/payment.py @@ -1,31 +1,50 @@ # -*- coding: utf-8 -*- -"""支付记录加载器""" +"""支付事实表加载器""" from ..base_loader import BaseLoader class PaymentLoader(BaseLoader): - """支付记录加载器""" + """支付数据加载器""" def upsert_payments(self, records: list, store_id: int) -> tuple: - """加载支付记录""" + """加载支付数据""" if not records: return (0, 0, 0) sql = """ INSERT INTO billiards.fact_payment ( - store_id, pay_id, order_id, pay_time, pay_amount, - pay_type, pay_status, remark, raw_data + store_id, pay_id, site_id, tenant_id, + order_settle_id, order_trade_no, + relate_type, relate_id, + create_time, pay_time, + pay_amount, fee_amount, discount_amount, + payment_method, online_pay_channel, pay_terminal, + pay_status, raw_data ) VALUES ( - %(store_id)s, %(pay_id)s, %(order_id)s, %(pay_time)s, %(pay_amount)s, - %(pay_type)s, %(pay_status)s, %(remark)s, %(raw_data)s + %(store_id)s, %(pay_id)s, %(site_id)s, %(tenant_id)s, + %(order_settle_id)s, %(order_trade_no)s, + %(relate_type)s, %(relate_id)s, + %(create_time)s, %(pay_time)s, + %(pay_amount)s, %(fee_amount)s, %(discount_amount)s, + %(payment_method)s, %(online_pay_channel)s, %(pay_terminal)s, + %(pay_status)s, %(raw_data)s ) ON CONFLICT (store_id, pay_id) DO UPDATE SET - order_id = EXCLUDED.order_id, + order_settle_id = EXCLUDED.order_settle_id, + order_trade_no = EXCLUDED.order_trade_no, + relate_type = EXCLUDED.relate_type, + relate_id = EXCLUDED.relate_id, + site_id = EXCLUDED.site_id, + tenant_id = EXCLUDED.tenant_id, + create_time = EXCLUDED.create_time, pay_time = EXCLUDED.pay_time, pay_amount = EXCLUDED.pay_amount, - pay_type = EXCLUDED.pay_type, + fee_amount = EXCLUDED.fee_amount, + discount_amount = EXCLUDED.discount_amount, + payment_method = EXCLUDED.payment_method, + online_pay_channel = EXCLUDED.online_pay_channel, + pay_terminal = EXCLUDED.pay_terminal, pay_status = EXCLUDED.pay_status, - remark = EXCLUDED.remark, raw_data = EXCLUDED.raw_data, updated_at = now() RETURNING (xmax = 0) AS inserted diff --git a/etl_billiards/loaders/facts/ticket.py b/etl_billiards/loaders/facts/ticket.py new file mode 100644 index 0000000..62319ff --- /dev/null +++ b/etl_billiards/loaders/facts/ticket.py @@ -0,0 +1,188 @@ +# -*- coding: utf-8 -*- +"""小票详情加载器""" +from ..base_loader import BaseLoader +import json + +class TicketLoader(BaseLoader): + """ + Loader for parsing Ticket Detail JSON and populating DWD fact tables. + Handles: + - fact_order (Header) + - fact_order_goods (Items) + - fact_table_usage (Items) + - fact_assistant_service (Items) + """ + + def process_tickets(self, tickets: list, store_id: int) -> tuple: + """ + Process a batch of ticket JSONs. + Returns (inserted_count, error_count) + """ + inserted_count = 0 + error_count = 0 + + # Prepare batch lists + orders = [] + goods_list = [] + table_usages = [] + assistant_services = [] + + for ticket in tickets: + try: + # 1. Parse Header (fact_order) + root_data = ticket.get("data", {}).get("data", {}) + if not root_data: + continue + + order_settle_id = root_data.get("orderSettleId") + if not order_settle_id: + continue + + orders.append({ + "store_id": store_id, + "order_settle_id": order_settle_id, + "order_trade_no": 0, + "order_no": str(root_data.get("orderSettleNumber", "")), + "member_id": 0, + "pay_time": root_data.get("payTime"), + "total_amount": root_data.get("consumeMoney", 0), + "pay_amount": root_data.get("actualPayment", 0), + "discount_amount": root_data.get("memberOfferAmount", 0), + "coupon_amount": root_data.get("couponAmount", 0), + "status": "PAID", + "cashier_name": root_data.get("cashierName", ""), + "remark": root_data.get("orderRemark", ""), + "raw_data": json.dumps(ticket, ensure_ascii=False) + }) + + # 2. Parse Items (orderItem list) + order_items = root_data.get("orderItem", []) + for item in order_items: + order_trade_no = item.get("siteOrderId") + + # 2.1 Table Ledger + table_ledger = item.get("tableLedger") + if table_ledger: + table_usages.append({ + "store_id": store_id, + "order_ledger_id": table_ledger.get("orderTableLedgerId"), + "order_settle_id": order_settle_id, + "table_id": table_ledger.get("siteTableId"), + "table_name": table_ledger.get("tableName"), + "start_time": table_ledger.get("chargeStartTime"), + "end_time": table_ledger.get("chargeEndTime"), + "duration_minutes": table_ledger.get("useDuration", 0), + "total_amount": table_ledger.get("consumptionAmount", 0), + "pay_amount": table_ledger.get("consumptionAmount", 0) - table_ledger.get("memberDiscountAmount", 0) + }) + + # 2.2 Goods Ledgers + goods_ledgers = item.get("goodsLedgers", []) + for g in goods_ledgers: + goods_list.append({ + "store_id": store_id, + "order_goods_id": g.get("orderGoodsLedgerId"), + "order_settle_id": order_settle_id, + "order_trade_no": order_trade_no, + "goods_id": g.get("siteGoodsId"), + "goods_name": g.get("goodsName"), + "quantity": g.get("goodsCount", 0), + "unit_price": g.get("goodsPrice", 0), + "total_amount": g.get("ledgerAmount", 0), + "pay_amount": g.get("realGoodsMoney", 0) + }) + + # 2.3 Assistant Services + assistant_ledgers = item.get("assistantPlayWith", []) + for a in assistant_ledgers: + assistant_services.append({ + "store_id": store_id, + "ledger_id": a.get("orderAssistantLedgerId"), + "order_settle_id": order_settle_id, + "assistant_id": a.get("assistantId"), + "assistant_name": a.get("ledgerName"), + "service_type": a.get("skillName", "Play"), + "start_time": a.get("ledgerStartTime"), + "end_time": a.get("ledgerEndTime"), + "duration_minutes": int(a.get("ledgerCount", 0) / 60) if a.get("ledgerCount") else 0, + "total_amount": a.get("ledgerAmount", 0), + "pay_amount": a.get("ledgerAmount", 0) + }) + + inserted_count += 1 + + except Exception as e: + self.logger.error(f"Error parsing ticket: {e}", exc_info=True) + error_count += 1 + + # 3. Batch Insert/Upsert + if orders: + self._upsert_orders(orders) + if goods_list: + self._upsert_goods(goods_list) + if table_usages: + self._upsert_table_usages(table_usages) + if assistant_services: + self._upsert_assistant_services(assistant_services) + + return inserted_count, error_count + + def _upsert_orders(self, rows): + sql = """ + INSERT INTO billiards.fact_order ( + store_id, order_settle_id, order_trade_no, order_no, member_id, + pay_time, total_amount, pay_amount, discount_amount, coupon_amount, + status, cashier_name, remark, raw_data + ) VALUES ( + %(store_id)s, %(order_settle_id)s, %(order_trade_no)s, %(order_no)s, %(member_id)s, + %(pay_time)s, %(total_amount)s, %(pay_amount)s, %(discount_amount)s, %(coupon_amount)s, + %(status)s, %(cashier_name)s, %(remark)s, %(raw_data)s + ) + ON CONFLICT (store_id, order_settle_id) DO UPDATE SET + pay_time = EXCLUDED.pay_time, + pay_amount = EXCLUDED.pay_amount, + updated_at = now() + """ + self.db.batch_execute(sql, rows) + + def _upsert_goods(self, rows): + sql = """ + INSERT INTO billiards.fact_order_goods ( + store_id, order_goods_id, order_settle_id, order_trade_no, + goods_id, goods_name, quantity, unit_price, total_amount, pay_amount + ) VALUES ( + %(store_id)s, %(order_goods_id)s, %(order_settle_id)s, %(order_trade_no)s, + %(goods_id)s, %(goods_name)s, %(quantity)s, %(unit_price)s, %(total_amount)s, %(pay_amount)s + ) + ON CONFLICT (store_id, order_goods_id) DO UPDATE SET + pay_amount = EXCLUDED.pay_amount + """ + self.db.batch_execute(sql, rows) + + def _upsert_table_usages(self, rows): + sql = """ + INSERT INTO billiards.fact_table_usage ( + store_id, order_ledger_id, order_settle_id, table_id, table_name, + start_time, end_time, duration_minutes, total_amount, pay_amount + ) VALUES ( + %(store_id)s, %(order_ledger_id)s, %(order_settle_id)s, %(table_id)s, %(table_name)s, + %(start_time)s, %(end_time)s, %(duration_minutes)s, %(total_amount)s, %(pay_amount)s + ) + ON CONFLICT (store_id, order_ledger_id) DO UPDATE SET + pay_amount = EXCLUDED.pay_amount + """ + self.db.batch_execute(sql, rows) + + def _upsert_assistant_services(self, rows): + sql = """ + INSERT INTO billiards.fact_assistant_service ( + store_id, ledger_id, order_settle_id, assistant_id, assistant_name, + service_type, start_time, end_time, duration_minutes, total_amount, pay_amount + ) VALUES ( + %(store_id)s, %(ledger_id)s, %(order_settle_id)s, %(assistant_id)s, %(assistant_name)s, + %(service_type)s, %(start_time)s, %(end_time)s, %(duration_minutes)s, %(total_amount)s, %(pay_amount)s + ) + ON CONFLICT (store_id, ledger_id) DO UPDATE SET + pay_amount = EXCLUDED.pay_amount + """ + self.db.batch_execute(sql, rows) diff --git a/etl_billiards/loaders/ods/__init__.py b/etl_billiards/loaders/ods/__init__.py new file mode 100644 index 0000000..44d9739 --- /dev/null +++ b/etl_billiards/loaders/ods/__init__.py @@ -0,0 +1,6 @@ +# -*- coding: utf-8 -*- +"""ODS loader helpers.""" + +from .generic import GenericODSLoader + +__all__ = ["GenericODSLoader"] diff --git a/etl_billiards/loaders/ods/generic.py b/etl_billiards/loaders/ods/generic.py new file mode 100644 index 0000000..9346292 --- /dev/null +++ b/etl_billiards/loaders/ods/generic.py @@ -0,0 +1,67 @@ +# -*- coding: utf-8 -*- +"""Generic ODS loader that keeps raw payload + primary keys.""" +from __future__ import annotations + +import json +from datetime import datetime, timezone +from typing import Iterable, Sequence + +from ..base_loader import BaseLoader + + +class GenericODSLoader(BaseLoader): + """Insert/update helper for ODS tables that share the same pattern.""" + + def __init__( + self, + db_ops, + table_name: str, + columns: Sequence[str], + conflict_columns: Sequence[str], + ): + super().__init__(db_ops) + if not conflict_columns: + raise ValueError("conflict_columns must not be empty for ODS loader") + self.table_name = table_name + self.columns = list(columns) + self.conflict_columns = list(conflict_columns) + self._sql = self._build_sql() + + def upsert_rows(self, rows: Iterable[dict]) -> tuple[int, int, int]: + """Insert/update the provided iterable of dictionaries.""" + rows = list(rows) + if not rows: + return (0, 0, 0) + + normalized = [self._normalize_row(row) for row in rows] + inserted, updated = self.db.batch_upsert_with_returning( + self._sql, normalized, page_size=self._batch_size() + ) + return inserted, updated, 0 + + def _build_sql(self) -> str: + col_list = ", ".join(self.columns) + placeholders = ", ".join(f"%({col})s" for col in self.columns) + conflict_clause = ", ".join(self.conflict_columns) + update_columns = [c for c in self.columns if c not in self.conflict_columns] + set_clause = ", ".join(f"{col} = EXCLUDED.{col}" for col in update_columns) + return ( + f"INSERT INTO {self.table_name} ({col_list}) " + f"VALUES ({placeholders}) " + f"ON CONFLICT ({conflict_clause}) DO UPDATE SET {set_clause} " + f"RETURNING (xmax = 0) AS inserted" + ) + + def _normalize_row(self, row: dict) -> dict: + normalized = {} + for col in self.columns: + value = row.get(col) + if col == "payload" and value is not None and not isinstance(value, str): + normalized[col] = json.dumps(value, ensure_ascii=False) + else: + normalized[col] = value + + if "fetched_at" in normalized and normalized["fetched_at"] is None: + normalized["fetched_at"] = datetime.now(timezone.utc) + + return normalized diff --git a/etl_billiards/orchestration/task_registry.py b/etl_billiards/orchestration/task_registry.py index a6fcbd4..5668174 100644 --- a/etl_billiards/orchestration/task_registry.py +++ b/etl_billiards/orchestration/task_registry.py @@ -14,6 +14,11 @@ from tasks.topups_task import TopupsTask from tasks.table_discount_task import TableDiscountTask from tasks.assistant_abolish_task import AssistantAbolishTask from tasks.ledger_task import LedgerTask +from tasks.ods_tasks import ODS_TASK_CLASSES +from tasks.ticket_dwd_task import TicketDwdTask +from tasks.manual_ingest_task import ManualIngestTask +from tasks.payments_dwd_task import PaymentsDwdTask +from tasks.members_dwd_task import MembersDwdTask class TaskRegistry: """任务注册和工厂""" @@ -55,3 +60,9 @@ default_registry.register("TOPUPS", TopupsTask) default_registry.register("TABLE_DISCOUNT", TableDiscountTask) default_registry.register("ASSISTANT_ABOLISH", AssistantAbolishTask) default_registry.register("LEDGER", LedgerTask) +default_registry.register("TICKET_DWD", TicketDwdTask) +default_registry.register("MANUAL_INGEST", ManualIngestTask) +default_registry.register("PAYMENTS_DWD", PaymentsDwdTask) +default_registry.register("MEMBERS_DWD", MembersDwdTask) +for code, task_cls in ODS_TASK_CLASSES.items(): + default_registry.register(code, task_cls) diff --git a/etl_billiards/schema_v2.sql b/etl_billiards/schema_v2.sql index 054aa1c..57f57de 100644 --- a/etl_billiards/schema_v2.sql +++ b/etl_billiards/schema_v2.sql @@ -1,39 +1,52 @@ -- -*- coding: utf-8 -*- -- Feiqiu-ETL schema (JSON-first alignment) +-- Updated: 2025-11-19 (Refactored for Manual Import & AI-Friendly DWS) CREATE SCHEMA IF NOT EXISTS billiards; CREATE SCHEMA IF NOT EXISTS billiards_ods; +CREATE SCHEMA IF NOT EXISTS billiards_dws; CREATE SCHEMA IF NOT EXISTS etl_admin; COMMENT ON SCHEMA billiards IS '门店业务数据 Schema,存放维度/事实层(与 JSON 字段对应)'; +COMMENT ON SCHEMA billiards_ods IS '原始数据层 (ODS),存放原始 JSON,支持 API 抓取和手工导入'; +COMMENT ON SCHEMA billiards_dws IS '数据汇总层 (DWS),存放面向 AI 分析的宽表视图'; COMMENT ON SCHEMA etl_admin IS 'ETL 调度、游标与运行记录 Schema'; -- ========================= --- Billiards ODS tables +-- 1. Billiards ODS tables -- ========================= +-- 1.1 Order & Settlement ODS +-- Corresponds to /order/list or manual export CREATE TABLE IF NOT EXISTS billiards_ods.ods_order_settle ( store_id bigint NOT NULL, order_settle_id bigint NOT NULL, order_trade_no bigint, - page_no integer, - page_size integer, + page_no integer, -- Nullable for manual import + page_size integer, -- Nullable for manual import source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, order_settle_id) ); COMMENT ON TABLE billiards_ods.ods_order_settle IS '订单/结算 ODS(/order/list、ticket 接口原始 JSON)'; -COMMENT ON COLUMN billiards_ods.ods_order_settle.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards_ods.ods_order_settle.order_settle_id IS '结算单 ID (order_settle_id)'; -COMMENT ON COLUMN billiards_ods.ods_order_settle.order_trade_no IS '订单交易号 (order_trade_no)'; -COMMENT ON COLUMN billiards_ods.ods_order_settle.page_no IS '来源分页序号'; -COMMENT ON COLUMN billiards_ods.ods_order_settle.page_size IS '来源分页大小'; -COMMENT ON COLUMN billiards_ods.ods_order_settle.source_file IS '离线导出文件名/路径'; -COMMENT ON COLUMN billiards_ods.ods_order_settle.fetched_at IS '入 ODS 时间'; -COMMENT ON COLUMN billiards_ods.ods_order_settle.payload IS '原始 JSON 负载'; +-- 1.2 Ticket Detail ODS (NEW) +-- Corresponds to "小票详情.json" - Contains full nested details +CREATE TABLE IF NOT EXISTS billiards_ods.ods_ticket_detail ( + store_id bigint NOT NULL, + order_settle_id bigint NOT NULL, + source_file varchar(255), + fetched_at timestamptz NOT NULL DEFAULT now(), + payload jsonb NOT NULL, + PRIMARY KEY (store_id, order_settle_id) +); + +COMMENT ON TABLE billiards_ods.ods_ticket_detail IS '小票详情 ODS(包含台费、商品、助教明细的完整 JSON)'; + +-- 1.3 Table Usage ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_table_use_detail ( store_id bigint NOT NULL, ledger_id bigint NOT NULL, @@ -41,13 +54,13 @@ CREATE TABLE IF NOT EXISTS billiards_ods.ods_table_use_detail ( order_settle_id bigint, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, ledger_id) ); -COMMENT ON TABLE billiards_ods.ods_table_use_detail IS '台费/开台流水 ODS(siteTableUseDetailsList 等接口)'; - +-- 1.4 Assistant Ledger ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_assistant_ledger ( store_id bigint NOT NULL, ledger_id bigint NOT NULL, @@ -55,25 +68,25 @@ CREATE TABLE IF NOT EXISTS billiards_ods.ods_assistant_ledger ( order_settle_id bigint, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, ledger_id) ); -COMMENT ON TABLE billiards_ods.ods_assistant_ledger IS '助教流水 ODS(orderAssistantDetails 等接口)'; - +-- 1.5 Assistant Abolish ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_assistant_abolish ( store_id bigint NOT NULL, abolish_id bigint NOT NULL, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, abolish_id) ); -COMMENT ON TABLE billiards_ods.ods_assistant_abolish IS '助教作废记录 ODS'; - +-- 1.6 Goods Ledger ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_goods_ledger ( store_id bigint NOT NULL, order_goods_id bigint NOT NULL, @@ -81,13 +94,13 @@ CREATE TABLE IF NOT EXISTS billiards_ods.ods_goods_ledger ( order_settle_id bigint, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, order_goods_id) ); -COMMENT ON TABLE billiards_ods.ods_goods_ledger IS '商品销售流水 ODS(orderGoodsLedgers/orderGoodsList)'; - +-- 1.7 Payment ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_payment ( store_id bigint NOT NULL, pay_id bigint NOT NULL, @@ -95,100 +108,99 @@ CREATE TABLE IF NOT EXISTS billiards_ods.ods_payment ( relate_id bigint, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, pay_id) ); -COMMENT ON TABLE billiards_ods.ods_payment IS '支付流水 ODS(payRecord/payList)'; - +-- 1.8 Refund ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_refund ( store_id bigint NOT NULL, refund_id bigint NOT NULL, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, refund_id) ); -COMMENT ON TABLE billiards_ods.ods_refund IS '退款流水 ODS'; - +-- 1.9 Coupon Verify ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_coupon_verify ( store_id bigint NOT NULL, coupon_id bigint NOT NULL, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, coupon_id) ); -COMMENT ON TABLE billiards_ods.ods_coupon_verify IS '团购/验券流水 ODS'; - +-- 1.10 Member ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_member ( store_id bigint NOT NULL, member_id bigint NOT NULL, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, member_id) ); -COMMENT ON TABLE billiards_ods.ods_member IS '会员档案 ODS'; - +-- 1.11 Member Card ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_member_card ( store_id bigint NOT NULL, card_id bigint NOT NULL, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, card_id) ); -COMMENT ON TABLE billiards_ods.ods_member_card IS '储值卡/会员卡 ODS'; - +-- 1.12 Package Coupon ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_package_coupon ( store_id bigint NOT NULL, package_id bigint NOT NULL, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, package_id) ); -COMMENT ON TABLE billiards_ods.ods_package_coupon IS '团购套餐定义 ODS'; - +-- 1.13 Inventory Stock ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_inventory_stock ( store_id bigint NOT NULL, site_goods_id bigint NOT NULL, snapshot_key varchar(100) NOT NULL DEFAULT 'default', page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, site_goods_id, snapshot_key) ); -COMMENT ON TABLE billiards_ods.ods_inventory_stock IS '库存汇总 ODS(inventorySummary 等接口)'; - +-- 1.14 Inventory Change ODS CREATE TABLE IF NOT EXISTS billiards_ods.ods_inventory_change ( store_id bigint NOT NULL, change_id bigint NOT NULL, page_no integer, source_file varchar(255), + source_endpoint varchar(255), fetched_at timestamptz NOT NULL DEFAULT now(), payload jsonb NOT NULL, PRIMARY KEY (store_id, change_id) ); -COMMENT ON TABLE billiards_ods.ods_inventory_change IS '库存变动记录 ODS'; - -- ========================= --- Billiards dimension tables +-- 2. Billiards Dimension Tables -- ========================= CREATE TABLE IF NOT EXISTS billiards.dim_store ( @@ -205,19 +217,6 @@ CREATE TABLE IF NOT EXISTS billiards.dim_store ( raw_data jsonb NOT NULL DEFAULT '{}'::jsonb ); -COMMENT ON TABLE billiards.dim_store IS '门店/场馆维度(siteProfile 信息快照)'; -COMMENT ON COLUMN billiards.dim_store.store_id IS '门店 ID,主键(API storeId)'; -COMMENT ON COLUMN billiards.dim_store.store_name IS '门店名称 (shop_name)'; -COMMENT ON COLUMN billiards.dim_store.tenant_id IS '租户 ID (tenant_id)'; -COMMENT ON COLUMN billiards.dim_store.region_code IS '区域编码/城市代码 (tenant_site_region_id)'; -COMMENT ON COLUMN billiards.dim_store.address IS '完整地址 (full_address/address)'; -COMMENT ON COLUMN billiards.dim_store.contact_name IS '联系人(可扩展)'; -COMMENT ON COLUMN billiards.dim_store.contact_phone IS '联系电话 (business_tel)'; -COMMENT ON COLUMN billiards.dim_store.created_time IS '创建时间'; -COMMENT ON COLUMN billiards.dim_store.updated_time IS '更新时间'; -COMMENT ON COLUMN billiards.dim_store.remark IS '备注'; -COMMENT ON COLUMN billiards.dim_store.raw_data IS '原始 JSON 快照'; - CREATE TABLE IF NOT EXISTS billiards.dim_assistant ( store_id bigint NOT NULL, assistant_id bigint NOT NULL, @@ -253,39 +252,6 @@ CREATE TABLE IF NOT EXISTS billiards.dim_assistant ( PRIMARY KEY (store_id, assistant_id) ); -COMMENT ON TABLE billiards.dim_assistant IS '助教/技师维度(Assistant/List)'; -COMMENT ON COLUMN billiards.dim_assistant.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.dim_assistant.assistant_id IS '助教 ID (id)'; -COMMENT ON COLUMN billiards.dim_assistant.assistant_no IS '助教编号 (assistant_no)'; -COMMENT ON COLUMN billiards.dim_assistant.nickname IS '昵称 (nickname)'; -COMMENT ON COLUMN billiards.dim_assistant.real_name IS '真实姓名 (real_name)'; -COMMENT ON COLUMN billiards.dim_assistant.gender IS '性别'; -COMMENT ON COLUMN billiards.dim_assistant.mobile IS '手机号'; -COMMENT ON COLUMN billiards.dim_assistant.level IS '等级/段位 (level)'; -COMMENT ON COLUMN billiards.dim_assistant.team_id IS '团队 ID (team_id)'; -COMMENT ON COLUMN billiards.dim_assistant.team_name IS '团队名称 (team_name)'; -COMMENT ON COLUMN billiards.dim_assistant.assistant_status IS '账号状态 (assistant_status)'; -COMMENT ON COLUMN billiards.dim_assistant.work_status IS '工作状态 (work_status)'; -COMMENT ON COLUMN billiards.dim_assistant.entry_time IS '入职时间 (entry_time)'; -COMMENT ON COLUMN billiards.dim_assistant.resign_time IS '离职时间 (resign_time)'; -COMMENT ON COLUMN billiards.dim_assistant.start_time IS '可服务开始时间 (start_time)'; -COMMENT ON COLUMN billiards.dim_assistant.end_time IS '可服务结束时间 (end_time)'; -COMMENT ON COLUMN billiards.dim_assistant.create_time IS '创建时间 (create_time)'; -COMMENT ON COLUMN billiards.dim_assistant.update_time IS '最近更新时间 (update_time)'; -COMMENT ON COLUMN billiards.dim_assistant.system_role_id IS '系统角色 ID'; -COMMENT ON COLUMN billiards.dim_assistant.online_status IS '在线/离线状态'; -COMMENT ON COLUMN billiards.dim_assistant.allow_cx IS '是否允许撤销/重开'; -COMMENT ON COLUMN billiards.dim_assistant.charge_way IS '计费方式 (charge_way)'; -COMMENT ON COLUMN billiards.dim_assistant.pd_unit_price IS '陪打单价 (pd_unit_price)'; -COMMENT ON COLUMN billiards.dim_assistant.cx_unit_price IS '拆洗/其它单价 (cx_unit_price)'; -COMMENT ON COLUMN billiards.dim_assistant.is_guaranteed IS '是否保底'; -COMMENT ON COLUMN billiards.dim_assistant.is_team_leader IS '是否队长'; -COMMENT ON COLUMN billiards.dim_assistant.serial_number IS '序列号/展示码 (serial_number)'; -COMMENT ON COLUMN billiards.dim_assistant.show_sort IS '展示排序 (show_sort)'; -COMMENT ON COLUMN billiards.dim_assistant.is_delete IS '删除标记'; -COMMENT ON COLUMN billiards.dim_assistant.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.dim_assistant.updated_at IS 'ETL 更新时间'; - CREATE TABLE IF NOT EXISTS billiards.dim_member ( store_id bigint NOT NULL, member_id bigint NOT NULL, @@ -299,17 +265,6 @@ CREATE TABLE IF NOT EXISTS billiards.dim_member ( PRIMARY KEY (store_id, member_id) ); -COMMENT ON TABLE billiards.dim_member IS '会员维度(MemberProfile/GetTenantMemberList)'; -COMMENT ON COLUMN billiards.dim_member.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.dim_member.member_id IS '会员 ID (memberId)'; -COMMENT ON COLUMN billiards.dim_member.member_name IS '会员名称 (memberName)'; -COMMENT ON COLUMN billiards.dim_member.phone IS '手机号 (phone)'; -COMMENT ON COLUMN billiards.dim_member.balance IS '储值余额 (balance)'; -COMMENT ON COLUMN billiards.dim_member.status IS '会员状态 (status)'; -COMMENT ON COLUMN billiards.dim_member.register_time IS '注册时间 (registerTime)'; -COMMENT ON COLUMN billiards.dim_member.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.dim_member.updated_at IS 'ETL 更新时间'; - CREATE TABLE IF NOT EXISTS billiards.dim_package_coupon ( store_id bigint NOT NULL, package_id bigint NOT NULL, @@ -337,31 +292,6 @@ CREATE TABLE IF NOT EXISTS billiards.dim_package_coupon ( PRIMARY KEY (store_id, package_id) ); -COMMENT ON TABLE billiards.dim_package_coupon IS '团购/套餐定义维度(Package/List)'; -COMMENT ON COLUMN billiards.dim_package_coupon.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.dim_package_coupon.package_id IS '套餐主键 (id)'; -COMMENT ON COLUMN billiards.dim_package_coupon.package_code IS '套餐编码 (package_id 或自定义编码)'; -COMMENT ON COLUMN billiards.dim_package_coupon.package_name IS '套餐名称 (package_name)'; -COMMENT ON COLUMN billiards.dim_package_coupon.table_area_id IS '适用台桌区域 ID'; -COMMENT ON COLUMN billiards.dim_package_coupon.table_area_name IS '适用台桌区域名称'; -COMMENT ON COLUMN billiards.dim_package_coupon.selling_price IS '售卖价格 (selling_price)'; -COMMENT ON COLUMN billiards.dim_package_coupon.duration_seconds IS '可用时长(秒)(duration)'; -COMMENT ON COLUMN billiards.dim_package_coupon.start_time IS '可用时间起'; -COMMENT ON COLUMN billiards.dim_package_coupon.end_time IS '可用时间止'; -COMMENT ON COLUMN billiards.dim_package_coupon.type IS '套餐类型 (type/group_type)'; -COMMENT ON COLUMN billiards.dim_package_coupon.is_enabled IS '是否启用 (is_enabled)'; -COMMENT ON COLUMN billiards.dim_package_coupon.is_delete IS '删除标记 (is_delete)'; -COMMENT ON COLUMN billiards.dim_package_coupon.usable_count IS '可使用次数 (usable_count)'; -COMMENT ON COLUMN billiards.dim_package_coupon.creator_name IS '创建人 (creator_name)'; -COMMENT ON COLUMN billiards.dim_package_coupon.date_type IS '日期限制 (date_type)'; -COMMENT ON COLUMN billiards.dim_package_coupon.group_type IS '渠道/团购类型 (group_type)'; -COMMENT ON COLUMN billiards.dim_package_coupon.coupon_money IS '券面额 (coupon_money)'; -COMMENT ON COLUMN billiards.dim_package_coupon.area_tag_type IS '区域标签类型'; -COMMENT ON COLUMN billiards.dim_package_coupon.system_group_type IS '系统套餐类型 (system_group_type)'; -COMMENT ON COLUMN billiards.dim_package_coupon.card_type_ids IS '适用会员卡类型列表 (card_type_ids)'; -COMMENT ON COLUMN billiards.dim_package_coupon.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.dim_package_coupon.updated_at IS 'ETL 更新时间'; - CREATE TABLE IF NOT EXISTS billiards.dim_product ( store_id bigint NOT NULL, product_id bigint NOT NULL, @@ -385,27 +315,6 @@ CREATE TABLE IF NOT EXISTS billiards.dim_product ( PRIMARY KEY (store_id, product_id) ); -COMMENT ON TABLE billiards.dim_product IS '商品维度(TenantGoods/QueryTenantGoods)'; -COMMENT ON COLUMN billiards.dim_product.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.dim_product.product_id IS '商品 ID (siteGoodsId/tenantGoodsId)'; -COMMENT ON COLUMN billiards.dim_product.site_product_id IS '站点/门店商品 ID (siteGoodsId)'; -COMMENT ON COLUMN billiards.dim_product.product_name IS '商品名称 (goodsName/productName)'; -COMMENT ON COLUMN billiards.dim_product.category_id IS '一级分类 ID (goodsCategoryId)'; -COMMENT ON COLUMN billiards.dim_product.category_name IS '一级分类名称 (categoryName)'; -COMMENT ON COLUMN billiards.dim_product.second_category_id IS '二级分类 ID (goodsSecondCategoryId)'; -COMMENT ON COLUMN billiards.dim_product.unit IS '计量单位 (goodsUnit)'; -COMMENT ON COLUMN billiards.dim_product.cost_price IS '成本价 (costPrice)'; -COMMENT ON COLUMN billiards.dim_product.sale_price IS '销售价 (goodsPrice/salePrice)'; -COMMENT ON COLUMN billiards.dim_product.allow_discount IS '是否允许折扣 (able_discount)'; -COMMENT ON COLUMN billiards.dim_product.status IS '商品状态 (goodsState/status)'; -COMMENT ON COLUMN billiards.dim_product.supplier_id IS '供应商 ID'; -COMMENT ON COLUMN billiards.dim_product.barcode IS '条码 (barcode)'; -COMMENT ON COLUMN billiards.dim_product.is_combo IS '是否组合/套餐商品 (isCombo)'; -COMMENT ON COLUMN billiards.dim_product.created_time IS '创建时间 (createTime)'; -COMMENT ON COLUMN billiards.dim_product.updated_time IS '更新时间 (updateTime)'; -COMMENT ON COLUMN billiards.dim_product.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.dim_product.updated_at IS 'ETL 更新时间'; - CREATE TABLE IF NOT EXISTS billiards.dim_product_price_scd ( product_scd_id bigserial PRIMARY KEY, store_id bigint NOT NULL, @@ -426,28 +335,13 @@ CREATE TABLE IF NOT EXISTS billiards.dim_product_price_scd ( REFERENCES billiards.dim_product(store_id, product_id) ON DELETE CASCADE, CONSTRAINT ck_dpps_range CHECK ( valid_from < COALESCE(valid_to, '9999-12-31 00:00:00+00'::timestamptz) - ), - CONSTRAINT uq_dpps_current UNIQUE (store_id, product_id) WHERE (is_current) + ) ); -COMMENT ON TABLE billiards.dim_product_price_scd IS '商品维度价格 SCD(记录价格/分类变动历史)'; -COMMENT ON COLUMN billiards.dim_product_price_scd.product_scd_id IS '价格 SCD 主键'; -COMMENT ON COLUMN billiards.dim_product_price_scd.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.dim_product_price_scd.product_id IS '商品 ID'; -COMMENT ON COLUMN billiards.dim_product_price_scd.product_name IS '商品名称(冗余,方便简单查询)'; -COMMENT ON COLUMN billiards.dim_product_price_scd.category_id IS '一级分类 ID'; -COMMENT ON COLUMN billiards.dim_product_price_scd.category_name IS '一级分类名称'; -COMMENT ON COLUMN billiards.dim_product_price_scd.second_category_id IS '二级分类 ID'; -COMMENT ON COLUMN billiards.dim_product_price_scd.cost_price IS '成本价'; -COMMENT ON COLUMN billiards.dim_product_price_scd.sale_price IS '销售价'; -COMMENT ON COLUMN billiards.dim_product_price_scd.allow_discount IS '是否允许折扣'; -COMMENT ON COLUMN billiards.dim_product_price_scd.status IS '商品状态 (上/下架)'; -COMMENT ON COLUMN billiards.dim_product_price_scd.valid_from IS '生效时间'; -COMMENT ON COLUMN billiards.dim_product_price_scd.valid_to IS '失效时间,NULL 表示当前版本'; -COMMENT ON COLUMN billiards.dim_product_price_scd.is_current IS '当前有效版本标记'; -COMMENT ON COLUMN billiards.dim_product_price_scd.raw_data IS '原始 JSON 快照'; +-- Create partial unique index for current records only +CREATE UNIQUE INDEX uq_dpps_current ON billiards.dim_product_price_scd (store_id, product_id) WHERE is_current; -CREATE VIEW IF NOT EXISTS billiards.dim_product_price_current AS +CREATE VIEW billiards.dim_product_price_current AS SELECT product_scd_id, store_id, product_id, @@ -465,22 +359,6 @@ SELECT product_scd_id, FROM billiards.dim_product_price_scd WHERE is_current; -COMMENT ON VIEW billiards.dim_product_price_current IS '商品价格 SCD 当前有效版本视图'; -COMMENT ON COLUMN billiards.dim_product_price_current.product_scd_id IS '价格 SCD 主键'; -COMMENT ON COLUMN billiards.dim_product_price_current.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.dim_product_price_current.product_id IS '商品 ID'; -COMMENT ON COLUMN billiards.dim_product_price_current.product_name IS '商品名称'; -COMMENT ON COLUMN billiards.dim_product_price_current.category_id IS '一级分类 ID'; -COMMENT ON COLUMN billiards.dim_product_price_current.category_name IS '一级分类名称'; -COMMENT ON COLUMN billiards.dim_product_price_current.second_category_id IS '二级分类 ID'; -COMMENT ON COLUMN billiards.dim_product_price_current.cost_price IS '成本价'; -COMMENT ON COLUMN billiards.dim_product_price_current.sale_price IS '销售价'; -COMMENT ON COLUMN billiards.dim_product_price_current.allow_discount IS '是否允许折扣'; -COMMENT ON COLUMN billiards.dim_product_price_current.status IS '商品状态'; -COMMENT ON COLUMN billiards.dim_product_price_current.valid_from IS '生效时间'; -COMMENT ON COLUMN billiards.dim_product_price_current.valid_to IS '失效时间'; -COMMENT ON COLUMN billiards.dim_product_price_current.raw_data IS '原始 JSON'; - CREATE TABLE IF NOT EXISTS billiards.dim_table ( store_id bigint NOT NULL, table_id bigint NOT NULL, @@ -504,31 +382,104 @@ CREATE TABLE IF NOT EXISTS billiards.dim_table ( PRIMARY KEY (store_id, table_id) ); -COMMENT ON TABLE billiards.dim_table IS '台桌档案维度(Table/GetSiteTables)'; -COMMENT ON COLUMN billiards.dim_table.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.dim_table.table_id IS '台桌 ID (id)'; -COMMENT ON COLUMN billiards.dim_table.site_id IS '场馆/站点 ID (site_id)'; -COMMENT ON COLUMN billiards.dim_table.area_id IS '区域 ID (site_table_area_id)'; -COMMENT ON COLUMN billiards.dim_table.area_name IS '区域名称 (areaName)'; -COMMENT ON COLUMN billiards.dim_table.table_name IS '台桌名称 (table_name)'; -COMMENT ON COLUMN billiards.dim_table.table_price IS '台费单价 (table_price)'; -COMMENT ON COLUMN billiards.dim_table.table_status IS '台桌状态代码 (table_status)'; -COMMENT ON COLUMN billiards.dim_table.table_status_name IS '台桌状态名称 (tableStatusName)'; -COMMENT ON COLUMN billiards.dim_table.light_status IS '灯光状态 (light_status)'; -COMMENT ON COLUMN billiards.dim_table.is_rest_area IS '是否休息区 (is_rest_area)'; -COMMENT ON COLUMN billiards.dim_table.show_status IS '是否展示 (show_status)'; -COMMENT ON COLUMN billiards.dim_table.virtual_table IS '是否虚拟台 (virtual_table)'; -COMMENT ON COLUMN billiards.dim_table.charge_free IS '是否免收费 (charge_free)'; -COMMENT ON COLUMN billiards.dim_table.only_allow_groupon IS '是否仅限团购 (only_allow_groupon)'; -COMMENT ON COLUMN billiards.dim_table.is_online_reservation IS '是否可线上预约 (is_online_reservation)'; -COMMENT ON COLUMN billiards.dim_table.created_time IS '创建时间 (create_time)'; -COMMENT ON COLUMN billiards.dim_table.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.dim_table.updated_at IS 'ETL 更新时间'; - -- ========================= --- Billiards fact tables +-- 3. Billiards Fact Tables -- ========================= +-- 3.1 Order Fact (Header) +CREATE TABLE IF NOT EXISTS billiards.fact_order ( + store_id bigint NOT NULL, + order_settle_id bigint NOT NULL, -- Settle ID is the main key for payment + order_trade_no bigint, -- Can be one of many if merged, but usually 1-1 main + order_no varchar(100), + member_id bigint, + pay_time timestamptz, + total_amount numeric(14,4), -- Original price + pay_amount numeric(14,4), -- Actual paid + discount_amount numeric(14,4), + coupon_amount numeric(14,4), + status varchar(50), + cashier_name varchar(100), + remark text, + raw_data jsonb, + created_at timestamptz DEFAULT now(), + PRIMARY KEY (store_id, order_settle_id) +); + +-- 3.2 Order Items (Goods) +CREATE TABLE IF NOT EXISTS billiards.fact_order_goods ( + store_id bigint NOT NULL, + order_goods_id bigint NOT NULL, -- orderGoodsLedgerId + order_settle_id bigint NOT NULL, + order_trade_no bigint, + goods_id bigint, + goods_name varchar(200), + quantity numeric(10,2), + unit_price numeric(14,4), + total_amount numeric(14,4), -- quantity * price + pay_amount numeric(14,4), -- After discount + created_at timestamptz DEFAULT now(), + PRIMARY KEY (store_id, order_goods_id) +); + +-- 3.3 Table Usage Fact +CREATE TABLE IF NOT EXISTS billiards.fact_table_usage ( + store_id bigint NOT NULL, + order_ledger_id bigint NOT NULL, -- orderTableLedgerId + order_settle_id bigint NOT NULL, + table_id bigint, + table_name varchar(100), + start_time timestamptz, + end_time timestamptz, + duration_minutes integer, + total_amount numeric(14,4), + pay_amount numeric(14,4), + created_at timestamptz DEFAULT now(), + PRIMARY KEY (store_id, order_ledger_id) +); + +-- 3.4 Assistant Service Fact +CREATE TABLE IF NOT EXISTS billiards.fact_assistant_service ( + store_id bigint NOT NULL, + ledger_id bigint NOT NULL, -- orderAssistantLedgerId + order_settle_id bigint NOT NULL, + assistant_id bigint, + assistant_name varchar(100), + service_type varchar(50), -- e.g., "Play with" + start_time timestamptz, + end_time timestamptz, + duration_minutes integer, + total_amount numeric(14,4), + pay_amount numeric(14,4), + created_at timestamptz DEFAULT now(), + PRIMARY KEY (store_id, ledger_id) +); + +-- 3.5 Payment Fact +CREATE TABLE IF NOT EXISTS billiards.fact_payment ( + store_id bigint NOT NULL, + pay_id bigint NOT NULL, + site_id bigint, + tenant_id bigint, + order_settle_id bigint, + order_trade_no bigint, + relate_type varchar(50), + relate_id bigint, + create_time timestamptz, + pay_time timestamptz, + pay_amount numeric(14,4), + fee_amount numeric(14,4), + discount_amount numeric(14,4), + payment_method varchar(50), -- e.g., 'WeChat', 'Cash', 'Balance' + online_pay_channel varchar(50), + pay_terminal varchar(30), + pay_status varchar(30), + raw_data jsonb, + updated_at timestamptz DEFAULT now(), + PRIMARY KEY (store_id, pay_id) +); + +-- 3.6 Legacy/Other Facts (Preserved) CREATE TABLE IF NOT EXISTS billiards.fact_assistant_abolish ( store_id bigint NOT NULL, abolish_id bigint NOT NULL, @@ -547,22 +498,6 @@ CREATE TABLE IF NOT EXISTS billiards.fact_assistant_abolish ( PRIMARY KEY (store_id, abolish_id) ); -COMMENT ON TABLE billiards.fact_assistant_abolish IS '助教作废/撤单流水(Assistant/AbolishList)'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.abolish_id IS '作废记录 ID (id)'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.table_id IS '台桌 ID (tableId)'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.table_name IS '台桌名称'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.table_area_id IS '台桌区域 ID'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.table_area IS '台桌区域名称'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.assistant_no IS '助教编号 (assistantOn)'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.assistant_name IS '助教姓名'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.charge_minutes IS '计费分钟 (pdChargeMinutes)'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.abolish_amount IS '作废金额 (assistantAbolishAmount)'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.create_time IS '创建时间 (createTime)'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.trash_reason IS '作废原因 (trashReason)'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_assistant_abolish.updated_at IS 'ETL 更新时间'; - CREATE TABLE IF NOT EXISTS billiards.fact_assistant_ledger ( store_id bigint NOT NULL, ledger_id bigint NOT NULL, @@ -605,46 +540,6 @@ CREATE TABLE IF NOT EXISTS billiards.fact_assistant_ledger ( PRIMARY KEY (store_id, ledger_id) ); -COMMENT ON TABLE billiards.fact_assistant_ledger IS '助教陪打计费流水(Assistant/LedgerList)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_id IS '流水 ID (id)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.assistant_no IS '助教编号'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.assistant_name IS '助教姓名'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.nickname IS '昵称'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.level_name IS '等级名称'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.table_name IS '服务台桌名称'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_unit_price IS '计费单价 (ledger_unit_price)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_count IS '计费数量 (ledger_count)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_amount IS '计费金额 (ledger_amount)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.projected_income IS '预计收益 (projected_income)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.service_money IS '服务费 (service_money)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.member_discount_amount IS '会员折扣金额'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.manual_discount_amount IS '手动折扣金额'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.coupon_deduct_money IS '券抵扣金额'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.order_trade_no IS '订单交易号 (order_trade_no)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.order_settle_id IS '结算单 ID (order_settle_id)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.operator_id IS '操作人 ID'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.operator_name IS '操作人姓名'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.assistant_team_id IS '助教团队 ID'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.assistant_level IS '助教等级'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.site_table_id IS '台桌 ID'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.order_assistant_id IS '订单内助教 ID'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.site_assistant_id IS '场馆助教 ID'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.user_id IS '会员/顾客 ID'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_start_time IS '计费开始时间'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_end_time IS '计费结束时间'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.start_use_time IS '开始使用时间'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.last_use_time IS '最后使用时间'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.income_seconds IS '计费秒数 (income_seconds)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.real_use_seconds IS '实际使用秒数 (real_use_seconds)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.is_trash IS '是否作废 (is_trash)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.trash_reason IS '作废原因 (trash_reason)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.is_confirm IS '是否确认 (is_confirm)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.ledger_status IS '状态 (ledger_status)'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.create_time IS '创建时间'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_assistant_ledger.updated_at IS 'ETL 更新时间'; - CREATE TABLE IF NOT EXISTS billiards.fact_inventory_change ( store_id bigint NOT NULL, change_id bigint NOT NULL, @@ -666,105 +561,6 @@ CREATE TABLE IF NOT EXISTS billiards.fact_inventory_change ( PRIMARY KEY (store_id, change_id) ); -COMMENT ON TABLE billiards.fact_inventory_change IS '库存变更流水(Inventory/ChangeList)'; -COMMENT ON COLUMN billiards.fact_inventory_change.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_inventory_change.change_id IS '库存变动 ID (siteGoodsStockId)'; -COMMENT ON COLUMN billiards.fact_inventory_change.site_goods_id IS '门店商品 ID (siteGoodsId)'; -COMMENT ON COLUMN billiards.fact_inventory_change.stock_type IS '变动类型 (stockType)'; -COMMENT ON COLUMN billiards.fact_inventory_change.goods_name IS '商品名称 (goodsName)'; -COMMENT ON COLUMN billiards.fact_inventory_change.change_time IS '变动时间 (createTime)'; -COMMENT ON COLUMN billiards.fact_inventory_change.start_qty IS '变动前数量 (startNum)'; -COMMENT ON COLUMN billiards.fact_inventory_change.end_qty IS '变动后数量 (endNum)'; -COMMENT ON COLUMN billiards.fact_inventory_change.change_qty IS '变化数量 (changeNum)'; -COMMENT ON COLUMN billiards.fact_inventory_change.unit IS '单位 (unit)'; -COMMENT ON COLUMN billiards.fact_inventory_change.price IS '单价 (price)'; -COMMENT ON COLUMN billiards.fact_inventory_change.operator_name IS '操作人 (operatorName)'; -COMMENT ON COLUMN billiards.fact_inventory_change.remark IS '备注 (remark)'; -COMMENT ON COLUMN billiards.fact_inventory_change.goods_category_id IS '一级分类 ID'; -COMMENT ON COLUMN billiards.fact_inventory_change.goods_second_category_id IS '二级分类 ID'; -COMMENT ON COLUMN billiards.fact_inventory_change.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_inventory_change.updated_at IS 'ETL 更新时间'; - -CREATE TABLE IF NOT EXISTS billiards.fact_order ( - store_id bigint NOT NULL, - order_id bigint NOT NULL, - order_no varchar(64), - member_id bigint, - table_id bigint, - order_time timestamptz, - end_time timestamptz, - total_amount numeric(14,4), - discount_amount numeric(14,4), - final_amount numeric(14,4), - pay_status varchar(30), - order_status varchar(30), - remark text, - raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (store_id, order_id) -); - -COMMENT ON TABLE billiards.fact_order IS '订单事实表(/order/list 返回的结账记录)'; -COMMENT ON COLUMN billiards.fact_order.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_order.order_id IS '订单 ID (orderId)'; -COMMENT ON COLUMN billiards.fact_order.order_no IS '订单编号 (orderNo)'; -COMMENT ON COLUMN billiards.fact_order.member_id IS '会员 ID (memberId)'; -COMMENT ON COLUMN billiards.fact_order.table_id IS '台桌 ID (tableId)'; -COMMENT ON COLUMN billiards.fact_order.order_time IS '开单时间/开始时间 (orderTime)'; -COMMENT ON COLUMN billiards.fact_order.end_time IS '结账时间 (endTime)'; -COMMENT ON COLUMN billiards.fact_order.total_amount IS '订单总金额 (totalAmount)'; -COMMENT ON COLUMN billiards.fact_order.discount_amount IS '折扣金额 (discountAmount)'; -COMMENT ON COLUMN billiards.fact_order.final_amount IS '应付金额 (finalAmount)'; -COMMENT ON COLUMN billiards.fact_order.pay_status IS '支付状态 (payStatus)'; -COMMENT ON COLUMN billiards.fact_order.order_status IS '订单状态 (orderStatus)'; -COMMENT ON COLUMN billiards.fact_order.remark IS '备注 (remark)'; -COMMENT ON COLUMN billiards.fact_order.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_order.updated_at IS 'ETL 更新时间'; - -CREATE TABLE IF NOT EXISTS billiards.fact_payment ( - store_id bigint NOT NULL, - pay_id bigint NOT NULL, - order_id bigint, - order_settle_id bigint, - relate_type varchar(50), - relate_id bigint, - site_id bigint, - tenant_id bigint, - pay_time timestamptz, - create_time timestamptz, - pay_amount numeric(14,4), - pay_type varchar(50), - payment_method varchar(50), - online_pay_channel varchar(50), - pay_status varchar(30), - pay_terminal varchar(50), - remark text, - raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (store_id, pay_id) -); - -COMMENT ON TABLE billiards.fact_payment IS '支付流水(pay/records)'; -COMMENT ON COLUMN billiards.fact_payment.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_payment.pay_id IS '支付记录 ID (payId)'; -COMMENT ON COLUMN billiards.fact_payment.order_id IS '旧版订单 ID (orderId),保留兼容'; -COMMENT ON COLUMN billiards.fact_payment.order_settle_id IS '结算单 ID (relate_id 当 relate_type=2)'; -COMMENT ON COLUMN billiards.fact_payment.relate_type IS '关联类型 (relate_type)'; -COMMENT ON COLUMN billiards.fact_payment.relate_id IS '关联 ID (relate_id)'; -COMMENT ON COLUMN billiards.fact_payment.site_id IS '门店 ID(冗余自 payload)'; -COMMENT ON COLUMN billiards.fact_payment.tenant_id IS '租户 ID'; -COMMENT ON COLUMN billiards.fact_payment.pay_time IS '支付时间 (payTime)'; -COMMENT ON COLUMN billiards.fact_payment.create_time IS '创建时间 (create_time)'; -COMMENT ON COLUMN billiards.fact_payment.pay_amount IS '支付金额 (payAmount)'; -COMMENT ON COLUMN billiards.fact_payment.pay_type IS '支付渠道 (payType)'; -COMMENT ON COLUMN billiards.fact_payment.payment_method IS '支付方式 (payment_method)'; -COMMENT ON COLUMN billiards.fact_payment.online_pay_channel IS '线上支付渠道 (online_pay_channel)'; -COMMENT ON COLUMN billiards.fact_payment.pay_status IS '支付状态 (payStatus)'; -COMMENT ON COLUMN billiards.fact_payment.pay_terminal IS '支付终端 (pay_terminal)'; -COMMENT ON COLUMN billiards.fact_payment.remark IS '备注'; -COMMENT ON COLUMN billiards.fact_payment.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_payment.updated_at IS 'ETL 更新时间'; - CREATE TABLE IF NOT EXISTS billiards.fact_refund ( store_id bigint NOT NULL, refund_id bigint NOT NULL, @@ -778,327 +574,90 @@ CREATE TABLE IF NOT EXISTS billiards.fact_refund ( relate_id bigint, payment_method varchar(50), refund_amount numeric(14,4), - action_type varchar(30), - pay_terminal varchar(50), - operator_id bigint, - channel_pay_no varchar(100), - channel_fee numeric(14,4), - is_delete integer, - member_id bigint, - member_card_id bigint, + refund_reason text, raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, updated_at timestamptz NOT NULL DEFAULT now(), PRIMARY KEY (store_id, refund_id) ); -COMMENT ON TABLE billiards.fact_refund IS '退款流水(Pay/RefundList)'; -COMMENT ON COLUMN billiards.fact_refund.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_refund.refund_id IS '退款记录 ID (id)'; -COMMENT ON COLUMN billiards.fact_refund.site_id IS '场地 ID (site_id)'; -COMMENT ON COLUMN billiards.fact_refund.tenant_id IS '租户 ID (tenant_id)'; -COMMENT ON COLUMN billiards.fact_refund.pay_amount IS '原支付金额 (pay_amount)'; -COMMENT ON COLUMN billiards.fact_refund.pay_status IS '支付状态 (pay_status)'; -COMMENT ON COLUMN billiards.fact_refund.pay_time IS '支付时间 (pay_time)'; -COMMENT ON COLUMN billiards.fact_refund.create_time IS '创建时间 (create_time)'; -COMMENT ON COLUMN billiards.fact_refund.relate_type IS '关联对象类型 (relate_type)'; -COMMENT ON COLUMN billiards.fact_refund.relate_id IS '关联对象 ID (relate_id)'; -COMMENT ON COLUMN billiards.fact_refund.payment_method IS '支付方式 (payment_method)'; -COMMENT ON COLUMN billiards.fact_refund.refund_amount IS '退款金额 (refund_amount)'; -COMMENT ON COLUMN billiards.fact_refund.action_type IS '操作类型 (action_type)'; -COMMENT ON COLUMN billiards.fact_refund.pay_terminal IS '终端类型 (pay_terminal)'; -COMMENT ON COLUMN billiards.fact_refund.operator_id IS '操作人 ID (operator_id)'; -COMMENT ON COLUMN billiards.fact_refund.channel_pay_no IS '渠道支付单号 (channel_pay_no)'; -COMMENT ON COLUMN billiards.fact_refund.channel_fee IS '渠道手续费 (channel_fee)'; -COMMENT ON COLUMN billiards.fact_refund.is_delete IS '删除标记 (is_delete)'; -COMMENT ON COLUMN billiards.fact_refund.member_id IS '会员 ID (member_id)'; -COMMENT ON COLUMN billiards.fact_refund.member_card_id IS '会员卡 ID (member_card_id)'; -COMMENT ON COLUMN billiards.fact_refund.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_refund.updated_at IS 'ETL 更新时间'; - -CREATE TABLE IF NOT EXISTS billiards.fact_table_discount ( - store_id bigint NOT NULL, - discount_id bigint NOT NULL, - adjust_type varchar(50), - applicant_id bigint, - applicant_name varchar(100), - operator_id bigint, - operator_name varchar(100), - ledger_amount numeric(14,4), - ledger_count numeric(14,4), - ledger_name varchar(100), - ledger_status varchar(30), - order_settle_id bigint, - order_trade_no bigint, - site_table_id bigint, - table_area_id bigint, - table_area_name varchar(100), - create_time timestamptz, - is_delete integer, - raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (store_id, discount_id) -); - -COMMENT ON TABLE billiards.fact_table_discount IS '台费调价/折扣记录(Table/AdjustList)'; -COMMENT ON COLUMN billiards.fact_table_discount.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_table_discount.discount_id IS '调价记录 ID (id)'; -COMMENT ON COLUMN billiards.fact_table_discount.adjust_type IS '调价类型 (adjust_type/adjustType)'; -COMMENT ON COLUMN billiards.fact_table_discount.applicant_id IS '申请人 ID (applicant_id)'; -COMMENT ON COLUMN billiards.fact_table_discount.applicant_name IS '申请人姓名 (applicant_name)'; -COMMENT ON COLUMN billiards.fact_table_discount.operator_id IS '操作人 ID (operator_id)'; -COMMENT ON COLUMN billiards.fact_table_discount.operator_name IS '操作人姓名 (operator_name)'; -COMMENT ON COLUMN billiards.fact_table_discount.ledger_amount IS '台费金额调整 (ledger_amount)'; -COMMENT ON COLUMN billiards.fact_table_discount.ledger_count IS '台费数量调整 (ledger_count)'; -COMMENT ON COLUMN billiards.fact_table_discount.ledger_name IS '科目名称 (ledger_name)'; -COMMENT ON COLUMN billiards.fact_table_discount.ledger_status IS '记录状态 (ledger_status)'; -COMMENT ON COLUMN billiards.fact_table_discount.order_settle_id IS '结算单 ID (order_settle_id)'; -COMMENT ON COLUMN billiards.fact_table_discount.order_trade_no IS '订单号 (order_trade_no)'; -COMMENT ON COLUMN billiards.fact_table_discount.site_table_id IS '台桌 ID (site_table_id)'; -COMMENT ON COLUMN billiards.fact_table_discount.table_area_id IS '台桌区域 ID'; -COMMENT ON COLUMN billiards.fact_table_discount.table_area_name IS '台桌区域名称'; -COMMENT ON COLUMN billiards.fact_table_discount.create_time IS '创建时间 (create_time)'; -COMMENT ON COLUMN billiards.fact_table_discount.is_delete IS '删除标记 (is_delete)'; -COMMENT ON COLUMN billiards.fact_table_discount.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_table_discount.updated_at IS 'ETL 更新时间'; - -CREATE TABLE IF NOT EXISTS billiards.fact_topup ( - store_id bigint NOT NULL, - topup_id bigint NOT NULL, - member_id bigint, - member_name varchar(100), - member_phone varchar(30), - card_id bigint, - card_type_name varchar(100), - pay_amount numeric(14,4), - consume_money numeric(14,4), - settle_status varchar(30), - settle_type varchar(30), - settle_name varchar(100), - settle_relate_id bigint, - pay_time timestamptz, - create_time timestamptz, - operator_id bigint, - operator_name varchar(100), - payment_method varchar(50), - refund_amount numeric(14,4), - cash_amount numeric(14,4), - card_amount numeric(14,4), - balance_amount numeric(14,4), - online_amount numeric(14,4), - rounding_amount numeric(14,4), - adjust_amount numeric(14,4), - goods_money numeric(14,4), - table_charge_money numeric(14,4), - service_money numeric(14,4), - coupon_amount numeric(14,4), - order_remark text, - raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (store_id, topup_id) -); - -COMMENT ON TABLE billiards.fact_topup IS '储值充值结算流水(Topup/SettleList)'; -COMMENT ON COLUMN billiards.fact_topup.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_topup.topup_id IS '充值记录 ID (id)'; -COMMENT ON COLUMN billiards.fact_topup.member_id IS '会员 ID (memberId)'; -COMMENT ON COLUMN billiards.fact_topup.member_name IS '会员姓名 (memberName)'; -COMMENT ON COLUMN billiards.fact_topup.member_phone IS '会员手机号 (memberPhone)'; -COMMENT ON COLUMN billiards.fact_topup.card_id IS '会员卡 ID (tenantMemberCardId)'; -COMMENT ON COLUMN billiards.fact_topup.card_type_name IS '会员卡类型名称 (memberCardTypeName)'; -COMMENT ON COLUMN billiards.fact_topup.pay_amount IS '支付金额 (payAmount)'; -COMMENT ON COLUMN billiards.fact_topup.consume_money IS '可消费金额/储值 (consumeMoney)'; -COMMENT ON COLUMN billiards.fact_topup.settle_status IS '结算状态 (settleStatus)'; -COMMENT ON COLUMN billiards.fact_topup.settle_type IS '结算方式 (settleType)'; -COMMENT ON COLUMN billiards.fact_topup.settle_name IS '结算名称 (settleName)'; -COMMENT ON COLUMN billiards.fact_topup.settle_relate_id IS '结算关联 ID (settleRelateId)'; -COMMENT ON COLUMN billiards.fact_topup.pay_time IS '支付时间 (payTime)'; -COMMENT ON COLUMN billiards.fact_topup.create_time IS '创建时间 (createTime)'; -COMMENT ON COLUMN billiards.fact_topup.operator_id IS '操作人 ID (operatorId)'; -COMMENT ON COLUMN billiards.fact_topup.operator_name IS '操作人姓名 (operatorName)'; -COMMENT ON COLUMN billiards.fact_topup.payment_method IS '支付方式 (paymentMethod)'; -COMMENT ON COLUMN billiards.fact_topup.refund_amount IS '退款金额 (refundAmount)'; -COMMENT ON COLUMN billiards.fact_topup.cash_amount IS '现金金额 (cashAmount)'; -COMMENT ON COLUMN billiards.fact_topup.card_amount IS '银行卡金额 (cardAmount)'; -COMMENT ON COLUMN billiards.fact_topup.balance_amount IS '余额抵扣金额 (balanceAmount)'; -COMMENT ON COLUMN billiards.fact_topup.online_amount IS '在线支付金额 (onlineAmount)'; -COMMENT ON COLUMN billiards.fact_topup.rounding_amount IS '抹零金额 (roundingAmount)'; -COMMENT ON COLUMN billiards.fact_topup.adjust_amount IS '调整金额 (adjustAmount)'; -COMMENT ON COLUMN billiards.fact_topup.goods_money IS '商品金额 (goodsMoney)'; -COMMENT ON COLUMN billiards.fact_topup.table_charge_money IS '台费金额 (tableChargeMoney)'; -COMMENT ON COLUMN billiards.fact_topup.service_money IS '服务费 (serviceMoney)'; -COMMENT ON COLUMN billiards.fact_topup.coupon_amount IS '券抵扣金额 (couponAmount)'; -COMMENT ON COLUMN billiards.fact_topup.order_remark IS '备注 (orderRemark)'; -COMMENT ON COLUMN billiards.fact_topup.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_topup.updated_at IS 'ETL 更新时间'; - -CREATE TABLE IF NOT EXISTS billiards.fact_coupon_usage ( - store_id bigint NOT NULL, - usage_id bigint NOT NULL, - coupon_code varchar(100), - coupon_channel varchar(50), - coupon_name varchar(200), - sale_price numeric(14,4), - coupon_money numeric(14,4), - coupon_free_time integer, - use_status varchar(30), - create_time timestamptz, - consume_time timestamptz, - operator_id bigint, - operator_name varchar(100), - table_id bigint, - site_order_id bigint, - group_package_id bigint, - coupon_remark text, - deal_id varchar(100), - certificate_id varchar(100), - verify_id varchar(100), - is_delete integer, - raw_data jsonb NOT NULL DEFAULT '{}'::jsonb, - updated_at timestamptz NOT NULL DEFAULT now(), - PRIMARY KEY (store_id, usage_id) -); - -COMMENT ON TABLE billiards.fact_coupon_usage IS '平台券验券/核销流水(Coupon/UsageList)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.store_id IS '门店 ID'; -COMMENT ON COLUMN billiards.fact_coupon_usage.usage_id IS '核销记录 ID (id)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_code IS '券码 (coupon_code)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_channel IS '券渠道 (coupon_channel)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_name IS '券名称 (coupon_name)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.sale_price IS '售卖价 (sale_price)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_money IS '券面额 (coupon_money)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_free_time IS '赠送/免单时长 (coupon_free_time)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.use_status IS '核销状态 (use_status)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.create_time IS '创建时间 (create_time)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.consume_time IS '核销时间 (consume_time)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.operator_id IS '操作人 ID (operator_id)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.operator_name IS '操作人姓名 (operator_name)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.table_id IS '台桌 ID (table_id)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.site_order_id IS '场馆订单 ID (site_order_id)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.group_package_id IS '套餐 ID (group_package_id)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.coupon_remark IS '备注 (coupon_remark)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.deal_id IS '团购/渠道 deal_id'; -COMMENT ON COLUMN billiards.fact_coupon_usage.certificate_id IS '凭证号 (certificate_id)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.verify_id IS '核销流水号 (verify_id)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.is_delete IS '删除标记 (is_delete)'; -COMMENT ON COLUMN billiards.fact_coupon_usage.raw_data IS '原始 JSON'; -COMMENT ON COLUMN billiards.fact_coupon_usage.updated_at IS 'ETL 更新时间'; - -- ========================= --- ETL admin tables +-- 4. DWS Layer (Data Warehouse Summary) +-- Views for Analysis & AI -- ========================= -CREATE TYPE IF NOT EXISTS etl_admin.run_status_enum AS ENUM ('SUCC', 'FAIL', 'PARTIAL'); +-- 4.1 Sales Detail View (The "AI-Friendly" Wide Table) +-- Unifies Goods, Table Fees, and Assistant Services into a single stream of "Sales Items" +CREATE OR REPLACE VIEW billiards_dws.dws_sales_detail AS +SELECT + 'GOODS' as item_type, + g.store_id, + g.order_settle_id, + o.pay_time, + g.goods_name as item_name, + g.quantity, + g.pay_amount as amount, + o.cashier_name +FROM billiards.fact_order_goods g +JOIN billiards.fact_order o ON g.store_id = o.store_id AND g.order_settle_id = o.order_settle_id -COMMENT ON TYPE etl_admin.run_status_enum IS 'ETL 运行状态枚举(成功/失败/部分成功)'; +UNION ALL -CREATE TABLE IF NOT EXISTS etl_admin.etl_task ( - task_id bigserial PRIMARY KEY, - store_id bigint NOT NULL, - task_code varchar(50) NOT NULL, - description text, - enabled boolean NOT NULL DEFAULT true, - cursor_field varchar(100), - window_minutes_default integer DEFAULT 60, - overlap_seconds integer DEFAULT 120, - page_size integer DEFAULT 200, - retry_max integer DEFAULT 3, - params jsonb NOT NULL DEFAULT '{}'::jsonb, - created_at timestamptz NOT NULL DEFAULT now(), - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE (store_id, task_code) -); +SELECT + 'TABLE' as item_type, + t.store_id, + t.order_settle_id, + o.pay_time, + t.table_name || ' (' || t.duration_minutes || ' mins)' as item_name, + 1 as quantity, + t.pay_amount as amount, + o.cashier_name +FROM billiards.fact_table_usage t +JOIN billiards.fact_order o ON t.store_id = o.store_id AND t.order_settle_id = o.order_settle_id -COMMENT ON TABLE etl_admin.etl_task IS 'ETL 任务配置(启停、窗口、分页参数等)'; -COMMENT ON COLUMN etl_admin.etl_task.task_id IS '任务 ID(自增主键)'; -COMMENT ON COLUMN etl_admin.etl_task.store_id IS '门店 ID(任务所属门店)'; -COMMENT ON COLUMN etl_admin.etl_task.task_code IS '任务代码(如 PRODUCTS、ORDERS)'; -COMMENT ON COLUMN etl_admin.etl_task.description IS '任务描述'; -COMMENT ON COLUMN etl_admin.etl_task.enabled IS '是否启用'; -COMMENT ON COLUMN etl_admin.etl_task.cursor_field IS '游标使用的字段(如时间或 ID)'; -COMMENT ON COLUMN etl_admin.etl_task.window_minutes_default IS '默认抓取窗口(分钟)'; -COMMENT ON COLUMN etl_admin.etl_task.overlap_seconds IS '窗口重叠秒数,用于防重'; -COMMENT ON COLUMN etl_admin.etl_task.page_size IS 'API 分页大小'; -COMMENT ON COLUMN etl_admin.etl_task.retry_max IS 'API 最大重试次数'; -COMMENT ON COLUMN etl_admin.etl_task.params IS '额外参数 (JSON)'; -COMMENT ON COLUMN etl_admin.etl_task.created_at IS '创建时间'; -COMMENT ON COLUMN etl_admin.etl_task.updated_at IS '更新时间'; +UNION ALL -CREATE TABLE IF NOT EXISTS etl_admin.etl_cursor ( - cursor_id bigserial PRIMARY KEY, - task_id bigint NOT NULL REFERENCES etl_admin.etl_task(task_id) ON DELETE CASCADE, - store_id bigint NOT NULL, - last_start timestamptz, - last_end timestamptz, - last_id bigint, - extra jsonb NOT NULL DEFAULT '{}'::jsonb, - last_run_id bigint, - updated_at timestamptz NOT NULL DEFAULT now(), - UNIQUE (task_id, store_id) -); - -COMMENT ON TABLE etl_admin.etl_cursor IS 'ETL 游标存储(记录每个任务的上次窗口与 ID)'; -COMMENT ON COLUMN etl_admin.etl_cursor.cursor_id IS '游标主键'; -COMMENT ON COLUMN etl_admin.etl_cursor.task_id IS '关联任务 ID'; -COMMENT ON COLUMN etl_admin.etl_cursor.store_id IS '门店 ID'; -COMMENT ON COLUMN etl_admin.etl_cursor.last_start IS '上次窗口起始时间'; -COMMENT ON COLUMN etl_admin.etl_cursor.last_end IS '上次窗口结束时间'; -COMMENT ON COLUMN etl_admin.etl_cursor.last_id IS '上次处理的 ID(适用于基于 ID 的翻页)'; -COMMENT ON COLUMN etl_admin.etl_cursor.extra IS '额外游标信息 (JSON)'; -COMMENT ON COLUMN etl_admin.etl_cursor.last_run_id IS '最后一次运行 run_id'; -COMMENT ON COLUMN etl_admin.etl_cursor.updated_at IS '更新时间'; - -CREATE TABLE IF NOT EXISTS etl_admin.etl_run ( - run_id bigserial PRIMARY KEY, - run_uuid varchar(64) NOT NULL, - task_id bigint NOT NULL REFERENCES etl_admin.etl_task(task_id) ON DELETE CASCADE, - store_id bigint NOT NULL, - status etl_admin.run_status_enum NOT NULL DEFAULT 'SUCC', - started_at timestamptz NOT NULL DEFAULT now(), - ended_at timestamptz, - window_start timestamptz, - window_end timestamptz, - window_minutes integer, - overlap_seconds integer, - fetched_count integer DEFAULT 0, - loaded_count integer DEFAULT 0, - updated_count integer DEFAULT 0, - skipped_count integer DEFAULT 0, - error_count integer DEFAULT 0, - unknown_fields integer DEFAULT 0, - export_dir text, - log_path text, - request_params jsonb NOT NULL DEFAULT '{}'::jsonb, - manifest jsonb NOT NULL DEFAULT '{}'::jsonb, - error_message text, - extra jsonb NOT NULL DEFAULT '{}'::jsonb -); - -CREATE INDEX IF NOT EXISTS idx_etl_run_task ON etl_admin.etl_run(task_id, started_at DESC); -CREATE INDEX IF NOT EXISTS idx_etl_run_status ON etl_admin.etl_run(status); - -COMMENT ON TABLE etl_admin.etl_run IS 'ETL 运行记录(针对每次任务执行)'; -COMMENT ON COLUMN etl_admin.etl_run.run_id IS '运行记录 ID'; -COMMENT ON COLUMN etl_admin.etl_run.run_uuid IS '运行批次 UUID'; -COMMENT ON COLUMN etl_admin.etl_run.task_id IS '任务 ID'; -COMMENT ON COLUMN etl_admin.etl_run.store_id IS '门店 ID'; -COMMENT ON COLUMN etl_admin.etl_run.status IS '运行状态 (SUCC/FAIL/PARTIAL)'; -COMMENT ON COLUMN etl_admin.etl_run.started_at IS '开始时间'; -COMMENT ON COLUMN etl_admin.etl_run.ended_at IS '结束时间'; -COMMENT ON COLUMN etl_admin.etl_run.window_start IS '窗口起始时间'; -COMMENT ON COLUMN etl_admin.etl_run.window_end IS '窗口结束时间'; -COMMENT ON COLUMN etl_admin.etl_run.window_minutes IS '窗口长度(分钟)'; -COMMENT ON COLUMN etl_admin.etl_run.overlap_seconds IS '重叠秒数'; -COMMENT ON COLUMN etl_admin.etl_run.fetched_count IS '抓取条数'; -COMMENT ON COLUMN etl_admin.etl_run.loaded_count IS '插入条数'; -COMMENT ON COLUMN etl_admin.etl_run.updated_count IS '更新条数'; -COMMENT ON COLUMN etl_admin.etl_run.skipped_count IS '跳过条数'; -COMMENT ON COLUMN etl_admin.etl_run.error_count IS '错误条数'; -COMMENT ON COLUMN etl_admin.etl_run.unknown_fields IS '未知字段次数'; -COMMENT ON COLUMN etl_admin.etl_run.export_dir IS '导出目录'; -COMMENT ON COLUMN etl_admin.etl_run.log_path IS '日志文件路径'; -COMMENT ON COLUMN etl_admin.etl_run.request_params IS '本次请求参数 (JSON)'; -COMMENT ON COLUMN etl_admin.etl_run.manifest IS '导出 manifest (JSON)'; -COMMENT ON COLUMN etl_admin.etl_run.error_message IS '错误信息'; -COMMENT ON COLUMN etl_admin.etl_run.extra IS '额外信息 (JSON)'; +SELECT + 'ASSISTANT' as item_type, + a.store_id, + a.order_settle_id, + o.pay_time, + a.assistant_name || ' (' || a.duration_minutes || ' mins)' as item_name, + 1 as quantity, + a.pay_amount as amount, + o.cashier_name +FROM billiards.fact_assistant_service a +JOIN billiards.fact_order o ON a.store_id = o.store_id AND a.order_settle_id = o.order_settle_id; +-- 4.2 Daily Revenue View +CREATE OR REPLACE VIEW billiards_dws.dws_daily_revenue AS +SELECT + store_id, + DATE(pay_time) as report_date, + COUNT(DISTINCT order_settle_id) as total_orders, + SUM(amount) as total_revenue, + SUM(amount) FILTER (WHERE item_type = 'GOODS') as goods_revenue, + SUM(amount) FILTER (WHERE item_type = 'TABLE') as table_revenue, + SUM(amount) FILTER (WHERE item_type = 'ASSISTANT') as assistant_revenue +FROM billiards_dws.dws_sales_detail +GROUP BY store_id, DATE(pay_time); +-- 4.3 Order Detail Wide View (For detailed inspection) +CREATE OR REPLACE VIEW billiards_dws.dws_order_detail AS +SELECT + o.store_id, + o.order_settle_id, + o.order_no, + o.pay_time, + o.total_amount, + o.pay_amount, + o.cashier_name, + -- Payment pivot (approximate, assumes simple mapping) + COALESCE(SUM(p.pay_amount) FILTER (WHERE p.payment_method = '1'), 0) AS pay_cash, + COALESCE(SUM(p.pay_amount) FILTER (WHERE p.payment_method = '2'), 0) AS pay_balance, + COALESCE(SUM(p.pay_amount) FILTER (WHERE p.payment_method = '4'), 0) AS pay_wechat, + -- Content summary + (SELECT string_agg(goods_name || 'x' || quantity, '; ') FROM billiards.fact_order_goods WHERE order_settle_id = o.order_settle_id) AS goods_summary, + (SELECT string_agg(assistant_name, '; ') FROM billiards.fact_assistant_service WHERE order_settle_id = o.order_settle_id) AS assistant_summary +FROM billiards.fact_order o +LEFT JOIN billiards.fact_payment p ON o.order_settle_id = p.order_settle_id +GROUP BY o.store_id, o.order_settle_id, o.order_no, o.pay_time, o.total_amount, o.pay_amount, o.cashier_name; diff --git a/etl_billiards/scripts/test_db_connection.py b/etl_billiards/scripts/test_db_connection.py new file mode 100644 index 0000000..7bf4a67 --- /dev/null +++ b/etl_billiards/scripts/test_db_connection.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +"""Quick utility for validating PostgreSQL connectivity.""" +from __future__ import annotations + +import argparse +import os +import sys + +PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) +if PROJECT_ROOT not in sys.path: + sys.path.insert(0, PROJECT_ROOT) + +from database.connection import DatabaseConnection + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="PostgreSQL connectivity smoke test") + parser.add_argument("--dsn", help="Override TEST_DB_DSN / env value") + parser.add_argument( + "--query", + default="SELECT 1 AS ok", + help="Custom SQL to run after connection (default: SELECT 1 AS ok)", + ) + parser.add_argument( + "--timeout", + type=int, + default=5, + help="connect_timeout seconds passed to psycopg2 (default: 5)", + ) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + dsn = args.dsn or os.environ.get("TEST_DB_DSN") + if not dsn: + print("❌ 未提供 DSN,请通过 --dsn 或 TEST_DB_DSN 指定连接串", file=sys.stderr) + return 2 + + print(f"尝试连接: {dsn}") + try: + conn = DatabaseConnection(dsn, connect_timeout=args.timeout) + except Exception as exc: # pragma: no cover - diagnostic output + print("❌ 连接失败:", exc, file=sys.stderr) + return 1 + + try: + result = conn.query(args.query) + print("✅ 连接成功,查询结果:") + for row in result: + print(row) + conn.close() + return 0 + except Exception as exc: # pragma: no cover - diagnostic output + print("⚠️ 连接成功但执行查询失败:", exc, file=sys.stderr) + try: + conn.close() + finally: + return 3 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/etl_billiards/scripts/test_presets.py b/etl_billiards/scripts/test_presets.py index f216ae2..1825632 100644 --- a/etl_billiards/scripts/test_presets.py +++ b/etl_billiards/scripts/test_presets.py @@ -56,6 +56,7 @@ from typing import List RUN_TESTS_SCRIPT = os.path.join(os.path.dirname(__file__), "run_tests.py") # 默认自动运行的预置(可自定义顺序) + AUTO_RUN_PRESETS = ["offline_realdb"] PRESETS = { @@ -66,6 +67,15 @@ PRESETS = { "pytest_args": "-vv", "preset_meta": "在线模式,仅跑订单任务并输出详细日志", }, + + "dbrun": { + "suite": ["integration"], + # "mode": "OFFLINE", + # "keyword": "ORDERS", + # "pytest_args": "-vv", + "preset_meta": "在线模式,仅跑订单任务并输出详细日志", + }, + "offline_realdb": { "suite": ["offline"], "mode": "OFFLINE", diff --git a/etl_billiards/tasks/base_dwd_task.py b/etl_billiards/tasks/base_dwd_task.py new file mode 100644 index 0000000..c88e250 --- /dev/null +++ b/etl_billiards/tasks/base_dwd_task.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +"""DWD任务基类""" +import json +from typing import Any, Dict, Iterator, List, Optional, Tuple +from datetime import datetime + +from .base_task import BaseTask +from models.parsers import TypeParser + +class BaseDwdTask(BaseTask): + """ + DWD 层任务基类 + 负责从 ODS 表读取数据,供子类清洗和写入事实/维度表 + """ + + def _get_ods_cursor(self, task_code: str) -> datetime: + """ + 获取上次处理的 ODS 数据的时间点 (fetched_at) + 这里简化处理,实际应该从 etl_cursor 表读取 + 目前先依赖 BaseTask 的时间窗口逻辑,或者子类自己管理 + """ + # TODO: 对接真正的 CursorManager + # 暂时返回一个较早的时间,或者由子类通过 _get_time_window 获取 + return None + + def iter_ods_rows( + self, + table_name: str, + columns: List[str], + start_time: datetime, + end_time: datetime, + time_col: str = "fetched_at", + batch_size: int = 1000 + ) -> Iterator[List[Dict[str, Any]]]: + """ + 分批迭代读取 ODS 表数据 + + Args: + table_name: ODS 表名 + columns: 需要查询的字段列表 (必须包含 payload) + start_time: 开始时间 (包含) + end_time: 结束时间 (包含) + time_col: 时间过滤字段,默认 fetched_at + batch_size: 批次大小 + """ + offset = 0 + cols_str = ", ".join(columns) + + while True: + sql = f""" + SELECT {cols_str} + FROM {table_name} + WHERE {time_col} >= %s AND {time_col} <= %s + ORDER BY {time_col} ASC + LIMIT %s OFFSET %s + """ + + rows = self.db.fetch_all(sql, (start_time, end_time, batch_size, offset)) + + if not rows: + break + + yield [dict(row) for row in rows] + + if len(rows) < batch_size: + break + + offset += batch_size + + def parse_payload(self, row: Dict[str, Any]) -> Dict[str, Any]: + """ + 解析 ODS 行中的 payload JSON + """ + payload = row.get("payload") + if isinstance(payload, str): + return json.loads(payload) + elif isinstance(payload, dict): + return payload + return {} diff --git a/etl_billiards/tasks/manual_ingest_task.py b/etl_billiards/tasks/manual_ingest_task.py new file mode 100644 index 0000000..c60ac6c --- /dev/null +++ b/etl_billiards/tasks/manual_ingest_task.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +import os +import json +from datetime import datetime +from .base_task import BaseTask +from loaders.ods.generic import GenericODSLoader + +class ManualIngestTask(BaseTask): + """ + Task to ingest manually fetched JSON files from a directory into ODS tables. + """ + + FILE_MAPPING = { + "小票详情": "billiards_ods.ods_ticket_detail", + "结账记录": "billiards_ods.ods_order_settle", + "支付记录": "billiards_ods.ods_payment", + "助教流水": "billiards_ods.ods_assistant_ledger", + "助教废除": "billiards_ods.ods_assistant_abolish", + "商品档案": "billiards_ods.ods_goods_ledger", # Note: This might be dim_product source, but mapping to ledger for now if it's sales + "库存变化": "billiards_ods.ods_inventory_change", + "会员档案": "billiards_ods.ods_member", + "充值记录": "billiards_ods.ods_member_card", # Approx + "团购套餐": "billiards_ods.ods_package_coupon", + "库存汇总": "billiards_ods.ods_inventory_stock" + } + + def get_task_code(self) -> str: + return "MANUAL_INGEST" + + def execute(self) -> dict: + self.logger.info("Starting Manual Ingest Task") + + # Configurable directory, default to tests/testdata_json for now + data_dir = self.config.get("manual.data_dir", r"c:\dev\LLTQ\ETL\feiqiu-ETL\etl_billiards\tests\testdata_json") + + if not os.path.exists(data_dir): + self.logger.error(f"Data directory not found: {data_dir}") + return {"status": "error", "message": "Directory not found"} + + total_files = 0 + total_rows = 0 + + for filename in os.listdir(data_dir): + if not filename.endswith(".json"): + continue + + # Determine target table + target_table = None + for key, table in self.FILE_MAPPING.items(): + if key in filename: + target_table = table + break + + if not target_table: + self.logger.warning(f"No mapping found for file: {filename}, skipping.") + continue + + self.logger.info(f"Ingesting {filename} into {target_table}") + + try: + with open(os.path.join(data_dir, filename), 'r', encoding='utf-8') as f: + data = json.load(f) + + if not isinstance(data, list): + data = [data] + + # Prepare rows for GenericODSLoader + # We need to adapt the data to what GenericODSLoader expects (or update it) + # GenericODSLoader expects dicts. It handles normalization. + # But we need to ensure the primary keys are present in the payload or extracted. + # The GenericODSLoader might need configuration for PK extraction if it's not standard. + # For now, let's assume the payload IS the row, and we wrap it. + + # Actually, GenericODSLoader.upsert_rows expects the raw API result list. + # It calls _normalize_row. + # We need to make sure _normalize_row works for these files. + # Most files have 'id' or similar. + + # Let's instantiate a loader for this table + # We need to know the PK for the table. + # This is usually defined in ODS_TASK_CLASSES but here we are dynamic. + # We might need a simpler loader or reuse GenericODSLoader with specific PK config. + + # For simplicity, let's use a custom ingestion here that mimics GenericODSLoader but is file-aware. + rows_to_insert = [] + for item in data: + # Extract Store ID (usually in siteProfile or data root) + store_id = self._extract_store_id(item) or self.config.get("app.store_id") + + # Extract PK (id, orderSettleId, etc.) + pk_val = self._extract_pk(item, target_table) + + if not pk_val: + # Try to find 'id' in the item + pk_val = item.get("id") + + if not pk_val: + # Special case for Ticket Detail + if "ods_ticket_detail" in target_table: + pk_val = item.get("orderSettleId") + + if not pk_val: + continue + + row = { + "store_id": store_id, + "payload": json.dumps(item, ensure_ascii=False), + "source_file": filename, + "fetched_at": datetime.now() + } + + # Add specific PK column + pk_col = self._get_pk_column(target_table) + row[pk_col] = pk_val + + rows_to_insert.append(row) + + if rows_to_insert: + self._bulk_insert(target_table, rows_to_insert) + total_rows += len(rows_to_insert) + total_files += 1 + + except Exception as e: + self.logger.error(f"Error processing {filename}: {e}", exc_info=True) + + return {"status": "success", "files_processed": total_files, "rows_inserted": total_rows} + + def _extract_store_id(self, item): + # Try common paths + if "store_id" in item: return item["store_id"] + if "siteProfile" in item and "id" in item["siteProfile"]: return item["siteProfile"]["id"] + if "data" in item and "data" in item["data"] and "siteId" in item["data"]["data"]: return item["data"]["data"]["siteId"] + return None + + def _extract_pk(self, item, table): + # Helper to find PK based on table + if "ods_order_settle" in table: + # Check for nested structure in some files + if "settleList" in item and "settleList" in item["settleList"]: + return item["settleList"]["settleList"].get("id") + return item.get("id") + return item.get("id") + + def _get_pk_column(self, table): + if "ods_ticket_detail" in table: return "order_settle_id" + if "ods_order_settle" in table: return "order_settle_id" + if "ods_payment" in table: return "pay_id" + if "ods_member" in table: return "member_id" + if "ods_assistant_ledger" in table: return "ledger_id" + if "ods_goods_ledger" in table: return "order_goods_id" + if "ods_inventory_change" in table: return "change_id" + if "ods_assistant_abolish" in table: return "abolish_id" + if "ods_coupon_verify" in table: return "coupon_id" + if "ods_member_card" in table: return "card_id" + if "ods_package_coupon" in table: return "package_id" + return "id" # Fallback + + def _bulk_insert(self, table, rows): + if not rows: return + + keys = list(rows[0].keys()) + cols = ", ".join(keys) + vals = ", ".join([f"%({k})s" for k in keys]) + + # Determine PK col for conflict + pk_col = self._get_pk_column(table) + + sql = f""" + INSERT INTO {table} ({cols}) + VALUES ({vals}) + ON CONFLICT (store_id, {pk_col}) DO UPDATE SET + payload = EXCLUDED.payload, + fetched_at = EXCLUDED.fetched_at, + source_file = EXCLUDED.source_file; + """ + self.db.batch_execute(sql, rows) diff --git a/etl_billiards/tasks/members_dwd_task.py b/etl_billiards/tasks/members_dwd_task.py new file mode 100644 index 0000000..65d1f78 --- /dev/null +++ b/etl_billiards/tasks/members_dwd_task.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +from .base_dwd_task import BaseDwdTask +from loaders.dimensions.member import MemberLoader +from models.parsers import TypeParser +import json + +class MembersDwdTask(BaseDwdTask): + """ + DWD Task: Process Member Records from ODS to Dimension Table + Source: billiards_ods.ods_member + Target: billiards.dim_member + """ + + def get_task_code(self) -> str: + return "MEMBERS_DWD" + + def execute(self) -> dict: + self.logger.info(f"Starting {self.get_task_code()} task") + + window_start, window_end, _ = self._get_time_window() + self.logger.info(f"Processing window: {window_start} to {window_end}") + + loader = MemberLoader(self.db) + store_id = self.config.get("app.store_id") + + total_inserted = 0 + total_updated = 0 + total_errors = 0 + + # Iterate ODS Data + batches = self.iter_ods_rows( + table_name="billiards_ods.ods_member", + columns=["store_id", "member_id", "payload", "fetched_at"], + start_time=window_start, + end_time=window_end + ) + + for batch in batches: + if not batch: + continue + + parsed_rows = [] + for row in batch: + payload = self.parse_payload(row) + if not payload: + continue + + parsed = self._parse_member(payload, store_id) + if parsed: + parsed_rows.append(parsed) + + if parsed_rows: + inserted, updated, skipped = loader.upsert_members(parsed_rows, store_id) + total_inserted += inserted + total_updated += updated + + self.db.commit() + + self.logger.info(f"Task {self.get_task_code()} completed. Inserted: {total_inserted}, Updated: {total_updated}") + + return { + "status": "success", + "inserted": total_inserted, + "updated": total_updated, + "window_start": window_start.isoformat(), + "window_end": window_end.isoformat() + } + + def _parse_member(self, raw: dict, store_id: int) -> dict: + """Parse ODS payload into Dim structure""" + try: + # Handle both API structure (camelCase) and manual structure + member_id = raw.get("id") or raw.get("memberId") + if not member_id: + return None + + return { + "store_id": store_id, + "member_id": member_id, + "member_name": raw.get("name") or raw.get("memberName"), + "phone": raw.get("phone") or raw.get("mobile"), + "balance": raw.get("balance", 0), + "status": str(raw.get("status", "NORMAL")), + "register_time": raw.get("createTime") or raw.get("registerTime"), + "raw_data": json.dumps(raw, ensure_ascii=False) + } + except Exception as e: + self.logger.warning(f"Error parsing member: {e}") + return None diff --git a/etl_billiards/tasks/ods_tasks.py b/etl_billiards/tasks/ods_tasks.py new file mode 100644 index 0000000..489779a --- /dev/null +++ b/etl_billiards/tasks/ods_tasks.py @@ -0,0 +1,400 @@ +# -*- coding: utf-8 -*- +"""ODS ingestion tasks.""" +from __future__ import annotations + +from dataclasses import dataclass, field +from datetime import datetime +from typing import Any, Callable, Dict, Iterable, List, Sequence, Tuple, Type + +from loaders.ods import GenericODSLoader +from models.parsers import TypeParser +from .base_task import BaseTask + + +ColumnTransform = Callable[[Any], Any] + + +@dataclass(frozen=True) +class ColumnSpec: + """Mapping between DB column and source JSON field.""" + + column: str + sources: Tuple[str, ...] = () + required: bool = False + default: Any = None + transform: ColumnTransform | None = None + + +@dataclass(frozen=True) +class OdsTaskSpec: + """Definition of a single ODS ingestion task.""" + + code: str + class_name: str + table_name: str + endpoint: str + data_path: Tuple[str, ...] = ("data",) + list_key: str | None = None + pk_columns: Tuple[ColumnSpec, ...] = () + extra_columns: Tuple[ColumnSpec, ...] = () + include_page_size: bool = False + include_page_no: bool = True + include_source_file: bool = True + include_source_endpoint: bool = True + requires_window: bool = True + description: str = "" + extra_params: Dict[str, Any] = field(default_factory=dict) + + +class BaseOdsTask(BaseTask): + """Shared functionality for ODS ingestion tasks.""" + + SPEC: OdsTaskSpec + + def get_task_code(self) -> str: + return self.SPEC.code + + def execute(self) -> dict: + spec = self.SPEC + self.logger.info("开始执行 %s (ODS)", spec.code) + + store_id = TypeParser.parse_int(self.config.get("app.store_id")) + if not store_id: + raise ValueError("app.store_id 未配置,无法执行 ODS 任务") + + page_size = self.config.get("api.page_size", 200) + params = self._build_params(spec, store_id) + columns = self._resolve_columns(spec) + conflict_columns = ["store_id"] + [col.column for col in spec.pk_columns] + loader = GenericODSLoader( + self.db, + spec.table_name, + columns, + conflict_columns, + ) + + counts = {"fetched": 0, "inserted": 0, "updated": 0, "skipped": 0, "errors": 0} + source_file = self._resolve_source_file_hint(spec) + + try: + for page_no, page_records, _, _ in self.api.iter_paginated( + endpoint=spec.endpoint, + params=params, + page_size=page_size, + data_path=spec.data_path, + list_key=spec.list_key, + ): + rows: List[dict] = [] + for raw in page_records: + row = self._build_row( + spec=spec, + store_id=store_id, + record=raw, + page_no=page_no if spec.include_page_no else None, + page_size_value=len(page_records) + if spec.include_page_size + else None, + source_file=source_file, + ) + if row is None: + counts["skipped"] += 1 + continue + rows.append(row) + + inserted, updated, _ = loader.upsert_rows(rows) + counts["inserted"] += inserted + counts["updated"] += updated + counts["fetched"] += len(page_records) + + self.db.commit() + self.logger.info("%s ODS 任务完成: %s", spec.code, counts) + return self._build_result("SUCCESS", counts) + + except Exception: + self.db.rollback() + counts["errors"] += 1 + self.logger.error("%s ODS 任务失败", spec.code, exc_info=True) + raise + + def _build_params(self, spec: OdsTaskSpec, store_id: int) -> dict: + params: dict[str, Any] = {"storeId": store_id} + params.update(spec.extra_params) + if spec.requires_window: + window_start, window_end, _ = self._get_time_window() + params["startTime"] = TypeParser.format_timestamp(window_start, self.tz) + params["endTime"] = TypeParser.format_timestamp(window_end, self.tz) + return params + + def _resolve_columns(self, spec: OdsTaskSpec) -> List[str]: + columns: List[str] = ["store_id"] + seen = set(columns) + for col_spec in list(spec.pk_columns) + list(spec.extra_columns): + if col_spec.column not in seen: + columns.append(col_spec.column) + seen.add(col_spec.column) + + if spec.include_page_no and "page_no" not in seen: + columns.append("page_no") + seen.add("page_no") + + if spec.include_page_size and "page_size" not in seen: + columns.append("page_size") + seen.add("page_size") + + if spec.include_source_file and "source_file" not in seen: + columns.append("source_file") + seen.add("source_file") + + if spec.include_source_endpoint and "source_endpoint" not in seen: + columns.append("source_endpoint") + seen.add("source_endpoint") + + if "fetched_at" not in seen: + columns.append("fetched_at") + seen.add("fetched_at") + if "payload" not in seen: + columns.append("payload") + + return columns + + def _build_row( + self, + spec: OdsTaskSpec, + store_id: int, + record: dict, + page_no: int | None, + page_size_value: int | None, + source_file: str | None, + ) -> dict | None: + row: dict[str, Any] = {"store_id": store_id} + + for col_spec in spec.pk_columns + spec.extra_columns: + value = self._extract_value(record, col_spec) + if value is None and col_spec.required: + self.logger.warning( + "%s 缺少必填字段 %s,原始记录: %s", + spec.code, + col_spec.column, + record, + ) + return None + row[col_spec.column] = value + + if spec.include_page_no: + row["page_no"] = page_no + if spec.include_page_size: + row["page_size"] = page_size_value + if spec.include_source_file: + row["source_file"] = source_file + if spec.include_source_endpoint: + row["source_endpoint"] = spec.endpoint + + row["fetched_at"] = datetime.now(self.tz) + row["payload"] = record + return row + + def _extract_value(self, record: dict, spec: ColumnSpec): + value = None + for key in spec.sources: + value = self._dig(record, key) + if value is not None: + break + if value is None and spec.default is not None: + value = spec.default + if value is not None and spec.transform: + value = spec.transform(value) + return value + + @staticmethod + def _dig(record: Any, path: str | None): + if not path: + return None + current = record + for part in path.split("."): + if isinstance(current, dict): + current = current.get(part) + else: + return None + return current + + def _resolve_source_file_hint(self, spec: OdsTaskSpec) -> str | None: + resolver = getattr(self.api, "get_source_hint", None) + if callable(resolver): + return resolver(spec.endpoint) + return None + + +def _int_col(name: str, *sources: str, required: bool = False) -> ColumnSpec: + return ColumnSpec( + column=name, + sources=sources, + required=required, + transform=TypeParser.parse_int, + ) + + +ODS_TASK_SPECS: Tuple[OdsTaskSpec, ...] = ( + OdsTaskSpec( + code="ODS_ORDER_SETTLE", + class_name="OdsOrderSettleTask", + table_name="billiards_ods.ods_order_settle", + endpoint="/order/list", + data_path=("data",), + pk_columns=(_int_col("order_settle_id", "orderSettleId", "order_settle_id", "id", required=True),), + extra_columns=(_int_col("order_trade_no", "orderTradeNo", "order_trade_no"),), + include_page_size=True, + description="订单/结算 ODS 原始记录", + ), + OdsTaskSpec( + code="ODS_TABLE_USE", + class_name="OdsTableUseTask", + table_name="billiards_ods.ods_table_use_detail", + endpoint="/Table/UseDetailList", + data_path=("data", "siteTableUseDetailsList"), + pk_columns=(_int_col("ledger_id", "id", required=True),), + extra_columns=( + _int_col("order_trade_no", "order_trade_no", "orderTradeNo"), + _int_col("order_settle_id", "order_settle_id", "orderSettleId"), + ), + description="台费/开台流水 ODS", + ), + OdsTaskSpec( + code="ODS_ASSISTANT_LEDGER", + class_name="OdsAssistantLedgerTask", + table_name="billiards_ods.ods_assistant_ledger", + endpoint="/Assistant/LedgerList", + data_path=("data", "orderAssistantDetails"), + pk_columns=(_int_col("ledger_id", "id", required=True),), + extra_columns=( + _int_col("order_trade_no", "order_trade_no", "orderTradeNo"), + _int_col("order_settle_id", "order_settle_id", "orderSettleId"), + ), + description="助教流水 ODS", + ), + OdsTaskSpec( + code="ODS_ASSISTANT_ABOLISH", + class_name="OdsAssistantAbolishTask", + table_name="billiards_ods.ods_assistant_abolish", + endpoint="/Assistant/AbolishList", + data_path=("data", "abolitionAssistants"), + pk_columns=(_int_col("abolish_id", "id", required=True),), + description="助教作废记录 ODS", + ), + OdsTaskSpec( + code="ODS_GOODS_LEDGER", + class_name="OdsGoodsLedgerTask", + table_name="billiards_ods.ods_goods_ledger", + endpoint="/Order/GoodsLedgerList", + data_path=("data", "orderGoodsLedgers"), + pk_columns=(_int_col("order_goods_id", "orderGoodsId", "id", required=True),), + extra_columns=( + _int_col("order_trade_no", "order_trade_no", "orderTradeNo"), + _int_col("order_settle_id", "order_settle_id", "orderSettleId"), + ), + description="商品销售流水 ODS", + ), + OdsTaskSpec( + code="ODS_PAYMENT", + class_name="OdsPaymentTask", + table_name="billiards_ods.ods_payment", + endpoint="/pay/records", + data_path=("data",), + pk_columns=(_int_col("pay_id", "payId", "id", required=True),), + extra_columns=( + ColumnSpec(column="relate_type", sources=("relate_type", "relateType")), + _int_col("relate_id", "relate_id", "relateId"), + ), + include_page_size=False, + description="支付流水 ODS", + ), + OdsTaskSpec( + code="ODS_REFUND", + class_name="OdsRefundTask", + table_name="billiards_ods.ods_refund", + endpoint="/Pay/RefundList", + data_path=(), + pk_columns=(_int_col("refund_id", "id", required=True),), + extra_columns=( + ColumnSpec(column="relate_type", sources=("relate_type", "relateType")), + _int_col("relate_id", "relate_id", "relateId"), + ), + description="退款流水 ODS", + ), + OdsTaskSpec( + code="ODS_COUPON_VERIFY", + class_name="OdsCouponVerifyTask", + table_name="billiards_ods.ods_coupon_verify", + endpoint="/Coupon/UsageList", + data_path=(), + pk_columns=(_int_col("coupon_id", "id", "couponId", required=True),), + description="平台验券/团购流水 ODS", + ), + OdsTaskSpec( + code="ODS_MEMBER", + class_name="OdsMemberTask", + table_name="billiards_ods.ods_member", + endpoint="/MemberProfile/GetTenantMemberList", + data_path=("data",), + pk_columns=(_int_col("member_id", "memberId", required=True),), + requires_window=False, + description="会员档案 ODS", + ), + OdsTaskSpec( + code="ODS_MEMBER_CARD", + class_name="OdsMemberCardTask", + table_name="billiards_ods.ods_member_card", + endpoint="/MemberCard/List", + data_path=("data", "tenantMemberCards"), + pk_columns=(_int_col("card_id", "tenantMemberCardId", "cardId", required=True),), + requires_window=False, + description="会员卡/储值卡 ODS", + ), + OdsTaskSpec( + code="ODS_PACKAGE", + class_name="OdsPackageTask", + table_name="billiards_ods.ods_package_coupon", + endpoint="/Package/List", + data_path=("data", "packageCouponList"), + pk_columns=(_int_col("package_id", "id", "packageId", required=True),), + requires_window=False, + description="团购/套餐定义 ODS", + ), + OdsTaskSpec( + code="ODS_INVENTORY_STOCK", + class_name="OdsInventoryStockTask", + table_name="billiards_ods.ods_inventory_stock", + endpoint="/Inventory/StockSummary", + data_path=(), + pk_columns=( + _int_col("site_goods_id", "siteGoodsId", required=True), + ColumnSpec(column="snapshot_key", default="default", required=True), + ), + requires_window=False, + description="库存汇总 ODS", + ), + OdsTaskSpec( + code="ODS_INVENTORY_CHANGE", + class_name="OdsInventoryChangeTask", + table_name="billiards_ods.ods_inventory_change", + endpoint="/Inventory/ChangeList", + data_path=("data", "queryDeliveryRecordsList"), + pk_columns=(_int_col("change_id", "siteGoodsStockId", "id", required=True),), + description="库存变动 ODS", + ), +) + + +def _build_task_class(spec: OdsTaskSpec) -> Type[BaseOdsTask]: + attrs = { + "SPEC": spec, + "__doc__": spec.description or f"ODS ingestion task {spec.code}", + "__module__": __name__, + } + return type(spec.class_name, (BaseOdsTask,), attrs) + + +ODS_TASK_CLASSES: Dict[str, Type[BaseOdsTask]] = { + spec.code: _build_task_class(spec) for spec in ODS_TASK_SPECS +} + +__all__ = ["ODS_TASK_CLASSES", "ODS_TASK_SPECS", "BaseOdsTask"] diff --git a/etl_billiards/tasks/payments_dwd_task.py b/etl_billiards/tasks/payments_dwd_task.py new file mode 100644 index 0000000..2651f01 --- /dev/null +++ b/etl_billiards/tasks/payments_dwd_task.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +from .base_dwd_task import BaseDwdTask +from loaders.facts.payment import PaymentLoader +from models.parsers import TypeParser +import json + +class PaymentsDwdTask(BaseDwdTask): + """ + DWD Task: Process Payment Records from ODS to Fact Table + Source: billiards_ods.ods_payment + Target: billiards.fact_payment + """ + + def get_task_code(self) -> str: + return "PAYMENTS_DWD" + + def execute(self) -> dict: + self.logger.info(f"Starting {self.get_task_code()} task") + + window_start, window_end, _ = self._get_time_window() + self.logger.info(f"Processing window: {window_start} to {window_end}") + + loader = PaymentLoader(self.db) + store_id = self.config.get("app.store_id") + + total_inserted = 0 + total_errors = 0 + + # Iterate ODS Data + batches = self.iter_ods_rows( + table_name="billiards_ods.ods_payment", + columns=["store_id", "pay_id", "payload", "fetched_at"], + start_time=window_start, + end_time=window_end + ) + + for batch in batches: + if not batch: + continue + + parsed_rows = [] + for row in batch: + payload = self.parse_payload(row) + if not payload: + continue + + parsed = self._parse_payment(payload, store_id) + if parsed: + parsed_rows.append(parsed) + + if parsed_rows: + inserted, errors = loader.upsert_payments(parsed_rows, store_id) + total_inserted += inserted + total_errors += errors + + self.db.commit() + + self.logger.info(f"Task {self.get_task_code()} completed. Inserted: {total_inserted}, Errors: {total_errors}") + + return { + "status": "success", + "inserted": total_inserted, + "errors": total_errors, + "window_start": window_start.isoformat(), + "window_end": window_end.isoformat() + } + + def _parse_payment(self, raw: dict, store_id: int) -> dict: + """Parse ODS payload into Fact structure""" + try: + pay_id = TypeParser.parse_int(raw.get("payId") or raw.get("id")) + if not pay_id: + return None + + relate_type = str(raw.get("relateType") or raw.get("relate_type") or "") + relate_id = TypeParser.parse_int(raw.get("relateId") or raw.get("relate_id")) + + # Attempt to populate settlement / trade identifiers + order_settle_id = TypeParser.parse_int( + raw.get("orderSettleId") or raw.get("order_settle_id") + ) + order_trade_no = TypeParser.parse_int( + raw.get("orderTradeNo") or raw.get("order_trade_no") + ) + + if relate_type in {"1", "SETTLE", "ORDER"}: + order_settle_id = order_settle_id or relate_id + + return { + "store_id": store_id, + "pay_id": pay_id, + "order_settle_id": order_settle_id, + "order_trade_no": order_trade_no, + "relate_type": relate_type, + "relate_id": relate_id, + "site_id": TypeParser.parse_int( + raw.get("siteId") or raw.get("site_id") or store_id + ), + "tenant_id": TypeParser.parse_int(raw.get("tenantId") or raw.get("tenant_id")), + "create_time": TypeParser.parse_timestamp( + raw.get("createTime") or raw.get("create_time"), self.tz + ), + "pay_time": TypeParser.parse_timestamp(raw.get("payTime"), self.tz), + "pay_amount": TypeParser.parse_decimal(raw.get("payAmount")), + "fee_amount": TypeParser.parse_decimal( + raw.get("feeAmount") + or raw.get("serviceFee") + or raw.get("channelFee") + or raw.get("fee_amount") + ), + "discount_amount": TypeParser.parse_decimal( + raw.get("discountAmount") + or raw.get("couponAmount") + or raw.get("discount_amount") + ), + "payment_method": str(raw.get("paymentMethod") or raw.get("payment_method") or ""), + "online_pay_channel": raw.get("onlinePayChannel") or raw.get("online_pay_channel"), + "pay_terminal": raw.get("payTerminal") or raw.get("pay_terminal"), + "pay_status": str(raw.get("payStatus") or raw.get("pay_status") or ""), + "raw_data": json.dumps(raw, ensure_ascii=False) + } + except Exception as e: + self.logger.warning(f"Error parsing payment: {e}") + return None diff --git a/etl_billiards/tasks/payments_task.py b/etl_billiards/tasks/payments_task.py index b3b227b..8a1b735 100644 --- a/etl_billiards/tasks/payments_task.py +++ b/etl_billiards/tasks/payments_task.py @@ -62,16 +62,42 @@ class PaymentsTask(BaseTask): def _parse_payment(self, raw: dict) -> dict: """解析支付记录""" try: + store_id = self.config.get("app.store_id") return { - "store_id": self.config.get("app.store_id"), - "pay_id": TypeParser.parse_int(raw.get("payId")), + "store_id": store_id, + "pay_id": TypeParser.parse_int(raw.get("payId") or raw.get("id")), "order_id": TypeParser.parse_int(raw.get("orderId")), + "order_settle_id": TypeParser.parse_int( + raw.get("orderSettleId") or raw.get("order_settle_id") + ), + "order_trade_no": TypeParser.parse_int( + raw.get("orderTradeNo") or raw.get("order_trade_no") + ), + "relate_type": raw.get("relateType") or raw.get("relate_type"), + "relate_id": TypeParser.parse_int(raw.get("relateId") or raw.get("relate_id")), + "site_id": TypeParser.parse_int(raw.get("siteId") or raw.get("site_id") or store_id), + "tenant_id": TypeParser.parse_int(raw.get("tenantId") or raw.get("tenant_id")), "pay_time": TypeParser.parse_timestamp(raw.get("payTime"), self.tz), + "create_time": TypeParser.parse_timestamp( + raw.get("createTime") or raw.get("create_time"), self.tz + ), "pay_amount": TypeParser.parse_decimal(raw.get("payAmount")), + "fee_amount": TypeParser.parse_decimal( + raw.get("feeAmount") + or raw.get("serviceFee") + or raw.get("channelFee") + or raw.get("fee_amount") + ), + "discount_amount": TypeParser.parse_decimal( + raw.get("discountAmount") or raw.get("couponAmount") or raw.get("discount_amount") + ), "pay_type": raw.get("payType"), + "payment_method": raw.get("paymentMethod") or raw.get("payment_method"), + "online_pay_channel": raw.get("onlinePayChannel") or raw.get("online_pay_channel"), "pay_status": raw.get("payStatus"), + "pay_terminal": raw.get("payTerminal") or raw.get("pay_terminal"), "remark": raw.get("remark"), - "raw_data": json.dumps(raw, ensure_ascii=False) + "raw_data": json.dumps(raw, ensure_ascii=False), } except Exception as e: self.logger.warning(f"解析支付记录失败: {e}, 原始数据: {raw}") diff --git a/etl_billiards/tasks/ticket_dwd_task.py b/etl_billiards/tasks/ticket_dwd_task.py new file mode 100644 index 0000000..ccbe5be --- /dev/null +++ b/etl_billiards/tasks/ticket_dwd_task.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +from .base_dwd_task import BaseDwdTask +from loaders.facts.ticket import TicketLoader + +class TicketDwdTask(BaseDwdTask): + """ + DWD Task: Process Ticket Details from ODS to Fact Tables + Source: billiards_ods.ods_ticket_detail + Targets: + - billiards.fact_order + - billiards.fact_order_goods + - billiards.fact_table_usage + - billiards.fact_assistant_service + """ + + def get_task_code(self) -> str: + return "TICKET_DWD" + + def execute(self) -> dict: + self.logger.info(f"Starting {self.get_task_code()} task") + + # 1. Get Time Window (Incremental Load) + window_start, window_end, _ = self._get_time_window() + self.logger.info(f"Processing window: {window_start} to {window_end}") + + # 2. Initialize Loader + loader = TicketLoader(self.db) + store_id = self.config.get("app.store_id") + + total_inserted = 0 + total_errors = 0 + + # 3. Iterate ODS Data + # We query ods_ticket_detail based on fetched_at + batches = self.iter_ods_rows( + table_name="billiards_ods.ods_ticket_detail", + columns=["store_id", "order_settle_id", "payload", "fetched_at"], + start_time=window_start, + end_time=window_end + ) + + for batch in batches: + if not batch: + continue + + # Extract payloads + tickets = [] + for row in batch: + payload = self.parse_payload(row) + if payload: + tickets.append(payload) + + # Process Batch + inserted, errors = loader.process_tickets(tickets, store_id) + total_inserted += inserted + total_errors += errors + + # 4. Commit + self.db.commit() + + self.logger.info(f"Task {self.get_task_code()} completed. Inserted: {total_inserted}, Errors: {total_errors}") + + return { + "status": "success", + "inserted": total_inserted, + "errors": total_errors, + "window_start": window_start.isoformat(), + "window_end": window_end.isoformat() + } diff --git a/etl_billiards/tests/testdata_json/summary.txt b/etl_billiards/tests/testdata_json/summary.txt new file mode 100644 index 0000000..19673fa --- /dev/null +++ b/etl_billiards/tests/testdata_json/summary.txt @@ -0,0 +1,144 @@ +========================================================================================== +20251110_034959_助教流水.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'orderAssistantDetails'] + list orderAssistantDetails len 100, elem type dict, keys ['assistantNo', 'nickname', 'levelName', 'assistantName', 'tableName', 'siteProfile', 'skillName', 'id', 'order_trade_no', 'site_id'] +========================================================================================== +20251110_035004_助教废除.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'abolitionAssistants'] + list abolitionAssistants len 15, elem type dict, keys ['siteProfile', 'createTime', 'id', 'siteId', 'tableAreaId', 'tableId', 'tableArea', 'tableName', 'assistantOn', 'assistantName'] +========================================================================================== +20251110_035011_台费流水.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'siteTableUseDetailsList'] + list siteTableUseDetailsList len 100, elem type dict, keys ['siteProfile', 'id', 'order_trade_no', 'site_id', 'tenant_id', 'member_id', 'operator_id', 'operator_name', 'order_settle_id', 'ledger_unit_price'] +========================================================================================== +20251110_035904_小票详情.json +root list len 193 +sample keys ['orderSettleId', 'data'] +data keys ['data', 'code'] + dict data keys ['tenantId', 'siteId', 'orderSettleId', 'orderSettleNumber', 'assistantManualDiscount', 'siteName', 'tenantName', 'siteAddress', 'siteBusinessTel', 'ticketRemark'] +========================================================================================== +20251110_035908_台费打折.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'taiFeeAdjustInfos'] + list taiFeeAdjustInfos len 100, elem type dict, keys ['tableProfile', 'siteProfile', 'id', 'adjust_type', 'applicant_id', 'applicant_name', 'create_time', 'is_delete', 'ledger_amount', 'ledger_count'] +========================================================================================== +20251110_035916_结账记录.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'settleList'] + list settleList len 100, elem type dict, keys ['siteProfile', 'settleList'] +========================================================================================== +20251110_035923_支付记录.json +root list len 200 +sample keys ['siteProfile', 'create_time', 'pay_amount', 'pay_status', 'pay_time', 'online_pay_channel', 'relate_type', 'relate_id', 'site_id', 'id', 'payment_method'] + dict siteProfile keys ['id', 'org_id', 'shop_name', 'avatar', 'business_tel', 'full_address', 'address', 'longitude', 'latitude', 'tenant_site_region_id'] +========================================================================================== +20251110_035929_退款记录.json +root list len 11 +sample keys ['tenantName', 'siteProfile', 'id', 'site_id', 'tenant_id', 'pay_sn', 'pay_amount', 'pay_status', 'pay_time', 'create_time', 'relate_type', 'relate_id', 'is_revoke', 'is_delete', 'online_pay_channel', 'payment_method', 'balance_frozen_amount', 'card_frozen_amount', 'member_id', 'member_card_id'] + dict siteProfile keys ['id', 'org_id', 'shop_name', 'avatar', 'business_tel', 'full_address', 'address', 'longitude', 'latitude', 'tenant_site_region_id'] +========================================================================================== +20251110_035934_平台验券记录.json +root list len 200 +sample keys ['siteProfile', 'id', 'tenant_id', 'site_id', 'sale_price', 'coupon_code', 'coupon_channel', 'site_order_id', 'coupon_free_time', 'use_status', 'create_time', 'is_delete', 'coupon_name', 'coupon_cover', 'coupon_remark', 'channel_deal_id', 'group_package_id', 'consume_time', 'groupon_type', 'coupon_money'] + dict siteProfile keys ['id', 'org_id', 'shop_name', 'avatar', 'business_tel', 'full_address', 'address', 'longitude', 'latitude', 'tenant_site_region_id'] +========================================================================================== +20251110_035941_商品档案.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'tenantGoodsList'] + list tenantGoodsList len 100, elem type dict, keys ['categoryName', 'isInSite', 'commodityCode', 'id', 'tenant_id', 'goods_name', 'goods_cover', 'goods_state', 'goods_category_id', 'unit'] +========================================================================================== +20251110_035948_门店销售记录.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'orderGoodsLedgers'] + list orderGoodsLedgers len 100, elem type dict, keys ['siteId', 'siteName', 'orderGoodsId', 'openSalesman', 'id', 'order_trade_no', 'site_id', 'tenant_id', 'operator_id', 'operator_name'] +========================================================================================== +20251110_043159_库存变化记录1.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'queryDeliveryRecordsList'] + list queryDeliveryRecordsList len 100, elem type dict, keys ['siteGoodsStockId', 'siteGoodsId', 'siteId', 'tenantId', 'stockType', 'goodsName', 'createTime', 'startNum', 'endNum', 'changeNum'] +========================================================================================== +20251110_043204_库存变化记录2.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'goodsCategoryList'] + list goodsCategoryList len 9, elem type dict, keys ['id', 'tenant_id', 'category_name', 'alias_name', 'pid', 'business_name', 'tenant_goods_business_id', 'open_salesman', 'categoryBoxes', 'sort'] +========================================================================================== +20251110_043209_会员档案.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'tenantMemberInfos'] + list tenantMemberInfos len 100, elem type dict, keys ['id', 'create_time', 'member_card_grade_code', 'mobile', 'nickname', 'register_site_id', 'site_name', 'member_card_grade_name', 'system_member_id', 'tenant_id'] +========================================================================================== +20251110_043217_余额变更记录.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'tenantMemberCardLogs'] + list tenantMemberCardLogs len 100, elem type dict, keys ['memberCardTypeName', 'paySiteName', 'registerSiteName', 'memberName', 'memberMobile', 'id', 'account_data', 'after', 'before', 'card_type_id'] +========================================================================================== +20251110_043223_储值卡列表.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'totalOther', 'tenantMemberCards'] + list tenantMemberCards len 100, elem type dict, keys ['site_name', 'member_name', 'member_mobile', 'member_card_type_name', 'table_service_discount', 'assistant_service_discount', 'coupon_discount', 'goods_service_discount', 'is_allow_give', 'able_cross_site'] +========================================================================================== +20251110_043231_充值记录.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'settleList'] + list settleList len 74, elem type dict, keys ['siteProfile', 'settleList'] +========================================================================================== +20251110_043237_助教账号1.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'assistantInfos'] + list assistantInfos len 50, elem type dict, keys ['job_num', 'shop_name', 'group_id', 'group_name', 'staff_profile_id', 'ding_talk_synced', 'entry_type', 'team_name', 'entry_sign_status', 'resign_sign_status'] +========================================================================================== +20251110_043243_助教账号2.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'assistantInfos'] + list assistantInfos len 50, elem type dict, keys ['job_num', 'shop_name', 'group_id', 'group_name', 'staff_profile_id', 'ding_talk_synced', 'entry_type', 'team_name', 'entry_sign_status', 'resign_sign_status'] +========================================================================================== +20251110_043250_台桌列表.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'siteTables'] + list siteTables len 71, elem type dict, keys ['id', 'audit_status', 'charge_free', 'self_table', 'create_time', 'is_rest_area', 'light_status', 'show_status', 'site_id', 'site_table_area_id'] +========================================================================================== +20251110_043255_团购套餐.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'packageCouponList'] + list packageCouponList len 17, elem type dict, keys ['site_name', 'effective_status', 'id', 'site_id', 'tenant_id', 'package_name', 'table_area_id', 'table_area_name', 'selling_price', 'duration'] +========================================================================================== +20251110_043302_团购套餐流水.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'couponAmountSum', 'siteTableUseDetailsList'] + list siteTableUseDetailsList len 100, elem type dict, keys ['tableName', 'tableAreaName', 'siteName', 'goodsOptionPrice', 'id', 'order_trade_no', 'table_id', 'site_id', 'tenant_id', 'operator_id'] +========================================================================================== +20251110_043308_库存汇总.json +root list len 161 +sample keys ['siteGoodsId', 'goodsName', 'goodsUnit', 'goodsCategoryId', 'goodsCategorySecondId', 'rangeStartStock', 'rangeEndStock', 'rangeIn', 'rangeOut', 'rangeInventory', 'rangeSale', 'rangeSaleMoney', 'currentStock', 'categoryName'] +========================================================================================== +20251110_051132_门店商品档案1.json +root list len 2 +sample keys ['data', 'code'] +data keys ['total', 'orderGoodsList'] + list orderGoodsList len 100, elem type dict, keys ['siteName', 'oneCategoryName', 'twoCategoryName', 'id', 'tenant_goods_id', 'site_id', 'tenant_id', 'goods_name', 'goods_cover', 'goods_state'] +========================================================================================== +20251110_051138_门店商品档案2.json +root list len 2 +sample keys ['data', 'code'] +data keys ['goodsStockA', 'goodsStockB', 'goodsSaleNum', 'stockSumMoney'] diff --git a/etl_billiards/tests/unit/task_test_utils.py b/etl_billiards/tests/unit/task_test_utils.py index 7d7e7ac..ef1cd9e 100644 --- a/etl_billiards/tests/unit/task_test_utils.py +++ b/etl_billiards/tests/unit/task_test_utils.py @@ -135,16 +135,41 @@ class FakeDBOperations: def __init__(self): self.upserts: List[Dict] = [] + self.executes: List[Dict] = [] self.commits = 0 self.rollbacks = 0 self.conn = FakeConnection() def batch_upsert_with_returning(self, sql: str, rows: List[Dict], page_size: int = 1000): - self.upserts.append({"sql": sql.strip(), "count": len(rows), "page_size": page_size}) + self.upserts.append( + { + "sql": sql.strip(), + "count": len(rows), + "page_size": page_size, + "rows": [dict(row) for row in rows], + } + ) return len(rows), 0 def batch_execute(self, sql: str, rows: List[Dict], page_size: int = 1000): - self.upserts.append({"sql": sql.strip(), "count": len(rows), "page_size": page_size}) + self.executes.append( + { + "sql": sql.strip(), + "count": len(rows), + "page_size": page_size, + "rows": [dict(row) for row in rows], + } + ) + + def execute(self, sql: str, params=None): + self.executes.append({"sql": sql.strip(), "params": params}) + + def query(self, sql: str, params=None): + self.executes.append({"sql": sql.strip(), "params": params, "type": "query"}) + return [] + + def cursor(self): + return self.conn.cursor() def commit(self): self.commits += 1 @@ -161,22 +186,53 @@ class FakeAPIClient: self.calls: List[Dict] = [] # pylint: disable=unused-argument - def get_paginated(self, endpoint: str, params=None, **kwargs): + def iter_paginated( + self, + endpoint: str, + params=None, + page_size: int = 200, + page_field: str = "pageIndex", + size_field: str = "pageSize", + data_path: Tuple[str, ...] = (), + list_key: str | None = None, + ): self.calls.append({"endpoint": endpoint, "params": params}) if endpoint not in self.data_map: raise AssertionError(f"Missing fixture for endpoint {endpoint}") - return list(self.data_map[endpoint]), [{"page": 1, "size": len(self.data_map[endpoint])}] + + records = list(self.data_map[endpoint]) + yield 1, records, dict(params or {}), {"data": records} + + def get_paginated(self, endpoint: str, params=None, **kwargs): + records = [] + pages = [] + for page_no, page_records, req, resp in self.iter_paginated(endpoint, params, **kwargs): + records.extend(page_records) + pages.append({"page": page_no, "request": req, "response": resp}) + return records, pages + + def get_source_hint(self, endpoint: str) -> str | None: + return None class OfflineAPIClient: - """离线模式专用 API Client,根据 endpoint 读取归档 JSON、套用 data_path 并回放列表数据。""" + """离线模式专用 API Client,根据 endpoint 读取归档 JSON、套入 data_path 并回放列表数据。""" def __init__(self, file_map: Dict[str, Path]): self.file_map = {k: Path(v) for k, v in file_map.items()} self.calls: List[Dict] = [] # pylint: disable=unused-argument - def get_paginated(self, endpoint: str, params=None, page_size: int = 200, data_path: Tuple[str, ...] = (), **kwargs): + def iter_paginated( + self, + endpoint: str, + params=None, + page_size: int = 200, + page_field: str = "pageIndex", + size_field: str = "pageSize", + data_path: Tuple[str, ...] = (), + list_key: str | None = None, + ): self.calls.append({"endpoint": endpoint, "params": params}) if endpoint not in self.file_map: raise AssertionError(f"Missing archive for endpoint {endpoint}") @@ -188,17 +244,42 @@ class OfflineAPIClient: for key in data_path: if isinstance(data, dict): data = data.get(key, []) - else: - data = [] - break + + if list_key and isinstance(data, dict): + data = data.get(list_key, []) if not isinstance(data, list): data = [] - return data, [{"page": 1, "mode": "offline"}] + total = len(data) + start = 0 + page = 1 + while start < total or (start == 0 and total == 0): + chunk = data[start : start + page_size] + if not chunk and total != 0: + break + yield page, list(chunk), dict(params or {}), payload + if len(chunk) < page_size: + break + start += page_size + page += 1 + + def get_paginated(self, endpoint: str, params=None, **kwargs): + records = [] + pages = [] + for page_no, page_records, req, resp in self.iter_paginated(endpoint, params, **kwargs): + records.extend(page_records) + pages.append({"page": page_no, "request": req, "response": resp}) + return records, pages + + def get_source_hint(self, endpoint: str) -> str | None: + if endpoint not in self.file_map: + return None + return str(self.file_map[endpoint]) class RealDBOperationsAdapter: + """连接真实 PostgreSQL 的适配器,为任务提供 batch_upsert + 事务能力。""" def __init__(self, dsn: str): diff --git a/etl_billiards/tests/unit/test_ods_tasks.py b/etl_billiards/tests/unit/test_ods_tasks.py new file mode 100644 index 0000000..336b9a2 --- /dev/null +++ b/etl_billiards/tests/unit/test_ods_tasks.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +"""Unit tests for the new ODS ingestion tasks.""" +import logging +import sys +from pathlib import Path + +# Ensure project root is resolvable when running tests in isolation +PROJECT_ROOT = Path(__file__).resolve().parents[2] +if str(PROJECT_ROOT) not in sys.path: + sys.path.insert(0, str(PROJECT_ROOT)) + +from tasks.ods_tasks import ODS_TASK_CLASSES +from .task_test_utils import create_test_config, get_db_operations, FakeAPIClient + + +def _build_config(tmp_path): + archive_dir = tmp_path / "archive" + temp_dir = tmp_path / "temp" + return create_test_config("ONLINE", archive_dir, temp_dir) + + +def test_ods_order_settle_ingest(tmp_path): + """Ensure ODS_ORDER_SETTLE task writes raw payload + metadata.""" + config = _build_config(tmp_path) + sample = [ + { + "orderSettleId": 701, + "orderTradeNo": 8001, + "anyField": "value", + } + ] + api = FakeAPIClient({"/order/list": sample}) + task_cls = ODS_TASK_CLASSES["ODS_ORDER_SETTLE"] + + with get_db_operations() as db_ops: + task = task_cls(config, db_ops, api, logging.getLogger("test_ods_order")) + result = task.execute() + + assert result["status"] == "SUCCESS" + assert result["counts"]["fetched"] == 1 + assert db_ops.commits == 1 + row = db_ops.upserts[0]["rows"][0] + assert row["order_settle_id"] == 701 + assert row["order_trade_no"] == 8001 + assert row["source_endpoint"] == "/order/list" + assert '"orderSettleId": 701' in row["payload"] + + +def test_ods_payment_ingest(tmp_path): + """Ensure ODS_PAYMENT task stores relate fields and payload.""" + config = _build_config(tmp_path) + sample = [ + { + "payId": 901, + "relateType": "ORDER", + "relateId": 123, + "payAmount": "100.00", + } + ] + api = FakeAPIClient({"/pay/records": sample}) + task_cls = ODS_TASK_CLASSES["ODS_PAYMENT"] + + with get_db_operations() as db_ops: + task = task_cls(config, db_ops, api, logging.getLogger("test_ods_payment")) + result = task.execute() + + assert result["status"] == "SUCCESS" + assert result["counts"]["fetched"] == 1 + assert db_ops.commits == 1 + row = db_ops.upserts[0]["rows"][0] + assert row["pay_id"] == 901 + assert row["relate_type"] == "ORDER" + assert row["relate_id"] == 123 + assert '"payId": 901' in row["payload"] diff --git a/schema_v2.sql b/schema_v2.sql new file mode 100644 index 0000000..5f28270 --- /dev/null +++ b/schema_v2.sql @@ -0,0 +1 @@ + \ No newline at end of file