delivery-message.service.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import { Injectable } from '@angular/core';
  2. import { FmodeParse, FmodeObject } from 'fmode-ng/parse';
  3. import { WxworkSDKService } from '../../../modules/project/services/wxwork-sdk.service';
  4. const Parse = FmodeParse.with('nova');
  5. /**
  6. * 消息类型
  7. */
  8. export type MessageType = 'text' | 'image' | 'text_with_image';
  9. /**
  10. * 交付消息接口
  11. */
  12. export interface DeliveryMessage {
  13. id?: string;
  14. project: string; // 项目ID
  15. stage: string; // 阶段 (white_model/soft_decor/rendering/post_process)
  16. messageType: MessageType; // 消息类型
  17. content: string; // 文本内容
  18. images?: string[]; // 图片URL数组
  19. sentBy: string; // 发送人ID
  20. sentByName?: string; // 发送人姓名
  21. sentAt: Date; // 发送时间
  22. }
  23. /**
  24. * 预设话术模板
  25. */
  26. export const MESSAGE_TEMPLATES = {
  27. white_model: [
  28. '老师我这里硬装模型做好了,看下是否有问题,如果没有,我去做渲染',
  29. '老师,白模阶段完成,请查看确认',
  30. '硬装结构已完成,请审阅'
  31. ],
  32. soft_decor: [
  33. '软装好了,准备渲染,有问题可以留言',
  34. '老师,软装设计已完成,请查看',
  35. '家具配置完成,准备进入渲染阶段'
  36. ],
  37. rendering: [
  38. '老师,渲染图已完成,请查看效果',
  39. '效果图已出,请审阅',
  40. '渲染完成,请查看是否需要调整'
  41. ],
  42. post_process: [
  43. '老师,后期处理完成,请验收',
  44. '最终成品已完成,请查看',
  45. '所有修图完成,请确认'
  46. ]
  47. };
  48. /**
  49. * 交付消息服务
  50. */
  51. @Injectable({
  52. providedIn: 'root'
  53. })
  54. export class DeliveryMessageService {
  55. constructor(private wxworkService: WxworkSDKService) {}
  56. /**
  57. * 获取阶段预设话术
  58. */
  59. getStageTemplates(stage: string): string[] {
  60. return MESSAGE_TEMPLATES[stage as keyof typeof MESSAGE_TEMPLATES] || [];
  61. }
  62. /**
  63. * 创建文本消息
  64. */
  65. async createTextMessage(
  66. projectId: string,
  67. stage: string,
  68. content: string,
  69. currentUser: FmodeObject
  70. ): Promise<DeliveryMessage> {
  71. const message: DeliveryMessage = {
  72. project: projectId,
  73. stage,
  74. messageType: 'text',
  75. content,
  76. sentBy: currentUser.id!,
  77. sentByName: currentUser.get('name') || '未知用户',
  78. sentAt: new Date()
  79. };
  80. // 🔥 保存到数据库
  81. await this.saveMessageToProject(projectId, message);
  82. // 🔥 发送到企业微信
  83. await this.sendToWxwork(content, []);
  84. return message;
  85. }
  86. /**
  87. * 创建图片消息
  88. */
  89. async createImageMessage(
  90. projectId: string,
  91. stage: string,
  92. imageUrls: string[],
  93. content: string = '',
  94. currentUser: FmodeObject
  95. ): Promise<DeliveryMessage> {
  96. const message: DeliveryMessage = {
  97. project: projectId,
  98. stage,
  99. messageType: content ? 'text_with_image' : 'image',
  100. content,
  101. images: imageUrls,
  102. sentBy: currentUser.id!,
  103. sentByName: currentUser.get('name') || '未知用户',
  104. sentAt: new Date()
  105. };
  106. // 🔥 保存到数据库
  107. await this.saveMessageToProject(projectId, message);
  108. // 🔥 发送到企业微信
  109. await this.sendToWxwork(content, imageUrls);
  110. return message;
  111. }
  112. /**
  113. * 保存消息到项目data字段
  114. */
  115. private async saveMessageToProject(projectId: string, message: DeliveryMessage): Promise<void> {
  116. try {
  117. const query = new Parse.Query('Project');
  118. const project = await query.get(projectId);
  119. const data = project.get('data') || {};
  120. const messages = data.deliveryMessages || [];
  121. // 生成消息ID
  122. message.id = `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  123. messages.push(message);
  124. data.deliveryMessages = messages;
  125. project.set('data', data);
  126. await project.save();
  127. console.log('✅ 消息已保存到项目:', message.id);
  128. } catch (error) {
  129. console.error('❌ 保存消息失败:', error);
  130. throw error;
  131. }
  132. }
  133. /**
  134. * 获取项目的所有消息
  135. */
  136. async getProjectMessages(projectId: string): Promise<DeliveryMessage[]> {
  137. try {
  138. const query = new Parse.Query('Project');
  139. const project = await query.get(projectId);
  140. const data = project.get('data') || {};
  141. return data.deliveryMessages || [];
  142. } catch (error) {
  143. console.error('❌ 获取消息失败:', error);
  144. return [];
  145. }
  146. }
  147. /**
  148. * 获取特定阶段的消息
  149. */
  150. async getStageMessages(projectId: string, stage: string): Promise<DeliveryMessage[]> {
  151. const allMessages = await this.getProjectMessages(projectId);
  152. return allMessages.filter(msg => msg.stage === stage);
  153. }
  154. /**
  155. * 🔥 发送消息到企业微信当前窗口
  156. */
  157. private async sendToWxwork(text: string, imageUrls: string[] = []): Promise<void> {
  158. try {
  159. console.log('🔍 [sendToWxwork] ========== 开始发送流程 ==========');
  160. console.log('🔍 [sendToWxwork] 当前URL:', window.location.href);
  161. // 检查是否在企业微信环境中
  162. const isWxwork = window.location.href.includes('/wxwork/');
  163. console.log('🔍 [sendToWxwork] 企业微信环境检测:', isWxwork);
  164. if (!isWxwork) {
  165. console.warn('⚠️ 非企业微信环境,跳过发送');
  166. console.warn('⚠️ 请从企业微信客户端的侧边栏打开应用');
  167. console.warn('⚠️ URL应该包含 /wxwork/ 路径');
  168. return;
  169. }
  170. // 从URL获取cid和appId
  171. const urlParts = window.location.pathname.split('/');
  172. console.log('🔍 [sendToWxwork] URL路径分段:', urlParts);
  173. const wxworkIndex = urlParts.indexOf('wxwork');
  174. console.log('🔍 [sendToWxwork] wxwork位置索引:', wxworkIndex);
  175. const cid = urlParts[wxworkIndex + 1];
  176. const appId = urlParts[wxworkIndex + 2] || 'crm';
  177. console.log('🔍 [sendToWxwork] 提取的CID:', cid);
  178. console.log('🔍 [sendToWxwork] 提取的AppID:', appId);
  179. if (!cid || cid === 'undefined') {
  180. throw new Error('❌ 无法从URL获取CID,请检查URL格式');
  181. }
  182. // 初始化WxworkSDKService
  183. console.log('🔍 [sendToWxwork] 开始初始化企业微信SDK...');
  184. await this.wxworkService.initialize(cid, appId);
  185. console.log('✅ [sendToWxwork] SDK初始化完成');
  186. console.log('📧 准备发送消息到企业微信...');
  187. console.log(' CID:', cid);
  188. console.log(' AppID:', appId);
  189. console.log(' 文本内容:', text || '(无文本)');
  190. console.log(' 图片数量:', imageUrls.length);
  191. console.log(' 图片URL列表:', imageUrls);
  192. // 🔥 发送文本消息
  193. if (text) {
  194. await this.wxworkService.sendChatMessage({
  195. msgtype: 'text',
  196. text: {
  197. content: text
  198. }
  199. });
  200. console.log('✅ 文本消息已发送');
  201. }
  202. // 🔥 发送图片消息(使用news图文消息类型)
  203. for (let i = 0; i < imageUrls.length; i++) {
  204. const imageUrl = imageUrls[i];
  205. try {
  206. // 使用news类型发送图文消息,可以显示图片预览
  207. await this.wxworkService.sendChatMessage({
  208. msgtype: 'news',
  209. news: {
  210. link: imageUrl,
  211. title: `图片 ${i + 1}/${imageUrls.length}`,
  212. desc: '点击查看大图',
  213. imgUrl: imageUrl
  214. }
  215. });
  216. console.log(`✅ 图文消息 ${i + 1}/${imageUrls.length} 已发送: ${imageUrl}`);
  217. // 避免发送过快,加入小延迟
  218. if (i < imageUrls.length - 1) {
  219. await new Promise(resolve => setTimeout(resolve, 300));
  220. }
  221. } catch (imgError) {
  222. console.error(`❌ 图片 ${i + 1} 发送失败:`, imgError);
  223. // 如果news类型失败,降级为纯文本链接
  224. try {
  225. await this.wxworkService.sendChatMessage({
  226. msgtype: 'text',
  227. text: {
  228. content: `📷 图片 ${i + 1}/${imageUrls.length}\n${imageUrl}`
  229. }
  230. });
  231. console.log(`✅ 已改用文本方式发送图片链接`);
  232. } catch (textError) {
  233. console.error(`❌ 文本方式也失败:`, textError);
  234. }
  235. }
  236. }
  237. console.log('✅ 所有消息已发送到企业微信');
  238. } catch (error) {
  239. console.error('❌ 发送消息到企业微信失败:', error);
  240. // 🔥 修复:必须抛出错误,让上层知道发送失败
  241. throw error;
  242. }
  243. }
  244. }