This commit is contained in:
ZhuJW
2026-07-10 18:55:55 +08:00
commit fce40c7d6c
317 changed files with 170079 additions and 0 deletions
+590
View File
@@ -0,0 +1,590 @@
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 "已删除",
}