Files
workspace/code/fms/ai-plans/QUERY_REFACTOR_PLAN.md
T
2026-09-23 16:54:54 +08:00

660 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 查询体验重构实施计划
## 1. 重构目标
本次重构的目标不是增加查询表达力,而是缩短业务人员从“知道要找什么”到“看到结果”的路径。
重构后,列表页应满足:
- 打开页面即可看到常用工作队列,不需要先打开高级查询;
- 默认只暴露高频查询条件和必要字段,减少视觉噪声;
- 状态页签、快捷筛选、精确筛选形成渐进式查询路径;
- 业务人员使用业务语言,不需要理解 `AND`、`OR`、括号或查询表达式;
- 重复查询可以保存、设为默认并再次使用;
- 查询条件、分页、排序和当前视图可通过 URL 分享和恢复;
- 查询权限、字段权限和数据范围始终由后端 SQL 强制执行;
- 不保留旧查询接口、旧查询配置或旧数据结构的兼容转发层。
## 2. 核心设计原则
### 2.1 三层查询模型
```text
工作队列页签 我现在要处理哪一类?
↓
快捷筛选 我大概知道是哪几条?
↓
更多筛选 我需要精确查找哪一条?
```
对应到页面:
1. **工作队列页签**:预置常用状态或业务视图,进入页面即可使用。
2. **快捷筛选**:展示 3~5 个最高频的结构化条件。
3. **更多筛选**:按字段类型提供完整但易懂的筛选控件,默认收起。
暂不实现条件树、任意层级括号和查询语言。只有在后续确认存在稳定的分析师角色及真实使用需求后,才单独评估 L3/L4 能力。
### 2.2 页签和筛选的职责分离
- 页签代表工作队列或预设查询视图,不等同于数据库状态字段;
- 快捷筛选用于进一步缩小当前页签结果;
- 页签条件与用户筛选条件之间固定使用 `AND`;
- `有异常`、`我的订单`、`即将到期` 等跨字段场景也可以是页签;
- 不把所有生命周期状态平铺到页签,默认显示 4~6 个,其余进入“更多”。
### 2.3 业务语言优先
控件和文案不暴露技术表达式:
- 多选枚举:显示“满足任一”或“满足全部”;
- 日期:提供“今天、 本周、 本月、近三个月、自定义”;
- 数值:提供“大于、小于、介于”;
- 文本:默认“包含”,可选“等于、开头是”;
- 布尔值:提供“是、否、不限”,不强迫用户选择一个值。
## 3. 目标页面结构
```text
模块标题 新增 导出 更多
全部 128 | 待处理 12 | 有异常 5 | 已订舱 31 | 已完成 72 | 更多
[ 搜索订单号、客户、提单号、船名、航次 ]
[ 客户 ] [ ETD ] [ 业务员 ] [ 目的港 ] [ 更多筛选 ] 清除全部
客户:东方国际 × ETD:本月 ×
订单号 | 客户 | 航线/目的港 | ETD | 负责人 | 状态 | 异常 | 操作
```
### 3.1 页面区域职责
| 区域 | 职责 | 默认行为 |
| --- | --- | --- |
| 页面头部 | 模块标题、主操作、导出和更多操作 | 保持简洁,不能被筛选器挤压 |
| 工作队列页签 | 切换预设查询视图 | 默认打开模块配置的默认视图 |
| 关键词搜索 | 跨关键字段模糊搜索 | 回车或防抖后查询,保留输入值 |
| 快捷筛选 | 高频字段筛选 | 最多展示 3~5 个,可由模块配置 |
| 更多筛选 | 完整字段筛选 | 抽屉或弹层打开,关闭后保留已选条件 |
| 条件回显区 | 展示当前生效条件 | 每个条件可单独删除 |
| 数据表格 | 展示精简后的默认列 | 默认 6~8 列,支持列设置 |
## 4. 工作队列页签设计
工作队列页签、关键词搜索和快捷筛选均必须通过模块查询配置生成,不能在页面组件中写死业务字段、状态值或显示顺序。前端只实现通用渲染器和交互,不针对海运订单、报关单等模块增加专用分支。
配置的职责边界如下:
| 能力 | 配置决定的内容 | 前端通用能力 |
| --- | --- | --- |
| 工作队列页签 | 编码、文案、顺序、默认项、数量开关、预设条件、权限范围 | 渲染页签、切换视图、展示数量 |
| 关键词搜索 | 占位文案、可搜索字段、匹配方式、触发方式 | 输入、清除、防抖/回车触发 |
| 快捷筛选 | 字段、标签、顺序、控件类型、选项来源、默认值 | 根据字段元数据渲染控件、编辑值 |
| 更多筛选 | 字段分组、可用操作符、字段顺序、显示条件 | 抽屉布局、字段控件和条件回显 |
模块配置变更后,页面无需重新发布前端代码即可改变查询入口。前端只允许从服务端返回的字段元数据生成查询请求,不接受页面代码中额外注入的可查询字段。
### 4.1 页签配置模型
页签使用统一的预设视图模型,不限定只能绑定一个状态字段。
```json
{
"moduleCode": "sea_order",
"code": "pending",
"label": "待处理",
"type": "preset",
"order": 10,
"showCount": true,
"isDefault": true,
"query": {
"conditions": [
{
"field": "status",
"operator": "in",
"value": ["pending_audit", "pending_booking"]
}
]
}
}
```
一个模块的完整查询入口配置示例:
```json
{
"moduleCode": "sea_order",
"defaultViewCode": "pending",
"views": [
{
"code": "all",
"label": "全部",
"type": "all",
"order": 1,
"showCount": true
},
{
"code": "pending",
"label": "待处理",
"type": "preset",
"order": 2,
"showCount": true,
"isDefault": true,
"query": {
"conditions": [
{
"field": "status",
"operator": "in",
"value": ["pending_audit", "pending_booking"]
}
]
}
},
{
"code": "exception",
"label": "有异常",
"type": "preset",
"order": 3,
"showCount": true,
"query": {
"conditions": [
{
"field": "has_exception",
"operator": "=",
"value": true
}
]
}
}
],
"keywordSearch": {
"placeholder": "搜索订单号、客户、提单号、船名、航次",
"trigger": "enter_or_debounce",
"fields": [
{ "field": "order_no", "operator": "contains" },
{ "field": "customer_name", "operator": "contains" },
{ "field": "bill_of_lading_no", "operator": "contains" },
{ "field": "vessel_name", "operator": "contains" },
{ "field": "voyage_no", "operator": "contains" }
]
},
"quickFilters": [
{
"field": "customer_id",
"label": "客户",
"control": "select",
"multiple": true,
"optionSource": "customer",
"matchMode": "any",
"order": 1
},
{
"field": "etd",
"label": "ETD",
"control": "date_range",
"presets": ["today", "this_week", "this_month", "next_month"],
"order": 2
},
{
"field": "owner_id",
"label": "业务员",
"control": "select",
"multiple": true,
"optionSource": "current_user_scope",
"matchMode": "any",
"order": 3
}
],
"filterGroups": [
{
"code": "basic",
"label": "基础信息",
"order": 1,
"fields": ["customer_id", "owner_id", "status", "carrier_id"]
},
{
"code": "schedule",
"label": "船期与港口",
"order": 2,
"fields": ["etd", "eta", "pol", "pod"]
}
]
}
```
配置中的 `field` 必须引用模块字段元数据中的合法字段,`control`、`operator` 和 `optionSource` 必须由后端字段定义校验。前端不得仅因为配置返回了一个字段就绕过字段权限。
支持的页签类型:
- `all`:全部数据;
- `preset`:模块配置的固定预设查询;
- `personal`:用户保存的查询方案;
- `shared`:角色或团队共享的查询方案。
### 4.2 页签规则
- 默认展示 4~6 个页签;
- 超出数量的视图进入“更多”;
- 页签顺序、显示/隐藏、默认页签由模块配置和用户偏好共同决定;
- 用户偏好只能调整布局和默认视图,不能扩大权限范围;
- 页签统计数量使用与列表相同的权限、数据范围和过滤规则;
- 统计请求失败不阻塞列表,显示无数量状态并记录错误日志;
- 切换页签时保留用户已有的快捷筛选,若发生明显冲突则移除冲突条件并提示;
- 页签条件和快捷/更多筛选条件始终按 `AND` 组合。
### 4.3 数量统计策略
优先为待处理、有异常等具有行动价值的页签显示数量。数量统计接口应支持批量返回当前模块所有页签的数量,避免一个页签发起一次请求。
建议接口返回:
```json
{
"counts": {
"all": 128,
"pending": 12,
"exception": 5,
"completed": 72
},
"generatedAt": "2026-09-23T08:30:00+08:00"
}
```
数量不是强一致业务数据。允许短时间缓存,但必须在用户切换页签或刷新列表时提供可预测的刷新行为。
## 5. 查询条件设计
### 5.1 关键词搜索
每个模块配置一组关键词字段,关键词默认对这些字段执行跨字段模糊匹配。关键词框本身是通用组件,模块只提供占位文案、字段清单和触发策略。
示例:
```json
{
"keyword": {
"placeholder": "搜索订单号、客户、提单号、船名、航次",
"searchableFields": [
"orderNo",
"customerName",
"billOfLadingNo",
"vesselName",
"voyageNo"
]
}
}
```
关键词搜索不要求用户先选择字段。字段可见性和可查询权限仍由后端校验。
### 5.2 快捷筛选
每个模块配置 3~5 个快捷条件。选择依据:使用频率、业务价值、结果收敛效果和字段稳定性。页面不根据模块名称或路由写 `if/else` 决定快捷条件。
推荐字段:
- 客户;
- 负责人/业务员;
- 日期范围;
- 目的港或业务区域;
- 运输方式、船公司等高频枚举。
快捷筛选控件由字段类型决定,不允许所有字段统一使用普通文本框。
### 5.3 更多筛选
“更多筛选”以抽屉形式打开,按业务分组展示字段。默认只加载当前模块允许查询的字段,按需展开低频分组。
字段控件规则:
| 字段类型 | 控件 | 默认语义 |
| --- | --- | --- |
| 枚举 | 多选下拉/标签选择 | 满足任一 |
| 布尔 | 三态选择 | 不限 |
| 日期 | 预置区间 + 自定义范围 | 区间包含起止日期 |
| 数值 | 操作符 + 数值框 | 大于、小于、介于 |
| 文本 | 匹配方式 + 输入框 | 包含 |
| 多值字段 | 任一/全部切换 + 多选 | 满足任一 |
筛选抽屉底部固定提供:
```text
[重置] [取消] [应用筛选]
```
点击“取消”不改变当前已应用查询,点击“应用筛选”后统一刷新列表并回到第一页。
## 6. 表格和视图设计
### 6.1 默认列
每个模块必须定义一套精简的默认列,原则上控制在 6~8 列。默认列优先展示:
- 用户识别记录所需的主标识;
- 判断是否需要处理所需的状态;
- 判断时效所需的日期;
- 处理责任人;
- 异常或风险提示;
- 最常用的业务维度。
不在默认列表展示低频、技术性或详情级字段。低频字段通过详情页、列设置和导出字段获取。
### 6.2 列设置和预设视图
提供以下视图能力:
- 日常处理:面向操作员的精简列;
- 运营跟踪:包含船期、港口、节点等字段;
- 财务核对:包含金额、费用、开票等字段;
- 自定义:用户调整后的列和顺序。
列可见性、列顺序和列宽属于用户布局偏好,不得影响后端权限判断。
## 7. 保存查询方案
保存查询方案是本次重构的核心留存能力之一,优先级高于复杂逻辑组合。
支持:
- 保存当前页签、关键词、筛选条件、排序和列视图;
- 自定义名称;
- 设为默认;
- 最近使用;
- 删除和重新命名;
- 按用户、角色或团队共享;
- 将高频方案固定为页签或快捷入口。
查询方案保存的内容应是结构化查询状态,不保存最终 SQL。
```json
{
"name": "本月未订舱",
"moduleCode": "sea_order",
"viewCode": "pending",
"keyword": "",
"filters": {
"etd": { "preset": "this_month" },
"bookingStatus": ["unbooked"]
},
"sort": { "field": "etd", "direction": "asc" },
"columnPreset": "daily"
}
```
## 8. URL 状态和查询行为
> **实施状态(2026-09)**:URL 状态同步与查询分享**本期不做**(下期评估)。
> 原因:布局层的页签 key 与 keep-alive 缓存 key 都是 `route.fullPath`,直接把查询状态写进
> URL 会派生出新页签、破坏页面缓存;做 URL 同步需要先给列表路由做 key 归一化,属独立改动。
> 本节以下内容保留为下期目标。
以下状态必须可序列化到 URL:
- 当前页签/查询方案;
- 关键词;
- 结构化筛选条件;
- 排序;
- 当前页码和每页数量;
- 列视图(如需要)。
要求:
- 刷新页面后可恢复查询;
- 复制 URL 给有权限的同事后可打开相同查询;
- 无权限字段或无效条件由后端拒绝并由前端清晰提示;
- URL 不包含最终 SQL,不把权限判断交给前端;
- 条件变化后重置到第一页;修改单个条件不清空其他条件。
## 9. 后端和接口设计
### 9.1 统一查询请求
前端提交结构化查询状态,后端负责将配置、页签条件、用户条件、权限和数据范围组合成 SQL。
```json
{
"moduleCode": "sea_order",
"viewCode": "pending",
"keyword": "东方",
"filters": {
"customerId": [1001],
"etd": {
"from": "2026-09-01",
"to": "2026-09-30"
}
},
"sort": {
"field": "etd",
"direction": "asc"
},
"page": 1,
"pageSize": 20
}
```
后端处理顺序:
1. 加载模块、字段、页签和视图配置;
2. 校验字段可查询权限和操作符;
3. 合并页签预设条件和用户筛选条件;
4. 注入用户数据范围和权限条件;
5. 生成参数化 SQL 或执行模块配置中的合法 `SELECT`;
6. 返回分页数据、总数和必要的展示元数据。
### 9.2 查询配置接口
页面首次加载时获取模块查询配置,配置接口应一次返回当前用户可用的视图、关键词搜索、快捷筛选、更多筛选分组、字段元数据和默认列。
```json
{
"moduleCode": "sea_order",
"version": 3,
"defaultViewCode": "pending",
"views": [],
"keywordSearch": {},
"quickFilters": [],
"filterGroups": [],
"fields": [],
"columnPresets": []
}
```
配置接口必须完成以下处理:
- 过滤当前用户无权查看的页签、字段和选项来源;
- 过滤已停用或不存在的字段,记录配置错误;
- 校验页签预设条件和快捷筛选字段的合法性;
- 返回可直接渲染的字段类型、标签、操作符和选项元数据;
- 返回配置版本,便于前端缓存失效和问题定位。
### 9.3 建议接口
接口名称可按现有 API 风格落地,职责保持以下边界:
```text
GET/POST 查询模块配置
POST 查询列表
POST 查询数量统计
GET 查询方案列表
POST 保存查询方案
PUT 修改查询方案
DELETE 删除查询方案
```
列表查询和数量统计必须复用相同的过滤、权限和数据范围构建逻辑,避免页签数量与列表结果不一致。
### 9.4 数据库配置模型
新模型至少需要表达:
- 模块可查询字段及字段类型;
- 字段权限和可选操作符;
- 关键词搜索字段;
- 快捷筛选字段;
- 更多筛选字段分组;
- 默认表格列和列预设;
- 工作队列页签;
- 默认视图;
- 查询方案的所属用户、角色或团队。
具体表名应复用现有模块、字段、权限和用户配置体系。若现有结构无法表达上述关系,直接调整为新结构,不增加旧结构兼容读取逻辑。
## 10. 前端实现拆分
建议按页面职责拆分,具体目录以现有列表模块目录为准:
```text
list-page/
├── index.vue 页面编排、查询状态和数据加载
├── WorkQueueTabs.vue 工作队列页签和数量
├── KeywordSearch.vue 关键词搜索
├── QuickFilters.vue 快捷筛选
├── FilterDrawer.vue 更多筛选
├── ActiveFilterPills.vue 已应用条件回显
├── ResultTable.vue 表格和列设置入口
├── SavedQueryMenu.vue 保存查询方案和最近使用
└── query-utils.js 查询状态序列化、规范化和纯转换函数
```
职责要求:
- `index.vue` 持有唯一查询状态源,负责加载配置、拼接请求、分页和刷新;
- `index.vue` 不包含模块编码判断、业务状态常量、固定快捷字段或固定关键词字段;
- 各筛选组件只负责展示和编辑条件,通过 `v-model` 或语义明确的事件通信;
- `WorkQueueTabs.vue`、`KeywordSearch.vue` 和 `QuickFilters.vue` 均只消费配置,不内置模块专用选项;
- 不在多个组件复制查询条件;
- 不在子组件中自行请求同一列表或配置数据;
- 查询状态规范化、URL 编解码和条件标签生成等纯逻辑集中维护并可单测;
- 组件不直接拼接 SQL,也不承担权限判断。
## 11. 实施阶段
### 阶段一:查询模型和接口
- 确认新查询请求、响应和配置结构;
- 建立字段类型、操作符和筛选值的统一定义;
- 建立页签/预设视图模型;
- 建立模块查询配置接口,统一返回工作队列、关键词搜索、快捷筛选和字段元数据;
- 为首批模块录入查询配置,不在前端页面写入模块专用查询定义;
- 建立统一列表查询和数量统计服务;
- 将权限、字段权限和数据范围接入查询构建流程;
- 直接切换到新接口,不保留旧查询兼容入口。
### 阶段二:列表页基础体验
- 实现工作队列页签;
- 实现批量数量统计;
- 实现关键词搜索;
- 实现快捷筛选;
- 实现条件回显、单条件删除和全部清除;
- 实现查询变更后的分页重置;
- 精简默认表格列。
### 阶段三:更多筛选
- 实现字段类型到控件的映射;
- 实现日期预设区间和自定义范围;
- 实现数值比较和文本匹配方式;
- 实现多值字段“任一/全部”;
- 实现筛选抽屉的应用、取消和重置行为;
- 实现空结果提示和条件放宽入口。
### 阶段四:保存和个性化
- 实现保存查询方案;
- 实现默认视图、最近使用和方案删除;
- 实现列预设和用户列布局;
- 实现方案共享;
- 实现 URL 状态同步和查询分享。
### 阶段五:按模块推广
- 选择海运订单、报关单、费用单等状态清晰且使用频繁的模块作为首批模块;
- 为每个模块定义 4~6 个工作队列页签;
- 根据实际查询日志调整快捷字段和默认列;
- 再推广到客户档案等没有明显生命周期的模块,此类模块可以只启用关键词、快捷筛选和更多筛选,不强制配置页签。
## 12. 测试计划
### 12.1 后端测试
- 页签条件与用户筛选条件按 `AND` 合并;
- 多选字段“任一/全部”生成正确 SQL;
- 日期边界、时区和包含起止日期行为正确;
- 数值比较符行为正确;
- 关键词跨字段搜索行为正确;
- 字段权限、模块权限和数据范围始终生效;
- 列表数量与页签统计使用相同条件;
- 非法字段、非法操作符和越权条件被拒绝;
- 分页、排序和筛选参数不会产生非查询 SQL;
- 查询方案只能被授权用户读取或修改。
### 12.2 前端测试
- 默认页签和默认条件正确加载;
- 切换页签保留或清理筛选条件的规则正确;
- 条件标签可单独删除;
- 更多筛选的取消不影响已应用条件;
- 应用筛选后回到第一页;
- 刷新或打开分享 URL 可恢复查询;
- 空结果状态能够定位和清除条件;
- 数量加载失败不阻塞列表;
- 表格列视图切换不改变查询结果;
- 保存、修改、删除和设为默认查询方案正常。
### 12.3 验收场景
至少用真实业务流程验证:
1. 业务员进入海运订单,直接点击“待处理”处理自己的工作队列;
2. 在“待处理”下筛选某客户和本月 ETD;
3. 通过“更多筛选”查找同时包含多个服务项的订单;
4. 将“本月未订舱”保存为个人默认查询;
5. 复制查询 URL 给另一位有权限的同事;
6. 无权限用户打开同一 URL 时看不到越权数据;
7. 查询无结果时能明确看到条件并一键放宽或清除。
## 13. 观测指标
上线后按模块记录以下指标,用于持续收敛默认配置:
- 从进入列表到首次有效查询的耗时;
- 页签点击占比及各页签使用频率;
- 快捷筛选使用频率;
- 更多筛选打开率和应用成功率;
- 空结果率;
- 查询后再次修改条件的次数;
- 保存查询方案数量和复用次数;
- 默认列设置被修改的比例;
- 列表查询接口耗时、数量统计接口耗时和错误率。
判断标准不是“可配置项是否足够多”,而是业务是否更少打开复杂筛选、更少遇到空结果、更快找到需要处理的数据。
## 14. 明确不做的事项
- 不在第一版实现任意层级的 AND/OR 条件树;
- 不实现查询表达式语言;
- 不把所有字段默认展示为筛选项;
- 不把所有生命周期状态都平铺为页签;
- 不把最终 SQL 保存到用户查询方案;
- 不在前端执行权限过滤代替后端 SQL;
- 不保留旧查询接口、旧查询配置、旧字段映射或旧数据兼容逻辑;
- 不为每个业务模块复制一套专用查询接口;
- **本期不做**:URL 状态同步与查询分享(见 §8 实施状态);保存查询方案与列预设视图亦下期再做。
## 15. 完成标准
- 新查询模型和新接口在目标模块生效,旧查询入口已移除;
- 工作队列页签、关键词搜索和快捷筛选均由模块配置生成,前端没有模块专用分支;
- 新增或调整模块查询入口只需要修改模块配置和字段元数据,不需要修改前端组件代码;
- 列表页默认只展示必要的工作队列、筛选条件和表格列;
- 业务人员可以不打开“更多筛选”完成主要日常工作;
- 复杂查询通过字段类型匹配的业务控件完成,不需要理解技术表达式;
- 查询条件可回显、单独修改、清除和通过 URL 恢复;
- 常用查询可以保存、设为默认并再次使用;
- 页签数量与列表结果遵守相同的权限和数据范围;
- 关键前后端测试通过,真实业务验收场景可完成;
- 查询日志和性能指标已接入,能够根据使用数据继续调整页签、快捷字段和默认列。