/** * 项目阶段截止时间工具函数 */ /** * 默认工期配置(天数) */ const DEFAULT_PHASE_DURATIONS = { modeling: 1, // 建模默认1天 softDecor: 1, // 软装默认1天 rendering: 1, // 渲染默认1天 postProcessing: 1 // 后期默认1天 }; /** * 生成项目阶段截止时间 * @param {Date} projectStartDate - 项目开始日期(可选) * @param {Date} projectDeadline - 项目截止日期(必填) * @param {Object} customDurations - 自定义工期(可选) * @returns {Object} phaseDeadlines对象 */ function generatePhaseDeadlines(projectStartDate, projectDeadline, customDurations = {}) { if (!projectDeadline) { throw new Error("项目截止日期(projectDeadline)是必填参数"); } // 合并默认工期和自定义工期 const durations = { ...DEFAULT_PHASE_DURATIONS, ...customDurations }; const deadlineTime = projectDeadline.getTime(); // 从后往前计算各阶段截止时间 const postProcessingDeadline = new Date(deadlineTime); const renderingDeadline = new Date(deadlineTime - durations.postProcessing * 24 * 60 * 60 * 1000); const softDecorDeadline = new Date(deadlineTime - (durations.postProcessing + durations.rendering) * 24 * 60 * 60 * 1000); const modelingDeadline = new Date(deadlineTime - (durations.postProcessing + durations.rendering + durations.softDecor) * 24 * 60 * 60 * 1000); // 如果提供了开始日期,使用它作为建模阶段的开始时间 const modelingStartDate = projectStartDate || new Date(deadlineTime - (durations.modeling + durations.softDecor + durations.rendering + durations.postProcessing) * 24 * 60 * 60 * 1000); // 构建阶段截止时间对象 return { modeling: { startDate: modelingStartDate, deadline: modelingDeadline, estimatedDays: durations.modeling, status: "in_progress", // 默认第一个阶段为进行中 priority: "medium" }, softDecor: { startDate: modelingDeadline, deadline: softDecorDeadline, estimatedDays: durations.softDecor, status: "not_started", priority: "medium" }, rendering: { startDate: softDecorDeadline, deadline: renderingDeadline, estimatedDays: durations.rendering, status: "not_started", priority: "medium" }, postProcessing: { startDate: renderingDeadline, deadline: postProcessingDeadline, estimatedDays: durations.postProcessing, status: "not_started", priority: "medium" } }; } /** * 从公司配置获取工期设置 * @param {String} companyId - 公司ID * @returns {Promise} 工期配置 */ async function getCompanyPhaseDurations(companyId) { try { const companyQuery = new Parse.Query("Company"); const company = await companyQuery.get(companyId, { useMasterKey: true }); const companyData = company.get("data") || {}; return companyData.phaseDefaultDurations || DEFAULT_PHASE_DURATIONS; } catch (error) { console.warn(`获取公司${companyId}的工期配置失败,使用默认配置:`, error.message); return DEFAULT_PHASE_DURATIONS; } } /** * 更新阶段状态 * @param {String} projectId - 项目ID * @param {String} phaseName - 阶段名称 (modeling/softDecor/rendering/postProcessing) * @param {String} status - 新状态 (not_started/in_progress/completed/delayed) * @param {Object} additionalData - 额外数据(如completedAt) * @returns {Promise} 更新后的项目对象 */ async function updatePhaseStatus(projectId, phaseName, status, additionalData = {}) { const validPhases = ['modeling', 'softDecor', 'rendering', 'postProcessing']; const validStatuses = ['not_started', 'in_progress', 'completed', 'delayed']; if (!validPhases.includes(phaseName)) { throw new Error(`无效的阶段名称: ${phaseName}`); } if (!validStatuses.includes(status)) { throw new Error(`无效的状态: ${status}`); } const projectQuery = new Parse.Query("Project"); const project = await projectQuery.get(projectId, { useMasterKey: true }); const data = project.get("data") || {}; const phaseDeadlines = data.phaseDeadlines || {}; if (!phaseDeadlines[phaseName]) { throw new Error(`项目${projectId}没有${phaseName}阶段信息`); } // 更新阶段状态 phaseDeadlines[phaseName].status = status; // 如果是完成状态,记录完成时间 if (status === 'completed' && !phaseDeadlines[phaseName].completedAt) { phaseDeadlines[phaseName].completedAt = new Date(); } // 合并额外数据 Object.assign(phaseDeadlines[phaseName], additionalData); data.phaseDeadlines = phaseDeadlines; project.set("data", data); await project.save(null, { useMasterKey: true }); return project; } /** * 获取项目当前阶段 * @param {Object} phaseDeadlines - 阶段截止时间对象 * @returns {String} 当前阶段名称 */ function getCurrentPhase(phaseDeadlines) { if (!phaseDeadlines) { return null; } const phases = ['modeling', 'softDecor', 'rendering', 'postProcessing']; // 找到第一个未完成的阶段 for (const phase of phases) { const phaseInfo = phaseDeadlines[phase]; if (phaseInfo && phaseInfo.status !== 'completed') { return phase; } } // 所有阶段都完成了,返回最后一个阶段 return 'postProcessing'; } /** * 检查阶段是否延期 * @param {Object} phaseInfo - 阶段信息 * @returns {Boolean} 是否延期 */ function isPhaseDelayed(phaseInfo) { if (!phaseInfo || !phaseInfo.deadline) { return false; } // 已完成的阶段不算延期 if (phaseInfo.status === 'completed') { return false; } const deadline = new Date(phaseInfo.deadline); const now = new Date(); return now > deadline; } /** * 获取阶段剩余天数 * @param {Object} phaseInfo - 阶段信息 * @returns {Number} 剩余天数(负数表示已逾期) */ function getPhaseDaysRemaining(phaseInfo) { if (!phaseInfo || !phaseInfo.deadline) { return 0; } const deadline = new Date(phaseInfo.deadline); const now = new Date(); const diff = deadline.getTime() - now.getTime(); return Math.ceil(diff / (24 * 60 * 60 * 1000)); } // 导出函数 module.exports = { DEFAULT_PHASE_DURATIONS, generatePhaseDeadlines, getCompanyPhaseDurations, updatePhaseStatus, getCurrentPhase, isPhaseDelayed, getPhaseDaysRemaining }; // Parse Cloud Code函数注册 Parse.Cloud.define("generateProjectPhaseDeadlines", async (request) => { const { projectStartDate, projectDeadline, companyId, customDurations } = request.params; if (!projectDeadline) { throw new Error("缺少projectDeadline参数"); } // 如果提供了companyId,获取公司的默认工期配置 let durations = customDurations || {}; if (companyId) { const companyDurations = await getCompanyPhaseDurations(companyId); durations = { ...companyDurations, ...customDurations }; } const startDate = projectStartDate ? new Date(projectStartDate) : null; const deadline = new Date(projectDeadline); return generatePhaseDeadlines(startDate, deadline, durations); }); Parse.Cloud.define("updateProjectPhaseStatus", async (request) => { const { projectId, phaseName, status, additionalData } = request.params; if (!projectId) { throw new Error("缺少projectId参数"); } if (!phaseName) { throw new Error("缺少phaseName参数"); } if (!status) { throw new Error("缺少status参数"); } return await updatePhaseStatus(projectId, phaseName, status, additionalData); });