const { formatDate } = require('./util') /** * Build the per-day target array for a plan from its formula fields. * * target(day) = startTarget + floor((day - 1) / cycleDays) * increment * * Examples: * { totalDays: 7, startTarget: 30, increment: 10, cycleDays: 1 } * → 30, 40, 50, 60, 70, 80, 90 (preset: 初级) * { totalDays: 14, startTarget: 60, increment: 15, cycleDays: 2 } * → 60, 60, 75, 75, 90, 90, ... (preset: 中级) * { totalDays: 30, startTarget: 90, increment: 15, cycleDays: 3 } * → 90, 90, 90, 105, 105, 105, ... (preset: 高级) */ const generatePlanDays = ({ totalDays, startTarget, increment, cycleDays }) => { const n = Math.max(0, Math.floor(totalDays) || 0) const start = Math.max(0, Math.floor(startTarget) || 0) const inc = Math.max(0, Math.floor(increment) || 0) const cycle = Math.max(1, Math.floor(cycleDays) || 1) const days = [] for (let i = 0; i < n; i++) { const d = i + 1 days.push({ day: d, target: start + Math.floor((d - 1) / cycle) * inc }) } return days } const _describe = (plan) => `${plan.totalDays}天计划 · ${plan.startTarget}秒起步` const _buildPlan = (base) => { const plan = { ...base } plan.days = generatePlanDays(plan) plan.description = _describe(plan) return plan } const plans = { beginner: _buildPlan({ id: 'beginner', name: '初级 (7天)', totalDays: 7, startTarget: 30, increment: 10, cycleDays: 1 }), intermediate: _buildPlan({ id: 'intermediate', name: '中级 (14天)', totalDays: 14, startTarget: 60, increment: 15, cycleDays: 2 }), advanced: _buildPlan({ id: 'advanced', name: '高级 (30天)', totalDays: 30, startTarget: 90, increment: 15, cycleDays: 3 }) } /** * Look up a plan by id. User-customized plans (passed in `customPlans`) * shadow the preset with the same id, so e.g. editing "beginner" replaces * the preset beginner for the user without changing the planId. */ const getPlan = (planId, customPlans) => { if (customPlans && customPlans[planId]) return customPlans[planId] return plans[planId] || plans.beginner } const getTodayTarget = (planId, currentDay) => { const plan = getPlan(planId) const day = plan.days.find(d => d.day === currentDay) return day ? day.target : plan.days[0].target } /** * Calculate the current plan day based on training records since plan start. * Only counts records from the current plan's start date onward. */ const getPlanDay = (planId, records, planStartDate) => { const plan = getPlan(planId) const today = formatDate(new Date()) // Filter records: same plan, after plan start date, and not today const eligibleRecords = records.filter(r => { if (r.planId !== planId) return false if (r.date === today) return false if (planStartDate && r.date < planStartDate) return false return true }) const trainedDays = new Set(eligibleRecords.map(r => r.date)).size return Math.min(trainedDays + 1, plan.totalDays) } module.exports = { plans, generatePlanDays, getPlan, getTodayTarget, getPlanDay }