designer-task.service.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. import { Injectable } from '@angular/core';
  2. import { FmodeParse } from 'fmode-ng/parse';
  3. export interface DesignerTask {
  4. id: string;
  5. projectId: string;
  6. projectName: string;
  7. stage: string;
  8. deadline: Date;
  9. isOverdue: boolean;
  10. priority: 'high' | 'medium' | 'low';
  11. customerName: string;
  12. space?: string; // 空间名称(如"主卧")
  13. productId?: string; // 关联的Product ID
  14. }
  15. @Injectable({
  16. providedIn: 'root'
  17. })
  18. export class DesignerTaskService {
  19. private Parse: any = null;
  20. private cid: string = '';
  21. constructor() {
  22. this.initParse();
  23. }
  24. private async initParse(): Promise<void> {
  25. try {
  26. const { FmodeParse } = await import('fmode-ng/parse');
  27. this.Parse = FmodeParse.with('nova');
  28. this.cid = localStorage.getItem('company') || '';
  29. } catch (error) {
  30. console.error('DesignerTaskService: Parse初始化失败:', error);
  31. }
  32. }
  33. /**
  34. * 获取当前设计师的任务列表
  35. * @param designerId Profile的objectId
  36. */
  37. async getMyTasks(designerId: string): Promise<DesignerTask[]> {
  38. if (!this.Parse) await this.initParse();
  39. if (!this.Parse || !this.cid) {
  40. console.error('❌ Parse 未初始化或缺少 CID');
  41. return [];
  42. }
  43. try {
  44. console.log('🔍 开始查询设计师任务,Profile ID:', designerId);
  45. console.log('📋 当前公司 ID:', this.cid);
  46. // 方案1:从ProjectTeam表查询(设计师实际负责的项目)
  47. const ProfileClass = this.Parse.Object.extend('Profile');
  48. const profilePointer = new ProfileClass();
  49. profilePointer.id = designerId;
  50. const teamQuery = new this.Parse.Query('ProjectTeam');
  51. teamQuery.equalTo('profile', profilePointer);
  52. teamQuery.notEqualTo('isDeleted', true);
  53. teamQuery.include('project');
  54. teamQuery.include('project.contact');
  55. teamQuery.limit(1000);
  56. console.log('🔍 查询 ProjectTeam 表...');
  57. const teamRecords = await teamQuery.find();
  58. console.log(`✅ ProjectTeam 查询结果: ${teamRecords.length} 条记录`);
  59. if (teamRecords.length === 0) {
  60. console.warn('⚠️ ProjectTeam 表中未找到该设计师的项目');
  61. console.log('🔄 尝试降级方案:从 Project.assignee 查询');
  62. return await this.getMyTasksFromAssignee(designerId);
  63. }
  64. console.log('📋 ProjectTeam 记录详情:');
  65. teamRecords.forEach((record: any, index: number) => {
  66. const project = record.get('project');
  67. console.log(` ${index + 1}. 项目:`, {
  68. projectId: project?.id,
  69. projectName: project?.get('title'),
  70. currentStage: project?.get('currentStage'),
  71. status: project?.get('status')
  72. });
  73. });
  74. const tasks: DesignerTask[] = [];
  75. for (const teamRecord of teamRecords) {
  76. try {
  77. const project = teamRecord.get('project');
  78. if (!project) {
  79. console.warn('⚠️ ProjectTeam 记录缺少 project 对象,跳过');
  80. continue;
  81. }
  82. const projectId = project.id;
  83. const projectName = project.get('title') || '未命名项目';
  84. const currentStage = project.get('currentStage') || '未知';
  85. // 安全获取 deadline
  86. let deadline = project.get('deadline') || project.get('deliveryDate') || project.get('expectedDeliveryDate');
  87. if (!deadline) {
  88. deadline = new Date();
  89. console.warn(`⚠️ 项目 ${projectName} 缺少 deadline,使用当前时间`);
  90. }
  91. const contact = project.get('contact');
  92. const customerName = contact?.get('name') || '未知客户';
  93. console.log(`✅ 处理项目: ${projectName} (${projectId})`);
  94. // 查询该项目下该设计师负责的Product(空间设计产品)
  95. let products: any[] = [];
  96. try {
  97. const ProfileClass = this.Parse.Object.extend('Profile');
  98. const profilePointer = new ProfileClass();
  99. profilePointer.id = designerId;
  100. const productQuery = new this.Parse.Query('Product');
  101. productQuery.equalTo('project', project);
  102. productQuery.equalTo('profile', profilePointer);
  103. productQuery.notEqualTo('isDeleted', true);
  104. productQuery.containedIn('status', ['in_progress', 'awaiting_review']);
  105. console.log(`🔍 查询项目 ${projectName} 的 Product...`);
  106. products = await productQuery.find();
  107. console.log(`✅ 找到 ${products.length} 个 Product`);
  108. } catch (productError: any) {
  109. console.warn(`⚠️ Product 查询失败,将创建项目级任务`);
  110. console.warn(`⚠️ Product 错误:`, productError.message || productError.toString());
  111. products = []; // 查询失败时视为没有 Product
  112. }
  113. if (products.length === 0) {
  114. // 如果没有具体的Product,创建项目级任务
  115. console.log(`📝 创建项目级任务: ${projectName}`);
  116. tasks.push({
  117. id: projectId,
  118. projectId,
  119. projectName,
  120. stage: currentStage,
  121. deadline: new Date(deadline),
  122. isOverdue: new Date(deadline) < new Date(),
  123. priority: this.calculatePriority(deadline, currentStage),
  124. customerName
  125. });
  126. } else {
  127. // 如果有Product,为每个Product创建任务
  128. console.log(`📝 为 ${products.length} 个 Product 创建任务`);
  129. products.forEach((product: any) => {
  130. const productName = product.get('productName') || '未命名空间';
  131. const productStage = product.get('stage') || currentStage;
  132. tasks.push({
  133. id: `${projectId}-${product.id}`,
  134. projectId,
  135. projectName: `${projectName} - ${productName}`,
  136. stage: productStage,
  137. deadline: new Date(deadline),
  138. isOverdue: new Date(deadline) < new Date(),
  139. priority: this.calculatePriority(deadline, productStage),
  140. customerName,
  141. space: productName,
  142. productId: product.id
  143. });
  144. });
  145. }
  146. } catch (recordError: any) {
  147. console.error('❌ 处理 ProjectTeam 记录时出错');
  148. console.error('❌ 错误类型:', typeof recordError);
  149. // Parse 错误对象特殊处理
  150. const errorDetails: any = {};
  151. for (const key in recordError) {
  152. if (recordError.hasOwnProperty(key)) {
  153. errorDetails[key] = recordError[key];
  154. }
  155. }
  156. console.error('❌ 错误对象属性:', errorDetails);
  157. console.error('❌ 完整错误:', recordError);
  158. if (recordError.message) console.error('❌ 错误消息:', recordError.message);
  159. if (recordError.code) console.error('❌ Parse错误代码:', recordError.code);
  160. if (recordError.stack) console.error('❌ 错误堆栈:', recordError.stack);
  161. continue;
  162. }
  163. }
  164. // 按截止日期排序
  165. tasks.sort((a, b) => a.deadline.getTime() - b.deadline.getTime());
  166. console.log(`✅ 成功加载 ${tasks.length} 个任务`);
  167. return tasks;
  168. } catch (error: any) {
  169. console.error('❌ 获取设计师任务失败');
  170. console.error('❌ 错误类型:', typeof error);
  171. // Parse 错误对象特殊处理
  172. const errorDetails: any = {};
  173. for (const key in error) {
  174. if (error.hasOwnProperty(key)) {
  175. errorDetails[key] = error[key];
  176. }
  177. }
  178. console.error('❌ 错误对象属性:', errorDetails);
  179. console.error('❌ 完整错误:', error);
  180. if (error.message) console.error('❌ 错误消息:', error.message);
  181. if (error.code) console.error('❌ Parse错误代码:', error.code);
  182. if (error.stack) console.error('❌ 错误堆栈:', error.stack);
  183. return [];
  184. }
  185. }
  186. /**
  187. * 计算任务优先级
  188. */
  189. private calculatePriority(deadline: Date, stage: string): 'high' | 'medium' | 'low' {
  190. const now = new Date();
  191. const daysLeft = Math.ceil((new Date(deadline).getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
  192. // 超期或临期(3天内)
  193. if (daysLeft < 0 || daysLeft <= 3) {
  194. return 'high';
  195. }
  196. // 渲染阶段优先级高
  197. if (stage === 'rendering' || stage === '渲染') {
  198. return 'high';
  199. }
  200. // 7天内
  201. if (daysLeft <= 7) {
  202. return 'medium';
  203. }
  204. return 'low';
  205. }
  206. /**
  207. * 方案2:从Project.assignee查询(降级方案)
  208. */
  209. async getMyTasksFromAssignee(designerId: string): Promise<DesignerTask[]> {
  210. if (!this.Parse) await this.initParse();
  211. if (!this.Parse || !this.cid) {
  212. console.error('❌ [降级方案] Parse 未初始化或缺少 CID');
  213. return [];
  214. }
  215. try {
  216. console.log('🔍 [降级方案] 从 Project.assignee 查询,Profile ID:', designerId);
  217. const ProfileClass = this.Parse.Object.extend('Profile');
  218. const profilePointer = new ProfileClass();
  219. profilePointer.id = designerId;
  220. const query = new this.Parse.Query('Project');
  221. query.equalTo('assignee', profilePointer);
  222. query.equalTo('company', this.cid);
  223. query.containedIn('status', ['进行中', '待审核']);
  224. query.notEqualTo('isDeleted', true);
  225. query.include('contact');
  226. query.ascending('deadline');
  227. query.limit(1000);
  228. console.log('🔍 [降级方案] 查询 Project 表...');
  229. const projects = await query.find();
  230. console.log(`✅ [降级方案] 从Project.assignee加载 ${projects.length} 个项目`);
  231. if (projects.length > 0) {
  232. console.log('📋 [降级方案] 项目详情:');
  233. projects.forEach((project: any, index: number) => {
  234. console.log(` ${index + 1}. 项目:`, {
  235. projectId: project.id,
  236. projectName: project.get('title'),
  237. currentStage: project.get('currentStage'),
  238. status: project.get('status'),
  239. deadline: project.get('deadline')
  240. });
  241. });
  242. } else {
  243. console.warn('⚠️ [降级方案] 也未找到项目');
  244. console.log('💡 提示:请检查 ProjectTeam 或 Project.assignee 字段是否正确设置');
  245. }
  246. return projects.map((project: any) => {
  247. const deadline = project.get('deadline') || project.get('deliveryDate') || project.get('expectedDeliveryDate') || new Date();
  248. const currentStage = project.get('currentStage') || '未知';
  249. const contact = project.get('contact');
  250. return {
  251. id: project.id,
  252. projectId: project.id,
  253. projectName: project.get('title') || '未命名项目',
  254. stage: currentStage,
  255. deadline: new Date(deadline),
  256. isOverdue: new Date(deadline) < new Date(),
  257. priority: this.calculatePriority(deadline, currentStage),
  258. customerName: contact?.get('name') || '未知客户'
  259. };
  260. });
  261. } catch (error: any) {
  262. console.error('❌ [降级方案] 从Project.assignee获取任务失败');
  263. console.error('❌ 错误类型:', typeof error);
  264. // Parse 错误对象特殊处理
  265. const errorDetails: any = {};
  266. for (const key in error) {
  267. if (error.hasOwnProperty(key)) {
  268. errorDetails[key] = error[key];
  269. }
  270. }
  271. console.error('❌ 错误对象属性:', errorDetails);
  272. console.error('❌ 完整错误:', error);
  273. if (error.message) console.error('❌ 错误消息:', error.message);
  274. if (error.code) console.error('❌ Parse错误代码:', error.code);
  275. if (error.stack) console.error('❌ 错误堆栈:', error.stack);
  276. return [];
  277. }
  278. }
  279. }