| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- 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<any> {
- 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<any[]> {
- 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<any[]> {
- 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<any[]> {
- 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<any[]> {
- 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<string, number>;
- byAction: Record<string, number>;
- byActor: Record<string, number>;
- }> {
- 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<string, number>,
- byAction: {} as Record<string, number>,
- byActor: {} as Record<string, number>
- };
- 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<string, string> = {
- '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<string, string> = {
- '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<string, string> = {
- create: '创建了',
- update: '更新了',
- delete: '删除了',
- complete: '完成了',
- assign: '分配了',
- upload: '上传了',
- share: '分享了',
- approve: '审批了',
- reject: '拒绝了',
- cancel: '取消了',
- comment: '评论了'
- };
- const moduleMap: Record<string, string> = {
- 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}`;
- }
- }
|