chore: commit remaining integration files, PM2 single instance, PRD doc
This commit is contained in:
@@ -36,6 +36,7 @@
|
||||
"bcryptjs": "^3.0.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.15.1",
|
||||
"echarts": "^6.1.0",
|
||||
"exceljs": "^4.4.0",
|
||||
"multer": "^2.1.1",
|
||||
"mysql2": "^3.22.2",
|
||||
|
||||
12
apps/server/src/integration/integration.module.ts
Normal file
12
apps/server/src/integration/integration.module.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { Department, User } from '../entities';
|
||||
import { DingTalkService } from './dingtalk.service';
|
||||
import { WeComService } from './wecom.service';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Department, User])],
|
||||
providers: [DingTalkService, WeComService],
|
||||
exports: [DingTalkService, WeComService],
|
||||
})
|
||||
export class IntegrationModule {}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { IntegrationModule } from '../integration/integration.module';
|
||||
import { SyncLog, SyncState } from '../entities';
|
||||
import { SyncService } from './sync.service';
|
||||
import { SyncController } from './sync.controller';
|
||||
@@ -9,6 +10,7 @@ import { SyncController } from './sync.controller';
|
||||
imports: [
|
||||
ScheduleModule.forRoot(),
|
||||
TypeOrmModule.forFeature([SyncLog, SyncState]),
|
||||
IntegrationModule,
|
||||
],
|
||||
controllers: [SyncController],
|
||||
providers: [SyncService],
|
||||
|
||||
@@ -4,6 +4,8 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { SyncLog, SyncState } from '../entities';
|
||||
import type { SyncPlatform, SyncType, SyncStatus } from '../entities/sync-log.entity';
|
||||
import { DingTalkService } from '../integration/dingtalk.service';
|
||||
import { WeComService } from '../integration/wecom.service';
|
||||
|
||||
@Injectable()
|
||||
export class SyncService {
|
||||
@@ -14,6 +16,8 @@ export class SyncService {
|
||||
private readonly syncLogRepo: Repository<SyncLog>,
|
||||
@InjectRepository(SyncState)
|
||||
private readonly syncStateRepo: Repository<SyncState>,
|
||||
private readonly dingTalkService: DingTalkService,
|
||||
private readonly weComService: WeComService,
|
||||
) {}
|
||||
|
||||
// ── Scheduled cron: daily at 2 AM ──
|
||||
@@ -43,9 +47,10 @@ export class SyncService {
|
||||
await this.updateLastSyncAt(platform);
|
||||
await this.finishSyncLog(log, 'success', recordsCount);
|
||||
this.logger.log(`DingTalk sync complete: ${recordsCount} records`);
|
||||
} catch (error: any) {
|
||||
await this.finishSyncLog(log, 'failed', 0, error.message);
|
||||
this.logger.error(`DingTalk sync failed: ${error.message}`, error.stack);
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
await this.finishSyncLog(log, 'failed', 0, message);
|
||||
this.logger.error(`DingTalk sync failed: ${message}`, error instanceof Error ? error.stack : undefined);
|
||||
}
|
||||
|
||||
return log;
|
||||
@@ -69,9 +74,10 @@ export class SyncService {
|
||||
await this.updateLastSyncAt(platform);
|
||||
await this.finishSyncLog(log, 'success', recordsCount);
|
||||
this.logger.log(`WeCom sync complete: ${recordsCount} records`);
|
||||
} catch (error: any) {
|
||||
await this.finishSyncLog(log, 'failed', 0, error.message);
|
||||
this.logger.error(`WeCom sync failed: ${error.message}`, error.stack);
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
await this.finishSyncLog(log, 'failed', 0, message);
|
||||
this.logger.error(`WeCom sync failed: ${message}`, error instanceof Error ? error.stack : undefined);
|
||||
}
|
||||
|
||||
return log;
|
||||
@@ -86,7 +92,7 @@ export class SyncService {
|
||||
|
||||
// ── Sync log queries ──
|
||||
async getLogs(platform?: SyncPlatform, limit: number = 50): Promise<SyncLog[]> {
|
||||
const where: any = {};
|
||||
const where: Record<string, SyncPlatform> = {};
|
||||
if (platform) where.platform = platform;
|
||||
return this.syncLogRepo.find({ where, order: { createdAt: 'DESC' }, take: limit });
|
||||
}
|
||||
@@ -145,36 +151,13 @@ export class SyncService {
|
||||
await this.syncLogRepo.save(log);
|
||||
}
|
||||
|
||||
// ── Integration stubs — replace with real API calls ──
|
||||
|
||||
/**
|
||||
* Perform the actual DingTalk data pull.
|
||||
* Pass `lastSyncAt` to the DingTalk API for incremental sync.
|
||||
*/
|
||||
private async performDingTalkSync(_lastSyncAt: Date | null): Promise<number> {
|
||||
const appKey = process.env.DINGTALK_APP_KEY;
|
||||
if (!appKey) {
|
||||
this.logger.warn('DingTalk not configured (DINGTALK_APP_KEY missing), skipping sync');
|
||||
return 0;
|
||||
}
|
||||
// 接入指引:
|
||||
// 1. 创建 apps/server/src/integration/dingtalk.service.ts,实现 fetchDepartments()/fetchUsers()
|
||||
// 2. 在 SyncModule 中注入 DingTalkService
|
||||
// 3. 取消以下注释并调用 this.dingTalkService.fetchDepartments()
|
||||
this.logger.warn('DingTalk sync stub: create DingTalkService in src/integration/ to enable real sync');
|
||||
return 0;
|
||||
const result = await this.dingTalkService.syncAll();
|
||||
return result.deptCount + result.userCount;
|
||||
}
|
||||
|
||||
private async performWeComSync(_lastSyncAt: Date | null): Promise<number> {
|
||||
const corpId = process.env.WECOM_CORP_ID;
|
||||
if (!corpId) {
|
||||
this.logger.warn('WeCom not configured (WECOM_CORP_ID missing), skipping sync');
|
||||
return 0;
|
||||
}
|
||||
|
||||
this.logger.warn(
|
||||
'WeCom integration service not yet implemented — add WeComService to SyncModule to enable real sync',
|
||||
);
|
||||
return 0;
|
||||
const result = await this.weComService.syncAll();
|
||||
return result.deptCount + result.userCount;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user