# MCP AI 查库手册

> **项目地址**：`apps/mcp-server`  
> **目标用户**：AI 应用（阿里云百炼）通过 MCP 协议查询 PostgreSQL 数据库  
> **数据库**：`etl_feiqiu`（ETL 数据仓库，六层架构）  
> **服务地址**：`https://mcp.langlangzhuoqiu.cn/mcp`

---

## 概述

MCP Server 为 AI 应用提供 PostgreSQL 只读查询能力，专门用于查询台球门店的运营数据。通过标准化的工具接口，AI 可以快速获取会员信息、订单数据、助教业绩、财务汇总等业务数据，为智能分析和决策提供数据支撑。

### 架构流程

```
微信小程序 → 百炼 AI 应用 → MCP (HTTP) → MCP Server → PostgreSQL (etl_feiqiu)
```

---

## 可访问的数据库架构

### Schema 层次结构

| Schema | 层级 | 说明 | 表数量 | 主要用途 |
|--------|------|------|--------|---------|
| `ods` | 原始数据层 | 飞球 API 原始数据，JSON 结构保持 | 23 表 | 数据源头，保持原始格式 |
| `dwd` | 明细数据层 | 清洗后的业务明细，标准化字段 | 44 表 | 业务分析的基础数据 |
| `dws` | 汇总数据层 | 按日/月汇总的业绩指标 | 34 表 + 8 物化视图 | 报表和 KPI 分析 |
| `core` | 跨门店标准化 | 统一的维度表和标准化数据 | 7 表 | 多门店对比分析 |
| `meta` | 调度元数据 | ETL 任务执行状态和日志 | 3 表 | 数据质量监控 |
| `app` | 应用视图层 | 带 RLS 的业务视图 | 43 视图 | 应用层数据访问 |

### 数据隔离机制

- **多门店隔离**：所有业务表都有 `site_id` 字段，实现门店级数据隔离
- **RLS（行级安全）**：`app` schema 的视图层自动过滤当前用户可访问的门店数据
- **时间范围**：大部分汇总表按营业日（08:00 为分界点）进行数据归属

---

## MCP 工具详解

### 1. list_tables - 列出表清单

**功能**：获取指定 schema 下的所有表和视图

**参数**：
- `schema` (string, 默认 "dwd"): 目标 schema，可选值：`ods`/`dwd`/`dws`/`core`/`meta`/`app`
- `include_views` (boolean, 默认 false): 是否包含视图

**返回示例**：
```json
{
  "schema": "dwd",
  "include_views": false,
  "tables": [
    {"name": "dim_member", "type": "BASE TABLE"},
    {"name": "dim_assistant", "type": "BASE TABLE"},
    {"name": "dwd_order_head", "type": "BASE TABLE"}
  ],
  "table_count": 44
}
```

**使用场景**：
- 探索某个层级有哪些可用的表
- 了解数据仓库的整体结构
- 查找特定业务域的相关表

### 2. describe_table - 查看表结构

**功能**：获取指定表的详细字段信息

**参数**：
- `table` (string, 必需): 表名
- `schema` (string, 默认 "dwd"): 所属 schema

**返回示例**：
```json
{
  "schema": "dwd",
  "table": "dim_member",
  "columns": [
    {
      "name": "member_id",
      "type": "bigint",
      "nullable": "NO",
      "default": null,
      "position": 1
    },
    {
      "name": "site_id",
      "type": "bigint", 
      "nullable": "NO",
      "default": null,
      "position": 2
    }
  ],
  "column_count": 25
}
```

**使用场景**：
- 了解表的字段结构和数据类型
- 确认主键和外键字段
- 编写 SQL 查询前的字段确认

### 3. describe_schemas - 批量获取架构信息

**功能**：一次性获取多个 schema 的完整表结构信息，包含主键信息

**参数**：
- `schemas` (array, 可选): 目标 schema 列表，不传则返回全部六个 schema
- `include_views` (boolean, 默认 false): 是否包含视图
- `max_tables_per_schema` (integer, 默认 500): 每个 schema 最大返回表数量

**返回示例**：
```json
{
  "schemas": {
    "dwd": {
      "table_count": 44,
      "tables": {
        "dim_member": {
          "type": "BASE TABLE",
          "primary_key": ["member_id"],
          "columns": [
            {
              "name": "member_id",
              "type": "bigint",
              "nullable": "NO",
              "default": null,
              "position": 1
            }
          ],
          "column_count": 25
        }
      }
    }
  },
  "include_views": false,
  "limits": {"max_tables_per_schema": 500}
}
```

**使用场景**：
- 快速了解整个数据仓库的结构
- 获取表间关联关系（通过主键信息）
- 为复杂查询做准备

### 4. query_sql - 执行 SQL 查询

**功能**：在指定 schema 内执行只读 SQL 查询

**参数**：
- `schema` (string, 必需): 执行查询的 schema
- `sql` (string, 必需): SQL 查询语句
- `max_rows` (integer, 默认 500): 最大返回行数

**安全限制**：
- 仅允许 SELECT、WITH、SHOW、EXPLAIN 语句
- 禁止 DDL/DML 操作（CREATE、DROP、INSERT、UPDATE、DELETE 等）
- 限制跨 schema 引用（仅允许在指定 schema 内查询）
- 自动设置 `search_path` 到指定 schema

**返回示例**：
```json
{
  "schema": "dwd",
  "columns": ["member_id", "real_name", "mobile", "site_id"],
  "rows": [
    [2947562271297029, "张三", "13800138000", 1001],
    [2947562271297030, "李四", "13800138001", 1001]
  ],
  "row_count": 2,
  "truncated": false,
  "max_rows": 500
}
```

**使用场景**：
- 执行具体的业务查询
- 获取统计分析数据
- 验证数据质量和完整性

---

## 核心业务表详解

### 会员相关表

#### dwd.dim_member - 会员主档
**业务含义**：会员基础信息维度表，包含会员的身份信息、联系方式、等级状态等

**关键字段**：
- `member_id` (BIGINT): 会员唯一标识，飞球系统雪花 ID
- `site_id` (BIGINT): 所属门店 ID，多门店隔离关键字段
- `real_name` (TEXT): 会员真实姓名
- `mobile` (TEXT): 手机号，11 位格式
- `member_no` (TEXT): 会员编号，门店内序号
- `level` (INTEGER): 会员等级，对应不同的服务权益
- `birthday` (DATE): 生日，用于生日营销
- `gender` (INTEGER): 性别，0=未设置，1=男，2=女
- `balance` (NUMERIC): 账户余额，单位元，精度到分
- `member_status` (INTEGER): 会员状态，1=正常，2=冻结
- `create_time` (TIMESTAMPTZ): 注册时间
- `scd2_is_current` (INTEGER): SCD2 当前版本标记，1=当前，0=历史

**查询示例**：
```sql
-- 查询某门店的活跃会员
SELECT member_id, real_name, mobile, balance, level
FROM dim_member 
WHERE site_id = 1001 
  AND member_status = 1 
  AND scd2_is_current = 1
ORDER BY create_time DESC
LIMIT 10;
```

#### dwd.dwd_member_balance_change - 会员余额变动
**业务含义**：记录会员账户余额的每一笔变动，包括充值、消费、退款、系统调整等，支持本金和赠送金分别记录

**关键字段**：
- `balance_change_id` (BIGINT): 变动记录唯一标识（主键）
- `site_id` (BIGINT): 门店 ID
- `tenant_member_id` (BIGINT): 租户维度会员 ID
- `system_member_id` (BIGINT): 系统维度会员 ID（跨租户唯一）
- `member_name` (VARCHAR): 会员姓名快照
- `member_mobile` (VARCHAR): 会员手机号快照
- `from_type` (INTEGER): 变动来源类型，1=结算扣款，2=充值，3=退款返还，4=系统调整，7=转账，9=其他
- `balance_before` (NUMERIC): 变动前余额（元）
- `change_amount` (NUMERIC): 变动金额（元），正数为充入，负数为扣减
- `balance_after` (NUMERIC): 变动后余额（元）
- `principal_before` (NUMERIC): 变动前本金余额（元），不含赠送金
- `principal_after` (NUMERIC): 变动后本金余额（元）
- `principal_change_amount` (NUMERIC): 本金变动金额（元）
- `payment_method` (INTEGER): 支付方式
- `change_time` (TIMESTAMPTZ): 变动发生时间
- `relate_id` (BIGINT): 关联业务单据 ID，按 from_type 指向不同表
- `operator_name` (VARCHAR): 操作员姓名
- `remark` (VARCHAR): 备注说明

**查询示例**：
```sql
-- 查询某会员的余额变动历史
SELECT 
    change_time,
    from_type,
    change_amount,
    balance_before,
    balance_after,
    remark
FROM dwd_member_balance_change 
WHERE site_id = 1001 
  AND tenant_member_id = 2947562271297029
ORDER BY change_time DESC
LIMIT 20;
```

### 订单相关表

#### dwd.dwd_order_head - 订单主表
**业务含义**：台球消费订单的主要信息，记录每次消费的基本情况

**关键字段**：
- `order_id` (BIGINT): 订单唯一标识
- `site_id` (BIGINT): 门店 ID
- `member_id` (BIGINT): 消费会员 ID，0 表示散客
- `table_id` (BIGINT): 台桌 ID
- `assistant_id` (BIGINT): 服务助教 ID
- `order_status` (INTEGER): 订单状态，1=进行中，2=已结算，3=已取消
- `start_time` (TIMESTAMPTZ): 开台时间
- `end_time` (TIMESTAMPTZ): 结台时间
- `duration_minutes` (INTEGER): 消费时长（分钟）
- `total_amount` (NUMERIC): 订单总金额
- `table_fee` (NUMERIC): 台费
- `service_fee` (NUMERIC): 服务费
- `goods_amount` (NUMERIC): 商品金额
- `discount_amount` (NUMERIC): 优惠金额
- `actual_amount` (NUMERIC): 实付金额

**查询示例**：
```sql
-- 查询某门店今日订单统计
SELECT 
    COUNT(*) as order_count,
    SUM(actual_amount) as total_revenue,
    AVG(duration_minutes) as avg_duration
FROM dwd_order_head 
WHERE site_id = 1001 
  AND DATE(start_time) = CURRENT_DATE
  AND order_status = 2;
```

#### dwd.dwd_settlement_head - 结算主表
**业务含义**：订单的结算信息，包含支付方式、优惠使用等详细信息

**关键字段**：
- `settlement_id` (BIGINT): 结算记录唯一标识
- `order_id` (BIGINT): 关联订单 ID
- `site_id` (BIGINT): 门店 ID
- `member_id` (BIGINT): 会员 ID
- `settle_type` (INTEGER): 结算类型，1=正常消费，3=退款，5=充值
- `pay_method` (INTEGER): 支付方式，1=现金，2=微信，3=支付宝，4=余额
- `total_amount` (NUMERIC): 结算总金额
- `discount_amount` (NUMERIC): 优惠金额
- `actual_amount` (NUMERIC): 实付金额
- `settle_time` (TIMESTAMPTZ): 结算时间

### 助教相关表

#### dwd.dim_assistant - 助教主档
**业务含义**：助教基础信息维度表，记录助教的身份和工作信息

**关键字段**：
- `assistant_id` (BIGINT): 助教唯一标识
- `site_id` (BIGINT): 所属门店 ID
- `assistant_no` (TEXT): 助教编号，门店内序号
- `real_name` (TEXT): 助教真实姓名
- `nickname` (TEXT): 昵称，用于小程序展示
- `mobile` (TEXT): 手机号
- `level` (INTEGER): 助教等级，对应技能水平
- `team_id` (BIGINT): 所属团队 ID，0 表示未分组
- `team_name` (TEXT): 团队名称
- `entry_time` (TIMESTAMPTZ): 入职时间
- `resign_time` (TIMESTAMPTZ): 离职时间，NULL 表示在职
- `leave_status` (INTEGER): 在职状态，0=在职，1=已离职
- `assistant_status` (INTEGER): 助教状态，1=正常，2=停用

#### dwd.dwd_assistant_service_log - 助教服务记录
**业务含义**：记录助教的每次服务活动，包含陪打和促销两种服务类型，用于业绩统计和服务质量分析

**关键字段**：
- `assistant_service_id` (BIGINT): 服务记录唯一标识（主键）
- `order_trade_no` (BIGINT): 关联的订单交易号
- `site_assistant_id` (BIGINT): 门店维度助教档案 ID，关联 dim_assistant.assistant_id
- `site_id` (BIGINT): 门店 ID
- `tenant_member_id` (BIGINT): 租户维度会员 ID
- `order_assistant_type` (INTEGER): 助教服务类型，1=陪打，2=促销
- `assistant_no` (VARCHAR): 助教编号（门店内序号）
- `nickname` (VARCHAR): 助教昵称
- `level_name` (VARCHAR): 助教等级名称，如"高级"、"中级"
- `skill_name` (VARCHAR): 服务技能名称，如"陪打"、"促销"
- `ledger_unit_price` (NUMERIC): 分账单价（元/小时）
- `ledger_amount` (NUMERIC): 分账总金额（元）
- `real_service_money` (NUMERIC): 实际服务金额（元），扣除折扣后的实收
- `income_seconds` (INTEGER): 计费时长（秒）
- `real_use_seconds` (INTEGER): 实际使用时长（秒）
- `start_use_time` (TIMESTAMPTZ): 服务开始时间
- `last_use_time` (TIMESTAMPTZ): 服务结束时间
- `composite_grade` (NUMERIC): 综合评分，如 4.50
- `is_trash` (INTEGER): 是否已废除，0=正常，1=已废除
- `trash_reason` (VARCHAR): 废除原因

### 台桌相关表

#### dwd.dim_table - 台桌维表
**业务含义**：门店台桌基础信息维度表，记录每个台桌的基本配置、区域归属、价格等信息

**关键字段**：
- `table_id` (BIGINT): 台桌唯一标识（主键）
- `site_id` (BIGINT): 门店 ID
- `table_name` (TEXT): 台桌名称，如"1号台"、"VIP1"
- `site_table_area_id` (BIGINT): 门店台桌区域 ID
- `site_table_area_name` (TEXT): 台桌区域名称，如"大厅"、"VIP区"
- `table_price` (NUMERIC): 台费单价（元/小时）
- `order_id` (BIGINT): 当前关联的订单 ID，0表示空闲
- `table_status` (INTEGER): 台桌状态枚举
- `is_online_reservation` (INTEGER): 是否支持在线预约，0=否，1=是
- `light_status` (INTEGER): 台灯状态，1=已关灯，2=已开灯
- `charge_free` (INTEGER): 是否免费台，0=收费，1=免费
- `virtual_table` (INTEGER): 是否虚拟台桌，0=实体台，1=虚拟台
- `applet_qr_code_url` (TEXT): 小程序二维码 URL，用于扫码开台
- `delay_lights_time` (INTEGER): 台灯熄灭延迟时间（秒）

**查询示例**：
```sql
-- 查询某门店的台桌使用情况
SELECT 
    table_name,
    site_table_area_name,
    table_price,
    CASE WHEN order_id = 0 THEN '空闲' ELSE '使用中' END as status,
    light_status
FROM dim_table 
WHERE site_id = 1001 
  AND scd2_is_current = 1
ORDER BY site_table_area_name, table_name;
```

### 商品库存相关表

#### dwd.dim_store_goods - 门店商品档案
**业务含义**：门店级商品主档，记录每个门店销售的商品基本信息、价格、库存状态等

**关键字段**：
- `site_goods_id` (BIGINT): 门店商品唯一标识（主键）
- `site_id` (BIGINT): 门店 ID
- `tenant_goods_id` (BIGINT): 租户商品 ID，关联 dim_tenant_goods
- `goods_name` (TEXT): 商品名称，如"百威啤酒"
- `goods_category_id` (BIGINT): 一级分类 ID
- `goods_second_category_id` (BIGINT): 二级分类 ID
- `category_level1_name` (TEXT): 一级分类名称，如"酒水"
- `category_level2_name` (TEXT): 二级分类名称，如"啤酒"
- `sale_price` (NUMERIC): 商品售价（元）
- `cost_price` (NUMERIC): 成本价（元）
- `stock_qty` (INTEGER): 当前库存数量（实时库存）
- `batch_stock_qty` (INTEGER): 批次库存数量（按批次管理的库存）
- `safety_stock_qty` (INTEGER): 安全库存数量（低于此值触发预警）
- `sale_qty` (INTEGER): 累计销售数量
- `avg_monthly_sales` (NUMERIC): 月均销量
- `goods_state` (INTEGER): 商品状态
- `not_sale` (INTEGER): 是否停售，0=在售，1=停售
- `is_discountable` (INTEGER): 是否可打折，0=不可，1=可
- `unit` (TEXT): 计量单位，如"瓶"、"包"
- `commodity_code` (TEXT): 商品编码

#### dwd.dwd_goods_stock_movement - 库存变动流水
**业务含义**：记录商品库存的每一笔变动，包括销售出库、采购入库、退货、领用等

**关键字段**：
- `site_goods_stock_id` (BIGINT): 库存变动记录唯一标识（主键）
- `site_id` (BIGINT): 门店 ID
- `site_goods_id` (BIGINT): 门店商品 ID，关联 dim_store_goods
- `goods_name` (TEXT): 商品名称
- `stock_type` (INTEGER): 库存变动类型，1=销售出库，2=采购入库，4=退货入库，7=采购退货，8=领用出库，9=领用退回
- `change_num` (NUMERIC): 变动数量（正数为增加，负数为减少）
- `start_num` (NUMERIC): 变动前库存量
- `end_num` (NUMERIC): 变动后库存量
- `price` (NUMERIC): 商品单价（元）
- `operator_name` (TEXT): 操作人姓名
- `create_time` (TIMESTAMPTZ): 变动发生时间
- `remark` (TEXT): 备注说明，如"结账退货"、"采购退货"、"系统自动领用"

**查询示例**：
```sql
-- 查询某商品的库存变动历史
SELECT 
    create_time,
    stock_type,
    change_num,
    start_num,
    end_num,
    operator_name,
    remark
FROM dwd_goods_stock_movement 
WHERE site_id = 1001 
  AND goods_name LIKE '%百威%'
ORDER BY create_time DESC
LIMIT 20;
```

#### dwd.dwd_recharge_order - 充值订单
**业务含义**：记录会员充值的详细信息，包括充值金额、支付方式、优惠活动等

**关键字段**：
- `recharge_order_id` (BIGINT): 充值记录唯一标识（主键）
- `site_id` (BIGINT): 门店 ID
- `member_id` (BIGINT): 会员 ID
- `member_name_snapshot` (TEXT): 会员姓名快照
- `member_phone_snapshot` (TEXT): 会员手机号快照
- `tenant_member_card_id` (BIGINT): 会员卡 ID
- `member_card_type_name` (TEXT): 会员卡类型名称，如"普通会员卡"
- `settle_type` (INTEGER): 结算类型枚举
- `is_first` (INTEGER): 是否首次充值，0=否，1=是
- `pay_amount` (NUMERIC): 实付金额（元）
- `cash_amount` (NUMERIC): 现金支付金额（元）
- `online_amount` (NUMERIC): 线上支付金额（元）
- `balance_amount` (NUMERIC): 余额支付金额（元）
- `payment_method` (TEXT): 支付方式，如"微信"、"支付宝"
- `create_time` (TIMESTAMPTZ): 充值记录创建时间
- `pay_time` (TIMESTAMPTZ): 支付完成时间
- `is_activity` (BOOLEAN): 是否参与活动
- `is_use_coupon` (BOOLEAN): 是否使用优惠券
- `operator_name_snapshot` (TEXT): 操作员姓名快照

#### dwd.dim_tenant_goods - 租户商品档案
**业务含义**：租户级商品主档，记录租户下所有商品的基本信息，是门店商品的上级概念

**关键字段**：
- `tenant_goods_id` (BIGINT): 租户商品唯一标识（主键）
- `tenant_id` (BIGINT): 租户 ID
- `supplier_id` (BIGINT): 供应商 ID
- `goods_name` (VARCHAR): 商品名称，如"百威啤酒"
- `goods_number` (VARCHAR): 商品编号
- `category_name` (VARCHAR): 一级分类名称，如"酒水"
- `goods_category_id` (BIGINT): 一级分类 ID
- `goods_second_category_id` (BIGINT): 二级分类 ID
- `unit` (VARCHAR): 计量单位，如"瓶"、"包"
- `market_price` (NUMERIC): 市场价/建议售价（元）
- `cost_price` (NUMERIC): 成本价（元）
- `min_discount_price` (NUMERIC): 最低折扣价（元）
- `goods_state` (INTEGER): 商品状态
- `not_sale` (INTEGER): 是否停售，0=在售，1=停售
- `able_discount` (INTEGER): 是否允许折扣，0=不允许，1=允许
- `is_warehousing` (INTEGER): 是否入库管理，0=否，1=是
- `goods_bar_code` (VARCHAR): 商品条码
- `commodity_code_list` (TEXT[]): 商品编码数组
- `pinyin_initial` (VARCHAR): 商品名称拼音首字母，用于快速检索

#### dwd.dwd_goods_stock_summary - 库存汇总快照
**业务含义**：按时间窗口汇总的商品库存变动统计，记录期初期末库存、进销存数据

**关键字段**：
- `site_goods_id` (BIGINT): 门店商品 ID（主键之一）
- `fetched_at` (TIMESTAMPTZ): ETL采集时间戳（主键之一）
- `goods_name` (TEXT): 商品名称
- `goods_unit` (TEXT): 计量单位
- `category_name` (TEXT): 一级分类名称
- `range_start_stock` (NUMERIC): 期初库存（查询时间窗口起点的库存量）
- `range_end_stock` (NUMERIC): 期末库存（查询时间窗口终点的库存量）
- `range_in` (NUMERIC): 入库数量（采购/调拨入库总量）
- `range_out` (NUMERIC): 出库数量（调拨出库/报损总量）
- `range_sale` (NUMERIC): 销售数量（销售出库总量）
- `range_sale_money` (NUMERIC): 销售金额（元）
- `range_inventory` (NUMERIC): 盘点调整量（盘盈/盘亏净量）
- `current_stock` (NUMERIC): 当前库存（API返回时的实时库存量）

### 小程序业务表（zqyy_app.biz Schema）

#### biz.coach_tasks - 助教任务表
**业务含义**：小程序助教任务系统的核心表，记录任务分配、状态、有效期、置顶、放弃原因等

**关键字段**：
- `id` (BIGSERIAL): 自增主键
- `site_id` (BIGINT): 门店 ID（多门店隔离）
- `assistant_id` (BIGINT): 助教 ID
- `member_id` (BIGINT): 客户 ID
- `task_type` (VARCHAR): 任务类型，high_priority_recall/priority_recall/follow_up_visit/relationship_building
- `status` (VARCHAR): 状态，active/inactive/completed/abandoned
- `priority_score` (NUMERIC): 优先级分数，取 max(WBI, NCI) 快照
- `expires_at` (TIMESTAMPTZ): 有效期时间戳，NULL表示无限期
- `is_pinned` (BOOLEAN): 是否置顶
- `abandon_reason` (TEXT): 放弃原因（放弃时必填）
- `completed_at` (TIMESTAMPTZ): 完成时间
- `parent_task_id` (BIGINT): 父任务 ID（自引用）

#### biz.notes - 统一备注表
**业务含义**：小程序备注系统，通过 type 字段区分普通备注/回访备注/放弃原因，含星星评分

**关键字段**：
- `id` (BIGSERIAL): 自增主键
- `site_id` (BIGINT): 门店 ID
- `user_id` (INTEGER): 小程序用户 ID
- `target_type` (VARCHAR): 目标类型（如 member）
- `target_id` (BIGINT): 目标 ID（如 member_id）
- `type` (VARCHAR): 备注类型，normal/follow_up/abandon_reason
- `content` (TEXT): 备注内容
- `rating_service_willingness` (SMALLINT): 再次服务意愿评分（1-5）
- `rating_revisit_likelihood` (SMALLINT): 再来店可能性评分（1-5）
- `task_id` (BIGINT): 关联任务 ID
- `ai_score` (SMALLINT): AI应用6评分（P5实现）
- `ai_analysis` (TEXT): AI分析结果（P5实现）

#### biz.trigger_jobs - 触发器配置表
**业务含义**：存储 cron/interval/event 三种触发方式的配置与执行状态

**关键字段**：
- `id` (SERIAL): 自增主键
- `job_type` (VARCHAR): 任务类型标识，映射到 Python handler
- `job_name` (VARCHAR): 任务名称（唯一）
- `trigger_condition` (VARCHAR): 触发方式，cron/interval/event
- `trigger_config` (JSONB): 触发配置（cron表达式/间隔秒数/事件名）
- `last_run_at` (TIMESTAMPTZ): 上次运行时间
- `next_run_at` (TIMESTAMPTZ): 下次运行时间（event类型为NULL）
- `status` (VARCHAR): 状态，enabled/disabled

**查询示例**：
```sql
-- 查询助教的活跃任务
SELECT 
    t.task_type,
    m.real_name as member_name,
    t.priority_score,
    t.expires_at,
    t.is_pinned,
    t.created_at
FROM biz.coach_tasks t
JOIN dim_member m ON t.member_id = m.member_id AND t.site_id = m.site_id
WHERE t.site_id = 1001 
  AND t.assistant_id = 2947562271297029
  AND t.status = 'active'
  AND m.scd2_is_current = 1
ORDER BY t.is_pinned DESC, t.priority_score DESC;
```

#### dws.dws_member_daily_summary - 会员日汇总
**业务含义**：按会员和日期汇总的消费数据，用于会员行为分析

**关键字段**：
- `site_id` (BIGINT): 门店 ID
- `member_id` (BIGINT): 会员 ID
- `stat_date` (DATE): 统计日期（营业日）
- `visit_count` (INTEGER): 到店次数
- `order_count` (INTEGER): 消费笔数
- `total_amount` (NUMERIC): 消费总金额
- `table_fee` (NUMERIC): 台费总额
- `service_fee` (NUMERIC): 服务费总额
- `goods_amount` (NUMERIC): 商品消费总额
- `play_minutes` (INTEGER): 游戏时长（分钟）
- `avg_order_amount` (NUMERIC): 平均客单价

#### dws.dws_assistant_daily_detail - 助教日业绩明细
**业务含义**：按助教和日期汇总的业绩数据，用于助教绩效考核

**关键字段**：
- `site_id` (BIGINT): 门店 ID
- `assistant_id` (BIGINT): 助教 ID
- `stat_date` (DATE): 统计日期
- `service_count` (INTEGER): 服务次数
- `service_minutes` (INTEGER): 服务时长
- `service_revenue` (NUMERIC): 服务收入
- `member_count` (INTEGER): 服务会员数
- `avg_rating` (NUMERIC): 平均评分
- `new_member_count` (INTEGER): 新增会员数
- `repeat_member_count` (INTEGER): 复购会员数

#### dws.dws_assistant_order_contribution - 助教订单贡献统计
**业务含义**：助教订单流水四项统计，用于精确计算助教的业绩贡献，支持多种分成模式

**关键字段**：
- `contribution_id` (BIGINT): 记录唯一标识，自增主键
- `site_id` (INTEGER): 门店 ID
- `assistant_id` (BIGINT): 助教 ID
- `assistant_nickname` (VARCHAR): 助教昵称
- `stat_date` (DATE): 统计日期
- `order_gross_revenue` (NUMERIC): 订单总流水 = 台费 + 酒水食品 + 所有助教服务费
- `order_net_revenue` (NUMERIC): 订单净流水 = 订单总流水 - 所有助教服务分成
- `time_weighted_revenue` (NUMERIC): 时效贡献流水 = 台费按时长分摊 + 个人服务费 + 酒水食品按时长比例
- `time_weighted_net_revenue` (NUMERIC): 时效净贡献 = 时效贡献流水 - 个人服务分成
- `order_count` (INTEGER): 当日参与订单数
- `total_service_seconds` (INTEGER): 当日总服务时长（秒）

**四项统计说明**：
1. **订单总流水**：助教参与的所有订单的总收入，每个参与助教获得相同值
2. **订单净流水**：扣除所有助教分成后的净收入，每个参与助教获得相同值  
3. **时效贡献流水**：按服务时长比例分摊的收入，体现个人贡献度
4. **时效净贡献**：扣除个人分成后的净贡献，最终的业绩指标

**查询示例**：
```sql
-- 助教业绩四项统计对比
SELECT 
    assistant_nickname,
    stat_date,
    order_gross_revenue,
    order_net_revenue,
    time_weighted_revenue,
    time_weighted_net_revenue,
    order_count,
    total_service_seconds/3600.0 as service_hours
FROM dws_assistant_order_contribution
WHERE site_id = 1001 
  AND stat_date >= CURRENT_DATE - INTERVAL '7 days'
ORDER BY stat_date DESC, time_weighted_net_revenue DESC;
```

#### dws.dws_goods_stock_daily_summary - 商品库存日汇总
**业务含义**：按日汇总的商品库存变动统计，用于库存分析和补货决策

**关键字段**：
- `site_id` (BIGINT): 门店 ID
- `stat_date` (DATE): 统计日期
- `site_goods_id` (BIGINT): 门店商品 ID
- `goods_name` (TEXT): 商品名称
- `goods_unit` (TEXT): 计量单位
- `category_name` (TEXT): 一级分类名称
- `range_start_stock` (NUMERIC): 期初库存，统计日期开始时的库存量
- `range_end_stock` (NUMERIC): 期末库存，统计日期结束时的库存量
- `range_in` (NUMERIC): 入库数量，采购/调拨入库总量
- `range_out` (NUMERIC): 出库数量，调拨出库/报损总量
- `range_sale` (NUMERIC): 销售数量，销售出库总量
- `range_sale_money` (NUMERIC): 销售金额，销售总金额
- `range_inventory` (NUMERIC): 盘点调整量，盘盈/盘亏净量
- `current_stock` (NUMERIC): 当前库存，统计日期末的实时库存量
- `stat_period` (TEXT): 汇总粒度标识，'daily'

**查询示例**：
```sql
-- 商品库存变动分析
SELECT 
    goods_name,
    category_name,
    range_start_stock,
    range_in,
    range_sale,
    range_out,
    range_end_stock,
    range_sale_money,
    CASE 
        WHEN range_sale > 0 THEN range_sale_money / range_sale 
        ELSE 0 
    END as avg_sale_price
FROM dws_goods_stock_daily_summary
WHERE site_id = 1001 
  AND stat_date = CURRENT_DATE - INTERVAL '1 day'
  AND (range_in > 0 OR range_sale > 0 OR range_out > 0)
ORDER BY range_sale_money DESC;

-- 库存周转率分析
SELECT 
    goods_name,
    category_name,
    AVG(range_start_stock + range_end_stock) / 2 as avg_stock,
    SUM(range_sale) as total_sale,
    CASE 
        WHEN AVG(range_start_stock + range_end_stock) > 0 
        THEN SUM(range_sale) / (AVG(range_start_stock + range_end_stock) / 2) 
        ELSE 0 
    END as turnover_rate
FROM dws_goods_stock_daily_summary
WHERE site_id = 1001 
  AND stat_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY site_goods_id, goods_name, category_name
HAVING SUM(range_sale) > 0
ORDER BY turnover_rate DESC;
```

#### dws.dws_assistant_order_contribution - 助教订单贡献统计
**业务含义**：助教订单流水四项统计，用于精确计算助教的业绩贡献，支持多种分成模式

**关键字段**：
- `contribution_id` (BIGINT): 贡献记录唯一标识（主键）
- `site_id` (BIGINT): 门店 ID
- `assistant_id` (BIGINT): 助教 ID
- `order_trade_no` (BIGINT): 订单交易号
- `member_id` (BIGINT): 会员 ID
- `stat_date` (DATE): 统计日期（营业日）
- `contribution_type` (VARCHAR): 贡献类型，如"service_fee"、"table_fee"等
- `base_amount` (NUMERIC): 基础金额（元）
- `contribution_amount` (NUMERIC): 贡献金额（元）
- `contribution_rate` (NUMERIC): 贡献比例（0-1）
- `calculation_method` (VARCHAR): 计算方法
- `is_primary_assistant` (BOOLEAN): 是否主要助教
- `service_duration_minutes` (INTEGER): 服务时长（分钟）

#### dws.dws_goods_stock_daily_summary - 商品库存日汇总
**业务含义**：按日汇总的商品库存变动统计，用于库存分析和补货决策

**关键字段**：
- `site_id` (BIGINT): 门店 ID
- `site_goods_id` (BIGINT): 门店商品 ID
- `stat_date` (DATE): 统计日期
- `goods_name` (TEXT): 商品名称
- `category_name` (TEXT): 商品分类
- `opening_stock` (NUMERIC): 期初库存
- `closing_stock` (NUMERIC): 期末库存
- `purchase_in` (NUMERIC): 采购入库量
- `sales_out` (NUMERIC): 销售出库量
- `transfer_in` (NUMERIC): 调拨入库量
- `transfer_out` (NUMERIC): 调拨出库量
- `adjustment_qty` (NUMERIC): 盘点调整量
- `total_sales_amount` (NUMERIC): 销售总金额（元）
- `avg_selling_price` (NUMERIC): 平均售价（元）
- `stock_turnover_days` (NUMERIC): 库存周转天数

**查询示例**：
```sql
-- 查询库存周转分析
SELECT 
    goods_name,
    category_name,
    AVG(stock_turnover_days) as avg_turnover_days,
    SUM(sales_out) as total_sales_qty,
    SUM(total_sales_amount) as total_sales_amount
FROM dws_goods_stock_daily_summary 
WHERE site_id = 1001 
  AND stat_date >= CURRENT_DATE - INTERVAL '30 days'
  AND sales_out > 0
GROUP BY goods_name, category_name
ORDER BY avg_turnover_days ASC;
```

### 指数分析表

#### dws.dws_member_spending_power_index - 消费力指数 (SPI)
**业务含义**：会员消费能力的综合评分，用于精准营销和客户分层

**关键字段**：
- `spi_id` (BIGINT): 记录唯一标识
- `site_id` (INTEGER): 门店 ID
- `member_id` (BIGINT): 会员 ID
- `spend_30` (NUMERIC): 近 30 天消费总额
- `spend_90` (NUMERIC): 近 90 天消费总额
- `recharge_90` (NUMERIC): 近 90 天充值总额
- `orders_30` (INTEGER): 近 30 天消费笔数
- `orders_90` (INTEGER): 近 90 天消费笔数
- `visit_days_30` (INTEGER): 近 30 天消费日数
- `visit_days_90` (INTEGER): 近 90 天消费日数
- `avg_ticket_90` (NUMERIC): 90 天平均客单价
- `active_weeks_90` (INTEGER): 近 90 天活跃周数
- `display_score` (NUMERIC): SPI 展示分（0-10 分）
- `calc_time` (TIMESTAMPTZ): 计算时间

**查询示例**：
```sql
-- 查询高价值会员（SPI > 8 分）
SELECT m.real_name, m.mobile, s.display_score, s.spend_90
FROM dws_member_spending_power_index s
JOIN dim_member m ON s.member_id = m.member_id AND s.site_id = m.site_id
WHERE s.site_id = 1001 
  AND s.display_score > 8.0
  AND m.scd2_is_current = 1
ORDER BY s.display_score DESC;
```

---

## 常用查询模式

### 1. 会员分析查询

```sql
-- 会员消费趋势分析
SELECT 
    stat_date,
    COUNT(DISTINCT member_id) as active_members,
    SUM(total_amount) as daily_revenue,
    AVG(avg_order_amount) as avg_ticket
FROM dws_member_daily_summary 
WHERE site_id = 1001 
  AND stat_date >= CURRENT_DATE - INTERVAL '30 days'
GROUP BY stat_date
ORDER BY stat_date;

-- 会员等级分布
SELECT 
    level,
    COUNT(*) as member_count,
    AVG(balance) as avg_balance
FROM dim_member 
WHERE site_id = 1001 
  AND member_status = 1 
  AND scd2_is_current = 1
GROUP BY level
ORDER BY level;
```

### 2. 助教业绩查询

```sql
-- 助教月度业绩排行
SELECT 
    a.real_name,
    a.nickname,
    SUM(d.service_revenue) as total_revenue,
    SUM(d.service_minutes) as total_minutes,
    AVG(d.avg_rating) as avg_rating
FROM dws_assistant_daily_detail d
JOIN dim_assistant a ON d.assistant_id = a.assistant_id 
  AND d.site_id = a.site_id
WHERE d.site_id = 1001 
  AND d.stat_date >= DATE_TRUNC('month', CURRENT_DATE)
  AND a.scd2_is_current = 1
GROUP BY a.assistant_id, a.real_name, a.nickname
ORDER BY total_revenue DESC;
```

### 3. 经营分析查询

```sql
-- 门店经营概况
SELECT 
    DATE_TRUNC('week', stat_date) as week_start,
    SUM(total_revenue) as weekly_revenue,
    SUM(order_count) as weekly_orders,
    AVG(avg_order_amount) as avg_ticket
FROM dws_finance_daily_summary 
WHERE site_id = 1001 
  AND stat_date >= CURRENT_DATE - INTERVAL '8 weeks'
GROUP BY DATE_TRUNC('week', stat_date)
ORDER BY week_start;

-- 收入结构分析
SELECT 
    SUM(table_revenue) as table_income,
    SUM(service_revenue) as service_income,
    SUM(goods_revenue) as goods_income,
    SUM(total_revenue) as total_income
FROM dws_finance_daily_summary 
WHERE site_id = 1001 
  AND stat_date >= CURRENT_DATE - INTERVAL '30 days';
```

### 4. 会员行为分析查询

```sql
-- 会员生命周期分析
SELECT 
    CASE 
        WHEN s.display_score >= 8.0 THEN '高价值客户'
        WHEN s.display_score >= 6.0 THEN '中价值客户'
        WHEN s.display_score >= 4.0 THEN '普通客户'
        ELSE '低价值客户'
    END as customer_segment,
    COUNT(*) as member_count,
    AVG(s.spend_90) as avg_spend_90d,
    AVG(s.visit_days_90) as avg_active_days,
    AVG(s.avg_ticket_90) as avg_ticket
FROM dws_member_spending_power_index s
WHERE s.site_id = 1001
GROUP BY customer_segment
ORDER BY AVG(s.display_score) DESC;

-- 会员流失风险分析
SELECT 
    m.real_name,
    m.mobile,
    s.spend_90,
    s.visit_days_90,
    s.display_score,
    EXTRACT(days FROM NOW() - MAX(md.stat_date)) as days_since_last_visit
FROM dws_member_spending_power_index s
JOIN dim_member m ON s.member_id = m.member_id AND s.site_id = m.site_id
LEFT JOIN dws_member_daily_summary md ON s.member_id = md.member_id AND s.site_id = md.site_id
WHERE s.site_id = 1001 
  AND s.display_score >= 6.0  -- 高价值客户
  AND m.scd2_is_current = 1
GROUP BY m.real_name, m.mobile, s.spend_90, s.visit_days_90, s.display_score
HAVING EXTRACT(days FROM NOW() - MAX(md.stat_date)) > 30  -- 30天未到店
ORDER BY s.display_score DESC;
```

### 5. 助教绩效分析查询

```sql
-- 助教服务质量分析
SELECT 
    a.real_name,
    a.level_name,
    COUNT(asl.assistant_service_id) as service_count,
    SUM(asl.real_use_seconds)/3600.0 as total_hours,
    AVG(asl.composite_grade) as avg_rating,
    SUM(asl.real_service_money) as total_revenue,
    COUNT(DISTINCT asl.tenant_member_id) as unique_members
FROM dwd_assistant_service_log asl
JOIN dim_assistant a ON asl.site_assistant_id = a.assistant_id 
  AND asl.site_id = a.site_id
WHERE asl.site_id = 1001 
  AND asl.start_use_time >= CURRENT_DATE - INTERVAL '30 days'
  AND asl.is_trash = 0  -- 排除已废除的服务
  AND a.scd2_is_current = 1
GROUP BY a.assistant_id, a.real_name, a.level_name
ORDER BY total_revenue DESC;

-- 助教任务完成情况
SELECT 
    a.real_name,
    COUNT(CASE WHEN t.status = 'active' THEN 1 END) as active_tasks,
    COUNT(CASE WHEN t.status = 'completed' THEN 1 END) as completed_tasks,
    COUNT(CASE WHEN t.status = 'abandoned' THEN 1 END) as abandoned_tasks,
    AVG(CASE WHEN t.status = 'completed' 
        THEN EXTRACT(days FROM t.completed_at - t.created_at) END) as avg_completion_days
FROM biz.coach_tasks t
JOIN dim_assistant a ON t.assistant_id = a.assistant_id AND t.site_id = a.site_id
WHERE t.site_id = 1001 
  AND t.created_at >= CURRENT_DATE - INTERVAL '30 days'
  AND a.scd2_is_current = 1
GROUP BY a.assistant_id, a.real_name
ORDER BY completed_tasks DESC;
```

### 6. 库存管理查询

```sql
-- 库存预警分析
SELECT 
    sg.goods_name,
    sg.category_level1_name,
    sg.stock_qty as current_stock,
    sg.safety_stock_qty,
    sg.avg_monthly_sales,
    CASE 
        WHEN sg.stock_qty <= sg.safety_stock_qty THEN '库存不足'
        WHEN sg.stock_qty <= sg.safety_stock_qty * 1.5 THEN '库存偏低'
        ELSE '库存正常'
    END as stock_status,
    CASE 
        WHEN sg.avg_monthly_sales > 0 
        THEN sg.stock_qty / (sg.avg_monthly_sales / 30.0)
        ELSE NULL 
    END as estimated_days_remaining
FROM dim_store_goods sg
WHERE sg.site_id = 1001 
  AND sg.scd2_is_current = 1
  AND sg.not_sale = 0  -- 在售商品
  AND sg.stock_qty <= sg.safety_stock_qty * 2  -- 关注库存偏低的商品
ORDER BY stock_status, estimated_days_remaining ASC;

-- 商品销售趋势分析
SELECT 
    gsm.goods_name,
    gsm.category_name,
    DATE_TRUNC('week', gsm.fetched_at) as week_start,
    AVG(gsm.range_sale) as avg_weekly_sales,
    AVG(gsm.range_sale_money) as avg_weekly_revenue,
    AVG(gsm.current_stock) as avg_stock_level
FROM dwd_goods_stock_summary gsm
WHERE gsm.site_id = 1001 
  AND gsm.fetched_at >= CURRENT_DATE - INTERVAL '8 weeks'
  AND gsm.range_sale > 0
GROUP BY gsm.goods_name, gsm.category_name, DATE_TRUNC('week', gsm.fetched_at)
ORDER BY gsm.goods_name, week_start;
```

### 7. 财务分析查询

```sql
-- 收入结构趋势分析
SELECT 
    stat_date,
    total_revenue,
    table_revenue,
    service_revenue,
    goods_revenue,
    ROUND(table_revenue / NULLIF(total_revenue, 0) * 100, 2) as table_revenue_pct,
    ROUND(service_revenue / NULLIF(total_revenue, 0) * 100, 2) as service_revenue_pct,
    ROUND(goods_revenue / NULLIF(total_revenue, 0) * 100, 2) as goods_revenue_pct
FROM dws_finance_daily_summary 
WHERE site_id = 1001 
  AND stat_date >= CURRENT_DATE - INTERVAL '30 days'
  AND total_revenue > 0
ORDER BY stat_date DESC;

-- 会员充值分析
SELECT 
    DATE_TRUNC('month', ro.pay_time) as month,
    COUNT(*) as recharge_count,
    COUNT(CASE WHEN ro.is_first = 1 THEN 1 END) as first_time_count,
    SUM(ro.pay_amount) as total_recharge_amount,
    AVG(ro.pay_amount) as avg_recharge_amount,
    COUNT(DISTINCT ro.member_id) as unique_members
FROM dwd_recharge_order ro
WHERE ro.site_id = 1001 
  AND ro.pay_time >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY DATE_TRUNC('month', ro.pay_time)
ORDER BY month DESC;
```

### 5. 商品分析查询

```sql
-- 商品销售排行
SELECT 
    g.goods_name,
    g.category_level1_name,
    SUM(s.ledger_count) as total_sold,
    SUM(s.real_goods_money) as total_revenue,
    AVG(s.ledger_unit_price) as avg_price,
    COUNT(DISTINCT s.order_trade_no) as order_count
FROM dwd_store_goods_sale s
JOIN dim_store_goods g ON s.site_goods_id = g.site_goods_id 
  AND s.site_id = g.site_id
WHERE s.site_id = 1001 
  AND DATE(s.create_time) >= CURRENT_DATE - INTERVAL '30 days'
  AND g.scd2_is_current = 1
GROUP BY g.site_goods_id, g.goods_name, g.category_level1_name
ORDER BY total_revenue DESC
LIMIT 20;

-- 库存预警商品
SELECT 
    g.goods_name,
    g.category_level1_name,
    ex.stock_qty as current_stock,
    ex.safety_stock_qty as safety_stock,
    g.avg_monthly_sales,
    CASE 
        WHEN ex.stock_qty <= 0 THEN '缺货'
        WHEN ex.stock_qty <= ex.safety_stock_qty THEN '库存预警'
        ELSE '库存正常'
    END as stock_status
FROM dim_store_goods g
JOIN dim_store_goods_ex ex ON g.site_goods_id = ex.site_goods_id
WHERE g.site_id = 1001 
  AND g.scd2_is_current = 1
  AND g.goods_state = 1
  AND (ex.stock_qty <= ex.safety_stock_qty OR ex.stock_qty <= 0)
ORDER BY ex.stock_qty ASC;
```

### 6. 助教业绩深度分析

```sql
-- 助教四项统计分析（基于订单贡献表）
SELECT 
    a.real_name,
    a.nickname,
    COUNT(*) as stat_days,
    SUM(c.order_count) as total_orders,
    SUM(c.total_service_seconds)/3600.0 as total_service_hours,
    AVG(c.order_gross_revenue) as avg_gross_revenue,
    AVG(c.order_net_revenue) as avg_net_revenue,
    AVG(c.time_weighted_revenue) as avg_time_weighted_revenue,
    AVG(c.time_weighted_net_revenue) as avg_time_weighted_net_revenue
FROM dws_assistant_order_contribution c
JOIN dim_assistant a ON c.assistant_id = a.assistant_id 
  AND c.site_id = a.site_id
WHERE c.site_id = 1001 
  AND c.stat_date >= CURRENT_DATE - INTERVAL '30 days'
  AND a.scd2_is_current = 1
GROUP BY a.assistant_id, a.real_name, a.nickname
ORDER BY AVG(c.time_weighted_net_revenue) DESC;
```
  AND m.scd2_is_current = 1
ORDER BY s.display_score DESC, s.spend_90 DESC;
```

---

## 安全与限制

### 访问控制
- **只读权限**：仅允许 SELECT、WITH、SHOW、EXPLAIN 操作
- **Schema 隔离**：查询自动限制在指定 schema 内，禁止跨 schema 引用
- **行数限制**：单次查询最多返回 500 行数据（可配置）
- **Token 鉴权**：支持可选的 Bearer Token 认证

### 数据安全
- **敏感信息**：手机号、身份证等敏感字段已脱敏处理
- **多门店隔离**：通过 `site_id` 实现数据隔离，防止跨门店数据泄露
- **审计日志**：所有查询操作都有完整的访问日志记录

### 性能优化
- **索引优化**：关键查询字段都建立了合适的索引
- **物化视图**：常用汇总查询使用物化视图加速
- **连接池**：使用连接池管理数据库连接，提高并发性能

---

## 故障排查

### 常见错误

1. **Schema 不存在**
   - 错误：`存在不允许的 schema`
   - 解决：检查 schema 名称是否正确，仅支持 `ods`/`dwd`/`dws`/`core`/`meta`/`app`

2. **SQL 被拒绝**
   - 错误：`SQL 被拒绝：仅允许只读`
   - 解决：检查 SQL 语句，确保只使用 SELECT、WITH、SHOW、EXPLAIN

3. **跨 Schema 引用**
   - 错误：`检测到跨 schema 引用`
   - 解决：在单个 schema 内查询，或使用 `describe_schemas` 获取多 schema 信息

4. **连接超时**
   - 错误：连接数据库失败
   - 解决：检查网络连接和数据库服务状态

### 调试建议

1. **先探索后查询**：使用 `list_tables` 和 `describe_table` 了解数据结构
2. **小范围测试**：先用 LIMIT 限制返回行数，确认查询正确后再扩大范围
3. **关注时间范围**：大部分汇总表按营业日归属，注意时间筛选条件
4. **利用索引**：查询时尽量使用 `site_id`、`stat_date` 等索引字段

---

## 配置说明

### 环境变量

| 变量名 | 说明 | 默认值 |
|--------|------|--------|
| `MCP_PG_HOST` | 数据库主机地址 | 回退到 `DB_HOST` |
| `MCP_PG_PORT` | 数据库端口 | 回退到 `DB_PORT`，默认 5432 |
| `MCP_PG_DATABASE` | 数据库名 | 回退到 `ETL_DB_NAME` |
| `MCP_PG_USER` | 数据库用户 | 回退到 `DB_USER` |
| `MCP_PG_PASSWORD` | 数据库密码 | 回退到 `DB_PASSWORD` |
| `MCP_TOKEN` | API 鉴权 Token | 空（不启用鉴权） |
| `MCP_MAX_ROWS` | 最大返回行数 | 500 |
| `PORT` | 服务监听端口 | 9000 |

### 启动服务

```bash
cd apps/mcp-server
python server.py
```

服务启动后，MCP 端点地址为：`http://localhost:9000/mcp`

---

## 最佳实践

### 1. 数据探索流程
1. 使用 `list_tables` 了解可用表
2. 使用 `describe_table` 查看表结构
3. 使用 `query_sql` 执行小范围测试查询
4. 逐步扩大查询范围和复杂度

### 2. 查询优化建议
- 总是在 WHERE 条件中包含 `site_id` 进行门店过滤
- 使用日期范围限制大表查询，避免全表扫描
- 优先使用 DWS 层的汇总表，避免实时聚合计算
- 合理使用 LIMIT 限制返回数据量

### 3. 业务理解要点
- **营业日概念**：08:00 为分界点，之前归属前一天，使用 `dws.biz_date()` 函数计算
- **金额精度**：所有金额字段精确到分（NUMERIC(18,2)），货币单位为人民币（CNY）
- **会员状态**：注意区分 `member_status`（账户状态）和 `scd2_is_current`（版本状态）
- **助教服务**：区分服务类型（陪打/促销）和计费方式，关注评分和时长，注意废除状态
- **多门店隔离**：所有查询都应包含 `site_id` 过滤条件，实现门店级数据隔离
- **SCD2 维度**：维度表查询时需要 `scd2_is_current = 1` 获取当前版本
- **库存管理**：区分当前库存、批次库存、安全库存，关注库存变动类型
- **任务系统**：助教任务有生命周期状态，支持置顶、过期、放弃等操作
- **备注分类**：统一备注表通过 type 字段区分不同业务场景的备注
- **触发器调度**：支持 cron、interval、event 三种触发方式的自动化任务

### 数据质量注意事项
- **时间字段**：注意区分创建时间、更新时间、业务发生时间
- **外键关联**：跨表查询时注意 `site_id` 的一致性约束
- **软删除**：部分表使用 `is_delete` 字段标记软删除，查询时需要过滤
- **枚举值**：状态字段使用整数枚举，需要了解具体含义
- **NULL 处理**：可选字段可能为 NULL，聚合计算时需要适当处理
- **精度问题**：时长字段有的用秒，有的用分钟，注意单位转换

通过本手册，AI 应用可以高效、安全地访问台球门店的运营数据，为智能分析和决策提供强有力的数据支撑。手册涵盖了从基础的表结构查询到复杂的业务分析，为各种数据需求提供了完整的解决方案。