Files
2026-07-10 18:55:55 +08:00

590 lines
21 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import datetime
import enum
from sqlalchemy import (
BIGINT,
INTEGER,
JSON,
Column,
DateTime,
Enum,
ForeignKey,
Index,
String,
Text,
func,
)
from app import db
class User(db.Model):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(255), nullable=False)
status = db.Column(db.Integer, default=1, comment="用户状态:1-正常,0-禁用")
created_at = db.Column(db.DateTime, default=datetime.now, comment="创建时间")
updated_at = db.Column(
db.DateTime, default=datetime.now, onupdate=datetime.now, comment="更新时间"
)
email = db.Column(db.String(255), nullable=True, comment="邮箱")
avatar = db.Column(db.String(255), nullable=True, comment="头像(通常存储图片URL")
region = db.Column(db.String(255), nullable=True, comment="国家地区")
phone = db.Column(db.String(255), nullable=True, comment="电话")
brief = db.Column(db.String(255), nullable=True, comment="个人简介")
third_party_account = db.Column(
db.String(255), nullable=True, comment="第三方账号(如微信、QQ等)"
)
position = db.Column(db.String(255), nullable=True, comment="职务")
department = db.Column(db.String(255), nullable=True, comment="部门")
label = db.Column(
db.String(255), nullable=True, comment="个人标签(多个标签可逗号分隔)"
)
# 自引用外键
created_by = db.Column(db.Integer, db.ForeignKey("users.id"))
updated_by = db.Column(db.Integer, db.ForeignKey("users.id"))
# 关系定义
creator = db.relationship(
"User", foreign_keys=[created_by], remote_side=[id], backref="created_users"
)
updater = db.relationship(
"User", foreign_keys=[updated_by], remote_side=[id], backref="updated_users"
)
# 关联关系
roles = db.relationship(
"Role",
secondary="user_roles",
back_populates="users",
foreign_keys="[UserRole.user_id, UserRole.role_id]",
) # 明确指定外键)
created_roles = db.relationship(
"Role", backref="creator", foreign_keys="Role.created_by"
)
updated_roles = db.relationship(
"Role", backref="updater", foreign_keys="Role.updated_by"
)
def __repr__(self):
return f"<User {self.username}>"
class Role(db.Model):
__tablename__ = "roles"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(50), unique=True, nullable=False, comment="角色名称")
description = db.Column(db.String(255), comment="角色描述")
status = db.Column(db.Integer, default=1, comment="状态:1-启用,0-禁用")
created_at = db.Column(db.DateTime, default=datetime.now)
updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
# 外键字段
created_by = db.Column(db.Integer, db.ForeignKey("users.id"))
updated_by = db.Column(db.Integer, db.ForeignKey("users.id"))
# 关系定义
users = db.relationship(
"User",
secondary="user_roles",
back_populates="roles",
foreign_keys="[UserRole.role_id, UserRole.user_id]",
)
def __repr__(self):
return f"<Role {self.name}>"
class UserRole(db.Model):
__tablename__ = "user_roles"
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), primary_key=True)
role_id = db.Column(db.Integer, db.ForeignKey("roles.id"), primary_key=True)
created_at = db.Column(db.DateTime, default=datetime.now)
created_by = db.Column(db.Integer, db.ForeignKey("users.id"))
# 关系定义
user = db.relationship("User", foreign_keys=[user_id], backref="role_assignments")
role = db.relationship("Role", foreign_keys=[role_id], backref="user_assignments")
creator_rel = db.relationship(
"User", foreign_keys=[created_by], backref="created_assignments"
)
def __repr__(self):
return f"<UserRole user={self.user_id} role={self.role_id}>"
class AccidList(db.Model):
"""事故工单列表模型类(对应 accid_list 表)"""
__tablename__ = "accid_list" # 对应 MySQL 表名
__table_args__ = (
# 修正:使用 db.Index(和 OperationHistory 模型一致),移除注释避免兼容问题
db.Index("idx_ticket_id", "ticket_id"),
db.Index("idx_creator", "creator"),
db.Index("idx_create_time", "create_time"),
db.Index("idx_carline", "carline"),
db.Index("idx_affects_versions", "affects_versions"),
db.Index("idx_components", "components"),
db.Index("idx_labels", "labels"),
db.Index("idx_fix_versions", "fix_versions"),
db.Index("idx_accident_level", "accident_level"),
db.Index("idx_assignee_name", "assignee_name"),
# 表级属性(参考 Fst 模型的 collation 写法)
{
"mysql_charset": "utf8mb4",
"mysql_collate": "utf8mb4_unicode_ci",
"mysql_engine": "InnoDB",
"mysql_row_format": "DYNAMIC",
},
)
# 核心修正:所有字段使用 db. 前缀(和 User 模型一致)
id = db.Column(
db.Integer, primary_key=True, autoincrement=True, comment="主键ID,自增"
)
ticket_id = db.Column(db.String(50), nullable=False, comment="工单ID")
model = db.Column(db.String(100), nullable=True, default=None, comment="型号")
vin = db.Column(db.String(50), nullable=False, comment="车辆识别码(VIN)")
creator = db.Column(
db.String(50), nullable=False, default="", comment="创建人(用户名,如ZHANMEN"
)
create_time = db.Column(db.DateTime, nullable=False, comment="工单新建时间")
occur_datetime = db.Column(db.DateTime, nullable=False, comment="问题发生时间")
case_description = db.Column(db.Text, nullable=False, comment="Case描述")
fo_solution = db.Column(db.Text, nullable=False, comment="Fo解答")
category = db.Column(db.String(100), nullable=False, default="", comment="分类")
keywords = db.Column(
db.String(200), nullable=False, default="", comment="关键词(多个用逗号分隔)"
)
handle_status = db.Column(
db.String(20),
nullable=True,
default="已完成",
comment="处理状态:已完成/未完成",
)
# 修正:时间字段默认值用 db.func.current_timestamp()(和 BagFile 模型一致)
create_at = db.Column(
db.DateTime,
nullable=True,
default=func.current_timestamp(),
comment="记录插入数据库时间",
)
update_at = db.Column(
db.DateTime,
nullable=True,
default=func.current_timestamp(),
onupdate=func.current_timestamp(),
comment="记录最后更新时间",
)
bmbs_name = db.Column(
db.String(50), nullable=True, default="", comment="BMBS联系人"
)
# 修正:Enum 默认值错误(原 default='' 改为 'P1'),使用 db.Enum
accident_level = db.Column(
db.Enum("P1", "P2", "P3"), nullable=True, default="P1", comment="事故评级"
)
rdca_reporter = db.Column(
db.String(50), nullable=True, default="", comment="RDCA报告人"
)
carline = db.Column(
db.String(100),
nullable=True,
default=None,
comment="车系(关联字典表dict_code=carline",
)
software_version = db.Column(
db.String(50), nullable=True, default="", comment="软件版本"
)
labels = db.Column(
db.String(100),
nullable=True,
default=None,
comment="事故类型标签(关联字典表dict_code=labels",
)
attachment_url = db.Column(
db.String(255), nullable=True, default="", comment="附件地址(多个用逗号分隔)"
)
assignee_name = db.Column(
db.String(50), nullable=True, default="", comment="经办人(关联字典表/用户表)"
)
affects_versions = db.Column(
db.String(100),
nullable=True,
default=None,
comment="受影响的版本(关联字典表dict_code=affects_versions",
)
components = db.Column(
db.String(100),
nullable=True,
default=None,
comment="零部件/组件(关联字典表dict_code=components",
)
fix_versions = db.Column(
db.String(100),
nullable=True,
default=None,
comment="修复方案对应的版本(关联字典表dict_code=fix_versions",
)
subtasks = db.Column(
db.String(200), nullable=False, default="", comment="子任务(多个用逗号分隔)"
)
linked_work_items = db.Column(
db.String(200),
nullable=False,
default="",
comment="关联的工作项(格式:类型:ID,类型:ID)",
)
descriptions = db.Column(
db.String(255),
nullable=False,
default="",
comment="补充描述(区别于case_description",
)
mviz_link = db.Column(
db.String(255), nullable=False, default="", comment="mviz的链接"
)
system_function = db.Column(
db.String(255), nullable=False, default="", comment="system_function字段"
)
image_ids = db.Column(
db.String(255),
nullable=False,
default="",
comment="关联图片ID串,逗号分隔(如:1,2,3)",
)
def __repr__(self):
return f"<AccidList(ticket_id='{self.ticket_id}', vin='{self.vin}')>"
def to_dict(self):
"""转为字典,适配接口返回(参考 DictType 的 to_dict 方法)"""
return {
"id": self.id,
"ticket_id": self.ticket_id,
"model": self.model,
"vin": self.vin,
"creator": self.creator,
"create_time": (
self.create_time.strftime("%Y-%m-%d %H:%M:%S")
if self.create_time
else None
),
"occur_datetime": (
self.occur_datetime.strftime("%Y-%m-%d %H:%M:%S")
if self.occur_datetime
else None
),
"case_description": self.case_description,
"fo_solution": self.fo_solution,
"category": self.category,
"keywords": self.keywords,
"handle_status": self.handle_status,
"create_at": (
self.create_at.strftime("%Y-%m-%d %H:%M:%S") if self.create_at else None
),
"update_at": (
self.update_at.strftime("%Y-%m-%d %H:%M:%S") if self.update_at else None
),
"bmbs_name": self.bmbs_name,
"accident_level": self.accident_level,
"rdca_reporter": self.rdca_reporter,
"carline": self.carline,
"software_version": self.software_version,
"labels": self.labels,
"attachment_url": self.attachment_url,
"assignee_name": self.assignee_name,
"affects_versions": self.affects_versions,
"components": self.components,
"fix_versions": self.fix_versions,
"subtasks": self.subtasks,
"linked_work_items": self.linked_work_items,
"descriptions": self.descriptions,
"mviz_link": self.mviz_link,
"system_function":self.system_function,
"image_ids":self.image_ids
}
class DictType(db.Model):
"""字典类型表模型类(对应 dict_type 表)"""
__tablename__ = "dict_type" # 匹配 MySQL 表名
__table_args__ = (
# 修正:使用 db.UniqueConstraint(参考 RuleVersions 模型)
db.UniqueConstraint(
"dict_code", name="uk_dict_code", comment="字典编码唯一(对应唯一字段)"
),
# 表级属性(对齐项目规范)
{
"mysql_charset": "utf8mb4",
"mysql_collate": "utf8mb4_unicode_ci",
"mysql_engine": "InnoDB",
"mysql_row_format": "Dynamic",
"comment": "字典类型表(管理需要下拉的字段)",
},
)
# 修正:所有字段使用 db. 前缀,类型改为 db.Integer(和 User 模型一致)
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment="主键ID")
dict_code = db.Column(
db.String(50),
nullable=False,
comment="字典编码(对应accid_list的字段名,如carline、labels",
)
dict_name = db.Column(
db.String(50),
nullable=False,
comment="字典名称(字段中文名称,如车系、事故类型)",
)
remark = db.Column(
db.String(200),
nullable=True,
default="",
comment="备注(如“事故工单-车系下拉选项”)",
)
status = db.Column(
db.Integer, nullable=False, default=1, comment="状态:1-启用,0-禁用"
)
create_time = db.Column(
db.DateTime, nullable=True, default=func.current_timestamp(), comment="创建时间"
)
update_time = db.Column(
db.DateTime,
nullable=True,
default=func.current_timestamp(),
onupdate=func.current_timestamp(),
comment="更新时间",
)
def __repr__(self):
return f"<DictType(dict_code='{self.dict_code}', dict_name='{self.dict_name}', status={self.status})>"
def to_dict(self):
"""将模型对象转为字典,便于接口返回数据"""
return {
"id": self.id,
"dict_code": self.dict_code,
"dict_name": self.dict_name,
"remark": self.remark,
"status": self.status,
"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
),
}
class DictItem(db.Model):
"""字典项表模型类(对应 dict_item 表)"""
__tablename__ = "dict_item" # 严格匹配 MySQL 表名
__table_args__ = (
# 修正:使用 db.Index(对齐 OperationHistory 模型)
db.Index("idx_dict_id", "dict_id"),
db.Index("idx_item_value", "item_value"),
# 表级属性(对齐项目规范)
{
"mysql_charset": "utf8mb4",
"mysql_collate": "utf8mb4_unicode_ci",
"mysql_engine": "InnoDB",
"mysql_row_format": "Dynamic",
"comment": "字典项表(管理每个字段的具体下拉选项)",
},
)
# 修正:所有字段使用 db. 前缀,类型改为 db.Integer(和 User 模型一致)
id = db.Column(db.Integer, primary_key=True, autoincrement=True, comment="主键ID")
# 修正:外键使用 db.ForeignKey(参考 BagFile 模型)
dict_id = db.Column(
db.Integer,
db.ForeignKey("dict_type.id"),
nullable=False,
comment="关联字典类型表的ID",
)
item_value = db.Column(
db.String(100),
nullable=False,
comment="选项值(存储到accid_list的实际值,如“大众帕萨特”“P1”)",
)
item_label = db.Column(
db.String(100),
nullable=False,
comment="选项标签(前端显示的文本,如“大众帕萨特(车系)”)",
)
sort = db.Column(
db.Integer,
nullable=True,
default=0,
comment="排序号(前端下拉框显示顺序,数字越小越靠前)",
)
status = db.Column(
db.Integer,
nullable=False,
default=1,
comment="状态:1-启用,0-禁用(禁用后前端不显示)",
)
remark = db.Column(
db.String(200),
nullable=True,
default="",
comment="备注(如“2025款帕萨特专属”)",
)
create_time = db.Column(
db.DateTime, nullable=True, default=func.current_timestamp(), comment="创建时间"
)
update_time = db.Column(
db.DateTime,
nullable=True,
default=func.current_timestamp(),
onupdate=func.current_timestamp(),
comment="更新时间",
)
# 修正:关系使用 db.relationship + db.backref(参考 User 模型)
dict_type = db.relationship(
"DictType", backref=db.backref("dict_items", lazy="joined"), lazy="joined"
)
def __repr__(self):
return f"<DictItem(dict_id={self.dict_id}, item_value='{self.item_value}', item_label='{self.item_label}', status={self.status})>"
def to_dict(self):
"""将模型对象转为字典,适配接口返回数据"""
return {
"id": self.id,
"dict_id": self.dict_id,
"item_value": self.item_value,
"item_label": self.item_label,
"sort": self.sort,
"status": self.status,
"remark": self.remark,
"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
),
# 关联返回字典类型信息(可选,提升接口易用性)
"dict_code": self.dict_type.dict_code if self.dict_type else None,
"dict_name": self.dict_type.dict_name if self.dict_type else None,
}
class FileUpload(db.Model):
"""通用文件上传元信息表模型类(对应 file_uploads 表)"""
__tablename__ = "file_uploads" # 对应 MySQL 表名
__table_args__ = (
# 索引定义(与建表语句完全一致)
db.Index("idx_business_type_id", "business_type", "business_id"),
db.Index("idx_create_time", "create_time"),
db.Index("idx_status", "status"),
# 表级属性(与 AccidList 模型格式一致)
{
"mysql_charset": "utf8mb4",
"mysql_collate": "utf8mb4_unicode_ci",
"mysql_engine": "InnoDB",
"mysql_row_format": "DYNAMIC",
},
)
# 核心修正:全用 db.Integer,通过 info 指定 MySQL 底层类型(无参数报错)
id = db.Column(
db.Integer, # 模型层用 int 兼容
primary_key=True,
autoincrement=True,
info={'mysql_type': 'BIGINT UNSIGNED'}, # 数据库底层仍是 BIGINT UNSIGNED
comment="主键(自增数值ID",
)
file_name = db.Column(
db.String(255), nullable=False, comment="原始文件名(如:事故图片1.jpg)"
)
file_alias = db.Column(
db.String(255),
nullable=False,
comment="存储别名(避免重名,如:20260210_153950_89782.jpg",
)
file_path = db.Column(
db.String(512),
nullable=False,
comment="存储路径/访问URL(本地:/uploads/202602/xxx.jpgOSShttps://xxx.oss-cn-beijing.aliyuncs.com/xxx.jpg",
)
file_size = db.Column(
db.Integer, # 模型层用 int 兼容
nullable=False,
info={'mysql_type': 'BIGINT UNSIGNED'}, # 数据库底层 BIGINT UNSIGNED
comment="文件大小(字节)"
)
file_type = db.Column(
db.String(50),
nullable=False,
comment="文件MIME类型(如:image/jpeg、image/png",
)
business_type = db.Column(
db.String(50),
nullable=True,
default=None,
comment="关联业务类型(如:case=案例、user=用户头像)",
)
business_id = db.Column(
db.Integer, # 模型层用 int 兼容
nullable=True,
default=None,
info={'mysql_type': 'BIGINT UNSIGNED'}, # 数据库底层 BIGINT UNSIGNED
comment="关联业务主键(案例ID/用户ID等,初始为NULL",
)
create_time = db.Column(
db.DateTime,
nullable=False,
default=func.current_timestamp(), # 对应 DEFAULT CURRENT_TIMESTAMP
comment="上传时间",
)
create_by = db.Column(
db.String(50), nullable=True, default=None, comment="上传人(用户ID/用户名)"
)
status = db.Column(
db.Integer, # 模型层用 int 兼容
nullable=False,
default=1,
info={'mysql_type': 'TINYINT(3) UNSIGNED'}, # 数据库底层 TINYINT(3) UNSIGNED
comment="状态:1=有效,0=逻辑删除",
)
def __repr__(self):
"""模型字符串表示,便于调试"""
return f"<FileUpload(id='{self.id}', file_name='{self.file_name}', business_type='{self.business_type}')>"
def to_dict(self):
"""转为字典,适配接口返回(与 AccidList 模型风格一致)"""
return {
"id": self.id,
"file_name": self.file_name,
"file_alias": self.file_alias,
"file_path": self.file_path,
"file_size": self.file_size,
"file_type": self.file_type,
"business_type": self.business_type,
"business_id": self.business_id,
"create_time": (
self.create_time.strftime("%Y-%m-%d %H:%M:%S")
if self.create_time
else None
),
"create_by": self.create_by,
"status": self.status,
"status_text": "有效" if self.status == 1 else "已删除",
}