Files
time-tracking-system/backend/models/models.py
bf1942 ef9432f6da feat(time-tracking): 添加个人工时记录系统设计文档
- 完成系统架构和数据模型设计,包括项目、工时记录、休息日和周期表模型
- 设计项目管理模块,支持传统项目与PSI项目管理及批量导入功能
- 规划工时记录模块,含日期、事件描述、项目选择及工时计算规则
- 定义休息日分类,支持周末、国定节假日、个人假期及调休工时管理
- 制定统计分析模块设计,支持按Cut-Off周期的周统计与项目工时分布
- 设计周期管理模块,提供周期设置及预设模板功能
- 制定用户界面布局及各页面表单、样式设计方案
- 规划RESTful API端点,涵盖项目、工时记录、休息日、周期及统计数据操作
- 设计数据流示意,阐明操作流程及前后端交互逻辑
- 制定数据存储方案,包括SQLite数据库配置及备份导出机制
2025-09-04 15:19:35 +08:00

137 lines
5.6 KiB
Python
Raw 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 sqlalchemy import Column, Integer, String, Boolean, DateTime, Text, Date, Time, ForeignKey, Enum
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from datetime import datetime
import enum
Base = declarative_base()
class ProjectType(enum.Enum):
TRADITIONAL = "traditional" # 传统项目
PSI = "psi" # PSI项目
class Project(Base):
"""项目表模型"""
__tablename__ = 'projects'
id = Column(Integer, primary_key=True, autoincrement=True)
project_name = Column(String(200), nullable=False)
project_type = Column(Enum(ProjectType), nullable=False)
# 通用字段
project_code = Column(String(50), nullable=False)
customer_name = Column(String(200), nullable=False)
# PSI项目特有字段
contract_number = Column(String(100)) # 合同号PSI项目必填
description = Column(Text)
is_active = Column(Boolean, default=True)
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# 关联关系
time_records = relationship("TimeRecord", back_populates="project")
def to_dict(self):
return {
'id': self.id,
'project_name': self.project_name,
'project_type': self.project_type.value if self.project_type else None,
'project_code': self.project_code,
'customer_name': self.customer_name,
'contract_number': self.contract_number,
'description': self.description,
'is_active': self.is_active,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class TimeRecord(Base):
"""工时记录表模型"""
__tablename__ = 'time_records'
id = Column(Integer, primary_key=True, autoincrement=True)
date = Column(Date, nullable=False)
event_description = Column(String(500)) # 事件描述
project_id = Column(Integer, ForeignKey('projects.id'))
start_time = Column(Time) # 可为空,支持"-"占位符
end_time = Column(Time) # 可为空,支持"-"占位符
activity_num = Column(String(100)) # Activity Num
hours = Column(String(20)) # 工时,支持"2:42"或"8:00:00"格式
is_holiday = Column(Boolean, default=False) # 是否为休息日
is_working_on_holiday = Column(Boolean, default=False) # 休息日是否工作
holiday_type = Column(String(50)) # 休息日类型
week_info = Column(String(50)) # 周信息
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# 关联关系
project = relationship("Project", back_populates="time_records")
def to_dict(self):
return {
'id': self.id,
'date': self.date.isoformat() if self.date else None,
'event_description': self.event_description,
'project_id': self.project_id,
'start_time': self.start_time.strftime('%H:%M') if self.start_time else None,
'end_time': self.end_time.strftime('%H:%M') if self.end_time else None,
'activity_num': self.activity_num,
'hours': self.hours,
'is_holiday': self.is_holiday,
'is_working_on_holiday': self.is_working_on_holiday,
'holiday_type': self.holiday_type,
'week_info': self.week_info,
'project': self.project.to_dict() if self.project else None,
'created_at': self.created_at.isoformat() if self.created_at else None,
'updated_at': self.updated_at.isoformat() if self.updated_at else None
}
class Holiday(Base):
"""休息日配置表模型"""
__tablename__ = 'holidays'
id = Column(Integer, primary_key=True, autoincrement=True)
date = Column(Date, nullable=False, unique=True)
holiday_name = Column(String(100)) # 节假日名称
holiday_type = Column(String(50), nullable=False) # weekend/national_holiday/personal_leave/makeup_day
is_working_day = Column(Boolean, default=False) # 调休工作日标记
created_at = Column(DateTime, default=datetime.utcnow)
def to_dict(self):
return {
'id': self.id,
'date': self.date.isoformat() if self.date else None,
'holiday_name': self.holiday_name,
'holiday_type': self.holiday_type,
'is_working_day': self.is_working_day,
'created_at': self.created_at.isoformat() if self.created_at else None
}
class CutoffPeriod(Base):
"""Cut-Off周期表模型"""
__tablename__ = 'cutoff_periods'
id = Column(Integer, primary_key=True, autoincrement=True)
period_name = Column(String(50), nullable=False)
start_date = Column(Date, nullable=False)
end_date = Column(Date, nullable=False)
target_hours = Column(Integer, default=160)
weeks = Column(Integer, default=4)
year = Column(Integer, nullable=False)
month = Column(Integer, nullable=False)
created_at = Column(DateTime, default=datetime.utcnow)
def to_dict(self):
return {
'id': self.id,
'period_name': self.period_name,
'start_date': self.start_date.isoformat() if self.start_date else None,
'end_date': self.end_date.isoformat() if self.end_date else None,
'target_hours': self.target_hours,
'weeks': self.weeks,
'year': self.year,
'month': self.month,
'created_at': self.created_at.isoformat() if self.created_at else None
}