# 环境检测模块 — 功能设计文档 > 日期:2026-06-11 > 状态:已实现(2026-06-11) > 需求来源:质检部门新增环境检测登记能力 > 已确认:**方案 B — 扁平单表**(一次登记对应一个检测项目) > 已确认:**检测人支持多选**,新增时默认选中当前登录用户 --- ## 1. 背景与目标 ### 1.1 业务背景 质检部门需对实验室/洁净区环境进行定期检测并留存记录,包括检测时间、检测人、检测项目、结果描述及检测附件。 ### 1.2 目标 1. 在 **质检基础信息** 下新增菜单 **环境检测项维护**,管理可选检测项目(编码、名称、启用/停用)。 2. 在 **质检管理** 下新增菜单 **环境检测管理**,支持环境检测信息的登记、查询、编辑、查看与软删除。 3. 迁移 SQL 预置 6 项初始检测项目,租户可通过维护菜单增删改(停用后新登记不可选,历史记录保留快照)。 ### 1.3 非目标(一期不做) - 一次登记包含多个检测项目(头 + 明细行模式) - 与洁净室主数据(`biz_clean_room`)关联 - 合格/不合格结论字段、审批流、到期提醒 - 与样本质检工单/任务联动 - 小程序端 --- ## 2. 已确认方案 | 方案 | 说明 | 结论 | |------|------|------| | A. 主表 + 明细行 | 一次登记可含多项,每项独立描述与附件 | 未采用 | | **B. 扁平单表** | **每条记录 = 一次检测 = 一个检测项目** | **采用** | | C. 硬编码项目 | 不做维护菜单 | 未采用 | **方案 B 含义**:用户登记「表面微生物检测」与「沉降菌检测」需分别创建两条记录;检测时间、检测人(可多人)、结果描述、附件均挂在单条记录上。 --- ## 3. 菜单与路由 | 菜单 | 父级 | path | 组件 | menuCode | |------|------|------|------|----------| | 环境检测项维护 | 质检基础信息 `QCB…001` | `env-detect-item` | `views/quality-inspection/env-detect-item/index.vue` | `biz:envDetectItem:list` | | 环境检测管理 | 质检管理 `QCM…001` | `env-detect-record` | `views/quality-inspection/env-detect-record/index.vue` | `biz:envDetectRecord:list` | 同步文件: - `web-admin/src/router/modules/sqxp.ts` - `backend/app/api/menu.py` - `docs/sql/migrations/2026-06-11-env-detect.sql` - `docs/sql/zkxw.sql`(全量基线,实现阶段更新) --- ## 4. 数据模型 命名空间:`biz_env_detect_*`。均含标准公共字段(`id` UUID、`tenant_no`、`order_num`、`remark`、`create_by`、`create_dept_id`、`create_time`、`update_by`、`update_time`、`del_flag`)。 ### 4.1 `biz_env_detect_item` — 环境检测项(主数据) | 字段 | 类型 | 说明 | |------|------|------| | item_code | VARCHAR(64) | 项目编码,租户内唯一(`del_flag=0`) | | item_name | VARCHAR(200) | 项目名称 | | status | CHAR(1) | `1` 启用 / `0` 停用 | **索引**:`uk_env_item_tenant_code (tenant_no, item_code, del_flag)` **初始种子数据**(迁移时按租户 `zkxw` 插入,其他租户可自行维护): | item_code | item_name | |-----------|-----------| | BMWS | 表面微生物检测 | | SYFS | 实验室风速检测 | | ZWQD | 紫外灯强度检测 | | CJJ | 沉降菌检测 | | JJZY | 洁净区噪音检测 | | XFZL | 洁净区悬浮粒子检测 | ### 4.2 `biz_env_detect_record` — 环境检测登记(业务记录) | 字段 | 类型 | 说明 | |------|------|------| | record_no | VARCHAR(32) | 登记编号:`HJ` + yyyyMMdd + 4 位日序,租户内唯一 | | detect_time | DATETIME | 检测时间(必填) | | inspector_ids | VARCHAR(500) | 检测人用户 ID,逗号分隔,至少 1 人(必填) | | inspector_names | VARCHAR(500) | 检测人姓名快照,逗号分隔,顺序与 `inspector_ids` 一致 | | env_detect_item_id | VARCHAR(64) | 检测项目主数据 ID(必填) | | item_code | VARCHAR(64) | 项目编码快照 | | item_name | VARCHAR(200) | 项目名称快照 | | result_desc | VARCHAR(2000) | 检测结果描述(必填) | | attachment_urls | VARCHAR(2000) | 检测附件 URL,逗号分隔(MinIO,可选) | **索引**: - `uk_env_rec_tenant_no (tenant_no, record_no, del_flag)` - `idx_env_rec_tenant_time (tenant_no, detect_time, del_flag)` - `idx_env_rec_tenant_item (tenant_no, env_detect_item_id, del_flag)` **快照规则**: - 检测项目:创建/更新时从 `biz_env_detect_item` 写入 `item_code`、`item_name`。 - 检测人:创建/更新时根据 `inspector_ids` 批量查询 `sys_user`,按提交顺序写入 `inspector_names`(`nick_name` 优先,无则 `user_name`)。 - 主数据或用户改名后,历史记录仍展示保存时的快照。 **存储约定**:`inspector_ids` / `inspector_names` 与 `attachment_urls` 相同,采用逗号分隔字符串落库;API 层 `_row_to_dict` 解析为 `inspectorIds: string[]`、`inspectorNames: string[]`,并额外输出 `inspectorDisplay`(姓名以 ` / ` 拼接,供列表列展示)。 --- ## 5. 后端 API 路由注册于 `app/api/env_detect.py`(或拆分为 `env_detect_item.py` + `env_detect_record.py`),在 `app/api/__init__.py` 与 `app/main.py` 注册。 前缀:`/web/admin/`。租户隔离 + 数据权限 + 软删除,列表默认 `create_time DESC`。 ### 5.1 环境检测项 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/env-detect-items` | 分页列表(keyword、status) | | GET | `/env-detect-items/active` | 启用项全量(登记下拉用,无分页) | | GET | `/env-detect-items/{id}` | 详情 | | POST | `/env-detect-items` | 新增 | | PUT | `/env-detect-items/{id}` | 更新 | | DELETE | `/env-detect-items/{id}` | 软删除 | **校验**:`item_code`、`item_name` 必填;编码租户内唯一;已被登记引用的项允许停用,不允许物理删除(软删除前可选:若存在引用则提示或禁止删除,一期采用**允许软删除**但登记下拉仅取 `status=1` 且 `del_flag=0`)。 ### 5.2 环境检测登记 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/env-detect-records` | 分页列表 | | GET | `/env-detect-records/{id}` | 详情 | | POST | `/env-detect-records` | 新增(服务端生成 `record_no`) | | PUT | `/env-detect-records/{id}` | 更新 | | DELETE | `/env-detect-records/{id}` | 软删除 | **列表筛选参数**:`keyword`(编号/检测人姓名/结果描述模糊)、`envDetectItemId`、`inspectorId`(命中 `inspector_ids` 中任一 ID 即返回,MySQL `FIND_IN_SET`)、`detectTimeStart`、`detectTimeEnd`。 **创建/更新 Body**: ```json { "detectTime": "2026-06-11 14:30:00", "inspectorIds": ["current-user-uuid", "other-user-uuid"], "envDetectItemId": "item-uuid", "resultDesc": "检测结果描述", "attachmentUrls": "url1,url2", "remark": "" } ``` **检测人校验**: - `inspectorIds` 必填,去重后至少 1 个有效用户 ID。 - 用户须属于当前租户且未删除;无效 ID 返回 400。 - 保存时按 `inspectorIds` 数组顺序生成快照,不依赖前端传入姓名。 **编号生成**:`HJ` + `yyyyMMdd` + 4 位序,逻辑对齐 `pilot_delivery._generate_delivery_no`。 --- ## 6. 前端设计 ### 6.1 界面模式 | 页面 | 模式 | 参考 | |------|------|------| | 环境检测项维护 | **P01** | `sample-reception-check-item/index.vue` | | 环境检测管理 | **P01** + 附件卡片 | `sample-reception-check-item` + OpenSpec 附件上传卡片 | ### 6.2 环境检测项维护 - 筛选:关键词(编码/名称)、状态 - 列表列:序号、项目编码、项目名称、状态 Tag、备注、操作(查看/编辑/删除) - 弹窗字段:项目编码、项目名称、状态、备注 - 筛选区提示:「停用后新登记不再展示,历史记录仍按快照展示。」 ### 6.3 环境检测管理 **筛选**:检测时间范围、检测项目(下拉,启用项)、检测人(用户单选下拉,筛「包含该检测人」的记录)、关键词(编号/结果描述) **列表列**:序号、登记编号、检测时间、检测人(`inspectorDisplay`,多人以 ` / ` 分隔)、检测项目、结果描述(tooltip 截断)、创建时间、操作 **新增/编辑弹窗**(`dialog-form-layout`,宽约 640px,`form-grid-2`): | 字段 | 控件 | 必填 | |------|------|------| | 检测时间 | `el-date-picker` type=datetime | 是 | | 检测人 | `el-select` **multiple** + `filterable`,数据源 `fetchUserOptions()` | 是(≥1 人) | | 检测项目 | 下拉,数据源 `/env-detect-items/active` | 是 | | 检测结果描述 | `el-input` textarea,`form-col-span-2` | 是 | | 检测附件 | 附件卡片上传(`fetchUploadAttachment`),`form-col-span-2` | 否 | | 备注 | `el-input` textarea,`form-col-span-2` | 否 | **检测人默认与交互**(对齐 `sample-reception` 经手人逻辑,扩展为多选): 1. **新增**:打开弹窗时 `inspectorIds` 默认包含 **当前登录用户 ID**;若用户列表中尚无当前用户,先 `ensureCurrentUserInOptions()` 注入选项(参考 `sample-reception/index.vue` `getCurrentUserHandler`)。 2. **新增默认值**:检测时间默认当前时刻(可选,与样本接收一致)。 3. 用户可继续勾选其他检测人;可取消当前登录人,但提交前须至少保留 1 人(前后端双重校验)。 4. **编辑**:回显已保存的 `inspectorIds`;若历史检测人不在下拉列表中,合并进选项以保证展示。 **查看**:`SystemViewDialog` 展示 `inspectorDisplay`(或逐人换行);附件区支持预览/下载。 **状态 Tag**:本模块无业务状态字段;检测项维护页 `status` 走 `status-tag-registry`。 ### 6.4 API 模块 - `web-admin/src/api/env-detect-item.ts` - `web-admin/src/api/env-detect-record.ts` 类型字段与后端 `_row_to_dict` 保持一致(camelCase)。 --- ## 7. 权限与菜单 SQL(草案) ```sql -- 环境检测项维护(父:质检基础信息 QCB00000000000000000000000000001) INSERT INTO sys_menu (...) VALUES ( '71000000000000000000000000000001', '环境检测项维护', 'QCB00000000000000000000000000001', 'C', 'env-detect-item', '/quality-inspection/env-detect-item/index', 'biz:envDetectItem:list', ... ); -- 环境检测管理(父:质检管理 QCM00000000000000000000000000001) INSERT INTO sys_menu (...) VALUES ( '71000000000000000000000000000002', '环境检测管理', 'QCM00000000000000000000000000001', 'C', 'env-detect-record', '/quality-inspection/env-detect-record/index', 'biz:envDetectRecord:list', ... ); ``` `order_num`:项维护置于「质检物料库」之前(建议 7);环境检测管理置于「质检工单」之后(建议 3)。 --- ## 8. 实现检查清单 - [ ] `python scripts/check_field_consistency.py` 通过(CRUD ↔ Schema ↔ 前端 formData) - [ ] `./scripts/update-graph.sh backend` 与 `frontend`(新增模块后) - [ ] `App.vue` `panelTitleBarTextRules` 写入「环境检测项列表」「环境检测登记列表」 - [ ] 手工验证:新增项 → 登记选到 → 编辑快照不变 → 停用项不出现在下拉 --- ## 9. 工作量估时 | 层 | 内容 | 估时 | |----|------|------| | DB | 2 表 + 菜单 + 种子 | 0.5d | | Backend | 2 组 CRUD + 编号生成 | 1d | | Frontend | 2 个 P01 页 + 附件上传 | 1d | | 联调 | 字段一致性 + 手工测试 | 0.5d | | **合计** | | **约 3 人天** | --- ## 10. 变更记录 | 日期 | 说明 | |------|------| | 2026-06-11 | 初稿;用户确认方案 B(扁平单表) | | 2026-06-11 | 检测人改为多选;新增默认选中当前登录用户;库表 `inspector_ids` / `inspector_names` |