> **前置条件** — 执行以下任何命令前，请先确认已完成认证。如未认证，参见 [`../../shb-shared/SKILL.md`](../../shb-shared/SKILL.md)。

# shb-cli task search

工单搜索。

> **本文档出现的命令、子命令、flag、参数名、接口/JSON 字段名，以及取数过程（分页、数量判断、全量拉取、字段裁剪、截断重试）都只供你后台执行，绝不出现在给用户的回复里**——不解释、不转述、不当理由说，换种说法也不行。回复只给业务结果与真实数量（如「共 189 条工单，待指派 76 条」）。

## 搜索工单列表

### 快捷 flag 方式（推荐）

```bash
# 关键字搜索
shb-cli task search --keyword <关键字>

# 按工单类型 ID 筛选
shb-cli task search --template-id <templateId>

# 按状态筛选
shb-cli task search --state <state>

# 分页
shb-cli task search --page 1 --page-size 20

# 组合使用
shb-cli task search --keyword "审批" --state created --page-size 20

# 格式化数据输出：字段文案和值都会转换为可读内容，可用 --fields 指定字段
shb-cli task search --keyword "审批" --format-data --fields taskNo,state,customer,executorName,createTime

# 获取全量数据：确定量不大就直接 --all 一次拿齐（>500 或不确定时见「获取全量数据：决策规则」）
shb-cli task search --template-id <templateId> --all --format-data --fields taskNo,state,customer,createTime
```

### 完整 JSON body 方式

```bash
# 内联 JSON （推荐）
shb-cli task search --data '{"page":1,"pageSize":10,"keyword":"审批","state":"created"}'

# 从文件读取
shb-cli task search --file ./search.json
```

**使用优先级：快捷 flags ＞ `--data` ＞ `--file`**——常规筛选直接用快捷 flags；需要完整或复杂 JSON 时用 `--data`；`--file` 仅在本地已有现成 JSON 文件时用。

### 按标签过滤（智能标签）

标签过滤走 `--data`（无快捷 flag），传入 `labelQuery`。**先读 [`../../shb-label/SKILL.md`](../../shb-label/SKILL.md)**——查 labelId、`labelQuery` 字段含义都在那份文档里，这里只给工单场景的示例：

```bash
shb-cli task search --data '{"keyword":"审批","labelQuery":{"labelIds":[12345,67890]}}'
```

### 高级搜索条件（`systemConditions` / `conditions`）

顶层快捷字段（`state` / `executor` / `createTimeStart` …）只覆盖最常用的几个过滤维度。**其它字段的过滤，以及需要 `in` / `between` / `like` / `不等于` 这类操作符的场景，走 `systemConditions` / `conditions`**——这就是 PC 端「高级搜索」提交的两个数组，只能通过 `--data` / `--file` 传，没有快捷 flag。

**先分清两个数组（传错数组=查不出结果，后端不会报错）：**

| 数组 | 放什么字段 | 判定方式 |
|------|-----------|---------|
| `systemConditions` | **系统字段**（工单状态、负责人、创建人、客户、服务类型、创建方式、里程、各类时间、异常标记等） | `task field list --type-id <templateId>` 返回 `isSystem = 1` |
| `conditions` | **自定义字段**（工单类型自定义的 `field_xxx`） | 同上，`isSystem = 0`；后端默认按 `attribute.<fieldName>` 检索 |

> 不确定某字段属于哪一类时，**先 `task field list --type-id <templateId>` 查 `isSystem`**，不要凭字段名猜。

**Condition 结构**（不要臆造字段名）：

| 字段 | 何时用 | 说明 |
|------|--------|------|
| `property` | 必填 | 过滤字段名，见下方「property 命名对照」 |
| `operator` | 必填 | 操作符，见下表 |
| `value` | `eq`/`notEq`/`like`/`contain`/`gt`/`ge`/`lt`/`le` | 单值（字符串） |
| `inValue` | `in`/`not_in`/`multiSelect` | 数组 |
| `betweenValue1` / `betweenValue2` | `between` | 区间起止（时间或数字） |
| `key` | 少数场景 | 需要检索对象内某个属性时用（如工单编号关联查询传 `"key":"taskNo"`） |

**合法 operator**：`eq`（等于）、`notEq`（不等于）、`like`（模糊）、`contain` / `not_contain`（包含/不包含）、`in` / `not_in`（多选/排除多选）、`between`（区间）、`gt` / `ge` / `lt` / `le`（大于/大于等于/小于/小于等于）、`cascader` / `multiSelect`（多级下拉单选/多选）、`array_contain` / `array_eq`（数组字段）。

**property 命名对照**——展示用的字段名和过滤用的 `property` **不是一回事**，按下表转换：

| 过滤目标 | `eq` 时的 property | `in` / `not_in` 时的 property | 值 |
|---------|-------------------|------------------------------|---|
| 工单状态 | `state` | `state` | 英文 value（见 State 表） |
| 负责人 | `executor` | `executorUser` | userId |
| 创建人 | `createUser` | `createUser` | userId |
| 协同人 | `synergyId` | `synergies` | userId |
| 派单人 | `allotUser` | `allotUser` | userId |
| 回访人 | `reviewUser` | `reviewUser` | userId |
| 结算人 | `balanceUser` | `balanceUser` | userId |
| 客户 | `customerId` | `customerId` | 客户 id |
| 联系人 | `tlmId` | `tlmId` | 联系人 id |
| 产品 | `productId` | `productIdList` | 产品 id |
| 服务部门 | — | `tagIds` | 部门/标签 id |
| 异常标记 | `flag` | `flags` | `曾超时`/`曾拒绝`/`曾暂停`/`曾回退`/`位置异常`/`曾转派` 等中文值 |
| 派单方式 | `allotType` | `allotType` | `1` 手动 / `2` 工单池 / `3` 自动 |
| 客户地址 | `cusAddress`（配 `like`） | — | 地址关键字 |
| 区域 | `country` / `province` / `city` / `dist` / `street` | 同左 | 单级值，逐级各写一条 |
| 质保状态 | `qualityStatus` | — | `IN`（保内）/ `OUT`（保外） |
| 里程 | — | `taskEstimatedMileage` / `estimatedMileage` / `actualMileage`（配 `between`） | 数字字符串 |
| 其它系统字段 | 与 `fieldName` 同名（`serviceType`、`serviceContent`、`source`、`level`、`description`、`createTime` 等） | 同左 | 见下方值格式 |

**值格式规则：**

- **人员类**一律传 userId，**客户/产品/联系人**传 id，**不要传姓名或名称**。
- **时间类**用 `yyyy-MM-dd HH:mm:ss`（`between` 也可传毫秒时间戳字符串），**不是** ISO 8601 的 `T` 格式——`createTimeStart` / `createTimeEnd` 这类顶层字段才用 `T` 格式，两者不要混。
- `value` 传 `"全部"` 会被后端整条忽略，等于没写这个条件。
- 多个 condition 之间是 **AND**；同一字段要「或」的关系用 `in` + `inValue`。

**示例：**

```bash
# 系统字段：多状态 + 指定负责人 + 创建时间区间
shb-cli task search --data '{
  "templateId": "<templateId>",
  "page": 1,
  "pageSize": 50,
  "systemConditions": [
    {"property":"state","operator":"in","inValue":["created","processing"]},
    {"property":"executorUser","operator":"in","inValue":["<userId>"]},
    {"property":"createTime","operator":"between","betweenValue1":"2026-01-01 00:00:00","betweenValue2":"2026-03-31 23:59:59"}
  ]
}' --format-data --fields taskNo,state,customer,executorName,createTime

# 系统字段：服务类型多选 + 排除某创建方式 + 客户地址模糊
shb-cli task search --data '{
  "templateId": "<templateId>",
  "systemConditions": [
    {"property":"serviceType","operator":"in","inValue":["安装","维修"]},
    {"property":"source","operator":"not_in","inValue":["导入创建"]},
    {"property":"cusAddress","operator":"like","value":"浦东"}
  ]
}' --format-data --fields taskNo,state,customer,serviceType,taddress

# 自定义字段（isSystem=0）走 conditions，property 用 fieldName
shb-cli task search --data '{
  "templateId": "<templateId>",
  "conditions": [
    {"property":"field_xxx","operator":"eq","value":"已完成验收"}
  ]
}' --format-data --fields taskNo,state,customer,field_xxx

# 两类字段可以同时传，条件之间是 AND
shb-cli task search --data '{
  "templateId": "<templateId>",
  "systemConditions": [{"property":"state","operator":"eq","value":"finished"}],
  "conditions": [{"property":"field_xxx","operator":"like","value":"空调"}]
}' --format-data --fields taskNo,state,customer
```

> `--all` / 分页叠加与高级条件可以叠加使用：把 `systemConditions` / `conditions` 写进同一个 `--data` 里即可，拉取策略仍按下文「获取全量数据：决策规则」。

**排错**：高级条件查出 0 条时，按序排查 —— ① 字段放错数组（`isSystem` 没核对）；② `property` 没做上表转换（如直接传了 `executorName` / `customer`）；③ 操作符与值字段不配套（`in` 却写了 `value`、`between` 却漏了 `betweenValue2`）；④ 时间格式用了 `T` 分隔；⑤ 人员/客户传了姓名而不是 id。后端对识别不了的 `property` 只会退化成同名字段精确匹配，**不报错、直接空结果**。

## 格式化数据输出

```bash
shb-cli task search --template-id <templateId> --format-data --fields taskNo,state,customer,executorName,createTime
shb-cli task search --format-data --fields taskNo,state,customer,executorName,createTime
shb-cli task search --format-data --include-raw
```

`--format-data` 会把接口原始响应整理为:

- `columns`: 字段 key、字段文案、字段类型
- `rows`: 格式化后的工单列表
- `page`: 分页信息
- `rawRows`: 仅在传 `--include-raw` 时包含

格式化规则包括工单状态、客户、联系人、产品、人员、协同人、审批状态、是否类字段、时间、用时、里程、自定义选择/人员字段等。

### 支持的搜索字段（JSON body）

| 字段 | 类型 | 说明 |
|------|------|------|
| `keyword` | string | 关键字搜索，匹配工单号、客户名等 |
| `page` | int | 页码，**从 1 开始**（默认 1） |
| `pageSize` | int | 每页条数，API 默认 10；但全量拉取/统计分析等场景**建议设 50**（与下文各场景一致） |
| `templateId` | string | 工单类型/模板 ID |
| `state` | string | 单个工单状态，传英文 value（见下方「state 合法值」），不要传中文 |
| `stateList` | string [] | 多个状态一起查时用（如「待指派+进行中」）；传英文 value 数组，不要传中文。只查单个状态用 `state` 即可 |
| `tenantId` | string | 租户 ID。会话已绑定当前租户，**通常无需传** |
| `executor` | string | 执行人用户 ID（"我的工单/分配给我的"传当前用户 userId）。**字段名是 `executor`，不是 `executorId`** |
| `createUser` | string | 创建人用户 ID（"我创建的工单"传当前用户 userId）。**字段名是 `createUser`，不是 `creatorId` / `creator` / `createUserId`** |
| `createTimeStart` | string | 创建时间起始（ISO 8601） |
| `createTimeEnd` | string | 创建时间截止（ISO 8601） |
| `systemConditions` | array | 系统字段高级搜索条件（`isSystem=1` 的字段），支持 in/between/like 等操作符。只能通过 `--data`/`--file` 传，详见上方「高级搜索条件」 |
| `conditions` | array | 自定义字段（`isSystem=0`，`field_xxx`）高级搜索条件，结构同上，详见上方「高级搜索条件」 |
| `labelQuery` | object | 智能标签过滤，`{"labelIds": number[]\|null, "labelExists": boolean\|null}`。只能通过 `--data`/`--file` 传入，无对应快捷 flag。详见 [`../../shb-label/SKILL.md`](../../shb-label/SKILL.md) |

人员类过滤字段一律传 userId,不要传姓名。当前登录用户的 userId 可通过 `shb-cli config` 查看(chat 场景下系统上下文已提供)。

> **flag 名与 JSON 字段名的对应**：快捷 flag 用**短横线**命名，`--data` 里的 JSON 用**驼峰**命名，二者不可混用。对照：`--page`↔`page`、`--page-size`↔`pageSize`、`--template-id`↔`templateId`、`--state`↔`state`、`--keyword`↔`keyword`。即用 `--data` 时务必写 `"pageSize"` 而**不是** `"page-size"`。

#### state 合法值

`state` 过滤时只能传英文 `value`（如 `created` 待指派、`processing` 进行中、`finished` 已完成），**不要传中文或臆造拼写**。完整的 value↔中文标签对照见 [`../SKILL.md`](../SKILL.md) Core Concepts 的 **State（状态）** 表。

## 获取全量数据：决策规则

**全量请求的处理顺序（`--all` 每个查询条件最多执行一次，别重复拉）：**

- **能判断数据量不大（≤500）**——按人 / 按类型 / 按状态的日常查询多属此类——**直接执行一次 `--all`** 拿齐即可，**无需先探量**（`--all` 自带总数与全部数据）。
- **不确定是否会超过 500**（怕一次拉太多被截断）——先用 `pageSize:1` 探一次 `totalElements`（只取 1 条、开销极小），再按下面判断路径；**不要用大 `pageSize` 探一遍后又 `--all` 把数据拉第二遍**。

**判断规则——把 `totalElements` 和 500 直接比大小：**
- `totalElements` ≤ 500（含 500 本身）→ **用 `--all`**（**必须配 `--fields` 收窄列**，见下文「--all 用法」）一次拿齐
- `totalElements` ≥ 501 → **分页叠加**（见场景六），逐页拉取由模型在上下文中汇总

例：215 → `--all`；500 → `--all`；501 → 分页。

> **别把「总条数」当成「总页数」**：`totalElements` 是**工单总条数**，`totalPages` 是**总页数**（≈ 总条数 ÷ pageSize）。如 215 条按每页 50 是 5 页，不是「215 页」。给用户只说条数即可，不要把页数说成条数。

> **注意**：单次输出过大会被截断，能否放下取决于**条数 × 每条字段宽度**。**用 `--format-data --fields` 只取少数列时**，约 500 条以内通常不会截断；若不加 `--format-data`（输出接口原始响应、含全部字段）或不限字段，几百条甚至更少就可能被截断。所以 `--all` 必须配 `--format-data --fields`，超出就改分页叠加。

### --all 用法

```bash
shb-cli task search --template-id <templateId> --state created --all \
  --format-data --fields taskNo,state,customer,executorName,createTime
```

> **`--all` 用法要点：**
> - **必须 `--format-data --fields <少数列>` 两者一起**才能收窄输出。⚠️ `--fields` **只在 `--format-data` 下生效**：不加 `--format-data` 时 CLI 输出的是**接口原始响应（含全部字段）**，`--fields` 被忽略、输出必然很大被截断；也**不要加 `--include-raw`**（它会把全字段原始行塞回来）。`--all` 不带 `--fields` 时即便有 `--format-data` 也会用全部可见字段（宽行），同样易截断——务必显式只取需要的列。
> - **每个查询条件最多执行一次**：`--all` 已遍历所有页、返回完整结果与总数，**不要再补跑分页、也不要再次 `--all`**。
> - 只为拿 `totalElements` 决定路径时，用 `pageSize:1` 探一次即可，**别用大 `pageSize` 探一遍后又 `--all` 把数据拉第二遍**。
> - 结果若因体积被截断，改用分页叠加（见场景六），而非反复重试 `--all`。

## 典型组合场景

### 场景一：查看某工单类型下指定状态的工单（以「待指派」为例）

先通过 `task type list` 拿到 templateId，再按数量决定拉取策略。下例以 `state":"created"`（待指派）为例，换其它状态只需替换该 value（合法值见 [`../SKILL.md`](../SKILL.md) 的 State 表）：

```bash
# 1. 查出可用工单类型（type list 返回的列表已含 id 与 name，直接读取即可）
shb-cli task type list --list-type writeList

# 2. 取全部（拉取策略见「获取全量数据：决策规则」）
shb-cli task search --template-id <templateId> --state created --all \
  --format-data --fields taskNo,state,customer,executorName,createTime
```

---

### 场景二：按关键字搜索工单并查看详情

```bash
# 1. 用 taskNo 或客户名关键字搜索，从返回结果中读取目标工单的 id
shb-cli task search --keyword "WO-20240101" --format-data --fields taskNo,id,state,customer

# 2. 查看该工单完整详情
shb-cli task detail --id <taskId>
```

> 若本轮对话中已经搜索过该工单（列表里出现过它的 `id`），**直接复用已有 id 调 detail，不要重新 search**（见 SKILL.md「复用已知 ID」）。

---

### 场景三：查询某人名下 / 我创建的工单

按执行人查传 `executor`，按创建人查（"我创建的工单"）传 `createUser`——都传 userId（不是姓名），流程完全一致：

```bash
# 取全部（拉取策略见「获取全量数据：决策规则」）
shb-cli task search --data '{"executor":"<userId>"}' --all \
  --format-data --fields taskNo,state,customer,executorName,createTime
```

> 查「我创建的工单」时把上面的 `executor` 换成 `createUser` 即可，其余写法不变。

---

### 场景四：按时间范围查询工单

时间格式为 ISO 8601（`2024-01-01T00:00:00`）：

```bash
# 取全部（拉取策略见「获取全量数据：决策规则」）
shb-cli task search --data '{
  "templateId": "<templateId>",
  "createTimeStart": "2024-01-01T00:00:00",
  "createTimeEnd": "2024-03-31T23:59:59"
}' --all --format-data --fields taskNo,state,customer,createTime
```

---

### 场景五：统计各状态工单数量

每个状态单独查一次，`pageSize` 设 1 只为拿到 `page.totalElements`（即该状态总数），由模型逐条调用并汇总。**不要用 `for` 循环或 `;` 串联多条命令**——headless 模式下 shell 仅允许单条 shb-cli 命令。

```bash
# 逐个状态查询，每条只取总数（page.totalElements）；state 取「state 合法值」表里的 value
shb-cli task search --data '{"templateId":"<templateId>","state":"created","page":1,"pageSize":1}' --format-data
shb-cli task search --data '{"templateId":"<templateId>","state":"processing","page":1,"pageSize":1}' --format-data
shb-cli task search --data '{"templateId":"<templateId>","state":"finished","page":1,"pageSize":1}' --format-data
# … 其余状态同理（allocated / accepted / refused / costed / closed / offed 等），模型读取各自 page.totalElements 后汇总
```

> 汇总展示给用户时**只用中文标签**，不要在后面附英文 value：写「待指派：76」而**不是**「待指派(created)：76」。英文 value 只是你查询时用的参数，不给用户看。

---

### 场景六：大量数据的多维分析（分页叠加）

适用于：生成报告、多角度统计等需要处理大量工单的场景。

> ✅ **首选：统计/报告用「聚合计数」，不要拉全量明细。**
> 生成报告、按状态/类型/月度等做分布统计时，**只需要各维度的数量**——用计数查询（每个取值单独查一次、`pageSize:1` 只取 `page.totalElements`，见场景五）逐项拿到总数后汇总即可。**这样既零截断风险，也不占上下文**，远优于把成百上千条明细全拉进来再数。
> 例：「最近 30 天各状态工单分布」→ 逐个 state 用 `pageSize:1` 查 `totalElements`，得到每个状态的条数后汇总，**完全不需要拉明细行**。
>
> ⚠️ **只有当报告确实需要每条工单的明细字段**（如逐单列出、按非接口字段二次加工）时，才走下面的「分页叠加拉明细」。能用计数搞定的，绝不拉明细。

> 📊 **多维度报告怎么选**：
> - **只要计数/分布**（各状态/类型/月度各多少条）→ 用计数查询，每个维度的每个取值 `pageSize:1` 取 `totalElements`；多个维度 = 多组计数查询，**不拉明细**。
> - **要数值型指标**（平均用时、里程合计、响应时长、超时率等需读每条字段值）**或维度交叉很多**（如 类型×状态 矩阵）→ **只拉一次明细**：`--fields` 只取这些指标用得到的列，分页累积全部行后，**在上下文里一次算出报告的所有维度/指标**。**切忌每个维度各拉一遍明细**——一份窄字段数据集即可支撑全部维度。

**分页叠加拉明细（仅在确需明细时）——第一步：查总量和首页**

```bash
shb-cli task search --data '{
  "executor": "<userId>",
  "createTimeStart": "2025-01-01T00:00:00",
  "createTimeEnd": "2026-01-01T23:59:59",
  "page": 1,
  "pageSize": 50
}' --format-data --fields taskNo,templateName,state,customer,createTime,serviceType
```

从返回的 `page.totalElements` / `page.totalPages` 确认需要拉取的页数。

**第二步：逐页拉取剩余页**

```bash
shb-cli task search --data '{
  "executor": "<userId>",
  "createTimeStart": "2025-01-01T00:00:00",
  "createTimeEnd": "2026-01-01T23:59:59",
  "page": 2,
  "pageSize": 50
}' --format-data --fields taskNo,templateName,state,customer,createTime,serviceType

# page=3, page=4 ...（按实际 totalPages 循环）
```

每页的 `rows` 由模型累积，全部拉完后统一汇总状态分布、类型分布、月度趋势等指标。

---

## --format-data 输出结构速查

`--format-data -o raw` 返回：

```json
{
  "page":    {"totalElements": 189, "totalPages": 1, ...},
  "columns": [{"field": "taskNo", "label": "工单编号"}, ...],
  "rows":    [{"taskNo": "TUB...", "state": "已完成", "customer": "南图测试客户", "templateName": "上门维修", ...}, ...]
}
```

> `page` 排在最前是有意为之：结果很大被截断时，开头的总量信息仍能保留，便于据此决定分页策略。

`rows` 中每条记录的常用字段（值已格式化为中文可读内容）：

| 字段 | 说明 | 备注 |
|------|------|------|
| `taskNo` | 工单编号 | |
| `templateName` | 工单类型名称 | **直接可读，无需再查 `task type list`** |
| `state` | 工单状态（中文） | |
| `customer` | 客户名称 | **直接可读，无需再查客户接口** |
| `executorName` | 执行人姓名 | |
| `createUserName` | 创建人姓名 | |
| `createTime` | 创建时间 | 格式 `YYYY-MM-DD HH:mm:ss`，取前 7 位即得 `YYYY-MM` |
| `updateTime` | 最近更新时间 | |
| `serviceType` | 服务类型 | |
| `serviceContent` | 服务内容 | |
| `taskUsedTimeStr` | 工单用时（格式化） | 如 `2天3小时` |
| `createToCompleteUsedTimeStr` | 创建到完成用时 | |
| `acceptUsedTimeStr` | 接单用时 | |
| `taskResponseTimeStr` | 响应用时 | |
| `onceOverTime` | 曾超时 | `是` / `否` |
| `onceReallot` | 曾转派 | `是` / `否` |

---

## 注意

- `task search` 默认输出原始响应；需要字段文案和值格式化时显式传 `--format-data`。
- `--format-data` 未指定 `--fields` 时，会优先使用字段元数据里的可见字段；取不到字段元数据时使用内置常用字段。用户只需要少数字段时，显式传 `--fields`。
- 搜索结果为空时，检查 `templateId` 和 `state` 是否匹配当前租户下实际存在的值。
- 查询自己或他人的信息时请传 userId，不要传姓名。
- **统计 / 报告优先聚合计数**：按状态/类型/月度等做分布统计时，用计数查询（每维度 `pageSize:1` 取 `totalElements`，见场景五/六）拿数量后汇总，**不要为了数数把全量明细拉进来**——零截断、不占上下文。只有确需逐条明细时才拉明细。
- **全量 / 大量数据（确需明细时）**：拉取策略统一见「获取全量数据：决策规则」；逐页一律用 `--format-data --fields` 收窄字段，不依赖 jq。
