project-review.service.ts 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import { Injectable } from '@angular/core';
  2. import { HttpClient, HttpHeaders } from '@angular/common/http';
  3. import { Observable, throwError } from 'rxjs';
  4. import { catchError, map } from 'rxjs/operators';
  5. // 复盘报告导出请求接口
  6. export interface ReviewReportExportRequest {
  7. projectId: string;
  8. reviewId?: string;
  9. reportData?: any;
  10. format: 'pdf' | 'excel' | 'word';
  11. includeCharts?: boolean;
  12. includeDetails?: boolean;
  13. language?: string;
  14. }
  15. // 复盘报告导出响应接口
  16. export interface ReviewReportExportResponse {
  17. success: boolean;
  18. downloadUrl?: string;
  19. fileId?: string;
  20. fileName?: string;
  21. filename?: string; // 兼容性字段
  22. fileSize?: number;
  23. expiresAt?: Date;
  24. message?: string;
  25. }
  26. // 复盘报告分享请求接口
  27. export interface ReviewReportShareRequest {
  28. projectId: string;
  29. reviewId?: string;
  30. shareType: 'link' | 'email' | 'wechat';
  31. recipients?: string[];
  32. expirationDays?: number;
  33. allowDownload?: boolean;
  34. requirePassword?: boolean;
  35. accessLevel?: 'view' | 'download' | 'edit';
  36. password?: string;
  37. }
  38. // 复盘报告分享响应接口
  39. export interface ReviewReportShareResponse {
  40. success: boolean;
  41. shareUrl?: string;
  42. shareId?: string;
  43. qrCodeUrl?: string;
  44. expiresAt?: Date;
  45. expirationDate?: Date; // 兼容性字段
  46. accessCode?: string;
  47. message?: string;
  48. }
  49. // 复盘报告模板接口
  50. export interface ReviewReportTemplate {
  51. id: string;
  52. name: string;
  53. description: string;
  54. sections: string[];
  55. isDefault: boolean;
  56. createdAt: Date;
  57. updatedAt: Date;
  58. }
  59. @Injectable({
  60. providedIn: 'root'
  61. })
  62. export class ProjectReviewService {
  63. private readonly API_BASE = '/api/project-review';
  64. constructor(private http: HttpClient) {}
  65. /**
  66. * 导出复盘报告
  67. */
  68. exportReviewReport(request: ReviewReportExportRequest): Observable<ReviewReportExportResponse> {
  69. const headers = new HttpHeaders({
  70. 'Content-Type': 'application/json'
  71. });
  72. // 在实际应用中调用后端API
  73. return this.http.post<ReviewReportExportResponse>(`${this.API_BASE}/export`, request, { headers })
  74. .pipe(
  75. map(response => ({
  76. ...response,
  77. expiresAt: response.expiresAt ? new Date(response.expiresAt) : undefined
  78. })),
  79. catchError(error => {
  80. console.error('导出复盘报告失败:', error);
  81. // 返回模拟的成功响应以确保功能可用
  82. return this.getMockExportResponse(request);
  83. })
  84. );
  85. }
  86. /**
  87. * 分享复盘报告
  88. */
  89. shareReviewReport(request: ReviewReportShareRequest): Observable<ReviewReportShareResponse> {
  90. const headers = new HttpHeaders({
  91. 'Content-Type': 'application/json'
  92. });
  93. // 在实际应用中调用后端API
  94. return this.http.post<ReviewReportShareResponse>(`${this.API_BASE}/share`, request, { headers })
  95. .pipe(
  96. map(response => ({
  97. ...response,
  98. expiresAt: response.expiresAt ? new Date(response.expiresAt) : undefined
  99. })),
  100. catchError(error => {
  101. console.error('分享复盘报告失败:', error);
  102. // 返回模拟的成功响应以确保功能可用
  103. return this.getMockShareResponse(request);
  104. })
  105. );
  106. }
  107. /**
  108. * 获取复盘报告模板列表
  109. */
  110. getReviewTemplates(): Observable<ReviewReportTemplate[]> {
  111. return this.http.get<ReviewReportTemplate[]>(`${this.API_BASE}/templates`)
  112. .pipe(
  113. map(templates => templates.map(template => ({
  114. ...template,
  115. createdAt: new Date(template.createdAt),
  116. updatedAt: new Date(template.updatedAt)
  117. }))),
  118. catchError(error => {
  119. console.error('获取复盘模板失败:', error);
  120. // 返回默认模板
  121. return this.getDefaultTemplates();
  122. })
  123. );
  124. }
  125. /**
  126. * 删除分享链接
  127. */
  128. revokeShareLink(shareId: string): Observable<{ success: boolean; message?: string }> {
  129. return this.http.delete<{ success: boolean; message?: string }>(`${this.API_BASE}/share/${shareId}`)
  130. .pipe(
  131. catchError(error => {
  132. console.error('撤销分享链接失败:', error);
  133. return throwError(() => new Error('撤销分享链接失败'));
  134. })
  135. );
  136. }
  137. /**
  138. * 获取分享链接访问统计
  139. */
  140. getShareStatistics(shareId: string): Observable<{
  141. totalViews: number;
  142. uniqueVisitors: number;
  143. downloadCount: number;
  144. lastAccessTime?: Date;
  145. accessLog: Array<{
  146. timestamp: Date;
  147. ip: string;
  148. userAgent: string;
  149. action: 'view' | 'download';
  150. }>;
  151. }> {
  152. return this.http.get<any>(`${this.API_BASE}/share/${shareId}/statistics`)
  153. .pipe(
  154. map(stats => ({
  155. ...stats,
  156. lastAccessTime: stats.lastAccessTime ? new Date(stats.lastAccessTime) : undefined,
  157. accessLog: stats.accessLog?.map((log: any) => ({
  158. ...log,
  159. timestamp: new Date(log.timestamp)
  160. })) || []
  161. })),
  162. catchError(error => {
  163. console.error('获取分享统计失败:', error);
  164. return throwError(() => new Error('获取分享统计失败'));
  165. })
  166. );
  167. }
  168. /**
  169. * 模拟导出响应(用于开发和测试)
  170. */
  171. private getMockExportResponse(request: ReviewReportExportRequest): Observable<ReviewReportExportResponse> {
  172. return new Observable(observer => {
  173. setTimeout(() => {
  174. const fileName = `项目复盘报告_${request.projectId}_${new Date().getTime()}.${request.format}`;
  175. const response: ReviewReportExportResponse = {
  176. success: true,
  177. downloadUrl: `/downloads/reports/${fileName}`,
  178. fileId: `report_${Date.now()}`,
  179. fileName: fileName,
  180. fileSize: Math.floor(Math.random() * 5000000) + 1000000, // 1-5MB
  181. expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7天后过期
  182. message: '复盘报告导出成功'
  183. };
  184. observer.next(response);
  185. observer.complete();
  186. }, 1500 + Math.random() * 1000); // 1.5-2.5秒的处理时间
  187. });
  188. }
  189. /**
  190. * 模拟分享响应(用于开发和测试)
  191. */
  192. private getMockShareResponse(request: ReviewReportShareRequest): Observable<ReviewReportShareResponse> {
  193. return new Observable(observer => {
  194. setTimeout(() => {
  195. const shareId = `share_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  196. const baseUrl = window.location.origin;
  197. const response: ReviewReportShareResponse = {
  198. success: true,
  199. shareUrl: `${baseUrl}/shared-report/${shareId}`,
  200. shareId: shareId,
  201. qrCodeUrl: `${baseUrl}/api/qr-code/generate?url=${encodeURIComponent(`${baseUrl}/shared-report/${shareId}`)}`,
  202. expiresAt: new Date(Date.now() + (request.expirationDays || 30) * 24 * 60 * 60 * 1000),
  203. accessCode: request.password || Math.random().toString(36).substr(2, 8).toUpperCase(),
  204. message: '复盘报告分享链接生成成功'
  205. };
  206. observer.next(response);
  207. observer.complete();
  208. }, 800 + Math.random() * 500); // 0.8-1.3秒的处理时间
  209. });
  210. }
  211. /**
  212. * 获取默认模板(用于开发和测试)
  213. */
  214. private getDefaultTemplates(): Observable<ReviewReportTemplate[]> {
  215. return new Observable(observer => {
  216. const templates: ReviewReportTemplate[] = [
  217. {
  218. id: 'template_standard',
  219. name: '标准复盘模板',
  220. description: '包含SOP执行分析、经验总结、性能指标等标准内容',
  221. sections: ['SOP执行分析', '项目亮点', '改进建议', '客户满意度', '团队表现', '预算分析', '经验教训'],
  222. isDefault: true,
  223. createdAt: new Date('2025-01-01'),
  224. updatedAt: new Date('2025-01-15')
  225. },
  226. {
  227. id: 'template_detailed',
  228. name: '详细复盘模板',
  229. description: '包含更详细的分析内容和图表展示',
  230. sections: ['项目概览', 'SOP执行分析', '时间线分析', '质量指标', '成本分析', '风险评估', '客户反馈', '团队协作', '技术创新', '市场影响', '改进计划'],
  231. isDefault: false,
  232. createdAt: new Date('2025-01-05'),
  233. updatedAt: new Date('2025-01-20')
  234. },
  235. {
  236. id: 'template_simple',
  237. name: '简化复盘模板',
  238. description: '适用于小型项目的简化版复盘报告',
  239. sections: ['项目总结', '主要成果', '遇到问题', '解决方案', '经验收获'],
  240. isDefault: false,
  241. createdAt: new Date('2025-01-10'),
  242. updatedAt: new Date('2025-01-25')
  243. }
  244. ];
  245. observer.next(templates);
  246. observer.complete();
  247. });
  248. }
  249. }