feat: 移除PDI和订单号字段,新增设备巡检模块
- 从物料跟踪页面移除订单号列和表单字段 - 从导航菜单移除PDI管理,添加设备巡检 - 新增InspectionLocation和InspectionRecord后端模型和API - 新增设备巡检前端页面(左侧点位列表,右侧设备和历史记录)
This commit is contained in:
25
backend/app/models/__init__.py
Normal file
25
backend/app/models/__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from app.models.user import User
|
||||
from app.models.material import Coil, MaterialTracking
|
||||
from app.models.production import ProductionRecord
|
||||
from app.models.plan import ProductionPlan
|
||||
from app.models.downtime import DowntimeRecord, DowntimeCategory
|
||||
from app.models.equipment import Equipment, EquipmentMaintenance
|
||||
from app.models.message import MessageLog
|
||||
from app.models.pdi import PDIRecord
|
||||
from app.models.quality import QualityRecord
|
||||
from app.models.energy import EnergyRecord
|
||||
from app.models.inspection import InspectionLocation, InspectionRecord
|
||||
|
||||
__all__ = [
|
||||
"User",
|
||||
"Coil", "MaterialTracking",
|
||||
"ProductionRecord",
|
||||
"ProductionPlan",
|
||||
"DowntimeRecord", "DowntimeCategory",
|
||||
"Equipment", "EquipmentMaintenance",
|
||||
"MessageLog",
|
||||
"PDIRecord",
|
||||
"QualityRecord",
|
||||
"EnergyRecord",
|
||||
"InspectionLocation", "InspectionRecord",
|
||||
]
|
||||
38
backend/app/models/downtime.py
Normal file
38
backend/app/models/downtime.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Text, ForeignKey, func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class DowntimeCategory(Base):
|
||||
"""停机类别"""
|
||||
__tablename__ = "downtime_categories"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
code = Column(String(20), unique=True, nullable=False)
|
||||
name = Column(String(50), nullable=False)
|
||||
category = Column(String(20), comment="大类: equipment/process/material/other")
|
||||
description = Column(Text)
|
||||
is_active = Column(Integer, default=1)
|
||||
|
||||
|
||||
class DowntimeRecord(Base):
|
||||
"""停机记录"""
|
||||
__tablename__ = "downtime_records"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
category_id = Column(Integer, ForeignKey("downtime_categories.id"))
|
||||
category_code = Column(String(20), index=True)
|
||||
category_name = Column(String(50))
|
||||
shift = Column(String(10), comment="班次")
|
||||
shift_date = Column(DateTime, comment="班期")
|
||||
start_time = Column(DateTime, nullable=False, comment="停机开始时间")
|
||||
end_time = Column(DateTime, comment="停机结束时间")
|
||||
duration = Column(Float, comment="停机时长min(自动计算)")
|
||||
equipment_code = Column(String(30), comment="停机设备编号")
|
||||
fault_desc = Column(Text, comment="故障描述")
|
||||
action_taken = Column(Text, comment="处理措施")
|
||||
root_cause = Column(Text, comment="根本原因")
|
||||
reporter = Column(String(50), comment="报告人")
|
||||
handler = Column(String(50), comment="处理人")
|
||||
is_planned = Column(Integer, default=0, comment="是否计划停机")
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
36
backend/app/models/energy.py
Normal file
36
backend/app/models/energy.py
Normal file
@@ -0,0 +1,36 @@
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Text, func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class EnergyRecord(Base):
|
||||
"""班次能耗记录"""
|
||||
__tablename__ = "energy_records"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
shift = Column(String(10), nullable=True, comment="班次 甲/乙/丙/丁")
|
||||
shift_date = Column(DateTime, nullable=True, comment="班期")
|
||||
|
||||
# 产量
|
||||
production_weight_kg = Column(Float, nullable=True, comment="本班产量 kg")
|
||||
|
||||
# 电力
|
||||
power_consumption_kwh = Column(Float, nullable=True, comment="电耗 kWh")
|
||||
power_unit = Column(Float, nullable=True, comment="电耗单耗 kWh/t")
|
||||
|
||||
# 蒸汽
|
||||
steam_consumption_kg = Column(Float, nullable=True, comment="蒸汽消耗 kg")
|
||||
steam_unit = Column(Float, nullable=True, comment="蒸汽单耗 kg/t")
|
||||
|
||||
# 盐酸
|
||||
acid_consumption_kg = Column(Float, nullable=True, comment="盐酸消耗 kg")
|
||||
acid_unit = Column(Float, nullable=True, comment="酸耗单耗 kg/t")
|
||||
|
||||
# 冷却水
|
||||
cooling_water_m3 = Column(Float, nullable=True, comment="冷却水 m³")
|
||||
water_unit = Column(Float, nullable=True, comment="冷却水单耗 m³/t")
|
||||
|
||||
# 各槽HCl平均浓度 (JSON字符串, e.g. '[180,175,170,165,160,155]')
|
||||
acid_conc_avg = Column(Text, nullable=True, comment="各槽HCl平均浓度 JSON g/L")
|
||||
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
53
backend/app/models/equipment.py
Normal file
53
backend/app/models/equipment.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Enum, Text, ForeignKey, func
|
||||
from app.database import Base
|
||||
import enum
|
||||
|
||||
|
||||
class EquipmentStatus(str, enum.Enum):
|
||||
NORMAL = "normal" # 正常
|
||||
FAULT = "fault" # 故障
|
||||
MAINTENANCE = "maintenance" # 检修
|
||||
STANDBY = "standby" # 备用
|
||||
|
||||
|
||||
class Equipment(Base):
|
||||
"""设备台账"""
|
||||
__tablename__ = "equipments"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
code = Column(String(30), unique=True, nullable=False, index=True, comment="设备编号")
|
||||
name = Column(String(100), nullable=False, comment="设备名称")
|
||||
category = Column(String(30), comment="设备类别")
|
||||
model = Column(String(50), comment="型号规格")
|
||||
manufacturer = Column(String(100), comment="制造厂商")
|
||||
install_date = Column(DateTime, comment="投用日期")
|
||||
location = Column(String(50), comment="安装位置")
|
||||
status = Column(Enum(EquipmentStatus), default=EquipmentStatus.NORMAL)
|
||||
rated_power = Column(Float, comment="额定功率kW")
|
||||
remark = Column(Text)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
|
||||
|
||||
class EquipmentMaintenance(Base):
|
||||
"""设备维保记录"""
|
||||
__tablename__ = "equipment_maintenance"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
equipment_id = Column(Integer, ForeignKey("equipments.id"), nullable=False)
|
||||
equipment_code = Column(String(30), index=True)
|
||||
maintenance_type = Column(String(20), comment="类型: repair/planned/inspection")
|
||||
title = Column(String(200), nullable=False)
|
||||
description = Column(Text, comment="维保内容")
|
||||
start_time = Column(DateTime, nullable=False)
|
||||
end_time = Column(DateTime)
|
||||
duration = Column(Float, comment="工时h")
|
||||
cost = Column(Float, comment="费用元")
|
||||
spare_parts = Column(Text, comment="更换备件(JSON格式)")
|
||||
technician = Column(String(50), comment="执行人")
|
||||
approver = Column(String(50), comment="审核人")
|
||||
next_maintenance = Column(DateTime, comment="下次维保时间")
|
||||
result = Column(String(20), comment="结果: pass/fail/pending")
|
||||
remark = Column(Text)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
28
backend/app/models/inspection.py
Normal file
28
backend/app/models/inspection.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey, func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class InspectionLocation(Base):
|
||||
__tablename__ = "inspection_locations"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
code = Column(String(30), unique=True, nullable=False, index=True)
|
||||
name = Column(String(100), nullable=False)
|
||||
description = Column(Text)
|
||||
sort_order = Column(Integer, default=0)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
|
||||
|
||||
class InspectionRecord(Base):
|
||||
__tablename__ = "inspection_records"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
location_id = Column(Integer, ForeignKey("inspection_locations.id"), nullable=False)
|
||||
location_name = Column(String(100))
|
||||
equipment_code = Column(String(30), index=True)
|
||||
equipment_name = Column(String(100))
|
||||
scan_code = Column(String(200))
|
||||
inspector = Column(String(50), nullable=False)
|
||||
result = Column(String(20), default="normal")
|
||||
notes = Column(Text)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
56
backend/app/models/material.py
Normal file
56
backend/app/models/material.py
Normal file
@@ -0,0 +1,56 @@
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Enum, Text, ForeignKey, func
|
||||
from sqlalchemy.orm import relationship
|
||||
from app.database import Base
|
||||
import enum
|
||||
|
||||
|
||||
class CoilStatus(str, enum.Enum):
|
||||
WAITING = "waiting" # 等待入线
|
||||
ON_LINE = "on_line" # 在线处理
|
||||
FINISHED = "finished" # 处理完成
|
||||
ABNORMAL = "abnormal" # 异常
|
||||
|
||||
|
||||
class Coil(Base):
|
||||
"""钢卷主档"""
|
||||
__tablename__ = "coils"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
coil_no = Column(String(30), unique=True, nullable=False, index=True, comment="卷号")
|
||||
order_no = Column(String(30), index=True, comment="订单号")
|
||||
steel_grade = Column(String(30), comment="钢种")
|
||||
spec_thickness = Column(Float, comment="规格厚度mm")
|
||||
spec_width = Column(Float, comment="规格宽度mm")
|
||||
target_thickness = Column(Float, comment="目标厚度mm")
|
||||
target_width = Column(Float, comment="目标宽度mm")
|
||||
gross_weight = Column(Float, comment="毛重kg")
|
||||
net_weight = Column(Float, comment="净重kg")
|
||||
inner_diameter = Column(Float, comment="内径mm")
|
||||
status = Column(Enum(CoilStatus), default=CoilStatus.WAITING)
|
||||
plan_id = Column(Integer, ForeignKey("production_plans.id"), nullable=True)
|
||||
remark = Column(Text)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
|
||||
tracking = relationship("MaterialTracking", back_populates="coil")
|
||||
|
||||
|
||||
class MaterialTracking(Base):
|
||||
"""物料跟踪记录"""
|
||||
__tablename__ = "material_tracking"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
coil_id = Column(Integer, ForeignKey("coils.id"), nullable=False, index=True)
|
||||
coil_no = Column(String(30), nullable=False, index=True)
|
||||
position = Column(String(50), comment="当前位置/工位")
|
||||
event_type = Column(String(30), comment="事件类型: entry/exit/process/inspect")
|
||||
event_desc = Column(String(200), comment="事件描述")
|
||||
actual_thickness = Column(Float, comment="实测厚度")
|
||||
actual_width = Column(Float, comment="实测宽度")
|
||||
speed = Column(Float, comment="线速度m/min")
|
||||
tension = Column(Float, comment="张力N")
|
||||
operator = Column(String(50))
|
||||
event_time = Column(DateTime, nullable=False, index=True)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
|
||||
coil = relationship("Coil", back_populates="tracking")
|
||||
20
backend/app/models/message.py
Normal file
20
backend/app/models/message.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Text, Float, func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class MessageLog(Base):
|
||||
"""L1报文日志"""
|
||||
__tablename__ = "message_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
msg_id = Column(String(50), index=True, comment="报文ID")
|
||||
msg_type = Column(String(30), index=True, comment="报文类型")
|
||||
direction = Column(String(10), comment="方向: recv/send")
|
||||
source = Column(String(50), comment="来源系统")
|
||||
raw_data = Column(Text, comment="原始报文")
|
||||
parsed_data = Column(Text, comment="解析结果JSON")
|
||||
status = Column(String(20), default="success", comment="处理状态: success/error")
|
||||
error_msg = Column(Text, comment="错误信息")
|
||||
process_time = Column(Float, comment="处理耗时ms")
|
||||
received_at = Column(DateTime, nullable=False, index=True)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
59
backend/app/models/pdi.py
Normal file
59
backend/app/models/pdi.py
Normal file
@@ -0,0 +1,59 @@
|
||||
import enum
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Text, Enum, func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class L3Status(str, enum.Enum):
|
||||
pending = "pending"
|
||||
sent = "sent"
|
||||
confirmed = "confirmed"
|
||||
cancelled = "cancelled"
|
||||
|
||||
|
||||
class L2Status(str, enum.Enum):
|
||||
pending = "pending"
|
||||
processing = "processing"
|
||||
done = "done"
|
||||
|
||||
|
||||
class PDIRecord(Base):
|
||||
"""PDI (Process Data Input) – per-coil process order from L3."""
|
||||
__tablename__ = "pdi_records"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
coil_no = Column(String(30), nullable=False, index=True, comment="卷号")
|
||||
order_no = Column(String(30), nullable=True, comment="订单号")
|
||||
customer = Column(String(100), nullable=True, comment="客户名称")
|
||||
steel_grade = Column(String(30), nullable=True, comment="钢种")
|
||||
|
||||
# 几何规格
|
||||
thickness = Column(Float, nullable=True, comment="来料厚度 mm")
|
||||
width = Column(Float, nullable=True, comment="来料宽度 mm")
|
||||
target_thickness = Column(Float, nullable=True, comment="目标厚度 mm")
|
||||
target_width = Column(Float, nullable=True, comment="目标宽度 mm")
|
||||
|
||||
# 力学性能
|
||||
yield_strength = Column(Float, nullable=True, comment="屈服强度 MPa")
|
||||
tensile_strength = Column(Float, nullable=True, comment="抗拉强度 MPa")
|
||||
elongation = Column(Float, nullable=True, comment="延伸率 %")
|
||||
|
||||
# 卷重/尺寸
|
||||
coil_weight = Column(Float, nullable=True, comment="卷重 kg")
|
||||
inner_diameter = Column(Float, nullable=True, comment="内径 mm")
|
||||
outer_diameter = Column(Float, nullable=True, comment="外径 mm")
|
||||
|
||||
# 工艺
|
||||
process_route = Column(String(100), nullable=True, comment="工艺路径 e.g. P1+P2+P3+P4+P5+P6")
|
||||
priority = Column(Integer, default=3, comment="优先级 1-5")
|
||||
|
||||
# 状态
|
||||
l3_status = Column(Enum(L3Status), default=L3Status.pending, comment="L3状态")
|
||||
l2_status = Column(Enum(L2Status), default=L2Status.pending, comment="L2状态")
|
||||
|
||||
# 时间戳
|
||||
send_time = Column(DateTime, nullable=True, comment="下发时间")
|
||||
confirm_time = Column(DateTime, nullable=True, comment="确认时间")
|
||||
|
||||
remark = Column(Text, nullable=True, comment="备注")
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
33
backend/app/models/plan.py
Normal file
33
backend/app/models/plan.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Enum, Text, func
|
||||
from app.database import Base
|
||||
import enum
|
||||
|
||||
|
||||
class PlanStatus(str, enum.Enum):
|
||||
DRAFT = "draft" # 草稿
|
||||
CONFIRMED = "confirmed" # 已确认
|
||||
IN_PROGRESS = "in_progress" # 执行中
|
||||
COMPLETED = "completed" # 完成
|
||||
CANCELLED = "cancelled" # 取消
|
||||
|
||||
|
||||
class ProductionPlan(Base):
|
||||
"""生产计划"""
|
||||
__tablename__ = "production_plans"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
plan_no = Column(String(30), unique=True, nullable=False, index=True, comment="计划号")
|
||||
plan_date = Column(DateTime, nullable=False, comment="计划日期")
|
||||
shift = Column(String(10), comment="班次")
|
||||
plan_quantity = Column(Integer, default=0, comment="计划数量(卷)")
|
||||
plan_weight = Column(Float, default=0, comment="计划重量kg")
|
||||
actual_quantity = Column(Integer, default=0, comment="实际数量(卷)")
|
||||
actual_weight = Column(Float, default=0, comment="实际重量kg")
|
||||
status = Column(Enum(PlanStatus), default=PlanStatus.DRAFT)
|
||||
steel_grade = Column(String(30), comment="主要钢种")
|
||||
spec_range = Column(String(50), comment="规格范围")
|
||||
priority = Column(Integer, default=5, comment="优先级1-10")
|
||||
remark = Column(Text)
|
||||
created_by = Column(String(50))
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
28
backend/app/models/production.py
Normal file
28
backend/app/models/production.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Text, ForeignKey, func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class ProductionRecord(Base):
|
||||
"""生产实绩"""
|
||||
__tablename__ = "production_records"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
coil_no = Column(String(30), nullable=False, index=True)
|
||||
plan_id = Column(Integer, ForeignKey("production_plans.id"), nullable=True)
|
||||
shift = Column(String(10), comment="班次: 甲/乙/丙/丁")
|
||||
shift_date = Column(DateTime, comment="班期")
|
||||
start_time = Column(DateTime, comment="开始时间")
|
||||
end_time = Column(DateTime, comment="结束时间")
|
||||
process_length = Column(Float, comment="处理长度m")
|
||||
process_weight = Column(Float, comment="处理重量kg")
|
||||
avg_speed = Column(Float, comment="平均速度m/min")
|
||||
max_speed = Column(Float, comment="最大速度m/min")
|
||||
acid_consumption = Column(Float, comment="酸耗量L")
|
||||
inlet_thickness = Column(Float, comment="入口厚度mm")
|
||||
outlet_thickness = Column(Float, comment="出口厚度mm")
|
||||
inlet_width = Column(Float, comment="入口宽度mm")
|
||||
quality_grade = Column(String(10), comment="质量等级")
|
||||
operator = Column(String(50))
|
||||
remark = Column(Text)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
38
backend/app/models/quality.py
Normal file
38
backend/app/models/quality.py
Normal file
@@ -0,0 +1,38 @@
|
||||
from sqlalchemy import Column, Integer, String, Float, DateTime, Text, Boolean, ForeignKey, func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class QualityRecord(Base):
|
||||
"""质量检验记录"""
|
||||
__tablename__ = "quality_records"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
coil_no = Column(String(30), nullable=False, index=True, comment="卷号")
|
||||
production_record_id = Column(Integer, ForeignKey("production_records.id"), nullable=True, comment="关联生产实绩")
|
||||
|
||||
# 实测规格
|
||||
thickness_actual = Column(Float, nullable=True, comment="实测厚度 mm")
|
||||
width_actual = Column(Float, nullable=True, comment="实测宽度 mm")
|
||||
flatness = Column(Float, nullable=True, comment="平直度 IU")
|
||||
crown = Column(Float, nullable=True, comment="凸度 μm")
|
||||
|
||||
# 表面缺陷
|
||||
surface_defect_type = Column(String(50), nullable=True, comment="表面缺陷类型")
|
||||
defect_length_m = Column(Float, nullable=True, comment="缺陷长度 m")
|
||||
defect_position = Column(String(50), nullable=True, comment="缺陷位置")
|
||||
|
||||
# 质量模型评分
|
||||
pi_score = Column(Float, nullable=True, comment="酸洗指数评分 0-100")
|
||||
surface_score = Column(Float, nullable=True, comment="表面质量评分 0-100")
|
||||
overall_grade = Column(String(5), nullable=True, comment="综合等级 A1/A2/B1/B2/C")
|
||||
|
||||
# 残酸 / 粗糙度
|
||||
acid_residual = Column(Float, nullable=True, comment="残酸量 g/m²")
|
||||
roughness_ra = Column(Float, nullable=True, comment="粗糙度 Ra μm")
|
||||
|
||||
# 检验信息
|
||||
inspector = Column(String(50), nullable=True, comment="检验员")
|
||||
inspect_time = Column(DateTime, nullable=True, comment="检验时间")
|
||||
is_passed = Column(Boolean, default=True, comment="是否合格")
|
||||
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
15
backend/app/models/user.py
Normal file
15
backend/app/models/user.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from sqlalchemy import Column, Integer, String, Boolean, DateTime, func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class User(Base):
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
username = Column(String(50), unique=True, nullable=False, index=True)
|
||||
full_name = Column(String(100))
|
||||
hashed_password = Column(String(255), nullable=False)
|
||||
role = Column(String(20), default="operator") # admin/engineer/operator/viewer
|
||||
is_active = Column(Boolean, default=True)
|
||||
created_at = Column(DateTime, server_default=func.now())
|
||||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||||
Reference in New Issue
Block a user