repair-project-stages-browser.js 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /**
  2. * 项目阶段数据修复脚本(浏览器版本)
  3. *
  4. * 使用方法:
  5. * 1. 打开项目管理页面
  6. * 2. 打开浏览器控制台(F12)
  7. * 3. 复制粘贴此整个脚本
  8. * 4. 输入:repairProjectStages() - 预览需要修复的项目
  9. * 5. 输入:repairProjectStages(false) - 执行实际修复
  10. */
  11. (function() {
  12. 'use strict';
  13. /**
  14. * 修复项目阶段数据
  15. * @param {boolean} dryRun - 是否为预览模式(默认true)
  16. */
  17. window.repairProjectStages = async function(dryRun = true) {
  18. console.log('\n' + '='.repeat(80));
  19. console.log(dryRun ? '🔍 预览模式:查看需要修复的项目' : '🔧 执行模式:开始修复项目阶段数据');
  20. console.log('='.repeat(80) + '\n');
  21. try {
  22. // 获取Parse实例
  23. const Parse = window.Parse;
  24. if (!Parse) {
  25. console.error('❌ 无法找到Parse对象,请确保在正确的页面运行此脚本');
  26. return;
  27. }
  28. // 查询所有项目
  29. const query = new Parse.Query('Project');
  30. query.notEqualTo('isDeleted', true);
  31. query.limit(1000);
  32. const projects = await query.find();
  33. console.log(`📊 共找到 ${projects.length} 个项目\n`);
  34. let needFixCount = 0;
  35. const fixedProjects = [];
  36. for (const project of projects) {
  37. const currentStage = project.get('currentStage') || '订单分配';
  38. const data = project.get('data') || {};
  39. let correctStage = null;
  40. let reasons = [];
  41. // ========== 检查订单分配阶段 ==========
  42. const hasTitle = !!project.get('title')?.trim();
  43. const hasType = !!project.get('projectType');
  44. const hasDemoday = !!project.get('demoday');
  45. const hasQuotation = data.quotation && data.quotation.total > 0;
  46. // 检查设计师分配
  47. const teamQuery = new Parse.Query('ProjectTeam');
  48. teamQuery.equalTo('project', project.toPointer());
  49. teamQuery.notEqualTo('isDeleted', true);
  50. const teams = await teamQuery.find();
  51. const hasDesigner = teams.length > 0;
  52. const isApproved = data.approvalStatus === 'approved';
  53. const orderCompleted = hasTitle && hasType && hasDemoday && hasQuotation && (hasDesigner || isApproved);
  54. if (!orderCompleted) {
  55. correctStage = '订单分配';
  56. if (!hasTitle) reasons.push('缺少项目名称');
  57. if (!hasType) reasons.push('缺少项目类型');
  58. if (!hasDemoday) reasons.push('缺少小图日期');
  59. if (!hasQuotation) reasons.push('缺少报价数据');
  60. if (!hasDesigner && !isApproved) reasons.push('未分配设计师且未审批');
  61. }
  62. // ========== 检查确认需求阶段 ==========
  63. else if (currentStage === '交付执行' || currentStage === '售后归档') {
  64. const hasRequirements = (data.requirementsAnalysis && Object.keys(data.requirementsAnalysis).length > 0) ||
  65. (data.spaceRequirements && Object.keys(data.spaceRequirements).length > 0);
  66. if (!hasRequirements) {
  67. correctStage = '确认需求';
  68. reasons.push('缺少需求分析数据');
  69. }
  70. // ========== 检查交付执行阶段 ==========
  71. else if (currentStage === '售后归档') {
  72. const deliveryStages = data.deliveryStages || {};
  73. const requiredStages = ['modeling', 'softDecor', 'rendering', 'postProcess'];
  74. let allCompleted = true;
  75. const uncompleted = [];
  76. for (const stageId of requiredStages) {
  77. const stageData = deliveryStages[stageId];
  78. if (!stageData || stageData.approvalStatus !== 'approved') {
  79. allCompleted = false;
  80. uncompleted.push(stageId);
  81. }
  82. }
  83. if (!allCompleted) {
  84. correctStage = '交付执行';
  85. reasons.push(`交付阶段未完成: ${uncompleted.join(', ')}`);
  86. }
  87. }
  88. }
  89. // 如果需要修复
  90. if (correctStage && currentStage !== correctStage) {
  91. const title = project.get('title') || '未命名项目';
  92. const reason = reasons.join(', ');
  93. console.log(`\n${'─'.repeat(60)}`);
  94. console.log(`📋 项目: ${title}`);
  95. console.log(` ID: ${project.id}`);
  96. console.log(` 当前阶段: ${currentStage}`);
  97. console.log(` 应该阶段: ${correctStage}`);
  98. console.log(` 回退原因: ${reason}`);
  99. if (!dryRun) {
  100. // 执行修复
  101. project.set('currentStage', correctStage);
  102. // 清除不合理的审批状态
  103. if (correctStage === '订单分配' && data.approvalStatus === 'approved') {
  104. data.approvalStatus = null;
  105. project.set('data', data);
  106. console.log(` 🧹 已清除错误的审批状态`);
  107. }
  108. await project.save();
  109. console.log(` ✅ 已回退到"${correctStage}"阶段`);
  110. } else {
  111. console.log(` 🔍 [预览] 需要回退到"${correctStage}"阶段`);
  112. }
  113. fixedProjects.push({
  114. id: project.id,
  115. title,
  116. oldStage: currentStage,
  117. newStage: correctStage,
  118. reason
  119. });
  120. needFixCount++;
  121. }
  122. }
  123. console.log(`\n${'='.repeat(80)}`);
  124. if (needFixCount === 0) {
  125. console.log('✅ 所有项目的阶段状态都是正确的,无需修复!');
  126. } else {
  127. if (dryRun) {
  128. console.log(`🔍 预览完成!发现 ${needFixCount} 个项目需要修复`);
  129. console.log(`\n💡 提示:输入 repairProjectStages(false) 执行实际修复`);
  130. } else {
  131. console.log(`✅ 修复完成!共回退 ${needFixCount} 个项目到正确阶段`);
  132. }
  133. console.log('\n📋 修复详情:');
  134. console.table(fixedProjects);
  135. }
  136. console.log('='.repeat(80) + '\n');
  137. return fixedProjects;
  138. } catch (error) {
  139. console.error('❌ 操作失败:', error);
  140. throw error;
  141. }
  142. };
  143. /**
  144. * 导出项目数据备份
  145. */
  146. window.exportProjectsBackup = async function() {
  147. console.log('📦 开始导出项目数据...');
  148. try {
  149. const Parse = window.Parse;
  150. if (!Parse) {
  151. console.error('❌ 无法找到Parse对象');
  152. return;
  153. }
  154. const query = new Parse.Query('Project');
  155. query.notEqualTo('isDeleted', true);
  156. query.limit(1000);
  157. const projects = await query.find();
  158. const backup = projects.map(p => ({
  159. id: p.id,
  160. title: p.get('title'),
  161. currentStage: p.get('currentStage'),
  162. projectType: p.get('projectType'),
  163. demoday: p.get('demoday'),
  164. createdAt: p.get('createdAt'),
  165. data: {
  166. quotation: p.get('data')?.quotation,
  167. approvalStatus: p.get('data')?.approvalStatus,
  168. requirementsAnalysis: p.get('data')?.requirementsAnalysis,
  169. deliveryStages: p.get('data')?.deliveryStages
  170. }
  171. }));
  172. console.log('✅ 导出完成,共', projects.length, '个项目');
  173. // 下载为JSON文件
  174. const dataStr = JSON.stringify(backup, null, 2);
  175. const dataBlob = new Blob([dataStr], { type: 'application/json' });
  176. const url = URL.createObjectURL(dataBlob);
  177. const link = document.createElement('a');
  178. link.href = url;
  179. link.download = `projects-backup-${new Date().toISOString().slice(0, 10)}.json`;
  180. link.click();
  181. URL.revokeObjectURL(url);
  182. console.log('💾 备份文件已下载');
  183. return backup;
  184. } catch (error) {
  185. console.error('❌ 导出失败:', error);
  186. throw error;
  187. }
  188. };
  189. // 显示使用说明
  190. console.log('✅ 项目阶段修复工具已加载');
  191. console.log('\n💡 使用方法:');
  192. console.log(' 1. repairProjectStages() - 预览需要修复的项目');
  193. console.log(' 2. repairProjectStages(false) - 执行实际修复');
  194. console.log(' 3. exportProjectsBackup() - 导出备份数据');
  195. console.log('\n⚠️ 建议:先运行 exportProjectsBackup() 备份数据,再执行修复\n');
  196. })();