import { Injectable, inject } from '@angular/core'; import { FmodeParse } from 'fmode-ng/core'; /** * 活动日志服务 * 用于记录和查询系统中所有成员的操作活动 */ @Injectable({ providedIn: 'root' }) export class ActivityLogService { private Parse = FmodeParse.with('nova'); /** * 记录活动日志 */ async logActivity(data: { actorId: string; actorName: string; actorRole: string; actionType: string; module: string; entityType: string; entityId: string; entityName: string; description: string; metadata?: any; }): Promise { try { const currentUser = await this.Parse.User.current(); if (!currentUser) { console.warn('No current user, skipping activity log'); return null; } const company = currentUser.get('company'); if (!company) { console.warn('No company found, skipping activity log'); return null; } const ActivityLog = this.Parse.Object.extend('ActivityLog'); const log = new ActivityLog(); log.set('company', company); log.set('actor', { __type: 'Pointer', className: 'Profile', objectId: data.actorId }); log.set('actorName', data.actorName); log.set('actorRole', data.actorRole); log.set('actionType', data.actionType); log.set('module', data.module); log.set('entityType', data.entityType); log.set('entityId', data.entityId); log.set('entityName', data.entityName); log.set('description', data.description); log.set('metadata', data.metadata || {}); log.set('userAgent', navigator.userAgent); log.set('isDeleted', false); const saved = await log.save(); return saved; } catch (error) { console.error('Error logging activity:', error); return null; } } /** * 获取最近的活动日志 */ async getRecentActivities(limit: number = 10): Promise { try { const currentUser = await this.Parse.User.current(); if (!currentUser) { return []; } const company = currentUser.get('company'); if (!company) { return []; } const query = new this.Parse.Query('ActivityLog'); query.equalTo('company', company); query.notEqualTo('isDeleted', true); query.include('actor'); query.descending('createdAt'); query.limit(limit); const results = await query.find(); return results.map(log => this.formatActivityLog(log)); } catch (error) { console.error('Error fetching recent activities:', error); return []; } } /** * 获取所有活动日志(支持分页) */ async getAllActivities(page: number = 1, limit: number = 50): Promise<{ activities: any[], total: number }> { try { const currentUser = await this.Parse.User.current(); if (!currentUser) { return { activities: [], total: 0 }; } const company = currentUser.get('company'); if (!company) { return { activities: [], total: 0 }; } const query = new this.Parse.Query('ActivityLog'); query.equalTo('company', company); query.notEqualTo('isDeleted', true); query.include('actor'); query.descending('createdAt'); query.skip((page - 1) * limit); query.limit(limit); const [results, total] = await Promise.all([ query.find(), query.count() ]); return { activities: results.map(log => this.formatActivityLog(log)), total }; } catch (error) { console.error('Error fetching all activities:', error); return { activities: [], total: 0 }; } } /** * 按模块查询活动日志 */ async getActivitiesByModule(module: string, limit: number = 20): Promise { try { const currentUser = await this.Parse.User.current(); if (!currentUser) { return []; } const company = currentUser.get('company'); if (!company) { return []; } const query = new this.Parse.Query('ActivityLog'); query.equalTo('company', company); query.equalTo('module', module); query.notEqualTo('isDeleted', true); query.include('actor'); query.descending('createdAt'); query.limit(limit); const results = await query.find(); return results.map(log => this.formatActivityLog(log)); } catch (error) { console.error('Error fetching activities by module:', error); return []; } } /** * 按用户查询活动日志 */ async getActivitiesByActor(actorId: string, limit: number = 20): Promise { try { const currentUser = await this.Parse.User.current(); if (!currentUser) { return []; } const company = currentUser.get('company'); if (!company) { return []; } const query = new this.Parse.Query('ActivityLog'); query.equalTo('company', company); query.equalTo('actor', { __type: 'Pointer', className: 'Profile', objectId: actorId }); query.notEqualTo('isDeleted', true); query.descending('createdAt'); query.limit(limit); const results = await query.find(); return results.map(log => this.formatActivityLog(log)); } catch (error) { console.error('Error fetching activities by actor:', error); return []; } } /** * 按时间范围查询活动日志 */ async getActivitiesByDateRange(startDate: Date, endDate: Date): Promise { try { const currentUser = await this.Parse.User.current(); if (!currentUser) { return []; } const company = currentUser.get('company'); if (!company) { return []; } const query = new this.Parse.Query('ActivityLog'); query.equalTo('company', company); query.greaterThanOrEqualTo('createdAt', startDate); query.lessThanOrEqualTo('createdAt', endDate); query.notEqualTo('isDeleted', true); query.include('actor'); query.descending('createdAt'); const results = await query.find(); return results.map(log => this.formatActivityLog(log)); } catch (error) { console.error('Error fetching activities by date range:', error); return []; } } /** * 获取活动统计 */ async getActivityStats(startDate?: Date, endDate?: Date): Promise<{ total: number; byModule: Record; byAction: Record; byActor: Record; }> { try { const currentUser = await this.Parse.User.current(); if (!currentUser) { return { total: 0, byModule: {}, byAction: {}, byActor: {} }; } const company = currentUser.get('company'); if (!company) { return { total: 0, byModule: {}, byAction: {}, byActor: {} }; } const query = new this.Parse.Query('ActivityLog'); query.equalTo('company', company); query.notEqualTo('isDeleted', true); if (startDate) { query.greaterThanOrEqualTo('createdAt', startDate); } if (endDate) { query.lessThanOrEqualTo('createdAt', endDate); } const results = await query.find(); const stats = { total: results.length, byModule: {} as Record, byAction: {} as Record, byActor: {} as Record }; results.forEach(log => { const module = log.get('module'); const action = log.get('actionType'); const actor = log.get('actorName'); stats.byModule[module] = (stats.byModule[module] || 0) + 1; stats.byAction[action] = (stats.byAction[action] || 0) + 1; stats.byActor[actor] = (stats.byActor[actor] || 0) + 1; }); return stats; } catch (error) { console.error('Error fetching activity stats:', error); return { total: 0, byModule: {}, byAction: {}, byActor: {} }; } } /** * 格式化活动日志对象 */ private formatActivityLog(log: any): any { const actor = log.get('actor'); return { id: log.id, actorName: log.get('actorName'), actorRole: log.get('actorRole'), actorAvatar: actor?.get('data')?.avatar || '/assets/avatars/default.png', actionType: log.get('actionType'), module: log.get('module'), entityType: log.get('entityType'), entityId: log.get('entityId'), entityName: log.get('entityName'), description: log.get('description'), metadata: log.get('metadata') || {}, createdAt: log.get('createdAt'), formattedTime: this.formatTime(log.get('createdAt')), icon: this.getActivityIcon(log.get('module'), log.get('actionType')), color: this.getActivityColor(log.get('module'), log.get('actionType')) }; } /** * 获取活动图标 */ private getActivityIcon(module: string, actionType: string): string { const iconMap: Record = { 'project-create': '📋', 'project-update': '📝', 'project-complete': '✅', 'project-assign': '👤', 'customer-create': '🆕', 'customer-update': '✏️', 'task-create': '📌', 'task-complete': '✔️', 'design-create': '🎨', 'design-upload': '📤', 'design-complete': '🎨', 'finance-create': '💰', 'finance-approve': '✓', 'urgent_task-create': '🚨', 'urgent_task-complete': '✅', 'case_library-create': '📚', 'case_library-share': '🔗', 'groupchat-create': '💬', 'employee-create': '👥', 'system-login': '🔐' }; const key = `${module}-${actionType}`; return iconMap[key] || '📝'; } /** * 获取活动颜色 */ private getActivityColor(module: string, actionType: string): string { const colorMap: Record = { 'create': '#4CAF50', 'update': '#2196F3', 'delete': '#F44336', 'complete': '#8BC34A', 'assign': '#9C27B0', 'upload': '#FF9800', 'approve': '#00BCD4' }; return colorMap[actionType] || '#607D8B'; } /** * 格式化时间 */ private formatTime(date: Date): string { const now = new Date(); const diff = now.getTime() - date.getTime(); const seconds = Math.floor(diff / 1000); const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); if (seconds < 60) { return '刚刚'; } else if (minutes < 60) { return `${minutes}分钟前`; } else if (hours < 24) { return `${hours}小时前`; } else if (days < 7) { return `${days}天前`; } else { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hour = String(date.getHours()).padStart(2, '0'); const minute = String(date.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day} ${hour}:${minute}`; } } /** * 获取活动描述的友好文本 */ getActivityDescription(activity: any): string { const actionMap: Record = { create: '创建了', update: '更新了', delete: '删除了', complete: '完成了', assign: '分配了', upload: '上传了', share: '分享了', approve: '审批了', reject: '拒绝了', cancel: '取消了', comment: '评论了' }; const moduleMap: Record = { project: '项目', customer: '客户', task: '任务', design: '设计', finance: '报价单', urgent_task: '紧急事项', case_library: '案例', groupchat: '群聊', employee: '员工', department: '部门' }; return activity.description || `${actionMap[activity.actionType] || activity.actionType}${moduleMap[activity.module] || activity.module}`; } }