import { Injectable, signal } from '@angular/core'; import { Observable, of, BehaviorSubject } from 'rxjs'; import { delay, map } from 'rxjs/operators'; // 报价审核状态 export type QuotationApprovalStatus = 'pending' | 'approved' | 'rejected' | 'revision_required'; // 报价审核记录 export interface QuotationApproval { id: string; quotationId: string; projectId: string; projectName: string; customerName: string; submittedBy: string; // 提交人 submittedAt: Date; totalAmount: number; status: QuotationApprovalStatus; approvedBy?: string; // 审批人 approvedAt?: Date; rejectionReason?: string; revisionNotes?: string; scriptUsed?: string; // 使用的话术模板ID communicationLog: CommunicationRecord[]; } // 沟通记录 export interface CommunicationRecord { id: string; timestamp: Date; type: 'script_used' | 'customer_response' | 'internal_note' | 'approval_comment'; content: string; scriptId?: string; // 如果是使用话术,记录话术ID userId: string; userName: string; } // 话术使用统计 export interface ScriptUsageStats { scriptId: string; scriptTitle: string; usageCount: number; successRate: number; // 成功率(客户接受报价的比例) avgResponseTime: number; // 平均响应时间(小时) lastUsed: string; } @Injectable({ providedIn: 'root' }) export class QuotationApprovalService { private approvals = signal([]); private scriptUsageStats = signal([]); // 实时通知流 private approvalUpdates$ = new BehaviorSubject(null); constructor() { this.initializeMockData(); } // 初始化模拟数据 private initializeMockData(): void { const mockApprovals: QuotationApproval[] = [ { id: 'qa001', quotationId: 'q001', projectId: 'p001', projectName: '现代简约客厅设计', customerName: '张先生', submittedBy: '李设计师', submittedAt: new Date('2024-01-15T10:30:00'), totalAmount: 85000, status: 'pending', scriptUsed: 'script001', communicationLog: [ { id: 'comm001', timestamp: new Date('2024-01-15T10:30:00'), type: 'script_used', content: '使用了"高端设计报价说明"话术模板', scriptId: 'script001', userId: 'designer001', userName: '李设计师' } ] }, { id: 'qa002', quotationId: 'q002', projectId: 'p002', projectName: '北欧风卧室设计', customerName: '王女士', submittedBy: '陈设计师', submittedAt: new Date('2024-01-14T14:20:00'), totalAmount: 65000, status: 'approved', approvedBy: '财务主管', approvedAt: new Date('2024-01-14T16:45:00'), scriptUsed: 'script002', communicationLog: [ { id: 'comm002', timestamp: new Date('2024-01-14T14:20:00'), type: 'script_used', content: '使用了"中档设计报价说明"话术模板', scriptId: 'script002', userId: 'designer002', userName: '陈设计师' }, { id: 'comm003', timestamp: new Date('2024-01-14T16:45:00'), type: 'approval_comment', content: '报价合理,符合市场标准,批准通过', userId: 'finance001', userName: '财务主管' } ] } ]; const mockStats: ScriptUsageStats[] = [ { scriptId: 'script001', scriptTitle: '高端设计报价说明', usageCount: 15, successRate: 0.73, avgResponseTime: 2.5, lastUsed: '2024-01-15T10:30:00' }, { scriptId: 'script002', scriptTitle: '中档设计报价说明', usageCount: 28, successRate: 0.82, avgResponseTime: 1.8, lastUsed: '2024-01-14T14:20:00' }, { scriptId: 'script003', scriptTitle: '基础设计报价说明', usageCount: 35, successRate: 0.89, avgResponseTime: 1.2, lastUsed: '2024-01-13T09:15:00' } ]; this.approvals.set(mockApprovals); this.scriptUsageStats.set(mockStats); } // 获取报价审核列表 getQuotationApprovals(): Observable { return of(this.approvals()).pipe(delay(300)); } // 根据状态筛选报价审核记录 getApprovalsByStatus(status: QuotationApprovalStatus): Observable { return of(this.approvals().filter(approval => approval.status === status)).pipe(delay(300)); } // 获取单个报价审核记录 getApprovalById(id: string): Observable { return of(this.approvals().find(approval => approval.id === id)).pipe(delay(200)); } // 提交报价审核 submitQuotationForApproval(quotationData: { quotationId: string; projectId: string; projectName: string; customerName: string; submittedBy: string; totalAmount: number; scriptUsed?: string; }): Observable { const newApproval: QuotationApproval = { id: `qa${Date.now()}`, ...quotationData, submittedAt: new Date(), status: 'pending', communicationLog: quotationData.scriptUsed ? [{ id: `comm${Date.now()}`, timestamp: new Date(), type: 'script_used', content: `使用了话术模板进行报价说明`, scriptId: quotationData.scriptUsed, userId: 'current_user', userName: quotationData.submittedBy }] : [] }; const updatedApprovals = [...this.approvals(), newApproval]; this.approvals.set(updatedApprovals); // 通知更新 this.approvalUpdates$.next(newApproval); return of(newApproval).pipe(delay(500)); } // 审批报价 approveQuotation(approvalId: string, approvedBy: string, comment?: string): Observable { const approvals = this.approvals(); const approvalIndex = approvals.findIndex(a => a.id === approvalId); if (approvalIndex === -1) { throw new Error('报价审核记录不存在'); } const updatedApproval = { ...approvals[approvalIndex], status: 'approved' as QuotationApprovalStatus, approvedBy, approvedAt: new Date(), communicationLog: [ ...approvals[approvalIndex].communicationLog, { id: `comm${Date.now()}`, timestamp: new Date(), type: 'approval_comment' as const, content: comment || '报价已批准', userId: 'current_user', userName: approvedBy } ] }; const updatedApprovals = [...approvals]; updatedApprovals[approvalIndex] = updatedApproval; this.approvals.set(updatedApprovals); // 通知更新 this.approvalUpdates$.next(updatedApproval); return of(updatedApproval).pipe(delay(300)); } // 拒绝报价 rejectQuotation(approvalId: string, rejectedBy: string, reason: string): Observable { const approvals = this.approvals(); const approvalIndex = approvals.findIndex(a => a.id === approvalId); if (approvalIndex === -1) { throw new Error('报价审核记录不存在'); } const updatedApproval = { ...approvals[approvalIndex], status: 'rejected' as QuotationApprovalStatus, approvedBy: rejectedBy, approvedAt: new Date(), rejectionReason: reason, communicationLog: [ ...approvals[approvalIndex].communicationLog, { id: `comm${Date.now()}`, timestamp: new Date(), type: 'approval_comment' as const, content: `报价被拒绝:${reason}`, userId: 'current_user', userName: rejectedBy } ] }; const updatedApprovals = [...approvals]; updatedApprovals[approvalIndex] = updatedApproval; this.approvals.set(updatedApprovals); // 通知更新 this.approvalUpdates$.next(updatedApproval); return of(updatedApproval).pipe(delay(300)); } // 要求修订报价 requestRevision(approvalId: string, requestedBy: string, notes: string): Observable { const approvals = this.approvals(); const approvalIndex = approvals.findIndex(a => a.id === approvalId); if (approvalIndex === -1) { throw new Error('报价审核记录不存在'); } const updatedApproval = { ...approvals[approvalIndex], status: 'revision_required' as QuotationApprovalStatus, revisionNotes: notes, communicationLog: [ ...approvals[approvalIndex].communicationLog, { id: `comm${Date.now()}`, timestamp: new Date(), type: 'approval_comment' as const, content: `要求修订:${notes}`, userId: 'current_user', userName: requestedBy } ] }; const updatedApprovals = [...approvals]; updatedApprovals[approvalIndex] = updatedApproval; this.approvals.set(updatedApprovals); // 通知更新 this.approvalUpdates$.next(updatedApproval); return of(updatedApproval).pipe(delay(300)); } // 记录话术使用 recordScriptUsage(approvalId: string, scriptId: string, userId: string, userName: string): Observable { const approvals = this.approvals(); const approvalIndex = approvals.findIndex(a => a.id === approvalId); if (approvalIndex !== -1) { const communicationRecord: CommunicationRecord = { id: `comm${Date.now()}`, timestamp: new Date(), type: 'script_used', content: '使用了话术模板进行沟通', scriptId, userId, userName }; const updatedApproval = { ...approvals[approvalIndex], communicationLog: [...approvals[approvalIndex].communicationLog, communicationRecord] }; const updatedApprovals = [...approvals]; updatedApprovals[approvalIndex] = updatedApproval; this.approvals.set(updatedApprovals); // 更新话术使用统计 this.updateScriptUsageStats(scriptId); } return of(void 0).pipe(delay(200)); } // 记录客户响应 recordCustomerResponse(approvalId: string, response: string, userId: string, userName: string): Observable { const approvals = this.approvals(); const approvalIndex = approvals.findIndex(a => a.id === approvalId); if (approvalIndex !== -1) { const communicationRecord: CommunicationRecord = { id: `comm${Date.now()}`, timestamp: new Date(), type: 'customer_response', content: response, userId, userName }; const updatedApproval = { ...approvals[approvalIndex], communicationLog: [...approvals[approvalIndex].communicationLog, communicationRecord] }; const updatedApprovals = [...approvals]; updatedApprovals[approvalIndex] = updatedApproval; this.approvals.set(updatedApprovals); } return of(void 0).pipe(delay(200)); } // 获取话术使用统计 getScriptUsageStats(): Observable { return of(this.scriptUsageStats()).pipe(delay(300)); } // 获取实时更新流 getApprovalUpdates(): Observable { return this.approvalUpdates$.asObservable(); } // 更新话术使用统计 private updateScriptUsageStats(scriptId: string): void { const stats = this.scriptUsageStats(); const statIndex = stats.findIndex(s => s.scriptId === scriptId); if (statIndex !== -1) { const updatedStats = [...stats]; updatedStats[statIndex] = { ...updatedStats[statIndex], usageCount: updatedStats[statIndex].usageCount + 1, lastUsed: new Date().toISOString() }; this.scriptUsageStats.set(updatedStats); } } // 获取审核统计数据 getApprovalStats(): Observable<{ total: number; pending: number; approved: number; rejected: number; revisionRequired: number; avgApprovalTime: number; // 平均审批时间(小时) }> { const approvals = this.approvals(); const total = approvals.length; const pending = approvals.filter(a => a.status === 'pending').length; const approved = approvals.filter(a => a.status === 'approved').length; const rejected = approvals.filter(a => a.status === 'rejected').length; const revisionRequired = approvals.filter(a => a.status === 'revision_required').length; // 计算平均审批时间 const completedApprovals = approvals.filter(a => a.approvedAt); const avgApprovalTime = completedApprovals.length > 0 ? completedApprovals.reduce((sum, approval) => { const timeDiff = approval.approvedAt!.getTime() - approval.submittedAt.getTime(); return sum + (timeDiff / (1000 * 60 * 60)); // 转换为小时 }, 0) / completedApprovals.length : 0; return of({ total, pending, approved, rejected, revisionRequired, avgApprovalTime }).pipe(delay(200)); } }