from datetime import datetime from app import db from sqlalchemy.orm import relationship class TicketReply(db.Model): """工单回复模型""" __tablename__ = 'ticket_reply' reply_id = db.Column(db.Integer, primary_key=True) ticket_id = db.Column(db.Integer, db.ForeignKey('ticket.ticket_id'), nullable=False) content = db.Column(db.Text, nullable=False) # 回复内容(支持HTML) creator = db.Column(db.String(32), nullable=True) # 回复人(管理员账号或系统) create_time = db.Column(db.DateTime, default=datetime.utcnow) def to_dict(self): """转换为字典""" return { 'reply_id': self.reply_id, 'ticket_id': self.ticket_id, 'content': self.content, 'creator': self.creator, 'create_time': self.create_time.strftime('%Y-%m-%d %H:%M:%S') if self.create_time else None } class Ticket(db.Model): """工单模型""" __tablename__ = 'ticket' ticket_id = db.Column(db.Integer, primary_key=True) ticket_number = db.Column(db.String(32), unique=True, nullable=False) # 工单编号 title = db.Column(db.String(128), nullable=False) product_id = db.Column(db.String(32), db.ForeignKey('product.product_id'), nullable=False) software_version = db.Column(db.String(16), nullable=True) machine_code = db.Column(db.String(64), nullable=True) license_key = db.Column(db.String(32), nullable=True) description = db.Column(db.Text, nullable=False) priority = db.Column(db.Integer, nullable=False, default=1) # 0=低, 1=中, 2=高 status = db.Column(db.Integer, nullable=False, default=0) # 0=待处理, 1=处理中, 2=已解决, 3=已关闭 operator = db.Column(db.String(32), nullable=True) # 处理人(管理员账号) remark = db.Column(db.Text, nullable=True) # 处理备注 contact_person = db.Column(db.String(64), nullable=True) # 联系人姓名 phone = db.Column(db.String(20), nullable=True) # 联系电话 create_time = db.Column(db.DateTime, default=datetime.utcnow) update_time = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) resolve_time = db.Column(db.DateTime, nullable=True) # 解决时间 close_time = db.Column(db.DateTime, nullable=True) # 关闭时间 # 关系 replies = relationship('TicketReply', backref='ticket', lazy='dynamic', cascade='all, delete-orphan') def __init__(self, **kwargs): super(Ticket, self).__init__(**kwargs) # 如果没有提供工单编号,则自动生成 if not self.ticket_number: self.ticket_number = self.generate_ticket_number() def generate_ticket_number(self): """生成工单编号 TKT+日期+4位序号""" from datetime import datetime import random date_str = datetime.now().strftime('%Y%m%d') random_str = str(random.randint(1000, 9999)) return f"TKT{date_str}{random_str}" def get_priority_name(self): """获取优先级名称""" priority_map = { 0: '低', 1: '中', 2: '高' } return priority_map.get(self.priority, '未知') def get_status_name(self): """获取状态名称""" status_map = { 0: '待处理', 1: '处理中', 2: '已解决', 3: '已关闭' } return status_map.get(self.status, '未知') def is_pending(self): """是否待处理""" return self.status == 0 def is_processing(self): """是否处理中""" return self.status == 1 def is_resolved(self): """是否已解决""" return self.status == 2 def is_closed(self): """是否已关闭""" return self.status == 3 def assign_to(self, operator): """分配给处理人""" self.operator = operator if self.status == 0: self.status = 1 # 待处理 -> 处理中 db.session.commit() def resolve(self, remark=None): """解决工单""" self.status = 2 self.resolve_time = datetime.utcnow() if remark: self.remark = remark db.session.commit() def close(self, remark=None): """关闭工单""" self.status = 3 self.close_time = datetime.utcnow() if remark: self.remark = remark db.session.commit() def reopen(self): """重新打开工单""" self.status = 1 # 处理中 self.resolve_time = None self.close_time = None db.session.commit() def update_status(self, status, remark=None): """更新工单状态""" old_status = self.status self.status = status # 更新时间戳 if status == 2 and old_status != 2: self.resolve_time = datetime.utcnow() elif status == 3 and old_status != 3: self.close_time = datetime.utcnow() if remark: self.remark = remark db.session.commit() def get_processing_days(self): """获取处理天数""" if self.resolve_time: return (self.resolve_time - self.create_time).days return (datetime.utcnow() - self.create_time).days def to_dict(self): """转换为字典""" return { 'ticket_id': self.ticket_id, 'ticket_number': self.ticket_number, 'title': self.title, 'product_id': self.product_id, 'product_name': self.product.product_name if self.product else None, 'software_version': self.software_version, 'machine_code': self.machine_code, 'license_key': self.license_key, 'description': self.description, 'priority': self.priority, 'priority_name': self.get_priority_name(), 'status': self.status, 'status_name': self.get_status_name(), 'operator': self.operator, 'remark': self.remark, 'contact_person': self.contact_person, 'phone': self.phone, 'processing_days': self.get_processing_days(), 'create_time': self.create_time.strftime('%Y-%m-%d %H:%M:%S') if self.create_time else None, 'update_time': self.update_time.strftime('%Y-%m-%d %H:%M:%S') if self.update_time else None, 'resolve_time': self.resolve_time.strftime('%Y-%m-%d %H:%M:%S') if self.resolve_time else None, 'close_time': self.close_time.strftime('%Y-%m-%d %H:%M:%S') if self.close_time else None, 'replies': [reply.to_dict() for reply in self.replies.order_by(TicketReply.create_time.asc()).all()] } def __repr__(self): return f''