diff --git a/.codex/agents/luna.toml b/.codex/agents/luna.toml new file mode 100644 index 0000000..771ccb2 --- /dev/null +++ b/.codex/agents/luna.toml @@ -0,0 +1,14 @@ +name = "luna" +description = "低成本实现智能体,负责需求明确的功能、测试、局部重构和明确缺陷修复。" +model = "gpt-5.6-luna" +model_reasoning_effort = "max" +sandbox_mode = "workspace-write" +developer_instructions = """ +只有在主智能体已经明确目标、涉及文件或子系统、约束、验收标准和验证目标后,才作为实现执行者开展工作。 +负责边界清晰的功能实现、单元测试补齐、局部重构、机械性修改、明确缺陷修复和局部代码审查。 +保持上下文精简,严格遵循既定方案;未经明确授权不得修改接口或数据结构,并始终实施最小且正确的改动。 +使用针对性测试验证要求的行为,返回简洁总结,不要堆积原始探索日志。 +不得负责开放式根因分析、仅在生产环境出现的疑难问题、架构审查,或需要在多个子系统间持续演进假设的调试工作。 +需求含糊、证据与既定方案冲突或工作范围扩展到多个模块时,必须停止并向主智能体报告阻碍;应建议交由高级执行智能体处理,不得继续猜测性探索。 +代码库中还有其他协作者:不得撤销他人的修改,并应主动适配并行产生的变更。 +""" diff --git a/.codex/agents/spark.toml b/.codex/agents/spark.toml new file mode 100644 index 0000000..a83ead7 --- /dev/null +++ b/.codex/agents/spark.toml @@ -0,0 +1,14 @@ +name = "spark" +description = "快速探索与微小修改智能体,负责代码搜索、结构定位、简单验证和边界明确的小改动。" +model = "gpt-5.3-codex-spark" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +developer_instructions = """ +优先快速完成代码搜索、仓库结构定位、符号与调用点查找、简单验证以及明确的微小修改。 +只进行有针对性的搜索和文件读取,返回提炼后的结论,不要堆积原始日志。 +修改代码时只做能够成立的最小改动,避免无关重构和大范围清理。 +使用范围最小且有效的测试、类型检查、静态检查或构建命令,只验证本次修改影响的行为。 +不得负责模糊的根因调查、跨模块实现或架构决策。 +任务超出小而明确的范围时必须停止,将证据上报主智能体,并建议交由高级执行智能体继续处理。 +代码库中还有其他协作者:不得撤销他人的修改,并应主动适配并行产生的变更。 +""" diff --git a/.codex/agents/terra.toml b/.codex/agents/terra.toml new file mode 100644 index 0000000..43771d6 --- /dev/null +++ b/.codex/agents/terra.toml @@ -0,0 +1,14 @@ +name = "terra" +description = "高级执行智能体,负责复杂修改、跨模块调试和深度代码审查。" +model = "gpt-5.6-terra" +model_reasoning_effort = "high" +sandbox_mode = "workspace-write" +developer_instructions = """ +作为主智能体下属的高级工程执行者开展工作。 +负责复杂实现、跨模块修改、需要持续修正假设的调试,以及从调查到验证的深度正确性审查。 +在多次工具调用中持续维护任务的系统约束,追踪真实执行路径,并明确区分证据与假设。 +优先采用能够解决根因的最小可维护方案,保持无关行为不变,并按需新增或更新针对性测试。 +执行与风险相称的检查,返回便于决策的简洁总结,包括根因、修改文件、验证结果和剩余风险。 +涉及架构、产品范围或不可逆设计决策时,必须上报主智能体,不得擅自决定。 +代码库中还有其他协作者:不得撤销他人的修改,并应主动适配并行产生的变更。 +""" diff --git a/.gitignore b/.gitignore index 31137a9..28df4b9 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,9 @@ cover/ local_settings.py db.sqlite3 db.sqlite3-journal +deprecated/backend/risk.db +deprecated/backend/risk.db.*.bak +deprecated/backend/tests/test-risk.db # Flask stuff: instance/ @@ -217,6 +220,7 @@ build/Release # Dependency directories node_modules/ +.logs/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) @@ -312,3 +316,5 @@ dist .yarn/install-state.gz .pnp.* +# project +deprecated/ \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..cc399eb --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,73 @@ +# 项目协作原则 + +## 核心原则 + +- 保持简单:优先选择最小、清晰、可维护的方案。 +- 先读后改:以实际代码、配置和文档为依据,不凭经验猜测。 +- 聚焦目标:不做无关重构,不扩大任务范围。 +- 保持兼容:未经确认,不改变公开接口、数据结构、认证方式或既有行为。 +- 如实验证:没有实际执行的检查,不得声称通过。 +- 默认使用中文编写说明和项目文档;代码标识符遵循技术规范。 + +## 项目现状 + +- 本项目是风险防控管理系统,目前处于需求与方案整理阶段。 +- `deprecated/docs/前期讨论/` 仅作为参考输入,不代表最终需求。 +- 技术栈尚未确定,不得臆造目录、依赖、构建或测试命令。 + +## 智能体分工 + +- 主智能体:理解需求、制定方案、拆分任务、集成结果和最终审查。 +- `spark`:代码搜索、结构定位、简单验证和微小修改。 +- `luna`:执行目标、范围和验收标准均已明确的局部实现与测试。 +- `terra`:复杂实现、跨模块调试、深度审查和根因分析。 +- 升级顺序:`spark` 或 `luna` → `terra` → 主智能体。 + +## 协作规则 + +- 简单任务由主智能体直接完成;仅在子任务独立且可验证时使用多个智能体。 +- 委派时必须说明目标、负责范围、禁止修改范围、验收标准和验证方式。 +- 并行修改必须划分文件或模块所有权,不得让两个智能体同时修改同一处代码。 +- 不得撤销、覆盖或整理用户及其他智能体的修改。 +- 子智能体发现任务超出职责时立即上报,不得继续猜测性探索。 +- 主智能体负责审查子智能体的修改和验证证据。 + +## 开发要求 + +- 修改前检查仓库状态并阅读相关文件。 +- 优先复用现有模式,实施能够解决问题的最小改动。 +- 未经允许,不新增生产依赖,不修改数据库结构,不扩大权限。 +- 修复缺陷应处理根因,并补充必要的回归验证。 +- 涉及风险状态流转时,检查权限、审计、幂等、并发、回滚和非法状态跃迁。 +- 不得泄露或提交密码、令牌、个人敏感信息和内部安全数据。 +- 对于Windows系统,必须在WSL环境中进行开发。中间件推荐使用WSL中的docker运行。Python使用WSL中的venv虚拟环境进行开发与测试。 +- 在尚未完成1.0版本发布之前,不需要考虑数据库迁移问题,直接重构数据库即可。 + +## 验证与交付 + +- 从项目实际配置中识别测试、检查和构建命令。 +- 先运行与修改直接相关的最小验证,再按风险扩大范围。 +- 无法验证时,说明原因和剩余风险。 +- 最终说明应包含:完成内容、修改文件、验证结果和未解决问题。 +- 未经用户明确要求,不创建分支、不提交、不推送。 + +## 完成标准 + +- 用户要求已经满足。 +- 没有无关修改或临时内容。 +- 相关验证已通过,或限制与风险已明确说明。 +- 主智能体已审查最终差异。 + +## Agent skills + +### Issue tracker + +本仓库使用 `.scratch//` 下的本地 Markdown 文件管理需求、规格和任务。详见 `docs/agents/issue-tracker.md`。 + +### Triage labels + +本仓库使用五类默认 triage 状态:`needs-triage`、`needs-info`、`ready-for-agent`、`ready-for-human`、`wontfix`。详见 `docs/agents/triage-labels.md`。 + +### Domain docs + +本仓库采用单上下文领域文档结构:根目录使用 `CONTEXT.md`,架构决策记录放置于 `docs/adr/`。详见 `docs/agents/domain.md`。 diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 0000000..3cab550 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,51 @@ +# 风险防控工单域 + +本上下文统一 12398 风险防控工单在派工、调查、审核、回访和报告环节中的业务语言,避免将角色、责任类型与流程状态混用。 + +## Language + +**12398 工单**: +由 12398 渠道取得并导入系统、关联 95598 处理记录的核心业务对象。 +_Avoid_: 投诉单、事件单、任务单 + +**服务监督主管**: +负责判定责任类型并完成主管派工的业务角色,也是全面调查的一级审核人。 +_Avoid_: 主管、监督主管 + +**服务监督员**: +派工后承担全面调查、回访结果确认、调查报告和成果物输出的业务角色。 +_Avoid_: 服务监督人员、调查员 + +**责任类型**: +服务监督主管在派工阶段对工单作出的主责或协查判定;该判定是后续前置条件和审核分支的业务事实。 +_Avoid_: 责任状态、工单类型 + +**主责**: +需要同时完成首联和全部并行初查,业务部主任审核通过后进入回访的责任类型。 + +**协查**: +不产生首联任务、派工后直接进入多部门并行事件初查,业务部主任审核通过后直接进入调查报告的责任类型。 + +**首联**: +主责工单中客服班组首次联系客户形成的业务记录。 +_Avoid_: 首次回访、回访 + +**事件初查**: +一个或多个被派部门并行完成的初步调查任务集合。 +_Avoid_: 全面调查、调查结果 + +**全面调查**: +服务监督员在派工后即可先行填报并保存草稿、满足责任类型对应前置条件后才能提交的完整调查结果。 +_Avoid_: 事件初查、业务审核 + +**业务部主任审核**: +全面调查的二级审核;审核通过后依据派工阶段的责任类型创建唯一后续分支。 +_Avoid_: 主管审核、调查审核 + +**回访**: +仅主责工单进入的客户联系与结果确认环节;结果使用“同意”或“不同意或客户主动要求”。 +_Avoid_: 首联、满意度调查 + +**调查报告**: +基于已确认调查数据及主责回访数据形成的受控输出版本;协查报告不依赖首联或回访。 +_Avoid_: 全面调查、成果物 diff --git a/README.md b/README.md index 9f3e2bc..4502448 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,7 @@ # risk-management-process +## 工作说明 + +当前项目聚焦于 `frontend/`:直接依据原型界面设计复现前端,由外部团队实现后端,本项目负责接口规范设计、前端界面实现和前后端联调。 + +此前探索的完整系统实现、需求方案文档和构建产物已迁移至 `deprecated/`,仅作历史参考,不再作为当前实现依据。 diff --git a/docs/agents/domain.md b/docs/agents/domain.md new file mode 100644 index 0000000..e861aec --- /dev/null +++ b/docs/agents/domain.md @@ -0,0 +1,41 @@ +# 领域文档 + +本文件说明工程技能在分析和修改仓库前,应如何读取领域文档。 + +## 开始分析前 + +按需读取: + +- 根目录的 `CONTEXT.md` +- 如果将来出现 `CONTEXT-MAP.md`,由它定位当前主题对应的领域上下文 +- `docs/adr/` 中与当前修改相关的架构决策记录 + +文件不存在时静默继续,不需要预先创建空文件。领域词汇和架构决策应在实际形成结论时按需补充。 + +## 当前布局 + +本仓库采用单上下文结构: + +```text +/ +├── CONTEXT.md +└── docs/ + └── adr/ +``` + +`CONTEXT.md` 只保存领域术语和统一语言;`docs/adr/` 保存具有长期影响的架构决策。 + +## 使用统一领域语言 + +Issue 标题、规格、重构建议、测试名称和实现说明应使用 `CONTEXT.md` 中定义的术语,不应改用其中明确要求避免的同义词。 + +如果需要使用的概念尚未出现在词汇表中,应先判断: + +1. 是否正在引入项目并不存在的新术语; +2. 是否确实发现领域词汇缺口。 + +真实的领域词汇缺口应通过领域建模流程补充。 + +## 发现 ADR 冲突时 + +如果计划或实现与现有 ADR 冲突,必须明确指出冲突及理由,不得静默覆盖既有决策。 diff --git a/docs/agents/issue-tracker.md b/docs/agents/issue-tracker.md new file mode 100644 index 0000000..7e8d8dd --- /dev/null +++ b/docs/agents/issue-tracker.md @@ -0,0 +1,32 @@ +# Issue tracker:本地 Markdown + +本仓库的需求、规格和实施任务使用 `.scratch/` 下的 Markdown 文件管理。 + +## 约定 + +- 每个功能使用独立目录:`.scratch//` +- 功能规格为:`.scratch//spec.md` +- 实施任务存放于:`.scratch//issues/-.md` +- 任务从 `01` 开始编号,每个任务使用独立文件,不创建合并的 tickets 文件 +- Triage 状态记录在任务文件顶部附近的 `Status:` 行 +- 评论和讨论记录追加到文件底部的 `## Comments` 小节 + +## 发布到 Issue Tracker + +当技能要求“发布到 Issue Tracker”时,在 `.scratch//` 下创建对应文件;目录不存在时可以创建。 + +## 获取任务 + +当技能要求获取相关任务时,读取用户提供的任务路径或任务编号对应的文件。 + +## Wayfinding 约定 + +- 地图:`.scratch//map.md` +- 子任务:`.scratch//issues/NN-.md` +- 子任务通过 `Type:` 记录类型:`research`、`prototype`、`grilling` 或 `task` +- 子任务通过 `Status:` 记录 `claimed` 或 `resolved` +- 依赖关系使用 `Blocked by: NN, NN` +- 所有依赖任务为 `resolved` 后,当前任务才解除阻塞 +- 可执行任务按编号顺序领取 +- 领取时先将状态设为 `claimed` 并保存 +- 完成时在 `## Answer` 下记录结果,将状态改为 `resolved`,并向 `map.md` 的 Decisions-so-far 添加摘要和链接 diff --git a/docs/agents/triage-labels.md b/docs/agents/triage-labels.md new file mode 100644 index 0000000..24f31b8 --- /dev/null +++ b/docs/agents/triage-labels.md @@ -0,0 +1,13 @@ +# Triage Labels + +工程技能使用五类标准 triage 角色。本文件记录标准角色与本仓库实际状态字符串的对应关系。 + +| 标准角色 | 本仓库状态 | 含义 | +| --- | --- | --- | +| `needs-triage` | `needs-triage` | 等待维护者评估 | +| `needs-info` | `needs-info` | 等待报告人补充信息 | +| `ready-for-agent` | `ready-for-agent` | 需求完整,可由智能体独立实施 | +| `ready-for-human` | `ready-for-human` | 需要人工实施或决策 | +| `wontfix` | `wontfix` | 确定不处理 | + +当技能使用某类 triage 角色时,应采用本表右侧对应的状态字符串。 diff --git a/docs/前期讨论/001-后端API文档-用户管理相关.md b/docs/前期讨论/001-后端API文档-用户管理相关.md new file mode 100644 index 0000000..63f10f1 --- /dev/null +++ b/docs/前期讨论/001-后端API文档-用户管理相关.md @@ -0,0 +1,294 @@ +# 标题:风险防控管理系统_接口文档 + +> 版本:版本号:3.9.2 +> 服务地址:http://localhost:8080 +> 生成说明:基于 Swagger OpenAPI 3.1 自动提取(规范共 155 个接口路径,本文档仅整理以下 7 个目标接口)。该文档由后端团队提供,仅提供基础用户登录相关API。其他API尚待设计。 + +## 认证方式 +- 方案 `apikey`:请求头 `Authorization: Bearer `(scheme=Bearer) + - 除「验证码」「用户登录」外,其它接口均需在请求头携带该令牌。 + +## 通用返回结构说明 + +本项目所有业务接口统一返回 `AjaxResult`(列表接口返回 `TableDataInfo`)。 +> 注意:本 Swagger 规范中 `AjaxResult` 仅声明了 `error/success/warn/empty` 四个布尔字段, +> 未建模真实的 `code / msg / data` 字段。以下为运行时实际返回结构,编写调用代码请以实际返回为准。 + +**AjaxResult(单对象 / 操作结果):** + +```json +{ + "code": 200, + "msg": "操作成功", + "data": {} +} +``` + +**TableDataInfo(分页列表):** + +```json +{ + "total": 0, + "rows": [ + {} + ], + "code": 200, + "msg": "查询成功" +} +``` + +- `code`:状态码(200 成功,其它为错误码) +- `msg`:提示信息 +- `data`:业务数据对象(列表接口的 `rows` 为该对象数组) + +## 接口总览 + +| 序号 | 接口 | 方法 | 路径 | 分组 | +| --- | --- | --- | --- | --- | +| 1 | 用户列表 | GET | `/system/user/list` | 系统管理 | +| 2 | 部门树 | GET | `/system/user/deptTree` | 系统管理 | +| 3 | 用户信息-任意人员 | GET | `/system/user/{userId}` | 系统管理 | +| 4 | 用户信息-个人信息 | GET | `/system/user/profile` | 系统管理 | +| 5 | 修改密码 | PUT | `/system/user/profile/updatePwd` | 系统管理 | +| 6 | 验证码 | GET | `/captchaImage` | 公共 | +| 7 | 用户登陆 | POST | `/login` | 公共 | + +## 1. 用户列表 + +- **路径**:`GET /system/user/list` +- **分组(Tag)**:sys-user-controller + +### 请求参数 + +查询通过对象参数 `user`(类型 `SysUser`,必填:是)绑定,以下字段以同名 `query` 参数传入: + +| 字段(参数名) | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| createBy | string | 否 | | +| createTime | string | 否 | | +| updateBy | string | 否 | | +| updateTime | string | 否 | | +| remark | string | 否 | | +| params | object | 否 | | +| userId | integer(int64) | 否 | | +| deptId | integer(int64) | 否 | | +| userName | string | 否 | | +| nickName | string | 否 | | +| email | string | 否 | | +| phonenumber | string | 否 | | +| sex | string | 否 | | +| avatar | string | 否 | | +| password | string | 否 | | +| status | string | 否 | | +| delFlag | string | 否 | | +| loginIp | string | 否 | | +| loginDate | string | 否 | | +| pwdUpdateDate | string | 否 | | +| dept | SysDept | 否 | | +| roles | array | 否 | | +| roleIds | array | 否 | | +| postIds | array | 否 | | +| roleId | integer(int64) | 否 | | +| admin | boolean | 否 | | + +> 分页参数 `pageNum`、`pageSize` 由分页拦截器自动读取(不体现在方法签名中),示例:`?pageNum=1&pageSize=10`。 + + +### 响应(HTTP 200) + +响应结构(`TableDataInfo`)字段(按本规范建模): + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| total | integer(int64) | 否 | | +| rows | array | 否 | | +| code | integer(int32) | 否 | | +| msg | string | 否 | | + +> 实际返回结构见上文「通用返回结构说明」(`code/msg/data` 等字段未在规范中建模)。 + +> 业务数据说明:`rows` 为 `SysUser` 对象数组,`total` 为总记录数。 + +--- + +## 2. 部门树 + +- **路径**:`GET /system/user/deptTree` +- **分组(Tag)**:sys-user-controller + +### 请求参数 + +查询通过对象参数 `dept`(类型 `SysDept`,必填:是)绑定,以下字段以同名 `query` 参数传入: + +| 字段(参数名) | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| createBy | string | 否 | | +| createTime | string | 否 | | +| updateBy | string | 否 | | +| updateTime | string | 否 | | +| remark | string | 否 | | +| params | object | 否 | | +| deptId | integer(int64) | 否 | | +| parentId | integer(int64) | 否 | | +| ancestors | string | 否 | | +| deptLevel | integer(int32) | 否 | | +| deptName | string | 否 | | +| orderNum | integer(int32) | 否 | | +| leader | string | 否 | | +| phone | string | 否 | | +| email | string | 否 | | +| deptType | string | 否 | | +| status | string | 否 | | +| delFlag | string | 否 | | +| parentName | string | 否 | | +| children | array | 否 | | + + +### 响应(HTTP 200) + +响应结构(`AjaxResult`)字段(按本规范建模): + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| error | boolean | 否 | | +| success | boolean | 否 | | +| warn | boolean | 否 | | +| empty | boolean | 否 | | + +> 实际返回结构见上文「通用返回结构说明」(`code/msg/data` 等字段未在规范中建模)。 + +--- + +## 3. 用户信息-任意人员 + +- **路径**:`GET /system/user/{userId}` +- **分组(Tag)**:sys-user-controller + +### 请求参数 + +- `userId`(path,类型 `integer(int64)`,必填:是): + +### 响应(HTTP 200) + +响应结构(`AjaxResult`)字段(按本规范建模): + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| error | boolean | 否 | | +| success | boolean | 否 | | +| warn | boolean | 否 | | +| empty | boolean | 否 | | + +> 实际返回结构见上文「通用返回结构说明」(`code/msg/data` 等字段未在规范中建模)。 + +> 业务数据说明:`data` 返回指定 userId 对应用户的 `SysUser` 信息对象。 + +> 备注:路径变量在本文档中命名为 `userId`,即需求中的 `{id}`(按用户 ID 查询任意人员信息)。 + +--- + +## 4. 用户信息-个人信息 + +- **路径**:`GET /system/user/profile` +- **分组(Tag)**:sys-profile-controller + +### 响应(HTTP 200) + +响应结构(`AjaxResult`)字段(按本规范建模): + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| error | boolean | 否 | | +| success | boolean | 否 | | +| warn | boolean | 否 | | +| empty | boolean | 否 | | + +> 实际返回结构见上文「通用返回结构说明」(`code/msg/data` 等字段未在规范中建模)。 + +> 业务数据说明:`data` 返回当前登录用户的 `SysUser` 信息对象。 + +--- + +## 5. 修改密码 + +- **路径**:`PUT /system/user/profile/updatePwd` +- **分组(Tag)**:sys-profile-controller + +### 请求体(application/json) + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| oldPassword | string | 是 | 原密码 | +| newPassword | string | 是 | 新密码 | + +> 注:OpenAPI 中该请求体被建模为自由对象(`Map`),上表为实际使用的字段。 + +### 响应(HTTP 200) + +响应结构(`AjaxResult`)字段(按本规范建模): + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| error | boolean | 否 | | +| success | boolean | 否 | | +| warn | boolean | 否 | | +| empty | boolean | 否 | | + +> 实际返回结构见上文「通用返回结构说明」(`code/msg/data` 等字段未在规范中建模)。 + +--- + +## 6. 验证码 + +- **路径**:`GET /captchaImage` +- **分组(Tag)**:captcha-controller + +### 响应(HTTP 200) + +响应结构(`AjaxResult`)字段(按本规范建模): + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| error | boolean | 否 | | +| success | boolean | 否 | | +| warn | boolean | 否 | | +| empty | boolean | 否 | | + +> 实际返回结构见上文「通用返回结构说明」(`code/msg/data` 等字段未在规范中建模)。 + +> 业务数据说明:成功时 `data` 返回 `{"uuid":"...","img":"data:image/png;base64,..."}`,`uuid` 与 `img` 用于登录时校验验证码。 + +--- + +## 7. 用户登陆 + +- **路径**:`POST /login` +- **分组(Tag)**:sys-login-controller + +### 请求体(application/json) + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| username | string | 是 | 用户名 | +| password | string | 是 | 密码 | +| code | string | 是 | 验证码(由 /captchaImage 返回) | +| uuid | string | 是 | 验证码唯一标识(由 /captchaImage 返回) | + +> 注:OpenAPI 中该请求体被建模为自由对象(`Map`),上表为实际使用的字段。 + +### 响应(HTTP 200) + +响应结构(`AjaxResult`)字段(按本规范建模): + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| error | boolean | 否 | | +| success | boolean | 否 | | +| warn | boolean | 否 | | +| empty | boolean | 否 | | + +> 实际返回结构见上文「通用返回结构说明」(`code/msg/data` 等字段未在规范中建模)。 + +> 业务数据说明:登录成功时 `data` 返回 `"token"` 字符串,后续请求须在请求头 `Authorization: Bearer ` 中携带。 + +--- diff --git a/docs/前期讨论/002-服务风险防控-工作流程-V2.pos b/docs/前期讨论/002-服务风险防控-工作流程-V2.pos new file mode 100644 index 0000000..5452960 --- /dev/null +++ b/docs/前期讨论/002-服务风险防控-工作流程-V2.pos @@ -0,0 +1 @@ +{"diagram":{"elements":{"editorVersion":"V1","comments":[],"plugins":{},"elements":{"pYpXuiJgIG904868":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"
STEP:06
调查结果审核"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"iahcssNhcX401804","category":"default","type":"number","value":""},{"name":"名称","id":"XhLzznwZzX919787","category":"default","type":"string","value":""},{"name":"所有者","id":"UfNTBGykkH487556","category":"default","type":"string","value":""},{"name":"连接","id":"ZYuiJjbVxg208759","category":"default","type":"link","value":""},{"name":"便笺","id":"KXixlFQweI012768","category":"default","type":"string","value":""},{"name":"成本","id":"NxMopegdvR006136","category":"default","type":"number","value":""},{"name":"时间","id":"EpPHXgfDuN237446","category":"default","type":"number","value":""},{"name":"部门","id":"sJhNevNXXZ564737","category":"default","type":"string","value":""},{"name":"输入","id":"oNZXhxWJwG364519","category":"default","type":"string","value":""},{"name":"输出","id":"IgefrAPRkx837244","category":"default","type":"string","value":""},{"name":"风险","id":"mBuirTnJWm944896","category":"default","type":"string","value":""},{"name":"备注","id":"uHizhviQlg993558","category":"default","type":"string","value":""}],"props":{"zindex":40,"w":119.49737548828125,"h":40,"x":1316.689468383789,"y":251,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"121,133,236"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"210,214,249","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"pYpXuiJgIG904868","category":"flow","locked":false,"group":""},"CjQwnWCNQM355407":{"textBlock":[],"points":[{"x":88,"y":789.75}],"dataAttributes":[],"props":{"zindex":68},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":88,"y":601.5130310058594,"angle":4.71238898038469,"id":"sUhmpatnEJ757074"},"id":"CjQwnWCNQM355407","text":"","to":{"x":260.99737548828125,"y":789.75,"angle":0,"id":"ECDMDlaeel664481"},"locked":false,"group":""},"MOMzbcoKtR007310":{"textBlock":[],"points":[{"x":742.7486877441406,"y":580.875},{"x":742.7486877441406,"y":580.875}],"dataAttributes":[],"props":{"zindex":58},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":835.5,"y":580.875,"angle":0,"id":"hdqGquYZcK553832"},"id":"MOMzbcoKtR007310","text":"","to":{"x":649.9973754882812,"y":580.875,"angle":3.1415926535897936,"id":"kTwsHFDdZV819309"},"locked":false,"group":""},"NxxbIpWjiQ577652":{"textBlock":[{"pos":"in","location":0.13052071729308296,"text":"主责"}],"points":[{"x":708.3743438720703,"y":271},{"x":708.3743438720703,"y":271}],"dataAttributes":[],"props":{"zindex":75},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":575.2486877441406,"y":271,"angle":3.141592653589793,"id":"dTvUCwbzxT320388"},"textPos":{"t":62,"pos":"in","x":610,"y":271},"id":"NxxbIpWjiQ577652","text":"主责","to":{"x":841.5,"y":271,"angle":0,"id":"JLnZuiPLJR644363"},"locked":false,"group":""},"wqXHIZjZhF101421":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:04
操作人员:3类部门主管
工作内容:填事件调查初查表单
派单条件:只保存,无派单权限
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":37,"w":207.6056099749627,"x":887.2553191489362,"h":65,"y":75.13802337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"wqXHIZjZhF101421","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"HekdwAsgjs116777":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:11
操作人员:服务监督员
工作内容:调查报告编写
派单条件:服务监督员(岗位)
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":59,"w":218.50262451171875,"x":497.7460632324219,"h":65,"y":471.01302337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"HekdwAsgjs116777","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"zvVEcFNkqZ514296":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"STEP:12
成果物输出
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"GFNZWALaLi316232","category":"default","type":"number","value":""},{"name":"名称","id":"mTObpEMigL328580","category":"default","type":"string","value":""},{"name":"所有者","id":"ILyVFdiOyG190038","category":"default","type":"string","value":""},{"name":"连接","id":"pkTOPmECXT146870","category":"default","type":"link","value":""},{"name":"便笺","id":"OLiueTtwaD436265","category":"default","type":"string","value":""},{"name":"成本","id":"kJyBqMDyzv992167","category":"default","type":"number","value":""},{"name":"时间","id":"RuyXWAoyrY207176","category":"default","type":"number","value":""},{"name":"部门","id":"eCzgAMWCvs304305","category":"default","type":"string","value":""},{"name":"输入","id":"ehThkJWFyG858003","category":"default","type":"string","value":""},{"name":"输出","id":"MPkZblZtSA627763","category":"default","type":"string","value":""},{"name":"风险","id":"YvxObbCLtq875405","category":"default","type":"string","value":""},{"name":"备注","id":"aYGQchMhDb155899","category":"default","type":"string","value":""}],"props":{"zindex":60,"w":112,"h":35,"x":285.00262451171875,"y":563.375,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"153,204,255"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"204,229,255","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"zvVEcFNkqZ514296","category":"flow","locked":false,"group":""},"DrSKdtMtMy945726":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"STEP:01
12398工单线下导入
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"cSgyNyKFUO109164","category":"default","type":"number","value":""},{"name":"名称","id":"ERIsyvwDfa443371","category":"default","type":"string","value":""},{"name":"所有者","id":"BvJRqoedZX552299","category":"default","type":"string","value":""},{"name":"连接","id":"kYxYZLZcqf343728","category":"default","type":"link","value":""},{"name":"便笺","id":"ePINPZYmlw396871","category":"default","type":"string","value":""},{"name":"成本","id":"ZSMtrXrFFu889946","category":"default","type":"number","value":""},{"name":"时间","id":"GHaUoheBLH556662","category":"default","type":"number","value":""},{"name":"部门","id":"HerfbKWhtb795586","category":"default","type":"string","value":""},{"name":"输入","id":"RTtpjMPMIj365634","category":"default","type":"string","value":""},{"name":"输出","id":"cGPqnBiQSb502770","category":"default","type":"string","value":""},{"name":"风险","id":"bzdmwpLCuc626301","category":"default","type":"string","value":""},{"name":"备注","id":"KGfLtaOCQZ308254","category":"default","type":"string","value":""}],"props":{"zindex":2,"w":143.75390625,"h":42,"x":260.08203125,"y":250,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"121,133,236"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"210,214,249","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"DrSKdtMtMy945726","category":"flow","locked":false,"group":""},"nDIQVwUmHL533636":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"办结工单统计
操作人员:有菜单权限人员
派单条件:不存在,工单已办结
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":69,"w":224,"x":407.2486877441406,"h":61.50000762939453,"y":728,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"nDIQVwUmHL533636","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"gvxZPYwoLA263974":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:07
操作人员:业务部主任
工作内容:意见审核
派单条件:
不通过:调查结果填报(服务监督员)
通过-主责:客服部门主管(岗位)
通过-协查:服务监督员(岗位)
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":82,"w":323.66927337646484,"x":1698.287234042553,"h":114,"y":56.513023376464844,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"gvxZPYwoLA263974","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"aaYPuwCxaw534695":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:09
操作人员:客服部班组组长/成员
工作内容:填回访表单
派单条件:服务监督员(岗位)
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":52,"w":218.50262451171875,"x":1029.9973754882812,"h":65,"y":466.01302337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"aaYPuwCxaw534695","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"hzhNvXIsAZ599834":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:01
操作人员:服务监督主管
权限限制:有菜单权限即可导入
工作内容:
1. 识别:主责或协查
2. 派单:主责时同时有首联和事件调查,协查时只有事件调查
02首联派单条件:有客服权限部门的主管(岗位),单选
04事件调查派单条件:客服部门、智能团队和总值团队主管(岗位),可多选
05调查结果派单条件:服务监督员(岗位)
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"top"},"dataAttributes":[],"props":{"zindex":26,"w":459.7460632324219,"x":38,"h":146.00002337646484,"y":69.38802337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"rounding":1,"lineColor":"153,204,255"},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{"hachureGap":4,"fillWeight":1,"preserveVertices":true,"color":"204,229,255","roughness":1,"bowing":3,"drawType":"sketch","disableMultiStroke":false,"type":"zigzag"},"theme":{},"id":"hzhNvXIsAZ599834","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"uJUIuvARmt536146":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:03
操作人员:客服部班组组长/成员
工作内容:填首联表单
派单条件:只保存,无派单权限
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":36,"w":218.50262451171875,"x":664.3803542116856,"h":70.75,"y":72.26302337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"uJUIuvARmt536146","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"hdqGquYZcK553832":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"STEP:10
回访结果填报
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"vDLfWbkByk646786","category":"default","type":"number","value":""},{"name":"名称","id":"kYywsPazum597026","category":"default","type":"string","value":""},{"name":"所有者","id":"CRSTffrYEn262713","category":"default","type":"string","value":""},{"name":"连接","id":"SnaYVkkmpL566573","category":"default","type":"link","value":""},{"name":"便笺","id":"EYQEQyBpnX609516","category":"default","type":"string","value":""},{"name":"成本","id":"kLPPAEYwpO319965","category":"default","type":"number","value":""},{"name":"时间","id":"GKuqcQqpZZ049741","category":"default","type":"number","value":""},{"name":"部门","id":"pSgdlRTFKC643099","category":"default","type":"string","value":""},{"name":"输入","id":"wPzvqyDxGx420058","category":"default","type":"string","value":""},{"name":"输出","id":"UMVoGxGeRp535308","category":"default","type":"string","value":""},{"name":"风险","id":"fSEPqtSTXY522701","category":"default","type":"string","value":""},{"name":"备注","id":"jcVibySpLg900170","category":"default","type":"string","value":""}],"props":{"zindex":55,"w":112,"h":35,"x":835.5,"y":563.375,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"153,204,255"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"204,229,255","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"hdqGquYZcK553832","category":"flow","locked":false,"group":""},"LWqGbJuqaO558212":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"
STEP:05
调查结果填报"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"UiQmELulGS756021","category":"default","type":"number","value":""},{"name":"名称","id":"HUPInYFkwn223297","category":"default","type":"string","value":""},{"name":"所有者","id":"KtYNoVDnsm177640","category":"default","type":"string","value":""},{"name":"连接","id":"BEDwqpORFq774905","category":"default","type":"link","value":""},{"name":"便笺","id":"lEfhXGQtJB364010","category":"default","type":"string","value":""},{"name":"成本","id":"PAaYtWtrWG152469","category":"default","type":"number","value":""},{"name":"时间","id":"DzHidgcYtn646632","category":"default","type":"number","value":""},{"name":"部门","id":"uSesKHClvH469904","category":"default","type":"string","value":""},{"name":"输入","id":"QAhSVEZHLp366553","category":"default","type":"string","value":""},{"name":"输出","id":"BsJtwFALAS480364","category":"default","type":"string","value":""},{"name":"风险","id":"echLXDywmQ969765","category":"default","type":"string","value":""},{"name":"备注","id":"GFrzghfexr944746","category":"default","type":"string","value":""}],"props":{"zindex":32,"w":119.49737548828125,"h":40,"x":1075.2513122558594,"y":251,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"153,204,255"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"204,229,255","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"LWqGbJuqaO558212","category":"flow","locked":false,"group":""},"NKnCpVjKHZ565594":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"选择部门下主管的手机号(岗位)"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{},"dataAttributes":[],"props":{"zindex":77,"w":209,"x":567.3424377441406,"h":40,"y":368.125,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"NKnCpVjKHZ565594","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"vpxMMiWRDN438379":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:02
操作人员:客服部主管
工作内容:派单给部门班组组长/成员
派单条件:本部门下班组人员
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":35,"w":224,"x":411.89624153299536,"h":65,"y":75.13802337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"vpxMMiWRDN438379","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"tXHDbjfcpc414760":{"textBlock":[],"points":[{"x":88,"y":716}],"dataAttributes":[],"props":{"zindex":67},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":88,"y":601.5130310058594,"angle":4.71238898038469,"id":"sUhmpatnEJ757074"},"id":"tXHDbjfcpc414760","text":"","to":{"x":260.99737548828125,"y":716,"angle":0,"id":"xAtGIvWlAG260736"},"locked":false,"group":""},"ECDMDlaeel664481":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"统计分析与展示"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"lrbgfDiquh229922","category":"default","type":"number","value":""},{"name":"名称","id":"OquYQPcGui586697","category":"default","type":"string","value":""},{"name":"所有者","id":"gPywPjRhtz171877","category":"default","type":"string","value":""},{"name":"连接","id":"moKtSQPKVO895647","category":"default","type":"link","value":""},{"name":"便笺","id":"oOIizPuTbw782495","category":"default","type":"string","value":""},{"name":"成本","id":"hlHJEaipHJ313226","category":"default","type":"number","value":""},{"name":"时间","id":"UUcSmeUJNG600861","category":"default","type":"number","value":""},{"name":"部门","id":"DXydmuMxOz074507","category":"default","type":"string","value":""},{"name":"输入","id":"QMXrXiJrkh051010","category":"default","type":"string","value":""},{"name":"输出","id":"qKMXFXsimn633308","category":"default","type":"string","value":""},{"name":"风险","id":"loFKBUuCIK800214","category":"default","type":"string","value":""},{"name":"备注","id":"PwyxTGEAcQ958255","category":"default","type":"string","value":""}],"props":{"zindex":66,"w":125.0052490234375,"h":40,"x":260.99737548828125,"y":769.75,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"97,97,97"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"231,235,237","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"ECDMDlaeel664481","category":"flow","locked":false,"group":""},"zAPfZMrcnr106072":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:10
操作人员:服务监督员
工作内容:填回访结果表单
填报说明:不满意或客户主动诉求时附加上传答复书和送达凭证(强制),再填回访结果;满意时直接填回访结果。
派单条件:服务监督员(岗位)
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":56,"w":232.25131225585938,"x":768.5,"h":114,"y":441.51302337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"zAPfZMrcnr106072","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"PNxbVNdYtd208378":{"textBlock":[{"location":0.48182105064647035,"text":"不通过"}],"points":[{"x":1376.4381561279297,"y":337.6666666666667,"fixed":true},{"x":1140.0026245117188,"y":337.6666666666667,"fixed":true}],"dataAttributes":[],"props":{"zindex":43},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1376.4381561279297,"y":291,"angle":4.71238898038469,"id":"pYpXuiJgIG904868"},"textPos":{"t":30,"x":1368.6256561279297,"y":337.66666666666663},"id":"PNxbVNdYtd208378","text":"不通过","to":{"x":1140.0026245117188,"y":291,"angle":4.71238898038469,"id":"LWqGbJuqaO558212"},"locked":false,"group":""},"SGfrYURbVb642028":{"textBlock":[{"pos":"in","location":0.25824760017158155,"text":"协查"}],"points":[{"x":519.2486877441406,"y":313.96875,"fixed":true},{"x":891.5,"y":313.96875,"fixed":true}],"dataAttributes":[],"props":{"zindex":73},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":519.2486877441406,"y":296.5,"angle":4.71238898038469,"id":"dTvUCwbzxT320388"},"textPos":{"t":25,"pos":"in","x":612.3115158081055,"y":313.96875},"id":"SGfrYURbVb642028","text":"协查","to":{"x":891.5,"y":291,"angle":4.71238898038469,"id":"JLnZuiPLJR644363"},"locked":false,"group":""},"XZUalKAKUA328328":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"12398转办系统"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"size":15,"bold":true},"title":"开始/结束","dataAttributes":[{"name":"序号","id":"CxRmIOGUSG586394","category":"default","type":"number","value":""},{"name":"名称","id":"NujQziAxii934693","category":"default","type":"string","value":""},{"name":"所有者","id":"lrCOAZkrnm852693","category":"default","type":"string","value":""},{"name":"连接","id":"ZXOewjLdfr971701","category":"default","type":"link","value":""},{"name":"便笺","id":"cGcqcyOxAW729634","category":"default","type":"string","value":""},{"name":"成本","id":"bRqbVLddaF540444","category":"default","type":"number","value":""},{"name":"时间","id":"vrCYLQvVfb874331","category":"default","type":"number","value":""},{"name":"部门","id":"mQNfSjlbfp250884","category":"default","type":"string","value":""},{"name":"输入","id":"stzyrFctlW482962","category":"default","type":"string","value":""},{"name":"输出","id":"aurjRlyyHV584809","category":"default","type":"string","value":""},{"name":"风险","id":"baOSAZlONp994561","category":"default","type":"string","value":""},{"name":"备注","id":"grRTZcBfnm214833","category":"default","type":"string","value":""}],"props":{"zindex":1,"w":100,"h":50,"x":38,"y":246,"angle":0},"path":[{"actions":[{"x":"Math.min(w,h)/3","action":"move","y":"0"},{"x":"w-Math.min(w,h)/3","action":"line","y":"0"},{"y1":"0","x":"w-Math.min(w,h)/3","action":"curve","y2":"h","y":"h","x1":"w+Math.min(w,h)/3/3","x2":"w+Math.min(w,h)/3/3"},{"x":"Math.min(w,h)/3","action":"line","y":"h"},{"y1":"h","x":"Math.min(w,h)/3","action":"curve","y2":"0","y":"0","x1":"-Math.min(w,h)/3/3","x2":"-Math.min(w,h)/3/3"},{"action":"close"}]}],"lineStyle":{"lineColor":"255,230,204","lineWidth":3},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"terminator","fillStyle":{"color":"255,255,204","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"XZUalKAKUA328328","category":"flow","locked":false,"group":""},"FYIRUsZJnI938358":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"
STEP:07
业务部审核"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"kFaGiOnqrE075428","category":"default","type":"number","value":""},{"name":"名称","id":"wkQckIwDLy900365","category":"default","type":"string","value":""},{"name":"所有者","id":"AogbxXkKQf167529","category":"default","type":"string","value":""},{"name":"连接","id":"nWAfThpMlb843977","category":"default","type":"link","value":""},{"name":"便笺","id":"eszFXricDH248002","category":"default","type":"string","value":""},{"name":"成本","id":"WdKfpidnoX745455","category":"default","type":"number","value":""},{"name":"时间","id":"NouUcHWQwB482730","category":"default","type":"number","value":""},{"name":"部门","id":"hFLWejuWgo672898","category":"default","type":"string","value":""},{"name":"输入","id":"cDENfDcbZp143438","category":"default","type":"string","value":""},{"name":"输出","id":"YupmGFNzzI763630","category":"default","type":"string","value":""},{"name":"风险","id":"hNAYVgrbgk380208","category":"default","type":"string","value":""},{"name":"备注","id":"xtWHROVnmj027528","category":"default","type":"string","value":""}],"props":{"zindex":81,"w":118.4368782043457,"h":40,"x":1544.8463745117188,"y":251,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"233,214,111"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"246,237,193","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"FYIRUsZJnI938358","category":"flow","locked":false,"group":""},"OUnBLzGnYj584072":{"textBlock":[],"fontStyle":{"color":"255,0,0"},"dataAttributes":[],"points":[{"x":1008.3756561279297,"y":271},{"x":1008.3756561279297,"y":271}],"props":{"zindex":85,"x":40,"y":40},"linkerType":"broken","lineStyle":{"lineStyle":"dashed","lineColor":"92,92,92","lineWidth":2},"name":"linker","from":{"x":941.5,"angle":3.1415926535897936,"y":271,"id":"JLnZuiPLJR644363"},"to":{"x":1075.2513122558594,"angle":0,"y":271,"id":"LWqGbJuqaO558212"},"id":"OUnBLzGnYj584072","text":"","locked":false,"group":""},"yTAbWViyed702525":{"textBlock":[],"points":[{"x":1263.6256561279297,"y":580.875},{"x":1263.6256561279297,"y":580.875}],"dataAttributes":[],"props":{"zindex":50},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1336.2513122558594,"y":580.875,"angle":0,"id":"FdEYHdiVsq676549"},"id":"yTAbWViyed702525","text":"","to":{"x":1191,"y":580.875,"angle":3.1415926535897936,"id":"vdHGMpCEva617874"},"locked":false,"group":""},"TjGTGWgETZ413674":{"textBlock":[],"points":[{"x":1698.9763239440917,"y":271},{"x":1698.9763239440917,"y":271}],"dataAttributes":[],"props":{"zindex":83},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1663.2833745117186,"y":271,"angle":3.1415926535897936,"id":"FYIRUsZJnI938358"},"id":"TjGTGWgETZ413674","text":"","to":{"x":1734.6692733764648,"y":271,"angle":0,"id":"FAxXzTMgwM128204"},"locked":false,"group":""},"FdEYHdiVsq676549":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"STEP:08
回访客服派班组
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"uyxpFIIyNj676364","category":"default","type":"number","value":""},{"name":"名称","id":"NCdNdjYdIt242791","category":"default","type":"string","value":""},{"name":"所有者","id":"BezrxTqZoq887486","category":"default","type":"string","value":""},{"name":"连接","id":"LWhjDjcKRs430314","category":"default","type":"link","value":""},{"name":"便笺","id":"zufReIBtuT373122","category":"default","type":"string","value":""},{"name":"成本","id":"uNOGEYAUGi038959","category":"default","type":"number","value":""},{"name":"时间","id":"ENjwRgeJxv662977","category":"default","type":"number","value":""},{"name":"部门","id":"zAMOLzbaqo721268","category":"default","type":"string","value":""},{"name":"输入","id":"dRIRcaDObn669427","category":"default","type":"string","value":""},{"name":"输出","id":"RfueNnAowb444574","category":"default","type":"string","value":""},{"name":"风险","id":"gDGAdCoSGq114207","category":"default","type":"string","value":""},{"name":"备注","id":"SZnEDpjrFN878173","category":"default","type":"string","value":""}],"props":{"zindex":47,"w":112,"h":35,"x":1336.2513122558594,"y":563.375,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"241,149,148"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"250,220,219","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"FdEYHdiVsq676549","category":"flow","locked":false,"group":""},"FqxWBjKJwx687362":{"textBlock":[],"points":[{"x":211.0087803414212,"y":580.875},{"x":211.0087803414212,"y":580.875}],"dataAttributes":[],"props":{"zindex":64},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":285.00262451171875,"y":580.875,"angle":0,"id":"zvVEcFNkqZ514296"},"id":"FqxWBjKJwx687362","text":"","to":{"x":137.01493617112362,"y":580.875,"angle":3.2986722862692828,"id":"sUhmpatnEJ757074"},"locked":false,"group":""},"TkHrYImdSR284060":{"textBlock":[{"pos":"in","location":0.10049389759417036,"text":"协查"}],"points":[{"x":1876.96875,"y":271,"fixed":true},{"x":1876.96875,"y":637.5130310058594,"fixed":true},{"x":587.8359375,"y":637.5130310058594}],"dataAttributes":[],"props":{"zindex":48},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1846.6692733764648,"y":271,"angle":3.141592653589793,"id":"FAxXzTMgwM128204"},"textPos":{"t":28,"pos":"in","x":1876.96875,"y":417.60521240234374},"id":"TkHrYImdSR284060","text":"协查","to":{"x":587.8359375,"y":597.5130310058594,"angle":4.71238898038469,"id":"kTwsHFDdZV819309"},"locked":false,"group":""},"DCnkMCZPxW921049":{"textBlock":[],"points":[{"x":467.5,"y":580.875},{"x":467.5,"y":580.875}],"dataAttributes":[],"props":{"zindex":62},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":537.9973754882812,"y":580.875,"angle":0,"id":"kTwsHFDdZV819309"},"id":"DCnkMCZPxW921049","text":"","to":{"x":397.00262451171875,"y":580.875,"angle":3.1415926535897936,"id":"zvVEcFNkqZ514296"},"locked":false,"group":""},"NSnVnvszSH612824":{"textBlock":[],"points":[{"x":331.95903125,"y":388.125}],"dataAttributes":[],"props":{"zindex":79},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":331.95903125,"y":292,"angle":4.71238898038469,"id":"DrSKdtMtMy945726"},"id":"NSnVnvszSH612824","text":"","to":{"x":453.00262451171875,"y":388.125,"angle":0,"id":"afRrnYYiEz515753"},"locked":false,"group":""},"xAtGIvWlAG260736":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"绩效考核与整改"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"JvxOrKjgJL103740","category":"default","type":"number","value":""},{"name":"名称","id":"ZWVzfFifJL106511","category":"default","type":"string","value":""},{"name":"所有者","id":"OzXFTfyffd157101","category":"default","type":"string","value":""},{"name":"连接","id":"osBoKjxRDZ043182","category":"default","type":"link","value":""},{"name":"便笺","id":"yyvjzGaAYn711447","category":"default","type":"string","value":""},{"name":"成本","id":"FdwCQHwqrI319001","category":"default","type":"number","value":""},{"name":"时间","id":"WCWuReDkfX803402","category":"default","type":"number","value":""},{"name":"部门","id":"CtuSBFvZHJ425295","category":"default","type":"string","value":""},{"name":"输入","id":"rGuNUuNvYI608722","category":"default","type":"string","value":""},{"name":"输出","id":"FErjZXFozL690582","category":"default","type":"string","value":""},{"name":"风险","id":"KqcpkAIUEB607129","category":"default","type":"string","value":""},{"name":"备注","id":"lwidtlGidW355202","category":"default","type":"string","value":""}],"props":{"zindex":65,"w":125.0052490234375,"h":40,"x":260.99737548828125,"y":696,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"97,97,97"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"231,235,237","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"xAtGIvWlAG260736","category":"flow","locked":false,"group":""},"dAUlWwIrVZ548320":{"textBlock":[],"points":[{"x":1490.516421447754,"y":271},{"x":1490.516421447754,"y":271}],"dataAttributes":[],"props":{"zindex":45},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1436.1864683837891,"y":271,"angle":3.1415926535897936,"id":"pYpXuiJgIG904868"},"id":"dAUlWwIrVZ548320","text":"","to":{"x":1544.8463745117188,"y":271,"angle":0,"id":"FYIRUsZJnI938358"},"locked":false,"group":""},"dTvUCwbzxT320388":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"主责或协查"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"判定","dataAttributes":[{"name":"序号","id":"qdKPwMOjjm309325","category":"default","type":"number","value":""},{"name":"名称","id":"dorCMYSDVX905239","category":"default","type":"string","value":""},{"name":"所有者","id":"NZTDszdVps068790","category":"default","type":"string","value":""},{"name":"连接","id":"lfdnMyKpSS003746","category":"default","type":"link","value":""},{"name":"便笺","id":"MIcpIOxHGS202475","category":"default","type":"string","value":""},{"name":"成本","id":"EBwbhVYWuh944887","category":"default","type":"number","value":""},{"name":"时间","id":"jraSdJHqTC652634","category":"default","type":"number","value":""},{"name":"部门","id":"hlKQAhuTBc508808","category":"default","type":"string","value":""},{"name":"输入","id":"UtSgASfPyy689322","category":"default","type":"string","value":""},{"name":"输出","id":"WBQkxCmlsl206950","category":"default","type":"string","value":""},{"name":"风险","id":"xrpOVSExDq294687","category":"default","type":"string","value":""},{"name":"备注","id":"KYRjJKZphS383663","category":"default","type":"string","value":""}],"props":{"zindex":71,"w":112,"h":51,"x":463.2486877441406,"y":245.5,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"h/2"},{"x":"w/2","action":"line","y":"0"},{"x":"w","action":"line","y":"h/2"},{"x":"w/2","action":"line","y":"h"},{"x":"0","action":"line","y":"h/2"},{"action":"close"}]}],"lineStyle":{"lineColor":"0,0,0"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"decision","fillStyle":{"color":"231,235,237","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"dTvUCwbzxT320388","category":"flow","locked":false,"group":""},"FAxXzTMgwM128204":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"主责或协查"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"判定","dataAttributes":[{"name":"序号","id":"dAyWtuXSGV772155","category":"default","type":"number","value":""},{"name":"名称","id":"LgblsPvjAu737125","category":"default","type":"string","value":""},{"name":"所有者","id":"yQYqMOFOQj505934","category":"default","type":"string","value":""},{"name":"连接","id":"yXnPhquDdm591725","category":"default","type":"link","value":""},{"name":"便笺","id":"dYzMyHdhvQ195128","category":"default","type":"string","value":""},{"name":"成本","id":"NlUhcgPVYN511585","category":"default","type":"number","value":""},{"name":"时间","id":"WURSgjvovU920713","category":"default","type":"number","value":""},{"name":"部门","id":"siJLyBBGJZ436020","category":"default","type":"string","value":""},{"name":"输入","id":"IcfaiLvGlt918047","category":"default","type":"string","value":""},{"name":"输出","id":"iffEtJlPHs640503","category":"default","type":"string","value":""},{"name":"风险","id":"vjifRCISrr870991","category":"default","type":"string","value":""},{"name":"备注","id":"iwrmkiCPlm843820","category":"default","type":"string","value":""}],"props":{"zindex":44,"w":112,"h":51,"x":1734.6692733764648,"y":245.5,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"h/2"},{"x":"w/2","action":"line","y":"0"},{"x":"w","action":"line","y":"h/2"},{"x":"w/2","action":"line","y":"h"},{"x":"0","action":"line","y":"h/2"},{"action":"close"}]}],"lineStyle":{"lineColor":"0,0,0"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"decision","fillStyle":{"color":"231,235,237","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"FAxXzTMgwM128204","category":"flow","locked":false,"group":""},"gLqFgvjKaG269463":{"textBlock":[],"points":[{"x":1255.7188903198244,"y":271},{"x":1255.7188903198244,"y":271}],"dataAttributes":[],"props":{"zindex":42},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1194.7483122558594,"y":271,"angle":3.1415926535897936,"id":"LWqGbJuqaO558212"},"id":"gLqFgvjKaG269463","text":"","to":{"x":1316.689468383789,"y":271,"angle":0,"id":"pYpXuiJgIG904868"},"locked":false,"group":""},"llSIjUxtwI041270":{"textBlock":[],"points":[{"x":356.00262451171875,"y":339.74219004313153,"fixed":true},{"x":1125.1071205622034,"y":339.74219004313153,"fixed":true}],"dataAttributes":[],"props":{"zindex":80},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":356.00262451171875,"angle":4.71238898038469,"y":291.51302337646484,"id":"DrSKdtMtMy945726"},"id":"llSIjUxtwI041270","text":"","to":{"x":1125.1071205622034,"y":291.51302337646484,"angle":4.71238898038469,"id":"LWqGbJuqaO558212"},"locked":false,"group":""},"sUhmpatnEJ757074":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"工单办结"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"size":15,"bold":true},"title":"开始/结束","dataAttributes":[{"name":"序号","id":"XMaEqHLsWs793489","category":"default","type":"number","value":""},{"name":"名称","id":"QWaXFDjnDX949808","category":"default","type":"string","value":""},{"name":"所有者","id":"DqnJPnyTKL968347","category":"default","type":"string","value":""},{"name":"连接","id":"tBBiaUMOUO498002","category":"default","type":"link","value":""},{"name":"便笺","id":"AQvdhbNvon828680","category":"default","type":"string","value":""},{"name":"成本","id":"UliLkZUCvq643122","category":"default","type":"number","value":""},{"name":"时间","id":"ARIjKfvnSI409368","category":"default","type":"number","value":""},{"name":"部门","id":"VrvvNAjReC370957","category":"default","type":"string","value":""},{"name":"输入","id":"fwadArUnAY762085","category":"default","type":"string","value":""},{"name":"输出","id":"TwZVoeQJeK786638","category":"default","type":"string","value":""},{"name":"风险","id":"IASqZlqCnt332467","category":"default","type":"string","value":""},{"name":"备注","id":"aaexYPWnSp509312","category":"default","type":"string","value":""}],"props":{"zindex":63,"w":100,"h":50,"x":38,"angle":0,"y":551.5130310058594},"path":[{"actions":[{"x":"Math.min(w,h)/3","action":"move","y":"0"},{"x":"w-Math.min(w,h)/3","action":"line","y":"0"},{"y1":"0","x":"w-Math.min(w,h)/3","action":"curve","y2":"h","y":"h","x1":"w+Math.min(w,h)/3/3","x2":"w+Math.min(w,h)/3/3"},{"x":"Math.min(w,h)/3","action":"line","y":"h"},{"y1":"h","x":"Math.min(w,h)/3","action":"curve","y2":"0","y":"0","x1":"-Math.min(w,h)/3/3","x2":"-Math.min(w,h)/3/3"},{"action":"close"}]}],"lineStyle":{"lineColor":"255,230,204","lineWidth":3},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"terminator","fillStyle":{"color":"255,255,204","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"sUhmpatnEJ757074","category":"flow","locked":false,"group":""},"SfvxaxIynE129130":{"textBlock":[],"points":[{"x":1013.25,"y":580.875},{"x":1013.25,"y":580.875}],"dataAttributes":[],"props":{"zindex":54},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1079,"y":580.875,"angle":0,"id":"vdHGMpCEva617874"},"id":"SfvxaxIynE129130","text":"","to":{"x":947.5,"y":580.875,"angle":3.1415926535897936,"id":"hdqGquYZcK553832"},"locked":false,"group":""},"mSqUfOQUPW807628":{"textBlock":[{"pos":"in","location":0.6815216115363368,"text":"不通过"}],"points":[{"x":1604.0643745117188,"y":357.71875,"fixed":true},{"x":1140.6270027160645,"y":357.71875,"fixed":true}],"dataAttributes":[],"props":{"zindex":84},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1604.0643745117188,"y":291,"angle":4.71238898038469,"id":"FYIRUsZJnI938358"},"textPos":{"t":35,"pos":"in","x":1264,"y":357.71875},"id":"mSqUfOQUPW807628","text":"不通过","to":{"x":1140.6270027160645,"y":291,"angle":4.71238898038469,"id":"LWqGbJuqaO558212"},"locked":false,"group":""},"JLnZuiPLJR644363":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"
STEP:04
事件调查"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"zMtsZxTRAe101739","category":"default","type":"number","value":""},{"name":"名称","id":"spWHaXHaxJ852463","category":"default","type":"string","value":""},{"name":"所有者","id":"sSEPUYxOXg529302","category":"default","type":"string","value":""},{"name":"连接","id":"GJqOzFjJJe918668","category":"default","type":"link","value":""},{"name":"便笺","id":"okaoekFdcn069558","category":"default","type":"string","value":""},{"name":"成本","id":"DyiGpAbsCD188454","category":"default","type":"number","value":""},{"name":"时间","id":"qlSdLPTsdX955915","category":"default","type":"number","value":""},{"name":"部门","id":"zRGqRzwFpQ851771","category":"default","type":"string","value":""},{"name":"输入","id":"KNAglDPTad176294","category":"default","type":"string","value":""},{"name":"输出","id":"OLiPNKdDls741804","category":"default","type":"string","value":""},{"name":"风险","id":"PtApAnxvgv843712","category":"default","type":"string","value":""},{"name":"备注","id":"lulmkMkbZC819278","category":"default","type":"string","value":""}],"props":{"zindex":31,"w":100,"h":40,"x":841.5,"y":251,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"148,224,225"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"219,245,245","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"JLnZuiPLJR644363","category":"flow","locked":false,"group":""},"kTwsHFDdZV819309":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"STEP:11
调查报告编写
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"vJqYVQaZzs972970","category":"default","type":"number","value":""},{"name":"名称","id":"bYsPoQzdxG812952","category":"default","type":"string","value":""},{"name":"所有者","id":"ccGSUNEbxv611412","category":"default","type":"string","value":""},{"name":"连接","id":"RRcxNRszya984281","category":"default","type":"link","value":""},{"name":"便笺","id":"SGrBywXlpS618800","category":"default","type":"string","value":""},{"name":"成本","id":"nAefhAKqTf801688","category":"default","type":"number","value":""},{"name":"时间","id":"cMckpHcCXu620375","category":"default","type":"number","value":""},{"name":"部门","id":"ZFZVuFJDsw579529","category":"default","type":"string","value":""},{"name":"输入","id":"SYEoEZwziG068281","category":"default","type":"string","value":""},{"name":"输出","id":"vtRMFWPjiV076976","category":"default","type":"string","value":""},{"name":"风险","id":"RuHSOxlJXk410289","category":"default","type":"string","value":""},{"name":"备注","id":"vlhldPQuZP830835","category":"default","type":"string","value":""}],"props":{"zindex":57,"w":112,"h":35,"x":537.9973754882812,"y":563.375,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"153,204,255"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"204,229,255","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"kTwsHFDdZV819309","category":"flow","locked":false,"group":""},"sisNVeCzvt610908":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:12
操作人员:服务监督员
工作内容:智能辅助撰写,五级溯源等
派单条件:工单办结
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":61,"w":233.57940673828125,"x":229.66928100585938,"h":65.00000762939453,"y":471.0130195617676,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"sisNVeCzvt610908","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"cZjUEVBuFR854998":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"
STEP:02
客服派班组"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"sUtLYniqaZ499920","category":"default","type":"number","value":""},{"name":"名称","id":"LjsWiJzcXC661029","category":"default","type":"string","value":""},{"name":"所有者","id":"LvltbYjxlD663579","category":"default","type":"string","value":""},{"name":"连接","id":"RkWzPqYncU329803","category":"default","type":"link","value":""},{"name":"便笺","id":"miMYWeGqNO391743","category":"default","type":"string","value":""},{"name":"成本","id":"NOMMHGhDMf373931","category":"default","type":"number","value":""},{"name":"时间","id":"JIoBMNTXXc087095","category":"default","type":"number","value":""},{"name":"部门","id":"rCHRfSVqWU786583","category":"default","type":"string","value":""},{"name":"输入","id":"MhASBfRASA712986","category":"default","type":"string","value":""},{"name":"输出","id":"RSBjMhdMPX068914","category":"default","type":"string","value":""},{"name":"风险","id":"zNLXmemoZY608760","category":"default","type":"string","value":""},{"name":"备注","id":"YXWUoVkLGw866820","category":"default","type":"string","value":""}],"props":{"zindex":28,"w":100,"h":40,"x":692.9973754882812,"y":176,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"241,149,148"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"250,220,219","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"cZjUEVBuFR854998","category":"flow","locked":false,"group":""},"EjmvJofwCa886102":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:08
操作人员:客服部主管
工作内容:派单给部门班组组长/成员
派单条件:本部门下班组人员
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":51,"w":224,"x":1287.0026245117188,"h":65,"y":466.01302337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"EjmvJofwCa886102","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"ERZSdsQKMV338747":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:05
操作人员:服务监督员
工作内容:填全面调查表单
派单条件:服务监督主管(岗位)
注意:03/04未填完时可以看到,但只有填过后才能派单(1个首联和多个事件调查要全部填完)
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":39,"w":282.03125,"x":1108.0026245117188,"h":97,"y":69.38802337646484,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"ERZSdsQKMV338747","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"sVFidxBvZU264339":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"STEP:06
操作人员:服务监督主管
工作内容:意见审核
派单条件:业务部主任(岗位)
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"textAlign":"left","vAlign":"middle"},"dataAttributes":[],"props":{"zindex":41,"w":323.66927337646484,"x":1422.7021276595747,"h":65,"y":72.26302337646483,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"sVFidxBvZU264339","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"zVdEluwhja475650":{"textBlock":[],"points":[{"x":434.00003161621095,"y":271},{"x":434.00003161621095,"y":271}],"dataAttributes":[],"props":{"zindex":72},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":404.75137548828127,"y":271,"angle":3.1415926535897936,"id":"DrSKdtMtMy945726"},"id":"zVdEluwhja475650","text":"","to":{"x":463.2486877441406,"y":271,"angle":0,"id":"dTvUCwbzxT320388"},"locked":false,"group":""},"INbEJnpaTx789332":{"textBlock":[],"fontStyle":{"color":"255,0,0"},"dataAttributes":[],"points":[{"x":199.041015625,"y":271},{"x":199.041015625,"y":271}],"props":{"zindex":27,"x":20,"y":20},"linkerType":"broken","lineStyle":{"lineStyle":"dashed","lineColor":"92,92,92","lineWidth":2},"name":"linker","from":{"x":138,"angle":3.141592653589793,"y":271,"id":"XZUalKAKUA328328"},"to":{"x":260.08203125,"angle":0,"y":271,"id":"DrSKdtMtMy945726"},"id":"INbEJnpaTx789332","text":"","locked":false,"group":""},"DCJfAPALAB578388":{"textBlock":[],"fontStyle":{"color":"255,0,0"},"dataAttributes":[],"points":[{"x":891.5,"y":230.75},{"x":891.5,"y":230.75}],"props":{"zindex":86,"x":60,"y":60},"linkerType":"broken","lineStyle":{"lineStyle":"dashed","lineColor":"92,92,92","lineWidth":2},"name":"linker","from":{"x":891.5,"angle":4.71238898038469,"y":216,"id":"YQNeALwjdX841325"},"to":{"x":891.5,"y":245.5},"id":"DCJfAPALAB578388","text":"","locked":false,"group":""},"hpzcFNHYmp935523":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w","x":0,"h":"h","y":0},"text":"工单的服务监督员首次指定后,后续环节不允许变更。"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"title":"","fontStyle":{"bold":true},"dataAttributes":[],"props":{"zindex":70,"w":346.42383461810175,"x":22.550566977643,"h":40,"y":16.513023376464844,"angle":0},"path":[{"lineStyle":{"lineWidth":0},"fillStyle":{"type":"none"},"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{},"children":[],"resizeDir":["tl","tr","br","bl","l","t","r","b"],"name":"standardText","fillStyle":{},"theme":{},"id":"hpzcFNHYmp935523","attribute":{"container":false,"rotatable":true,"visible":true,"fixedLink":false,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"category":"standard","locked":false,"group":""},"dtojiXQPYn533165":{"textBlock":[],"points":[{"x":651,"y":271,"fixed":true},{"x":651,"y":196,"fixed":true}],"dataAttributes":[],"props":{"zindex":74},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":575.2486877441406,"y":271,"angle":3.141592653589793,"id":"dTvUCwbzxT320388"},"textPos":{"t":52,"pos":"in","x":676.1984252929688,"y":196},"id":"dtojiXQPYn533165","text":"","to":{"x":692.9973754882812,"y":196,"angle":0,"id":"cZjUEVBuFR854998"},"locked":false,"group":""},"sOERVEKDyK507497":{"textBlock":[{"pos":"in","location":0.19224848949255155,"text":"主责"}],"points":[{"x":1790.6692733764648,"y":580.875}],"dataAttributes":[],"props":{"zindex":46},"linkerType":"broken","lineStyle":{"lineWidth":1.5},"name":"linker","from":{"x":1790.6692733764648,"y":296.5,"angle":4.71238898038469,"id":"FAxXzTMgwM128204"},"textPos":{"t":8,"pos":"in","x":1790.6692733764648,"y":417},"id":"sOERVEKDyK507497","text":"主责","to":{"x":1448.2513122558594,"y":580.875,"angle":3.1415926535897936,"id":"FdEYHdiVsq676549"},"locked":false,"group":""},"GSzCrXQVBQ868893":{"textBlock":[],"dataAttributes":[],"points":[{"x":817.2486877441406,"y":196},{"x":817.2486877441406,"y":196}],"props":{"zindex":30,"x":20,"y":20},"linkerType":"broken","lineStyle":{"lineColor":"92,92,92","lineWidth":2},"name":"linker","from":{"x":792.9973754882812,"angle":3.1415926535897936,"y":196,"id":"cZjUEVBuFR854998"},"to":{"x":841.5,"angle":0,"y":196,"id":"YQNeALwjdX841325"},"id":"GSzCrXQVBQ868893","text":"","locked":false,"group":""},"afRrnYYiEz515753":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"提级督办"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"viTTNzQPAd986111","category":"default","type":"number","value":""},{"name":"名称","id":"YOgozgTYMa834769","category":"default","type":"string","value":""},{"name":"所有者","id":"LMllQOxgRv674061","category":"default","type":"string","value":""},{"name":"连接","id":"mdinYhQgtW318812","category":"default","type":"link","value":""},{"name":"便笺","id":"VJNnhbiToh226104","category":"default","type":"string","value":""},{"name":"成本","id":"NUOzKEPSiE585469","category":"default","type":"number","value":""},{"name":"时间","id":"MLHFQrEQvc083133","category":"default","type":"number","value":""},{"name":"部门","id":"OGXQpCoOPI704053","category":"default","type":"string","value":""},{"name":"输入","id":"NutSOqzzwF236935","category":"default","type":"string","value":""},{"name":"输出","id":"LPsBmJbJAZ896662","category":"default","type":"string","value":""},{"name":"风险","id":"CNWOIpFSPU950300","category":"default","type":"string","value":""},{"name":"备注","id":"uSdxTYbWBS975574","category":"default","type":"string","value":""}],"props":{"zindex":76,"w":112,"h":35,"x":453.00262451171875,"y":370.625,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"97,97,97"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"231,235,237","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"afRrnYYiEz515753","category":"flow","locked":false,"group":""},"YQNeALwjdX841325":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"
STEP:03
班组首联"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"ujcKBRkdcV024683","category":"default","type":"number","value":""},{"name":"名称","id":"fTOpEFPPLL768440","category":"default","type":"string","value":""},{"name":"所有者","id":"KKiVeBwpEh265332","category":"default","type":"string","value":""},{"name":"连接","id":"AoSIOBPXAf674500","category":"default","type":"link","value":""},{"name":"便笺","id":"dxduNfDVTU666994","category":"default","type":"string","value":""},{"name":"成本","id":"JcRoRXJJiZ578095","category":"default","type":"number","value":""},{"name":"时间","id":"thyoXaLvZJ706027","category":"default","type":"number","value":""},{"name":"部门","id":"WCzPRHLewj855219","category":"default","type":"string","value":""},{"name":"输入","id":"PlECQSphzi653399","category":"default","type":"string","value":""},{"name":"输出","id":"XUrMKgHbuN379749","category":"default","type":"string","value":""},{"name":"风险","id":"ySueDJeuVK904044","category":"default","type":"string","value":""},{"name":"备注","id":"cqwxwlxHGC879158","category":"default","type":"string","value":""}],"props":{"zindex":29,"w":100,"h":40,"x":841.5,"y":176,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"176,227,143"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"229,246,218","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"YQNeALwjdX841325","category":"flow","locked":false,"group":""},"vdHGMpCEva617874":{"parent":"","link":"","shapeStyle":{"alpha":1},"textBlock":[{"position":{"w":"w-20","h":"h","x":10,"y":0},"text":"STEP:09
班组回访
"}],"anchors":[{"x":"w/2","y":"0"},{"x":"w/2","y":"h"},{"x":"0","y":"h/2"},{"x":"w","y":"h/2"}],"fontStyle":{"bold":true},"title":"流程","dataAttributes":[{"name":"序号","id":"rwPFKhrOlq600030","category":"default","type":"number","value":""},{"name":"名称","id":"hSQjlUQkkX294119","category":"default","type":"string","value":""},{"name":"所有者","id":"XGsFqWINhr846766","category":"default","type":"string","value":""},{"name":"连接","id":"cAQKkRviqx726709","category":"default","type":"link","value":""},{"name":"便笺","id":"GZrCDIrLUx240233","category":"default","type":"string","value":""},{"name":"成本","id":"hmkbLSOygV504851","category":"default","type":"number","value":""},{"name":"时间","id":"ZyoiUsLxQf530647","category":"default","type":"number","value":""},{"name":"部门","id":"HieLZLwjWP459449","category":"default","type":"string","value":""},{"name":"输入","id":"QZLVnvOexd108254","category":"default","type":"string","value":""},{"name":"输出","id":"AITfBVWtuq585095","category":"default","type":"string","value":""},{"name":"风险","id":"PpPBVJtYDC480110","category":"default","type":"string","value":""},{"name":"备注","id":"sTxAMymYtO585242","category":"default","type":"string","value":""}],"props":{"zindex":49,"w":112,"h":35,"x":1079,"y":563.375,"angle":0},"path":[{"actions":[{"x":"0","action":"move","y":"0"},{"x":"w","action":"line","y":"0"},{"x":"w","action":"line","y":"h"},{"x":"0","action":"line","y":"h"},{"action":"close"}]}],"lineStyle":{"lineColor":"176,227,143"},"children":[],"resizeDir":["tl","tr","br","bl"],"name":"process","fillStyle":{"color":"229,246,218","drawType":"none","type":"solid"},"attribute":{"container":false,"rotatable":true,"visible":true,"collapsable":false,"collapsed":false,"linkable":true,"markerOffset":5},"id":"vdHGMpCEva617874","category":"flow","locked":false,"group":""}},"postPayment":{},"theme":{"page":{"backgroundColor":"transparent"}},"page":{"padding":20,"backgroundColor":"transparent","orientation":"portrait","gridSize":15,"width":2022,"showGrid":true,"lineJumps":false,"height":1190},"version":306,"aiContainers":{},"insertPageOpts":{}}},"meta":{"exportTime":"2026-08-11 18:14:33","member":"","diagramInfo":{"creator":"","created":"2026-08-10 15:47:45","modified":"2026-08-11 17:58:32","title":"服务风险防控-工作流程-V2","category":"flowbase"},"id":"6a798221b89b8d3e2ac402d9","cloneId":"6a79702628ef053174c806fa","type":"ProcessOn Schema File","version":"5.0"}} \ No newline at end of file diff --git a/docs/前期讨论/003-基于前端原型的业务与技术推断报告.md b/docs/前期讨论/003-基于前端原型的业务与技术推断报告.md new file mode 100644 index 0000000..9456aeb --- /dev/null +++ b/docs/前期讨论/003-基于前端原型的业务与技术推断报告.md @@ -0,0 +1,2646 @@ +# 风险防控管理系统业务与技术需求分析推断报告 + +> 文档版本:V0.5(接口字段契约完善稿) +> 分析日期:2026-08-13 +> 主要依据:`deprecated/build/20260812 prototype` 静态原型 +> 参考输入:`docs/前期讨论/001-后端API文档-用户管理相关.md`、`docs/前期讨论/002-服务风险防控-工作流程-V2.pos` + +## 1. 文档目的与结论 + +本文档将静态原型还原为可评审的业务与技术需求,覆盖页面、角色、业务流程、字段、接口和后端约束。20260812 原型共 41 个画板,其中 1 个名称带“备份”的画板属于页面状态变体;按业务能力合并后,可归纳为 18 类页面、12 个主要流程步骤、7 类业务角色。 + +系统核心业务是将线下取得的 12398 工单导入系统,关联 95598 业务处理信息,由服务监督主管判定主责或协查并派工。派工后服务监督员可以先行填报部分调查信息并保存草稿,但主责工单必须等待首联及全部初查完成、协查工单必须等待全部初查完成后才能提交。两级审核完成后,业务部主任基于派工阶段持久化的责任类型分支:主责进入回访,协查直接进入 11 调查报告编写;报告页面以汇总调查及回访信息并确认下载为主要交互。 + +原型能够证明页面展示、操作入口和大部分表单字段,但不能证明数据库结构、全部枚举值、必填规则、接口路径、异常处理、状态码和数据权限。本报告对这些内容采用以下标记: + +| 标记 | 含义 | +| --- | --- | +| 已确认 | 原型或已有接口文档直接展示,可作为当前需求基线 | +| 参考补充 | 来自前期流程图,仅作需求评审输入,需业务确认 | +| 技术建议 | 为保证系统可实现、可审计而提出的设计建议 | +| 待确认 | 现有材料存在缺口或冲突,不能直接作为开发定论 | + +### 1.1 版本变化 + +V0.5 主要变化: + +- 将第 8 章重排为 8.1~8.12 连续章节,修复接口目录合并后遗留的跳号问题。 +- 所有目录接口统一补充请求字段定义和响应字段定义,明确字段位置、类型、必填条件及业务含义。 +- 保留请求与响应示例作为字段表的补充,并修正登录返回结构及章节交叉引用。 + +V0.4 主要变化: + +- 为接口补充可直接用于联调的请求头、请求字段、响应字段、分页、错误响应和状态变更约定。 +- 明确主责/协查派工、前置任务校验及业务部主任审核分支在接口层的表达方式。 +- 补充联调枚举、版本字段、幂等键、文件引用和异步任务查询规则。 + +V0.3 主要变化: + +- 将 41 张画板编号和名称整体切换到 20260812 原型,删除旧原型中的重复/备份编号判断。 +- 固化服务监督主管判定主责/协查并派工、服务监督员先行填报及分支前置条件。 +- 补入业务部主任审核弹窗和完整审核页,并明确协查审核通过后直接进入 11 调查报告编写。 +- 将报告需求由“在线编辑并提交”调整为“汇总业务信息、确认生成并受权下载”,同时统一回访结果枚举和条件附件规则。 + +## 2. 分析范围与证据 + +### 2.1 分析范围 + +- 登录与当前用户识别; +- 12398 工单文件导入、预览、确认和批次查询; +- 工单查询、待办和工单详情; +- 服务监督主管判定主责/协查并派工、智能派工、回访分派; +- 首联、事件初查、调查结果填报; +- 服务监督主管审核、业务部主任审核; +- 回访结果、答复书及送达凭证; +- 调查报告、成果物输出; +- 派工规则、流程时限和统计分析; +- 角色权限、状态流转、审计、幂等、并发、文件与安全约束。 + +### 2.2 不在本稿中定论的内容 + +- 技术栈、部署架构、中间件和数据库产品; +- 组织机构与岗位编码的最终数据模型; +- 与 12398、95598 外部系统的在线对接方式; +- 智能派工、智能写作和“五级溯源”的算法实现; +- 统计指标口径、绩效考核与整改闭环的详细规则; +- 法定保存期限、附件大小和文件格式白名单的具体数值。 + +### 2.3 证据边界 + +1. 原型的日期、姓名、电话、工单号和数量均为示例数据,不应固化为业务规则。 +2. 画板间没有足够完整的可执行跳转链,流程顺序主要由页面名称、按钮、表单内容及前期流程图交叉还原。 +3. “备份”画板只用于识别方案差异,不应直接形成重复页面或重复接口。 +4. `001-后端API文档-用户管理相关.md` 只确认登录及用户查询相关接口,不代表工单业务接口已经存在。 +5. 20260812 原型替代 20260811 原型作为当前页面和字段基线;旧原型仅用于识别变化,不再作为编号或页面状态依据。 +6. 已确认的主责/协查流程规则优先于画板中的静态展示。若画板仍同时展示不适用于协查的首联或回访字段,应视为设计待同步,而不能据此改变业务分支。 + +## 3. 业务范围与领域对象 + +### 3.1 核心领域对象 + +| 对象 | 说明 | 关键关系 | +| --- | --- | --- | +| 导入批次 ImportBatch | 一次 Excel/CSV 预览和确认导入记录 | 包含多条导入行,成功后产生或更新工单 | +| 12398 工单 WorkOrder | 系统的核心业务聚合 | 关联客户、分类、任务、95598 记录、附件、报告和审计轨迹 | +| 95598 处理记录 ServiceRecord | 与 12398 工单关联的历史业务处理信息 | 一张 12398 工单可关联多条记录、录音和转译文本 | +| 流程实例 ProcessInstance | 记录工单当前环节、状态、时限和流程版本 | 一张工单原则上对应一个有效流程实例 | +| 待办任务 Task | 分派到角色、部门、岗位或人员的可执行工作项 | 支持并行初查、单人首联、审核和回访任务 | +| 派工记录 Dispatch | 服务监督主管的主责/协查判定,以及首联、初查、监督员和提级督办选择 | 按责任类型产生对应待办任务,并向服务监督员开放调查填报 | +| 首联记录 FirstContact | 客服班组首次联系客户的结果 | 属于一张工单及一个首联任务 | +| 初查记录 PreliminaryInvestigation | 被选部门对事件的初步调查结果 | 多部门可并行,一任务一记录 | +| 全面调查 Investigation | 服务监督员可在派工后先行填报并保存草稿,待前置任务完成后提交完整调查结果 | 主责依赖首联及全部初查完成;协查依赖全部初查完成 | +| 审核记录 Review | 服务监督主管、业务部主任的审核结论 | 保留审核层级、结论、意见及驳回去向 | +| 回访记录 Callback | 主责工单的客服回访及服务监督员结果填报 | “不同意或客户主动要求”触发附件要求;协查工单不进入回访 | +| 调查报告 InvestigationReport | 基于调查及回访信息形成的报告版本和输出记录 | 最新原型以“确认下载”为主要动作;是否允许在线编辑或智能生成待确认 | +| 成果物 Deliverable | 办结输出物及其版本 | 与报告、附件和办结动作关联 | +| 规则配置 RuleConfig | 智能派工规则和流程时限配置 | 必须版本化并记录生效范围 | + +### 3.2 业务关系要点 + +- 一个导入批次包含多条导入行;导入行应独立记录成功、失败和失败原因。 +- 一张 12398 工单可关联多条 95598 处理记录。 +- 一张工单可并行产生多个事件初查任务,但首联任务原则上为单选部门下的单个有效任务。 +- 服务监督员首次指定后,后续调查、回访结果、报告和成果物环节原则上保持同一责任人(参考补充,待确认);协查工单不产生回访任务,但仍由服务监督员完成报告。 +- 审核、驳回和重新提交必须新增记录,不覆盖历史版本。 + +## 4. 页面梳理 + +### 4.1 页面结构总览 + +| 页面域 | 逻辑页面 | 主要能力 | 主要角色 | +| --- | --- | --- | --- | +| 公共 | 登录 | 账号、密码、验证码登录 | 全部用户 | +| 导入 | 工单导入、导入预览、导入明细 | 文件选择、批次号、预览、确认、批次历史 | 服务监督主管 | +| 查询 | 工单查询、工单详情 | 多条件查询、分页、详情查看 | 6 类业务角色 | +| 待办 | 待办列表、分类统计 | 按环节查看个人/岗位待办 | 除纯查询人员外的业务角色 | +| 派工 | 主管派单、智能派工 | 初查多选、首联单选、监督员、提级督办 | 服务监督主管 | +| 首联 | 首联分派、首联填报 | 分派班组/人员,填写首次联系信息 | 客服部主管、客服部班长/成员 | +| 调查 | 初查填报、全面调查填报 | 初查内容、调查问卷和处置结果 | 调查部门、服务监督员 | +| 审核 | 调查审核、业务审核 | 同意/不同意、驳回理由、按责任类型确定下步分支 | 服务监督主管、业务部主任 | +| 回访 | 回访分派、回访填报、结果确认 | 班组分派、回复信息、同意/不同意结果、条件附件 | 客服部主管、客服班组、服务监督员 | +| 输出 | 调查报告、成果物 | 汇总调查/回访信息、条件附件、确认下载、成果物输出 | 服务监督员 | +| 配置 | 智能派工规则、流程时限 | 规则维护、环节时限维护 | 服务监督主管或配置管理员 | +| 分析 | 统计分析 | 办结统计与展示 | 有菜单权限人员 | + +### 4.2 41 个原型画板清单 + +| 原型编号 | 画板名称 | 页面状态/用途 | 处理结论 | +| --- | --- | --- | --- | +| 1-1 | 登录页2 | 登录 | 有效页面 | +| 1-2 | 服务监督主管-首页(工单导入)导入预览 | 导入文件后的行级预览 | 有效页面状态 | +| 1-3 | 服务监督主管-首页(工单导入)导入明细 | 历史批次列表 | 有效页面状态 | +| 1-4 | 服务监督主管-工单处理(审核弹窗) | 主管审核弹窗:审核结果、驳回理由、回访分派 | 有效页面状态;回访分派的实际生效时点待统一 | +| 1-5 | 服务监督主管-工单处理(待审核) | 主管待审核列表 | 有效页面状态 | +| 1-6 | 服务监督主管-规则配置-智能派工规则配置 | 智能派工规则入口 | 页面内容待补齐 | +| 1-7 | 服务监督主管-规则配置-流程时限管理 | 整体及环节时限配置 | 有效页面 | +| 1-8 | 服务监督主管-统计分析 | 统计分析入口 | 指标和图表待补齐 | +| 1-9 | 服务监督主管-工单处理(待审核) | 全量调查结果审核长页面 | 有效长页面 | +| 1-10 | 服务监督主管-工单处理(待分配派工) | 待派工列表和派工入口 | 有效页面状态 | +| 1-11 | 服务监督主管-待分派(确定标题版) | 主管派工详情 | 有效长页面;业务上由服务监督主管完成主责/协查判定和派工 | +| 1-12 | 服务监督主管-工单处理 | 主管任务总览 | 有效页面 | +| 1-13 | 服务监督人员-待办工单(调查结果填报)业务类型 | 调查表单简化/业务类型方案 | 与 1-21 的适用条件待确认 | +| 1-14 | 服务监督人员-待办工单(调查报告编写-不同意) | 报告确认下载及条件附件状态 | 有效条件分支 | +| 1-15 | 服务监督人员-待办工单(调查报告编写-同意) | 报告确认下载状态 | 有效页面状态 | +| 1-16 | 服务监督人员-待办工单(成果物输出) | 成果物输出 | 有效长页面;成果物清单仍待定义 | +| 1-17 | 服务监督人员-待办工单(处理工单)事件类型(回访结果填写)不同意上传 | “不同意或客户主动要求”附件状态 | 有效条件分支 | +| 1-18 | 业务部主任-待办工单(审核)(弹窗) | 二级审核弹窗:审核结果、驳回理由、回访分派 | 有效页面状态;协查通过后业务上不应要求回访分派 | +| 1-19 | 业务部主任-待办工单(审核) | 二级审核完整详情 | 有效长页面 | +| 1-20 | 服务监督人员-待办工单(处理工单)事件类型(回访结果填写) | 回访结果完整表单 | 有效长页面 | +| 1-21 | 服务监督人员-待办工单(调查结果填报) | 全面调查表单 | 有效长页面 | +| 1-22 | 服务监督人员-待办工单 | 调查、报告、回访、成果物待办总览 | 有效页面 | +| 1-23 | 总值班经理待办工单-待调查(处理) | 初查内容填报 | 有效页面 | +| 1-24 | 智能运营团队-待办工单-待调查(处理) | 初查内容填报 | 有效页面 | +| 1-25 | 客服部主管-待办工单-待调研 | 客服部门初查 | 有效页面 | +| 1-26 | 客服部班长-待办工单备份 | 首联填写状态 | 页面状态有效,名称应去掉“备份” | +| 1-27 | 业务部主任-待办工单 | 业务审核列表 | 有效页面 | +| 1-28 | 总值班经理-待办工单 | 初查待办列表 | 有效页面 | +| 1-29 | 智能运营团队-待办工单 | 初查待办列表 | 有效页面 | +| 1-30 | 客服部班长-待办工单 | 首联待办列表 | 有效页面 | +| 1-31 | 客服部主管-待办工单-回访分派 | 回访任务选择班组/人员 | 有效页面 | +| 1-32 | 客服部主管-待办工单-首联分派 | 首联任务选择班组/人员 | 有效页面 | +| 1-33 | 客服部主管-待办工单 | 任务总数、首联分派、回访分派入口 | 有效页面 | +| 1-34 | 业务部主任 | 工单查询 | 有效页面,名称需规范化 | +| 1-35 | 总值班经理-工单查询 | 工单查询 | 有效页面 | +| 1-36 | 智能运营团队-工单查询 | 工单查询 | 有效页面 | +| 1-37 | 客服部班长-工单查询 | 工单查询 | 有效页面 | +| 1-38 | 客服部主管-工单查询 | 工单查询 | 有效页面 | +| 1-39 | 服务监督人员-工单查询 | 工单查询 | 有效页面 | +| 1-40 | 服务监督主管-工单查询 | 工单查询 | 有效页面 | +| 1-41 | 服务监督主管-首页(工单导入) | 导入入口 | 有效页面 | + +### 4.3 通用列表需求 + +工单查询页在各角色间基本一致,应实现为同一页面组件加数据权限,而不是复制六套接口。查询条件包括:12398 工单编号、工单环节、信息来源、客户致电 12398 日期、转办日期、客户名称、联系方式、用户地址、客户所在地区(省)、省份、事件类型、转办事项分类、问题分类。 + +列表至少展示:序号、12398 工单编号、工单状态/环节、信息来源、客户致电日期、转办日期、客户名称、联系方式、用户地址、客户所在地区及操作。列表必须服务端分页,排序字段使用白名单,手机号按权限脱敏。 + +待办列表应与查询列表区分:查询页返回用户可见工单,待办页只返回当前用户可办理的有效任务。任务统计卡片必须与相同筛选口径下的列表一致。 + +## 5. 角色与权限梳理 + +### 5.1 业务角色 + +| 角色 | 主要职责 | 原型可见菜单 | 关键操作 | +| --- | --- | --- | --- | +| 服务监督主管 | 导入、主责/协查判定与派工、调查一级审核、规则与时限管理 | 工单导入、工单查询、工单处理、规则配置、统计分析 | 预览/确认导入、判定责任类型并派工、审核、维护配置;原型显示的回访分派生效时点待统一 | +| 服务监督人员 | 全面调查、回访结果、调查报告、成果物 | 工单查询、待办工单 | 派工后先行填报调查草稿,满足前置条件后提交;填回访结果、上传材料、确认下载报告、输出成果物 | +| 客服部主管 | 将首联或回访任务分派到本部门班组/人员,也可承担部门初查 | 工单查询、待办工单 | 首联分派、回访分派、初查提交 | +| 客服部班长/成员 | 执行首联或客户回访 | 工单查询、待办工单 | 填写首联时间、首联工单号、首联内容;回访表单待进一步确认 | +| 智能运营团队 | 执行被派发的事件初查 | 工单查询、待办工单 | 填写初查内容并提交 | +| 总值班经理/团队 | 执行被派发的事件初查 | 工单查询、待办工单 | 填写初查内容并提交 | +| 业务部主任 | 对调查结果进行第二级业务审核,并依据前序责任类型触发后续分支 | 工单查询、待办工单 | 审核同意/不同意并填写意见;主责进入回访,协查直接进入调查报告 | +| 系统任务(建议) | 导入解析、时限计时、提醒、统计、智能派工/写作 | 无人工菜单 | 异步处理、规则计算、通知和审计 | + +> 原型出现 7 个业务称谓,但“客服部班长/成员”可建模为同一执行角色下的岗位或人员范围;“智能运营团队”和“总值班经理”更适合作为可被派工的组织/岗位,而非写死在流程代码中。 + +### 5.2 权限矩阵 + +| 能力 | 监督主管 | 监督人员 | 客服主管 | 客服班组 | 智能运营 | 总值班 | 业务主任 | +| --- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | +| 导入工单 | 写 | - | - | - | - | - | - | +| 查询可见工单 | 读 | 读 | 读 | 读 | 读 | 读 | 读 | +| 查看完整客户联系方式 | 按权限 | 按任务 | 按任务 | 按任务 | 按任务 | 按任务 | 按审核任务 | +| 主管派工 | 写 | - | - | - | - | - | - | +| 首联/回访班组分派 | - | - | 写 | - | - | - | - | +| 首联填报 | - | - | 可选 | 写 | - | - | - | +| 初查填报 | - | - | 写 | - | 写 | 写 | - | +| 全面调查填报 | - | 写 | - | - | - | - | - | +| 调查一级审核 | 写 | - | - | - | - | - | - | +| 调查二级审核 | - | - | - | - | - | - | 写 | +| 回访结果填报/材料 | - | 写 | 分派 | 执行待确认 | - | - | - | +| 报告和成果物 | - | 写 | - | - | - | - | - | +| 规则与时限 | 写或配置管理员 | - | - | - | - | - | - | +| 统计分析 | 按菜单权限 | 按菜单权限 | 按菜单权限 | 按菜单权限 | 按菜单权限 | 按菜单权限 | 按菜单权限 | + +### 5.3 数据权限原则 + +- 服务监督主管:可查看其管理范围内全部工单和任务。 +- 执行角色:默认只查看本人、本岗位或本部门被分派的工单;跨部门信息按任务需要开放。 +- 查询权限与办理权限分离;能查看工单不代表可以提交当前任务。 +- 联系方式、地址、录音、转译文本和附件属于敏感业务数据,应分别控制查看、播放、下载和导出权限。 +- 所有写操作必须以服务端当前任务所有者、角色、状态和版本为准,不能只依赖前端隐藏按钮。 + +## 6. 业务流程 + +### 6.1 主流程 + +```mermaid +flowchart TD + A["01 线下导入12398工单"] --> B["服务监督主管判定主责或协查并派工"] + B -. "同时开放" .-> H["05 服务监督员可先行填报并保存调查草稿"] + B --> R{"服务监督主管判定结果"} + R -->|"主责"| C["02 客服主管分派首联"] + C --> D["03 客服班组完成首联"] + R -->|"主责"| E["04 多部门并行事件初查"] + R -->|"协查"| E + D --> F{"主责:首联及全部初查均完成"} + E -->|"主责"| F + E -->|"协查"| G{"协查:全部初查均完成"} + H -. "只能在对应前置条件满足后提交" .-> S["05 提交全面调查"] + F --> S + G --> S + S --> I["06 服务监督主管审核"] + I -->|"不通过"| H + I -->|"通过"| J["07 业务部主任审核"] + J -->|"不通过"| H + J -->|"通过"| Q{"按服务监督主管前序判定分支"} + Q -->|"主责"| K["08 客服主管分派回访"] + Q -->|"协查"| O["11 调查报告编写"] + K --> L["09 客服班组回访"] + L --> M["10 服务监督员填报回访结果"] + M -->|"不同意或客户主动要求"| N["上传答复书和送达凭证"] + M -->|"同意"| O["11 调查报告编写"] + N --> O + O --> P["12 成果物输出并办结"] +``` + +### 6.2 流程步骤明细 + +| 步骤 | 操作人 | 输入 | 操作/输出 | 进入下一步条件 | +| --- | --- | --- | --- | --- | +| 01 导入 | 服务监督主管 | Excel/CSV、导入批次 | 预览校验,确认生成工单 | 批次确认成功且行级校验通过 | +| 01A 判定派工 | 服务监督主管 | 工单、95598 记录 | 判定主责/协查,选择初查部门、首联部门、监督员、提级督办;同时向服务监督员开放调查填报 | 派工事务成功;服务监督员可开始填报和保存草稿 | +| 02 首联分派 | 客服部主管 | 首联任务 | 选择本部门班组/人员 | 目标人员有效且属于授权范围 | +| 03 班组首联 | 客服班组 | 客户及工单信息 | 填首联时间、首联工单号、首联内容 | 首联任务提交完成 | +| 04 事件初查 | 被派部门主管/岗位 | 工单及 95598 记录 | 填写初查内容 | 每个并行初查任务均提交完成 | +| 05 全面调查 | 服务监督人员 | 工单信息,以及办理过程中逐步形成的首联和初查信息 | 派工后可先行填报并保存部分信息;前置任务完成后提交完整调查表 | 主责:首联及全部初查完成;协查:全部初查完成;表单校验通过 | +| 06 主管审核 | 服务监督主管 | 调查版本 | 同意或驳回并填写意见 | 同意进入二级审核;驳回重开调查 | +| 07 业务审核 | 业务部主任 | 已通过一级审核的调查版本、服务监督主管前序判定结果 | 同意或驳回并填写意见 | 不同意则重开调查;同意且为主责进入回访,同意且为协查直接进入调查报告 | +| 08 回访分派 | 客服部主管 | 主责工单的回访任务 | 选择本部门班组/人员 | 仅主责工单进入;目标人员有效 | +| 09 班组回访 | 客服班组 | 主责工单的联系信息、处理结果 | 记录客户联系及原始回访情况 | 回访执行任务完成 | +| 10 回访结果 | 服务监督人员 | 主责工单的回访执行记录 | 填回复人、时间、电话、回复情况、结果 | “不同意或客户主动要求”分支附件齐全 | +| 11 调查报告 | 服务监督人员 | 调查数据;主责还包括回访及条件附件 | 核对报告上下文并确认生成/下载 | 主责在回访结果完成后进入;协查在业务部主任审核通过后直接进入;报告输出版本生成成功 | +| 12 成果物/办结 | 服务监督人员 | 报告和附件 | 汇总、输出、确认办结 | 必备成果物齐全且不存在未完成任务 | + +### 6.3 主责与协查分支 + +流程分支规则如下: + +- 服务监督主管负责判定工单属于主责还是协查,并完成派工;该判定结果必须随流程实例持久化,供后续分支使用。 +- 主责工单同时产生首联任务、一个或多个并行事件初查任务,并向指定服务监督员开放全面调查填报。 +- 协查工单从服务监督主管派工直接进入 04 多部门并行事件初查,不产生首联任务;同时向指定服务监督员开放全面调查填报。 +- 服务监督员在派工完成后即可先行填写部分调查信息并保存草稿,无需等待首联或初查结束。 +- 全面调查的“提交”动作受前置条件约束:主责必须等待首联及全部初查完成;协查必须等待全部初查完成。前置条件未满足时只能继续编辑或保存草稿。 +- 服务监督主管审核通过后进入业务部主任审核。业务部主任审核通过时,系统读取服务监督主管在派工阶段作出的判定:主责进入 08~10 回访流程,协查直接进入 11 调查报告编写。 +- 事件初查部门可多选;主责工单的首联部门单选。 + +### 6.4 建议状态模型 + +不建议只使用原型中的“待分派/处理中/已结束”三个展示状态驱动流程。建议拆分“工单生命周期状态”和“任务环节状态”: + +| 类型 | 建议状态 | +| --- | --- | +| 工单生命周期 | `IMPORTED` 已导入、`DISPATCHING` 待派工、`PROCESSING` 办理中、`REVIEWING` 审核中、`CALLBACK` 回访中、`REPORTING` 报告中、`CLOSED` 已办结、`CANCELLED` 已取消 | +| 任务状态 | `PENDING` 待领取/待办理、`ASSIGNED` 已分派、`IN_PROGRESS` 办理中、`SUBMITTED` 已提交、`APPROVED` 已通过、`REJECTED` 已驳回、`COMPLETED` 已完成、`CANCELLED` 已取消 | +| 流程环节 | `DISPATCH`、`FIRST_CONTACT_ASSIGN`、`FIRST_CONTACT`、`PRELIMINARY_INVESTIGATION`、`INVESTIGATION`、`SUPERVISOR_REVIEW`、`BUSINESS_REVIEW`、`CALLBACK_ASSIGN`、`CALLBACK`、`CALLBACK_RESULT`、`REPORT`、`DELIVERABLE` | + +工单状态由有效任务和流程实例聚合计算,不允许客户端直接提交任意目标状态。 + +## 7. 字段梳理 + +### 7.1 字段约定 + +- `ID` 建议使用系统内部不可猜测标识;业务编号另设唯一字段。 +- 日期使用 `YYYY-MM-DD`,日期时间使用带时区的 ISO 8601;存储使用统一时区,展示按用户时区转换。 +- `是否`类字段使用布尔或明确枚举,不使用自由文本。 +- 所有枚举保存稳定编码,界面显示名称通过字典返回。 +- “必填”在原型中未用星号完整表达,以下仅将流程成立所需字段标为“建议必填”,最终以业务评审为准。 + +### 7.2 登录与用户上下文 + +| 字段 | 建议类型 | 必填 | 来源/说明 | +| --- | --- | :---: | --- | +| username | string(64) | 是 | 登录账号 | +| password | string | 是 | 只用于认证请求,不落业务日志 | +| code | string(16) | 是 | 验证码 | +| uuid | string(64) | 是 | 验证码唯一标识 | +| userId | int64/string | 系统 | 当前用户标识 | +| nickName | string(64) | 系统 | 页头显示名称 | +| deptId | int64/string | 系统 | 数据权限和派工候选范围 | +| roleIds/postIds | array | 系统 | 菜单与操作授权 | + +### 7.3 导入批次与导入行 + +| 字段 | 建议类型 | 必填 | 说明 | +| --- | --- | :---: | --- | +| batchId | string | 系统 | 导入批次内部标识 | +| batchNo | string(64) | 是 | 原型示例 `20260806-B01`,需唯一性规则 | +| fileId/fileName | string | 是 | 原始导入文件 | +| importMode | enum | 是 | `EXCEL`/`CSV` | +| status | enum | 系统 | 上传中、预览成功、部分失败、已确认、已取消 | +| totalCount/successCount/failureCount | integer | 系统 | 批次数量统计 | +| importedBy/importedAt | string/datetime | 系统 | 操作人和时间 | +| previewToken | string | 系统 | 预览到确认之间的短期凭证,防止文件被替换 | +| rowNo | integer | 系统 | 原文件行号 | +| rowStatus | enum | 系统 | 有效、重复、错误、待确认 | +| errorCode/errorMessage | string | 否 | 行级错误原因 | + +### 7.4 12398 工单基础信息 + +| 字段 | 建议类型 | 必填 | 说明 | +| --- | --- | :---: | --- | +| workOrderId | string | 系统 | 内部主键 | +| workOrderNo12398 | string(64) | 是 | 12398 工单编号,建议唯一 | +| orderStatus | enum | 系统 | 工单生命周期状态 | +| currentStage | enum | 系统 | 当前流程环节 | +| sourceCode | enum | 是 | 信息来源,如 12398 电话 | +| customerCalledAt | date/datetime | 是 | 客户致电 12398 日期/时间 | +| transferredAt | date/datetime | 是 | 转办日期/时间 | +| customerName | string(128) | 建议 | 客户名称 | +| contactPhone | string(32) | 建议 | 联系方式,密文或受控存储 | +| customerAddress | string(512) | 否 | 用户地址,敏感字段 | +| customerRegion | string(128) | 否 | 客户所在地区(省) | +| provinceCode | string(32) | 建议 | 省份稳定编码 | +| eventTypeCode | string(32) | 建议 | 事件类型 | +| transferCategoryCode | string(32) | 建议 | 转办事项分类 | +| problemCategoryCode | string(32) | 建议 | 问题分类 | +| responsibilityType | enum | 是 | 由服务监督主管在派工时判定:`PRIMARY` 主责 / `ASSIST` 协查;后续业务审核分支以该值为准 | +| escalationFlag | boolean | 否 | 是否提级督办 | +| processVersion | integer | 系统 | 流程/乐观锁版本 | +| createdAt/updatedAt | datetime | 系统 | 审计时间 | + +### 7.5 95598 业务处理信息 + +| 字段 | 建议类型 | 必填 | 说明 | +| --- | --- | :---: | --- | +| serviceRecordId | string | 系统 | 记录标识 | +| serviceOrderNo95598 | string(64) | 建议 | 95598 工单编号 | +| acceptanceContent | text | 建议 | 受理内容 | +| businessTypeCode | string(32) | 否 | 业务类型 | +| acceptedAt | datetime | 否 | 受理时间 | +| powerSupplyOrgId/name | string | 否 | 供电单位编码及名称 | +| recordingFileId | string | 否 | 录音文件引用 | +| recordingDuration | integer | 否 | 录音时长(秒) | +| transcriptText | text | 否 | 转译文本 | +| sequenceNo | integer | 系统 | 同一工单内排序 | + +### 7.6 派工信息 + +| 字段 | 建议类型 | 必填 | 说明 | +| --- | --- | :---: | --- | +| dispatchId | string | 系统 | 派工记录标识 | +| workOrderId | string | 是 | 所属工单 | +| responsibilityType | enum | 是 | 服务监督主管在派工时判定主责/协查,并持久化到流程实例 | +| preliminaryDeptIds | array | 是 | 初查部门,多选且去重 | +| firstContactDeptId | string | 主责必填 | 首联部门,单选;协查应为空 | +| supervisorUserId | string | 是 | 服务监督员,单选 | +| escalationDeptIds | array | 否 | 提级督办部门多选 | +| dispatchMode | enum | 是 | `MANUAL`/`SMART` | +| ruleVersion | integer | 智能派工时 | 命中的规则版本 | +| dispatchedBy/dispatchedAt | string/datetime | 系统 | 派工人和时间 | + +### 7.7 首联与初查 + +| 对象 | 字段 | 建议类型 | 必填 | 说明 | +| --- | --- | --- | :---: | --- | +| 首联分派 | assigneeDeptId | string | 是 | 客服主管所在部门 | +| 首联分派 | assigneeUserId/postId | string | 是 | 本部门班组人员或岗位 | +| 首联 | firstContactAt | datetime | 是 | 首联时间 | +| 首联 | firstContactOrderNo | string(64) | 是 | 首联工单号 | +| 首联 | firstContactContent | text | 是 | 首联内容 | +| 初查 | taskId | string | 是 | 一任务一初查,保证并行结果可追踪 | +| 初查 | preliminaryContent | text | 是 | 原型中的“初查内容” | +| 通用 | submittedBy/submittedAt | string/datetime | 系统 | 提交审计字段 | + +### 7.8 全面调查 + +| 字段 | 建议类型 | 建议必填 | 说明 | +| --- | --- | :---: | --- | +| investigationId/version | string/integer | 系统 | 每次提交生成不可变版本 | +| acceptedContent12398 | text | 否 | 原型业务类型方案中展示“导入的12398投诉内容” | +| externalExposureRisk | boolean | 是 | 是否存在国家级媒体、党政机关、监管机构等外部渠道风险 | +| internalAppealIssued | boolean | 是 | 是否派发内部诉求工单 | +| customerAppealSummary | text | 是 | 客户诉求概况 | +| escalationReason | text | 是 | 客户升级主要原因 | +| investigationProcess | text | 是 | 本期诉求调查过程 | +| localResponsibility | boolean | 条件 | 是否本单位事项 | +| handlingSituation | text | 条件 | 办理情况 | +| accountable | boolean | 条件 | 是否有责 | +| hasHandlingBasis | boolean | 条件 | 是否有处理依据 | +| handlingMethod | text | 条件 | 处理方式 | +| handlingResult | text | 条件 | 处理结果 | +| investigationSummary | text | 待确认 | 原型出现独立“调查结果”,与上述字段可能重复 | +| draftFlag | boolean | 系统 | 是否草稿;原型未显示“暂存”按钮,建议补充 | + +“是否本单位事项”之后的字段在 1-21 完整调查页出现、在 1-13 业务类型方案中未出现,可能是按事件类型或责任判定动态展示,需确认显示条件及条件必填规则。 + +### 7.9 审核、回访、报告与成果物 + +| 对象 | 字段 | 建议类型 | 必填/条件 | +| --- | --- | --- | --- | +| 审核 | reviewLevel | enum | 是:主管审核/业务审核 | +| 审核 | reviewResult | enum | 是:同意/不同意 | +| 审核 | rejectReason | text | 不同意时必填 | +| 审核 | reviewedBy/reviewedAt | string/datetime | 系统生成 | +| 回访分派 | callbackDeptId | string | 是 | +| 回访分派 | assigneeUserId/postId | string | 客服主管二次分派时必填 | +| 回访 | respondentName | string(64) | 原型显示“回复人” | +| 回访 | repliedAt | datetime | 是 | +| 回访 | replyPhone | string(32) | 是,敏感字段 | +| 回访 | replySituation | text | 是 | +| 回访 | callbackResult | enum | 是;最新原型统一为 `AGREE` 同意 / `DISAGREE_OR_CUSTOMER_REQUEST` 不同意或客户主动要求 | +| 附件 | responseLetterFileIds | array | 不同意或客户主动要求时至少 1 个答复书 | +| 附件 | deliveryProofFileIds | array | 不同意或客户主动要求时至少 1 个送达凭证 | +| 报告 | reportId/version | string/integer | 系统 | +| 报告 | sourceInvestigationVersion | string/integer | 是;锁定生成报告所依据的调查版本 | +| 报告 | sourceCallbackVersion | string/integer | 主责必填;协查为空 | +| 报告 | outputFileId | string | 确认生成后返回的可控下载文件 | +| 报告 | outputStatus | enum | 待生成、生成中、可下载、生成失败、已确认 | +| 报告 | generationMode | enum | 模板生成/智能辅助;是否开放在线编辑待确认 | +| 成果物 | deliverableType/fileId | enum/string | 待确认具体成果物类型 | +| 办结 | closedBy/closedAt/closeReason | string/datetime/text | 系统/条件 | + +20260812 原型未提供独立富文本报告编辑器,而是展示调查信息、主责工单的回访信息及条件附件,并通过“确认下载”输出报告。因此 `reportContent` 不应在当前阶段被直接定为前端必填字段;后端可内部保存结构化快照或模板渲染内容,但其格式属于技术方案而非原型已确认需求。 + +### 7.10 规则与时限 + +| 字段 | 建议类型 | 说明 | +| --- | --- | --- | +| configId/configVersion | string/integer | 配置标识和版本 | +| totalLimitMinutes | integer | 整体时限,原型示例 188 分钟不能视为正式值 | +| stageCode | enum | 办理派工、首联、事件调查、调查审核、回访、调查报告、编写提交等 | +| stageLimitMinutes | integer | 环节时限,必须为正整数 | +| smartDispatchRule | json/rule DSL | 智能派工条件和结果,原型未提供字段 | +| effectiveFrom/effectiveTo | datetime | 生效区间 | +| status | enum | 草稿、启用、停用 | +| changedBy/changedAt/changeReason | string/datetime/text | 配置审计 | + +## 8. 接口梳理 + +### 8.1 接口约定 + +现有后端文档采用 Bearer Token、`AjaxResult` 和 `TableDataInfo`。为保持兼容,本报告沿用这些返回结构;新业务接口路径是建议契约,最终应与后端统一前缀和命名规范后进入 OpenAPI。 + +通用约定: + +- 除验证码和登录外,接口请求头携带 `Authorization: Bearer `。 +- 分页统一使用 `pageNum`、`pageSize`;响应使用 `total`、`rows`、`code`、`msg`。 +- 写接口携带 `requestId` 或 `Idempotency-Key`,涉及状态变更时携带 `version`。 +- 业务校验失败返回稳定业务错误码,不依赖提示语解析。 +- 列表只返回摘要;详情接口按权限返回敏感字段和附件权限。 + +### 8.2 接口目录 + +接口目录只用于定位,不替代字段契约。每个接口的请求字段定义、响应字段定义和 JSON 示例见 8.5~8.11 节。 + +#### 8.2.1 已有认证与用户接口 + +| 方法 | 路径 | 用途 | 状态 | +| --- | --- | --- | --- | +| GET | `/captchaImage` | 获取验证码和 uuid | 已有文档确认 | +| POST | `/login` | 账号、密码、验证码登录 | 已有文档确认 | +| GET | `/system/user/profile` | 当前用户信息 | 已有文档确认 | +| GET | `/system/user/list` | 用户候选查询 | 已有文档确认,业务派工应增加数据范围过滤 | +| GET | `/system/user/deptTree` | 部门树 | 已有文档确认 | +| GET | `/system/user/{userId}` | 用户详情 | 已有文档确认 | +| PUT | `/system/user/profile/updatePwd` | 修改密码 | 已有文档确认 | + +#### 8.2.2 导入接口 + +| 编号 | 方法 | 建议路径 | 用途 | 备注 | +| --- | --- | --- | --- | --- | +| IMP-01 | GET | `/risk/import-batches/template` | 下载导入模板 | 返回文件流及模板版本 | +| IMP-02 | POST | `/risk/import-batches/preview` | 上传并解析 Excel/CSV | multipart:file、batchNo;返回 previewToken、统计、行级预览 | +| IMP-03 | GET | `/risk/import-batches/{previewToken}/rows` | 分页查看预览行 | pageNum/pageSize/rowStatus | +| IMP-04 | POST | `/risk/import-batches/confirm` | 确认导入 | previewToken、batchNo、requestId;返回 batchId 和成功/失败数量 | +| IMP-05 | GET | `/risk/import-batches` | 导入批次列表 | 时间、批次号、人员、状态、分页 | +| IMP-06 | GET | `/risk/import-batches/{batchId}` | 批次详情 | 批次统计和文件信息 | +| IMP-07 | GET | `/risk/import-batches/{batchId}/rows` | 导入结果行明细 | 行状态、错误码、关联工单 | + +#### 8.2.3 工单查询与详情接口 + +| 编号 | 方法 | 建议路径 | 用途 | 备注 | +| --- | --- | --- | --- | --- | +| WO-01 | GET | `/risk/work-orders` | 权限范围内工单查询 | 原型全部查询条件、分页、排序 | +| WO-02 | GET | `/risk/work-orders/{workOrderId}` | 工单基础详情 | 基础信息、当前环节、权限动作 | +| WO-03 | GET | `/risk/work-orders/{workOrderId}/service-records` | 95598 处理信息 | 列表、录音授权、转译文本 | +| WO-04 | GET | `/risk/work-orders/{workOrderId}/process` | 流程视图 | 当前节点、并行任务、历史节点、时限 | +| WO-05 | GET | `/risk/work-orders/{workOrderId}/timeline` | 操作轨迹 | 派工、提交、审核、驳回、附件、办结事件 | +| WO-06 | GET | `/risk/work-orders/{workOrderId}/allowed-actions` | 当前用户可执行动作 | 动作编码及所需版本;也可并入详情响应 | + +#### 8.2.4 待办与派工接口 + +| 编号 | 方法 | 建议路径 | 用途 | 备注 | +| --- | --- | --- | --- | --- | +| TASK-01 | GET | `/risk/tasks` | 当前用户待办列表 | stage/status/工单条件/分页 | +| TASK-02 | GET | `/risk/tasks/summary` | 待办统计卡片 | 各环节数量,与列表同口径 | +| TASK-03 | GET | `/risk/tasks/{taskId}` | 待办上下文 | 工单摘要、表单数据、任务版本、允许动作 | +| DSP-01 | GET | `/risk/work-orders/{id}/dispatch-candidates` | 派工候选 | 初查部门、首联部门、监督员、督办部门 | +| DSP-02 | POST | `/risk/work-orders/{id}/smart-dispatch-suggestion` | 获取智能派工建议 | 工单版本;返回建议和规则解释,不直接落单 | +| DSP-03 | POST | `/risk/work-orders/{id}/dispatch` | 确认人工/智能派工 | 主责/协查、部门、监督员、督办、version、requestId | +| DSP-04 | POST | `/risk/tasks/{taskId}/assign` | 客服主管分派班组/人员 | assigneeUserId/postId、version | +| DSP-05 | POST | `/risk/work-orders/{id}/callback-department` | 审核环节确定主责工单的回访承接部门 | callbackDeptId、reviewTaskId、version;具体由一级还是二级审核生效待确认 | + +#### 8.2.5 办理与审核接口 + +| 编号 | 方法 | 建议路径 | 用途 | 备注 | +| --- | --- | --- | --- | --- | +| ACT-01 | PUT | `/risk/tasks/{taskId}/draft` | 保存草稿 | formType、formData、version;技术建议 | +| ACT-02 | POST | `/risk/tasks/{taskId}/first-contact/submit` | 提交首联 | 时间、工单号、内容、version | +| ACT-03 | POST | `/risk/tasks/{taskId}/preliminary-investigation/submit` | 提交初查 | preliminaryContent、version | +| ACT-04 | GET | `/risk/tasks/{taskId}/investigation-context` | 获取全面调查上下文 | 派工后即可调用;返回已形成的首联/初查、历史调查版本和 `submitReady`/未完成前置任务 | +| ACT-05 | POST | `/risk/tasks/{taskId}/investigation/submit` | 提交全面调查 | 第 7.8 节字段、version;主责校验首联及全部初查,协查校验全部初查 | +| ACT-06 | POST | `/risk/tasks/{taskId}/supervisor-review` | 主管审核 | result、rejectReason、investigationVersion、version | +| ACT-07 | POST | `/risk/tasks/{taskId}/business-review` | 业务审核 | result、rejectReason、investigationVersion、version | +| ACT-08 | POST | `/risk/tasks/{taskId}/callback/submit` | 提交班组回访 | 原始回访字段,具体字段待确认 | +| ACT-09 | POST | `/risk/tasks/{taskId}/callback-result/submit` | 提交回访结果 | 回复信息、结果、附件 fileIds、version | +| ACT-10 | GET | `/risk/tasks/{taskId}/report-context` | 获取报告确认上下文 | 返回责任类型、锁定的调查版本、主责回访版本、附件及当前输出状态 | +| ACT-11 | POST | `/risk/tasks/{taskId}/report/confirm` | 确认生成报告 | 模板版本、调查/回访数据版本、version、requestId;可同步返回 reportId,或异步返回 jobId | +| ACT-12 | GET | `/risk/reports/{reportId}/download` | 受权下载已生成报告 | 返回受控文件流或短期下载地址,不改变流程状态 | +| ACT-13 | POST | `/risk/tasks/{taskId}/deliverables/submit` | 提交成果物并申请办结 | deliverables、version | +| ACT-14 | POST | `/risk/work-orders/{id}/close` | 确认办结 | version、requestId;仅在所有前置条件满足时允许 | + +#### 8.2.6 文件、字典、配置和统计接口 + +| 编号 | 方法 | 建议路径 | 用途 | +| --- | --- | --- | --- | +| FILE-01 | POST | `/risk/files` | 上传录音、答复书、送达凭证或成果物,返回 fileId | +| FILE-02 | GET | `/risk/files/{fileId}` | 受权下载/播放,建议返回短期签名地址或受控流 | +| FILE-03 | DELETE | `/risk/files/{fileId}` | 删除尚未绑定且由当前用户上传的临时文件 | +| DICT-01 | GET | `/risk/dictionaries/{type}` | 信息来源、省份、事件类型、事项分类、问题分类等 | +| CFG-01 | GET | `/risk/config/sla` | 获取当前时限配置和版本 | +| CFG-02 | PUT | `/risk/config/sla` | 保存并发布整体/环节时限 | +| CFG-03 | GET | `/risk/config/dispatch-rules` | 获取智能派工规则 | +| CFG-04 | PUT | `/risk/config/dispatch-rules` | 保存并发布规则版本 | +| ANA-01 | GET | `/risk/analytics/overview` | 统计概览;指标口径待定义 | +| ANA-02 | GET | `/risk/analytics/closed-orders` | 办结工单统计与明细 | +| JOB-01 | GET | `/risk/jobs/{jobId}` | 查询导入、报告生成等异步任务进度 | + +### 8.3 页面—接口映射 + +| 页面 | 读接口 | 写接口 | +| --- | --- | --- | +| 登录 | `/captchaImage` | `/login` | +| 工单导入 | IMP-01、IMP-05 | IMP-02、IMP-04 | +| 导入预览/明细 | IMP-03、IMP-06、IMP-07 | IMP-04 | +| 工单查询 | WO-01、DICT-01 | 无 | +| 工单详情 | WO-02~WO-06 | 按允许动作进入对应接口 | +| 主管处理列表 | TASK-01、TASK-02 | 无 | +| 派工 | WO-02、WO-03、DSP-01 | DSP-02、DSP-03 | +| 首联/回访分派 | TASK-03、候选用户/岗位 | DSP-04、DSP-05 | +| 首联/初查 | TASK-03、WO-03 | ACT-01~ACT-03 | +| 全面调查 | ACT-04、WO-02、WO-03 | ACT-01、ACT-05 | +| 两级审核 | TASK-03、历史调查版本 | ACT-06、ACT-07 | +| 回访结果 | TASK-03、附件读取 | FILE-01、ACT-09 | +| 报告/成果物 | ACT-10、报告输出状态、流程上下文 | ACT-11~ACT-14、FILE-01 | +| 时限/规则配置 | CFG-01、CFG-03 | CFG-02、CFG-04 | +| 统计分析 | ANA-01、ANA-02 | 无 | + +### 8.4 联调公共契约 + +本节给出前后端联调基线。除 8.2 节已有接口外,以下均为建议契约;进入开发前应固化为 OpenAPI 3.1。示例中的 ID、日期、人员和数量均为演示值。 + +8.5~8.11 节统一按以下顺序描述接口: + +1. **请求字段定义**:逐项说明字段位置、名称、类型、是否必填和业务含义;无请求体也必须明确写出。 +2. **响应字段定义**:逐项说明业务数据字段路径、类型和业务含义;公共包装字段引用 8.4.2~8.4.4。 +3. **请求示例**:GET/DELETE 接口没有请求体时给出完整 URL;JSON 或 multipart 接口给出请求体。 +4. **响应示例**:JSON 接口给出完整响应;文件流接口说明状态码、响应头和响应体类型。 + +字段路径中的 `[]` 表示数组元素,例如 `data.rows[].workOrderId`。标记为“条件必填”的字段必须在定义中写明触发条件。未在字段表中定义的业务字段,前后端不得仅凭示例自行增加。 + +#### 8.4.1 请求头 + +| 请求头 | 必填 | 适用范围 | 说明 | +| --- | :---: | --- | --- | +| `Authorization: Bearer ` | 是 | 除验证码、登录外 | 当前用户身份 | +| `Content-Type: application/json` | 是 | JSON 写接口 | 文件上传除外 | +| `Idempotency-Key` | 条件 | 导入确认、派工、提交、审核、报告确认、办结 | 客户端生成 UUID;同一业务动作重试必须复用 | +| `X-Request-Id` | 建议 | 全部接口 | 链路追踪;未传时由服务端生成 | +| `Accept-Language: zh-CN` | 否 | 全部接口 | 提示语语言,不影响业务错误码 | + +#### 8.4.2 单对象成功响应 + +| 响应字段 | 类型 | 必有 | 定义 | +| --- | --- | :---: | --- | +| `code` | integer/string | 是 | 成功时为 `200`;失败时为稳定业务错误码 | +| `msg` | string | 是 | 面向用户的结果说明,不作为前端业务判断依据 | +| `data` | object/array/null | 是 | 业务响应数据;无业务数据时为 `null` 或空对象,项目内须统一 | +| `requestId` | string | 是 | 服务端链路追踪标识,应同时写入服务端日志 | + +```json +{ + "code": 200, + "msg": "操作成功", + "data": {}, + "requestId": "req-20260813-000001" +} +``` + +#### 8.4.3 分页成功响应 + +| 响应字段 | 类型 | 必有 | 定义 | +| --- | --- | :---: | --- | +| `total` | integer | 是 | 符合查询条件的数据总数,不是当前页条数 | +| `rows` | object[] | 是 | 当前页数据;无数据时返回空数组 | +| `code` | integer | 是 | 成功结果码,固定为 `200` | +| `msg` | string | 是 | 查询结果说明 | +| `requestId` | string | 是 | 服务端链路追踪标识 | + +```json +{ + "total": 1, + "rows": [], + "code": 200, + "msg": "查询成功", + "requestId": "req-20260813-000002" +} +``` + +分页请求统一使用 `pageNum`、`pageSize`;`pageNum` 从 1 开始,`pageSize` 建议限制为 10~100。排序使用 `orderByColumn` 和 `isAsc`,服务端必须使用字段白名单。 + +#### 8.4.4 业务错误响应 + +新增 `/risk/**` 接口建议由 HTTP 状态表达协议结果,响应体 `code` 使用第 9.7 节稳定业务错误码,不用 HTTP 200 包装业务失败;8.2 节列出的既有接口继续保持现有返回约定,避免破坏兼容性。 + +```json +{ + "code": "RISK-4220", + "msg": "首联及全部初查完成后才能提交全面调查", + "data": { + "currentStage": "INVESTIGATION", + "responsibilityType": "PRIMARY", + "incompletePrerequisites": [ + { "type": "FIRST_CONTACT", "taskId": "TASK-FC-001", "status": "IN_PROGRESS" }, + { "type": "PRELIMINARY_INVESTIGATION", "taskId": "TASK-PI-003", "status": "PENDING" } + ], + "fieldErrors": [] + }, + "requestId": "req-20260813-000003" +} +``` + +建议 HTTP 映射:认证失效 `401`,越权 `403`,不存在 `404`,版本/状态冲突 `409`,业务前置条件或字段校验失败 `422`,限流 `429`,服务端异常 `500`。 + +#### 8.4.5 状态变更通用响应 + +所有任务写接口至少返回最新任务版本、工单版本、当前环节和新建后续任务,前端必须用响应覆盖本地版本,不能自行推算状态。 + +```json +{ + "code": 200, + "msg": "提交成功", + "data": { + "workOrderId": "WO-20260806-001", + "taskId": "TASK-INV-001", + "taskStatus": "COMPLETED", + "currentStage": "SUPERVISOR_REVIEW", + "workOrderVersion": 8, + "taskVersion": 3, + "nextTasks": [ + { + "taskId": "TASK-SR-001", + "stage": "SUPERVISOR_REVIEW", + "status": "PENDING", + "assigneeRole": "SERVICE_SUPERVISOR" + } + ] + }, + "requestId": "req-20260813-000004" +} +``` + +### 8.5 认证与用户接口请求与响应字段定义 + +#### 8.5.1 请求字段定义 + +| 接口 | 字段位置 | 字段 | 类型 | 必填 | 定义 | +| --- | --- | --- | --- | :---: | --- | +| `GET /captchaImage` | — | — | — | — | 无查询参数、无请求体 | +| `POST /login` | body | `username` | string | 是 | 登录账号 | +| `POST /login` | body | `password` | string | 是 | 登录密码;仅通过 HTTPS 传输 | +| `POST /login` | body | `code` | string | 条件 | 验证码开启时必填 | +| `POST /login` | body | `uuid` | string | 条件 | 验证码开启时必填,与验证码图片绑定 | +| `GET /system/user/profile` | — | — | — | — | 无查询参数、无请求体;用户由 Token 确定 | +| `GET /system/user/list` | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| `GET /system/user/list` | query | `pageSize` | integer | 是 | 每页数量,建议 10~100 | +| `GET /system/user/list` | query | `userName` | string | 否 | 用户名模糊查询 | +| `GET /system/user/list` | query | `phonenumber` | string | 否 | 手机号查询 | +| `GET /system/user/list` | query | `status` | string | 否 | 用户状态 | +| `GET /system/user/list` | query | `deptId` | string | 否 | 部门标识;服务端仍需执行数据范围过滤 | +| `GET /system/user/deptTree` | — | — | — | — | 无查询参数、无请求体 | +| `GET /system/user/{userId}` | path | `userId` | string | 是 | 用户标识 | +| `PUT /system/user/profile/updatePwd` | query | `oldPassword` | string | 是 | 原密码 | +| `PUT /system/user/profile/updatePwd` | query | `newPassword` | string | 是 | 新密码,须满足密码策略 | + +#### 8.5.2 响应字段定义 + +| 接口 | 响应字段 | 类型 | 定义 | +| --- | --- | --- | --- | +| `GET /captchaImage` | `code` | integer | 结果码 | +| `GET /captchaImage` | `data.uuid` | string | 验证码会话标识 | +| `GET /captchaImage` | `data.img` | string | Base64 编码的验证码图片 | +| `POST /login` | `code` | integer | 结果码 | +| `POST /login` | `msg` | string | 结果说明 | +| `POST /login` | `data` | string | Bearer Token;前端不得写入日志 | +| `GET /system/user/profile` | `data.user` | object | 当前用户基本信息 | +| `GET /system/user/profile` | `data.roles[]` | string[] | 当前用户角色编码 | +| `GET /system/user/profile` | `data.permissions[]` | string[] | 当前用户权限编码 | +| `GET /system/user/list` | `total` | integer | 符合条件的用户总数 | +| `GET /system/user/list` | `rows[]` | object[] | 用户摘要列表 | +| `GET /system/user/list` | `rows[].userId` | string | 用户标识 | +| `GET /system/user/list` | `rows[].userName` | string | 用户名 | +| `GET /system/user/list` | `rows[].nickName` | string | 用户显示名称 | +| `GET /system/user/list` | `rows[].dept` | object | 所属部门摘要 | +| `GET /system/user/list` | `rows[].status` | string | 用户状态 | +| `GET /system/user/deptTree` | `data[]` | object[] | 部门树根节点 | +| `GET /system/user/deptTree` | `data[].id` | string | 部门标识 | +| `GET /system/user/deptTree` | `data[].label` | string | 部门名称 | +| `GET /system/user/deptTree` | `data[].children[]` | object[] | 子部门,递归结构 | +| `GET /system/user/{userId}` | `data.user` | object | 用户详情 | +| `GET /system/user/{userId}` | `data.roleIds[]` | string[] | 已关联角色标识 | +| `GET /system/user/{userId}` | `data.postIds[]` | string[] | 已关联岗位标识 | +| `PUT /system/user/profile/updatePwd` | `code` | integer | 结果码,成功为 200 | +| `PUT /system/user/profile/updatePwd` | `msg` | string | 修改结果说明 | + +#### 8.5.3 请求与响应示例 + +##### `GET /captchaImage` + +无查询参数、无请求体。 + +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "uuid": "4c25fa10-6f61-4ea3-aea5-977c26759468", + "img": "data:image/png;base64,iVBORw0KGgoAAA..." + } +} +``` + +##### `POST /login` + +```json +{ + "username": "admin", + "password": "******", + "code": "TB5K", + "uuid": "4c25fa10-6f61-4ea3-aea5-977c26759468" +} +``` + +```json +{ + "code": 200, + "msg": "登录成功", + "data": "eyJhbGciOiJIUzI1NiJ9...", + "requestId": "req-20260813-000010" +} +``` + +##### 用户和部门查询 + +- `GET /system/user/list?pageNum=1&pageSize=20&deptId=101&status=0`:无 body;响应 `rows` 为用户数组。 +- `GET /system/user/deptTree?status=0`:无 body;响应 `data` 为部门树。 +- `GET /system/user/{userId}`、`GET /system/user/profile`:无 body;响应 `data` 为用户对象。 + +```json +{ + "total": 1, + "rows": [ + { + "userId": 1001, + "deptId": 101, + "userName": "zhangsan", + "nickName": "张某", + "phonenumber": "131****7777", + "status": "0", + "roleIds": [201], + "postIds": [301] + } + ], + "code": 200, + "msg": "查询成功" +} +``` + +派工候选不得直接使用无业务范围限制的全量用户列表;后端应基于部门、岗位、角色和启用状态过滤。 + +### 8.6 导入接口请求与响应字段定义 + +#### 8.6.1 请求字段定义 + +| 接口 | 字段位置 | 字段 | 类型 | 必填 | 定义 | +| --- | --- | --- | --- | :---: | --- | +| IMP-01 | query | `templateVersion` | string | 否 | 指定模板版本;不传则下载当前有效版本 | +| IMP-02 | multipart | `file` | binary | 是 | 待解析的 Excel/CSV 文件,须通过扩展名、MIME、大小和安全扫描校验 | +| IMP-02 | multipart | `batchNo` | string | 是 | 用户填写的业务批次号,同一有效批次内唯一 | +| IMP-02 | multipart | `templateVersion` | string | 是 | 文件使用的模板版本 | +| IMP-03 | path | `previewToken` | string | 是 | 预览会话标识,过期后不可使用 | +| IMP-03 | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| IMP-03 | query | `pageSize` | integer | 是 | 每页数量 | +| IMP-03 | query | `rowStatus` | enum | 否 | 行状态:`VALID`、`DUPLICATE`、`INVALID` | +| IMP-04 | body | `previewToken` | string | 是 | IMP-02 返回的预览会话标识 | +| IMP-04 | body | `batchNo` | string | 是 | 必须与预览阶段批次号一致 | +| IMP-04 | body | `fileSha256` | string | 是 | 文件摘要,防止预览后文件被替换 | +| IMP-04 | body | `duplicatePolicy` | enum | 是 | 重复处理策略;当前建议仅支持 `REJECT` | +| IMP-04 | body | `requestId` | string | 是 | 幂等请求标识,与 `Idempotency-Key` 一致 | +| IMP-05 | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| IMP-05 | query | `pageSize` | integer | 是 | 每页数量 | +| IMP-05 | query | `batchNo` | string | 否 | 批次号模糊或精确查询,开发前统一口径 | +| IMP-05 | query | `status` | enum | 否 | 批次状态 | +| IMP-05 | query | `importedFrom` | date | 否 | 导入开始日期,含边界 | +| IMP-05 | query | `importedTo` | date | 否 | 导入结束日期,含边界 | +| IMP-06 | path | `batchId` | string | 是 | 导入批次标识 | +| IMP-07 | path | `batchId` | string | 是 | 导入批次标识 | +| IMP-07 | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| IMP-07 | query | `pageSize` | integer | 是 | 每页数量 | +| IMP-07 | query | `rowStatus` | enum | 否 | 行处理状态 | + +#### 8.6.2 响应字段定义 + +| 接口 | 响应字段 | 类型 | 定义 | +| --- | --- | --- | --- | +| IMP-01 | HTTP body | binary | 模板文件流 | +| IMP-01 | `Content-Type` | string | Excel 模板 MIME 类型 | +| IMP-01 | `Content-Disposition` | string | 下载文件名 | +| IMP-01 | `X-Template-Version` | string | 实际下载的模板版本 | +| IMP-02 | `data.previewToken` | string | 预览会话标识 | +| IMP-02 | `data.expiresAt` | datetime | 预览会话过期时间 | +| IMP-02 | `data.fileName` | string | 原始文件名 | +| IMP-02 | `data.fileSha256` | string | 文件 SHA-256 摘要 | +| IMP-02 | `data.templateVersion` | string | 解析所用模板版本 | +| IMP-02 | `data.batchNo` | string | 业务批次号 | +| IMP-02 | `data.totalCount` | integer | 数据总行数,不含标题行 | +| IMP-02 | `data.validCount` | integer | 校验通过且可导入行数 | +| IMP-02 | `data.duplicateCount` | integer | 重复行数 | +| IMP-02 | `data.invalidCount` | integer | 校验失败行数 | +| IMP-02 | `data.canConfirm` | boolean | 当前预览是否允许确认导入 | +| IMP-02 | `data.sampleRows[]` | object[] | 少量预览行;完整数据通过 IMP-03 查询 | +| IMP-02 | `data.sampleRows[].rowNo` | integer | 文件中的原始行号 | +| IMP-02 | `data.sampleRows[].rowStatus` | enum | 行校验状态 | +| IMP-02 | `data.sampleRows[].workOrderNo12398` | string | 12398 工单号 | +| IMP-02 | `data.sampleRows[].customerName` | string | 客户名称 | +| IMP-02 | `data.sampleRows[].errors[]` | object[] | 行级错误集合 | +| IMP-03/IMP-07 | `total` | integer | 符合行状态条件的总数 | +| IMP-03/IMP-07 | `rows[]` | object[] | 当前页行明细 | +| IMP-03/IMP-07 | `rows[].rowNo` | integer | 原始行号 | +| IMP-03/IMP-07 | `rows[].rowStatus` | enum | 行状态 | +| IMP-03/IMP-07 | `rows[].rawData` | object | 原始单元格键值,不作为正式业务数据 | +| IMP-03/IMP-07 | `rows[].normalizedData` | object/null | 类型和格式归一化后的业务数据 | +| IMP-03/IMP-07 | `rows[].errorCode` | string/null | 行级稳定错误码 | +| IMP-03/IMP-07 | `rows[].errorMessage` | string/null | 行级错误说明 | +| IMP-03/IMP-07 | `rows[].workOrderId` | string/null | 导入成功后关联的工单标识;预览阶段为空 | +| IMP-04 | `data.batchId` | string | 服务端生成的导入批次标识 | +| IMP-04 | `data.batchNo` | string | 业务批次号 | +| IMP-04 | `data.status` | enum | 批次处理结果:成功、部分成功或失败 | +| IMP-04 | `data.totalCount` | integer | 确认处理总数 | +| IMP-04 | `data.successCount` | integer | 成功创建/关联工单数 | +| IMP-04 | `data.failureCount` | integer | 失败数 | +| IMP-04 | `data.createdWorkOrderIds[]` | string[] | 本次新建工单标识 | +| IMP-05 | `total` | integer | 批次总数 | +| IMP-05 | `rows[]` | object[] | 批次摘要列表,字段结构见下方批次对象 | +| IMP-06 | `data` | object | 批次详情,字段结构见下方批次对象 | +| IMP-05/IMP-06 | `batchId` | string | 导入批次标识 | +| IMP-05/IMP-06 | `batchNo` | string | 业务批次号 | +| IMP-05/IMP-06 | `fileId` | string | 原始导入文件标识 | +| IMP-05/IMP-06 | `fileName` | string | 原始文件名 | +| IMP-05/IMP-06 | `templateVersion` | string | 模板版本 | +| IMP-05/IMP-06 | `status` | enum | 批次状态 | +| IMP-05/IMP-06 | `totalCount` | integer | 总行数 | +| IMP-05/IMP-06 | `successCount` | integer | 成功行数 | +| IMP-05/IMP-06 | `failureCount` | integer | 失败行数 | +| IMP-05/IMP-06 | `importedBy` | object | 导入人摘要,包含 `userId`、`name` | +| IMP-05/IMP-06 | `importedAt` | datetime | 导入时间 | + +#### 8.6.3 请求与响应示例 + +##### IMP-01 下载模板 + +`GET /risk/import-batches/template?templateVersion=2026.08` 无 body。成功响应为文件流,并返回: + +```http +Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet +Content-Disposition: attachment; filename="12398-work-order-template-2026.08.xlsx" +X-Template-Version: 2026.08 +``` + +##### IMP-02 导入预览 + +`POST /risk/import-batches/preview` 使用 `multipart/form-data`: + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | :---: | --- | +| file | binary | 是 | Excel/CSV 文件 | +| batchNo | string | 是 | 客户端填写的业务批次号 | +| templateVersion | string | 是 | 使用的模板版本 | + +```json +{ + "code": 200, + "msg": "文件解析完成", + "data": { + "previewToken": "pvw_6d0726d6", + "expiresAt": "2026-08-13T18:00:00+08:00", + "fileName": "12398工单.xlsx", + "fileSha256": "84a3...e02c", + "templateVersion": "2026.08", + "batchNo": "20260813-B01", + "totalCount": 10, + "validCount": 8, + "duplicateCount": 1, + "invalidCount": 1, + "canConfirm": true, + "sampleRows": [ + { + "rowNo": 2, + "rowStatus": "VALID", + "workOrderNo12398": "2026-08-06-001", + "customerName": "某某有限公司", + "errors": [] + } + ] + } +} +``` + +##### IMP-03/IMP-07 行明细 + +`GET /risk/import-batches/{previewToken}/rows?pageNum=1&pageSize=20&rowStatus=INVALID` 或 `GET /risk/import-batches/{batchId}/rows` 无 body。 + +```json +{ + "total": 1, + "rows": [ + { + "rowNo": 5, + "rowStatus": "INVALID", + "rawData": { + "workOrderNo12398": "", + "customerCalledAt": "2026/13/40" + }, + "normalizedData": null, + "errorCode": "INVALID_DATE", + "errorMessage": "客户致电12398日期格式不正确", + "workOrderId": null + } + ], + "code": 200, + "msg": "查询成功" +} +``` + +##### IMP-04 确认导入 + +```json +{ + "previewToken": "pvw_6d0726d6", + "batchNo": "20260813-B01", + "fileSha256": "84a3...e02c", + "duplicatePolicy": "REJECT", + "requestId": "f332e303-d335-4a58-b09e-e5879122fb7b" +} +``` + +```json +{ + "code": 200, + "msg": "导入完成", + "data": { + "batchId": "IB-20260813-001", + "batchNo": "20260813-B01", + "status": "PARTIAL_SUCCESS", + "totalCount": 10, + "successCount": 8, + "failureCount": 2, + "createdWorkOrderIds": ["WO-20260806-001", "WO-20260806-002"] + } +} +``` + +##### IMP-05/IMP-06 批次列表与详情 + +`GET /risk/import-batches?pageNum=1&pageSize=20&batchNo=20260813-B01&status=CONFIRMED&importedFrom=2026-08-01&importedTo=2026-08-31` 无 body。 + +列表 `rows` 和详情 `data` 统一使用: + +```json +{ + "batchId": "IB-20260813-001", + "batchNo": "20260813-B01", + "fileId": "FILE-001", + "fileName": "12398工单.xlsx", + "templateVersion": "2026.08", + "status": "PARTIAL_SUCCESS", + "totalCount": 10, + "successCount": 8, + "failureCount": 2, + "importedBy": { "userId": "1001", "name": "服务监督主管" }, + "importedAt": "2026-08-13T10:20:00+08:00" +} +``` + +### 8.7 工单与流程查询接口请求与响应字段定义 + +#### 8.7.1 请求字段定义 + +| 接口 | 字段位置 | 字段 | 类型 | 必填 | 定义 | +| --- | --- | --- | --- | :---: | --- | +| WO-01 | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| WO-01 | query | `pageSize` | integer | 是 | 每页数量 | +| WO-01 | query | `workOrderNo12398` | string | 否 | 12398 工单号 | +| WO-01 | query | `currentStage` | enum | 否 | 当前流程环节 | +| WO-01 | query | `orderStatus` | enum | 否 | 工单状态 | +| WO-01 | query | `sourceCode` | string | 否 | 信息来源编码 | +| WO-01 | query | `calledFrom`/`calledTo` | datetime | 否 | 客户致电 12398 时间范围 | +| WO-01 | query | `transferredFrom`/`transferredTo` | datetime | 否 | 工单转入时间范围 | +| WO-01 | query | `customerName` | string | 否 | 客户名称模糊查询 | +| WO-01 | query | `contactPhone` | string | 否 | 联系电话精确或后四位查询,须受敏感数据权限控制 | +| WO-01 | query | `customerAddress` | string | 否 | 客户地址模糊查询 | +| WO-01 | query | `regionCode` | string | 否 | 区域编码 | +| WO-01 | query | `provinceCode` | string | 否 | 省份编码 | +| WO-01 | query | `eventTypeCode` | string | 否 | 事件类型编码 | +| WO-01 | query | `transferCategoryCode` | string | 否 | 转办事项分类编码 | +| WO-01 | query | `problemCategoryCode` | string | 否 | 问题分类编码 | +| WO-01 | query | `responsibilityType` | enum | 否 | `PRIMARY` 主责或 `ASSIST` 协查 | +| WO-01 | query | `orderByColumn` | string | 否 | 排序字段,仅允许服务端白名单字段 | +| WO-01 | query | `isAsc` | enum | 否 | `asc` 或 `desc` | +| WO-02 | path | `workOrderId` | string | 是 | 工单标识 | +| WO-03 | path | `workOrderId` | string | 是 | 工单标识 | +| WO-03 | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| WO-03 | query | `pageSize` | integer | 是 | 每页数量 | +| WO-04 | path | `workOrderId` | string | 是 | 工单标识 | +| WO-05 | path | `workOrderId` | string | 是 | 工单标识 | +| WO-05 | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| WO-05 | query | `pageSize` | integer | 是 | 每页数量 | +| WO-06 | path | `workOrderId` | string | 是 | 工单标识 | + +WO-01~WO-06 均无请求体,数据范围由当前用户身份和权限决定。 + +#### 8.7.2 响应字段定义 + +| 接口 | 响应字段 | 类型 | 定义 | +| --- | --- | --- | --- | +| WO-01 | `total` | integer | 符合查询条件且当前用户有权查看的工单总数 | +| WO-01 | `rows[]` | object[] | 当前页工单摘要 | +| WO-01 | `rows[].workOrderId` | string | 工单标识 | +| WO-01 | `rows[].workOrderNo12398` | string | 12398 工单号 | +| WO-01 | `rows[].orderStatus` | enum | 工单状态 | +| WO-01 | `rows[].currentStage` | enum | 当前流程环节编码 | +| WO-01 | `rows[].currentStageName` | string | 当前环节显示名称 | +| WO-01 | `rows[].sourceCode`/`sourceName` | string | 信息来源编码及名称 | +| WO-01 | `rows[].customerCalledAt` | datetime | 客户致电时间 | +| WO-01 | `rows[].transferredAt` | datetime | 工单转入时间 | +| WO-01 | `rows[].customerName` | string | 客户名称,按权限脱敏 | +| WO-01 | `rows[].contactPhoneMasked` | string | 脱敏联系电话 | +| WO-01 | `rows[].customerAddressMasked` | string | 脱敏地址 | +| WO-01 | `rows[].responsibilityType` | enum/null | 派工前为空,派工后为主责或协查 | +| WO-01 | `rows[].version` | integer | 工单乐观锁版本 | +| WO-01 | `rows[].allowedActions[]` | string[] | 当前用户在该工单上的允许动作 | +| WO-02 | `data.workOrderId`/`workOrderNo12398` | string | 工单标识及 12398 工单号 | +| WO-02 | `data.orderStatus`/`currentStage` | enum | 工单状态及当前环节 | +| WO-02 | `data.responsibilityType` | enum/null | 主责/协查,派工前为空 | +| WO-02 | `data.sourceCode` | string | 信息来源编码 | +| WO-02 | `data.customerCalledAt`/`transferredAt` | datetime | 客户致电及转入时间 | +| WO-02 | `data.customer` | object | 客户信息对象 | +| WO-02 | `data.customer.name` | string | 客户名称 | +| WO-02 | `data.customer.contactPhone` | string | 联系电话,按权限脱敏 | +| WO-02 | `data.customer.address` | string | 地址,按权限脱敏 | +| WO-02 | `data.customer.regionCode`/`provinceCode` | string | 区域及省份编码 | +| WO-02 | `data.classification` | object | 事件、事项和问题分类编码集合 | +| WO-02 | `data.version` | integer | 工单版本 | +| WO-02 | `data.allowedActions[]` | string[] | 当前用户允许动作 | +| WO-03 | `total` | integer | 关联 95598 记录总数 | +| WO-03 | `rows[]` | object[] | 95598 业务记录 | +| WO-03 | `rows[].serviceRecordId` | string | 记录标识 | +| WO-03 | `rows[].serviceOrderNo95598` | string | 95598 工单号 | +| WO-03 | `rows[].acceptanceContent` | string | 受理内容 | +| WO-03 | `rows[].businessTypeCode` | string | 业务类型编码 | +| WO-03 | `rows[].acceptedAt` | datetime | 受理时间 | +| WO-03 | `rows[].powerSupplyOrg` | object | 供电单位,包含 `id`、`name` | +| WO-03 | `rows[].recording` | object/null | 录音信息,包含 `fileId`、`durationSeconds`、`playable` | +| WO-03 | `rows[].transcriptText` | string/null | 录音转译文本 | +| WO-04 | `data.processInstanceId` | string | 流程实例标识 | +| WO-04 | `data.processVersion` | integer | 流程定义/实例版本 | +| WO-04 | `data.responsibilityType` | enum | 主责或协查 | +| WO-04 | `data.currentStage` | enum | 当前环节 | +| WO-04 | `data.workOrderVersion` | integer | 工单版本 | +| WO-04 | `data.nodes[]` | object[] | 流程节点集合 | +| WO-04 | `data.nodes[].stage`/`status` | enum | 节点编码及状态 | +| WO-04 | `data.nodes[].completedAt` | datetime/null | 节点完成时间 | +| WO-04 | `data.nodes[].completedCount`/`totalCount` | integer/null | 并行节点完成数及总数 | +| WO-04 | `data.nodes[].submitReady` | boolean/null | 当前节点是否满足提交条件 | +| WO-04 | `data.deadlineAt` | datetime | 当前时限截止时间 | +| WO-04 | `data.overdue` | boolean | 是否逾期 | +| WO-05 | `total` | integer | 轨迹事件总数 | +| WO-05 | `rows[]` | object[] | 当前页轨迹事件 | +| WO-05 | `rows[].eventId`/`eventType` | string | 事件标识及类型 | +| WO-05 | `rows[].stage` | enum | 发生事件的环节 | +| WO-05 | `rows[].actor` | object | 操作人,包含 `userId`、`name`、`role` | +| WO-05 | `rows[].occurredAt` | datetime | 发生时间 | +| WO-05 | `rows[].fromStatus`/`toStatus` | enum/null | 变更前后状态 | +| WO-05 | `rows[].summary` | string | 事件摘要 | +| WO-06 | `data.workOrderId` | string | 工单标识 | +| WO-06 | `data.workOrderVersion` | integer | 当前工单版本 | +| WO-06 | `data.actions[]` | object[] | 动作集合 | +| WO-06 | `data.actions[].code` | string | 动作编码 | +| WO-06 | `data.actions[].enabled` | boolean | 是否允许执行 | +| WO-06 | `data.actions[].reasonCode` | string/null | 禁止执行时的稳定原因码 | + +#### 8.7.3 请求与响应示例 + +##### WO-01 工单列表 + +```http +GET /risk/work-orders?pageNum=1&pageSize=20&workOrderNo12398=2026-08-06-001¤tStage=DISPATCH&customerName=某某&orderByColumn=transferredAt&isAsc=desc +``` + +无 body。响应: + +```json +{ + "total": 1, + "rows": [ + { + "workOrderId": "WO-20260806-001", + "workOrderNo12398": "2026-08-06-001", + "orderStatus": "PROCESSING", + "currentStage": "DISPATCH", + "currentStageName": "待分配派工", + "sourceCode": "12398_PHONE", + "sourceName": "12398 电话", + "customerCalledAt": "2026-08-06T09:15:00+08:00", + "transferredAt": "2026-08-06T11:00:00+08:00", + "customerName": "某某有限公司", + "contactPhoneMasked": "138****8888", + "customerAddressMasked": "XX市XX区XX路88号", + "responsibilityType": null, + "version": 3, + "allowedActions": ["VIEW", "DISPATCH"] + } + ], + "code": 200, + "msg": "查询成功" +} +``` + +##### WO-02 工单详情 + +`GET /risk/work-orders/{workOrderId}` 无 body。 + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "workOrderId": "WO-20260806-001", + "workOrderNo12398": "2026-08-06-001", + "orderStatus": "PROCESSING", + "currentStage": "INVESTIGATION", + "responsibilityType": "PRIMARY", + "sourceCode": "12398_PHONE", + "customerCalledAt": "2026-08-06T09:15:00+08:00", + "transferredAt": "2026-08-06T11:00:00+08:00", + "customer": { + "name": "某某有限公司", + "contactPhone": "138****8888", + "address": "XX市XX区XX路88号", + "regionCode": "120101", + "provinceCode": "120000" + }, + "classification": { + "eventTypeCode": "COMPLAINT", + "transferCategoryCode": "COMPLAINT_ITEM", + "problemCategoryCode": "SAFETY_RISK" + }, + "version": 7, + "allowedActions": ["VIEW", "SAVE_INVESTIGATION_DRAFT"] + } +} +``` + +##### WO-03 95598 记录 + +`GET /risk/work-orders/{workOrderId}/service-records?pageNum=1&pageSize=20` 无 body,返回 `TableDataInfo`: + +```json +{ + "serviceRecordId": "SR-001", + "serviceOrderNo95598": "95598-20260806-001", + "acceptanceContent": "客户反映近期电压不稳,设备频繁重启。", + "businessTypeCode": "COMPLAINT", + "acceptedAt": "2026-08-06T08:45:00+08:00", + "powerSupplyOrg": { "id": "ORG-1200", "name": "国网天津市电力公司" }, + "recording": { + "fileId": "FILE-AUDIO-001", + "durationSeconds": 384, + "playable": true + }, + "transcriptText": "客户反映近期电压不稳……" +} +``` + +##### WO-04 流程视图 + +`GET /risk/work-orders/{workOrderId}/process` 无 body。 + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "processInstanceId": "PI-001", + "processVersion": 1, + "responsibilityType": "PRIMARY", + "currentStage": "INVESTIGATION", + "workOrderVersion": 7, + "nodes": [ + { "stage": "DISPATCH", "status": "COMPLETED", "completedAt": "2026-08-06T12:00:00+08:00" }, + { "stage": "FIRST_CONTACT", "status": "COMPLETED", "completedAt": "2026-08-06T14:32:00+08:00" }, + { "stage": "PRELIMINARY_INVESTIGATION", "status": "IN_PROGRESS", "completedCount": 1, "totalCount": 3 }, + { "stage": "INVESTIGATION", "status": "IN_PROGRESS", "submitReady": false } + ], + "deadlineAt": "2026-08-08T18:00:00+08:00", + "overdue": false + } +} +``` + +##### WO-05/WO-06 时间线与允许动作 + +`GET /risk/work-orders/{workOrderId}/timeline?pageNum=1&pageSize=50` 返回分页事件;`GET /risk/work-orders/{workOrderId}/allowed-actions` 返回动作集合。 + +```json +{ + "eventId": "EVT-001", + "eventType": "DISPATCHED", + "stage": "DISPATCH", + "actor": { "userId": "1001", "name": "服务监督主管", "role": "SERVICE_SUPERVISOR" }, + "occurredAt": "2026-08-06T12:00:00+08:00", + "fromStatus": "PENDING", + "toStatus": "COMPLETED", + "summary": "判定主责并完成派工" +} +``` + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "workOrderId": "WO-20260806-001", + "workOrderVersion": 7, + "actions": [ + { "code": "SAVE_INVESTIGATION_DRAFT", "enabled": true }, + { "code": "SUBMIT_INVESTIGATION", "enabled": false, "reasonCode": "PREREQUISITES_INCOMPLETE" } + ] + } +} +``` + +### 8.8 待办与派工接口请求与响应字段定义 + +#### 8.8.1 请求字段定义 + +| 接口 | 字段位置 | 字段 | 类型 | 必填 | 定义 | +| --- | --- | --- | --- | :---: | --- | +| TASK-01 | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| TASK-01 | query | `pageSize` | integer | 是 | 每页数量 | +| TASK-01 | query | `stage` | enum | 否 | 任务环节 | +| TASK-01 | query | `status` | enum | 否 | 任务状态 | +| TASK-01 | query | `workOrderNo12398` | string | 否 | 12398 工单号 | +| TASK-01 | query | `customerName` | string | 否 | 客户名称 | +| TASK-02 | query | `workOrderNo12398` | string | 否 | 与任务列表相同口径的工单号条件 | +| TASK-02 | query | `customerName` | string | 否 | 与任务列表相同口径的客户条件 | +| TASK-02 | query | `status` | enum | 否 | 需要统计的任务状态 | +| TASK-03 | path | `taskId` | string | 是 | 任务标识 | +| DSP-01 | path | `id` | string | 是 | 工单标识 | +| DSP-01 | query | `responsibilityType` | enum | 是 | 用于筛选候选项;`PRIMARY` 或 `ASSIST` | +| DSP-02 | path | `id` | string | 是 | 工单标识 | +| DSP-02 | body | `workOrderVersion` | integer | 是 | 工单当前版本 | +| DSP-02 | body | `responsibilityType` | enum | 是 | 拟判定责任类型 | +| DSP-03 | path | `id` | string | 是 | 工单标识 | +| DSP-03 | body | `responsibilityType` | enum | 是 | 服务监督主管判定的 `PRIMARY` 或 `ASSIST` | +| DSP-03 | body | `preliminaryDeptIds[]` | string[] | 是 | 初查部门,至少 1 个且不可重复 | +| DSP-03 | body | `firstContactDeptId` | string/null | 条件 | 主责必填;协查必须为空 | +| DSP-03 | body | `serviceOfficerUserId` | string | 是 | 承办服务监督员用户标识 | +| DSP-03 | body | `escalationDeptIds[]` | string[] | 否 | 督办/协同部门标识集合 | +| DSP-03 | body | `dispatchMode` | enum | 是 | `MANUAL` 或 `SMART_CONFIRMED` | +| DSP-03 | body | `suggestionId` | string/null | 条件 | 采用智能建议时必填,人工派工为空 | +| DSP-03 | body | `workOrderVersion` | integer | 是 | 工单版本,用于并发校验 | +| DSP-03 | body | `requestId` | string | 是 | 幂等请求标识 | +| DSP-04 | path | `taskId` | string | 是 | 待分派任务标识 | +| DSP-04 | body | `assigneeType` | enum | 是 | `USER` 或 `POST` | +| DSP-04 | body | `assigneeUserId` | string/null | 条件 | `assigneeType=USER` 时必填 | +| DSP-04 | body | `assigneePostId` | string/null | 条件 | `assigneeType=POST` 时必填 | +| DSP-04 | body | `taskVersion` | integer | 是 | 任务版本 | +| DSP-04 | body | `requestId` | string | 是 | 幂等请求标识 | +| DSP-05 | path | `id` | string | 是 | 工单标识 | +| DSP-05 | body | `reviewTaskId` | string | 是 | 当前审核任务标识 | +| DSP-05 | body | `callbackDeptId` | string | 是 | 主责工单回访承接部门 | +| DSP-05 | body | `workOrderVersion` | integer | 是 | 工单版本 | +| DSP-05 | body | `requestId` | string | 是 | 幂等请求标识 | + +TASK-01、TASK-02、TASK-03 和 DSP-01 均无请求体。 + +#### 8.8.2 响应字段定义 + +| 接口 | 响应字段 | 类型 | 定义 | +| --- | --- | --- | --- | +| TASK-01 | `total` | integer | 当前用户符合条件的任务总数 | +| TASK-01 | `rows[]` | object[] | 当前页任务摘要 | +| TASK-01 | `rows[].taskId` | string | 任务标识 | +| TASK-01 | `rows[].workOrderId`/`workOrderNo12398` | string | 工单标识及 12398 工单号 | +| TASK-01 | `rows[].stage`/`stageName` | string | 环节编码及名称 | +| TASK-01 | `rows[].status` | enum | 任务状态 | +| TASK-01 | `rows[].assignee` | object/null | 承办人或岗位摘要 | +| TASK-01 | `rows[].deadlineAt` | datetime/null | 截止时间 | +| TASK-01 | `rows[].version` | integer | 任务版本 | +| TASK-01 | `rows[].allowedActions[]` | string[] | 当前用户允许动作 | +| TASK-02 | `data.total` | integer | 与当前过滤条件一致的任务总数 | +| TASK-02 | `data.counts[]` | object[] | 按环节统计集合 | +| TASK-02 | `data.counts[].stage`/`name` | string | 环节编码及名称 | +| TASK-02 | `data.counts[].count` | integer | 环节任务数量 | +| TASK-02 | `data.filterFingerprint` | string | 统计条件指纹,用于核对列表与卡片口径 | +| TASK-03 | `data.task` | object | 当前任务,至少包含 `taskId`、`stage`、`status`、`version` | +| TASK-03 | `data.workOrder` | object | 工单摘要及 `responsibilityType`、`version` | +| TASK-03 | `data.form` | object/null | 当前表单类型、草稿版本和保存时间 | +| TASK-03 | `data.allowedActions[]` | string[] | 当前任务允许动作 | +| DSP-01 | `data.preliminaryDepartments[]` | object[] | 可选初查部门,包含 `deptId`、`deptName`、`enabled` | +| DSP-01 | `data.firstContactDepartments[]` | object[] | 主责可选首联部门;协查返回空数组 | +| DSP-01 | `data.serviceOfficers[]` | object[] | 可选服务监督员,包含用户和部门标识 | +| DSP-01 | `data.escalationDepartments[]` | object[] | 可选督办/协同部门 | +| DSP-01 | `data.workOrderVersion` | integer | 当前工单版本 | +| DSP-02 | `data.suggestionId` | string | 智能建议标识 | +| DSP-02 | `data.ruleVersion` | integer | 命中的规则版本 | +| DSP-02 | `data.responsibilityType` | enum | 建议对应的责任类型 | +| DSP-02 | `data.preliminaryDeptIds[]` | string[] | 建议初查部门 | +| DSP-02 | `data.firstContactDeptId` | string/null | 建议首联部门,协查为空 | +| DSP-02 | `data.serviceOfficerUserId` | string | 建议服务监督员 | +| DSP-02 | `data.escalationDeptIds[]` | string[] | 建议督办部门 | +| DSP-02 | `data.explanations[]` | string[] | 规则命中说明,不作为最终派工事实 | +| DSP-03 | `data.dispatchId` | string | 派工记录标识 | +| DSP-03 | `data.workOrderId` | string | 工单标识 | +| DSP-03 | `data.responsibilityType` | enum | 已持久化的责任类型 | +| DSP-03 | `data.currentStage` | enum | 派工后的当前环节;主责与协查取值不同 | +| DSP-03 | `data.workOrderVersion` | integer | 更新后的工单版本 | +| DSP-03 | `data.createdTasks[]` | object[] | 本次派工生成的任务集合 | +| DSP-03 | `data.createdTasks[].taskId` | string | 新任务标识 | +| DSP-03 | `data.createdTasks[].stage`/`status` | enum | 新任务环节及状态 | +| DSP-03 | `data.createdTasks[].deptId` | string/null | 部门任务的承接部门 | +| DSP-03 | `data.createdTasks[].submitReady` | boolean/null | 调查任务当前是否可提交 | +| DSP-04 | `data.taskId` | string | 被分派任务标识 | +| DSP-04 | `data.assignee` | object | 新承办对象 | +| DSP-04 | `data.assignedBy` | object | 分派操作人 | +| DSP-04 | `data.assignedAt` | datetime | 分派时间 | +| DSP-04 | `data.taskVersion` | integer | 更新后的任务版本 | +| DSP-05 | `data.workOrderId` | string | 工单标识 | +| DSP-05 | `data.callbackDeptId` | string | 已确定的回访承接部门 | +| DSP-05 | `data.workOrderVersion` | integer | 更新后的工单版本 | + +#### 8.8.3 请求与响应示例 + +##### TASK-01/TASK-02/TASK-03 + +`GET /risk/tasks?pageNum=1&pageSize=20&stage=INVESTIGATION&status=IN_PROGRESS&workOrderNo12398=2026-08-06-001` 无 body。 + +```json +{ + "total": 1, + "rows": [ + { + "taskId": "TASK-INV-001", + "workOrderId": "WO-20260806-001", + "workOrderNo12398": "2026-08-06-001", + "stage": "INVESTIGATION", + "stageName": "调查结果填报", + "status": "IN_PROGRESS", + "assignee": { "type": "USER", "id": "1002", "name": "张某" }, + "deadlineAt": "2026-08-08T18:00:00+08:00", + "version": 2, + "allowedActions": ["VIEW", "SAVE_DRAFT"] + } + ], + "code": 200, + "msg": "查询成功" +} +``` + +`GET /risk/tasks/summary?workOrderNo12398=&customerName=` 无 body: + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "total": 103, + "counts": [ + { "stage": "INVESTIGATION", "name": "调查结果填报", "count": 34 }, + { "stage": "REPORT", "name": "调查报告编写", "count": 12 } + ], + "filterFingerprint": "sha256:62e8..." + } +} +``` + +`GET /risk/tasks/{taskId}` 返回工单摘要、任务、允许动作及当前表单版本: + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "task": { "taskId": "TASK-INV-001", "stage": "INVESTIGATION", "status": "IN_PROGRESS", "version": 2 }, + "workOrder": { "workOrderId": "WO-20260806-001", "workOrderNo12398": "2026-08-06-001", "responsibilityType": "PRIMARY", "version": 7 }, + "form": { "formType": "INVESTIGATION", "draftVersion": 3, "lastSavedAt": "2026-08-13T10:10:00+08:00" }, + "allowedActions": ["SAVE_DRAFT"] + } +} +``` + +##### DSP-01 派工候选 + +`GET /risk/work-orders/{id}/dispatch-candidates?responsibilityType=PRIMARY` 无 body。 + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "preliminaryDepartments": [{ "deptId": "D-OPS", "deptName": "运营部", "enabled": true }], + "firstContactDepartments": [{ "deptId": "D-CS1", "deptName": "客服一部", "enabled": true }], + "serviceOfficers": [{ "userId": "1002", "name": "张某", "deptId": "D-SERVICE", "enabled": true }], + "escalationDepartments": [{ "deptId": "D-BIZ", "deptName": "业务部", "enabled": true }], + "workOrderVersion": 3 + } +} +``` + +##### DSP-02 智能派工建议 + +```json +{ + "workOrderVersion": 3, + "responsibilityType": "PRIMARY" +} +``` + +```json +{ + "code": 200, + "msg": "建议生成成功", + "data": { + "suggestionId": "DS-001", + "ruleVersion": 5, + "responsibilityType": "PRIMARY", + "preliminaryDeptIds": ["D-OPS"], + "firstContactDeptId": "D-CS1", + "serviceOfficerUserId": "1002", + "escalationDeptIds": [], + "explanations": ["问题分类为安全隐患,命中运营部初查规则"] + } +} +``` + +##### DSP-03 服务监督主管确认派工 + +主责请求体示例: + +```json +{ + "responsibilityType": "PRIMARY", + "preliminaryDeptIds": ["D-OPS", "D-BIZ"], + "firstContactDeptId": "D-CS1", + "serviceOfficerUserId": "1002", + "escalationDeptIds": [], + "dispatchMode": "MANUAL", + "suggestionId": null, + "workOrderVersion": 3, + "requestId": "04d7de39-3e48-4b1e-b327-e0048678ac6b" +} +``` + +协查请求体必须将 `firstContactDeptId` 设为 `null`: + +```json +{ + "responsibilityType": "ASSIST", + "preliminaryDeptIds": ["D-OPS", "D-BIZ"], + "firstContactDeptId": null, + "serviceOfficerUserId": "1002", + "escalationDeptIds": [], + "dispatchMode": "MANUAL", + "suggestionId": null, + "workOrderVersion": 3, + "requestId": "bc14ae85-cebd-4784-a30a-b42a56419ee5" +} +``` + +```json +{ + "code": 200, + "msg": "派工成功", + "data": { + "dispatchId": "DISP-001", + "workOrderId": "WO-20260806-001", + "responsibilityType": "PRIMARY", + "currentStage": "FIRST_CONTACT_AND_PRELIMINARY_INVESTIGATION", + "workOrderVersion": 4, + "createdTasks": [ + { "taskId": "TASK-FC-001", "stage": "FIRST_CONTACT_ASSIGN", "status": "PENDING" }, + { "taskId": "TASK-PI-001", "stage": "PRELIMINARY_INVESTIGATION", "status": "PENDING", "deptId": "D-OPS" }, + { "taskId": "TASK-PI-002", "stage": "PRELIMINARY_INVESTIGATION", "status": "PENDING", "deptId": "D-BIZ" }, + { "taskId": "TASK-INV-001", "stage": "INVESTIGATION", "status": "IN_PROGRESS", "submitReady": false } + ] + } +} +``` + +##### DSP-04 班组/人员分派 + +```json +{ + "assigneeType": "USER", + "assigneeUserId": "2001", + "assigneePostId": null, + "taskVersion": 1, + "requestId": "20cdb204-dc60-4182-bb9c-b6a1b5415c35" +} +``` + +响应采用 8.4.5 通用状态变更响应,并返回新的 `assignee`、`assignedBy`、`assignedAt`。 + +##### DSP-05 回访承接部门 + +```json +{ + "reviewTaskId": "TASK-BR-001", + "callbackDeptId": "D-CS1", + "workOrderVersion": 10, + "requestId": "3197100b-1508-49c0-9d10-74459badbda2" +} +``` + +```json +{ + "code": 200, + "msg": "回访承接部门已确定", + "data": { + "workOrderId": "WO-20260806-001", + "callbackDeptId": "D-CS1", + "workOrderVersion": 11 + } +} +``` + +仅 `PRIMARY` 工单允许调用;具体由一级还是二级审核调用仍属于 D-03 待确认项。 + +### 8.9 办理、审核与回访接口请求与响应字段定义 + +#### 8.9.1 请求字段定义 + +| 接口 | 字段位置 | 字段 | 类型 | 必填 | 定义 | +| --- | --- | --- | --- | :---: | --- | +| ACT-01 | path | `taskId` | string | 是 | 当前办理任务标识 | +| ACT-01 | body | `formType` | enum | 是 | 表单类型,如 `INVESTIGATION` | +| ACT-01 | body | `formData` | object | 是 | 草稿业务字段;结构必须与对应正式提交接口一致,可部分填写 | +| ACT-01 | body | `draftVersion` | integer | 是 | 草稿版本;首次保存传 0 | +| ACT-01 | body | `taskVersion` | integer | 是 | 任务版本 | +| ACT-02 | path | `taskId` | string | 是 | 首联任务标识 | +| ACT-02 | body | `firstContactAt` | datetime | 是 | 实际首联时间,不得晚于提交时间 | +| ACT-02 | body | `firstContactOrderNo` | string | 是 | 首联关联工单号 | +| ACT-02 | body | `firstContactContent` | string | 是 | 首联内容 | +| ACT-02 | body | `taskVersion` | integer | 是 | 任务版本 | +| ACT-02 | body | `requestId` | string | 是 | 幂等请求标识 | +| ACT-03 | path | `taskId` | string | 是 | 初查任务标识 | +| ACT-03 | body | `preliminaryContent` | string | 是 | 本部门初查结论和事实说明 | +| ACT-03 | body | `attachmentFileIds[]` | string[] | 否 | 已上传且有权绑定的初查附件 | +| ACT-03 | body | `taskVersion` | integer | 是 | 任务版本 | +| ACT-03 | body | `requestId` | string | 是 | 幂等请求标识 | +| ACT-04 | path | `taskId` | string | 是 | 全面调查任务标识;无请求体 | +| ACT-05 | path | `taskId` | string | 是 | 全面调查任务标识 | +| ACT-05 | body | `acceptedContent12398` | string | 是 | 12398 受理内容 | +| ACT-05 | body | `externalExposureRisk` | boolean | 是 | 是否存在外部曝光风险 | +| ACT-05 | body | `internalAppealIssued` | boolean | 是 | 是否已下发内部诉求 | +| ACT-05 | body | `customerAppealSummary` | string | 是 | 客户诉求摘要 | +| ACT-05 | body | `escalationReason` | string | 否 | 升级/督办原因 | +| ACT-05 | body | `investigationProcess` | string | 是 | 调查过程 | +| ACT-05 | body | `localResponsibility` | boolean | 是 | 是否属于本单位责任 | +| ACT-05 | body | `handlingSituation` | string | 是 | 当前处理情况 | +| ACT-05 | body | `accountable` | boolean | 是 | 是否需要问责 | +| ACT-05 | body | `hasHandlingBasis` | boolean | 是 | 是否存在处理依据 | +| ACT-05 | body | `handlingMethod` | string | 是 | 处理方式 | +| ACT-05 | body | `handlingResult` | string | 是 | 处理结果 | +| ACT-05 | body | `investigationSummary` | string | 是 | 调查结论摘要 | +| ACT-05 | body | `draftVersion` | integer | 是 | 本次提交依据的草稿版本 | +| ACT-05 | body | `taskVersion` | integer | 是 | 任务版本 | +| ACT-05 | body | `workOrderVersion` | integer | 是 | 工单版本 | +| ACT-05 | body | `requestId` | string | 是 | 幂等请求标识 | +| ACT-06 | path | `taskId` | string | 是 | 服务监督主管审核任务标识 | +| ACT-06 | body | `result` | enum | 是 | `APPROVED` 或 `REJECTED` | +| ACT-06 | body | `rejectReason` | string/null | 条件 | 驳回时必填,通过时为空 | +| ACT-06 | body | `investigationVersion` | integer | 是 | 审核所依据的调查版本 | +| ACT-06 | body | `taskVersion` | integer | 是 | 审核任务版本 | +| ACT-06 | body | `workOrderVersion` | integer | 是 | 工单版本 | +| ACT-06 | body | `requestId` | string | 是 | 幂等请求标识 | +| ACT-07 | path | `taskId` | string | 是 | 业务部主任审核任务标识 | +| ACT-07 | body | `result` | enum | 是 | `APPROVED` 或 `REJECTED` | +| ACT-07 | body | `rejectReason` | string/null | 条件 | 驳回时必填 | +| ACT-07 | body | `callbackDeptId` | string/null | 条件 | 若 D-03 确认为二级审核选部门,则主责通过时必填;协查必须为空 | +| ACT-07 | body | `investigationVersion` | integer | 是 | 审核依据的调查版本 | +| ACT-07 | body | `taskVersion` | integer | 是 | 审核任务版本 | +| ACT-07 | body | `workOrderVersion` | integer | 是 | 工单版本 | +| ACT-07 | body | `requestId` | string | 是 | 幂等请求标识 | +| ACT-08 | path | `taskId` | string | 是 | 主责工单的班组回访任务标识 | +| ACT-08 | body | `contactedAt` | datetime | 是 | 联系客户时间 | +| ACT-08 | body | `contactPhone` | string | 是 | 实际联系号码,敏感字段 | +| ACT-08 | body | `contactResult` | enum | 是 | 联系结果,如 `CONNECTED`、`NOT_CONNECTED` | +| ACT-08 | body | `contactSummary` | string | 是 | 回访执行摘要 | +| ACT-08 | body | `taskVersion` | integer | 是 | 任务版本 | +| ACT-08 | body | `requestId` | string | 是 | 幂等请求标识 | +| ACT-09 | path | `taskId` | string | 是 | 服务监督员回访结果任务标识 | +| ACT-09 | body | `respondentName` | string | 是 | 回复人姓名 | +| ACT-09 | body | `repliedAt` | datetime | 是 | 回复时间 | +| ACT-09 | body | `replyPhone` | string | 是 | 回复联系电话,敏感字段 | +| ACT-09 | body | `replySituation` | string | 是 | 回复情况 | +| ACT-09 | body | `callbackResult` | enum | 是 | `AGREE` 或 `DISAGREE_OR_CUSTOMER_REQUEST` | +| ACT-09 | body | `responseLetterFileIds[]` | string[] | 条件 | 不同意或客户主动要求时至少 1 个 | +| ACT-09 | body | `deliveryProofFileIds[]` | string[] | 条件 | 不同意或客户主动要求时至少 1 个 | +| ACT-09 | body | `taskVersion` | integer | 是 | 任务版本 | +| ACT-09 | body | `workOrderVersion` | integer | 是 | 工单版本 | +| ACT-09 | body | `requestId` | string | 是 | 幂等请求标识 | + +#### 8.9.2 响应字段定义 + +除下列业务字段外,状态变更接口同时返回 8.4.5 定义的任务状态、当前环节、工单版本、任务版本和后续任务。 + +| 接口 | 响应字段 | 类型 | 定义 | +| --- | --- | --- | --- | +| ACT-01 | `data.taskId` | string | 草稿所属任务 | +| ACT-01 | `data.draftVersion` | integer | 保存后的草稿版本 | +| ACT-01 | `data.taskVersion` | integer | 更新后的任务版本 | +| ACT-01 | `data.savedAt` | datetime | 服务端保存时间 | +| ACT-01 | `data.submitReady` | boolean | 当前是否满足正式提交条件 | +| ACT-02 | `data.firstContactId` | string | 不可变首联记录标识 | +| ACT-02 | `data.taskStatus` | enum | 首联任务完成后的状态 | +| ACT-02 | `data.workOrderVersion`/`taskVersion` | integer | 更新后的版本 | +| ACT-02 | `data.nextTasks[]` | object[] | 因首联完成而更新或生成的任务 | +| ACT-03 | `data.preliminaryInvestigationId` | string | 不可变初查记录标识 | +| ACT-03 | `data.taskId`/`taskStatus` | string/enum | 初查任务及完成状态 | +| ACT-03 | `data.completedCount`/`totalCount` | integer | 并行初查完成数及总数 | +| ACT-03 | `data.investigationSubmitReady` | boolean | 全面调查是否满足提交前置条件 | +| ACT-03 | `data.workOrderVersion`/`taskVersion` | integer | 更新后的版本 | +| ACT-04 | `data.taskId` | string | 全面调查任务标识 | +| ACT-04 | `data.taskVersion`/`workOrderVersion` | integer | 当前任务及工单版本 | +| ACT-04 | `data.responsibilityType` | enum | 主责或协查 | +| ACT-04 | `data.firstContact` | object/null | 主责首联结果;协查固定为空 | +| ACT-04 | `data.preliminaryProgress` | object | 并行初查完成数、总数及任务明细 | +| ACT-04 | `data.preliminaryProgress.items[]` | object[] | 各部门初查任务的 `taskId`、部门和状态 | +| ACT-04 | `data.draft` | object/null | 草稿版本及表单数据 | +| ACT-04 | `data.submitReady` | boolean | 是否允许提交全面调查 | +| ACT-04 | `data.incompletePrerequisites[]` | string[] | 未完成的前置任务标识 | +| ACT-05 | `data.investigationId` | string | 不可变调查记录标识 | +| ACT-05 | `data.investigationVersion` | integer | 本次形成的调查版本 | +| ACT-05 | `data.currentStage` | enum | 提交后进入 `SUPERVISOR_REVIEW` | +| ACT-05 | `data.nextTasks[]` | object[] | 新建的服务监督主管审核任务 | +| ACT-06 | `data.reviewId` | string | 审核记录标识 | +| ACT-06 | `data.reviewLevel` | enum | 固定为 `SUPERVISOR` | +| ACT-06 | `data.reviewResult` | enum | 审核结果 | +| ACT-06 | `data.currentStage` | enum | 通过进入业务审核,驳回回到调查 | +| ACT-06 | `data.nextTasks[]` | object[] | 业务审核或返工任务 | +| ACT-07 | `data.reviewId` | string | 业务审核记录标识 | +| ACT-07 | `data.responsibilityType` | enum | 服务端从派工记录读取的责任类型 | +| ACT-07 | `data.currentStage` | enum | 主责通过为 `CALLBACK_ASSIGN`;协查通过为 `REPORT`;驳回为 `INVESTIGATION` | +| ACT-07 | `data.nextTasks[]` | object[] | 按唯一业务分支生成的任务 | +| ACT-08 | `data.callbackExecutionId` | string | 班组回访执行记录标识 | +| ACT-08 | `data.taskStatus` | enum | 回访执行任务状态 | +| ACT-08 | `data.nextTasks[]` | object[] | 后续服务监督员回访结果任务 | +| ACT-09 | `data.callbackResultId` | string | 回访结果记录标识 | +| ACT-09 | `data.callbackResult` | enum | 已保存的回访结果 | +| ACT-09 | `data.currentStage` | enum | 成功后进入 `REPORT` | +| ACT-09 | `data.nextTasks[]` | object[] | 新建调查报告任务 | + +#### 8.9.3 请求与响应示例 + +##### ACT-01 保存草稿 + +```json +{ + "formType": "INVESTIGATION", + "formData": { + "investigationSummary": "经初查,台区监测记录存在短时电压波动。", + "customerAppealSummary": "客户要求核查原因并恢复稳定供电。", + "investigationProcess": "已调取历史记录并组织现场检查。" + }, + "draftVersion": 3, + "taskVersion": 2 +} +``` + +```json +{ + "code": 200, + "msg": "草稿已保存", + "data": { + "taskId": "TASK-INV-001", + "draftVersion": 4, + "taskVersion": 3, + "savedAt": "2026-08-13T10:30:00+08:00", + "submitReady": false + } +} +``` + +##### ACT-02 提交首联 + +```json +{ + "firstContactAt": "2026-08-12T14:32:00+08:00", + "firstContactOrderNo": "2026-08-06-001", + "firstContactContent": "已联系客户核实情况并告知后续调查安排。", + "taskVersion": 1, + "requestId": "b4e95b87-3ec4-4207-aa85-d2df28641cc0" +} +``` + +响应采用 8.4.5,并返回 `firstContactId`;协查工单调用时返回 `RISK-4091`。 + +##### ACT-03 提交事件初查 + +```json +{ + "preliminaryContent": "现场检查设备运行正常,监测到短时电压波动。", + "attachmentFileIds": ["FILE-PI-001"], + "taskVersion": 1, + "requestId": "85dd9477-c5df-444e-8b31-6fd660bc36ee" +} +``` + +```json +{ + "code": 200, + "msg": "初查结果已提交", + "data": { + "preliminaryInvestigationId": "PINV-001", + "taskId": "TASK-PI-001", + "taskStatus": "COMPLETED", + "completedCount": 2, + "totalCount": 3, + "investigationSubmitReady": false, + "workOrderVersion": 6, + "taskVersion": 2 + } +} +``` + +##### ACT-04 全面调查上下文 + +`GET /risk/tasks/{taskId}/investigation-context` 无 body。 + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "taskId": "TASK-INV-001", + "taskVersion": 3, + "workOrderVersion": 7, + "responsibilityType": "PRIMARY", + "firstContact": { "status": "COMPLETED", "firstContactAt": "2026-08-12T14:32:00+08:00", "content": "已完成首联" }, + "preliminaryProgress": { + "completedCount": 1, + "totalCount": 3, + "items": [ + { "taskId": "TASK-PI-001", "deptId": "D-OPS", "deptName": "运营部", "status": "COMPLETED" }, + { "taskId": "TASK-PI-002", "deptId": "D-BIZ", "deptName": "业务部", "status": "PENDING" } + ] + }, + "draft": { "draftVersion": 4, "formData": {} }, + "submitReady": false, + "incompletePrerequisites": ["TASK-PI-002", "TASK-PI-003"] + } +} +``` + +协查响应中的 `firstContact` 必须为 `null`,其 `submitReady` 只依据全部事件初查是否完成。 + +##### ACT-05 提交全面调查 + +```json +{ + "acceptedContent12398": "客户反映近期电压不稳,设备频繁重启。", + "externalExposureRisk": false, + "internalAppealIssued": false, + "customerAppealSummary": "客户要求核查电压不稳原因并恢复稳定供电。", + "escalationReason": "问题偶发且多次反映。", + "investigationProcess": "调取95598历史记录及台区监测数据并现场检查。", + "localResponsibility": true, + "handlingSituation": "已完成线路及设备检查。", + "accountable": false, + "hasHandlingBasis": true, + "handlingMethod": "调整运行参数并增加监测频次。", + "handlingResult": "当前数据恢复正常,后续持续跟踪。", + "investigationSummary": "存在短时波动,已完成处置。", + "draftVersion": 4, + "taskVersion": 3, + "workOrderVersion": 7, + "requestId": "b6848224-1277-421e-907e-4a1f4c1039bb" +} +``` + +响应采用 8.4.5,并额外返回不可变的 `investigationId` 和 `investigationVersion`。前置任务未完成返回 `RISK-4220`。 + +##### ACT-06 服务监督主管审核 + +```json +{ + "result": "APPROVED", + "rejectReason": null, + "investigationVersion": 1, + "taskVersion": 1, + "workOrderVersion": 8, + "requestId": "2ec9fd4b-c493-44dc-b1fd-8e70f72754ab" +} +``` + +```json +{ + "code": 200, + "msg": "审核通过", + "data": { + "reviewId": "REV-S-001", + "reviewLevel": "SUPERVISOR", + "reviewResult": "APPROVED", + "currentStage": "BUSINESS_REVIEW", + "workOrderVersion": 9, + "taskVersion": 2, + "nextTasks": [{ "taskId": "TASK-BR-001", "stage": "BUSINESS_REVIEW", "status": "PENDING" }] + } +} +``` + +驳回时 `result` 为 `REJECTED` 且 `rejectReason` 必填;响应的 `currentStage` 回到 `INVESTIGATION`,并返回新建返工任务。 + +##### ACT-07 业务部主任审核 + +主责审核通过且 D-03 决定由二级审核选回访部门时: + +```json +{ + "result": "APPROVED", + "rejectReason": null, + "callbackDeptId": "D-CS1", + "investigationVersion": 1, + "taskVersion": 1, + "workOrderVersion": 9, + "requestId": "6c704449-6022-40fa-a7bb-509b879e93ee" +} +``` + +协查审核请求体必须省略或将 `callbackDeptId` 设为 `null`。主责响应: + +```json +{ + "code": 200, + "msg": "审核通过,进入回访分配", + "data": { + "reviewId": "REV-B-001", + "responsibilityType": "PRIMARY", + "currentStage": "CALLBACK_ASSIGN", + "workOrderVersion": 10, + "taskVersion": 2, + "nextTasks": [{ "taskId": "TASK-CA-001", "stage": "CALLBACK_ASSIGN", "status": "PENDING", "deptId": "D-CS1" }] + } +} +``` + +协查响应: + +```json +{ + "code": 200, + "msg": "审核通过,直接进入调查报告编写", + "data": { + "reviewId": "REV-B-002", + "responsibilityType": "ASSIST", + "currentStage": "REPORT", + "workOrderVersion": 10, + "taskVersion": 2, + "nextTasks": [{ "taskId": "TASK-RPT-001", "stage": "REPORT", "status": "PENDING" }] + } +} +``` + +服务端必须根据派工记录中的 `responsibilityType` 生成唯一分支,不接受客户端通过目标状态控制分支。 + +##### ACT-08 提交班组回访 + +班组回访字段仍待业务确认,当前最小请求体建议为: + +```json +{ + "contactedAt": "2026-08-13T15:20:00+08:00", + "contactPhone": "13800008888", + "contactResult": "CONNECTED", + "contactSummary": "已向客户说明处理情况。", + "taskVersion": 1, + "requestId": "8f596b61-fcd9-4d52-93cc-0908c4654b8c" +} +``` + +响应采用 8.4.5,并返回 `callbackExecutionId`;仅主责回访任务允许调用。 + +##### ACT-09 服务监督员提交回访结果 + +“同意”分支: + +```json +{ + "respondentName": "监督员A(张某)", + "repliedAt": "2026-08-13T16:00:00+08:00", + "replyPhone": "13100007777", + "replySituation": "客户认可处理结果。", + "callbackResult": "AGREE", + "responseLetterFileIds": [], + "deliveryProofFileIds": [], + "taskVersion": 1, + "workOrderVersion": 12, + "requestId": "84808ee5-c306-4543-89e1-53f733e2a3c8" +} +``` + +“不同意或客户主动要求”分支: + +```json +{ + "respondentName": "监督员A(张某)", + "repliedAt": "2026-08-13T16:00:00+08:00", + "replyPhone": "13100007777", + "replySituation": "客户要求提供书面答复。", + "callbackResult": "DISAGREE_OR_CUSTOMER_REQUEST", + "responseLetterFileIds": ["FILE-RL-001"], + "deliveryProofFileIds": ["FILE-DP-001"], + "taskVersion": 1, + "workOrderVersion": 12, + "requestId": "560215f4-cab9-4ff2-806f-d47950116eea" +} +``` + +响应采用 8.4.5,并创建 `REPORT` 任务。缺少任一类附件返回 `RISK-4221`。 + +### 8.10 调查报告、成果物与办结接口请求与响应字段定义 + +#### 8.10.1 请求字段定义 + +| 接口 | 字段位置 | 字段 | 类型 | 必填 | 定义 | +| --- | --- | --- | --- | :---: | --- | +| ACT-10 | path | `taskId` | string | 是 | 调查报告任务标识;无请求体 | +| ACT-11 | path | `taskId` | string | 是 | 调查报告任务标识 | +| ACT-11 | body | `templateId` | string | 是 | 报告模板标识 | +| ACT-11 | body | `templateVersion` | integer | 是 | 报告模板版本 | +| ACT-11 | body | `investigationVersion` | integer | 是 | 锁定的调查版本 | +| ACT-11 | body | `callbackVersion` | integer/null | 条件 | 主责必填;协查必须为空 | +| ACT-11 | body | `taskVersion` | integer | 是 | 报告任务版本 | +| ACT-11 | body | `workOrderVersion` | integer | 是 | 工单版本 | +| ACT-11 | body | `requestId` | string | 是 | 幂等请求标识 | +| ACT-12 | path | `reportId` | string | 是 | 已生成报告标识;无请求体 | +| ACT-13 | path | `taskId` | string | 是 | 成果物提交任务标识 | +| ACT-13 | body | `reportId` | string | 是 | 已生成报告标识 | +| ACT-13 | body | `reportVersion` | integer | 是 | 报告版本 | +| ACT-13 | body | `deliverables[]` | object[] | 是 | 成果物集合,至少 1 项 | +| ACT-13 | body | `deliverables[].type` | enum | 是 | 成果物类型 | +| ACT-13 | body | `deliverables[].fileId` | string | 是 | 已上传并有权绑定的文件标识 | +| ACT-13 | body | `taskVersion` | integer | 是 | 任务版本 | +| ACT-13 | body | `workOrderVersion` | integer | 是 | 工单版本 | +| ACT-13 | body | `requestId` | string | 是 | 幂等请求标识 | +| ACT-14 | path | `id` | string | 是 | 工单标识 | +| ACT-14 | body | `closeReason` | string | 是 | 办结原因/说明 | +| ACT-14 | body | `deliverableSetId` | string | 是 | 已提交成果物集合标识 | +| ACT-14 | body | `workOrderVersion` | integer | 是 | 工单版本 | +| ACT-14 | body | `requestId` | string | 是 | 幂等请求标识 | + +#### 8.10.2 响应字段定义 + +| 接口 | 响应字段 | 类型 | 定义 | +| --- | --- | --- | --- | +| ACT-10 | `data.taskId`/`taskVersion` | string/integer | 报告任务及版本 | +| ACT-10 | `data.workOrder` | object | 工单标识、12398 工单号、责任类型和版本 | +| ACT-10 | `data.investigation` | object | 锁定的调查记录、版本、提交时间和结构化数据 | +| ACT-10 | `data.callback` | object/null | 主责的回访结果;协查固定为空 | +| ACT-10 | `data.attachments[]` | object[] | 报告可引用附件 | +| ACT-10 | `data.template` | object | 模板标识、版本和名称 | +| ACT-10 | `data.output` | object | 输出状态、报告标识和文件标识 | +| ACT-10 | `data.allowedActions[]` | string[] | 当前允许的报告动作 | +| ACT-11 | `data.reportId` | string | 报告标识 | +| ACT-11 | `data.jobId` | string/null | 异步生成任务标识;同步生成时为空 | +| ACT-11 | `data.outputStatus` | enum | `GENERATING`、`READY` 或 `FAILED` | +| ACT-11 | `data.fileId`/`fileName` | string/null | 同步完成或异步完成后的文件信息 | +| ACT-11 | `data.contentType` | string/null | 报告文件 MIME 类型 | +| ACT-11 | `data.downloadable` | boolean | 当前是否可下载 | +| ACT-11 | `data.taskVersion`/`workOrderVersion` | integer | 更新后的版本 | +| ACT-12 | HTTP body | binary | 报告文件流 | +| ACT-12 | `Content-Type` | string | 报告文件 MIME 类型 | +| ACT-12 | `Content-Disposition` | string | 下载文件名 | +| ACT-12 | `X-File-Id` | string | 输出文件标识 | +| ACT-12 | `X-Report-Version` | integer | 报告版本 | +| ACT-13 | `data.deliverableSetId` | string | 成果物集合标识 | +| ACT-13 | `data.taskStatus` | enum | 成果物任务状态 | +| ACT-13 | `data.currentStage` | enum | 当前流程环节 | +| ACT-13 | `data.readyToClose` | boolean | 是否满足办结条件 | +| ACT-13 | `data.taskVersion`/`workOrderVersion` | integer | 更新后的版本 | +| ACT-14 | `data.workOrderId` | string | 已办结工单标识 | +| ACT-14 | `data.orderStatus` | enum | 固定为 `CLOSED` | +| ACT-14 | `data.currentStage` | enum | 办结时所在环节 | +| ACT-14 | `data.closedAt` | datetime | 服务端办结时间 | +| ACT-14 | `data.closedBy` | object | 办结人,包含 `userId`、`name` | +| ACT-14 | `data.workOrderVersion` | integer | 更新后的工单版本 | + +#### 8.10.3 请求与响应示例 + +##### ACT-10 报告上下文 + +`GET /risk/tasks/{taskId}/report-context` 无 body。 + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "taskId": "TASK-RPT-001", + "taskVersion": 1, + "workOrder": { "workOrderId": "WO-20260806-009", "workOrderNo12398": "2026-08-06-009", "responsibilityType": "ASSIST", "version": 10 }, + "investigation": { "investigationId": "INV-009", "version": 1, "submittedAt": "2026-08-13T10:00:00+08:00", "data": {} }, + "callback": null, + "attachments": [], + "template": { "templateId": "TPL-RPT-001", "templateVersion": 2, "name": "12398调查报告" }, + "output": { "status": "PENDING", "reportId": null, "fileId": null }, + "allowedActions": ["CONFIRM_REPORT"] + } +} +``` + +主责工单的 `callback` 必须包含已完成的回访版本;协查必须为 `null`。 + +##### ACT-11 确认生成报告 + +```json +{ + "templateId": "TPL-RPT-001", + "templateVersion": 2, + "investigationVersion": 1, + "callbackVersion": null, + "taskVersion": 1, + "workOrderVersion": 10, + "requestId": "aec607a6-a962-4f97-9650-d49624988bcb" +} +``` + +异步生成响应建议使用 HTTP `202 Accepted`: + +```json +{ + "code": 200, + "msg": "报告生成任务已创建", + "data": { + "reportId": "RPT-001", + "jobId": "JOB-RPT-001", + "outputStatus": "GENERATING", + "taskVersion": 2, + "workOrderVersion": 11 + } +} +``` + +若实现为同步生成,可返回 HTTP 200,并直接提供 `fileId`、`fileName`、`contentType` 和 `downloadable: true`。 + +##### ACT-12 下载报告 + +`GET /risk/reports/{reportId}/download` 无 body。成功返回受控文件流: + +```http +Content-Type: application/pdf +Content-Disposition: attachment; filename="2026-08-06-009-调查报告.pdf" +X-File-Id: FILE-RPT-001 +X-Report-Version: 1 +``` + +报告仍在生成时返回 HTTP 409、`RISK-4091`;生成失败时响应应包含可展示的失败原因和是否允许重新生成。 + +##### ACT-13 提交成果物 + +```json +{ + "reportId": "RPT-001", + "reportVersion": 1, + "deliverables": [ + { "type": "INVESTIGATION_REPORT", "fileId": "FILE-RPT-001" }, + { "type": "SUPPORTING_MATERIAL", "fileId": "FILE-SUP-001" } + ], + "taskVersion": 1, + "workOrderVersion": 11, + "requestId": "e2f3d22c-2be0-461c-b9e5-c089ea4c0e9c" +} +``` + +```json +{ + "code": 200, + "msg": "成果物已提交", + "data": { + "deliverableSetId": "DEL-001", + "taskStatus": "COMPLETED", + "currentStage": "DELIVERABLE", + "readyToClose": true, + "taskVersion": 2, + "workOrderVersion": 12 + } +} +``` + +##### ACT-14 确认办结 + +```json +{ + "closeReason": "调查、审核、报告及成果物均已完成", + "deliverableSetId": "DEL-001", + "workOrderVersion": 12, + "requestId": "995a8d84-a776-4dd8-98ca-779004ab8cbb" +} +``` + +```json +{ + "code": 200, + "msg": "工单已办结", + "data": { + "workOrderId": "WO-20260806-001", + "orderStatus": "CLOSED", + "currentStage": "DELIVERABLE", + "closedAt": "2026-08-13T17:30:00+08:00", + "closedBy": { "userId": "1002", "name": "张某" }, + "workOrderVersion": 13 + } +} +``` + +### 8.11 文件、字典、配置、统计与异步任务接口请求与响应字段定义 + +#### 8.11.1 请求字段定义 + +| 接口 | 字段位置 | 字段 | 类型 | 必填 | 定义 | +| --- | --- | --- | --- | :---: | --- | +| FILE-01 | multipart | `file` | binary | 是 | 待上传文件,须校验大小、扩展名、MIME 和安全风险 | +| FILE-01 | multipart | `purpose` | enum | 是 | 文件用途,取值见 8.12 | +| FILE-01 | multipart | `workOrderId` | string | 是 | 所属工单标识 | +| FILE-01 | multipart | `taskId` | string | 条件 | 任务办理过程中上传时必填 | +| FILE-02 | path | `fileId` | string | 是 | 文件标识;无请求体 | +| FILE-03 | path | `fileId` | string | 是 | 临时文件标识 | +| FILE-03 | query | `version` | integer | 是 | 文件记录版本;无请求体 | +| DICT-01 | path | `type` | string | 是 | 字典类型编码 | +| DICT-01 | query | `enabled` | boolean | 否 | 是否只返回启用项,默认 `true` | +| CFG-01 | — | — | — | — | 无查询参数、无请求体 | +| CFG-02 | body | `configVersion` | integer | 是 | 当前时限配置版本 | +| CFG-02 | body | `totalLimitMinutes` | integer | 是 | 全流程总时限,必须为正整数 | +| CFG-02 | body | `stages[]` | object[] | 是 | 环节时限集合 | +| CFG-02 | body | `stages[].stageCode` | enum | 是 | 环节编码,不得重复 | +| CFG-02 | body | `stages[].limitMinutes` | integer | 是 | 环节时限,必须为正整数 | +| CFG-02 | body | `effectiveFrom` | datetime | 是 | 新版本生效时间 | +| CFG-02 | body | `changeReason` | string | 是 | 变更原因 | +| CFG-02 | body | `publish` | boolean | 是 | 是否立即发布 | +| CFG-03 | — | — | — | — | 无查询参数、无请求体 | +| CFG-04 | body | `ruleVersion` | integer | 是 | 当前规则版本 | +| CFG-04 | body | `rules[]` | object[] | 是 | 派工规则集合 | +| CFG-04 | body | `rules[].ruleId` | string | 条件 | 修改已有规则时必填;新增时可为空 | +| CFG-04 | body | `rules[].priority` | integer | 是 | 规则优先级,数值冲突处理方式待确认 | +| CFG-04 | body | `rules[].conditions[]` | object[] | 是 | 匹配条件集合 | +| CFG-04 | body | `rules[].conditions[].field` | string | 是 | 条件字段,必须来自白名单 | +| CFG-04 | body | `rules[].conditions[].operator` | enum | 是 | 比较操作符 | +| CFG-04 | body | `rules[].conditions[].value` | any | 是 | 比较值,类型须与字段一致 | +| CFG-04 | body | `rules[].result` | object | 是 | 派工建议结果 | +| CFG-04 | body | `rules[].enabled` | boolean | 是 | 规则是否启用 | +| CFG-04 | body | `effectiveFrom` | datetime | 是 | 生效时间 | +| CFG-04 | body | `changeReason` | string | 是 | 变更原因 | +| CFG-04 | body | `publish` | boolean | 是 | 是否发布 | +| ANA-01 | query | `from` | date | 是 | 统计开始日期,含边界 | +| ANA-01 | query | `to` | date | 是 | 统计结束日期,含边界 | +| ANA-01 | query | `deptId` | string | 否 | 部门范围;仍受当前用户数据权限约束 | +| ANA-02 | query | `pageNum` | integer | 是 | 页码,从 1 开始 | +| ANA-02 | query | `pageSize` | integer | 是 | 每页数量 | +| ANA-02 | query | `from` | date | 是 | 办结开始日期 | +| ANA-02 | query | `to` | date | 是 | 办结结束日期 | +| ANA-02 | query | `deptId` | string | 否 | 部门范围 | +| JOB-01 | path | `jobId` | string | 是 | 异步任务标识;无请求体 | + +#### 8.11.2 响应字段定义 + +| 接口 | 响应字段 | 类型 | 定义 | +| --- | --- | --- | --- | +| FILE-01 | `data.fileId` | string | 文件标识,后续业务接口仅传此标识 | +| FILE-01 | `data.fileName` | string | 安全处理后的文件名 | +| FILE-01 | `data.contentType` | string | 服务端识别的 MIME 类型 | +| FILE-01 | `data.size` | integer | 文件字节数 | +| FILE-01 | `data.sha256` | string | 文件摘要 | +| FILE-01 | `data.purpose` | enum | 文件用途 | +| FILE-01 | `data.bindingStatus` | enum | `TEMPORARY` 或 `BOUND` | +| FILE-01 | `data.uploadedAt` | datetime | 上传完成时间 | +| FILE-02 | HTTP body | binary | 受控文件流 | +| FILE-02 | `Content-Type` | string | 文件 MIME 类型 | +| FILE-02 | `Content-Disposition` | string | 内联播放或附件下载及安全文件名 | +| FILE-02 | `Content-Length` | integer | 文件字节数 | +| FILE-03 | `data.fileId` | string | 已删除临时文件标识 | +| FILE-03 | `data.deleted` | boolean | 是否删除成功 | +| DICT-01 | `data.type` | string | 字典类型 | +| DICT-01 | `data.version` | integer | 字典版本 | +| DICT-01 | `data.items[]` | object[] | 字典项集合 | +| DICT-01 | `data.items[].value`/`label` | string | 字典编码及显示名称 | +| DICT-01 | `data.items[].sort` | integer | 排序值 | +| DICT-01 | `data.items[].enabled` | boolean | 是否启用 | +| CFG-01/CFG-02 | `data.configId` | string | 时限配置标识 | +| CFG-01/CFG-02 | `data.configVersion` | integer | 配置版本 | +| CFG-01/CFG-02 | `data.status` | enum | 草稿、启用或停用 | +| CFG-01/CFG-02 | `data.totalLimitMinutes` | integer | 全流程总时限 | +| CFG-01/CFG-02 | `data.stages[]` | object[] | 环节编码及分钟数 | +| CFG-01/CFG-02 | `data.effectiveFrom` | datetime | 生效时间 | +| CFG-03/CFG-04 | `data.ruleSetId` | string | 规则集标识 | +| CFG-03/CFG-04 | `data.ruleVersion` | integer | 规则版本 | +| CFG-03/CFG-04 | `data.status` | enum | 规则集状态 | +| CFG-03/CFG-04 | `data.rules[]` | object[] | 规则、条件和建议结果集合 | +| CFG-03/CFG-04 | `data.effectiveFrom` | datetime | 生效时间 | +| ANA-01 | `data.period` | object | 实际统计日期范围 | +| ANA-01 | `data.metrics.importedCount` | integer | 导入工单数 | +| ANA-01 | `data.metrics.processingCount` | integer | 处理中工单数 | +| ANA-01 | `data.metrics.closedCount` | integer | 已办结工单数 | +| ANA-01 | `data.metrics.overdueCount` | integer | 逾期工单数 | +| ANA-01 | `data.metrics.primaryCount` | integer | 主责工单数 | +| ANA-01 | `data.metrics.assistCount` | integer | 协查工单数 | +| ANA-01 | `data.generatedAt` | datetime | 统计数据生成时间 | +| ANA-02 | `total` | integer | 已办结工单总数 | +| ANA-02 | `rows[]` | object[] | 工单摘要,继承 WO-01 并增加办结字段 | +| ANA-02 | `rows[].closedAt` | datetime | 办结时间 | +| ANA-02 | `rows[].durationMinutes` | integer | 从规定起点到办结的耗时分钟数,口径待 D-07 确认 | +| ANA-02 | `rows[].overdue` | boolean | 是否逾期 | +| JOB-01 | `data.jobId` | string | 异步任务标识 | +| JOB-01 | `data.jobType` | enum | 任务类型,如导入或报告生成 | +| JOB-01 | `data.status` | enum | `PENDING`、`RUNNING`、`SUCCEEDED`、`FAILED`、`CANCELLED` | +| JOB-01 | `data.progress` | integer | 0~100 的整数进度 | +| JOB-01 | `data.createdAt`/`finishedAt` | datetime/null | 创建和结束时间 | +| JOB-01 | `data.result` | object/null | 成功结果;结构由 `jobType` 决定 | +| JOB-01 | `data.error` | object/null | 失败信息,至少包含稳定错误码和说明 | + +#### 8.11.3 请求与响应示例 + +##### FILE-01/FILE-02/FILE-03 文件接口 + +`POST /risk/files` 使用 `multipart/form-data`:`file`、`purpose`、`workOrderId`、`taskId`。`purpose` 取值见 8.12。 + +```json +{ + "code": 200, + "msg": "上传成功", + "data": { + "fileId": "FILE-RL-001", + "fileName": "答复书.pdf", + "contentType": "application/pdf", + "size": 248102, + "sha256": "53dd...a8cb", + "purpose": "RESPONSE_LETTER", + "bindingStatus": "TEMPORARY", + "uploadedAt": "2026-08-13T15:45:00+08:00" + } +} +``` + +FILE-02 `GET /risk/files/{fileId}` 无 body,成功返回受控文件流;无权访问返回 `RISK-4031`。FILE-03: + +```http +DELETE /risk/files/{fileId}?version=1 +``` + +```json +{ "code": 200, "msg": "临时文件已删除", "data": { "fileId": "FILE-RL-001", "deleted": true } } +``` + +已绑定业务记录的文件不得通过 FILE-03 删除。 + +##### DICT-01 字典 + +`GET /risk/dictionaries/{type}?enabled=true` 无 body。 + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "type": "RESPONSIBILITY_TYPE", + "version": 1, + "items": [ + { "value": "PRIMARY", "label": "主责", "sort": 1, "enabled": true }, + { "value": "ASSIST", "label": "协查", "sort": 2, "enabled": true } + ] + } +} +``` + +##### CFG-01/CFG-02 时限配置 + +`GET /risk/config/sla` 无 body;响应: + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "configId": "SLA-001", + "configVersion": 3, + "status": "ENABLED", + "totalLimitMinutes": 188, + "stages": [ + { "stageCode": "DISPATCH", "limitMinutes": 20 }, + { "stageCode": "FIRST_CONTACT", "limitMinutes": 30 }, + { "stageCode": "PRELIMINARY_INVESTIGATION", "limitMinutes": 60 } + ], + "effectiveFrom": "2026-08-01T00:00:00+08:00" + } +} +``` + +`PUT /risk/config/sla` 请求体: + +```json +{ + "configVersion": 3, + "totalLimitMinutes": 188, + "stages": [ + { "stageCode": "DISPATCH", "limitMinutes": 20 }, + { "stageCode": "FIRST_CONTACT", "limitMinutes": 30 }, + { "stageCode": "PRELIMINARY_INVESTIGATION", "limitMinutes": 60 } + ], + "effectiveFrom": "2026-09-01T00:00:00+08:00", + "changeReason": "调整事件初查时限", + "publish": true +} +``` + +响应返回新的 `configId`、`configVersion`、`status`、`effectiveFrom`,不得覆盖旧版本。 + +##### CFG-03/CFG-04 派工规则 + +`GET /risk/config/dispatch-rules` 无 body;响应 `data` 至少包含 `ruleSetId`、`ruleVersion`、`status`、`rules`。 + +```json +{ + "ruleVersion": 5, + "rules": [ + { + "ruleId": "RULE-001", + "priority": 10, + "conditions": [{ "field": "problemCategoryCode", "operator": "EQ", "value": "SAFETY_RISK" }], + "result": { "preliminaryDeptIds": ["D-OPS"] }, + "enabled": true + } + ], + "effectiveFrom": "2026-09-01T00:00:00+08:00", + "changeReason": "安全隐患默认由运营部初查", + "publish": true +} +``` + +响应返回新规则版本和发布状态。规则 DSL、冲突策略和试算接口仍属于 D-06 待确认项。 + +##### ANA-01/ANA-02 统计 + +`GET /risk/analytics/overview?from=2026-08-01&to=2026-08-31&deptId=D-OPS` 无 body。 + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "period": { "from": "2026-08-01", "to": "2026-08-31" }, + "metrics": { + "importedCount": 103, + "processingCount": 34, + "closedCount": 69, + "overdueCount": 2, + "primaryCount": 61, + "assistCount": 42 + }, + "generatedAt": "2026-08-13T18:00:00+08:00" + } +} +``` + +`GET /risk/analytics/closed-orders?pageNum=1&pageSize=20&from=2026-08-01&to=2026-08-31` 返回 `TableDataInfo`,`rows` 使用 WO-01 工单摘要并增加 `closedAt`、`durationMinutes`、`overdue`。指标正式口径仍需关闭 D-07 后确定。 + +##### JOB-01 异步任务状态 + +`GET /risk/jobs/{jobId}` 无 body。 + +```json +{ + "code": 200, + "msg": "查询成功", + "data": { + "jobId": "JOB-RPT-001", + "jobType": "REPORT_GENERATION", + "status": "SUCCEEDED", + "progress": 100, + "createdAt": "2026-08-13T17:00:00+08:00", + "finishedAt": "2026-08-13T17:00:08+08:00", + "result": { "reportId": "RPT-001", "fileId": "FILE-RPT-001", "downloadable": true }, + "error": null + } +} +``` + +任务状态建议为 `PENDING`、`RUNNING`、`SUCCEEDED`、`FAILED`、`CANCELLED`。前端轮询间隔建议不小于 1 秒,并在完成或失败后停止轮询。 + +### 8.12 联调枚举与字段规则 + +| 业务字段 | 编码 | 中文显示 | +| --- | --- | --- | +| responsibilityType | `PRIMARY` / `ASSIST` | 主责 / 协查 | +| reviewResult | `APPROVED` / `REJECTED` | 同意 / 不同意 | +| callbackResult | `AGREE` / `DISAGREE_OR_CUSTOMER_REQUEST` | 同意 / 不同意或客户主动要求 | +| dispatchMode | `MANUAL` / `SMART` | 人工派工 / 智能建议后确认 | +| rowStatus | `VALID` / `DUPLICATE` / `INVALID` | 有效 / 重复 / 错误 | +| filePurpose | `IMPORT_SOURCE` / `RECORDING` / `PRELIMINARY_ATTACHMENT` / `RESPONSE_LETTER` / `DELIVERY_PROOF` / `REPORT_OUTPUT` / `DELIVERABLE` | 导入原件 / 录音 / 初查附件 / 答复书 / 送达凭证 / 报告输出 / 成果物 | + +联调必须遵守以下规则: + +1. 前端提交稳定编码,展示使用后端返回的中文名称或字典,不提交中文作为枚举值。 +2. 所有时间使用带时区的 ISO 8601,例如 `2026-08-13T16:00:00+08:00`;纯日期使用 `YYYY-MM-DD`。 +3. 金额以最小货币单位整数传输;当前原型没有金额字段,未来新增时不得使用浮点数。 +4. 空数组使用 `[]`,无值对象使用 `null`;不得混用空字符串表达 `null`。 +5. ID 一律按字符串处理,前端不得转换为 JavaScript `number` 后再传回。 +6. 手机号、地址、录音和附件是否返回完整数据由服务端权限决定;前端不得依据掩码值发起写操作。 +7. 前端只能提交当前详情响应中的 `version`;收到 `RISK-4090` 后重新获取详情,不自动覆盖。 +8. 写接口成功后以响应中的 `currentStage`、`nextTasks` 和最新版本为准,不按按钮名称本地推断流程。 +9. `PRIMARY` 的全面调查提交依赖首联和全部初查;`ASSIST` 只依赖全部初查。该判断必须以后端 `submitReady` 为权威。 +10. 业务部主任审核请求不携带目标环节;后端依据持久化责任类型决定进入回访或调查报告。 +11. 报告“确认生成”和“下载”是两个动作;生成接口使用幂等键,下载接口不得推进任务状态。 +12. 文件先上传取得 `fileId`,业务提交再绑定;业务提交失败时文件保持临时状态并由后台清理。 + +## 9. 后端业务与技术约束 + +### 9.1 状态与权限约束 + +1. 每个写接口同时校验:用户权限、数据范围、任务所有权、任务状态、工单当前环节和数据版本。 +2. 状态只允许按定义的状态机迁移;跳步、重复提交、对已完成任务再次提交均拒绝。 +3. 驳回必须指定目标环节并新建返工任务,旧任务和旧调查版本保持不可变。 +4. 并行初查使用任务集合判断完成,不能以工单上的单一“调查状态”覆盖多个部门结果。 +5. 派工完成后即创建或开放服务监督员全面调查任务,允许编辑和保存草稿;是否允许提交由服务端根据 `responsibilityType` 及前置任务完成情况实时计算。 +6. 业务部主任审核通过后必须读取派工阶段持久化的主责/协查判定:主责创建回访任务,协查创建调查报告任务,两个分支不得同时创建。 +7. 主责/协查切换如已生成任务,必须撤销旧任务并记录原因;禁止静默修改。 +8. 已办结工单默认只读;重新开启需独立权限、原因和审计事件。 + +### 9.2 幂等、并发与事务 + +- 确认导入、派工、提交、审核、办结必须支持幂等键,重复请求返回同一业务结果。 +- 工单、任务、调查版本和配置均使用乐观锁版本号;版本冲突返回明确错误,提示用户刷新。 +- “派工记录 + 多个任务创建 + 流程状态更新 + 审计事件”必须处于同一事务。 +- “审核结果 + 当前任务完成 + 下游任务创建/返工任务创建 + 工单状态更新”必须处于同一事务。 +- 文件上传与业务提交分两阶段处理:先上传临时文件,业务提交成功后绑定;定期清理超时未绑定文件。 +- 异步导入和智能报告生成不能在 HTTP 请求中长时间阻塞,应使用任务状态查询,并保证重复消费幂等。 + +### 9.3 导入约束 + +1. 文件扩展名和实际 MIME/内容类型必须同时校验,禁止只看后缀。 +2. 导入模板应带版本;字段缺失、类型错误、枚举无效、日期非法、编号重复必须形成行级错误。 +3. 预览不写入正式工单;确认导入必须绑定原预览文件摘要和 previewToken。 +4. 批次号唯一性、12398 工单编号重复处理策略需确认:拒绝、覆盖、忽略或形成新版本。 +5. 部分成功策略需确认。建议默认“整批校验、有效行可确认”,确认结果保留失败行,不允许悄然丢弃。 +6. 原始文件、解析版本、操作人和导入结果必须可追溯。 + +### 9.4 表单校验约束 + +- 所有文本字段设置长度上限并进行服务端校验;富文本需消毒,防止脚本注入。 +- 电话号码保留业务允许的字符,但标准化后再用于查询;模糊查询需防止全表扫描。 +- 日期不得明显晚于当前业务允许时间;首联、调查、审核、回访时间应满足流程先后顺序。 +- 主责必须选择首联部门;协查不得生成首联任务;初查部门至少 1 个且不得重复。 +- 调查草稿可在前置任务完成前保存;调查提交时,主责必须校验首联及全部初查完成,协查必须校验全部初查完成。 +- 业务部主任审核通过时,主责必须进入回访分支,协查必须直接进入调查报告分支。 +- 审核不同意时驳回理由必填;同意时不得携带会误导流程的驳回理由。 +- 回访结果为“不同意或客户主动要求”时,答复书和送达凭证均为强制附件;“同意”分支不得被错误阻塞。 +- 报告确认必须锁定所依据的调查版本;主责还必须锁定回访版本,协查不得伪造或要求回访版本。 +- 报告生成与下载应分离:确认生成属于幂等写操作,重复下载属于受权读操作,不得因重复下载重复推进流程。 +- 办结前必须校验不存在未完成的有效任务,报告和成果物满足配置要求。 + +### 9.5 文件与敏感数据 + +- 附件存储使用 fileId,不在业务表保存本地路径或公开 URL。 +- 下载/播放每次鉴权;链接短期有效,禁止通过猜测 fileId 越权访问。 +- 文件执行病毒/恶意内容扫描,限制类型、大小、数量和总容量;具体阈值待安全评审。 +- 客户姓名、电话、地址、录音、转译文本、答复书和送达凭证按敏感等级控制,日志中脱敏。 +- 导出和批量下载属于独立权限并记录审计;原型未展示导出入口,未经确认不实现。 + +### 9.6 时限、规则和审计 + +- 时限以流程配置版本计算,已开始实例是否随新配置变化必须明确;建议锁定启动时版本。 +- 暂停、节假日、退回返工、并行任务的计时方式需业务确认。 +- 智能派工只产生可解释建议,主管确认后才创建任务;必须记录输入摘要、规则版本、建议和人工调整。 +- 配置发布需要高权限、二次确认、变更原因、生效时间和回滚能力。 +- 审计事件至少记录 actor、角色、部门、动作、对象、前后状态、业务版本、requestId、IP、时间和结果。 + +### 9.7 错误码建议 + +| 错误码 | 含义 | +| --- | --- | +| `RISK-4001` | 请求字段校验失败 | +| `RISK-4002` | 枚举或字典值无效 | +| `RISK-4010` | 登录失效或令牌无效 | +| `RISK-4030` | 无菜单/操作权限 | +| `RISK-4031` | 超出数据范围或任务不属于当前用户 | +| `RISK-4040` | 工单、任务或文件不存在 | +| `RISK-4090` | 数据版本冲突 | +| `RISK-4091` | 当前状态不允许该操作 | +| `RISK-4092` | 幂等请求参数与首次请求不一致 | +| `RISK-4220` | 前置任务尚未全部完成 | +| `RISK-4221` | 条件必填附件缺失 | +| `RISK-4222` | 导入批次或工单编号重复 | +| `RISK-4290` | 请求过于频繁 | +| `RISK-5001` | 异步任务处理失败 | + +## 10. 非功能需求 + +| 维度 | 需求建议 | +| --- | --- | +| 可用性 | 所有提交操作给出明确成功/失败反馈;长表单提供草稿和离开提醒 | +| 性能 | 普通分页查询在约定数据量下稳定响应;录音、附件和统计使用独立加载,不阻塞基础详情 | +| 可扩展性 | 流程环节、部门类型、事件分类和规则使用配置/字典,不写死角色名称 | +| 可观测性 | 请求链路、异步任务、状态迁移和外部存储均可追踪;requestId 贯穿日志与审计 | +| 安全 | 认证、最小权限、敏感字段脱敏、文件扫描、下载鉴权、防重放和输入消毒 | +| 可恢复性 | 导入和异步任务可重试;配置可回滚;关键写操作可根据审计恢复业务事实 | +| 兼容性 | 保持已有 Bearer Token、用户/部门接口及通用返回结构,变更需版本化 | +| 无障碍/易用性 | 表单标签、错误提示和键盘焦点明确;状态不只依赖颜色表达 | + +## 11. 原型问题、冲突与待确认事项 + +### 11.1 高优先级业务决策 + +| 编号 | 问题 | 影响 | 建议决策方向 | +| --- | --- | --- | --- | +| D-01 | 派工完成后是否允许服务监督主管变更主责/协查判定 | 可能导致已生成的首联、初查、调查或回访任务失效 | 建议默认禁止;如业务必须支持,应限定时点并采用撤销旧任务、重新派工和全量审计 | +| D-02 | 全面调查的 1-13 与 1-21 字段范围不同 | 影响动态表单和必填规则 | 明确按业务类型/事件类型的适用条件 | +| D-03 | 1-4 与 1-18 审核弹窗均出现“回访分派”,但主流程只允许业务部主任通过后按责任类型分支 | 可能重复选择回访部门,或让协查错误进入回访 | 明确回访承接部门在哪一级审核生效;协查审核界面必须隐藏或禁用回访分派 | +| D-04 | 班组回访表单缺失,只有分派和结果页 | 无法区分执行回访与监督员结果填报 | 明确两个环节是否合并及各自字段 | +| D-05 | 调查报告已改为“确认下载”,但报告模板、文件格式、签章、重生成和历史版本规则未展示 | 无法确定报告生成与下载接口契约 | 明确模板、输出格式、生成失败处理、版本保留和重新下载规则 | +| D-06 | 智能派工规则配置页为空 | 无法定义规则数据结构 | 补充条件、优先级、冲突、试算、发布和回滚需求 | +| D-07 | 统计分析页为空 | 无法定义指标和接口 | 明确指标、口径、维度、权限和刷新频率 | +| D-08 | 重复工单和部分导入失败策略未说明 | 影响数据一致性 | 确认唯一键、覆盖/忽略/拒绝策略 | +| D-09 | 状态仅出现待分派/处理中/已结束,与实际 12 步不匹配 | 容易形成非法状态跃迁 | 采用生命周期 + 任务环节双层状态 | +| D-10 | 流程时限只展示分钟,未说明工作日、暂停和驳回重算 | 影响 SLA | 明确日历、起止点、暂停和重算规则 | + +### 11.2 原型一致性问题 + +- “智能运营团队”与页头“运营智能团队”名称不一致。 +- 总值班经理工单查询页页头错误显示“运营智能团队”。 +- 监督员待办统计出现“成果无输出”,疑似应为“成果物输出”。 +- 服务监督主管和业务部主任审核弹窗均出现回访分派字段,与“业务部主任审核通过后再按主责/协查分支”的流程规则存在职责重叠。 +- 1-14/1-15 调查报告页面已经区分不同意与同意状态,但报告模板、生成进度、失败重试和历史版本未展示。 +- 1-16 成果物输出继续复用大量调查/回访信息,专属成果物类型和办结清单仍不明确。 +- 最新原型将回访结果展示为“同意/不同意或客户主动要求”,旧文档或流程中如仍使用“满意/不满意”应统一替换。 +- “客户所在地区(省)”和“省份”同时存在,需明确一个是原始文本、一个是标准编码,还是重复字段。 +- “工单状态”和“工单环节”在部分列表列中混用,必须拆分定义。 + +## 12. 验收建议与追踪矩阵 + +### 12.1 关键业务验收场景 + +1. 有效文件预览后确认导入,生成批次、工单和完整审计记录;重复确认不重复建单。 +2. 含错误行的文件展示准确行号和原因,未确认前不写正式工单。 +3. 服务监督主管判定主责并派工后,系统生成 1 个首联任务、N 个初查任务,并立即向指定服务监督员开放全面调查填报;服务监督员可先行填写和保存草稿。 +4. 服务监督主管判定协查并派工后,系统只生成 N 个初查任务,不生成首联任务,同时立即向指定服务监督员开放全面调查填报。 +5. 主责任一首联或初查任务未完成时、协查任一初查任务未完成时,全面调查均只能编辑/保存草稿,不能提交。 +6. 服务监督主管审核或业务部主任审核驳回后生成返工任务,历史调查和审核记录仍可查看。 +7. 业务部主任审核通过主责工单后生成回访分支任务;审核通过协查工单后不生成回访任务,直接生成调查报告任务。 +8. 无任务所有权的用户即使直接调用接口也不能办理。 +9. 两人同时操作同一任务时,后提交者收到版本冲突,不覆盖先提交结果。 +10. 回访选择“不同意或客户主动要求”但缺少任一必需附件时不能提交;“同意”分支无需附件。 +11. 协查报告上下文不要求首联或回访数据;主责报告必须引用已完成的回访版本。 +12. 报告确认生成重复请求只产生一个业务版本;报告生成完成后可重复受权下载,不重复推进流程。 +13. 已办结前存在未完成任务时拒绝办结;成功办结后普通用户不能再修改。 +14. 修改时限或派工规则产生新版本,不篡改既有流程实例的历史计算结果。 +15. 手机号、地址、录音和附件在无权限用户的列表、详情、日志和下载接口中均不可泄露。 +16. 各待办统计卡片数量与同条件列表总数一致。 + +### 12.2 需求追踪 + +| 需求域 | 原型证据 | 主要接口 | 主要约束 | +| --- | --- | --- | --- | +| 登录 | 1-1 | 认证接口 | 验证码、Bearer Token | +| 导入 | 1-2、1-3、1-41 | IMP-01~07 | 预览隔离、幂等、行级错误 | +| 查询 | 1-34~1-40 | WO-01~06 | 数据权限、分页、脱敏 | +| 派工 | 1-10~1-12 | DSP-01~03 | 服务监督主管判定主责/协查、单选/多选、事务 | +| 首联 | 1-26、1-30、1-32、1-33 | DSP-04、ACT-02 | 仅主责、本部门候选、任务所有权 | +| 初查 | 1-23~1-25、1-28、1-29 | ACT-03 | 多部门并行任务全部完成 | +| 全面调查 | 1-13、1-21、1-22 | ACT-04、ACT-05 | 先行填报、动态字段、版本化、分支前置条件 | +| 主管审核 | 1-4、1-5、1-9 | ACT-06 | 驳回理由、不可覆盖历史 | +| 业务审核 | 1-18、1-19、1-27 | ACT-07 | 按持久化责任类型创建唯一后续分支 | +| 回访 | 1-17、1-20、1-31、1-33 | DSP-04/05、ACT-08/09、FILE | 仅主责、条件附件、敏感信息 | +| 报告/成果物 | 1-14~1-16 | ACT-10~14 | 数据版本快照、确认生成、受权下载、办结校验 | +| 配置/统计 | 1-6~1-8 | CFG、ANA | 版本发布、指标口径待补齐 | + +## 13. 后续交付建议 + +在进入后端开发前,建议按以下顺序完成需求基线: + +1. 业务评审本报告第 6、7、11 节,先关闭 D-01~D-10。 +2. 设计人员统一两级审核中的回访分派职责,并补齐班组回访、报告生成状态、成果物清单、智能规则和统计分析页面。 +3. 将字段字典固化为数据模型与枚举表,将状态模型固化为状态迁移矩阵。 +4. 基于第 8 节生成 OpenAPI 3.1,补充请求/响应 Schema、权限编码和错误码。 +5. 用第 12.1 节场景形成接口验收测试和流程集成测试。 + +本稿可作为需求评审和接口设计的输入,但在高优先级待确认项关闭前,不应直接据此创建数据库表或固化流程代码。 diff --git a/frontend/.npmrc b/frontend/.npmrc new file mode 100644 index 0000000..1223b5c --- /dev/null +++ b/frontend/.npmrc @@ -0,0 +1,2 @@ +fund=false +audit=false diff --git a/frontend/.openai/hosting.json b/frontend/.openai/hosting.json new file mode 100644 index 0000000..47c28cb --- /dev/null +++ b/frontend/.openai/hosting.json @@ -0,0 +1,4 @@ +{ + "d1": null, + "r2": null +} diff --git a/frontend/AGENTS.md b/frontend/AGENTS.md new file mode 100644 index 0000000..0129896 --- /dev/null +++ b/frontend/AGENTS.md @@ -0,0 +1,9 @@ +# Prototype Instructions + +Run the local server yourself and open the preview in the browser available to this environment. Do not give the user server-start instructions when you can run it. + +Before making substantial visual changes, use the Product Design plugin's `get-context` skill when the visual source is unclear or no longer matches the current goal. When the user gives durable prototype-specific design feedback, preferences, or decisions, record them in `AGENTS.md`. + +When implementing from a selected generated mock, treat that image as the source of truth for layout, component anatomy, density, spacing, color, typography, visible content, and hierarchy. + +Build app UI in `src/`. Keep `.openai/hosting.json`, `worker/index.js`, `scripts/prepare-sites-build.mjs`, and `tests/sites-worker.test.mjs` intact so the same local prototype can be handed to Sites. Before a Sites handoff, run `npm run build` and `npm run test:sites`; the build must leave `dist/client/index.html`, `dist/server/index.js`, and `dist/.openai/hosting.json`. diff --git a/frontend/design-audit-20260813/01-login.png b/frontend/design-audit-20260813/01-login.png new file mode 100644 index 0000000..7a1cb3b Binary files /dev/null and b/frontend/design-audit-20260813/01-login.png differ diff --git a/frontend/design-audit-20260813/02-supervisor-review.png b/frontend/design-audit-20260813/02-supervisor-review.png new file mode 100644 index 0000000..dca0098 Binary files /dev/null and b/frontend/design-audit-20260813/02-supervisor-review.png differ diff --git a/frontend/design-audit-20260813/03-supervisor-dispatch-main.png b/frontend/design-audit-20260813/03-supervisor-dispatch-main.png new file mode 100644 index 0000000..7ada137 Binary files /dev/null and b/frontend/design-audit-20260813/03-supervisor-dispatch-main.png differ diff --git a/frontend/design-audit-20260813/04-supervisor-dispatch-assist.png b/frontend/design-audit-20260813/04-supervisor-dispatch-assist.png new file mode 100644 index 0000000..1607a7f Binary files /dev/null and b/frontend/design-audit-20260813/04-supervisor-dispatch-assist.png differ diff --git a/frontend/design-audit-20260813/05-investigation-waiting.png b/frontend/design-audit-20260813/05-investigation-waiting.png new file mode 100644 index 0000000..29b1596 Binary files /dev/null and b/frontend/design-audit-20260813/05-investigation-waiting.png differ diff --git a/frontend/design-audit-20260813/06-director-review-assist.png b/frontend/design-audit-20260813/06-director-review-assist.png new file mode 100644 index 0000000..d6ab87f Binary files /dev/null and b/frontend/design-audit-20260813/06-director-review-assist.png differ diff --git a/frontend/design-audit-20260813/07-assist-approved-to-report.png b/frontend/design-audit-20260813/07-assist-approved-to-report.png new file mode 100644 index 0000000..53a7b72 Binary files /dev/null and b/frontend/design-audit-20260813/07-assist-approved-to-report.png differ diff --git a/frontend/design-audit-20260813/08-report-writing-assist.png b/frontend/design-audit-20260813/08-report-writing-assist.png new file mode 100644 index 0000000..dbd0e11 Binary files /dev/null and b/frontend/design-audit-20260813/08-report-writing-assist.png differ diff --git a/frontend/design-audit-20260813/09-report-writing-bottom.png b/frontend/design-audit-20260813/09-report-writing-bottom.png new file mode 100644 index 0000000..7458a6b Binary files /dev/null and b/frontend/design-audit-20260813/09-report-writing-bottom.png differ diff --git a/frontend/design-audit-20260813/10-compare-supervisor-review.png b/frontend/design-audit-20260813/10-compare-supervisor-review.png new file mode 100644 index 0000000..5c6f41d Binary files /dev/null and b/frontend/design-audit-20260813/10-compare-supervisor-review.png differ diff --git a/frontend/design-audit-20260813/11-compare-director-review.png b/frontend/design-audit-20260813/11-compare-director-review.png new file mode 100644 index 0000000..7086881 Binary files /dev/null and b/frontend/design-audit-20260813/11-compare-director-review.png differ diff --git a/frontend/design-audit-20260813/12-compare-dispatch.png b/frontend/design-audit-20260813/12-compare-dispatch.png new file mode 100644 index 0000000..d6634e7 Binary files /dev/null and b/frontend/design-audit-20260813/12-compare-dispatch.png differ diff --git a/frontend/design-audit-20260813/13-compare-report-bottom.png b/frontend/design-audit-20260813/13-compare-report-bottom.png new file mode 100644 index 0000000..920a16e Binary files /dev/null and b/frontend/design-audit-20260813/13-compare-report-bottom.png differ diff --git a/frontend/design-qa.md b/frontend/design-qa.md new file mode 100644 index 0000000..7a21f6e --- /dev/null +++ b/frontend/design-qa.md @@ -0,0 +1,84 @@ +# 20260813 高保真修复设计 QA + +## 登录后应用工作台 + +### 结论 + +基于用户选定并修订的第一套视觉方案,完成“风险防控处置系统”登录后应用工作台。页面已移除“全部应用”左侧分类导航,全部应用使用三列完整内容宽度;登录、应用搜索、未接入应用提示、进入 12398 工单系统及返回应用工作台均已验证。未发现 P0、P1 或 P2 问题。 + +### 比较基准 + +- 修订视觉稿:`design-qa/20260813-app-workbench/workbench-reference.png` +- 浏览器实现:`design-qa/20260813-app-workbench/workbench-1440x1024.png` +- 同屏对照:`design-qa/20260813-app-workbench/workbench-comparison.png` +- 对照视口:1440 × 1024,设备像素比 1。 + +### 设计与交互检查 + +- 系统名称统一为“风险防控处置系统”,顶部保留统一身份、角色、通知与设置入口。 +- 门户层只承担应用选择:最近使用、应用搜索、全部应用和权限状态,不堆叠具体业务指标。 +- 12398 工单系统为可用状态;风险事件管理、合规检查以“即将接入”呈现,点击提供明确反馈。 +- 点击最近使用或全部应用中的 12398 工单系统可进入现有业务界面;点击业务系统左上角品牌可返回应用工作台。 +- 视觉稿中的专用应用图标由项目现有 Ant Design Vue 图标替代,尺寸、层级与语义一致,属于可接受的 P3 差异。 + +### 工程验证 + +- `npm run build` 通过,Vite 转换 1616 个模块。 +- `npm run test:sites` 通过,4 项测试全部成功。 + +## 结论 + +本轮以 `deprecated/build/20260812 prototype` 为最新视觉真值,针对主任务检查中确认的登录页、服务监督主管审核弹窗和客服部主管首联分派弹窗完成高保真修复。经同尺寸对照、浏览器交互检查、生产构建和现有流程测试复核,未发现遗留的 P0、P1 或 P2 问题。 + +## 比较基准 + +- 登录页:`../deprecated/build/20260812 prototype/preview/icons/页面-1-1-登录页2/登录页2@0.5x.png` +- 服务监督主管审核:`../deprecated/build/20260812 prototype/preview/icons/页面-1-4-服务监督主管-工单处理(审核弹窗)/服务监督主管-工单处理(审核弹窗)@0.5x.png` +- 客服部主管首联分派:`../deprecated/build/20260812 prototype/preview/icons/页面-1-32-客服部主管-待办工单-首联分派/客服部主管-待办工单-首联分派@0.5x.png` +- 浏览器终稿截图:`design-qa/20260813-fidelity-fix/10-login-fixed-final.png`、`design-qa/20260813-fidelity-fix/04-supervisor-review-fixed-final.png`、`design-qa/20260813-fidelity-fix/06-first-assignment-fixed-final.png` +- 同屏对照:`design-qa/20260813-fidelity-fix/login-comparison-final.png`、`design-qa/20260813-fidelity-fix/supervisor-review-comparison.png`、`design-qa/20260813-fidelity-fix/first-assignment-comparison.png` + +## 尺寸与归一化 + +- 浏览器 CSS 视口:1920 × 1080。 +- 原型基准图:960 × 540(`@0.5x`)。 +- 对照时将实现截图缩放至 960 × 540;比较图左侧为原型基准,右侧为实现终稿。 +- 服务监督主管审核弹窗终稿:1080 × 731,顶部约 145;内部面板 1000 × 504。 +- 首联分派弹窗终稿:1080 × 492,顶部 194;内部面板 1000 × 265。 + +## 本轮修复 + +1. 登录页 + - 标题颜色顺序恢复为深蓝主标题、青绿色强调语。 + - 放大主副标题并禁止不符合设计的换行,恢复原型的横向构图。 + - 登录表单默认保持空白,移除原型中不存在的输入框前置图标。 + - 校准登录卡片宽度、内边距、字段高度、验证码和按钮样式。 +2. 服务监督主管审核弹窗 + - 校准弹窗宽度、垂直位置、内面板高度、字段高度、驳回理由区域和底部按钮尺寸。 + - 保持审核结果、驳回理由及回访分派的既有交互和条件显示。 +3. 客服部主管首联分派弹窗 + - 将弹窗整体上移 100px,使外框、内面板和操作按钮与原型基准对齐。 + - 移动端断点取消位移,避免窄屏内容越界。 + +## 五项保真检查 + +- 字体与排版:登录标题、弹窗标题、字段标签和按钮层级与原型一致,无异常换行或截断。 +- 间距与布局:三处重点页面的容器尺寸、垂直位置、内边距和按钮组已按同尺寸对照校准。 +- 色彩:保持原型的深蓝标题、青绿色强调色、浅青弹窗背景、白色内容面板和灰色遮罩。 +- 图片与图标:继续复用原型登录背景和既有 Ant Design 图标;登录输入框未保留基准图中不存在的装饰图标。 +- 文案与内容:重点页面标题、字段、占位提示和按钮文案与 20260812 原型一致。 + +## 交互与工程验证 + +- 浏览器交互:登录、角色切换、首联分派筛选、首联分派弹窗打开、服务监督主管审核弹窗打开和取消均正常。 +- 生产构建:`npm run build` 通过,Vite 共转换 1616 个模块。 +- 流程测试:`npm run test:sites` 通过,4 项测试全部成功。 + +## 已确认的有意差异 + +- 派工主体仍为服务监督主管。 +- 主责事件必须等待首联及全部初查完成;协查事件从主管判定和派工进入 04 多部门并行事件初查。 +- 业务部主任审核通过后,主责进入回访分支,协查直接进入 11 调查报告编写。 +- 以上差异来自已确认业务规则,优先级高于静态画板,不计入视觉缺陷,也未在本轮改动中回退。 + +final result: passed diff --git a/frontend/design-qa/20260812-compare-director-review.png b/frontend/design-qa/20260812-compare-director-review.png new file mode 100644 index 0000000..1a06df9 Binary files /dev/null and b/frontend/design-qa/20260812-compare-director-review.png differ diff --git a/frontend/design-qa/20260812-compare-first-assignment.png b/frontend/design-qa/20260812-compare-first-assignment.png new file mode 100644 index 0000000..4559617 Binary files /dev/null and b/frontend/design-qa/20260812-compare-first-assignment.png differ diff --git a/frontend/design-qa/20260812-compare-report-disagree-bottom.png b/frontend/design-qa/20260812-compare-report-disagree-bottom.png new file mode 100644 index 0000000..071cb84 Binary files /dev/null and b/frontend/design-qa/20260812-compare-report-disagree-bottom.png differ diff --git a/frontend/design-qa/20260812-compare-report-disagree.png b/frontend/design-qa/20260812-compare-report-disagree.png new file mode 100644 index 0000000..9c06adf Binary files /dev/null and b/frontend/design-qa/20260812-compare-report-disagree.png differ diff --git a/frontend/design-qa/20260812-compare-supervisor-review.png b/frontend/design-qa/20260812-compare-supervisor-review.png new file mode 100644 index 0000000..60eab31 Binary files /dev/null and b/frontend/design-qa/20260812-compare-supervisor-review.png differ diff --git a/frontend/design-qa/20260812-director-review.png b/frontend/design-qa/20260812-director-review.png new file mode 100644 index 0000000..a541d9d Binary files /dev/null and b/frontend/design-qa/20260812-director-review.png differ diff --git a/frontend/design-qa/20260812-first-assignment.png b/frontend/design-qa/20260812-first-assignment.png new file mode 100644 index 0000000..0fe7747 Binary files /dev/null and b/frontend/design-qa/20260812-first-assignment.png differ diff --git a/frontend/design-qa/20260812-report-disagree-bottom.png b/frontend/design-qa/20260812-report-disagree-bottom.png new file mode 100644 index 0000000..47c297f Binary files /dev/null and b/frontend/design-qa/20260812-report-disagree-bottom.png differ diff --git a/frontend/design-qa/20260812-report-disagree.png b/frontend/design-qa/20260812-report-disagree.png new file mode 100644 index 0000000..7c176f7 Binary files /dev/null and b/frontend/design-qa/20260812-report-disagree.png differ diff --git a/frontend/design-qa/20260812-supervisor-review.png b/frontend/design-qa/20260812-supervisor-review.png new file mode 100644 index 0000000..4387260 Binary files /dev/null and b/frontend/design-qa/20260812-supervisor-review.png differ diff --git a/frontend/design-qa/20260813-app-workbench/workbench-1440x1024.png b/frontend/design-qa/20260813-app-workbench/workbench-1440x1024.png new file mode 100644 index 0000000..7b2ebae Binary files /dev/null and b/frontend/design-qa/20260813-app-workbench/workbench-1440x1024.png differ diff --git a/frontend/design-qa/20260813-app-workbench/workbench-comparison.png b/frontend/design-qa/20260813-app-workbench/workbench-comparison.png new file mode 100644 index 0000000..3d3c096 Binary files /dev/null and b/frontend/design-qa/20260813-app-workbench/workbench-comparison.png differ diff --git a/frontend/design-qa/20260813-app-workbench/workbench-reference.png b/frontend/design-qa/20260813-app-workbench/workbench-reference.png new file mode 100644 index 0000000..c96fe0b Binary files /dev/null and b/frontend/design-qa/20260813-app-workbench/workbench-reference.png differ diff --git a/frontend/design-qa/20260813-fidelity-fix/04-supervisor-review-fixed-final.png b/frontend/design-qa/20260813-fidelity-fix/04-supervisor-review-fixed-final.png new file mode 100644 index 0000000..863d925 Binary files /dev/null and b/frontend/design-qa/20260813-fidelity-fix/04-supervisor-review-fixed-final.png differ diff --git a/frontend/design-qa/20260813-fidelity-fix/06-first-assignment-fixed-final.png b/frontend/design-qa/20260813-fidelity-fix/06-first-assignment-fixed-final.png new file mode 100644 index 0000000..02b70f0 Binary files /dev/null and b/frontend/design-qa/20260813-fidelity-fix/06-first-assignment-fixed-final.png differ diff --git a/frontend/design-qa/20260813-fidelity-fix/10-login-fixed-final.png b/frontend/design-qa/20260813-fidelity-fix/10-login-fixed-final.png new file mode 100644 index 0000000..80227e1 Binary files /dev/null and b/frontend/design-qa/20260813-fidelity-fix/10-login-fixed-final.png differ diff --git a/frontend/design-qa/20260813-fidelity-fix/first-assignment-comparison.png b/frontend/design-qa/20260813-fidelity-fix/first-assignment-comparison.png new file mode 100644 index 0000000..9920c6a Binary files /dev/null and b/frontend/design-qa/20260813-fidelity-fix/first-assignment-comparison.png differ diff --git a/frontend/design-qa/20260813-fidelity-fix/login-comparison-final.png b/frontend/design-qa/20260813-fidelity-fix/login-comparison-final.png new file mode 100644 index 0000000..442ce70 Binary files /dev/null and b/frontend/design-qa/20260813-fidelity-fix/login-comparison-final.png differ diff --git a/frontend/design-qa/20260813-fidelity-fix/supervisor-review-comparison.png b/frontend/design-qa/20260813-fidelity-fix/supervisor-review-comparison.png new file mode 100644 index 0000000..1baf251 Binary files /dev/null and b/frontend/design-qa/20260813-fidelity-fix/supervisor-review-comparison.png differ diff --git a/frontend/design-qa/callback-upload-modal.png b/frontend/design-qa/callback-upload-modal.png new file mode 100644 index 0000000..1a61442 Binary files /dev/null and b/frontend/design-qa/callback-upload-modal.png differ diff --git a/frontend/design-qa/compare-callback-upload-final.png b/frontend/design-qa/compare-callback-upload-final.png new file mode 100644 index 0000000..6af26cb Binary files /dev/null and b/frontend/design-qa/compare-callback-upload-final.png differ diff --git a/frontend/design-qa/compare-dashboard-final.png b/frontend/design-qa/compare-dashboard-final.png new file mode 100644 index 0000000..b8fb236 Binary files /dev/null and b/frontend/design-qa/compare-dashboard-final.png differ diff --git a/frontend/design-qa/compare-dispatch.png b/frontend/design-qa/compare-dispatch.png new file mode 100644 index 0000000..01ea007 Binary files /dev/null and b/frontend/design-qa/compare-dispatch.png differ diff --git a/frontend/design-qa/compare-first-contact.png b/frontend/design-qa/compare-first-contact.png new file mode 100644 index 0000000..3eac902 Binary files /dev/null and b/frontend/design-qa/compare-first-contact.png differ diff --git a/frontend/design-qa/compare-import-final.png b/frontend/design-qa/compare-import-final.png new file mode 100644 index 0000000..e4015ca Binary files /dev/null and b/frontend/design-qa/compare-import-final.png differ diff --git a/frontend/design-qa/compare-investigation.png b/frontend/design-qa/compare-investigation.png new file mode 100644 index 0000000..dc1a83d Binary files /dev/null and b/frontend/design-qa/compare-investigation.png differ diff --git a/frontend/design-qa/compare-review-detail.png b/frontend/design-qa/compare-review-detail.png new file mode 100644 index 0000000..ba8f78b Binary files /dev/null and b/frontend/design-qa/compare-review-detail.png differ diff --git a/frontend/design-qa/dispatch-assist.png b/frontend/design-qa/dispatch-assist.png new file mode 100644 index 0000000..ebb92a6 Binary files /dev/null and b/frontend/design-qa/dispatch-assist.png differ diff --git a/frontend/design-qa/first-contact.png b/frontend/design-qa/first-contact.png new file mode 100644 index 0000000..b9adc05 Binary files /dev/null and b/frontend/design-qa/first-contact.png differ diff --git a/frontend/design-qa/import-default.png b/frontend/design-qa/import-default.png new file mode 100644 index 0000000..2189ffb Binary files /dev/null and b/frontend/design-qa/import-default.png differ diff --git a/frontend/design-qa/investigation.png b/frontend/design-qa/investigation.png new file mode 100644 index 0000000..099bad5 Binary files /dev/null and b/frontend/design-qa/investigation.png differ diff --git a/frontend/design-qa/mobile-app.png b/frontend/design-qa/mobile-app.png new file mode 100644 index 0000000..8071a0e Binary files /dev/null and b/frontend/design-qa/mobile-app.png differ diff --git a/frontend/design-qa/review-detail.png b/frontend/design-qa/review-detail.png new file mode 100644 index 0000000..b75cf79 Binary files /dev/null and b/frontend/design-qa/review-detail.png differ diff --git a/frontend/design-qa/supervisor-dashboard-final.png b/frontend/design-qa/supervisor-dashboard-final.png new file mode 100644 index 0000000..9acd5c6 Binary files /dev/null and b/frontend/design-qa/supervisor-dashboard-final.png differ diff --git a/frontend/design-qa/supervisor-dispatch.png b/frontend/design-qa/supervisor-dispatch.png new file mode 100644 index 0000000..60444bc Binary files /dev/null and b/frontend/design-qa/supervisor-dispatch.png differ diff --git a/frontend/frontend.7z b/frontend/frontend.7z new file mode 100644 index 0000000..d3b3124 Binary files /dev/null and b/frontend/frontend.7z differ diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..306b3fc --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,12 @@ + + + + + + 12398 工单系统 + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..058e508 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,1310 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@ant-design/icons-vue": "7.0.1", + "@vitejs/plugin-vue": "6.0.1", + "vite": "6.4.2", + "vue": "3.5.24" + }, + "devDependencies": {} + }, + "node_modules/@ant-design/colors": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz", + "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==", + "license": "MIT", + "dependencies": { + "@ctrl/tinycolor": "^3.4.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.5.0.tgz", + "integrity": "sha512-1BTUFyKPTBZ53MuTP8s0k5SFEXL7o3VHEOwLgzaoWKwnBeqIcqUtVshc4SKzhI6uACfqhJqBwBUE9FsWR3uULA==", + "license": "MIT" + }, + "node_modules/@ant-design/icons-vue": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/icons-vue/-/icons-vue-7.0.1.tgz", + "integrity": "sha512-eCqY2unfZK6Fe02AwFlDHLfoyEFreP6rBwAZMIJ1LugmfMiVgwWDYlp1YsRugaPtICYOabV1iWxXdP12u9U43Q==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^6.0.0", + "@ant-design/icons-svg": "^4.2.1" + }, + "peerDependencies": { + "vue": ">=3.0.3" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@ctrl/tinycolor": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", + "integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@vitejs/plugin-vue": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz", + "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==", + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.29" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0", + "vue": "^3.2.25" + } + }, + "node_modules/@vitejs/plugin-vue/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.29", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz", + "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==", + "license": "MIT" + }, + "node_modules/@vue/compiler-core": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.24.tgz", + "integrity": "sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/shared": "3.5.24", + "entities": "^4.5.0", + "estree-walker": "^2.0.2", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-dom": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.24.tgz", + "integrity": "sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==", + "license": "MIT", + "dependencies": { + "@vue/compiler-core": "3.5.24", + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/compiler-sfc": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.24.tgz", + "integrity": "sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@vue/compiler-core": "3.5.24", + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24", + "estree-walker": "^2.0.2", + "magic-string": "^0.30.21", + "postcss": "^8.5.6", + "source-map-js": "^1.2.1" + } + }, + "node_modules/@vue/compiler-ssr": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.24.tgz", + "integrity": "sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.24", + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/reactivity": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.24.tgz", + "integrity": "sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==", + "license": "MIT", + "dependencies": { + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/runtime-core": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.24.tgz", + "integrity": "sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.24", + "@vue/shared": "3.5.24" + } + }, + "node_modules/@vue/runtime-dom": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.24.tgz", + "integrity": "sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==", + "license": "MIT", + "dependencies": { + "@vue/reactivity": "3.5.24", + "@vue/runtime-core": "3.5.24", + "@vue/shared": "3.5.24", + "csstype": "^3.1.3" + } + }, + "node_modules/@vue/server-renderer": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.24.tgz", + "integrity": "sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==", + "license": "MIT", + "dependencies": { + "@vue/compiler-ssr": "3.5.24", + "@vue/shared": "3.5.24" + }, + "peerDependencies": { + "vue": "3.5.24" + } + }, + "node_modules/@vue/shared": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.24.tgz", + "integrity": "sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vue": { + "version": "3.5.24", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", + "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", + "license": "MIT", + "dependencies": { + "@vue/compiler-dom": "3.5.24", + "@vue/compiler-sfc": "3.5.24", + "@vue/runtime-dom": "3.5.24", + "@vue/server-renderer": "3.5.24", + "@vue/shared": "3.5.24" + }, + "peerDependencies": { + "typescript": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..488caa9 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,19 @@ +{ + "name": "frontend", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build && node scripts/prepare-sites-build.mjs", + "preview": "vite preview", + "test:sites": "node --test tests/sites-worker.test.mjs" + }, + "dependencies": { + "@ant-design/icons-vue": "7.0.1", + "@vitejs/plugin-vue": "6.0.1", + "vite": "6.4.2", + "vue": "3.5.24" + }, + "devDependencies": {} +} diff --git a/frontend/prototype-coverage.md b/frontend/prototype-coverage.md new file mode 100644 index 0000000..38f586a --- /dev/null +++ b/frontend/prototype-coverage.md @@ -0,0 +1,62 @@ +# 20260812 原型板覆盖矩阵 + +本矩阵以 `build/20260812 prototype/preview` 的 41 张画板为范围。弹窗态和同一业务页的条件状态复用 Vue 组件,但均可从对应角色工作台进入。 + +| 编号 | 最新原型板 | Vue 对应入口/状态 | 覆盖结果 | +| --- | --- | --- | --- | +| 1-1 | 登录页 | 登录页 | 已覆盖 | +| 1-2 | 工单导入预览 | 服务监督主管 → 工单导入 → 导入预览 | 已覆盖 | +| 1-3 | 工单导入明细 | 服务监督主管 → 工单导入 → 导入明细 | 已覆盖 | +| 1-4 | 服务监督主管审核弹窗 | 服务监督主管 → 待审核 → 审核 | 已覆盖;审核结果、驳回理由,主责时显示回访部门 | +| 1-5 | 服务监督主管待审核 | 服务监督主管 → 待审核统计卡 | 已覆盖 | +| 1-6 | 智能派工规则配置 | 规则配置 → 智能派工规则配置 | 已覆盖,保持原型空白内容区 | +| 1-7 | 流程时限管理 | 规则配置 → 流程时限管理 | 已覆盖 | +| 1-8 | 统计分析 | 统计分析 | 已覆盖,保持原型空白内容区 | +| 1-9 | 服务监督主管完整审核 | 审核页面状态 | 已覆盖 | +| 1-10 | 待分配派工列表 | 服务监督主管 → 待分配派工 | 已覆盖 | +| 1-11 | 待分派详情 | 待分配派工 → 派工 | 已覆盖;派工主体、主责/协查和前置条件保持业务修订 | +| 1-12 | 服务监督主管工单处理 | 服务监督主管 → 工单处理 | 已覆盖 | +| 1-13 | 调查结果填报(业务类型) | 服务监督人员 → 调查结果填报 → 处理 | 已覆盖 | +| 1-14 | 调查报告编写(不同意) | 服务监督人员 → 调查报告编写 → 选择“不同意或客户主动要求” | 已覆盖;内联显示答复书和送达凭证 | +| 1-15 | 调查报告编写(同意) | 服务监督人员 → 调查报告编写 → 选择“同意” | 已覆盖;无附件区,支持确认下载 | +| 1-16 | 成果物输出 | 服务监督人员 → 成果物输出 | 已覆盖 | +| 1-17 | 回访结果不同意上传 | 回访结果选择“不同意或客户主动要求” | 已覆盖;附件直接显示于表单,不再使用二次弹窗 | +| 1-18 | 业务部主任审核弹窗 | 业务部主任 → 待审核 → 审核 | 已覆盖;主责选择回访部门,协查直接进入 11 调查报告 | +| 1-19 | 业务部主任完整审核 | 业务部主任 → 待审核 → 工单审核状态 | 已覆盖 | +| 1-20 | 回访结果填写 | 服务监督人员 → 回访结果填报 → 处理 | 已覆盖;同意/不同意两种状态 | +| 1-21 | 调查结果填报 | 服务监督人员 → 调查结果填报 → 处理 | 已覆盖 | +| 1-22 | 服务监督人员待办 | 切换角色 → 服务监督人员 | 已覆盖 | +| 1-23 | 总值班经理初查 | 总值班经理 → 待调查 → 处理 | 已覆盖 | +| 1-24 | 智能运营团队初查 | 智能运营团队 → 待调查 → 处理 | 已覆盖 | +| 1-25 | 客服部主管待调研 | 客服部主管对应调查任务 | 已覆盖 | +| 1-26 | 客服部班长待办备份 | 客服部班长 → 待首联 | 已覆盖 | +| 1-27 | 业务部主任待办 | 切换角色 → 业务部主任 | 已覆盖;待审核/已完成两类统计 | +| 1-28 | 总值班经理待办 | 切换角色 → 总值班经理 | 已覆盖 | +| 1-29 | 智能运营团队待办 | 切换角色 → 智能运营团队 | 已覆盖 | +| 1-30 | 客服部班长待办 | 切换角色 → 客服部班长 | 已覆盖 | +| 1-31 | 客服部主管回访分派 | 客服部主管 → 回访分派 → 分派弹窗 | 已覆盖 | +| 1-32 | 客服部主管首联分派 | 客服部主管 → 首联分派 → 分派弹窗 | 已覆盖 | +| 1-33 | 客服部主管待办 | 切换角色 → 客服部主管 | 已覆盖;任务总数、首联分派、回访分派三类统计 | +| 1-34 | 业务部主任 | 业务部主任审核详情 | 已覆盖 | +| 1-35 | 总值班经理工单查询 | 总值班经理 → 工单查询 | 已覆盖 | +| 1-36 | 智能运营团队工单查询 | 智能运营团队 → 工单查询 | 已覆盖 | +| 1-37 | 客服部班长工单查询 | 客服部班长 → 工单查询 | 已覆盖 | +| 1-38 | 客服部主管工单查询 | 客服部主管 → 工单查询 | 已覆盖 | +| 1-39 | 服务监督人员工单查询 | 服务监督人员 → 工单查询 | 已覆盖 | +| 1-40 | 服务监督主管工单查询 | 服务监督主管 → 工单查询 | 已覆盖 | +| 1-41 | 工单导入入口 | 服务监督主管 → 工单导入 | 已覆盖 | + +## 20260812 关键变化 + +- 审核弹窗统一为选择式审核结果、驳回理由和条件化回访部门;未选择审核结果不可提交。 +- 业务部主任审核成为独立可操作弹窗;主责审核通过进入回访,协查审核通过直接进入 11 调查报告编写。 +- 调查报告页不再提供自定义报告编辑器,改为展示调查及回访信息,并以“确认下载”完成报告输出。 +- 回访结果统一为“同意 / 不同意或客户主动要求”;不同意时答复书和送达凭证在当前页面直接出现并执行双附件校验。 +- 客服部主管待办统计调整为任务总数、首联分派、回访分派;两类分派弹窗均使用班组/人员单选。 + +## 保留的业务约束 + +- 派工主体为服务监督主管。 +- 主责工单允许服务监督员先保存草稿,但必须等待首联和全部初查完成后提交。 +- 协查工单派工后直接进入 04 多部门并行事件初查,全部初查完成后才能提交调查。 +- 07 业务部主任审核通过后,根据前序责任类型分支:主责进入回访,协查直接进入 11 调查报告编写。 diff --git a/frontend/public/assets/login-bg.png b/frontend/public/assets/login-bg.png new file mode 100644 index 0000000..72010de Binary files /dev/null and b/frontend/public/assets/login-bg.png differ diff --git a/frontend/scripts/prepare-sites-build.mjs b/frontend/scripts/prepare-sites-build.mjs new file mode 100644 index 0000000..86a9e8c --- /dev/null +++ b/frontend/scripts/prepare-sites-build.mjs @@ -0,0 +1,21 @@ +#!/usr/bin/env node +import { copyFileSync, existsSync, mkdirSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const dist = path.join(root, "dist"); +const index = path.join(dist, "client", "index.html"); +const worker = path.join(root, "worker", "index.js"); +const hosting = path.join(root, ".openai", "hosting.json"); + +for (const file of [index, worker, hosting]) { + if (!existsSync(file)) throw new Error("Missing Sites build input: " + file); +} + +mkdirSync(path.join(dist, "server"), { recursive: true }); +mkdirSync(path.join(dist, ".openai"), { recursive: true }); +copyFileSync(worker, path.join(dist, "server", "index.js")); +copyFileSync(hosting, path.join(dist, ".openai", "hosting.json")); + +console.log("Prepared Sites build: dist/server/index.js and dist/.openai/hosting.json"); diff --git a/frontend/src/App.vue b/frontend/src/App.vue new file mode 100644 index 0000000..8e8b1d8 --- /dev/null +++ b/frontend/src/App.vue @@ -0,0 +1,659 @@ + + + diff --git a/frontend/src/components/FilterPanel.vue b/frontend/src/components/FilterPanel.vue new file mode 100644 index 0000000..347c2cd --- /dev/null +++ b/frontend/src/components/FilterPanel.vue @@ -0,0 +1,27 @@ + + + diff --git a/frontend/src/components/InvestigationFields.vue b/frontend/src/components/InvestigationFields.vue new file mode 100644 index 0000000..eeca52a --- /dev/null +++ b/frontend/src/components/InvestigationFields.vue @@ -0,0 +1,41 @@ + + + diff --git a/frontend/src/components/OrderContext.vue b/frontend/src/components/OrderContext.vue new file mode 100644 index 0000000..265ed9d --- /dev/null +++ b/frontend/src/components/OrderContext.vue @@ -0,0 +1,13 @@ + + + diff --git a/frontend/src/components/OrderTable.vue b/frontend/src/components/OrderTable.vue new file mode 100644 index 0000000..b726cc3 --- /dev/null +++ b/frontend/src/components/OrderTable.vue @@ -0,0 +1,17 @@ + + + diff --git a/frontend/src/main.js b/frontend/src/main.js new file mode 100644 index 0000000..8d6a5ff --- /dev/null +++ b/frontend/src/main.js @@ -0,0 +1,5 @@ +import { createApp } from "vue"; +import App from "./App.vue"; +import "./styles.css"; + +createApp(App).mount("#app"); diff --git a/frontend/src/mock/data.js b/frontend/src/mock/data.js new file mode 100644 index 0000000..ea8a5ac --- /dev/null +++ b/frontend/src/mock/data.js @@ -0,0 +1,91 @@ +export const roles = [ + { id: "supervisor", name: "服务监督主管" }, + { id: "officer", name: "服务监督人员" }, + { id: "customer-manager", name: "客服部主管" }, + { id: "team-leader", name: "客服部班长" }, + { id: "operation", name: "智能运营团队" }, + { id: "duty-manager", name: "总值班经理" }, + { id: "business-director", name: "业务部主任" }, +]; + +const mockStages = [ + "待分配派工", "事件调查", "待审核", "回访结果填报", "首联分派", "多部门并行事件初查", + "待审核", "待回访分配", "调查报告编写", "成果物输出", "已办结", "待业务部主任审核", + "首联与多部门并行初查", "调查结果填报", "回访结果填报", "调查报告编写", "成果物输出", "待分配派工", +]; + +export const workOrders = Array.from({ length: 18 }, (_, index) => ({ + id: `wo-${index + 1}`, + no: `2026-08-06-${String(index + 1).padStart(3, "0")}`, + status: mockStages[index] === "已办结" ? "已结束" : /待|分派/.test(mockStages[index]) ? "待分派" : "处理中", + stage: mockStages[index], + source: "12398 电话", + calledAt: "2026-08-06", + transferredAt: "2026-08-06", + customerName: index % 2 ? "张某某" : "某某有限公司", + phone: index % 2 ? "137****1111" : "138****8888", + address: "XX市XX区XX路88号", + region: "XX省XX市", + province: "天津", + eventType: "投诉事项", + transferCategory: "投诉事项", + problemCategory: "安全隐患", + responsibilityType: [5, 6, 8, 11, 15].includes(index) ? "协查" : "主责", + firstContactCompleted: ![0, 4, 12, 17].includes(index), + preliminaryRequired: index % 2 === 0 ? 2 : 3, + preliminaryCompletedCount: [0, 1, 5, 12, 13, 17].includes(index) ? index % 2 : (index % 2 === 0 ? 2 : 3), + preliminarySubmittedBy: [], + preliminaryCompleted: ![0, 1, 5, 12, 13, 17].includes(index), +})); + +export const serviceRecords = Array.from({ length: 5 }, (_, index) => ({ + id: index + 1, + no: "2026-08-06-001", + content: "客户反映近期电压不稳,设备频繁重启。", + type: "投诉事项", + acceptedAt: "2026-08-06", + company: "国网天津市电力公司", + duration: "06:24", + transcript: "客户反映近期电压不稳,设备频繁重启,要求尽快处理。", +})); + +export const importBatches = Array.from({ length: 5 }, (_, index) => ({ + id: index + 1, + importedAt: `2026-08-0${index + 2} 12:00`, + batchNo: `2026080${index + 2}-B01`, + count: 157 - index * 8, + importedBy: index % 2 ? "李工" : "张工", +})); + +export const taskSummary = { + supervisor: [ + ["任务总数", 103], + ["待分配派工", 22], + ["已分派完成", 63], + ["待审核", 47], + ], + officer: [ + ["任务总数", 103], + ["调查结果填报", 34], + ["调查报告编写", 12], + ["回访结果填报", 56], + ["成果物输出", 60], + ["已完成", 69], + ], + "customer-manager": [ + ["任务总数", 103], + ["首联分派", 69], + ["回访分派", 69], + ], + "team-leader": [["待首联", 103], ["已完成", 69]], + operation: [["待调查", 83], ["已完成", 69]], + "duty-manager": [["待调查", 83], ["已完成", 69]], + "business-director": [["待审核", 83], ["已完成", 69]], +}; + +export const dictionary = { + departments: ["客服一部", "客服二部", "客服三部", "客服五部", "业务部", "运营部"], + firstContactDepartments: ["客服一部", "客服二部", "客服三部", "客服五部"], + supervisors: ["监督员A(张某)", "监督员B(李某)", "监督员C(王某)"], + teamMembers: ["客服一班 / 王小明", "客服二班 / 李小红", "客服三班 / 赵强"], +}; diff --git a/frontend/src/styles.css b/frontend/src/styles.css new file mode 100644 index 0000000..c468973 --- /dev/null +++ b/frontend/src/styles.css @@ -0,0 +1,430 @@ +:root { + font-family: "Microsoft YaHei", "PingFang SC", Arial, sans-serif; + color: #303838; + background: #f3f6f6; + font-synthesis: none; + --brand: #2a8b7f; + --brand-dark: #176a61; + --brand-soft: #e5f5f2; + --line: #dbe3e2; + --muted: #84908f; + --page: #f4f6f7; + --danger: #d85c5c; +} + +* { box-sizing: border-box; } + +html, body, #app { margin: 0; min-width: 1180px; min-height: 100%; } +body { min-height: 100vh; overflow-x: auto; -webkit-font-smoothing: antialiased; } +button, input, select, textarea { font: inherit; } +button { cursor: pointer; } + +.login-screen { + min-height: 100vh; + background-color: #dceeed; + background-image: url("/assets/login-bg.png"); + background-repeat: no-repeat; + background-position: center; + background-size: cover; + display: flex; + align-items: center; + justify-content: flex-end; + padding-right: 14vw; +} +.login-panel { width: 500px; transform: translateY(2px); } +.login-heading { text-align: center; margin-bottom: 75px; } +.login-kicker { color: #111d38; font-size: 52px; font-weight: 800; letter-spacing: 1px; white-space: nowrap; } +.login-kicker span { margin-left: 10px; color: #26887d; } +.login-heading p { color: #51706d; font-size: 24px; margin: 18px 0 0; letter-spacing: 2px; white-space: nowrap; } +.login-card { background: rgba(255,255,255,.96); padding: 46px 42px 71px; border-radius: 14px; box-shadow: 0 24px 70px rgba(35,86,80,.16); } +.login-card h1 { font-size: 34px; text-align: center; margin: 0 0 39px; color: #111d38; } +.login-field { height: 58px; border: 1px solid #4ca69c; border-radius: 4px; display: flex; align-items: center; gap: 14px; padding: 0 18px; margin-bottom: 32px; color: #67a79f; background: #fff; transition: border-color .2s, box-shadow .2s; } +.login-field:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(42,139,127,.12); } +.login-field input { flex: 1; border: 0; outline: 0; font-size: 16px; color: #33403f; min-width: 0; } +.captcha-row { display: grid; grid-template-columns: 1fr 160px; gap: 20px; } +.captcha { height: 58px; border: 0; border-radius: 4px; background: #64aaa2; color: #fff; font-size: 24px; font-weight: 800; font-style: italic; letter-spacing: 5px; } +.login-button, .primary-button { color: #fff; border: 1px solid var(--brand); background: var(--brand); border-radius: 7px; font-weight: 600; } +.login-button { width: 100%; height: 58px; font-size: 18px; margin-top: 0; border-radius: 4px; } +.login-button:hover, .primary-button:hover { background: var(--brand-dark); border-color: var(--brand-dark); } +.login-button:disabled { opacity: .7; cursor: wait; } +.primary-button:disabled { opacity: .48; cursor: not-allowed; background: #7fa7a2; border-color: #7fa7a2; } + +.portal-shell { min-height: 100vh; background: #f7f9f9; color: #111d38; display: flex; flex-direction: column; } +.portal-header { height: 92px; padding: 0 32px; background: #fff; border-bottom: 1px solid #dfe7e6; display: flex; align-items: center; gap: 14px; position: relative; z-index: 20; } +.portal-brand { display: flex; align-items: center; gap: 15px; font-size: 26px; } +.portal-brand svg { color: var(--brand); font-size: 42px; } +.portal-brand strong { white-space: nowrap; } +.portal-header-spacer { flex: 1; } +.portal-header .role-button { min-width: 154px; } +.portal-role-menu { top: 61px; } +.portal-notice { top: 76px; right: 54px; } +.portal-content { width: min(1328px, calc(100% - 80px)); margin: 0 auto; padding: 62px 0 64px; flex: 1; } +.portal-intro { display: flex; align-items: center; justify-content: space-between; gap: 60px; margin-bottom: 58px; } +.portal-intro h1 { margin: 0; font-size: 40px; line-height: 1.3; letter-spacing: .5px; } +.portal-intro p { margin: 16px 0 0; color: #6d7776; font-size: 18px; } +.portal-search { width: 440px; height: 54px; flex: 0 0 440px; background: #fff; border: 1px solid #bfcfcd; border-radius: 6px; display: flex; align-items: center; gap: 13px; padding: 0 18px; color: #697573; } +.portal-search:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px rgba(42,139,127,.1); } +.portal-search input { flex: 1; min-width: 0; border: 0; outline: 0; color: #273332; background: transparent; font-size: 16px; } +.portal-section { margin-top: 0; } +.portal-section + .portal-section { margin-top: 42px; } +.portal-section h2 { margin: 0 0 20px; font-size: 20px; color: #18243e; } +.recent-app { width: 100%; height: 148px; padding: 0 28px; background: #fff; border: 1px solid #ccd9d7; border-radius: 8px; display: flex; align-items: center; text-align: left; color: inherit; transition: border-color .18s, box-shadow .18s, transform .18s; } +.recent-app:hover { border-color: var(--brand); box-shadow: 0 10px 28px rgba(31,85,78,.08); transform: translateY(-1px); } +.portal-app-icon { width: 78px; height: 78px; flex: 0 0 78px; border-radius: 10px; background: var(--brand); color: #fff; display: grid; place-items: center; } +.portal-app-icon svg { font-size: 40px; } +.recent-copy { margin-left: 30px; min-width: 0; display: flex; flex-direction: column; gap: 13px; flex: 1; } +.recent-copy strong { font-size: 24px; } +.recent-copy em, .micro-app-copy em { color: #6d7776; font-style: normal; line-height: 1.65; } +.recent-todo { width: 210px; min-height: 82px; padding-left: 30px; border-left: 1px solid #d9e2e1; display: flex; flex-direction: column; justify-content: center; gap: 6px; } +.recent-todo em { color: #6d7776; font-style: normal; } +.recent-todo strong { color: var(--brand); font-size: 31px; } +.portal-enter { min-width: 124px; height: 48px; margin-left: 18px; border: 1px solid var(--brand); border-radius: 6px; color: var(--brand); font-weight: 700; display: inline-flex; align-items: center; justify-content: center; } +.recent-arrow { margin-left: 26px; color: var(--brand); font-size: 18px; } +.micro-app-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; } +.micro-app-card { min-height: 276px; padding: 30px 26px 26px; border: 1px solid #ccd9d7; border-radius: 8px; background: #fff; color: inherit; text-align: left; display: grid; grid-template-columns: 72px minmax(0, 1fr); grid-template-rows: 1fr auto; gap: 0 22px; transition: border-color .18s, box-shadow .18s, transform .18s; } +.micro-app-card:hover:not(.disabled) { border-color: var(--brand); box-shadow: 0 10px 28px rgba(31,85,78,.08); transform: translateY(-2px); } +.micro-app-card .portal-app-icon { width: 72px; height: 72px; flex-basis: 72px; } +.micro-app-card .portal-app-icon svg { font-size: 36px; } +.micro-app-card.disabled { cursor: default; } +.micro-app-card.disabled .portal-app-icon { background: #64aaa2; } +.micro-app-copy { display: flex; flex-direction: column; gap: 18px; min-width: 0; } +.micro-app-copy > span { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } +.micro-app-copy strong { font-size: 22px; white-space: nowrap; } +.micro-app-copy i { padding: 5px 12px; border-radius: 16px; background: #f0f1f2; color: #7e8585; font-style: normal; font-size: 13px; white-space: nowrap; } +.micro-app-copy i.available { color: #278778; background: #e3f5ef; } +.micro-app-action { grid-column: 1 / 3; align-self: end; color: #6f7776; font-weight: 700; display: inline-flex; align-items: center; gap: 8px; } +.micro-app-card:not(.disabled) .micro-app-action { color: var(--brand); } +.portal-empty { height: 160px; border: 1px dashed #ccd8d6; border-radius: 8px; background: #fff; color: #8a9492; display: grid; place-items: center; } +.portal-footer { min-height: 88px; padding: 18px 32px; border-top: 1px solid #dfe7e6; background: #fff; color: #53605e; display: flex; align-items: center; justify-content: center; gap: 12px; } +.portal-footer > svg { color: #34465f; font-size: 20px; } +.portal-footer i { width: 1px; height: 26px; background: #d7dfde; margin: 0 14px; } +.portal-footer button { border: 0; background: transparent; color: var(--brand); font-weight: 700; display: inline-flex; align-items: center; gap: 6px; } + +.app-shell { min-height: 100vh; display: flex; background: var(--page); } +.sidebar { position: fixed; z-index: 30; inset: 0 auto 0 0; width: 234px; background: #fff; border-right: 1px solid #e7eceb; padding: 0 18px; display: flex; flex-direction: column; } +.brand { height: 70px; display: flex; align-items: center; gap: 11px; font-size: 22px; color: #151b1b; border-bottom: 1px solid #eef1f1; } +.brand svg { color: var(--brand); font-size: 32px; } +.brand strong { white-space: nowrap; } +.app-home-return { width: 100%; padding: 0; background: transparent; border: 0; text-align: left; } +.main-nav { padding: 16px 0; flex: 1; } +.nav-block { margin-bottom: 7px; } +.nav-item { width: 100%; height: 54px; border: 0; border-radius: 8px; background: transparent; display: grid; grid-template-columns: 25px 1fr 16px; align-items: center; text-align: left; gap: 8px; padding: 0 13px; color: #2f3636; font-size: 16px; font-weight: 600; } +.nav-item > svg:first-child { font-size: 20px; color: #5c6968; } +.nav-item:hover { background: #f0f7f6; } +.nav-item.active { background: var(--brand); color: #fff; } +.nav-item.active > svg { color: #fff; } +.nav-arrow { font-size: 12px; justify-self: end; } +.nav-arrow.expanded { transform: rotate(180deg); } +.sub-nav { margin: 4px 0 6px 23px; border-left: 1px solid #d9e0df; padding-left: 12px; } +.sub-nav button { width: 100%; height: 42px; border: 0; background: transparent; color: #5f6968; text-align: left; font-size: 14px; } +.sub-nav button:hover, .sub-nav button.active { color: var(--brand); font-weight: 700; } +.sidebar-foot { padding: 18px 0; color: #a6afae; font-size: 12px; text-align: center; } + +.app-main { width: calc(100% - 234px); margin-left: 234px; min-height: 100vh; } +.topbar { height: 70px; position: sticky; top: 0; z-index: 20; background: #e4f4f2; display: flex; align-items: center; padding: 0 27px; gap: 12px; border-bottom: 1px solid #d4e9e6; } +.topbar-spacer { flex: 1; } +.icon-button { width: 36px; height: 36px; border: 0; border-radius: 50%; color: #33897f; background: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 18px; } +.icon-button.ghost { background: transparent; color: #6b7a78; } +.clock { text-align: right; color: #273130; line-height: 1.15; min-width: 130px; } +.clock span { display: block; font-size: 14px; } +.clock strong { display: block; font-size: 15px; margin-top: 4px; letter-spacing: 1px; } +.divider { height: 34px; width: 1px; background: #b9d2cf; margin: 0 2px; } +.role-switcher { position: relative; } +.role-button { border: 0; background: transparent; min-width: 138px; text-align: right; color: #273130; display: grid; grid-template-columns: 1fr auto 13px; column-gap: 5px; align-items: end; } +.role-button span { grid-column: 1 / 3; font-size: 12px; } +.role-button strong { grid-row: 2; grid-column: 1 / 3; font-size: 14px; } +.role-button svg { grid-row: 2; grid-column: 3; font-size: 10px; align-self: center; } +.role-menu, .notice-popover { position: absolute; top: 58px; right: 68px; background: #fff; box-shadow: 0 14px 40px rgba(28,61,58,.18); border: 1px solid #e1e7e6; border-radius: 9px; padding: 8px; z-index: 50; } +.role-menu { right: 0; width: 190px; } +.role-menu button { width: 100%; height: 38px; border: 0; background: #fff; text-align: left; padding: 0 12px; border-radius: 5px; color: #465150; } +.role-menu button:hover, .role-menu button.active { background: var(--brand-soft); color: var(--brand); } +.role-menu .logout { border-top: 1px solid #edf0f0; color: #b25050; margin-top: 4px; padding-top: 4px; } +.notice-button { position: relative; } +.notice-button i { position: absolute; width: 7px; height: 7px; border-radius: 50%; background: #e63232; right: 3px; top: 2px; } +.notice-popover { width: 270px; right: 62px; top: 62px; padding: 18px; } +.notice-popover strong { font-size: 16px; } +.notice-popover p { margin: 12px 0 0; color: #6b7675; font-size: 13px; } + +.workspace { padding: 18px 20px 32px; } +.page-card { min-height: calc(100vh - 120px); background: #fff; border-radius: 10px; border: 1px solid #edf0f0; overflow: hidden; } +.page-heading { min-height: 62px; padding: 0 22px; display: flex; align-items: center; border-bottom: 1px solid #e8ecec; } +.page-heading h1 { margin: 0; font-size: 19px; color: #252e2d; padding-left: 14px; position: relative; } +.page-heading h1::before { content: ""; width: 5px; height: 23px; border-radius: 3px; background: var(--brand); position: absolute; left: 0; top: 0; } +.page-heading .outline-button { margin-left: auto; } +.section-title { margin: 20px 20px 14px; font-weight: 700; font-size: 16px; color: #35403f; display: flex; align-items: center; gap: 8px; } +.section-title::before { content: ""; width: 7px; height: 7px; background: var(--brand); border-radius: 50%; } +.result-count { color: #87918f; font-size: 13px; font-weight: 400; margin-left: auto; } + +.primary-button, .outline-button { min-width: 104px; height: 40px; padding: 0 19px; display: inline-flex; align-items: center; justify-content: center; gap: 7px; } +.outline-button { color: var(--brand); background: #fff; border: 1px solid var(--brand); border-radius: 7px; font-weight: 600; } +.outline-button:hover { background: #f0f8f7; } +.text-button, .link-button, .action-button { border: 0; background: transparent; color: var(--brand); } +.text-button { margin-left: auto; display: inline-flex; gap: 5px; align-items: center; } +.button-row { display: flex; justify-content: center; gap: 18px; padding: 28px 0 10px; } + +.filter-panel { margin: 20px; padding: 20px; background: #f8faf9; border: 1px solid #e8edec; border-radius: 8px; display: flex; gap: 22px; } +.filter-grid { flex: 1; display: grid; grid-template-columns: repeat(3, minmax(210px, 1fr)); gap: 16px 20px; } +.filter-grid label { display: grid; grid-template-columns: 96px 1fr; align-items: center; gap: 8px; color: #596361; font-size: 14px; } +.filter-grid input, .filter-grid select, .field { width: 100%; height: 40px; padding: 0 12px; border: 1px solid #cfd7d6; border-radius: 5px; background: #fff; color: #394442; outline: none; } +.filter-grid input:focus, .filter-grid select:focus, .field:focus { border-color: var(--brand); box-shadow: 0 0 0 2px rgba(42,139,127,.1); } +.field[readonly], textarea.field[readonly] { background: #f1f8f7; color: #596663; } +.filter-actions { align-self: end; display: flex; gap: 10px; } + +.summary-grid { display: flex; padding: 20px; gap: 15px; overflow-x: auto; } +.summary-grid button { min-width: 160px; flex: 1; height: 88px; border: 1px solid #d9e5e3; border-radius: 8px; background: #fff; padding: 13px 16px; text-align: left; position: relative; transition: transform .2s, border-color .2s; display: flex; align-items: center; gap: 14px; } +.summary-grid button:hover { transform: translateY(-2px); border-color: var(--brand); } +.summary-grid button.active { background: var(--brand-soft); border-color: var(--brand); } +.summary-grid span { display: block; color: #687371; } +.summary-grid .summary-icon { flex: 0 0 48px; width: 48px; height: 48px; border-radius: 7px; display: grid; place-items: center; color: #fff; background: linear-gradient(145deg, #65c6ba, #168b7e); box-shadow: 0 4px 10px rgba(22,139,126,.16); } +.summary-grid .summary-icon svg { font-size: 23px; } +.summary-grid .summary-copy > span { margin-bottom: 5px; white-space: nowrap; } +.summary-grid strong { color: var(--brand); font-size: 25px; } +.summary-grid em { margin-left: 4px; font-style: normal; font-size: 12px; color: #86908f; } + +.table-wrap { margin: 0 20px 18px; overflow-x: auto; } +.data-table { width: 100%; min-width: 1180px; border-collapse: collapse; font-size: 13px; } +.data-table th { height: 48px; background: #dcefed; color: #273332; font-weight: 700; white-space: nowrap; border-bottom: 2px solid #72b8b0; padding: 0 12px; } +.data-table td { height: 52px; text-align: center; padding: 8px 12px; color: #45504e; border-bottom: 1px solid #edf1f0; } +.data-table tbody tr:nth-child(even) { background: #f2f9f8; } +.data-table tbody tr:hover { background: #eaf5f3; } +.data-table .address { min-width: 170px; } +.data-table.compact { min-width: 700px; } +.data-table.compact td { height: 46px; } +.link-button { text-decoration: underline; text-underline-offset: 3px; } +.action-button { padding: 5px 10px; border: 1px solid #8ec7c0; border-radius: 4px; } +.status { padding: 4px 9px; border-radius: 12px; font-size: 12px; } +.status.pending { background: #fff1d9; color: #a76a08; } +.status.progress { background: #e1f1ff; color: #3474a8; } +.status.done { background: #e3f5e9; color: #368051; } +.empty-cell { height: 150px !important; color: #929c9a !important; } +.pagination { display: flex; justify-content: flex-end; align-items: center; gap: 6px; padding-top: 16px; color: #7c8785; } +.pagination button { min-width: 30px; height: 30px; border: 1px solid #d6dddc; background: #fff; border-radius: 4px; color: #596462; } +.pagination button.active { background: var(--brand); color: #fff; border-color: var(--brand); } + +.import-layout { display: grid; grid-template-columns: 1.1fr 1fr; gap: 24px; padding: 0 20px; } +.import-box, .batch-panel, .preview-panel, .sla-card, .rule-card, .chart-grid section { border: 1px solid #e1e8e7; border-radius: 8px; background: #fff; } +.import-box { padding: 0 20px 20px; } +.import-main { width: min(1500px, calc(100% - 40px)); margin: 22px auto; border: 0; } +.import-main > .form-row { width: min(1160px, 100%); margin-left: auto; margin-right: auto; } +.import-main .file-drop { margin: 42px 0 4px; min-height: 360px; } +.batch-panel { overflow: hidden; } +.batch-panel .table-wrap, .preview-panel .table-wrap { margin-bottom: 20px; } +.file-drop { position: relative; min-height: 92px; border: 1px dashed #79bcb4; border-radius: 7px; flex: 1; display: flex; align-items: center; justify-content: center; gap: 15px; color: var(--brand); background: #f5fbfa; } +.file-drop > svg { font-size: 30px; } +.file-drop div { display: flex; flex-direction: column; gap: 7px; } +.file-drop span { color: #8a9492; font-size: 12px; } +.file-drop input { position: absolute; inset: 0; opacity: 0; cursor: pointer; } +.preview-panel { margin: 22px 20px; } + +.detail-section { margin: 0 20px 22px; } +.info-grid { border: 1px solid #8fc6bf; background: #f0f9f8; border-radius: 8px; padding: 23px 28px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 24px 34px; } +.info-grid div { display: grid; grid-template-columns: 130px 1fr; gap: 10px; align-items: center; } +.info-grid span { color: #85908f; text-align: right; } +.info-grid strong { color: #37413f; } +.service-title { margin-left: 0; margin-right: 0; } +.detail-section .table-wrap { margin: 0; } +.service-table th:nth-child(3), .service-table th:nth-child(8) { min-width: 220px; } +.audio-button { border: 0; background: transparent; color: #67b2aa; display: inline-flex; flex-direction: column; gap: 2px; align-items: center; } +.audio-button svg { font-size: 23px; } +.audio-button span { font-size: 11px; color: #899290; } + +.form-section { border-top: 1px solid #eef1f0; padding-top: 2px; } +.wide-form { width: min(1060px, 86%); margin: 0 auto; } +.form-row { display: flex; align-items: flex-start; gap: 20px; margin: 18px 0; } +.form-row > label:first-child { width: 150px; text-align: right; padding-top: 10px; color: #414b49; flex: none; } +.form-row .field { flex: 1; } +textarea.field { min-height: 78px; resize: vertical; padding: 11px 12px; line-height: 1.6; } +textarea.field.large { min-height: 130px; } +.radio-group { min-height: 40px; display: flex; align-items: center; gap: 34px; } +.radio-group label, .checkbox-select label { display: inline-flex; align-items: center; gap: 7px; color: #4d5856; } +input[type="radio"], input[type="checkbox"] { accent-color: var(--brand); width: 16px; height: 16px; } +.checkbox-select { flex: 1; min-height: 42px; border: 1px solid #d4dcdb; border-radius: 6px; padding: 10px 15px; display: flex; flex-wrap: wrap; gap: 12px 28px; } +.long-label > label:first-child { width: 360px; line-height: 1.6; padding-top: 0; } +.tip, .branch-tip, .flow-tip { background: #eff8f7; border: 1px solid #cce6e2; border-radius: 6px; color: #47716c; padding: 12px 15px; display: flex; align-items: center; gap: 9px; font-size: 13px; } +.branch-tip { margin: 18px 0; } +.flow-tip { margin: 4px 0 18px 142px; line-height: 1.65; } +.static-value { color: #253833; line-height: 38px; } +.required-files { margin: 20px 0; } +.review-actions { margin-top: 20px; border-top: 1px solid #edf1f0; padding-top: 2px; } +.upload-list { display: grid; grid-template-columns: 1fr 1fr; gap: 22px; margin: 20px 0; } +.upload-list > div { display: grid; gap: 10px; } +.upload-slot { min-height: 110px; border: 1px dashed #78b9b1; background: #f6fbfa; color: var(--brand); border-radius: 7px; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 8px; } +.upload-slot svg { font-size: 26px; } +.upload-slot.uploaded { border-style: solid; background: #eaf7f4; } +.upload-file-row { min-height: 118px; display: grid; grid-template-columns: 190px 92px minmax(0, 1fr); align-items: center; gap: 22px; } +.upload-file-row > strong { text-align: right; } +.upload-square { width: 92px; height: 92px; border: 1px solid #c7d2d0; border-radius: 7px; background: #fff; color: #8c9795; display: grid; place-items: center; font-size: 30px; } +.upload-square.uploaded { color: var(--brand); border-color: var(--brand); background: #eff8f7; } +.upload-file-name { justify-self: start; border: 0; border-bottom: 1px solid #8d9997; background: transparent; color: #74817f; padding: 4px 0; } +.upload-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-top: 16px; } +.upload-grid button { min-height: 116px; border: 1px dashed #78b9b1; background: #f6fbfa; color: var(--brand); border-radius: 7px; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 6px; } +.upload-grid button > svg { font-size: 26px; } +.upload-grid button span { font-size: 12px; color: #8a9492; } +.report-editor { max-width: 1120px; } +.deliverable-list { display: grid; gap: 12px; margin-bottom: 18px; } +.deliverable-list article { min-height: 70px; border: 1px solid #dce5e3; background: #fff; border-radius: 7px; display: flex; align-items: center; gap: 14px; padding: 12px 16px; } +.deliverable-list article > svg { color: var(--brand); font-size: 26px; } +.deliverable-list article div { display: flex; flex-direction: column; gap: 5px; flex: 1; } +.deliverable-list article span { color: #8b9493; font-size: 12px; } +.add-deliverable { min-height: 76px; border: 1px dashed #76b6ae; background: #f6fbfa; color: var(--brand); border-radius: 7px; display: flex; align-items: center; justify-content: center; gap: 9px; } + +.rules-layout { display: grid; grid-template-columns: 1.15fr .85fr; gap: 24px; padding: 20px; } +.rules-layout.single { grid-template-columns: 1fr; max-width: 980px; margin: 0 auto; } +.prototype-blank { min-height: calc(100vh - 185px); } +.sla-card, .rule-card { padding: 0 20px 22px; } +.total-limit { display: flex; align-items: center; gap: 12px; padding: 16px 20px; border-radius: 7px; background: #f2f8f7; } +.total-limit input { width: 120px; height: 42px; border: 1px solid #b9cfcc; border-radius: 5px; padding: 0 10px; color: var(--brand); font-weight: 700; font-size: 20px; } +.total-limit em, .sla-grid span { color: #8b9593; font-style: normal; } +.sla-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px 22px; margin-top: 22px; } +.sla-grid label { display: grid; grid-template-columns: 110px 1fr 42px; align-items: center; gap: 8px; color: #4d5755; } +.sla-grid input { height: 40px; border: 1px solid #ced7d5; border-radius: 5px; padding: 0 10px; } +.rule-empty { min-height: 380px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #82908d; text-align: center; } +.rule-empty > svg { font-size: 52px; color: #83bfb8; } +.rule-empty strong { color: #394542; font-size: 19px; margin-top: 16px; } +.rule-empty p { max-width: 320px; line-height: 1.7; } + +.analytics-grid { padding: 20px; display: grid; grid-template-columns: repeat(4, 1fr); gap: 18px; } +.analytics-grid article { border: 1px solid #e1e9e7; border-radius: 8px; padding: 22px; box-shadow: 0 5px 18px rgba(37,91,84,.05); } +.analytics-grid article span { color: #74807e; } +.analytics-grid article strong { display: block; color: var(--brand); font-size: 30px; margin: 14px 0 8px; } +.analytics-grid article em { color: #5f8f74; font-size: 12px; font-style: normal; } +.chart-grid { padding: 0 20px 22px; display: grid; grid-template-columns: 1.4fr 1fr; gap: 18px; } +.bar-chart { height: 310px; padding: 30px 30px 20px; display: flex; align-items: flex-end; justify-content: space-around; border-top: 1px solid #eef1f0; } +.bar-chart > div { height: 240px; width: 9%; display: flex; flex-direction: column; justify-content: flex-end; align-items: center; gap: 9px; } +.bar-chart span { width: 100%; background: #76bdb4; border-radius: 5px 5px 0 0; } +.bar-chart em { color: #899391; font-size: 11px; font-style: normal; white-space: nowrap; } +.donut-wrap { height: 310px; border-top: 1px solid #eef1f0; display: flex; align-items: center; justify-content: center; gap: 42px; } +.donut { width: 160px; height: 160px; border: 30px solid #68b4ab; border-right-color: #a2d0cb; border-bottom-color: #d3e9e6; border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; } +.donut strong { font-size: 23px; } +.donut span { color: #8b9492; font-size: 12px; } +.donut-wrap ul { list-style: none; margin: 0; padding: 0; } +.donut-wrap li { margin: 13px 0; color: #66716f; font-size: 13px; } +.donut-wrap li i { display: inline-block; width: 9px; height: 9px; border-radius: 50%; background: #68b4ab; margin-right: 8px; } +.donut-wrap li i.c2 { background: #86c3bc; }.donut-wrap li i.c3 { background: #a2d0cb; }.donut-wrap li i.c4 { background: #d3e9e6; } + +.modal-mask { position: fixed; z-index: 100; inset: 0; background: rgba(24,35,34,.52); display: flex; align-items: center; justify-content: center; padding: 30px; } +.modal { width: min(760px, 90vw); background: #f4fbfa; border-radius: 18px; padding: 30px 40px 34px; box-shadow: 0 20px 80px rgba(0,0,0,.22); position: relative; } +.small-modal { width: min(1080px, 90vw); } +.assignment-modal { width: min(1080px, 90vw); transform: translateY(-100px); } +.review-modal-v2 { width: min(1080px, 90vw); transform: translateY(-30px); } +.modal-inner-panel { min-height: 265px; background: #fff; border: 1px solid #d5dfdd; border-radius: 8px; padding: 32px 40px 36px; } +.review-modal-v2 .modal-inner-panel { min-height: 504px; } +.modal-inner-panel .section-title { margin-left: 0; } +.modal-inner-panel .form-row { max-width: 520px; margin: 22px 0; } +.modal-inner-panel .form-row > label:first-child { width: 75px; } +.modal-inner-panel .form-row .field { height: 58px; } +.modal-inner-panel .section-title { font-size: 18px; margin-top: 24px; margin-bottom: 16px; } +.modal-inner-panel .section-title:first-child { margin-top: 0; } +.modal-inner-panel textarea.review-reason { min-height: 112px; resize: vertical; } +.inline-callback-files { margin-left: 170px; padding: 8px 0 12px; } +.modal h2 { margin: 0 0 28px; font-size: 28px; } +.modal-close { position: absolute; right: 24px; top: 22px; border: 0; background: transparent; color: #879390; font-size: 22px; } +.modal .section-title { margin-left: 0; } +.modal > .button-row { gap: 20px; padding-top: 30px; } +.modal > .button-row .primary-button, .modal > .button-row .outline-button { min-width: 168px; height: 58px; font-size: 17px; } +.review-order { background: #fff; border: 1px solid #d6e3e1; border-radius: 7px; display: grid; grid-template-columns: 130px 1fr 100px 1fr 100px 1fr; padding: 16px; gap: 8px; font-size: 13px; } +.review-order span { color: #86908e; }.review-order strong { color: #3d4846; } + +.toast { position: fixed; z-index: 200; left: 50%; top: 95px; transform: translateX(-50%); background: #234f49; color: #fff; padding: 13px 20px; border-radius: 7px; box-shadow: 0 10px 35px rgba(0,0,0,.2); display: flex; gap: 9px; align-items: center; } +.toast-enter-active, .toast-leave-active { transition: opacity .2s, transform .2s; } +.toast-enter-from, .toast-leave-to { opacity: 0; transform: translate(-50%,-10px); } + +@media (max-width: 1360px) { + .portal-content { width: calc(100% - 48px); } + .portal-intro { margin-bottom: 44px; } + .micro-app-copy strong { font-size: 19px; } + .filter-grid { grid-template-columns: repeat(2, minmax(210px, 1fr)); } + .info-grid { grid-template-columns: repeat(3, 1fr); } + .info-grid div { grid-template-columns: 110px 1fr; } + .summary-grid button { min-width: 145px; } +} + +@media (max-width: 900px) { + html, body, #app { min-width: 0; } + body { overflow-x: hidden; } + .portal-header { height: 70px; padding: 0 16px; } + .portal-brand { font-size: 20px; } + .portal-brand svg { font-size: 32px; } + .portal-header .clock, .portal-header .divider { display: none; } + .portal-content { width: calc(100% - 32px); padding: 34px 0 42px; } + .portal-intro { align-items: stretch; flex-direction: column; gap: 26px; margin-bottom: 36px; } + .portal-intro h1 { font-size: 30px; } + .portal-search { width: 100%; flex-basis: 52px; } + .recent-app { height: auto; min-height: 190px; padding: 24px; flex-wrap: wrap; } + .recent-copy { flex-basis: calc(100% - 110px); } + .recent-todo { width: auto; min-height: 64px; margin: 20px 0 0 100px; padding: 0 22px 0 0; border-left: 0; } + .portal-enter { margin-top: 20px; } + .micro-app-grid { grid-template-columns: 1fr; } + .micro-app-card { min-height: 210px; } + .portal-footer { text-align: center; } + .sidebar { display: none; } + .app-shell, .app-main, .workspace, .page-card { min-width: 0; } + .app-main { width: 100%; margin-left: 0; } + .topbar { padding: 0 12px; gap: 8px; } + .clock, .divider { display: none; } + .workspace { padding: 10px 8px 24px; } + .page-heading { padding: 0 14px; } + .page-heading h1 { font-size: 17px; } + .summary-grid { padding: 12px; gap: 10px; } + .summary-grid button { flex: 0 0 178px; } + .filter-panel { margin: 12px; padding: 14px; flex-direction: column; } + .filter-grid { grid-template-columns: 1fr; gap: 12px; } + .filter-grid label { grid-template-columns: 82px minmax(0, 1fr); } + .filter-actions { align-self: stretch; justify-content: flex-end; } + .table-wrap { margin: 0 12px 14px; max-width: calc(100vw - 42px); } + .detail-section { margin: 0 12px 18px; } + .info-grid { grid-template-columns: 1fr; gap: 12px; padding: 16px; } + .info-grid div { grid-template-columns: 112px minmax(0, 1fr); } + .info-grid span { text-align: left; } + .import-layout, .rules-layout, .chart-grid { grid-template-columns: 1fr; padding: 0 12px; } + .analytics-grid { grid-template-columns: repeat(2, 1fr); padding: 12px; } + .wide-form { width: 100%; } + .form-row { flex-direction: column; gap: 7px; margin: 14px 0; } + .form-row > label:first-child, .long-label > label:first-child { width: auto; text-align: left; padding-top: 0; } + .form-row .field, .checkbox-select { width: 100%; } + .flow-tip { margin: 4px 0 16px; } + .upload-grid { grid-template-columns: 1fr; } + .upload-list { grid-template-columns: 1fr; } + .upload-file-row { grid-template-columns: 1fr; gap: 8px; padding: 12px 0; } + .upload-file-row > strong { text-align: left; } + .upload-square { width: 100%; height: 72px; } + .review-order { grid-template-columns: 110px 1fr; } + .modal-mask { padding: 12px; } + .modal { width: 100%; max-height: 92vh; overflow-y: auto; padding: 25px 20px 28px; } + .review-modal-v2 { transform: none; } + .assignment-modal { transform: none; } +} + +@media (max-width: 520px) { + .portal-header > .icon-button, .portal-header .role-switcher { display: none; } + .portal-brand { font-size: 18px; } + .portal-content { width: calc(100% - 24px); } + .portal-intro h1 { font-size: 26px; } + .portal-intro p { font-size: 15px; } + .recent-app { display: grid; grid-template-columns: 66px 1fr; gap: 16px; } + .recent-app .portal-app-icon { width: 66px; height: 66px; flex-basis: 66px; } + .recent-copy { margin-left: 0; } + .recent-copy strong { font-size: 20px; } + .recent-todo { margin: 14px 0 0; padding: 0; } + .portal-enter { min-width: 112px; margin: 14px 0 0; } + .recent-arrow { display: none; } + .micro-app-card { grid-template-columns: 60px minmax(0, 1fr); padding: 22px 18px; gap: 0 16px; } + .micro-app-card .portal-app-icon { width: 60px; height: 60px; } + .micro-app-copy strong { white-space: normal; } + .portal-footer i, .portal-footer button { display: none; } + .topbar > .icon-button:not(.ghost) { display: none; } + .role-button { min-width: 132px; } + .login-screen { justify-content: center; padding: 24px 16px; background-position: 38% center; } + .login-panel { width: 100%; transform: none; } + .login-heading { margin-bottom: 24px; } + .login-kicker { font-size: 25px; } + .login-kicker span { display: block; margin: 8px 0 0; } + .login-heading p { font-size: 13px; } + .login-card { padding: 30px 22px 34px; } + .login-card h1 { font-size: 21px; } + .captcha-row { grid-template-columns: minmax(0, 1fr) 96px; } + .captcha-row .login-field { min-width: 0; } + .analytics-grid, .sla-grid { grid-template-columns: 1fr; } + .donut-wrap { flex-direction: column; height: auto; padding: 28px 0; } + .button-row { flex-wrap: wrap; padding-left: 8px; padding-right: 8px; } +} diff --git a/frontend/tests/sites-worker.test.mjs b/frontend/tests/sites-worker.test.mjs new file mode 100644 index 0000000..64f277e --- /dev/null +++ b/frontend/tests/sites-worker.test.mjs @@ -0,0 +1,68 @@ +import assert from "node:assert/strict"; +import { access } from "node:fs/promises"; +import test from "node:test"; +import worker from "../worker/index.js"; + +test("serves existing static assets without a fallback", async () => { + const calls = []; + const response = await worker.fetch(new Request("https://example.test/assets/app.js"), { + ASSETS: { + fetch: async (request) => { + calls.push(new URL(request.url).pathname); + return new Response("asset", { status: 200 }); + }, + }, + }); + + assert.equal(response.status, 200); + assert.deepEqual(calls, ["/assets/app.js"]); +}); + +test("falls back to index.html for an unknown app route", async () => { + const calls = []; + const response = await worker.fetch( + new Request("https://example.test/flow/step-two?source=share", { + headers: { accept: "text/html" }, + }), + { + ASSETS: { + fetch: async (request) => { + const url = new URL(request.url); + calls.push(url.pathname + url.search); + return new Response(url.pathname === "/index.html" ? "app" : "missing", { + status: url.pathname === "/index.html" ? 200 : 404, + }); + }, + }, + }, + ); + + assert.equal(response.status, 200); + assert.deepEqual(calls, ["/flow/step-two?source=share", "/index.html"]); +}); + +test("does not turn missing API or write requests into the app shell", async () => { + for (const request of [ + new Request("https://example.test/api/missing", { headers: { accept: "application/json" } }), + new Request("https://example.test/flow", { method: "POST", headers: { accept: "text/html" } }), + ]) { + let calls = 0; + const response = await worker.fetch(request, { + ASSETS: { + fetch: async () => { + calls += 1; + return new Response("missing", { status: 404 }); + }, + }, + }); + + assert.equal(response.status, 404); + assert.equal(calls, 1); + } +}); + +test("emits the files required by Sites packaging", async () => { + await access(new URL("../dist/client/index.html", import.meta.url)); + await access(new URL("../dist/server/index.js", import.meta.url)); + await access(new URL("../dist/.openai/hosting.json", import.meta.url)); +}); diff --git a/frontend/vite.config.mjs b/frontend/vite.config.mjs new file mode 100644 index 0000000..fc18945 --- /dev/null +++ b/frontend/vite.config.mjs @@ -0,0 +1,19 @@ +import { defineConfig } from "vite"; +import vue from "@vitejs/plugin-vue"; + +export default defineConfig({ + build: { + outDir: "dist/client", + }, + optimizeDeps: { + include: ["vue", "@ant-design/icons-vue"], + }, + server: { + host: "0.0.0.0", + allowedHosts: ["terminal.local"], + warmup: { + clientFiles: ["./src/main.js", "./src/App.vue"], + }, + }, + plugins: [vue()], +}); diff --git a/frontend/worker/index.js b/frontend/worker/index.js new file mode 100644 index 0000000..5f35412 --- /dev/null +++ b/frontend/worker/index.js @@ -0,0 +1,15 @@ +export default { + async fetch(request, env) { + const response = await env.ASSETS.fetch(request); + const acceptsHtml = request.headers.get("accept")?.includes("text/html"); + + if (response.status !== 404 || !acceptsHtml || !["GET", "HEAD"].includes(request.method)) { + return response; + } + + const indexUrl = new URL(request.url); + indexUrl.pathname = "/index.html"; + indexUrl.search = ""; + return env.ASSETS.fetch(new Request(indexUrl, request)); + }, +};