1108 lines
41 KiB
Markdown
1108 lines
41 KiB
Markdown
|
|
# 背景
|
|||
|
|
文件名:2025-09-18_定氮仪接入.md
|
|||
|
|
创建于:2025-09-18 15:30:00
|
|||
|
|
创建者:用户
|
|||
|
|
主分支:dev-plsw
|
|||
|
|
任务分支:feature/nitrogen_analyzer_integration
|
|||
|
|
Yolo模式:Ask
|
|||
|
|
|
|||
|
|
# 任务描述
|
|||
|
|
新增定氮仪质检模块,实现TCP协议数据接收和与现有四种质检单的关联功能。
|
|||
|
|
|
|||
|
|
## 核心需求
|
|||
|
|
1. **TCP数据接收**:接收定氮仪通过网口TCP传输的蛋白检测数据
|
|||
|
|
2. **数据存储**:建立专门的定氮仪质检表和配置表
|
|||
|
|
3. **关联绑定**:与原料、中间产品、外来品、最终产品质检单建立关联
|
|||
|
|
4. **前端展示**:创建定氮仪质检的主页和表单页面
|
|||
|
|
5. **操作集成**:在四种质检单主页添加"定氮仪质检"操作按钮
|
|||
|
|
|
|||
|
|
## TCP数据格式示例
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"addAlkali_mode": "0",
|
|||
|
|
"alkali_volume": "10.0",
|
|||
|
|
"analysis_time": "2025-07-01 15:16:54",
|
|||
|
|
"conversion_coefficient": "0.014",
|
|||
|
|
"created": "Admin",
|
|||
|
|
"dilution_volume": "50.0",
|
|||
|
|
"distillation_Unit": "0",
|
|||
|
|
"distillation_Value": "300.0",
|
|||
|
|
"events": "",
|
|||
|
|
"high_error_limit": "0.0",
|
|||
|
|
"intercept": "0.0",
|
|||
|
|
"low_error_limit": "0.0",
|
|||
|
|
"method_name": "(NH4)2S04",
|
|||
|
|
"preheating_time": "0.0",
|
|||
|
|
"protein_factor": "0.0",
|
|||
|
|
"receive_cup_cleaning": "0",
|
|||
|
|
"receiver_volume": "25.0",
|
|||
|
|
"result_Value": "0.0",
|
|||
|
|
"result_type": "% Nitrogen",
|
|||
|
|
"sample_Name": "2",
|
|||
|
|
"sample_Unit": "g",
|
|||
|
|
"sample_Value": "0.014",
|
|||
|
|
"sample_id": "8935",
|
|||
|
|
"slope": "0.0",
|
|||
|
|
"steam_power": "100.0",
|
|||
|
|
"test_method": "0",
|
|||
|
|
"titrant_volume_for_blank": "0.0438",
|
|||
|
|
"titrant_volume_for_sample": "0.0413",
|
|||
|
|
"titration_acid_concentration": "0.1194",
|
|||
|
|
"titration_method": "0",
|
|||
|
|
"titration_mode": "0",
|
|||
|
|
"tube_cleaning": "0",
|
|||
|
|
"tube_emptying": "1",
|
|||
|
|
"type": "2",
|
|||
|
|
"wait_time": "0.0",
|
|||
|
|
"water_coefficient": "1.0",
|
|||
|
|
"apparatusName": "KI160",
|
|||
|
|
"manufacturerName": "ZDYC",
|
|||
|
|
"data": [
|
|||
|
|
{
|
|||
|
|
"circulateOrder": "2",
|
|||
|
|
"inspectionDate": "2025-07-01",
|
|||
|
|
"inspectionTime": "15:16:54",
|
|||
|
|
"inspectionProject": "错题",
|
|||
|
|
"inspectionResult": "0.0000% Nitrogen"
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
# 项目概览
|
|||
|
|
MES制造执行系统质检模块扩展,集成定氮仪设备数据,形成完整的蛋白质检测数据链路。
|
|||
|
|
|
|||
|
|
⚠️ 警告:永远不要修改此部分 ⚠️
|
|||
|
|
遵循RIPER-5协议:
|
|||
|
|
- RESEARCH: 深入分析现有质检架构
|
|||
|
|
- INNOVATE: 设计多种实现方案
|
|||
|
|
- PLAN: 制定详细技术规范
|
|||
|
|
- EXECUTE: 精确实施计划
|
|||
|
|
- REVIEW: 验证实施结果
|
|||
|
|
⚠️ 警告:永远不要修改此部分 ⚠️
|
|||
|
|
|
|||
|
|
# 分析
|
|||
|
|
基于对现有质检系统的研究,发现了以下关键架构模式:
|
|||
|
|
|
|||
|
|
## 现有质检系统架构
|
|||
|
|
1. **数据库层**:
|
|||
|
|
- 质检主表:`qc_*_inspection`(如qc_material_inspection、qc_final_inspection等)
|
|||
|
|
- 配置表:`qc_*_inspection_config`,存储JSON格式的动态表单配置
|
|||
|
|
- 每种质检类型都有独立的表结构
|
|||
|
|
|
|||
|
|
2. **后端服务层**:
|
|||
|
|
- Controller:处理HTTP请求,标准RESTful API
|
|||
|
|
- Service接口和实现:业务逻辑处理
|
|||
|
|
- Mapper:数据访问层,使用MyBatis-Plus
|
|||
|
|
|
|||
|
|
3. **前端组件层**:
|
|||
|
|
- 主页(index.vue):列表展示、查询、操作
|
|||
|
|
- 表单页(form.vue):新增、编辑、查看
|
|||
|
|
- 动态表单:基于配置生成的表单字段
|
|||
|
|
- 路由配置:hidden路由用于表单页面
|
|||
|
|
|
|||
|
|
4. **关联机制**:
|
|||
|
|
- 质检单可从报工单、采购入库单等创建
|
|||
|
|
- 通过query参数传递关联信息
|
|||
|
|
- 支持查看已关联的质检单
|
|||
|
|
|
|||
|
|
5. **TCP数据处理**:
|
|||
|
|
- 系统已有Modbus TCP数据处理基础(WorkshopEquipmentServiceImpl)
|
|||
|
|
- 支持JSON、16进制、原始格式数据处理
|
|||
|
|
|
|||
|
|
## 定氮仪数据特点
|
|||
|
|
- 复杂的JSON结构,包含检测参数和结果
|
|||
|
|
- 需要与蛋白检测相关的其他质检单关联
|
|||
|
|
- 包含设备信息(apparatusName、manufacturerName)
|
|||
|
|
- 有嵌套的检测结果数组
|
|||
|
|
|
|||
|
|
# 提议的解决方案
|
|||
|
|
|
|||
|
|
## 方案一:独立质检类型架构
|
|||
|
|
### 优势
|
|||
|
|
- 与现有质检系统保持一致的架构模式
|
|||
|
|
- 独立的数据表和配置,便于扩展和维护
|
|||
|
|
- 可复用现有的动态表单生成机制
|
|||
|
|
- 符合系统的设计规范
|
|||
|
|
|
|||
|
|
### 实现路径
|
|||
|
|
1. 创建独立的定氮仪质检表和配置表
|
|||
|
|
2. 实现专门的TCP接收服务
|
|||
|
|
3. 建立与四种质检单的关联机制
|
|||
|
|
4. 创建前端页面和路由配置
|
|||
|
|
|
|||
|
|
### 技术考虑
|
|||
|
|
- 数据库设计需要支持复杂的JSON结构
|
|||
|
|
- TCP服务需要与现有Modbus处理机制集成
|
|||
|
|
- 关联绑定需要双向查询支持
|
|||
|
|
|
|||
|
|
## 方案二:扩展现有质检架构
|
|||
|
|
### 优势
|
|||
|
|
- 减少代码重复,复用现有组件
|
|||
|
|
- 统一的质检数据管理
|
|||
|
|
- 更紧密的系统集成
|
|||
|
|
|
|||
|
|
### 实现路径
|
|||
|
|
1. 扩展现有质检表结构,添加定氮仪专用字段
|
|||
|
|
2. 修改配置系统,支持定氮仪数据类型
|
|||
|
|
3. 在现有页面中添加定氮仪相关功能
|
|||
|
|
|
|||
|
|
### 技术考虑
|
|||
|
|
- 可能影响现有质检功能
|
|||
|
|
- 数据库变更风险较大
|
|||
|
|
- 配置复杂度增加
|
|||
|
|
|
|||
|
|
## 方案三:混合架构方案
|
|||
|
|
### 优势
|
|||
|
|
- 平衡独立性和集成性
|
|||
|
|
- 风险可控,渐进式实现
|
|||
|
|
- 保留未来扩展空间
|
|||
|
|
|
|||
|
|
### 实现路径
|
|||
|
|
1. 创建独立的定氮仪核心表
|
|||
|
|
2. 通过关联表建立与现有质检的联系
|
|||
|
|
3. 创建专门的管理界面
|
|||
|
|
4. 在现有质检页面添加关联查看功能
|
|||
|
|
|
|||
|
|
基于系统的复杂性和扩展性考虑,**推荐采用方案一:独立质检类型架构**,这样既能保持系统架构的一致性,又能确保功能的独立性和可维护性。
|
|||
|
|
|
|||
|
|
✅ **用户确认:采用方案一**
|
|||
|
|
|
|||
|
|
## 🔍 重新设计 - TCP字段映射策略
|
|||
|
|
|
|||
|
|
### 设计原则确认
|
|||
|
|
1. **扩展性好的方式** - 核心检测字段独立存储,data数组用子表,设备配置用JSON
|
|||
|
|
2. **部分字段放在JSON原始数据** - 设备操作参数(cleaning, emptying, mode等)
|
|||
|
|
3. **标准数据库命名规范** - 使用下划线命名,符合数据库规范
|
|||
|
|
|
|||
|
|
### TCP字段分类存储策略
|
|||
|
|
|
|||
|
|
**🎯 主表独立字段 (核心业务数据):**
|
|||
|
|
- 样品信息: sample_name, sample_id, sample_value, sample_unit
|
|||
|
|
- 检测结果: result_value, result_type, nitrogen_content, protein_content
|
|||
|
|
- 设备信息: apparatus_name, manufacturer_name, method_name
|
|||
|
|
- 关键参数: conversion_coefficient, protein_factor, alkali_volume等滴定参数
|
|||
|
|
- 质量控制: high_error_limit, low_error_limit, intercept, slope
|
|||
|
|
|
|||
|
|
**📋 子表存储 (data数组):**
|
|||
|
|
- qc_nitrogen_analyzer_detail表存储data数组中的检测详情
|
|||
|
|
- 支持一个检测对应多条详细结果
|
|||
|
|
|
|||
|
|
**📦 JSON字段存储 (设备配置参数):**
|
|||
|
|
```json
|
|||
|
|
{
|
|||
|
|
"addAlkali_mode": "0",
|
|||
|
|
"distillation_Unit": "0",
|
|||
|
|
"events": "",
|
|||
|
|
"preheating_time": "0.0",
|
|||
|
|
"receive_cup_cleaning": "0",
|
|||
|
|
"test_method": "0",
|
|||
|
|
"titration_method": "0",
|
|||
|
|
"titration_mode": "0",
|
|||
|
|
"tube_cleaning": "0",
|
|||
|
|
"tube_emptying": "1",
|
|||
|
|
"type": "2",
|
|||
|
|
"wait_time": "0.0"
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 完整字段对应关系
|
|||
|
|
| TCP字段 | 数据库字段 | 存储位置 | 说明 |
|
|||
|
|
|---------|------------|----------|------|
|
|||
|
|
| sample_Name | sample_name | 主表 | 样品名称 |
|
|||
|
|
| sample_id | sample_id | 主表 | 样品ID |
|
|||
|
|
| sample_Value | sample_value | 主表 | 样品重量 |
|
|||
|
|
| sample_Unit | sample_unit | 主表 | 样品单位 |
|
|||
|
|
| analysis_time | analysis_time | 主表 | 分析时间 |
|
|||
|
|
| result_Value | result_value | 主表 | 检测结果值 |
|
|||
|
|
| result_type | result_type | 主表 | 结果类型 |
|
|||
|
|
| method_name | method_name | 主表 | 检测方法 |
|
|||
|
|
| apparatusName | apparatus_name | 主表 | 设备名称 |
|
|||
|
|
| manufacturerName | manufacturer_name | 主表 | 制造商名称 |
|
|||
|
|
| conversion_coefficient | conversion_coefficient | 主表 | 转换系数 |
|
|||
|
|
| protein_factor | protein_factor | 主表 | 蛋白质因子 |
|
|||
|
|
| alkali_volume | alkali_volume | 主表 | 碱液体积 |
|
|||
|
|
| dilution_volume | dilution_volume | 主表 | 稀释体积 |
|
|||
|
|
| receiver_volume | receiver_volume | 主表 | 接收器体积 |
|
|||
|
|
| titrant_volume_for_blank | titrant_volume_blank | 主表 | 空白滴定液体积 |
|
|||
|
|
| titrant_volume_for_sample | titrant_volume_sample | 主表 | 样品滴定液体积 |
|
|||
|
|
| titration_acid_concentration | titration_acid_concentration | 主表 | 滴定酸浓度 |
|
|||
|
|
| distillation_Value | distillation_value | 主表 | 蒸馏值 |
|
|||
|
|
| steam_power | steam_power | 主表 | 蒸汽功率 |
|
|||
|
|
| water_coefficient | water_coefficient | 主表 | 水系数 |
|
|||
|
|
| high_error_limit | high_error_limit | 主表 | 高错误限值 |
|
|||
|
|
| low_error_limit | low_error_limit | 主表 | 低错误限值 |
|
|||
|
|
| intercept | intercept | 主表 | 截距 |
|
|||
|
|
| slope | slope | 主表 | 斜率 |
|
|||
|
|
| created | data_creator | 主表 | TCP数据创建者 |
|
|||
|
|
| data[].circulateOrder | circulate_order | 子表 | 循环顺序 |
|
|||
|
|
| data[].inspectionDate | inspection_date | 子表 | 检验日期 |
|
|||
|
|
| data[].inspectionTime | inspection_time | 子表 | 检验时间 |
|
|||
|
|
| data[].inspectionProject | inspection_project | 子表 | 检验项目 |
|
|||
|
|
| data[].inspectionResult | inspection_result | 子表 | 检验结果 |
|
|||
|
|
| 设备配置参数 | device_parameters | 主表JSON | 设备操作参数 |
|
|||
|
|
| 完整TCP数据 | tcp_raw_data | 主表JSON | 原始数据备份 |
|
|||
|
|
|
|||
|
|
# 详细技术规范
|
|||
|
|
|
|||
|
|
## 数据库设计
|
|||
|
|
|
|||
|
|
### 1. 定氮仪质检主表 (qc_nitrogen_analyzer_inspection)
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE `qc_nitrogen_analyzer_inspection` (
|
|||
|
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
|||
|
|
`inspection_no` varchar(50) NOT NULL COMMENT '质检单号',
|
|||
|
|
|
|||
|
|
-- 样品基本信息 (核心字段)
|
|||
|
|
`sample_name` varchar(100) DEFAULT NULL COMMENT '样品名称',
|
|||
|
|
`sample_id` varchar(50) DEFAULT NULL COMMENT '样品ID',
|
|||
|
|
`sample_value` decimal(10, 6) DEFAULT NULL COMMENT '样品重量(g)',
|
|||
|
|
`sample_unit` varchar(20) DEFAULT 'g' COMMENT '样品单位',
|
|||
|
|
`analysis_time` datetime DEFAULT NULL COMMENT '分析时间',
|
|||
|
|
|
|||
|
|
-- 检测结果 (核心字段)
|
|||
|
|
`result_value` decimal(10, 6) DEFAULT NULL COMMENT '检测结果值',
|
|||
|
|
`result_type` varchar(50) DEFAULT NULL COMMENT '结果类型',
|
|||
|
|
`nitrogen_content` decimal(10, 6) DEFAULT NULL COMMENT '氮含量(%) - 业务计算字段',
|
|||
|
|
`protein_content` decimal(10, 6) DEFAULT NULL COMMENT '蛋白质含量(%) - 业务计算字段',
|
|||
|
|
|
|||
|
|
-- 检测方法和设备 (核心字段)
|
|||
|
|
`method_name` varchar(100) DEFAULT NULL COMMENT '检测方法',
|
|||
|
|
`apparatus_name` varchar(100) DEFAULT NULL COMMENT '设备名称',
|
|||
|
|
`manufacturer_name` varchar(100) DEFAULT NULL COMMENT '制造商名称',
|
|||
|
|
|
|||
|
|
-- 关键检测参数 (核心字段)
|
|||
|
|
`conversion_coefficient` decimal(10, 6) DEFAULT NULL COMMENT '转换系数',
|
|||
|
|
`protein_factor` decimal(10, 6) DEFAULT NULL COMMENT '蛋白质因子',
|
|||
|
|
`alkali_volume` decimal(10, 3) DEFAULT NULL COMMENT '碱液体积(mL)',
|
|||
|
|
`dilution_volume` decimal(10, 3) DEFAULT NULL COMMENT '稀释体积(mL)',
|
|||
|
|
`receiver_volume` decimal(10, 3) DEFAULT NULL COMMENT '接收器体积(mL)',
|
|||
|
|
`titrant_volume_blank` decimal(12, 8) DEFAULT NULL COMMENT '空白滴定液体积(mL)',
|
|||
|
|
`titrant_volume_sample` decimal(12, 8) DEFAULT NULL COMMENT '样品滴定液体积(mL)',
|
|||
|
|
`titration_acid_concentration` decimal(12, 8) DEFAULT NULL COMMENT '滴定酸浓度(%)',
|
|||
|
|
`distillation_value` decimal(10, 3) DEFAULT NULL COMMENT '蒸馏值',
|
|||
|
|
`steam_power` decimal(5, 2) DEFAULT NULL COMMENT '蒸汽功率(%)',
|
|||
|
|
`water_coefficient` decimal(10, 6) DEFAULT NULL COMMENT '水系数',
|
|||
|
|
|
|||
|
|
-- 质量控制参数 (核心字段)
|
|||
|
|
`high_error_limit` decimal(10, 6) DEFAULT NULL COMMENT '高错误限值',
|
|||
|
|
`low_error_limit` decimal(10, 6) DEFAULT NULL COMMENT '低错误限值',
|
|||
|
|
`intercept` decimal(10, 6) DEFAULT NULL COMMENT '截距',
|
|||
|
|
`slope` decimal(10, 6) DEFAULT NULL COMMENT '斜率',
|
|||
|
|
|
|||
|
|
-- 业务字段
|
|||
|
|
`inspector` varchar(50) DEFAULT NULL COMMENT '检验员',
|
|||
|
|
`inspection_date` datetime DEFAULT NULL COMMENT '检验日期',
|
|||
|
|
`data_creator` varchar(50) DEFAULT NULL COMMENT 'TCP数据创建者',
|
|||
|
|
`status` varchar(20) DEFAULT 'pending' COMMENT '状态(pending/completed/failed)',
|
|||
|
|
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
|||
|
|
|
|||
|
|
-- 原始数据和设备配置参数 (JSON字段)
|
|||
|
|
`tcp_raw_data` text COMMENT 'TCP完整原始数据JSON',
|
|||
|
|
`device_parameters` text COMMENT '设备配置参数JSON(包含cleaning,emptying,mode等)',
|
|||
|
|
|
|||
|
|
-- 系统字段
|
|||
|
|
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
|
|||
|
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|||
|
|
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
|||
|
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|||
|
|
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
|
|||
|
|
|
|||
|
|
PRIMARY KEY (`id`),
|
|||
|
|
UNIQUE KEY `idx_inspection_no` (`inspection_no`),
|
|||
|
|
KEY `idx_sample_id` (`sample_id`),
|
|||
|
|
KEY `idx_analysis_time` (`analysis_time`),
|
|||
|
|
KEY `idx_inspection_date` (`inspection_date`),
|
|||
|
|
KEY `idx_apparatus` (`apparatus_name`)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定氮仪质检单主表';
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 1.1. 定氮仪检测详情表 (qc_nitrogen_analyzer_detail)
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE `qc_nitrogen_analyzer_detail` (
|
|||
|
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
|||
|
|
`inspection_id` bigint NOT NULL COMMENT '质检单ID',
|
|||
|
|
`circulate_order` varchar(20) DEFAULT NULL COMMENT '循环顺序',
|
|||
|
|
`inspection_date` date DEFAULT NULL COMMENT '检验日期',
|
|||
|
|
`inspection_time` time DEFAULT NULL COMMENT '检验时间',
|
|||
|
|
`inspection_project` varchar(100) DEFAULT NULL COMMENT '检验项目',
|
|||
|
|
`inspection_result` varchar(200) DEFAULT NULL COMMENT '检验结果',
|
|||
|
|
`sequence_no` int DEFAULT 0 COMMENT '序列号',
|
|||
|
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|||
|
|
PRIMARY KEY (`id`),
|
|||
|
|
KEY `idx_inspection_id` (`inspection_id`),
|
|||
|
|
KEY `idx_inspection_date` (`inspection_date`),
|
|||
|
|
FOREIGN KEY (`inspection_id`) REFERENCES `qc_nitrogen_analyzer_inspection` (`id`) ON DELETE CASCADE
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定氮仪检测详情表';
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 定氮仪质检配置表 (qc_nitrogen_analyzer_config)
|
|||
|
|
```sql
|
|||
|
|
CREATE TABLE `qc_nitrogen_analyzer_config` (
|
|||
|
|
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
|||
|
|
`config_name` varchar(100) NOT NULL COMMENT '配置名称',
|
|||
|
|
`config_content` text NOT NULL COMMENT '配置内容(JSON)',
|
|||
|
|
`is_default` char(1) NOT NULL DEFAULT '0' COMMENT '是否默认(0=否,1=是)',
|
|||
|
|
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
|||
|
|
`create_by` varchar(64) DEFAULT '' COMMENT '创建者',
|
|||
|
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|||
|
|
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
|||
|
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|||
|
|
`del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
|
|||
|
|
PRIMARY KEY (`id`),
|
|||
|
|
UNIQUE KEY `idx_config_name` (`config_name`)
|
|||
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定氮仪质检配置表';
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## ⚠️ **重要修正:一对一关系设计**
|
|||
|
|
|
|||
|
|
**业务逻辑确认**:
|
|||
|
|
- ✅ **一对一关系**:一条定氮仪检测数据 ↔ 一个质检单
|
|||
|
|
- ✅ **绑定后隐藏**:质检单绑定后不再显示为可选项
|
|||
|
|
- ✅ **独占绑定**:每个数据只能绑定一次
|
|||
|
|
|
|||
|
|
### 3. 修正后的关联设计
|
|||
|
|
|
|||
|
|
#### 3.1 定氮仪质检主表中直接存储关联信息
|
|||
|
|
```sql
|
|||
|
|
-- 在 qc_nitrogen_analyzer_inspection 主表中添加关联字段
|
|||
|
|
`associated_type` varchar(50) DEFAULT NULL COMMENT '关联质检类型(material/intermediate/foreign/final)',
|
|||
|
|
`associated_id` bigint DEFAULT NULL COMMENT '关联质检单ID',
|
|||
|
|
`association_time` datetime DEFAULT NULL COMMENT '关联时间',
|
|||
|
|
`associated_by` varchar(64) DEFAULT NULL COMMENT '关联操作人',
|
|||
|
|
`status` varchar(20) DEFAULT 'unbound' COMMENT '状态(unbound=未绑定/bound=已绑定/completed=已完成)',
|
|||
|
|
|
|||
|
|
-- 唯一约束确保一对一关系
|
|||
|
|
UNIQUE KEY `idx_associated_unique` (`associated_type`, `associated_id`),
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.2 现有质检表添加反向关联字段
|
|||
|
|
```sql
|
|||
|
|
-- 为现有四个质检表添加定氮仪关联字段
|
|||
|
|
ALTER TABLE `qc_material_inspection` ADD COLUMN `nitrogen_analyzer_id` bigint DEFAULT NULL;
|
|||
|
|
ALTER TABLE `qc_intermediate_inspection` ADD COLUMN `nitrogen_analyzer_id` bigint DEFAULT NULL;
|
|||
|
|
ALTER TABLE `qc_foreign_inspection` ADD COLUMN `nitrogen_analyzer_id` bigint DEFAULT NULL;
|
|||
|
|
ALTER TABLE `qc_final_inspection` ADD COLUMN `nitrogen_analyzer_id` bigint DEFAULT NULL;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 3.3 创建视图过滤未绑定质检单
|
|||
|
|
```sql
|
|||
|
|
-- 未绑定质检单视图(用于前端显示可选项)
|
|||
|
|
CREATE VIEW `v_unbound_material_inspections` AS
|
|||
|
|
SELECT * FROM `qc_material_inspection`
|
|||
|
|
WHERE `nitrogen_analyzer_id` IS NULL AND `del_flag` = '0';
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**关联设计优势**:
|
|||
|
|
- ✅ **性能优化**:无需中间关联表,减少查询复杂度
|
|||
|
|
- ✅ **数据一致性**:唯一约束确保一对一关系
|
|||
|
|
- ✅ **查询简化**:视图自动过滤已绑定数据
|
|||
|
|
- ✅ **状态清晰**:status字段明确绑定状态
|
|||
|
|
|
|||
|
|
## 后端架构设计
|
|||
|
|
|
|||
|
|
### 1. TCP数据接收服务
|
|||
|
|
**文件路径**: `yjh-mes/src/main/java/cn/sourceplan/quality/service/INitrogenAnalyzerTcpService.java`
|
|||
|
|
- 接收TCP数据的服务接口
|
|||
|
|
- 数据解析和验证
|
|||
|
|
- 异步处理机制
|
|||
|
|
|
|||
|
|
### 2. 质检服务层
|
|||
|
|
**文件路径**: `yjh-mes/src/main/java/cn/sourceplan/quality/service/INitrogenAnalyzerService.java`
|
|||
|
|
- 定氮仪质检CRUD操作
|
|||
|
|
- 关联绑定业务逻辑
|
|||
|
|
- 数据查询和统计
|
|||
|
|
|
|||
|
|
### 3. 控制器层
|
|||
|
|
**文件路径**: `yjh-mes/src/main/java/cn/sourceplan/quality/controller/KNAInfoController.java`
|
|||
|
|
- RESTful API接口 (映射路径: `/quality/KNAInfo`)
|
|||
|
|
- TCP数据接收端点
|
|||
|
|
- 关联操作接口
|
|||
|
|
- 权限注解: `@PreAuthorize("@ss.hasPermi('quality:KNAInfo:*')")`
|
|||
|
|
|
|||
|
|
### 4. 数据访问层
|
|||
|
|
**文件路径**: `yjh-mes/src/main/java/cn/sourceplan/quality/mapper/NitrogenAnalyzerMapper.java`
|
|||
|
|
- MyBatis-Plus数据访问
|
|||
|
|
- 复杂查询SQL
|
|||
|
|
- 关联查询支持
|
|||
|
|
|
|||
|
|
## 前端架构设计
|
|||
|
|
|
|||
|
|
### 1. 主页组件
|
|||
|
|
**文件路径**: `mes-ui/src/views/mes/quality/nitrogenAnalyzer/index.vue`
|
|||
|
|
- 列表展示和查询
|
|||
|
|
- 关联绑定操作
|
|||
|
|
- 状态管理
|
|||
|
|
|
|||
|
|
### 2. 表单组件
|
|||
|
|
**文件路径**: `mes-ui/src/views/mes/quality/nitrogenAnalyzer/form.vue`
|
|||
|
|
- 数据录入和编辑
|
|||
|
|
- 详情查看
|
|||
|
|
- TCP数据展示
|
|||
|
|
|
|||
|
|
### 3. 关联绑定弹窗
|
|||
|
|
**文件路径**: `mes-ui/src/views/mes/quality/nitrogenAnalyzer/components/AssociationDialog.vue`
|
|||
|
|
- 质检类型选择
|
|||
|
|
- 关联数据列表
|
|||
|
|
- 绑定操作确认
|
|||
|
|
|
|||
|
|
### 4. API接口
|
|||
|
|
**文件路径**: `mes-ui/src/api/mes/quality/nitrogenAnalyzer.js`
|
|||
|
|
- HTTP请求封装
|
|||
|
|
- 接口统一管理
|
|||
|
|
|
|||
|
|
### 5. 路由配置
|
|||
|
|
**文件路径**: `mes-ui/src/router/index.js`
|
|||
|
|
- 页面路由添加
|
|||
|
|
- 权限控制设置
|
|||
|
|
|
|||
|
|
## 集成点设计
|
|||
|
|
|
|||
|
|
### 1. 现有质检页面修改
|
|||
|
|
需要在以下文件的操作列添加"定氮仪质检"按钮:
|
|||
|
|
- `mes-ui/src/views/mes/quality/materialQuality/index.vue`
|
|||
|
|
- `mes-ui/src/views/mes/quality/foreignQuality/index.vue`
|
|||
|
|
- `mes-ui/src/views/mes/quality/intermediateQuality/index.vue`
|
|||
|
|
- `mes-ui/src/views/mes/quality/finalQuality/index.vue`
|
|||
|
|
|
|||
|
|
### 2. TCP服务集成
|
|||
|
|
创建独立的TCP Socket监听服务,不依赖现有设备管理服务。
|
|||
|
|
|
|||
|
|
### 3. 权限配置
|
|||
|
|
无需特殊权限配置,所有质检用户均可使用。
|
|||
|
|
|
|||
|
|
## 核心业务流程
|
|||
|
|
|
|||
|
|
### 1. TCP数据接收流程
|
|||
|
|
1. 定氮仪设备通过TCP发送检测数据
|
|||
|
|
2. 后端TCP服务接收并解析JSON数据
|
|||
|
|
3. 验证数据完整性和格式
|
|||
|
|
4. 创建定氮仪质检记录
|
|||
|
|
5. 存储原始TCP数据和解析后的结构化数据
|
|||
|
|
|
|||
|
|
### 2. 关联绑定流程(修正版)
|
|||
|
|
1. 用户在定氮仪质检主页点击"绑定质检单"按钮
|
|||
|
|
2. 弹出关联绑定对话框,选择质检类型(原料/中间产品/外来品/最终产品)
|
|||
|
|
3. 系统查询该类型的**未绑定**质检单列表(通过视图过滤)
|
|||
|
|
4. 用户选择具体质检单进行一对一绑定
|
|||
|
|
5. 系统执行双向绑定:
|
|||
|
|
- 更新定氮仪记录的`associated_type`、`associated_id`、`status`='bound'
|
|||
|
|
- 更新质检单的`nitrogen_analyzer_id`字段
|
|||
|
|
6. 绑定后该质检单从可选列表中消失
|
|||
|
|
7. 支持解绑操作(将两边字段重置为NULL)
|
|||
|
|
|
|||
|
|
### 3. 数据查询流程
|
|||
|
|
1. 支持独立查询定氮仪质检数据
|
|||
|
|
2. 支持从其他质检单查看关联的定氮仪数据
|
|||
|
|
3. 提供丰富的筛选和排序功能
|
|||
|
|
4. 支持数据导出功能
|
|||
|
|
|
|||
|
|
## 技术实现细节
|
|||
|
|
|
|||
|
|
### 1. TCP Socket监听服务
|
|||
|
|
- 使用Java原生ServerSocket监听TCP连接(端口8890)
|
|||
|
|
- 多线程处理并发连接
|
|||
|
|
- JSON数据解析和验证
|
|||
|
|
- 业务字段自动计算(nitrogen_content、protein_content)
|
|||
|
|
|
|||
|
|
### 2. 关联机制实现(修正版)
|
|||
|
|
- 直接在主表存储关联信息,实现一对一关系
|
|||
|
|
- 双向关联确保数据一致性
|
|||
|
|
- 唯一约束防止重复绑定
|
|||
|
|
- 状态字段管理绑定生命周期
|
|||
|
|
- 视图自动过滤已绑定数据,前端无需额外处理
|
|||
|
|
|
|||
|
|
### 3. 前端状态管理
|
|||
|
|
- 使用Vuex管理全局状态
|
|||
|
|
- 实时更新关联状态
|
|||
|
|
- 缓存机制优化性能
|
|||
|
|
|
|||
|
|
### 4. 安全考虑
|
|||
|
|
- TCP接口访问控制
|
|||
|
|
- 数据完整性验证
|
|||
|
|
- 操作权限控制
|
|||
|
|
- 审计日志记录
|
|||
|
|
|
|||
|
|
## 错误处理策略
|
|||
|
|
|
|||
|
|
### 1. TCP数据接收错误
|
|||
|
|
- 数据格式错误:记录错误日志,返回错误信息
|
|||
|
|
- 网络异常:重试机制,超时处理
|
|||
|
|
- 数据重复:去重处理,状态更新
|
|||
|
|
|
|||
|
|
### 2. 关联操作错误
|
|||
|
|
- 关联对象不存在:提示用户选择有效对象
|
|||
|
|
- 重复关联:检查现有关联,提示用户
|
|||
|
|
- 权限不足:返回权限错误提示
|
|||
|
|
|
|||
|
|
### 3. 前端异常处理
|
|||
|
|
- 网络请求失败:显示友好错误提示
|
|||
|
|
- 数据加载失败:提供重试机制
|
|||
|
|
- 操作失败:明确的错误信息展示
|
|||
|
|
|
|||
|
|
## 测试策略
|
|||
|
|
|
|||
|
|
### 1. 单元测试
|
|||
|
|
- 数据解析逻辑测试
|
|||
|
|
- 业务逻辑验证
|
|||
|
|
- 关联操作测试
|
|||
|
|
|
|||
|
|
### 2. 集成测试
|
|||
|
|
- TCP数据接收测试
|
|||
|
|
- 数据库操作测试
|
|||
|
|
- API接口测试
|
|||
|
|
|
|||
|
|
### 3. 端到端测试
|
|||
|
|
- 完整业务流程测试
|
|||
|
|
- 用户交互测试
|
|||
|
|
- 性能测试
|
|||
|
|
|
|||
|
|
## 实施清单
|
|||
|
|
|
|||
|
|
1. **数据库层创建**
|
|||
|
|
- 创建qc_nitrogen_analyzer_inspection主表
|
|||
|
|
- 创建qc_nitrogen_analyzer_detail子表
|
|||
|
|
- 创建qc_nitrogen_analyzer_config配置表
|
|||
|
|
- 创建qc_inspection_association关联表
|
|||
|
|
- 插入默认配置数据
|
|||
|
|
|
|||
|
|
2. **后端实体层**
|
|||
|
|
- 创建NitrogenAnalyzerInspection主表实体类
|
|||
|
|
- 创建NitrogenAnalyzerDetail子表实体类
|
|||
|
|
- 创建NitrogenAnalyzerConfig配置实体类
|
|||
|
|
- 创建InspectionAssociation关联实体类
|
|||
|
|
|
|||
|
|
3. **后端数据访问层**
|
|||
|
|
- 创建NitrogenAnalyzerMapper主表接口
|
|||
|
|
- 创建NitrogenAnalyzerDetailMapper子表接口
|
|||
|
|
- 创建NitrogenAnalyzerMapper.xml映射文件
|
|||
|
|
- 创建NitrogenAnalyzerDetailMapper.xml映射文件
|
|||
|
|
- 创建AssociationMapper接口和XML
|
|||
|
|
|
|||
|
|
4. **后端服务层**
|
|||
|
|
- 创建NitrogenAnalyzerTcpServer TCP Socket监听服务
|
|||
|
|
- 创建INitrogenDataProcessService数据处理服务接口
|
|||
|
|
- 实现NitrogenDataProcessServiceImpl
|
|||
|
|
- 创建INitrogenAnalyzerService质检业务接口
|
|||
|
|
- 实现NitrogenAnalyzerServiceImpl
|
|||
|
|
|
|||
|
|
5. **后端控制器层**
|
|||
|
|
- 创建NitrogenAnalyzerController
|
|||
|
|
- 实现RESTful API接口
|
|||
|
|
- 实现TCP数据接收端点
|
|||
|
|
|
|||
|
|
6. **前端API层**
|
|||
|
|
- 创建nitrogenAnalyzer.js API文件
|
|||
|
|
- 封装HTTP请求方法
|
|||
|
|
|
|||
|
|
7. **前端组件层**
|
|||
|
|
- 创建主页组件: `mes-ui/src/views/mes/quality/KNAInfo/index.vue`
|
|||
|
|
- 创建表单组件: `mes-ui/src/views/mes/quality/KNAInfo/form.vue`
|
|||
|
|
- 创建关联绑定弹窗: `mes-ui/src/views/mes/quality/KNAInfo/AssociationDialog.vue`
|
|||
|
|
- API文件: `mes-ui/src/api/mes/quality/KNAInfo.js`
|
|||
|
|
|
|||
|
|
8. **前端路由配置**
|
|||
|
|
- 添加定氮仪质检路由到router/index.js
|
|||
|
|
- 配置权限控制
|
|||
|
|
|
|||
|
|
9. **现有页面集成**
|
|||
|
|
- 修改materialQuality/index.vue添加操作按钮
|
|||
|
|
- 修改foreignQuality/index.vue添加操作按钮
|
|||
|
|
- 修改intermediateQuality/index.vue添加操作按钮
|
|||
|
|
- 修改finalQuality/index.vue添加操作按钮
|
|||
|
|
|
|||
|
|
10. **权限和菜单配置**
|
|||
|
|
- 添加权限配置项
|
|||
|
|
- 配置菜单显示
|
|||
|
|
|
|||
|
|
11. **测试和验证**
|
|||
|
|
- 编写单元测试
|
|||
|
|
- 执行集成测试
|
|||
|
|
- 验证TCP数据接收功能
|
|||
|
|
|
|||
|
|
12. **文档和部署**
|
|||
|
|
- 更新技术文档
|
|||
|
|
- 准备部署脚本
|
|||
|
|
- 验证生产环境兼容性
|
|||
|
|
|
|||
|
|
# 当前执行步骤:"2. 制定详细技术规范"
|
|||
|
|
|
|||
|
|
# 任务进度
|
|||
|
|
[2025-09-18 15:30:00]
|
|||
|
|
- 完成:现有质检系统架构分析
|
|||
|
|
- 完成:TCP数据处理机制研究
|
|||
|
|
- 完成:多种实现方案设计
|
|||
|
|
- 状态:研究阶段完成,等待方案确认
|
|||
|
|
|
|||
|
|
[2025-09-18 15:35:00]
|
|||
|
|
- 确认:用户选择方案一(独立质检类型架构)
|
|||
|
|
- 进行:制定详细技术规范和实施计划
|
|||
|
|
- 状态:规划阶段
|
|||
|
|
|
|||
|
|
[2025-09-18 15:45:00]
|
|||
|
|
- 完成:TCP字段与数据库字段完整对应分析
|
|||
|
|
- 修正:数据库表结构设计,新增子表和JSON字段策略
|
|||
|
|
- 确认:扩展性好的存储方案(主表+子表+JSON)
|
|||
|
|
- 完成:标准数据库命名规范设计
|
|||
|
|
- 更新:实施清单包含所有必要组件
|
|||
|
|
- 状态:详细规划完成,准备进入执行阶段
|
|||
|
|
|
|||
|
|
## TCP服务详细实现
|
|||
|
|
|
|||
|
|
### 1. TCP Socket监听服务
|
|||
|
|
```java
|
|||
|
|
@Component
|
|||
|
|
@Slf4j
|
|||
|
|
public class NitrogenAnalyzerTcpServer {
|
|||
|
|
private ServerSocket serverSocket;
|
|||
|
|
private static final int TCP_PORT = 8890;
|
|||
|
|
private volatile boolean isRunning = false;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private INitrogenDataProcessService dataProcessService;
|
|||
|
|
|
|||
|
|
@PostConstruct
|
|||
|
|
public void startTcpServer() {
|
|||
|
|
try {
|
|||
|
|
serverSocket = new ServerSocket(TCP_PORT);
|
|||
|
|
isRunning = true;
|
|||
|
|
new Thread(this::listenForConnections).start();
|
|||
|
|
log.info("定氮仪TCP服务器启动,监听端口: {}", TCP_PORT);
|
|||
|
|
} catch (IOException e) {
|
|||
|
|
log.error("TCP服务器启动失败", e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void listenForConnections() {
|
|||
|
|
while (isRunning && !serverSocket.isClosed()) {
|
|||
|
|
try {
|
|||
|
|
Socket clientSocket = serverSocket.accept();
|
|||
|
|
log.info("接收到定氮仪连接: {}", clientSocket.getRemoteSocketAddress());
|
|||
|
|
new Thread(() -> handleConnection(clientSocket)).start();
|
|||
|
|
} catch (IOException e) {
|
|||
|
|
if (isRunning) {
|
|||
|
|
log.error("接受TCP连接失败", e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void handleConnection(Socket socket) {
|
|||
|
|
try (BufferedReader reader = new BufferedReader(
|
|||
|
|
new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) {
|
|||
|
|
StringBuilder jsonBuilder = new StringBuilder();
|
|||
|
|
String line;
|
|||
|
|
while ((line = reader.readLine()) != null) {
|
|||
|
|
jsonBuilder.append(line);
|
|||
|
|
}
|
|||
|
|
String jsonData = jsonBuilder.toString();
|
|||
|
|
log.info("接收到定氮仪数据: {}", jsonData);
|
|||
|
|
|
|||
|
|
// 异步处理数据
|
|||
|
|
dataProcessService.processNitrogenData(jsonData);
|
|||
|
|
|
|||
|
|
} catch (IOException e) {
|
|||
|
|
log.error("处理TCP数据失败", e);
|
|||
|
|
} finally {
|
|||
|
|
try {
|
|||
|
|
socket.close();
|
|||
|
|
} catch (IOException e) {
|
|||
|
|
log.error("关闭socket失败", e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@PreDestroy
|
|||
|
|
public void stopTcpServer() {
|
|||
|
|
isRunning = false;
|
|||
|
|
try {
|
|||
|
|
if (serverSocket != null && !serverSocket.isClosed()) {
|
|||
|
|
serverSocket.close();
|
|||
|
|
log.info("定氮仪TCP服务器已停止");
|
|||
|
|
}
|
|||
|
|
} catch (IOException e) {
|
|||
|
|
log.error("停止TCP服务器失败", e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 数据处理服务接口
|
|||
|
|
```java
|
|||
|
|
public interface INitrogenDataProcessService {
|
|||
|
|
/**
|
|||
|
|
* 处理定氮仪TCP数据
|
|||
|
|
* @param jsonData TCP接收的JSON数据
|
|||
|
|
*/
|
|||
|
|
void processNitrogenData(String jsonData);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 数据处理服务实现
|
|||
|
|
```java
|
|||
|
|
@Service
|
|||
|
|
@Slf4j
|
|||
|
|
@Transactional
|
|||
|
|
public class NitrogenDataProcessServiceImpl implements INitrogenDataProcessService {
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private INitrogenAnalyzerService nitrogenAnalyzerService;
|
|||
|
|
|
|||
|
|
@Autowired
|
|||
|
|
private SysCodeRuleService sysCodeRuleService;
|
|||
|
|
|
|||
|
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
|||
|
|
|
|||
|
|
@Override
|
|||
|
|
public void processNitrogenData(String jsonData) {
|
|||
|
|
try {
|
|||
|
|
// 解析JSON数据
|
|||
|
|
JsonNode dataNode = objectMapper.readTree(jsonData);
|
|||
|
|
|
|||
|
|
// 数据验证
|
|||
|
|
validateTcpData(dataNode);
|
|||
|
|
|
|||
|
|
// 转换为实体对象
|
|||
|
|
NitrogenAnalyzerInspection inspection = convertToInspection(dataNode);
|
|||
|
|
List<NitrogenAnalyzerDetail> details = convertToDetails(dataNode);
|
|||
|
|
|
|||
|
|
// 计算业务字段
|
|||
|
|
calculateBusinessFields(inspection);
|
|||
|
|
|
|||
|
|
// 生成质检单号
|
|||
|
|
generateInspectionNo(inspection);
|
|||
|
|
|
|||
|
|
// 保存数据
|
|||
|
|
nitrogenAnalyzerService.saveInspectionWithDetails(inspection, details);
|
|||
|
|
|
|||
|
|
log.info("定氮仪数据处理成功,质检单号: {}", inspection.getInspectionNo());
|
|||
|
|
|
|||
|
|
} catch (Exception e) {
|
|||
|
|
log.error("处理定氮仪TCP数据失败: {}", jsonData, e);
|
|||
|
|
throw new ServiceException("TCP数据处理失败: " + e.getMessage());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 验证TCP数据
|
|||
|
|
*/
|
|||
|
|
private void validateTcpData(JsonNode dataNode) {
|
|||
|
|
if (dataNode == null || dataNode.isNull()) {
|
|||
|
|
throw new ServiceException("TCP数据不能为空");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证必填字段
|
|||
|
|
String[] requiredFields = {"sample_Name", "result_Value", "analysis_time", "apparatusName"};
|
|||
|
|
for (String field : requiredFields) {
|
|||
|
|
if (!dataNode.has(field) || dataNode.get(field).isNull()) {
|
|||
|
|
throw new ServiceException("缺少必填字段: " + field);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 转换为主表实体
|
|||
|
|
*/
|
|||
|
|
private NitrogenAnalyzerInspection convertToInspection(JsonNode dataNode) {
|
|||
|
|
NitrogenAnalyzerInspection inspection = new NitrogenAnalyzerInspection();
|
|||
|
|
|
|||
|
|
// 样品基本信息
|
|||
|
|
inspection.setSampleName(getStringValue(dataNode, "sample_Name"));
|
|||
|
|
inspection.setSampleId(getStringValue(dataNode, "sample_id"));
|
|||
|
|
inspection.setSampleValue(getBigDecimalValue(dataNode, "sample_Value"));
|
|||
|
|
inspection.setSampleUnit(getStringValue(dataNode, "sample_Unit"));
|
|||
|
|
inspection.setAnalysisTime(getDateTimeValue(dataNode, "analysis_time"));
|
|||
|
|
|
|||
|
|
// 检测结果
|
|||
|
|
inspection.setResultValue(getBigDecimalValue(dataNode, "result_Value"));
|
|||
|
|
inspection.setResultType(getStringValue(dataNode, "result_type"));
|
|||
|
|
|
|||
|
|
// 检测方法和设备
|
|||
|
|
inspection.setMethodName(getStringValue(dataNode, "method_name"));
|
|||
|
|
inspection.setApparatusName(getStringValue(dataNode, "apparatusName"));
|
|||
|
|
inspection.setManufacturerName(getStringValue(dataNode, "manufacturerName"));
|
|||
|
|
|
|||
|
|
// 关键检测参数
|
|||
|
|
inspection.setConversionCoefficient(getBigDecimalValue(dataNode, "conversion_coefficient"));
|
|||
|
|
inspection.setProteinFactor(getBigDecimalValue(dataNode, "protein_factor"));
|
|||
|
|
inspection.setAlkaliVolume(getBigDecimalValue(dataNode, "alkali_volume"));
|
|||
|
|
inspection.setDilutionVolume(getBigDecimalValue(dataNode, "dilution_volume"));
|
|||
|
|
inspection.setReceiverVolume(getBigDecimalValue(dataNode, "receiver_volume"));
|
|||
|
|
inspection.setTitrantVolumeBlank(getBigDecimalValue(dataNode, "titrant_volume_for_blank"));
|
|||
|
|
inspection.setTitrantVolumeSample(getBigDecimalValue(dataNode, "titrant_volume_for_sample"));
|
|||
|
|
inspection.setTitrationAcidConcentration(getBigDecimalValue(dataNode, "titration_acid_concentration"));
|
|||
|
|
inspection.setDistillationValue(getBigDecimalValue(dataNode, "distillation_Value"));
|
|||
|
|
inspection.setSteamPower(getBigDecimalValue(dataNode, "steam_power"));
|
|||
|
|
inspection.setWaterCoefficient(getBigDecimalValue(dataNode, "water_coefficient"));
|
|||
|
|
|
|||
|
|
// 质量控制参数
|
|||
|
|
inspection.setHighErrorLimit(getBigDecimalValue(dataNode, "high_error_limit"));
|
|||
|
|
inspection.setLowErrorLimit(getBigDecimalValue(dataNode, "low_error_limit"));
|
|||
|
|
inspection.setIntercept(getBigDecimalValue(dataNode, "intercept"));
|
|||
|
|
inspection.setSlope(getBigDecimalValue(dataNode, "slope"));
|
|||
|
|
|
|||
|
|
// 业务字段
|
|||
|
|
inspection.setDataCreator(getStringValue(dataNode, "created"));
|
|||
|
|
inspection.setStatus("completed");
|
|||
|
|
inspection.setInspectionDate(new Date());
|
|||
|
|
|
|||
|
|
// 存储原始JSON数据
|
|||
|
|
try {
|
|||
|
|
inspection.setRawData(objectMapper.writeValueAsString(dataNode));
|
|||
|
|
} catch (JsonProcessingException e) {
|
|||
|
|
log.warn("存储原始JSON数据失败", e);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return inspection;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 转换为子表实体列表
|
|||
|
|
*/
|
|||
|
|
private List<NitrogenAnalyzerDetail> convertToDetails(JsonNode dataNode) {
|
|||
|
|
List<NitrogenAnalyzerDetail> details = new ArrayList<>();
|
|||
|
|
|
|||
|
|
JsonNode dataArray = dataNode.get("data");
|
|||
|
|
if (dataArray != null && dataArray.isArray()) {
|
|||
|
|
for (JsonNode item : dataArray) {
|
|||
|
|
NitrogenAnalyzerDetail detail = new NitrogenAnalyzerDetail();
|
|||
|
|
detail.setCirculateOrder(getIntValue(item, "circulateOrder"));
|
|||
|
|
detail.setInspectionDate(getDateValue(item, "inspectionDate"));
|
|||
|
|
detail.setInspectionTime(getStringValue(item, "inspectionTime"));
|
|||
|
|
detail.setInspectionProject(getStringValue(item, "inspectionProject"));
|
|||
|
|
detail.setInspectionResult(getStringValue(item, "inspectionResult"));
|
|||
|
|
details.add(detail);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return details;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算业务字段
|
|||
|
|
*/
|
|||
|
|
private void calculateBusinessFields(NitrogenAnalyzerInspection inspection) {
|
|||
|
|
// nitrogen_content = result_value (当result_type="% Nitrogen"时)
|
|||
|
|
if ("% Nitrogen".equals(inspection.getResultType()) && inspection.getResultValue() != null) {
|
|||
|
|
inspection.setNitrogenContent(inspection.getResultValue());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// protein_content = result_value * protein_factor
|
|||
|
|
if (inspection.getResultValue() != null && inspection.getProteinFactor() != null) {
|
|||
|
|
BigDecimal proteinContent = inspection.getResultValue().multiply(inspection.getProteinFactor());
|
|||
|
|
inspection.setProteinContent(proteinContent);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生成质检单号
|
|||
|
|
*/
|
|||
|
|
private void generateInspectionNo(NitrogenAnalyzerInspection inspection) {
|
|||
|
|
try {
|
|||
|
|
// 使用定氮仪专用的编码规则(需要先在数据库中配置ID=23的规则)
|
|||
|
|
String number = sysCodeRuleService.queryNewCodeById(23L, true);
|
|||
|
|
inspection.setInspectionNo(number);
|
|||
|
|
} catch (Exception e) {
|
|||
|
|
// 如果编码规则不存在,使用默认规则生成
|
|||
|
|
String defaultNo = "DN" + DateUtil.format(new Date(), "yyyyMMdd") +
|
|||
|
|
String.format("%03d", System.currentTimeMillis() % 1000);
|
|||
|
|
inspection.setInspectionNo(defaultNo);
|
|||
|
|
log.warn("定氮仪编码规则不存在,使用默认编号: {}", defaultNo);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 辅助方法
|
|||
|
|
private String getStringValue(JsonNode node, String fieldName) {
|
|||
|
|
JsonNode field = node.get(fieldName);
|
|||
|
|
return field != null && !field.isNull() ? field.asText() : null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private BigDecimal getBigDecimalValue(JsonNode node, String fieldName) {
|
|||
|
|
JsonNode field = node.get(fieldName);
|
|||
|
|
if (field != null && !field.isNull()) {
|
|||
|
|
try {
|
|||
|
|
return new BigDecimal(field.asText());
|
|||
|
|
} catch (NumberFormatException e) {
|
|||
|
|
log.warn("字段{}的值{}不是有效数字", fieldName, field.asText());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Integer getIntValue(JsonNode node, String fieldName) {
|
|||
|
|
JsonNode field = node.get(fieldName);
|
|||
|
|
if (field != null && !field.isNull()) {
|
|||
|
|
try {
|
|||
|
|
return Integer.parseInt(field.asText());
|
|||
|
|
} catch (NumberFormatException e) {
|
|||
|
|
log.warn("字段{}的值{}不是有效整数", fieldName, field.asText());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Date getDateTimeValue(JsonNode node, String fieldName) {
|
|||
|
|
String dateStr = getStringValue(node, fieldName);
|
|||
|
|
if (dateStr != null) {
|
|||
|
|
try {
|
|||
|
|
return DateUtil.parse(dateStr, "yyyy-MM-dd HH:mm:ss");
|
|||
|
|
} catch (Exception e) {
|
|||
|
|
log.warn("字段{}的值{}不是有效日期时间格式", fieldName, dateStr);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private Date getDateValue(JsonNode node, String fieldName) {
|
|||
|
|
String dateStr = getStringValue(node, fieldName);
|
|||
|
|
if (dateStr != null) {
|
|||
|
|
try {
|
|||
|
|
return DateUtil.parse(dateStr, "yyyy-MM-dd");
|
|||
|
|
} catch (Exception e) {
|
|||
|
|
log.warn("字段{}的值{}不是有效日期格式", fieldName, dateStr);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
# 最终审查
|
|||
|
|
|
|||
|
|
## ✅ 关键问题确认和修正
|
|||
|
|
|
|||
|
|
### 1. 关联关系设计 ✅
|
|||
|
|
- **确认**: 一对多关系 - 一个定氮仪检测可以关联多个质检单
|
|||
|
|
- **设计**: 保持多对多关系表设计,满足业务需求
|
|||
|
|
|
|||
|
|
### 2. TCP数据接收 ✅
|
|||
|
|
- **确认**: 定氮仪通过TCP发送数据
|
|||
|
|
- **实现**: Java原生ServerSocket监听8890端口,多线程处理并发
|
|||
|
|
|
|||
|
|
### 3. 业务字段计算 ✅
|
|||
|
|
- **确认**: nitrogen_content和protein_content通过业务计算得出
|
|||
|
|
- **逻辑**: nitrogen_content = result_value, protein_content = result_value × protein_factor
|
|||
|
|
|
|||
|
|
### 4. 权限控制 ✅
|
|||
|
|
- **确认**: 无需特殊权限级别
|
|||
|
|
- **实现**: 所有质检用户均可使用
|
|||
|
|
|
|||
|
|
## 🔧 技术方案修正
|
|||
|
|
|
|||
|
|
### 数据库精度优化
|
|||
|
|
- 滴定相关字段精度: decimal(10,6) → decimal(12,8)
|
|||
|
|
- 确保4位小数精度满足设备要求
|
|||
|
|
|
|||
|
|
### TCP服务架构优化
|
|||
|
|
- 独立TCP监听服务,不依赖设备管理模块
|
|||
|
|
- 异步数据处理,避免阻塞TCP连接
|
|||
|
|
- 完整的数据验证和异常处理机制
|
|||
|
|
|
|||
|
|
### 业务逻辑完善
|
|||
|
|
- 自动计算nitrogen_content和protein_content
|
|||
|
|
- 自动生成质检单号
|
|||
|
|
- 原始JSON数据完整保存
|
|||
|
|
|
|||
|
|
## 📋 实施就绪确认
|
|||
|
|
|
|||
|
|
✅ **数据库设计**: 完整的4张表结构,字段精度已优化
|
|||
|
|
✅ **后端架构**: TCP服务+业务服务+REST API完整设计
|
|||
|
|
✅ **前端组件**: 主页+表单+关联弹窗完整规划
|
|||
|
|
✅ **集成方案**: 四个质检页面集成点明确
|
|||
|
|
✅ **测试策略**: TCP模拟+业务功能+集成测试规划
|
|||
|
|
|
|||
|
|
**状态**: 技术方案完整,可以开始实施 🚀
|
|||
|
|
|
|||
|
|
## 📋 数据库初始化脚本
|
|||
|
|
|
|||
|
|
### 1. 定氮仪编码规则配置
|
|||
|
|
```sql
|
|||
|
|
-- 插入定氮仪质检编码规则
|
|||
|
|
INSERT INTO `sys_code_rule` (id, name, basic_domain, status, create_by, create_time, remark) VALUES
|
|||
|
|
(23, '定氮仪质检编码规则', 'NitrogenInspection', '0', 'admin', NOW(), '定氮仪质检单号生成规则');
|
|||
|
|
|
|||
|
|
-- 插入编码规则详细配置: DN + 日期 + 3位流水号 (例: DN20250918001)
|
|||
|
|
INSERT INTO `sys_code_rule_entry` (rule_id, sort, type_id, length_flow, max_flow, constant_char, date_format, code_cover) VALUES
|
|||
|
|
(23, 1, 'B', 0, 0, 'DN', NULL, ''),
|
|||
|
|
(23, 2, 'C', 0, 0, '', 'yyyyMMdd', ''),
|
|||
|
|
(23, 3, 'A', 3, 1, '', NULL, '0');
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. TCP端口配置检查
|
|||
|
|
```yaml
|
|||
|
|
# 需要在application.yml中添加定氮仪TCP配置
|
|||
|
|
nitrogen:
|
|||
|
|
tcp:
|
|||
|
|
port: 8890 # 确保此端口未被占用
|
|||
|
|
enabled: true
|
|||
|
|
timeout: 30000 # 连接超时30秒
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 菜单权限配置 ✅ 已完成
|
|||
|
|
```sql
|
|||
|
|
-- ✅ 实际菜单配置已插入数据库:
|
|||
|
|
-- 2364 定氮仪信息 2123 7 KNAInfo mes/quality/KNAInfo/index 0 0 C 0 0 quality:KNAInfo #
|
|||
|
|
-- 2365 定氮仪信息查询 2364 0 query 1 0 F 0 0 quality:KNAInfo:query #
|
|||
|
|
-- 2366 定氮仪信息新增 2364 1 create 1 0 F 0 0 quality:KNAInfo:create #
|
|||
|
|
-- 2367 定氮仪信息修改 2364 2 edit 1 0 F 0 0 quality:KNAInfo:edit #
|
|||
|
|
-- 2368 定氮仪信息删除 2364 3 remove 1 0 F 0 0 quality:KNAInfo:remove #
|
|||
|
|
-- 2369 定氮仪信息导出 2364 4 export 1 0 F 0 0 quality:KNAInfo:export #
|
|||
|
|
|
|||
|
|
-- 实际配置信息:
|
|||
|
|
-- 主菜单ID: 2364 (定氮仪信息)
|
|||
|
|
-- 父菜单ID: 2123 (质量管理)
|
|||
|
|
-- 路径: KNAInfo
|
|||
|
|
-- 组件: mes/quality/KNAInfo/index
|
|||
|
|
-- 权限前缀: quality:KNAInfo
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## ⚠️ 实施前必须确认的问题
|
|||
|
|
|
|||
|
|
### 1. TCP端口冲突检查
|
|||
|
|
- **端口8890**是否已被其他服务占用?
|
|||
|
|
- 现有系统使用端口: 8080(HTTP), 6379(Redis), 1883(MQTT)
|
|||
|
|
- 建议确认:`netstat -an | grep 8890`
|
|||
|
|
|
|||
|
|
### 2. 编码规则ID分配
|
|||
|
|
- **ID=23**是否可用?需要确认数据库中下一个可用ID
|
|||
|
|
- 建议查询:`SELECT MAX(id) FROM sys_code_rule;`
|
|||
|
|
|
|||
|
|
### 3. 菜单父ID确认 ✅ 已确认
|
|||
|
|
- **parent_id=2123**为实际质量管理菜单ID
|
|||
|
|
- **主菜单ID=2364**为定氮仪信息菜单ID
|
|||
|
|
|
|||
|
|
### 4. 路由组件路径确认 ✅ 已调整
|
|||
|
|
- ✅ **前端组件路径**:`mes/quality/KNAInfo/index` (与实际菜单配置一致)
|
|||
|
|
- ✅ **前端路由路径**:`/quality/KNAInfo` (与实际菜单配置一致)
|
|||
|
|
- ✅ **API路径**:`/quality/KNAInfo` (与权限配置一致)
|
|||
|
|
|
|||
|
|
### 5. 前端路由配置 ✅ 需要调整
|
|||
|
|
```javascript
|
|||
|
|
// mes-ui/src/router/index.js 中需要添加的路由 (根据实际菜单配置调整)
|
|||
|
|
{
|
|||
|
|
path: '/quality/KNAInfo',
|
|||
|
|
component: Layout,
|
|||
|
|
hidden: true,
|
|||
|
|
redirect: 'noRedirect',
|
|||
|
|
children: [
|
|||
|
|
{
|
|||
|
|
path: 'index',
|
|||
|
|
component: () => import('@/views/mes/quality/KNAInfo/index'),
|
|||
|
|
name: 'KNAInfo',
|
|||
|
|
meta: { title: '定氮仪信息', icon: 'tool' }
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
path: 'form',
|
|||
|
|
component: () => import('@/views/mes/quality/KNAInfo/form'),
|
|||
|
|
name: 'KNAInfoForm',
|
|||
|
|
meta: { title: '定氮仪信息详情', activeMenu: '/quality/KNAInfo' }
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**重要调整**:
|
|||
|
|
- ✅ 路径改为:`/quality/KNAInfo` (与菜单配置一致)
|
|||
|
|
- ✅ 组件目录:`mes/quality/KNAInfo/` (与菜单配置一致)
|
|||
|
|
- ✅ 权限前缀:`quality:KNAInfo` (与菜单配置一致)
|