/** * 项目阶段数据修复脚本 * * 功能:将没有完成阶段工作的项目回退到正确的阶段 * * 使用方法: * 1. 在浏览器控制台粘贴此脚本 * 2. 调用 repairProjectStages() 预览需要修复的项目 * 3. 调用 repairProjectStages(false) 执行实际修复 */ import { FmodeParse } from 'fmode-ng/core'; const Parse = FmodeParse.with('nova'); /** * 修复项目阶段数据 * @param dryRun 是否为预览模式(默认true,只预览不修复) */ export async function repairProjectStages(dryRun: boolean = true): Promise { console.log('\n' + '='.repeat(80)); console.log(dryRun ? '🔍 预览模式:查看需要修复的项目' : '🔧 执行模式:开始修复项目阶段数据'); console.log('='.repeat(80) + '\n'); try { // 查询所有未删除的项目 const query = new Parse.Query('Project'); query.notEqualTo('isDeleted', true); query.limit(1000); const projects = await query.find(); console.log(`📊 共找到 ${projects.length} 个项目\n`); let needFixCount = 0; const fixedProjects: Array<{ id: string; title: string; oldStage: string; newStage: string; reason: string; }> = []; for (const project of projects) { const currentStage = project.get('currentStage') || '订单分配'; const correctStage = await getCorrectStage(project); // 如果当前阶段与正确阶段不一致 if (currentStage !== correctStage) { const title = project.get('title') || '未命名项目'; const reason = await getFixReason(project, correctStage); console.log(`\n${'─'.repeat(60)}`); console.log(`📋 项目: ${title}`); console.log(` ID: ${project.id}`); console.log(` 当前阶段: ${currentStage}`); console.log(` 应该阶段: ${correctStage}`); console.log(` 回退原因: ${reason}`); if (!dryRun) { // 执行修复 project.set('currentStage', correctStage); // 清除不合理的审批状态 const data = project.get('data') || {}; if (correctStage === '订单分配' && data.approvalStatus === 'approved') { const isCompleted = await isOrderStageCompleted(project); if (!isCompleted) { data.approvalStatus = null; project.set('data', data); console.log(` 🧹 已清除错误的审批状态`); } } await project.save(); console.log(` ✅ 已回退到"${correctStage}"阶段`); } else { console.log(` 🔍 [预览] 需要回退到"${correctStage}"阶段`); } fixedProjects.push({ id: project.id || 'unknown', title, oldStage: currentStage, newStage: correctStage, reason }); needFixCount++; } } console.log(`\n${'='.repeat(80)}`); if (needFixCount === 0) { console.log('✅ 所有项目的阶段状态都是正确的,无需修复!'); } else { if (dryRun) { console.log(`🔍 预览完成!发现 ${needFixCount} 个项目需要修复`); console.log(`\n💡 提示:调用 repairProjectStages(false) 执行实际修复`); } else { console.log(`✅ 修复完成!共回退 ${needFixCount} 个项目到正确阶段`); } console.log('\n📋 修复详情:'); console.table(fixedProjects); } console.log('='.repeat(80) + '\n'); } catch (error) { console.error('❌ 操作失败:', error); throw error; } } /** * 获取项目应该处于的正确阶段 */ async function getCorrectStage(project: any): Promise { // 检查订单分配阶段是否完成 const orderCompleted = await isOrderStageCompleted(project); if (!orderCompleted) { return '订单分配'; } // 检查确认需求阶段是否完成 const requirementsCompleted = await isRequirementsStageCompleted(project); if (!requirementsCompleted) { return '确认需求'; } // 检查交付执行阶段是否完成 const deliveryCompleted = await isDeliveryStageCompleted(project); if (!deliveryCompleted) { return '交付执行'; } // 所有阶段都完成了,应该在售后归档 return '售后归档'; } /** * 获取修复原因说明 */ async function getFixReason(project: any, correctStage: string): Promise { const reasons: string[] = []; if (correctStage === '订单分配') { if (!project.get('title')?.trim()) reasons.push('缺少项目名称'); if (!project.get('projectType')) reasons.push('缺少项目类型'); if (!project.get('demoday')) reasons.push('缺少小图日期'); const data = project.get('data') || {}; if (!data.quotation || data.quotation.total <= 0) reasons.push('缺少报价数据'); const query = new Parse.Query('ProjectTeam'); query.equalTo('project', project.toPointer()); query.notEqualTo('isDeleted', true); const teams = await query.find(); if (teams.length === 0 && data.approvalStatus !== 'approved') { reasons.push('未分配设计师且未审批'); } } if (correctStage === '确认需求') { const data = project.get('data') || {}; if (!data.requirementsAnalysis || Object.keys(data.requirementsAnalysis).length === 0) { if (!data.spaceRequirements || Object.keys(data.spaceRequirements).length === 0) { reasons.push('缺少需求分析数据'); } } } if (correctStage === '交付执行') { const data = project.get('data') || {}; const deliveryStages = data.deliveryStages || {}; const requiredStages = [ { id: 'modeling', name: '建模' }, { id: 'softDecor', name: '软装' }, { id: 'rendering', name: '渲染' }, { id: 'postProcess', name: '后期' } ]; for (const stage of requiredStages) { if (!deliveryStages[stage.id] || deliveryStages[stage.id].approvalStatus !== 'approved') { reasons.push(`${stage.name}阶段未审批`); } } } return reasons.length > 0 ? reasons.join(', ') : '未知原因'; } /** * 检查订单分配阶段是否完成 */ async function isOrderStageCompleted(project: any): Promise { // 1. 检查基本信息 if (!project.get('title')?.trim()) return false; if (!project.get('projectType')) return false; if (!project.get('demoday')) return false; // 2. 检查报价 const data = project.get('data') || {}; if (!data.quotation || data.quotation.total <= 0) return false; // 3. 检查设计师分配或审批状态 const query = new Parse.Query('ProjectTeam'); query.equalTo('project', project.toPointer()); query.notEqualTo('isDeleted', true); const teams = await query.find(); const hasDesigner = teams.length > 0; const isApproved = data.approvalStatus === 'approved'; // 至少满足一个条件:已分配设计师 或 已审批通过 return hasDesigner || isApproved; } /** * 检查确认需求阶段是否完成 */ async function isRequirementsStageCompleted(project: any): Promise { const data = project.get('data') || {}; // 检查是否有需求分析数据 if (data.requirementsAnalysis && Object.keys(data.requirementsAnalysis).length > 0) { return true; } // 检查是否有空间需求数据 if (data.spaceRequirements && Object.keys(data.spaceRequirements).length > 0) { return true; } // 如果没有任何需求数据,视为未完成 return false; } /** * 检查交付执行阶段是否完成 */ async function isDeliveryStageCompleted(project: any): Promise { const data = project.get('data') || {}; const deliveryStages = data.deliveryStages || {}; // 检查所有必需的交付阶段 const requiredStages = ['modeling', 'softDecor', 'rendering', 'postProcess']; for (const stageId of requiredStages) { const stageData = deliveryStages[stageId]; // 如果某个阶段不存在或未审批通过,则交付执行未完成 if (!stageData || stageData.approvalStatus !== 'approved') { return false; } } // 所有阶段都已审批通过 return true; } /** * 导出项目数据(用于备份) */ export async function exportProjectsForBackup(): Promise { console.log('📦 开始导出项目数据...'); const query = new Parse.Query('Project'); query.notEqualTo('isDeleted', true); query.limit(1000); const projects = await query.find(); const backup = projects.map(p => ({ id: p.id, title: p.get('title'), currentStage: p.get('currentStage'), projectType: p.get('projectType'), demoday: p.get('demoday'), data: p.get('data') })); console.log('✅ 导出完成,共', projects.length, '个项目'); console.log('📋 备份数据:', JSON.stringify(backup, null, 2)); // 下载为JSON文件 const dataStr = JSON.stringify(backup, null, 2); const dataBlob = new Blob([dataStr], { type: 'application/json' }); const url = URL.createObjectURL(dataBlob); const link = document.createElement('a'); link.href = url; link.download = `projects-backup-${new Date().toISOString()}.json`; link.click(); console.log('💾 备份文件已下载'); } // 导出全局方法(用于浏览器控制台) if (typeof window !== 'undefined') { (window as any).repairProjectStages = repairProjectStages; (window as any).exportProjectsForBackup = exportProjectsForBackup; console.log('✅ 数据修复工具已加载'); console.log('💡 使用方法:'); console.log(' 1. repairProjectStages() - 预览需要修复的项目'); console.log(' 2. repairProjectStages(false) - 执行实际修复'); console.log(' 3. exportProjectsForBackup() - 导出备份数据'); }