Files
bttoxin-pipeline/backend/app/models/job.py
zly 9835b6e341 feat(deploy): fix docker deployment and add backend i18n
- Docker Deployment Fixes:
  - Switch base images to docker.m.daocloud.io to resolve registry 401 errors
  - Add Postgres and Redis services to docker-compose.traefik.yml
  - Fix frontend build: replace missing icons (Globe->Location, Chart->TrendCharts)
  - Fix frontend build: resolve pnpm CI/TTY issues and frozen lockfile errors
  - Add missing backend dependencies (sqlalchemy, psycopg2, redis-py, celery, docker-py) in pixi.toml
  - Ensure database tables are created on startup (lifespan event)

- Backend Internationalization (i18n):
  - Add backend/app/core/i18n.py for locale handling
  - Update API endpoints (jobs, tasks, uploads, results) to return localized messages
  - Support 'Accept-Language' header (en/zh)

- Documentation:
  - Update DOCKER_DEPLOYMENT.md with new architecture and troubleshooting
  - Update AGENTS.md with latest stack details and deployment steps
  - Update @fix_plan.md status

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-14 12:38:54 +08:00

93 lines
2.9 KiB
Python

"""任务模型"""
from sqlalchemy import Column, String, Integer, DateTime, JSON, Enum, Text
from sqlalchemy.sql import func
import enum
from ..database import Base
class JobStatus(str, enum.Enum):
"""任务状态"""
PENDING = "pending" # 等待进入队列
QUEUED = "queued" # 已排队,等待执行
RUNNING = "running" # 正在执行
COMPLETED = "completed" # 执行完成
FAILED = "failed" # 执行失败
class StepStatus(str, enum.Enum):
"""步骤状态"""
PENDING = "PENDING"
RUNNING = "RUNNING"
SUCCESS = "SUCCESS"
FAILED = "FAILED"
SKIPPED = "SKIPPED"
class Job(Base):
__tablename__ = "jobs"
id = Column(String, primary_key=True, index=True)
celery_task_id = Column(String, nullable=True, index=True)
status = Column(Enum(JobStatus), default=JobStatus.PENDING, index=True)
input_files = Column(JSON)
sequence_type = Column(String, default="nucl")
scaf_suffix = Column(String, default=".fna")
threads = Column(Integer, default=4)
# 分析参数
min_identity = Column(Integer, default=80) # 存储为百分比 (0-100)
min_coverage = Column(Integer, default=60)
allow_unknown_families = Column(Integer, default=0) # 0 = False, 1 = True
require_index_hit = Column(Integer, default=1)
result_url = Column(String, nullable=True)
logs = Column(Text, nullable=True)
error_message = Column(Text, nullable=True)
# 队列位置
queue_position = Column(Integer, nullable=True)
# 进度信息
current_stage = Column(String, nullable=True) # digger, shoter, plots, bundle
progress_percent = Column(Integer, default=0)
created_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
started_at = Column(DateTime(timezone=True), nullable=True)
completed_at = Column(DateTime(timezone=True), nullable=True)
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
class Step(Base):
"""任务步骤"""
__tablename__ = "job_steps"
id = Column(Integer, primary_key=True, index=True)
job_id = Column(String, index=True) # ForeignKey("jobs.id") - simplified for SQLite/No-Relation
step_id = Column(String) # digger, shoter, etc.
name = Column(String)
status = Column(Enum(StepStatus), default=StepStatus.PENDING)
start_at = Column(DateTime(timezone=True), nullable=True)
end_at = Column(DateTime(timezone=True), nullable=True)
duration_ms = Column(Integer, nullable=True)
summary = Column(Text, nullable=True)
error = Column(Text, nullable=True)
class JobLog(Base):
"""任务日志"""
__tablename__ = "job_logs"
id = Column(Integer, primary_key=True, index=True)
job_id = Column(String, index=True)
step_id = Column(String, nullable=True)
level = Column(String, default="INFO")
message = Column(Text)
timestamp = Column(DateTime(timezone=True), server_default=func.now())
seq = Column(Integer, default=0)