test-project-complete.service.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  1. import { Injectable } from '@angular/core';
  2. import { FmodeParse } from 'fmode-ng/parse';
  3. import { ProjectToCaseService } from './project-to-case.service';
  4. const Parse = FmodeParse.with('nova');
  5. /**
  6. * 测试项目完成服务
  7. * 用于将"10.28 测试"项目推进到售后归档阶段并验证案例创建
  8. */
  9. @Injectable({
  10. providedIn: 'root'
  11. })
  12. export class TestProjectCompleteService {
  13. private companyId = localStorage.getItem("company")!;
  14. constructor(private projectToCaseService: ProjectToCaseService) {}
  15. /**
  16. * 完成测试项目 - 将"10.28 测试"项目推进到售后归档
  17. */
  18. async completeTestProject(): Promise<{
  19. success: boolean;
  20. projectId?: string;
  21. caseId?: string;
  22. message: string;
  23. }> {
  24. try {
  25. console.log('🚀 开始处理测试项目...');
  26. // 1. 查找"10.28 测试"项目
  27. const project = await this.findTestProject();
  28. if (!project) {
  29. return {
  30. success: false,
  31. message: '未找到"10.28 测试"或"10.28 测试项目"'
  32. };
  33. }
  34. const projectId = project.id;
  35. const projectTitle = project.get('title');
  36. console.log(`✅ 找到项目: ${projectTitle} (${projectId})`);
  37. // 2. 填充项目必要信息
  38. await this.fillProjectData(project);
  39. console.log('✅ 项目数据已填充');
  40. // 3. 创建/更新产品数据
  41. await this.createProducts(project);
  42. console.log('✅ 产品数据已创建');
  43. // 4. 更新项目阶段为"售后归档"
  44. project.set('currentStage', '售后归档');
  45. project.set('stage', '售后归档');
  46. project.set('status', '已完成');
  47. await project.save();
  48. console.log('✅ 项目阶段已更新为: 售后归档');
  49. // 5. 触发自动创建案例
  50. console.log('📦 触发案例自动创建...');
  51. await this.projectToCaseService.onProjectStageChanged(projectId, '售后归档');
  52. // 6. 验证案例是否创建
  53. const caseObj = await this.findProjectCase(projectId);
  54. if (caseObj) {
  55. console.log(`✅ 案例创建成功! 案例ID: ${caseObj.id}`);
  56. console.log(`📋 案例名称: ${caseObj.get('name')}`);
  57. console.log(`🎨 封面图片: ${caseObj.get('coverImage') ? '已设置' : '未设置'}`);
  58. console.log(`📸 图片数量: ${(caseObj.get('images') || []).length}`);
  59. console.log(`💰 项目总额: ${caseObj.get('totalPrice')}`);
  60. console.log(`🏷️ 标签: ${(caseObj.get('tag') || []).join(', ')}`);
  61. return {
  62. success: true,
  63. projectId: projectId,
  64. caseId: caseObj.id,
  65. message: `测试成功!项目"${projectTitle}"已完成售后归档,案例已自动创建 (案例ID: ${caseObj.id})`
  66. };
  67. } else {
  68. return {
  69. success: false,
  70. projectId: projectId,
  71. message: '项目已更新为售后归档,但案例创建失败或需要稍后查看'
  72. };
  73. }
  74. } catch (error: any) {
  75. console.error('❌ 测试失败:', error);
  76. return {
  77. success: false,
  78. message: `测试失败: ${error.message || error}`
  79. };
  80. }
  81. }
  82. /**
  83. * 查找测试项目
  84. */
  85. private async findTestProject(): Promise<any> {
  86. const query = new Parse.Query('Project');
  87. query.equalTo('company', this.getCompanyPointer());
  88. // 尝试查找"10.28 测试"或"10.28 测试项目"
  89. const titleQuery1 = new Parse.Query('Project');
  90. titleQuery1.equalTo('company', this.getCompanyPointer());
  91. titleQuery1.equalTo('title', '10.28 测试');
  92. const titleQuery2 = new Parse.Query('Project');
  93. titleQuery2.equalTo('company', this.getCompanyPointer());
  94. titleQuery2.equalTo('title', '10.28 测试项目');
  95. const combinedQuery = Parse.Query.or(titleQuery1, titleQuery2);
  96. combinedQuery.descending('createdAt');
  97. try {
  98. const projects = await combinedQuery.find();
  99. return projects.length > 0 ? projects[0] : null;
  100. } catch (error) {
  101. console.error('查找项目失败:', error);
  102. return null;
  103. }
  104. }
  105. /**
  106. * 填充项目数据
  107. */
  108. private async fillProjectData(project: any): Promise<void> {
  109. const projectData = project.get('data') || {};
  110. // 填充基础信息
  111. if (!projectData.area) {
  112. projectData.area = 120; // 120平米
  113. }
  114. if (!projectData.projectType) {
  115. projectData.projectType = '家装';
  116. }
  117. if (!projectData.roomType) {
  118. projectData.roomType = '三居室';
  119. }
  120. if (!projectData.spaceType) {
  121. projectData.spaceType = '平层';
  122. }
  123. if (!projectData.renderingLevel) {
  124. projectData.renderingLevel = '高端';
  125. }
  126. // 填充预算信息
  127. if (!projectData.budget) {
  128. projectData.budget = {
  129. total: 350000,
  130. designFee: 50000,
  131. constructionFee: 200000,
  132. softDecorFee: 100000
  133. };
  134. }
  135. // 填充时间线
  136. if (!projectData.timeline) {
  137. projectData.timeline = {
  138. startDate: '2024-10-01',
  139. completionDate: '2024-10-28',
  140. duration: 27,
  141. milestones: [
  142. { stage: '订单分配', date: '2024-10-01', status: 'completed' },
  143. { stage: '需求沟通', date: '2024-10-05', status: 'completed' },
  144. { stage: '方案确认', date: '2024-10-10', status: 'completed' },
  145. { stage: '建模', date: '2024-10-15', status: 'completed' },
  146. { stage: '渲染', date: '2024-10-25', status: 'completed' },
  147. { stage: '售后归档', date: '2024-10-28', status: 'in_progress' }
  148. ]
  149. };
  150. }
  151. // 填充设计亮点
  152. if (!projectData.highlights) {
  153. projectData.highlights = [
  154. '现代简约风格设计',
  155. '智能家居系统集成',
  156. '开放式厨房与客厅设计',
  157. '采用环保材料',
  158. '充分利用自然采光'
  159. ];
  160. }
  161. // 填充设计特色
  162. if (!projectData.features) {
  163. projectData.features = [
  164. '流畅的空间动线',
  165. '中性色调搭配',
  166. '隐藏式储物空间',
  167. '多功能家具设计'
  168. ];
  169. }
  170. // 填充材料信息
  171. if (!projectData.materials) {
  172. projectData.materials = {
  173. floor: '实木复合地板',
  174. wall: '乳胶漆+硅藻泥',
  175. ceiling: '石膏板吊顶',
  176. door: '实木复合门',
  177. window: '断桥铝合金窗'
  178. };
  179. }
  180. // 填充客户信息
  181. if (!projectData.clientInfo) {
  182. projectData.clientInfo = {
  183. familyMembers: 4,
  184. ageRange: '30-45岁',
  185. lifestyle: '现代都市家庭',
  186. specialNeeds: ['儿童房设计', '老人房无障碍设计'],
  187. satisfactionScore: 95
  188. };
  189. }
  190. // 填充标签
  191. if (!projectData.tags || projectData.tags.length === 0) {
  192. projectData.tags = ['现代简约', '北欧风', '家装', '三居室'];
  193. }
  194. // 保存数据
  195. project.set('data', projectData);
  196. // 确保项目有必要的关联字段
  197. if (!project.get('totalAmount') && !project.get('orderAmount')) {
  198. project.set('totalAmount', 350000);
  199. }
  200. // 确保有设计师和团队
  201. if (!project.get('assignee') && !project.get('designer')) {
  202. // 查找一个设计师
  203. const designerQuery = new Parse.Query('Profile');
  204. designerQuery.equalTo('company', this.getCompanyPointer());
  205. designerQuery.equalTo('identyType', 'designer');
  206. designerQuery.limit(1);
  207. try {
  208. const designers = await designerQuery.find();
  209. if (designers.length > 0) {
  210. project.set('assignee', designers[0]);
  211. project.set('designer', designers[0]);
  212. }
  213. } catch (error) {
  214. console.warn('未找到设计师,使用默认值');
  215. }
  216. }
  217. if (!project.get('team') && !project.get('department')) {
  218. // 查找一个团队
  219. const teamQuery = new Parse.Query('Department');
  220. teamQuery.equalTo('company', this.getCompanyPointer());
  221. teamQuery.limit(1);
  222. try {
  223. const teams = await teamQuery.find();
  224. if (teams.length > 0) {
  225. project.set('team', teams[0]);
  226. project.set('department', teams[0]);
  227. }
  228. } catch (error) {
  229. console.warn('未找到团队,使用默认值');
  230. }
  231. }
  232. await project.save();
  233. }
  234. /**
  235. * 创建产品数据
  236. */
  237. private async createProducts(project: any): Promise<void> {
  238. // 查询已有产品
  239. const existingQuery = new Parse.Query('Product');
  240. existingQuery.equalTo('project', project);
  241. const existingProducts = await existingQuery.find();
  242. // 如果已有产品,跳过
  243. if (existingProducts.length > 0) {
  244. console.log(`已有 ${existingProducts.length} 个产品,跳过创建`);
  245. return;
  246. }
  247. // 创建示例产品
  248. const products = [
  249. {
  250. name: '主卧效果图',
  251. spaceArea: '主卧',
  252. stage: '渲染',
  253. status: 'completed',
  254. description: '现代简约风格主卧设计',
  255. images: [
  256. 'https://via.placeholder.com/800x600/667eea/ffffff?text=Master+Bedroom+1',
  257. 'https://via.placeholder.com/800x600/764ba2/ffffff?text=Master+Bedroom+2'
  258. ]
  259. },
  260. {
  261. name: '客厅效果图',
  262. spaceArea: '客厅',
  263. stage: '渲染',
  264. status: 'completed',
  265. description: '开放式客厅与餐厅设计',
  266. images: [
  267. 'https://via.placeholder.com/800x600/f093fb/ffffff?text=Living+Room+1',
  268. 'https://via.placeholder.com/800x600/4facfe/ffffff?text=Living+Room+2'
  269. ]
  270. },
  271. {
  272. name: '厨房效果图',
  273. spaceArea: '厨房',
  274. stage: '渲染',
  275. status: 'completed',
  276. description: '现代化开放式厨房',
  277. images: [
  278. 'https://via.placeholder.com/800x600/43e97b/ffffff?text=Kitchen'
  279. ]
  280. }
  281. ];
  282. const ProductClass = Parse.Object.extend('Product');
  283. for (const productData of products) {
  284. const product = new ProductClass();
  285. product.set('company', this.getCompanyPointer());
  286. product.set('project', project);
  287. product.set('name', productData.name);
  288. product.set('spaceArea', productData.spaceArea);
  289. product.set('stage', productData.stage);
  290. product.set('status', productData.status);
  291. product.set('description', productData.description);
  292. // 创建附件
  293. const attachments = [];
  294. for (const imageUrl of productData.images) {
  295. const AttachmentClass = Parse.Object.extend('Attachment');
  296. const attachment = new AttachmentClass();
  297. attachment.set('url', imageUrl);
  298. attachment.set('type', 'image');
  299. attachment.set('name', `${productData.spaceArea}_${Date.now()}.jpg`);
  300. await attachment.save();
  301. attachments.push(attachment);
  302. }
  303. product.set('attachments', attachments);
  304. await product.save();
  305. }
  306. console.log(`✅ 创建了 ${products.length} 个产品`);
  307. }
  308. /**
  309. * 查找项目关联的案例
  310. */
  311. private async findProjectCase(projectId: string): Promise<any> {
  312. // 等待2秒让案例创建完成
  313. await new Promise(resolve => setTimeout(resolve, 2000));
  314. const query = new Parse.Query('Case');
  315. query.equalTo('company', this.getCompanyPointer());
  316. query.equalTo('project', {
  317. __type: 'Pointer',
  318. className: 'Project',
  319. objectId: projectId
  320. });
  321. query.notEqualTo('isDeleted', true);
  322. query.descending('createdAt');
  323. try {
  324. const cases = await query.find();
  325. return cases.length > 0 ? cases[0] : null;
  326. } catch (error) {
  327. console.error('查找案例失败:', error);
  328. return null;
  329. }
  330. }
  331. /**
  332. * 获取公司指针
  333. */
  334. private getCompanyPointer(): any {
  335. return {
  336. __type: 'Pointer',
  337. className: 'Company',
  338. objectId: this.companyId
  339. };
  340. }
  341. }