You've already forked DataMate
feat: 实现任务拆分和分配功能
## 功能概述 实现完整的任务拆分、分配和进度跟踪功能,支持将任务拆分为子任务并分配给不同用户。 ## Phase 1: 数据库层 - 新增 t_task_meta 表(任务元数据协调表) - 新增 t_task_assignment_log 表(分配日志表) - 新增 3 个权限条目(read/write/assign) - 新增 SQLAlchemy ORM 模型 ## Phase 2: 后端 API (Java) - 新增 task-coordination-service 模块(32 个文件) - 实现 11 个 API 端点: - 任务查询(列表、子任务、我的任务) - 任务拆分(支持 4 种策略) - 任务分配(单个、批量、重新分配、撤回) - 进度管理(查询、更新、聚合) - 分配日志 - 集成权限控制和路由规则 ## Phase 3: 前端 UI (React + TypeScript) - 新增 10 个文件(模型、API、组件、页面) - 实现 5 个核心组件: - SplitTaskDialog - 任务拆分对话框 - AssignTaskDialog - 任务分配对话框 - BatchAssignDialog - 批量分配对话框 - TaskProgressPanel - 进度面板 - AssignmentLogDrawer - 分配记录 - 实现 2 个页面: - TaskCoordination - 任务管理主页 - MyTasks - 我的任务页面 - 集成侧边栏菜单和路由 ## 问题修复 - 修复 getMyTasks 分页参数缺失 - 修复子任务 assignee 信息缺失(批量查询优化) - 修复 proportion 精度计算(余量分配) ## 技术亮点 - 零侵入设计:通过独立协调表实现,不修改现有模块 - 批量查询优化:避免 N+1 查询问题 - 4 种拆分策略:按比例/数量/文件/手动 - 进度自动聚合:子任务更新自动聚合到父任务 - 权限细粒度控制:read/write/assign 三级权限 ## 验证 - Maven 编译:✅ 零错误 - TypeScript 编译:✅ 零错误 - Vite 生产构建:✅ 成功
This commit is contained in:
@@ -23,6 +23,11 @@ from .data_evaluation import (
|
||||
EvaluationItem
|
||||
)
|
||||
|
||||
from .task_coordination import (
|
||||
TaskMeta,
|
||||
TaskAssignmentLog
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"Dataset",
|
||||
"DatasetTag",
|
||||
@@ -36,4 +41,6 @@ __all__ = [
|
||||
"LabelingProjectFile",
|
||||
"EvaluationTask",
|
||||
"EvaluationItem",
|
||||
"TaskMeta",
|
||||
"TaskAssignmentLog",
|
||||
]
|
||||
|
||||
220
runtime/datamate-python/app/db/models/task_coordination.py
Normal file
220
runtime/datamate-python/app/db/models/task_coordination.py
Normal file
@@ -0,0 +1,220 @@
|
||||
"""Tables of Task Coordination Module (任务拆分与分配)"""
|
||||
|
||||
import uuid
|
||||
from sqlalchemy import (
|
||||
Column,
|
||||
String,
|
||||
Integer,
|
||||
BigInteger,
|
||||
TIMESTAMP,
|
||||
Text,
|
||||
JSON,
|
||||
ForeignKey,
|
||||
Index,
|
||||
)
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
from app.db.session import Base
|
||||
|
||||
|
||||
# ── 任务元状态常量 ──────────────────────────────────────────
|
||||
TASK_META_STATUS_PENDING = "PENDING"
|
||||
TASK_META_STATUS_IN_PROGRESS = "IN_PROGRESS"
|
||||
TASK_META_STATUS_COMPLETED = "COMPLETED"
|
||||
TASK_META_STATUS_FAILED = "FAILED"
|
||||
TASK_META_STATUS_STOPPED = "STOPPED"
|
||||
TASK_META_STATUS_CANCELLED = "CANCELLED"
|
||||
|
||||
TASK_META_STATUS_VALUES = {
|
||||
TASK_META_STATUS_PENDING,
|
||||
TASK_META_STATUS_IN_PROGRESS,
|
||||
TASK_META_STATUS_COMPLETED,
|
||||
TASK_META_STATUS_FAILED,
|
||||
TASK_META_STATUS_STOPPED,
|
||||
TASK_META_STATUS_CANCELLED,
|
||||
}
|
||||
|
||||
# ── 所属模块常量 ────────────────────────────────────────────
|
||||
MODULE_ANNOTATION = "ANNOTATION"
|
||||
MODULE_CLEANING = "CLEANING"
|
||||
MODULE_EVALUATION = "EVALUATION"
|
||||
MODULE_SYNTHESIS = "SYNTHESIS"
|
||||
MODULE_COLLECTION = "COLLECTION"
|
||||
MODULE_RATIO = "RATIO"
|
||||
|
||||
MODULE_VALUES = {
|
||||
MODULE_ANNOTATION,
|
||||
MODULE_CLEANING,
|
||||
MODULE_EVALUATION,
|
||||
MODULE_SYNTHESIS,
|
||||
MODULE_COLLECTION,
|
||||
MODULE_RATIO,
|
||||
}
|
||||
|
||||
# ── 拆分策略常量 ────────────────────────────────────────────
|
||||
SPLIT_STRATEGY_BY_COUNT = "BY_COUNT"
|
||||
SPLIT_STRATEGY_BY_FILE = "BY_FILE"
|
||||
SPLIT_STRATEGY_BY_PERCENTAGE = "BY_PERCENTAGE"
|
||||
SPLIT_STRATEGY_MANUAL = "MANUAL"
|
||||
|
||||
# ── 分配操作常量 ────────────────────────────────────────────
|
||||
ASSIGNMENT_ACTION_ASSIGN = "ASSIGN"
|
||||
ASSIGNMENT_ACTION_REASSIGN = "REASSIGN"
|
||||
ASSIGNMENT_ACTION_REVOKE = "REVOKE"
|
||||
|
||||
|
||||
class TaskMeta(Base):
|
||||
"""任务拆分与分配元数据表"""
|
||||
|
||||
__tablename__ = "t_task_meta"
|
||||
|
||||
id = Column(
|
||||
String(36),
|
||||
primary_key=True,
|
||||
default=lambda: str(uuid.uuid4()),
|
||||
comment="任务元ID (UUID)",
|
||||
)
|
||||
parent_id = Column(
|
||||
String(36),
|
||||
ForeignKey("t_task_meta.id", ondelete="CASCADE"),
|
||||
nullable=True,
|
||||
comment="父任务ID,顶层任务为NULL",
|
||||
)
|
||||
module = Column(
|
||||
String(50),
|
||||
nullable=False,
|
||||
comment="所属模块: ANNOTATION/CLEANING/EVALUATION/SYNTHESIS/COLLECTION/RATIO",
|
||||
)
|
||||
ref_task_id = Column(
|
||||
String(64),
|
||||
nullable=False,
|
||||
comment="关联的业务任务ID(各模块自己的task表ID)",
|
||||
)
|
||||
task_name = Column(String(255), nullable=False, comment="任务名称")
|
||||
status = Column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
default=TASK_META_STATUS_PENDING,
|
||||
comment="状态: PENDING/IN_PROGRESS/COMPLETED/FAILED/STOPPED/CANCELLED",
|
||||
)
|
||||
assigned_to = Column(
|
||||
BigInteger,
|
||||
ForeignKey("users.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
comment="分配给的用户ID (users.id)",
|
||||
)
|
||||
created_by = Column(String(255), nullable=False, comment="创建者用户名")
|
||||
|
||||
# ── 进度字段 ──
|
||||
progress = Column(
|
||||
Integer, nullable=False, default=0, comment="进度百分比 0-100"
|
||||
)
|
||||
total_items = Column(
|
||||
Integer, nullable=False, default=0, comment="总条目数"
|
||||
)
|
||||
completed_items = Column(
|
||||
Integer, nullable=False, default=0, comment="已完成条目数"
|
||||
)
|
||||
failed_items = Column(
|
||||
Integer, nullable=False, default=0, comment="失败条目数"
|
||||
)
|
||||
|
||||
# ── 拆分配置 ──
|
||||
split_strategy = Column(
|
||||
String(50),
|
||||
nullable=True,
|
||||
comment="拆分策略: BY_COUNT/BY_FILE/BY_PERCENTAGE/MANUAL",
|
||||
)
|
||||
split_config = Column(JSON, nullable=True, comment="拆分配置参数")
|
||||
|
||||
# ── 调度 ──
|
||||
priority = Column(
|
||||
Integer, nullable=False, default=0, comment="优先级 0=普通 1=高 2=紧急"
|
||||
)
|
||||
deadline = Column(TIMESTAMP, nullable=True, comment="截止时间")
|
||||
|
||||
# ── 时间戳 ──
|
||||
started_at = Column(TIMESTAMP, nullable=True, comment="开始时间")
|
||||
completed_at = Column(TIMESTAMP, nullable=True, comment="完成时间")
|
||||
created_at = Column(
|
||||
TIMESTAMP,
|
||||
server_default=func.current_timestamp(),
|
||||
comment="创建时间",
|
||||
)
|
||||
updated_at = Column(
|
||||
TIMESTAMP,
|
||||
server_default=func.current_timestamp(),
|
||||
onupdate=func.current_timestamp(),
|
||||
comment="更新时间",
|
||||
)
|
||||
deleted_at = Column(TIMESTAMP, nullable=True, comment="删除时间(软删除)")
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_task_meta_parent_id", "parent_id"),
|
||||
Index("idx_task_meta_module_ref", "module", "ref_task_id"),
|
||||
Index("idx_task_meta_assigned_to", "assigned_to"),
|
||||
Index("idx_task_meta_status", "status"),
|
||||
Index("idx_task_meta_created_by", "created_by"),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"<TaskMeta(id={self.id}, task_name={self.task_name}, status={self.status})>"
|
||||
|
||||
@property
|
||||
def is_deleted(self) -> bool:
|
||||
"""检查是否已被软删除"""
|
||||
return self.deleted_at is not None
|
||||
|
||||
@property
|
||||
def is_parent(self) -> bool:
|
||||
"""是否为父任务(顶层任务)"""
|
||||
return self.parent_id is None
|
||||
|
||||
|
||||
class TaskAssignmentLog(Base):
|
||||
"""任务分配操作日志表"""
|
||||
|
||||
__tablename__ = "t_task_assignment_log"
|
||||
|
||||
id = Column(
|
||||
String(36),
|
||||
primary_key=True,
|
||||
default=lambda: str(uuid.uuid4()),
|
||||
comment="日志ID (UUID)",
|
||||
)
|
||||
task_meta_id = Column(
|
||||
String(36),
|
||||
ForeignKey("t_task_meta.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
comment="任务元ID",
|
||||
)
|
||||
action = Column(
|
||||
String(20),
|
||||
nullable=False,
|
||||
comment="操作类型: ASSIGN/REASSIGN/REVOKE",
|
||||
)
|
||||
from_user_id = Column(
|
||||
BigInteger, nullable=True, comment="原分配用户ID"
|
||||
)
|
||||
to_user_id = Column(
|
||||
BigInteger, nullable=True, comment="新分配用户ID"
|
||||
)
|
||||
operated_by = Column(
|
||||
String(255), nullable=False, comment="操作者用户名"
|
||||
)
|
||||
remark = Column(String(500), nullable=True, comment="备注")
|
||||
created_at = Column(
|
||||
TIMESTAMP,
|
||||
server_default=func.current_timestamp(),
|
||||
comment="创建时间",
|
||||
)
|
||||
|
||||
__table_args__ = (
|
||||
Index("idx_assignment_log_task_meta_id", "task_meta_id"),
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<TaskAssignmentLog(id={self.id}, task_meta_id={self.task_meta_id}, "
|
||||
f"action={self.action})>"
|
||||
)
|
||||
Reference in New Issue
Block a user