feat: add team-aware CRM queries and project tracking

This commit is contained in:
nac
2026-08-14 11:00:13 +00:00
parent 4b098eb4dd
commit 76e4cbf58a
13 changed files with 2761 additions and 263 deletions
@@ -0,0 +1,408 @@
# 团队 CRM 查询与项目追踪设计
## 目标
在保持现有只读 CRM MCP 和受控项目写入兼容的前提下,增加:
- 基于 CRM 正式团队关系的查询;
- 团队与成员双层客户、跟进和项目统计;
- 长期未跟进客户查询,并明确标注当前 NocoBase 不支持的闭环能力;
- 客户归属、实际记录人、参与人员等角色的明确区分;
- CRM 项目完整跟进时间线;
- 北京时间、完整分页、稳定 JSON 解析和数据质量提示。
本设计以 2026-07-24 从目标 NocoBase 读取的集合和字段元数据为依据。元数据盘点未读取或修改业务记录。
## 已确认的数据模型
### 团队与用户
团队集合为 `t_team_management`(团队管理)。
| 业务含义 | 字段 | 类型 | 目标 |
| --- | --- | --- | --- |
| 团队名 | `f_team_name` | string | - |
| 团队负责人 | `tdgl_manager` | belongsToMany | `users` |
| 团队成员 | `tdgl_team_members` | belongsToMany | `users` |
`users` 中存在反向关系:
- `yh_belong_teams`:所属团队;
- `yh_managed_team`:管理的团队;
- `yh_status`:员工状态。
员工状态中已确认:
| 标签 | 内部值 |
| --- | --- |
| 正式员工 | `nkluuumb1r1` |
| 实习生 | `n7cnn29kzo1` |
| 已离职 | `bpsfm63uc8i` |
| 交接中 | `yv6pkhjxtfz` |
| 临时员工 | `4g6m1y5uy1s` |
“正式团队成员”定义为:
1. 用户出现在团队的 `tdgl_team_members` 关系中;
2. 用户的 `yh_status` 等于 `nkluuumb1r1`。
负责人可以有多人,不应在 MCP 中强制压缩为单个负责人。
系统另有 `departments` 树形部门集合。部门和 CRM 团队是两套不同关系,首版团队查询以 `t_team_management` 为准,不用部门关系替代团队关系。
### 客户角色
客户集合为 `t_nt3pjadelqi`。
| 字段 | 标题 | 建议语义 |
| --- | --- | --- |
| `kehu_manage_person` | 管理人 | 管理或权限相关人员,不代表客户归属 |
| `kehu_followup_person` | 跟进人 | 客户归属人员 |
| `kehu_lookup_people` | 可查看人 | ACL 可见人员,不代表业务协同 |
| `createdBy` | 创建人 | 数据创建者,不代表客户负责人 |
上述前三个字段均可能包含多人。客户归属以 `kehu_followup_person` 为准。MCP 不应将
`kehu_manage_person` 或 `kehu_lookup_people` 计为客户归属或协同贡献,也不应把
`createdBy` 当作客户负责人。
由于 `kehu_followup_person` 是多值关系,接口使用复数 `customer_owners`,不能从列表
顺序推断唯一主负责人。
### 跟进角色与分类
跟进集合为 `t_07667a5evtr`。
| 字段 | 标题 | 建议语义 |
| --- | --- | --- |
| `createdBy` | 创建人 | 跟进记录提交人 |
| `gjjl_followup_person` | 参与跟进人员 | 明确的参与人员 |
| `gjjl_client` | 客户 | 关联客户 |
| `gjjl_client_contacts` | 客户联系人 | 关联联系人 |
| `f_gjjl_projects` | 相关项目 | 关联 CRM 项目 |
| `f_srysmjkwfq1` | 跟进时间 | 业务发生时间 |
| `gjjl_followup_type` | 跟进类型 | 沟通方式 |
| `f_19iplxz0nds` | 跟进内容 | 跟进正文 |
| `f_hskl7gjvos9` | 状态 | 记录状态 |
已确认的跟进方式:
- 邮件
- 电话
- 视频会议
- 拜访/请客
- 审厂/来访
- 论坛/大会
- 代理跟进情况代填
已确认的跟进状态为“审批中、OK、迟交、无效”。它更接近记录审批或提交质量状态,不能直接解释为下一步事项的完成状态。
首版角色输出必须同时保留:
- `record_creator`:`createdBy`,只表示记录提交人;
- `participants`:`gjjl_followup_person`;
- `customer_owners`:关联客户的 `kehu_followup_person`;
- `customer_management_users`:关联客户的 `kehu_manage_person`,仅作为补充信息;
- `team_managers`:命中团队的 `tdgl_manager`。
`createdBy` 不一定是实际跟进人,因此不得用于实际跟进贡献统计。实际跟进贡献只按
`gjjl_followup_person` 统计;多人参与时每位参与人各获得一次参与贡献,同时团队总跟进
记录按记录 ID 去重。`record_creator` 仅用于审计和数据质量检查。
### CRM 项目
CRM 项目集合为 `t_projects`,与现有 MCP 项目写入使用同一集合。
已确认关系:
- 客户:`f_projects_client` / 外键 `client_ID`;
- 联系人:`f_projects_contacts`;
- 跟进记录:`f_projects_followup_records`;
- 项目进展:`f_projects_progress`;
- 项目情况更新:`f_u28gm9h2uzl`;
- 附件:`f_projects_attachments`。
项目进展为:
- 询价阶段
- 已报价
- 送样
- 已定点
- SOP
- 终止
项目情况更新集合为 `t_qxeepjvi39s`,包含:
- 项目:`f_k37rbpi4vye`;
- 更新日期:`f_6g9fw5oqef9`;
- 更新内容:`f_ei16g0ihwwd`;
- 附件:`f_6pk5pv17i27`;
- 创建人:`createdBy`。
当前没有独立的报价、样品、测试或定点事件集合映射。首版只能使用项目阶段和项目情况更新正文展示这些信息,不能可靠统计每类业务事件的数量或状态。
### 另一套项目管理模型
系统还存在:
- `market_pm`(项目管理);
- `project_updates`(项目节点)。
这套模型有项目负责人、预计完成时间、项目状态,以及节点负责人、节点状态和节点预计完成时间,但没有已确认的客户关系,也没有与 `t_projects` 的关联字段。
因此:
- `t_projects` 是 CRM 项目查询的权威来源;
- `market_pm` / `project_updates` 作为独立的任务型项目模型;
- 在没有稳定关联键之前,两套项目不得按名称自动合并;
- 如后续需要统一展示,返回值必须带 `project_source`,例如 `crm` 或 `management`。
## 当前数据模型缺口
跟进记录中没有已确认的以下结构化字段:
- 下一步事项;
- 事项责任人;
- 截止日期;
- 完成状态;
- 完成时间;
- 优先级;
- 风险类型。
因此“逾期未完成事项”和“无下一步的重点项目”不能仅凭现有跟进记录准确计算。
本项目不得修改 NocoBase 数据模型,因此不新增行动项集合,也不增加跟进字段。以下能力
明确跳过:
- 精确查询逾期未完成事项;
- 按行动项完成状态统计;
- 权威判断跟进是否存在下一步;
- 权威判断下一步责任人和截止日期。
不从跟进正文推断权威的下一步、风险或逾期状态。
报价、样品、测试等完整链路同样需要结构化事件模型。项目阶段只能表示当前状态,不能还原每次状态变化的时间和责任人。
## 配置扩展
建议在 `config.json` 增加以下可配置映射,默认值采用本次确认字段:
```json
{
"timezone": "Asia/Shanghai",
"teams": {
"resource": "t_team_management",
"idField": "id",
"nameField": "f_team_name",
"managerField": "tdgl_manager",
"memberField": "tdgl_team_members",
"userStatusField": "yh_status",
"formalUserStatusValue": "nkluuumb1r1"
},
"roles": {
"customerOwnerField": "kehu_followup_person",
"customerManagementField": "kehu_manage_person",
"customerViewerField": "kehu_lookup_people",
"followupCreatorField": "createdBy",
"followupParticipantField": "gjjl_followup_person"
},
"crmProjects": {
"contactField": "f_projects_contacts",
"followupField": "f_projects_followup_records",
"updateField": "f_u28gm9h2uzl",
"updateResource": "t_qxeepjvi39s",
"updateProjectField": "f_k37rbpi4vye",
"updateDateField": "f_6g9fw5oqef9",
"updateContentField": "f_ei16g0ihwwd"
}
}
```
不设计或预留会写入 NocoBase 的闭环字段。相关能力在 MCP capability 中明确标为不可用。
## MCP 工具设计
### 团队发现
```text
list_teams(query?, include_members=false, formal_members_only=true)
resolve_team(query)
get_team(team_id, include_members=true, formal_members_only=true)
```
团队解析规则与负责人解析一致:无匹配或多匹配返回结构化候选结果,不静默选择。
### 查询工具兼容扩展
现有工具保留原参数,并增加:
```text
list_customers(..., team?, team_id?)
list_followups(..., team?, team_id?, project_id?, followup_type?, record_status?)
crm_overview(..., team?, team_id?)
```
`team` 和 `team_id` 不得同时提供。团队过滤使用正式成员 ID 集合,客户和跟进分别应用对应角色关系,不能把“团队客户”和“团队实际跟进”混成同一口径。
### 团队统计
```text
team_crm_overview(
team,
date_from?,
date_to?,
formal_members_only=true,
include_member_breakdown=true
)
```
建议返回:
```json
{
"team": {},
"period": {},
"totals": {
"assigned_customers": 0,
"owned_customers": 0,
"followup_records": 0,
"active_customers": 0,
"active_projects": 0
},
"by_member": [],
"by_customer": [],
"by_followup_type": [],
"by_project_stage": [],
"contribution_basis": {
"participant": "gjjl_followup_person"
}
}
```
同一条多人参与的跟进:
- 团队总量只计一次;
- 成员贡献中每位参与人各计一次;
- 返回 `unique_followups` 和 `participation_count`,防止将两种口径混淆。
### 项目追溯
```text
get_project(project_id, include_contacts=true, include_followups=true)
get_project_timeline(project_id, date_from?, date_to?)
list_projects(customer_id?, team?, stage?, updated_since?)
```
时间线事件来源:
- 项目创建与阶段;
- 项目情况更新;
- 关联跟进记录;
- 联系人;
- 附件元数据。
每个事件返回 `event_type`、`occurred_at`、`actors`、`source_resource` 和 `source_id`。
### 长期未跟进客户
现有模型可以支持:
```text
list_stale_customers(team?, owner?, inactive_days=30, as_of?)
```
该工具仅依据客户最后一次有效跟进时间计算。以下工具不实现:
- `list_action_items`
- `list_overdue_action_items`
不得以跟进记录的“迟交”状态代替行动项逾期状态。
## 时间、分页与统计规则
### 北京时间
- 服务内部使用带时区的 `datetime`;
- NocoBase UTC 时间转换到 `Asia/Shanghai`;
- `dateOnly` 保持日期不做时区偏移;
- 输出统一为 ISO 8601,例如 `2026-07-24T15:30:00+08:00`;
- 响应增加 `"timezone": "Asia/Shanghai"`。
### 分页
保持现有 `limit`、`offset` 参数兼容,但修复任意 offset:
- 不再直接用 `offset // limit + 1` 近似;
- 读取覆盖目标窗口的底层页并在服务层精确切片;
- 返回 `limit`、`offset`、`total`、`has_more`、`next_offset`;
- 内部全量统计循环读取所有页;
- 设置独立的最大扫描记录数和超时保护,超过时返回明确的截断警告。
### 聚合
- 团队总量按记录 ID 去重;
- 成员贡献保留参与次数;
- 所有聚合返回统计口径;
- “无效”跟进不计入有效贡献;“审批中、OK、迟交”保留并分别展示;
- 不再使用固定 50 个分组作为完整统计结果。
## JSON 与元数据稳定性
`nb` 输出解析需要支持 stdout 混入非 JSON 日志的情况:
1. 继续设置 `NB_CLI_LOG_DISABLED=1` 和 `NB_SKIP_STARTUP_UPDATE=1`;
2. 首先尝试对完整 stdout 执行 `json.loads`;
3. 失败时使用 `json.JSONDecoder.raw_decode` 定位完整顶层 JSON 对象;
4. 仅接受唯一、完整的 JSON 对象;
5. 其余文本作为已脱敏诊断信息,不进入正常 MCP 数据;
6. 无法可靠定位时返回 `nb_invalid_json`,不返回部分业务数据;
7. API Token 必须同时从命令、stdout 和 stderr 中脱敏。
## 数据质量提示
查询响应增加统一的 `data_quality_warnings`,包括:
- 客户有多个归属跟进人;
- 跟进没有参与人员,无法归属实际贡献;
- 跟进没有客户或项目;
- 项目更新没有业务日期;
- 项目阶段有变化但没有结构化事件记录;
- 团队成员不是正式员工;
- 统计因扫描上限被截断。
## 兼容与实施顺序
### 第一阶段:基础稳定性
- 北京时间转换;
- 精确分页和全量聚合;
- JSON 日志混入容错;
- 统一响应元数据和测试。
### 第二阶段:团队查询
- 团队配置、解析和成员发现;
- 客户与跟进增加团队过滤;
- 分离客户归属跟进人、管理人、记录创建人和参与人员;
- 团队整体与成员贡献统计。
### 第三阶段:CRM 项目时间线
- CRM 项目只读查询;
- 关联客户、联系人、跟进和项目情况更新;
- 长期未跟进客户查询。
现有工具名、参数和默认紧凑输出保持兼容。新字段只做增量添加;团队和项目查询工具为新工具。现有 stdio 模式继续只读,HTTP 写入范围仍只限当前项目字段和附件操作。
## 已确认业务决策
1. 客户归属以 `kehu_followup_person`(跟进人)为准。
2. `createdBy` 不一定是实际跟进人,只作为记录提交人展示。
3. 不修改 NocoBase 数据模型;现有模型不支持的闭环能力直接跳过。
## 实施时采用的默认口径
1. 实际跟进贡献按 `gjjl_followup_person`(参与跟进人员)统计。
2. 正式成员只包含 `yh_status = nkluuumb1r1`,排除实习、离职、交接中和临时员工。
3. `t_projects` 作为 CRM 项目权威表;不与 `market_pm` 按名称自动合并。
4. 团队统计默认排除“无效”跟进;“审批中、OK、迟交”保留并分别展示。
5. 长期未跟进默认阈值为 30 天,允许调用方覆盖。