副业时间管理工具 - 全栈开发实践
1. 实际应用场景描述
本工具面向程序员、设计师、咨询师、自媒体创作者等有副业需求的职场人士,提供智能化的时间管理和任务规划服务。在当前经济环境下,越来越多人希望通过副业增加收入,但如何平衡主业和副业的时间分配成为一个普遍难题。
典型使用场景:
- 程序员接私活:周末开发小程序,工作日晚上处理bug修复
- 设计师兼职:晚上进行logo设计,周末完成整套VI方案
- 咨询师副业:下班后进行线上咨询,周末安排深度访谈
- 自媒体运营:利用碎片时间写文章,集中时间录制视频
- 电商创业者:白天上班,晚上处理订单和客户咨询
- 翻译工作者:通勤路上翻译短文本,晚上处理长篇文档
用户画像分析:
- 25-40岁为主,有一定专业技能和工作经验
- 主业相对稳定,但希望增加收入来源
- 时间管理能力较强,但需要工具辅助规划
- 对副业收入有明确预期,注重投入产出比
- 担心副业影响主业,需要风险预警机制
2. 引入痛点分析
2.1 现有解决方案的不足
1. 工具割裂:日程管理、任务跟踪、时间统计分散在不同应用中
2. 缺乏统筹:无法综合考虑主业约束和副业任务的优先级
3. 动态调整困难:突发主业事务时,副业计划难以快速重新安排
4. 风险评估缺失:无法预警副业过度投入的风险
5. 收益分析薄弱:缺乏对副业投入产出比的量化分析
2.2 市场机会洞察
- 中国灵活就业人员已超过2亿人,副业市场需求巨大
- 疫情加速了远程工作和副业经济的发展
- 年轻人更愿意尝试多元化收入来源
- 企业对员工副业的态度逐渐开放,但需要规范管理
3. 核心逻辑深度解析
3.1 系统架构设计
graph TB
A[用户界面层] --> B[业务逻辑层]
B --> C[数据分析层]
C --> D[数据存储层]
E[时间追踪服务] --> B
F[任务调度引擎] --> B
G[风险评估模块] --> B
H[收益计算器] --> B
subgraph "核心技术栈"
A(Vue.js + Vuetify)
B(Python FastAPI + Celery)
C(Pandas + NumPy + SciPy)
D(PostgreSQL + Redis)
end
subgraph "外部集成"
I[日历同步]
J[项目管理工具]
K[财务软件]
L[时间追踪设备]
end
3.2 核心算法逻辑
3.2.1 智能时间分配算法
def generate_optimal_schedule(main_job_constraints: MainJobConstraints,
side_projects: List[SideProject],
preferences: UserPreferences) -> ScheduleResult:
"""
生成最优副业时间表
基于约束满足和遗传算法的混合优化
"""
# 1. 数据预处理和约束分析
constraints = analyze_constraints(main_job_constraints, side_projects, preferences)
# 2. 任务优先级排序
prioritized_projects = prioritize_projects(side_projects, constraints)
# 3. 时间槽分配
schedule_slots = allocate_time_slots(constraints, prioritized_projects)
# 4. 冲突检测和解决
resolved_schedule = resolve_conflicts(schedule_slots, constraints)
# 5. 风险评估和优化
risk_assessed_schedule = assess_and_mitigate_risks(resolved_schedule, constraints)
return ScheduleResult(
weekly_schedule=risk_assessed_schedule,
utilization_rate=calculate_utilization_rate(risk_assessed_schedule),
risk_level=evaluate_overall_risk(risk_assessed_schedule),
recommendations=generate_recommendations(risk_assessed_schedule, constraints)
)
def analyze_constraints(main_job: MainJobConstraints,
projects: List[SideProject],
preferences: UserPreferences) -> ConstraintAnalysis:
"""分析所有约束条件"""
analysis = ConstraintAnalysis()
# 主业时间约束
analysis.main_job_work_hours = main_job.daily_work_hours
analysis.main_job_commute_time = main_job.commute_time
analysis.main_job_meeting_blocks = main_job.fixed_meetings
analysis.main_job_deadlines = main_job.upcoming_deadlines
# 个人约束
analysis.sleep_hours_needed = preferences.sleep_hours
analysis.leisure_time_required = preferences.leisure_time
analysis.health_considerations = preferences.health_constraints
analysis.family_commitments = preferences.family_time
# 计算可用时间
analysis.available_weekday_hours = calculate_available_weekday_hours(analysis)
analysis.available_weekend_hours = calculate_available_weekend_hours(analysis)
# 项目约束
analysis.total_side_project_hours = sum(p.estimated_hours for p in projects)
analysis.project_deadlines = [p.deadline for p in projects]
analysis.project_dependencies = extract_dependencies(projects)
return analysis
def prioritize_projects(projects: List[SideProject],
constraints: ConstraintAnalysis) -> List[PrioritizedProject]:
"""多维度项目优先级排序"""
scored_projects = []
for project in projects:
score = calculate_project_score(project, constraints)
scored_projects.append(PrioritizedProject(project=project, priority_score=score))
# 按优先级排序
return sorted(scored_projects, key=lambda x: x.priority_score, reverse=True)
def calculate_project_score(project: SideProject,
constraints: ConstraintAnalysis) -> float:
"""计算项目综合优先级分数"""
factors = {
'urgency': calculate_urgency_score(project.deadline, constraints),
'profitability': calculate_profitability_score(project.income, project.hours_estimate),
'strategic_value': calculate_strategic_value_score(project.skill_development),
'client_importance': calculate_client_importance_score(project.client_type),
'dependency_impact': calculate_dependency_score(project, constraints.project_dependencies)
}
# 权重配置(可根据用户偏好调整)
weights = {
'urgency': 0.25,
'profitability': 0.30,
'strategic_value': 0.20,
'client_importance': 0.15,
'dependency_impact': 0.10
}
total_score = sum(factors[factor] * weights[factor] for factor in factors)
return min(100.0, max(0.0, total_score))
def allocate_time_slots(constraints: ConstraintAnalysis,
prioritized_projects: List[PrioritizedProject]) -> List[TimeSlot]:
"""智能时间槽分配算法"""
slots = []
current_date = date.today()
# 按周循环分配
for week_offset in range(4): # 规划未来4周
week_start = current_date + timedelta(weeks=week_offset)
# 分配工作日晚上时间
weekday_slots = allocate_weekday_evenings(constraints, prioritized_projects, week_start)
slots.extend(weekday_slots)
# 分配周末时间
weekend_slots = allocate_weekend_time(constraints, prioritized_projects, week_start)
slots.extend(weekend_slots)
return slots
def allocate_weekday_evenings(constraints: ConstraintAnalysis,
projects: List[PrioritizedProject],
week_start: date) -> List[TimeSlot]:
"""分配工作日晚上时间"""
slots = []
for day_offset in range(5): # 周一到周五
current_date = week_start + timedelta(days=day_offset)
# 检查是否有主业会议冲突
if has_main_job_conflict(current_date, constraints.main_job_meeting_blocks):
continue
# 计算可用晚上时间
available_start = constraints.main_job_work_hours + constraints.main_job_commute_time + timedelta(hours=1)
available_end = datetime.combine(current_date, datetime.min.time()) + timedelta(hours=23)
# 扣除必要的休息和用餐时间
available_end -= timedelta(hours=1) # 睡前准备时间
available_duration = (available_end - available_start).total_seconds() / 3600
if available_duration >= 1: # 至少1小时才分配
# 选择最适合的项目
selected_project = select_best_fit_project(projects, available_duration, 'weekday_evening')
if selected_project:
slot = TimeSlot(
date=current_date,
start_time=available_start.time(),
end_time=(available_start + timedelta(hours=min(selected_project.project.estimated_hours_per_session, available_duration))).time(),
project_id=selected_project.project.id,
slot_type='weekday_evening',
energy_level='medium' # 晚上精力中等
)
slots.append(slot)
return slots
def assess_and_mitigate_risks(schedule: List[TimeSlot],
constraints: ConstraintAnalysis) -> RiskMitigatedSchedule:
"""风险评估和风险缓解"""
risk_assessment = assess_schedule_risks(schedule, constraints)
mitigated_schedule = schedule.copy()
# 高风险项目的风险缓解措施
for risk in risk_assessment.high_risks:
if risk.risk_type == 'main_job_conflict':
mitigated_schedule = mitigate_main_job_conflict(mitigated_schedule, risk)
elif risk.risk_type == 'burnout_risk':
mitigated_schedule = mitigate_burnout_risk(mitigated_schedule, risk)
elif risk.risk_type == 'deadline_risk':
mitigated_schedule = mitigate_deadline_risk(mitigated_schedule, risk)
return RiskMitigatedSchedule(
schedule=mitigated_schedule,
remaining_risks=risk_assessment.medium_risks + risk_assessment.low_risks,
mitigation_actions=risk_assessment.mitigation_actions
)
3.2.2 动态调度算法
class DynamicScheduler:
"""动态调度器"""
def __init__(self):
self.event_listeners = []
async def handle_main_job_change(self, change_event: MainJobChangeEvent) -> ScheduleUpdate:
"""处理主业变化事件"""
# 1. 评估影响范围
affected_slots = self.identify_affected_slots(change_event)
# 2. 重新计算可用时间
updated_constraints = self.recalculate_constraints(change_event)
# 3. 重新分配受影响的时间段
rescheduled_slots = await self.reschedule_affected_slots(affected_slots, updated_constraints)
# 4. 通知相关方
await self.notify_schedule_changes(rescheduled_slots)
return ScheduleUpdate(
original_slots=affected_slots,
rescheduled_slots=rescheduled_slots,
impact_analysis=self.analyze_impact(rescheduled_slots),
recommendations=self.generate_rescheduling_recommendations(rescheduled_slots)
)
def identify_affective_slots(self, change_event: MainJobChangeEvent) -> List[TimeSlot]:
"""识别受影响的时段"""
affected_slots = []
if change_event.change_type == 'meeting_added':
# 新增会议可能影响晚上的副业时间
meeting_date = change_event.new_meeting.start_time.date()
affected_slots = [
slot for slot in self.current_schedule
if slot.date == meeting_date and slot.slot_type == 'weekday_evening'
]
elif change_event.change_type == 'work_extension':
# 加班延长影响后续时间
overtime_date = change_event.overtime_date
affected_slots = [
slot for slot in self.current_schedule
if slot.date >= overtime_date
]
elif change_event.change_type == 'emergency_task':
# 紧急任务可能需要取消部分副业安排
emergency_date = change_event.emergency_date
affected_slots = [
slot for slot in self.current_schedule
if slot.date == emergency_date
]
return affected_slots
async def reschedule_affected_slots(self, affected_slots: List[TimeSlot],
updated_constraints: ConstraintAnalysis) -> List[TimeSlot]:
"""重新安排受影响的时间段"""
rescheduled = []
for slot in affected_slots:
# 尝试在同一天找到替代时间段
alternative_slot = self.find_alternative_slot_same_day(slot, updated_constraints)
if alternative_slot:
rescheduled.append(alternative_slot)
else:
# 尝试在其他天安排
alternative_slot = await self.find_alternative_slot_other_day(slot, updated_constraints)
if alternative_slot:
rescheduled.append(alternative_slot)
else:
# 无法重新安排,标记为取消
self.cancel_slot_with_compensation(slot)
return rescheduled
def find_alternative_slot_same_day(self, original_slot: TimeSlot,
constraints: ConstraintAnalysis) -> Optional[TimeSlot]:
"""在同一天寻找替代时间段"""
project = self.get_project_by_id(original_slot.project_id)
# 检查早上时间(如果可行)
morning_slot = self.create_morning_slot(original_slot, project, constraints)
if morning_slot and self.is_slot_available(morning_slot):
return morning_slot
# 检查深夜时间(谨慎使用)
late_night_slot = self.create_late_night_slot(original_slot, project, constraints)
if late_night_slot and self.is_slot_available(late_night_slot):
return late_night_slot
return None
async def find_alternative_slot_other_day(self, original_slot: TimeSlot,
constraints: ConstraintAnalysis) -> Optional[TimeSlot]:
"""在其他天寻找替代时间段"""
project = self.get_project_by_id(original_slot.project_id)
# 优先在周末寻找
weekend_slots = self.find_available_weekend_slots(project, constraints, lookahead_days=14)
if weekend_slots:
return weekend_slots[0]
# 然后在工作日寻找
weekday_slots = self.find_available_weekday_slots(project, constraints, lookahead_days=7)
if weekday_slots:
return weekday_slots[0]
return None
3.3 数据流设计
sequenceDiagram
participant U as 用户
participant UI as 前端界面
participant API as 后端API
participant Scheduler as 调度引擎
participant DB as 数据库
participant Notifier as 通知服务
U->>UI: 录入副业任务
UI->>API: 保存任务信息
API->>DB: 存储任务数据
DB-->>API: 确认存储
API->>Scheduler: 触发重新调度
Scheduler->>DB: 获取主业约束
Scheduler->>Scheduler: 计算最优时间表
Scheduler-->>API: 返回调度结果
API-->>UI: 显示新的时间表
UI-->>U: 展示更新后的计划
Note over Scheduler,Notifier: 实时监控和通知
Scheduler->>Scheduler: 检测时间冲突
Scheduler->>Notifier: 发送预警通知
Notifier->>U: 推送通知提醒
4. 模块化实现
4.1 领域模型层
# core/domain/models.py
"""
副业时间管理领域模型
定义核心业务实体和值对象
"""
from dataclasses import dataclass, field
from datetime import datetime, date, time, timedelta
from enum import Enum
from typing import List, Optional, Dict, Set, Union
import uuid
from decimal import Decimal
from pydantic import BaseModel, validator, Field
import math
class ProjectStatus(Enum):
"""项目状态"""
PENDING = "pending" # 待开始
IN_PROGRESS = "in_progress" # 进行中
ON_HOLD = "on_hold" # 暂停
COMPLETED = "completed" # 已完成
CANCELLED = "cancelled" # 已取消
class ProjectPriority(Enum):
"""项目优先级"""
LOW = "low" # 低
MEDIUM = "medium" # 中
HIGH = "high" # 高
URGENT = "urgent" # 紧急
class TimeSlotType(Enum):
"""时间槽类型"""
WEEKDAY_EVENING = "weekday_evening" # 工作日晚上
WEEKEND_MORNING = "weekend_morning" # 周末上午
WEEKEND_AFTERNOON = "weekend_afternoon" # 周末下午
WEEKEND_EVENING = "weekend_evening" # 周末晚上
FLEXIBLE = "flexible" # 灵活时间
class ClientType(Enum):
"""客户类型"""
INDIVIDUAL = "individual" # 个人客户
SMALL_BUSINESS = "small_business" # 小企业
ENTERPRISE = "enterprise" # 大企业
AGENCY = "agency" # 代理公司
PLATFORM = "platform" # 平台派单
class WorkIntensity(Enum):
"""工作强度"""
LIGHT = "light" # 轻松
MODERATE = "moderate" # 适中
INTENSE = "intense" # 高强度
EXTREME = "extreme" # 极限
@dataclass
class TimeSlot:
"""时间槽实体"""
slot_id: str = field(default_factory=lambda: str(uuid.uuid4()))
project_id: str = ""
date: date = field(default_factory=date.today)
start_time: time = time(19, 0) # 默认晚上7点开始
end_time: time = time(21, 0) # 默认晚上9点结束
slot_type: TimeSlotType = TimeSlotType.WEEKDAY_EVENING
energy_level: str = "medium" # low, medium, high
is_confirmed: bool = False
is_completed: bool = False
actual_start_time: Optional[datetime] = None
actual_end_time: Optional[datetime] = None
notes: str = ""
def __post_init__(self):
"""初始化后处理"""
if self.end_time <= self.start_time:
raise ValueError("结束时间必须晚于开始时间")
@property
def duration_hours(self) -> float:
"""计算时间槽持续时间(小时)"""
start_datetime = datetime.combine(self.date, self.start_time)
end_datetime = datetime.combine(self.date, self.end_time)
return (end_datetime - start_datetime).total_seconds() / 3600
@property
def is_past(self) -> bool:
"""检查时间槽是否已过去"""
now = datetime.now()
slot_start = datetime.combine(self.date, self.start_time)
return slot_start < now
@property
def is_today(self) -> bool:
"""检查是否为今天的时间槽"""
return self.date == date.today()
def overlaps_with(self, other: 'TimeSlot') -> bool:
"""检查是否与另一个时间槽重叠"""
if self.date != other.date:
return False
self_start = datetime.combine(self.date, self.start_time)
self_end = datetime.combine(self.date, self.end_time)
other_start = datetime.combine(other.date, other.start_time)
other_end = datetime.combine(other.date, other.end_time)
return (self_start < other_end) and (other_start < self_end)
def to_calendar_event(self) -> Dict[str, Any]:
"""转换为日历事件格式"""
return {
'id': self.slot_id,
'title': f'副业工作 - {self.project_id}',
'start': datetime.combine(self.date, self.start_time).isoformat(),
'end': datetime.combine(self.date, self.end_time).isoformat(),
'type': self.slot_type.value,
'energy_level': self.energy_level
}
@dataclass
class SideProject:
"""副业项目实体"""
project_id: str = field(default_factory=lambda: str(uuid.uuid4()))
name: str = ""
description: str = ""
client_name: str = ""
client_type: ClientType = ClientType.INDIVIDUAL
status: ProjectStatus = ProjectStatus.PENDING
priority: ProjectPriority = ProjectPriority.MEDIUM
# 时间相关信息
estimated_total_hours: int = 0
estimated_hours_per_session: int = 2
deadline: Optional[date] = None
start_date: Optional[date] = None
actual_start_date: Optional[date] = None
# 财务信息
budget: Decimal = Decimal('0')
hourly_rate: Decimal = Decimal('0')
total_earned: Decimal = Decimal('0')
payment_terms: str = "50%预付,50%完成付"
# 技能和发展
required_skills: List[str] = field(default_factory=list)
skill_development_value: int = 1 # 1-5,技能发展价值
portfolio_impact: int = 1 # 1-5,作品集影响
networking_value: int = 1 # 1-5,人脉价值
# 项目属性
is_recurring: bool = False
recurrence_pattern: str = "" # 如 "weekly", "monthly"
complexity_level: int = 1 # 1-5,复杂度
risk_level: int = 1 # 1-5,风险等级
# 时间管理
scheduled_slots: List[TimeSlot] = field(default_factory=list)
completed_hours: int = 0
remaining_hours: int = 0
is_on_track: bool = True
delay_reason: str = ""
def __post_init__(self):
"""初始化后处理"""
self.remaining_hours = self.estimated_total_hours - self.completed_hours
self.update_status_based_on_progress()
def update_status_based_on_progress(self):
"""根据进度更新状态"""
if self.completed_hours >= self.estimated_total_hours:
if self.status != ProjectStatus.COMPLETED:
self.status = ProjectStatus.COMPLETED
self.actual_end_date = date.today()
elif self.completed_hours > 0:
if self.status == ProjectStatus.PENDING:
self.status = ProjectStatus.IN_PROGRESS
if not self.actual_start_date:
self.actual_start_date = date.today()
def add_scheduled_slot(self, slot: TimeSlot):
"""添加计划时间槽"""
if slot.project_id != self.project_id:
slot.project_id = self.project_id
self.scheduled_slots.append(slot)
self.remaining_hours = max(0, self.remaining_hours - int(slot.duration_hours))
self.update_status_based_on_progress()
def mark_slot_completed(self, slot_id: str, actual_start: datetime = None,
actual_end: datetime = None):
"""标记时间槽完成"""
for slot in self.scheduled_slots:
if slot.slot_id == slot_id:
slot.is_completed = True
slot.actual_start_time = actual_start
slot.actual_end_time = actual_end
break
# 重新计算已完成小时数
self.completed_hours = sum(int(slot.duration_hours) for slot in self.scheduled_slots if slot.is_completed)
self.remaining_hours = max(0, self.estimated_total_hours - self.completed_hours)
self.update_status_based_on_progress()
def calculate_profitab
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!