# nearboss 业务数据 API

> 给 **Agent** 使用的 nearboss 业务数据 API。纯接口契约：只列**端点 + 输入参数 + 输出参数**。
> 基于真实库表校验、优化建议、目标态 v1 / 优化态 v2 全部结论蒸馏。
> 不含意图识别 —— 自然语言→端点+参数的转换由消费侧（本项目）负责，本文档只定义 API 本身。
> 版本前缀：`/pos/ai/v1`；时间一律 13 位 epoch 毫秒（UTC+4）；金额一律 `Money` 对象。

---

## 0. 公共约定

### 0.1 响应信封 `Envelope<T>`（所有端点统一外壳）

| 字段 | 类型 | 说明 |
|---|---|---|
| `api_version` | string | 契约版本，如 `v1` |
| `code` | int | 0=成功；非 0 见 `error` |
| `message` | string | 人类可读信息 |
| `data` | T | 业务载荷（各端点 Output） |
| `error` | Error? | 失败时存在 |
| `meta` | Meta? | 货币/时区/数据来源等自描述 |
| `request_id` | string | 链路追踪 |
| `server_time` | epoch ms | 服务端时间 |

失败回真实 HTTP 状态（400/401/403/404/409/410/422/429/503）。

### 0.2 `Error`

| 字段 | 类型 | 说明 |
|---|---|---|
| `error_code` | string | 语义码，如 `AIGENT_QUERY_RANGE_TOO_LARGE` |
| `category` | enum | `validation` / `conflict` / `degraded` / `not_found` / `rate_limited` / `unsupported` |
| `retriable` | bool | 是否可退避重试 |
| `details` | object? | 结构化补充（如 `{max_days,requested_days}`） |
| `retry_after_ms` | int? | retriable 时的建议退避 |

### 0.3 `Money`

| 字段 | 类型 | 说明 |
|---|---|---|
| `amount` | int | 最小货币单位整数（fils） |
| `currency` | string | ISO 4217，如 `AED` |
| `minor_unit` | int | 小数位数，AED=2 |

### 0.4 `PageMeta`（分页响应共用）

`{ total, page, page_size, pages, has_next }`

### 0.5 分页/排序/投影请求壳（列表类端点共用入参）

| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
| `page` | int | 1 | 页码 |
| `page_size` | int | 20 | ≤100 |
| `sort` | `[{field,order}]` | — | `order∈ASC\|DESC` |
| `view` | enum | `FULL` | `SLIM`=仅 id/name/status 等轻量列 |

---

# 一、数据专家 API

## 1.1 指标查询 `POST /pos/ai/v1/metrics:query`

聚合 / 趋势 / 同比三形态合一。

**Input**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `store_id` | string | ✓ | 门店 |
| `metrics` | string[] | ✓ | 指标 key（见 §1.6 白名单，可多选） |
| `aggregation` | enum | ✓ | `SUM\|AVG\|MAX\|MIN\|COUNT` |
| `time_granularity` | enum | ✓ | `DAY\|MONTH\|YEAR\|NONE`（NONE=区间汇总单值） |
| `date_from` | epoch ms | ✓ | 区间起（含） |
| `date_to` | epoch ms | ✓ | 区间止（含） |
| `compare` | enum | — | `NONE\|WOW\|MOM\|YOY`，默认 NONE |

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `series` | Row[] | 按粒度分组的数据行 |
| `series[].period` | string | 周期标签（如 `2026-06-10` / `2026-06`） |
| `series[].values` | map | `{<metric>: {value, prev, pct, unit, is_ratio}}`；`prev`/`pct` 无同比时为 null |
| `meta` | Meta | `{currency, tz_offset, source, data_basis, coverage_days}` |

> 越界：`AIGENT_QUERY_RANGE_TOO_LARGE`（>400 天，`details{max_days,requested_days}`）；
> 粒度不支持：`AIGENT_GRANULARITY_UNSUPPORTED{supported:[...]}`。

## 1.2 维度拆分取数 `POST /pos/ai/v1/metrics:dimensions`

按渠道 / 支付 / 来源 / 会员维拆分（源 `report_daily_statistics` 预聚合列；维度不可用时按门店 `available:false`）。

**Input**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `store_id` | string | ✓ | 门店 |
| `metric` | string | ✓ | 单指标 key |
| `dimension` | enum | ✓ | `CHANNEL\|PAYMENT\|ORDER_SOURCE\|MEMBER_TYPE` |
| `date_from` / `date_to` | epoch ms | ✓ | 区间（含头含尾） |

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `breakdown` | Item[] | 各维度值切片 |
| `breakdown[].dimension_value` | string | 如 `DINING_ROOM`/`PAY_BY_CARD` |
| `breakdown[].value` | number | 该切片指标值 |
| `breakdown[].share` | number | 占比 0–1 |
| `meta` | Meta | 同 §1.1 |

## 1.3 经营健康分级 `POST /pos/ai/v1/insights/health`

**Input**：`store_id`、`date_from`、`date_to`（≥14 天两期对比）

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `grade` | enum | `EXCELLENT\|GOOD\|WARNING\|RISK` |
| `signals` | Item[] | `{metric, current, prev, pct, direction}` |
| `summary` | string | 结论摘要 |
| `meta` | Meta | 含 `coverage_days` |

## 1.4 Peer 对标 `POST /pos/ai/v1/insights/peer-benchmark`

**Input**：`store_id`、`metric`(如 `avg_ticket`)、`date_from`、`date_to`、`peer_scope`(`AREA_CUISINE` 默认)

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `self_value` | number | 本店值 |
| `peer_median` | number | peer 中位 |
| `peer_p25` / `peer_p75` | number | 四分位 |
| `percentile` | number | 本店在 peer 中的分位 0–100 |
| `peer_size` | int | peer 样本店数（<5 返 `AIGENT_INSUFFICIENT_DATA`） |
| `meta` | Meta | peer 口径（area×cuisine） |

## 1.5 菜单工程四象限 `POST /pos/ai/v1/insights/menu-engineering`

**Input**：`store_id`、`date_from`、`date_to`（≥30 天明细）

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `items` | Item[] | 商品级 |
| `items[].spu_id` / `spu_name` | — | 商品 |
| `items[].quadrant` | enum | `STAR\|PLOWHORSE\|PUZZLE\|DOG` |
| `items[].sold_qty` | int | 销量 |
| `items[].profit_index` | number | 毛利指数 |
| `meta` | Meta | — |

## 1.6 滞销 / 死 SKU `POST /pos/ai/v1/insights/slow-movers`

**Input**：`store_id`、`window_days`(默 30)、`threshold`(默销量阈)

**Output**：`items[]{spu_id, spu_name, sold_qty, last_sold_at, stock_num, status}`、`meta`

## 1.7 时段×星期热度 `POST /pos/ai/v1/insights/time-heatmap`

源 `main_order.create_time`（唯一时刻源）。

**Input**：`store_id`、`metric`(`order_count`/`income`)、`date_from`、`date_to`（≥4 周）

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `grid` | Cell[] | 7×24 |
| `grid[].weekday` | int | 0–6 |
| `grid[].hour` | int | 0–23 |
| `grid[].value` | number | 指标值 |
| `peak` | object | `{weekday, hour, value}` |

## 1.8 会员 RFM 分层 `POST /pos/ai/v1/insights/member-rfm`

**Input**：`store_id`、`date_from`、`date_to`

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `segments` | Seg[] | `{segment, member_count, share, avg_recency_days, avg_frequency, avg_monetary:Money}` |
| `meta` | Meta | `member_id` 填充率不足时返 `AIGENT_INSUFFICIENT_DATA` |

## 1.9 会员构成总览 `GET /pos/ai/v1/member/overview`

**Input（query）**：`store_id`、`date_from`、`date_to`

**Output**：`{total, new_in_period, returning, by_source:[{source_type,count,share}], trend:[{period,new_count}]}`、`meta{scope_note:MEMBER_IS_MER_SCOPED}`

## 1.10 门店画像 `GET /pos/ai/v1/stores/{store_id}/profile`

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `store_id` / `store_name` | — | 基本 |
| `cuisine` | string | 菜系 |
| `trading_area` | object | `{area_id, area_name}` |
| `geo` | object? | 未审计字段为 null + `field_audit` 标记 |
| `launch_date` | epoch ms? | 同上 |

## 1.11 报表导出（异步作业，3 端点）

| 步骤 | 端点 | Input | Output |
|---|---|---|---|
| 创建草稿 | `POST /pos/ai/v1/report/exports` | `store_id`、`metrics[]`、`date_from`、`date_to`、`format`(`CSV\|XLSX`) | `{draft_token, expires_at, estimated_rows}` |
| 确认 | `POST /pos/ai/v1/report/exports:confirm` | `draft_token`（幂等键） | `{job_id, status}` |
| 查终态 | `GET /pos/ai/v1/report/exports/{job_id}` | — | `{job_id, status, download_url?, error?}`，`status∈ACCEPTED\|PROCESSING\|DELIVERED\|FAILED` |

> 越界：`AIGENT_EXPORT_RANGE_TOO_LARGE`（>90 天）；token 门店绑定不符：`AIGENT_TOKEN_SCOPE_MISMATCH`(403)。

## 1.12 能力自描述 `GET /pos/ai/v1/metrics/capabilities`

**Input（query）**：`store_id`（按店返可用维度/能力）

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `metrics` | Item[] | `{key, label, unit, is_ratio}` |
| `supported_aggregations` | enum[] | — |
| `supported_granularities` | enum[] | — |
| `compare_bases` | enum[] | `WOW/MOM/YOY` |
| `dimensions` | Item[] | `{dimension, available, data_basis, fill_rate}` |
| `insights` | Item[] | `{key, available, data_basis}` |
| `limits` | object | `{bi_max_days, export_max_days}` |
| `currency` / `tz_offset` | — | — |

## §指标白名单（12 项，`metrics[]` 取值）

| key | 含义 | 单位 | 来源 |
|---|---|---|---|
| `income` | 净收入(成功额−退款额) | Money | `turnover−refund` 派生 |
| `success_amount` | 成功交易额 | Money | `turnover` 直取 |
| `success_count` | 成功笔数 | 笔 | `order_quantity` |
| `refund_amount` | 退款额 | Money | `refund` 直取 |
| `refund_count` | 退款笔数 | 笔 | `main_order.order_status=refunded` 聚合 |
| `fail_amount` | 失败额 | Money | 废单/取消聚合 |
| `fail_count` | 失败笔数 | 笔 | `blankout+cancel` 聚合 |
| `settle_amount` | 总结算额 | Money | `turnover−refund` 派生 |
| `order_count` | 订单总数 | 笔 | `order_quantity` |
| `refund_rate` | 退款率 | ratio | `refund/turnover` |
| `success_rate` | 成功率 | ratio | 成功/(成功+失败) |
| `avg_ticket` | 客单价 | Money | `average_order_price` 直取 |

---

# 二、营销专家 API

## 2.1 活动类型目录 `GET /pos/ai/v1/meta/campaign-activity-types`

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `types` | Type[] | `{type_code, label, description, discount_model}` |
| `types[].discount_model` | enum | `PERCENT_TO_PAY\|FIXED_AMOUNT_OFF\|FLASH_PRICE\|...` |

> 类型码：`FIXED_AMOUNT_OFF`（定额减）/`WHOLE_ORDER_PERCENT`（整单折）/`FLASH_SALE`（秒杀）等，均带 `legacy_code` 过渡。

## 2.2 商品检索 `GET /pos/ai/v1/products`

**Input（query）**：`store_id`、`spu_keyword`、`page`、`page_size`

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `items` | Item[] | `{spu_id, spu_name, price:Money, original_price:Money, stock_num}` |
| `page_meta` | PageMeta | — |

## 2.3 活动预览 `POST /pos/ai/v1/campaigns:preview`

生成草稿 + 一次性 token（不落库）。

**Input**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `store_id` | string | ✓ | — |
| `activity_type` | enum | ✓ | 见 §2.1 |
| `activity_name` | string | ✓ | — |
| `date_start` / `date_end` | epoch ms | ✓ | 活动期 |
| `time_start` / `time_end` | epoch ms | — | 每日时段；空=全天 |
| `is_long_term` | bool | — | 长期活动 |
| `percent_to_pay` | int | 条件 | 10–99（折扣类） |
| `amount_off` | Money | 条件 | 定额减类 |
| `flash_price` | Money | 条件 | 秒杀类 |
| `spu_ids` | string[] | 条件 | 适用商品（秒杀/单品类） |

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `preview_token` | string | commit 幂等键 |
| `expires_at` | epoch ms | TTL |
| `summary` | object | 可读摘要（展示用） |
| `draft` | object | 规范化后的活动草稿（机读原始字段，可回填编辑） |

## 2.4 活动确认发布 `POST /pos/ai/v1/campaigns:commit`

**Input**

| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `preview_token` | string | ✓ | 幂等：同 token TTL 内二次提交返同一 `campaign_id` |
| `draft` | object | — | 编辑后回传（提供则以最新资源重校验） |

**Output**：`{campaign_id, status, published_at}`

> 快照过期：`AIGENT_PREVIEW_STALE`（区别于 `EXPIRED`）；
> 门店越权：`AIGENT_TOKEN_SCOPE_MISMATCH`(403)。

## 2.5 活动列表 `GET /pos/ai/v1/campaigns`

**Input（query，含 §0.5 分页/排序/投影壳）**

| 字段 | 类型 | 说明 |
|---|---|---|
| `store_id` | string | ✓ |
| `statuses` | enum[] | 多状态过滤 |
| `keyword` | string | 匹配活动名 |
| `created_from` / `created_to` | epoch ms | 创建时间区间 |
| `ended_reason` | enum | 结束原因过滤 |

**Output**

| 字段 | 类型 | 说明 |
|---|---|---|
| `items` | Row[] | 活动行 |
| `items[].campaign_id` / `activity_name` / `activity_type` / `status` | — | 基本 |
| `items[].date_start` / `date_end` / `time_start` / `time_end` | epoch ms | 机读原始值 |
| `items[].is_long_term` / `is_all_day` | bool | 显式布尔 |
| `items[].enabled_actions` | enum[] | 当前可执行动作（见 §2.7） |
| `items[].ended_reason` | enum? | 已结束时 |
| `page_meta` | PageMeta | — |

## 2.6 活动详情 `GET /pos/ai/v1/campaigns/{id}`

**Output**：§2.5 行字段超集 + 明细

| 追加字段 | 类型 | 说明 |
|---|---|---|
| `discount_detail` | object | `{discount_model, percent_to_pay?, amount_off?:Money, flash_price?:Money}` |
| `spus` | Item[] | 适用商品 `{spu_id, spu_name, price:Money}` |
| `progress` | object? | 秒杀类 `{sold, total, progress}` |
| `all_actions` | `[{action, endpoint, available, status}]` | 机读可调对象 |

## 2.7 活动生命周期（4 端点）

| 动作 | 端点 | Input | Output |
|---|---|---|---|
| 暂停 | `POST /pos/ai/v1/campaigns/{id}:pause` | `expected_status?`(CAS) | `{campaign_id, status}` |
| 恢复 | `POST /pos/ai/v1/campaigns/{id}:resume` | `expected_status?` | `{campaign_id, status}` |
| 终止 | `POST /pos/ai/v1/campaigns/{id}:terminate` | `expected_status?` | `{campaign_id, status}`（库存归还恰好一次） |
| 撤回 | `POST /pos/ai/v1/campaigns/{id}:withdraw` | `expected_status?` | `{campaign_id, status}`（PENDING/DRAFT 用） |

> 状态前置不满足：`AIGENT_STATE_CONFLICT`(409，`details{current_status, allowed_actions}`)；
> 目标态已达成→幂等成功返当前状态。

## 2.8 会员标签操作（preview→commit）

| 步骤 | 端点 | Input | Output |
|---|---|---|---|
| 预览 | `POST /pos/ai/v1/member/tags:preview` | `store_id`、`member_id`、`op`(`ADD\|REMOVE`)、`tag` | `{preview_token, expires_at, affected}` |
| 确认 | `POST /pos/ai/v1/member/tags:commit` | `preview_token`（幂等） | `{member_id, tags}` |

## 2.9 会员查询

| 端点 | Input（query） | Output |
|---|---|---|
| `GET /pos/ai/v1/member` 列表 | `store_id`、`keyword`、§0.5 壳 | `items[]{member_id, nickname, phone(脱敏), level, tags}`、`page_meta` |
| `GET /pos/ai/v1/member/{id}` 详情 | — | 上行超集 + `{join_at, source_type, recent_orders}`；手机/邮箱默认脱敏，`view=FULL`+平台身份才全量 |

---

## 附：端点清单一览

**数据专家（14）**：`metrics:query` · `metrics:dimensions` · `insights/health` · `insights/peer-benchmark` · `insights/menu-engineering` · `insights/slow-movers` · `insights/time-heatmap` · `insights/member-rfm` · `member/overview` · `stores/{id}/profile` · `report/exports`(创建) · `report/exports:confirm` · `report/exports/{id}` · `metrics/capabilities`

**营销专家（13）**：`meta/campaign-activity-types` · `products` · `campaigns:preview` · `campaigns:commit` · `campaigns`(列表) · `campaigns/{id}` · `campaigns/{id}:pause` · `:resume` · `:terminate` · `:withdraw` · `member/tags:preview` · `member/tags:commit` · `member`/`member/{id}`
