44 KiB
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(行级安全):
appschema 的视图层自动过滤当前用户可访问的门店数据 - 时间范围:大部分汇总表按营业日(08:00 为分界点)进行数据归属
MCP 工具详解
1. list_tables - 列出表清单
功能:获取指定 schema 下的所有表和视图
参数:
schema(string, 默认 "dwd"): 目标 schema,可选值:ods/dwd/dws/core/meta/appinclude_views(boolean, 默认 false): 是否包含视图
返回示例:
{
"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
返回示例:
{
"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 列表,不传则返回全部六个 schemainclude_views(boolean, 默认 false): 是否包含视图max_tables_per_schema(integer, 默认 500): 每个 schema 最大返回表数量
返回示例:
{
"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, 必需): 执行查询的 schemasql(string, 必需): SQL 查询语句max_rows(integer, 默认 500): 最大返回行数
安全限制:
- 仅允许 SELECT、WITH、SHOW、EXPLAIN 语句
- 禁止 DDL/DML 操作(CREATE、DROP、INSERT、UPDATE、DELETE 等)
- 限制跨 schema 引用(仅允许在指定 schema 内查询)
- 自动设置
search_path到指定 schema
返回示例:
{
"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): 会员唯一标识,飞球系统雪花 IDsite_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=历史
查询示例:
-- 查询某门店的活跃会员
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): 门店 IDtenant_member_id(BIGINT): 租户维度会员 IDsystem_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): 备注说明
查询示例:
-- 查询某会员的余额变动历史
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): 门店 IDmember_id(BIGINT): 消费会员 ID,0 表示散客table_id(BIGINT): 台桌 IDassistant_id(BIGINT): 服务助教 IDorder_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): 实付金额
查询示例:
-- 查询某门店今日订单统计
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): 关联订单 IDsite_id(BIGINT): 门店 IDmember_id(BIGINT): 会员 IDsettle_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): 所属门店 IDassistant_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_idsite_id(BIGINT): 门店 IDtenant_member_id(BIGINT): 租户维度会员 IDorder_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.50is_trash(INTEGER): 是否已废除,0=正常,1=已废除trash_reason(VARCHAR): 废除原因
台桌相关表
dwd.dim_table - 台桌维表
业务含义:门店台桌基础信息维度表,记录每个台桌的基本配置、区域归属、价格等信息
关键字段:
table_id(BIGINT): 台桌唯一标识(主键)site_id(BIGINT): 门店 IDtable_name(TEXT): 台桌名称,如"1号台"、"VIP1"site_table_area_id(BIGINT): 门店台桌区域 IDsite_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): 台灯熄灭延迟时间(秒)
查询示例:
-- 查询某门店的台桌使用情况
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): 门店 IDtenant_goods_id(BIGINT): 租户商品 ID,关联 dim_tenant_goodsgoods_name(TEXT): 商品名称,如"百威啤酒"goods_category_id(BIGINT): 一级分类 IDgoods_second_category_id(BIGINT): 二级分类 IDcategory_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): 门店 IDsite_goods_id(BIGINT): 门店商品 ID,关联 dim_store_goodsgoods_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): 备注说明,如"结账退货"、"采购退货"、"系统自动领用"
查询示例:
-- 查询某商品的库存变动历史
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): 门店 IDmember_id(BIGINT): 会员 IDmember_name_snapshot(TEXT): 会员姓名快照member_phone_snapshot(TEXT): 会员手机号快照tenant_member_card_id(BIGINT): 会员卡 IDmember_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): 租户 IDsupplier_id(BIGINT): 供应商 IDgoods_name(VARCHAR): 商品名称,如"百威啤酒"goods_number(VARCHAR): 商品编号category_name(VARCHAR): 一级分类名称,如"酒水"goods_category_id(BIGINT): 一级分类 IDgoods_second_category_id(BIGINT): 二级分类 IDunit(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): 助教 IDmember_id(BIGINT): 客户 IDtask_type(VARCHAR): 任务类型,high_priority_recall/priority_recall/follow_up_visit/relationship_buildingstatus(VARCHAR): 状态,active/inactive/completed/abandonedpriority_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): 门店 IDuser_id(INTEGER): 小程序用户 IDtarget_type(VARCHAR): 目标类型(如 member)target_id(BIGINT): 目标 ID(如 member_id)type(VARCHAR): 备注类型,normal/follow_up/abandon_reasoncontent(TEXT): 备注内容rating_service_willingness(SMALLINT): 再次服务意愿评分(1-5)rating_revisit_likelihood(SMALLINT): 再来店可能性评分(1-5)task_id(BIGINT): 关联任务 IDai_score(SMALLINT): AI应用6评分(P5实现)ai_analysis(TEXT): AI分析结果(P5实现)
biz.trigger_jobs - 触发器配置表
业务含义:存储 cron/interval/event 三种触发方式的配置与执行状态
关键字段:
id(SERIAL): 自增主键job_type(VARCHAR): 任务类型标识,映射到 Python handlerjob_name(VARCHAR): 任务名称(唯一)trigger_condition(VARCHAR): 触发方式,cron/interval/eventtrigger_config(JSONB): 触发配置(cron表达式/间隔秒数/事件名)last_run_at(TIMESTAMPTZ): 上次运行时间next_run_at(TIMESTAMPTZ): 下次运行时间(event类型为NULL)status(VARCHAR): 状态,enabled/disabled
查询示例:
-- 查询助教的活跃任务
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): 门店 IDmember_id(BIGINT): 会员 IDstat_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): 门店 IDassistant_id(BIGINT): 助教 IDstat_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): 门店 IDassistant_id(BIGINT): 助教 IDassistant_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): 当日总服务时长(秒)
四项统计说明:
- 订单总流水:助教参与的所有订单的总收入,每个参与助教获得相同值
- 订单净流水:扣除所有助教分成后的净收入,每个参与助教获得相同值
- 时效贡献流水:按服务时长比例分摊的收入,体现个人贡献度
- 时效净贡献:扣除个人分成后的净贡献,最终的业绩指标
查询示例:
-- 助教业绩四项统计对比
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): 门店 IDstat_date(DATE): 统计日期site_goods_id(BIGINT): 门店商品 IDgoods_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'
查询示例:
-- 商品库存变动分析
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): 门店 IDassistant_id(BIGINT): 助教 IDorder_trade_no(BIGINT): 订单交易号member_id(BIGINT): 会员 IDstat_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): 门店 IDsite_goods_id(BIGINT): 门店商品 IDstat_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): 库存周转天数
查询示例:
-- 查询库存周转分析
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): 门店 IDmember_id(BIGINT): 会员 IDspend_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): 计算时间
查询示例:
-- 查询高价值会员(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. 会员分析查询
-- 会员消费趋势分析
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. 助教业绩查询
-- 助教月度业绩排行
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. 经营分析查询
-- 门店经营概况
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. 会员行为分析查询
-- 会员生命周期分析
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. 助教绩效分析查询
-- 助教服务质量分析
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. 库存管理查询
-- 库存预警分析
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. 财务分析查询
-- 收入结构趋势分析
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. 商品分析查询
-- 商品销售排行
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. 助教业绩深度分析
-- 助教四项统计分析(基于订单贡献表)
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. 数据探索流程
- 使用
list_tables了解可用表 - 使用
describe_table查看表结构 - 使用
query_sql执行小范围测试查询 - 逐步扩大查询范围和复杂度
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 应用可以高效、安全地访问台球门店的运营数据,为智能分析和决策提供强有力的数据支撑。手册涵盖了从基础的表结构查询到复杂的业务分析,为各种数据需求提供了完整的解决方案。