feat: initial commit - Phase 1 & 2 core features

This commit is contained in:
hiderfong
2026-04-22 17:07:33 +08:00
commit 1773bda06b
25005 changed files with 6252106 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
from app.models.user import User, Role, Dept, UserRole
from app.models.metadata import DataSource, Database, DataTable, DataColumn, UnstructuredFile
from app.models.classification import Category, DataLevel, RecognitionRule, ClassificationTemplate
from app.models.project import ClassificationProject, ClassificationTask, ClassificationResult, ClassificationChange
from app.models.log import OperationLog
__all__ = [
"User", "Role", "Dept", "UserRole",
"DataSource", "Database", "DataTable", "DataColumn", "UnstructuredFile",
"Category", "DataLevel", "RecognitionRule", "ClassificationTemplate",
"ClassificationProject", "ClassificationTask", "ClassificationResult", "ClassificationChange",
"OperationLog",
]
+68
View File
@@ -0,0 +1,68 @@
from datetime import datetime
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, JSON, Float, Boolean
from sqlalchemy.orm import relationship
from app.core.database import Base
class Category(Base):
__tablename__ = "category"
id = Column(Integer, primary_key=True, index=True)
parent_id = Column(Integer, ForeignKey("category.id"), nullable=True)
level = Column(Integer, default=1) # 1, 2, 3
code = Column(String(50), unique=True, nullable=False)
name = Column(String(100), nullable=False)
description = Column(Text)
sort_order = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
parent = relationship("Category", remote_side=[id], backref="children")
class DataLevel(Base):
__tablename__ = "data_level"
id = Column(Integer, primary_key=True, index=True)
code = Column(String(20), unique=True, nullable=False) # L1, L2, L3, L4, L5
name = Column(String(50), nullable=False)
description = Column(Text)
color = Column(String(20), default="#999999")
control_requirements = Column(JSON)
sort_order = Column(Integer, default=0)
class RecognitionRule(Base):
__tablename__ = "recognition_rule"
id = Column(Integer, primary_key=True, index=True)
template_id = Column(Integer, ForeignKey("classification_template.id"), nullable=False)
category_id = Column(Integer, ForeignKey("category.id"), nullable=True)
level_id = Column(Integer, ForeignKey("data_level.id"), nullable=True)
rule_type = Column(String(20), nullable=False) # regex, keyword, enum, ml
rule_name = Column(String(100))
rule_content = Column(Text, nullable=False) # regex pattern / keyword list / enum values
target_field = Column(String(20), default="column_name") # column_name, comment, sample_data
priority = Column(Integer, default=100)
hit_count = Column(Integer, default=0)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
template = relationship("ClassificationTemplate", back_populates="rules")
category = relationship("Category")
level = relationship("DataLevel")
class ClassificationTemplate(Base):
__tablename__ = "classification_template"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), nullable=False)
industry_type = Column(String(50), default="insurance_property")
version = Column(String(20), default="1.0")
description = Column(Text)
is_builtin = Column(Boolean, default=False)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
rules = relationship("RecognitionRule", back_populates="template", cascade="all, delete-orphan")
+22
View File
@@ -0,0 +1,22 @@
from datetime import datetime
from sqlalchemy import Column, Integer, String, Text, DateTime, ForeignKey
from app.core.database import Base
class OperationLog(Base):
__tablename__ = "sys_operation_log"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("sys_user.id"), nullable=True)
username = Column(String(50))
module = Column(String(50))
action = Column(String(50))
method = Column(String(10))
path = Column(String(500))
ip = Column(String(50))
request_body = Column(Text)
response_body = Column(Text)
status_code = Column(Integer)
duration_ms = Column(Integer)
created_at = Column(DateTime, default=datetime.utcnow)
+86
View File
@@ -0,0 +1,86 @@
from datetime import datetime
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text, BigInteger
from sqlalchemy.orm import relationship
from app.core.database import Base
class DataSource(Base):
__tablename__ = "data_source"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), nullable=False)
source_type = Column(String(50), nullable=False) # mysql, postgresql, oracle, dm, etc.
host = Column(String(200))
port = Column(Integer)
database_name = Column(String(100))
username = Column(String(100))
encrypted_password = Column(Text)
extra_params = Column(Text) # JSON string
status = Column(String(20), default="active") # active, inactive, error
dept_id = Column(Integer, ForeignKey("sys_dept.id"), nullable=True)
created_by = Column(Integer, ForeignKey("sys_user.id"))
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
databases = relationship("Database", back_populates="source", cascade="all, delete-orphan")
creator = relationship("User")
class Database(Base):
__tablename__ = "meta_database"
id = Column(Integer, primary_key=True, index=True)
source_id = Column(Integer, ForeignKey("data_source.id"), nullable=False)
name = Column(String(100), nullable=False)
charset = Column(String(50))
table_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
source = relationship("DataSource", back_populates="databases")
tables = relationship("DataTable", back_populates="database", cascade="all, delete-orphan")
class DataTable(Base):
__tablename__ = "meta_table"
id = Column(Integer, primary_key=True, index=True)
database_id = Column(Integer, ForeignKey("meta_database.id"), nullable=False)
name = Column(String(200), nullable=False)
comment = Column(String(500))
row_count = Column(BigInteger, default=0)
column_count = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
database = relationship("Database", back_populates="tables")
columns = relationship("DataColumn", back_populates="table", cascade="all, delete-orphan")
class DataColumn(Base):
__tablename__ = "meta_column"
id = Column(Integer, primary_key=True, index=True)
table_id = Column(Integer, ForeignKey("meta_table.id"), nullable=False)
name = Column(String(200), nullable=False)
data_type = Column(String(100))
length = Column(Integer)
comment = Column(String(500))
is_nullable = Column(Boolean, default=True)
sample_data = Column(Text) # JSON array of sample values
created_at = Column(DateTime, default=datetime.utcnow)
table = relationship("DataTable", back_populates="columns")
class UnstructuredFile(Base):
__tablename__ = "unstructured_file"
id = Column(Integer, primary_key=True, index=True)
original_name = Column(String(255), nullable=False)
file_type = Column(String(50)) # word, pdf, txt, excel
file_size = Column(BigInteger)
storage_path = Column(String(500))
extracted_text = Column(Text)
status = Column(String(20), default="pending") # pending, processed, error
created_by = Column(Integer, ForeignKey("sys_user.id"))
created_at = Column(DateTime, default=datetime.utcnow)
+114
View File
@@ -0,0 +1,114 @@
from datetime import datetime
from sqlalchemy import Column, Integer, String, Text, ForeignKey, DateTime, Float, Enum as SAEnum
from sqlalchemy.orm import relationship
import enum
from app.core.database import Base
class ProjectStatus(str, enum.Enum):
CREATED = "created"
SCANNING = "scanning"
ASSIGNING = "assigning"
LABELING = "labeling"
REVIEWING = "reviewing"
ACCEPTING = "accepting"
PUBLISHED = "published"
class TaskStatus(str, enum.Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
REJECTED = "rejected"
class ResultStatus(str, enum.Enum):
AUTO = "auto"
MANUAL = "manual"
REVIEWED = "reviewed"
PUBLISHED = "published"
CONFLICT = "conflict"
class ClassificationProject(Base):
__tablename__ = "classification_project"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(200), nullable=False)
template_id = Column(Integer, ForeignKey("classification_template.id"))
description = Column(Text)
status = Column(String(20), default=ProjectStatus.CREATED.value)
target_source_ids = Column(Text) # comma separated source ids
target_database_ids = Column(Text)
target_table_ids = Column(Text)
planned_start = Column(DateTime)
planned_end = Column(DateTime)
created_by = Column(Integer, ForeignKey("sys_user.id"), nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
tasks = relationship("ClassificationTask", back_populates="project", cascade="all, delete-orphan")
results = relationship("ClassificationResult", back_populates="project", cascade="all, delete-orphan")
class ClassificationTask(Base):
__tablename__ = "classification_task"
id = Column(Integer, primary_key=True, index=True)
project_id = Column(Integer, ForeignKey("classification_project.id"), nullable=False)
name = Column(String(200))
assigner_id = Column(Integer, ForeignKey("sys_user.id"))
assignee_id = Column(Integer, ForeignKey("sys_user.id"))
target_type = Column(String(20), default="table") # table, column, file
target_ids = Column(Text) # comma separated ids
status = Column(String(20), default=TaskStatus.PENDING.value)
deadline = Column(DateTime)
completed_at = Column(DateTime)
created_at = Column(DateTime, default=datetime.utcnow)
project = relationship("ClassificationProject", back_populates="tasks")
assigner = relationship("User", foreign_keys=[assigner_id])
assignee = relationship("User", foreign_keys=[assignee_id])
class ClassificationResult(Base):
__tablename__ = "classification_result"
id = Column(Integer, primary_key=True, index=True)
project_id = Column(Integer, ForeignKey("classification_project.id"), nullable=False)
column_id = Column(Integer, ForeignKey("meta_column.id"), nullable=True)
file_id = Column(Integer, ForeignKey("unstructured_file.id"), nullable=True)
category_id = Column(Integer, ForeignKey("category.id"))
level_id = Column(Integer, ForeignKey("data_level.id"))
source = Column(String(20), default="auto") # auto, manual, ml
confidence = Column(Float, default=0.0) # 0-1
labeler_id = Column(Integer, ForeignKey("sys_user.id"))
reviewer_id = Column(Integer, ForeignKey("sys_user.id"))
status = Column(String(20), default=ResultStatus.AUTO.value)
label_time = Column(DateTime)
review_time = Column(DateTime)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
project = relationship("ClassificationProject", back_populates="results")
category = relationship("Category")
level = relationship("DataLevel")
class ClassificationChange(Base):
__tablename__ = "classification_change"
id = Column(Integer, primary_key=True, index=True)
result_id = Column(Integer, ForeignKey("classification_result.id"), nullable=False)
change_type = Column(String(20), nullable=False) # category, level, both
old_category_id = Column(Integer, ForeignKey("category.id"))
new_category_id = Column(Integer, ForeignKey("category.id"))
old_level_id = Column(Integer, ForeignKey("data_level.id"))
new_level_id = Column(Integer, ForeignKey("data_level.id"))
reason = Column(Text)
applicant_id = Column(Integer, ForeignKey("sys_user.id"))
approver_id = Column(Integer, ForeignKey("sys_user.id"))
approval_status = Column(String(20), default="pending") # pending, approved, rejected
approval_comment = Column(Text)
created_at = Column(DateTime, default=datetime.utcnow)
+54
View File
@@ -0,0 +1,54 @@
from datetime import datetime
from sqlalchemy import Column, Integer, String, Boolean, DateTime, ForeignKey, Text
from sqlalchemy.orm import relationship
from app.core.database import Base
class Dept(Base):
__tablename__ = "sys_dept"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(100), nullable=False)
parent_id = Column(Integer, ForeignKey("sys_dept.id"), nullable=True)
sort_order = Column(Integer, default=0)
created_at = Column(DateTime, default=datetime.utcnow)
parent = relationship("Dept", remote_side=[id], backref="children")
class Role(Base):
__tablename__ = "sys_role"
id = Column(Integer, primary_key=True, index=True)
name = Column(String(50), unique=True, nullable=False)
code = Column(String(50), unique=True, nullable=False)
description = Column(String(200))
created_at = Column(DateTime, default=datetime.utcnow)
class User(Base):
__tablename__ = "sys_user"
id = Column(Integer, primary_key=True, index=True)
username = Column(String(50), unique=True, nullable=False, index=True)
email = Column(String(100), unique=True, nullable=True)
hashed_password = Column(String(255), nullable=False)
real_name = Column(String(50))
phone = Column(String(20))
is_active = Column(Boolean, default=True)
is_superuser = Column(Boolean, default=False)
dept_id = Column(Integer, ForeignKey("sys_dept.id"), nullable=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
dept = relationship("Dept")
roles = relationship("Role", secondary="sys_user_role", backref="users")
class UserRole(Base):
__tablename__ = "sys_user_role"
id = Column(Integer, primary_key=True, index=True)
user_id = Column(Integer, ForeignKey("sys_user.id"), nullable=False)
role_id = Column(Integer, ForeignKey("sys_role.id"), nullable=False)