Files
niumalll/backend/routes/quality.js
2025-09-02 21:59:27 +08:00

548 lines
14 KiB
JavaScript

const express = require('express');
const router = express.Router();
const Joi = require('joi');
// 模拟质量检测数据
let qualityRecords = [
{
id: 1,
orderId: 1,
inspectionCode: 'QC001',
inspectorName: '张检验员',
inspectionDate: '2024-01-15',
inspectionLocation: '山东省济南市历城区牲畜养殖基地',
cattleCount: 50,
samplingCount: 5,
inspectionType: 'pre_transport',
healthStatus: 'healthy',
quarantineCertificate: 'QC001_certificate.pdf',
vaccineRecords: [
{
vaccineName: '口蹄疫疫苗',
vaccineDate: '2024-01-01',
batchNumber: 'VAC20240101'
}
],
diseaseTests: [
{
testName: '布鲁氏菌病检测',
result: 'negative',
testDate: '2024-01-10'
},
{
testName: '结核病检测',
result: 'negative',
testDate: '2024-01-10'
}
],
weightCheck: {
averageWeight: 450,
weightRange: '420-480',
weightVariance: 15
},
qualityGrade: 'A',
qualityScore: 95,
issues: [],
recommendations: [
'建议继续保持当前饲养标准',
'注意观察牲畜健康状况'
],
photos: [
'inspection_001_1.jpg',
'inspection_001_2.jpg'
],
status: 'passed',
createdAt: new Date('2024-01-15'),
updatedAt: new Date('2024-01-15')
},
{
id: 2,
orderId: 2,
inspectionCode: 'QC002',
inspectorName: '李检验员',
inspectionDate: '2024-01-16',
inspectionLocation: '内蒙古呼和浩特市草原牧场',
cattleCount: 80,
samplingCount: 8,
inspectionType: 'pre_transport',
healthStatus: 'healthy',
quarantineCertificate: 'QC002_certificate.pdf',
vaccineRecords: [
{
vaccineName: '口蹄疫疫苗',
vaccineDate: '2023-12-15',
batchNumber: 'VAC20231215'
}
],
diseaseTests: [
{
testName: '布鲁氏菌病检测',
result: 'negative',
testDate: '2024-01-12'
}
],
weightCheck: {
averageWeight: 480,
weightRange: '450-520',
weightVariance: 20
},
qualityGrade: 'A',
qualityScore: 92,
issues: [
{
type: 'minor',
description: '个别牲畜体重偏轻',
solution: '加强营养补充'
}
],
recommendations: [
'对体重偏轻的牲畜进行重点关注',
'适当调整饲料配比'
],
photos: [
'inspection_002_1.jpg',
'inspection_002_2.jpg',
'inspection_002_3.jpg'
],
status: 'passed',
createdAt: new Date('2024-01-16'),
updatedAt: new Date('2024-01-16')
}
];
// 验证schemas
const inspectionCreateSchema = Joi.object({
orderId: Joi.number().integer().required(),
inspectorName: Joi.string().min(2).max(50).required(),
inspectionDate: Joi.date().iso().required(),
inspectionLocation: Joi.string().min(5).max(200).required(),
cattleCount: Joi.number().integer().min(1).required(),
samplingCount: Joi.number().integer().min(1).required(),
inspectionType: Joi.string().valid('pre_transport', 'during_transport', 'post_transport', 'arrival').required()
});
const qualityResultSchema = Joi.object({
healthStatus: Joi.string().valid('healthy', 'sick', 'quarantine').required(),
qualityGrade: Joi.string().valid('A+', 'A', 'B+', 'B', 'C', 'D').required(),
qualityScore: Joi.number().min(0).max(100).required(),
weightCheck: Joi.object({
averageWeight: Joi.number().min(0),
weightRange: Joi.string(),
weightVariance: Joi.number().min(0)
}),
diseaseTests: Joi.array().items(Joi.object({
testName: Joi.string().required(),
result: Joi.string().valid('positive', 'negative', 'inconclusive').required(),
testDate: Joi.date().iso().required()
})),
issues: Joi.array().items(Joi.object({
type: Joi.string().valid('critical', 'major', 'minor').required(),
description: Joi.string().required(),
solution: Joi.string()
})),
recommendations: Joi.array().items(Joi.string())
});
// 获取质量检测列表
router.get('/', (req, res) => {
try {
const {
page = 1,
pageSize = 20,
keyword,
inspectionType,
qualityGrade,
status,
startDate,
endDate
} = req.query;
let filteredRecords = [...qualityRecords];
// 关键词搜索
if (keyword) {
filteredRecords = filteredRecords.filter(record =>
record.inspectionCode.includes(keyword) ||
record.inspectorName.includes(keyword) ||
record.inspectionLocation.includes(keyword)
);
}
// 检测类型筛选
if (inspectionType) {
filteredRecords = filteredRecords.filter(record => record.inspectionType === inspectionType);
}
// 质量等级筛选
if (qualityGrade) {
filteredRecords = filteredRecords.filter(record => record.qualityGrade === qualityGrade);
}
// 状态筛选
if (status) {
filteredRecords = filteredRecords.filter(record => record.status === status);
}
// 时间范围筛选
if (startDate) {
filteredRecords = filteredRecords.filter(record =>
new Date(record.inspectionDate) >= new Date(startDate)
);
}
if (endDate) {
filteredRecords = filteredRecords.filter(record =>
new Date(record.inspectionDate) <= new Date(endDate)
);
}
// 分页处理
const startIndex = (page - 1) * pageSize;
const endIndex = startIndex + parseInt(pageSize);
const paginatedRecords = filteredRecords.slice(startIndex, endIndex);
res.json({
success: true,
data: {
list: paginatedRecords,
pagination: {
page: parseInt(page),
pageSize: parseInt(pageSize),
total: filteredRecords.length,
totalPages: Math.ceil(filteredRecords.length / pageSize)
}
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取质量检测列表失败',
error: error.message
});
}
});
// 获取质量检测详情
router.get('/:id', (req, res) => {
try {
const { id } = req.params;
const record = qualityRecords.find(r => r.id === parseInt(id));
if (!record) {
return res.status(404).json({
success: false,
message: '质量检测记录不存在'
});
}
res.json({
success: true,
data: record
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取质量检测详情失败',
error: error.message
});
}
});
// 创建质量检测记录
router.post('/', (req, res) => {
try {
const { error, value } = inspectionCreateSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
message: '参数验证失败',
errors: error.details.map(detail => detail.message)
});
}
const inspectionCode = `QC${String(Date.now()).slice(-6)}`;
const newRecord = {
id: Math.max(...qualityRecords.map(r => r.id)) + 1,
...value,
inspectionCode,
healthStatus: 'pending',
quarantineCertificate: '',
vaccineRecords: [],
diseaseTests: [],
weightCheck: null,
qualityGrade: '',
qualityScore: 0,
issues: [],
recommendations: [],
photos: [],
status: 'pending',
createdAt: new Date(),
updatedAt: new Date()
};
qualityRecords.push(newRecord);
res.status(201).json({
success: true,
message: '质量检测记录创建成功',
data: newRecord
});
} catch (error) {
res.status(500).json({
success: false,
message: '创建质量检测记录失败',
error: error.message
});
}
});
// 更新质量检测结果
router.put('/:id/result', (req, res) => {
try {
const { id } = req.params;
const { error, value } = qualityResultSchema.validate(req.body);
if (error) {
return res.status(400).json({
success: false,
message: '参数验证失败',
errors: error.details.map(detail => detail.message)
});
}
const recordIndex = qualityRecords.findIndex(r => r.id === parseInt(id));
if (recordIndex === -1) {
return res.status(404).json({
success: false,
message: '质量检测记录不存在'
});
}
// 根据检测结果确定状态
let status = 'passed';
if (value.healthStatus === 'sick' || value.qualityScore < 60) {
status = 'failed';
} else if (value.healthStatus === 'quarantine' || value.issues.some(issue => issue.type === 'critical')) {
status = 'quarantine';
}
qualityRecords[recordIndex] = {
...qualityRecords[recordIndex],
...value,
status,
updatedAt: new Date()
};
res.json({
success: true,
message: '质量检测结果更新成功',
data: qualityRecords[recordIndex]
});
} catch (error) {
res.status(500).json({
success: false,
message: '更新质量检测结果失败',
error: error.message
});
}
});
// 上传检测照片
router.post('/:id/photos', (req, res) => {
try {
const { id } = req.params;
const { photos } = req.body;
if (!Array.isArray(photos) || photos.length === 0) {
return res.status(400).json({
success: false,
message: '照片列表不能为空'
});
}
const recordIndex = qualityRecords.findIndex(r => r.id === parseInt(id));
if (recordIndex === -1) {
return res.status(404).json({
success: false,
message: '质量检测记录不存在'
});
}
qualityRecords[recordIndex].photos = [...qualityRecords[recordIndex].photos, ...photos];
qualityRecords[recordIndex].updatedAt = new Date();
res.json({
success: true,
message: '照片上传成功',
data: {
photos: qualityRecords[recordIndex].photos
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '上传照片失败',
error: error.message
});
}
});
// 获取质量统计
router.get('/stats/overview', (req, res) => {
try {
const totalInspections = qualityRecords.length;
const passedCount = qualityRecords.filter(r => r.status === 'passed').length;
const failedCount = qualityRecords.filter(r => r.status === 'failed').length;
const quarantineCount = qualityRecords.filter(r => r.status === 'quarantine').length;
const pendingCount = qualityRecords.filter(r => r.status === 'pending').length;
// 平均质量分数
const completedRecords = qualityRecords.filter(r => r.qualityScore > 0);
const averageScore = completedRecords.length > 0
? completedRecords.reduce((sum, r) => sum + r.qualityScore, 0) / completedRecords.length
: 0;
// 质量等级分布
const gradeDistribution = qualityRecords
.filter(r => r.qualityGrade)
.reduce((dist, record) => {
dist[record.qualityGrade] = (dist[record.qualityGrade] || 0) + 1;
return dist;
}, {});
// 检测类型分布
const typeDistribution = qualityRecords.reduce((dist, record) => {
dist[record.inspectionType] = (dist[record.inspectionType] || 0) + 1;
return dist;
}, {});
// 合格率
const passRate = totalInspections > 0 ? Math.round((passedCount / totalInspections) * 100) : 0;
res.json({
success: true,
data: {
totalInspections,
passedCount,
failedCount,
quarantineCount,
pendingCount,
averageScore: Math.round(averageScore * 10) / 10,
passRate,
gradeDistribution,
typeDistribution
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取质量统计失败',
error: error.message
});
}
});
// 获取质量趋势报告
router.get('/reports/trend', (req, res) => {
try {
const { period = 'month' } = req.query;
// 按时间分组统计
const now = new Date();
const trends = [];
if (period === 'month') {
// 最近12个月
for (let i = 11; i >= 0; i--) {
const date = new Date(now.getFullYear(), now.getMonth() - i, 1);
const monthRecords = qualityRecords.filter(r => {
const recordDate = new Date(r.inspectionDate);
return recordDate.getMonth() === date.getMonth() &&
recordDate.getFullYear() === date.getFullYear();
});
const passed = monthRecords.filter(r => r.status === 'passed').length;
const total = monthRecords.length;
const averageScore = monthRecords.length > 0
? monthRecords.reduce((sum, r) => sum + (r.qualityScore || 0), 0) / monthRecords.length
: 0;
trends.push({
period: `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`,
totalInspections: total,
passedCount: passed,
passRate: total > 0 ? Math.round((passed / total) * 100) : 0,
averageScore: Math.round(averageScore * 10) / 10
});
}
}
res.json({
success: true,
data: {
period,
trends
}
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取质量趋势报告失败',
error: error.message
});
}
});
// 获取检测标准配置
router.get('/standards', (req, res) => {
try {
const standards = {
weightStandards: {
cattle: {
min: 400,
max: 600,
optimal: 500
}
},
healthRequirements: [
{
name: '口蹄疫疫苗',
required: true,
validityDays: 365
},
{
name: '布鲁氏菌病检测',
required: true,
validityDays: 30
},
{
name: '结核病检测',
required: true,
validityDays: 30
}
],
gradingCriteria: {
'A+': { minScore: 95, description: '优质级' },
'A': { minScore: 85, description: '良好级' },
'B+': { minScore: 75, description: '合格级' },
'B': { minScore: 65, description: '基本合格级' },
'C': { minScore: 50, description: '待改进级' },
'D': { minScore: 0, description: '不合格级' }
}
};
res.json({
success: true,
data: standards
});
} catch (error) {
res.status(500).json({
success: false,
message: '获取检测标准失败',
error: error.message
});
}
});
module.exports = router;