104 lines
3.6 KiB
TypeScript
104 lines
3.6 KiB
TypeScript
import { BadRequestException, Body, Controller, Get, Post, Query, UseGuards } from '@nestjs/common';
|
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
import { RequirePermission } from '../auth/decorators/permission.decorator';
|
|
import { SyncService } from './sync.service';
|
|
import type { SyncPlatform } from '../entities/sync-log.entity';
|
|
import { ImportUsersDto } from './dto/import-users.dto';
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@Controller('sync')
|
|
export class SyncController {
|
|
constructor(private readonly syncService: SyncService) {}
|
|
|
|
@Post('trigger')
|
|
@RequirePermission('sync:trigger')
|
|
async triggerSync(
|
|
@Query('platform') platform?: SyncPlatform,
|
|
@Query('rootDeptId') rootDeptId?: string,
|
|
) {
|
|
const rootId = rootDeptId ? this.parseRootDeptId(rootDeptId) : 1;
|
|
const logs = await this.syncService.triggerSync(platform, rootId);
|
|
return { synced: logs.length, logs };
|
|
}
|
|
|
|
@Get('status')
|
|
@RequirePermission('sync:read')
|
|
async getStatus() {
|
|
const lastDingTalk = await this.syncService.getLastSync('dingtalk');
|
|
const lastWeCom = await this.syncService.getLastSync('wecom');
|
|
return {
|
|
dingTalk: lastDingTalk ? { lastSyncAt: lastDingTalk.finishedAt, status: lastDingTalk.status } : null,
|
|
weCom: lastWeCom ? { lastSyncAt: lastWeCom.finishedAt, status: lastWeCom.status } : null,
|
|
};
|
|
}
|
|
|
|
/** 获取钉钉组织部门树,供前端选择同步起点 */
|
|
@Get('dingtalk/org-tree')
|
|
@RequirePermission('sync:read')
|
|
async getDingTalkOrgTree(@Query('rootDeptId') rootDeptId?: string) {
|
|
const rootId = rootDeptId ? this.parseRootDeptId(rootDeptId) : 1;
|
|
const tree = await this.syncService.getDingTalkOrgTree(rootId);
|
|
return { success: true, data: tree };
|
|
}
|
|
|
|
/** 获取钉钉组织部门树(含用户),供同步用户选择器使用 */
|
|
@Get('dingtalk/org-tree-with-users')
|
|
@RequirePermission('sync:read')
|
|
async getDingTalkOrgTreeWithUsers(@Query('rootDeptId') rootDeptId?: string) {
|
|
const rootId = rootDeptId ? this.parseRootDeptId(rootDeptId) : 1;
|
|
const tree = await this.syncService.getDingTalkOrgTreeWithUsers(rootId);
|
|
return { success: true, data: tree };
|
|
}
|
|
|
|
/** 导入钉钉用户:老师分配角色,学生创建 Student */
|
|
@Post('dingtalk/import-users')
|
|
@RequirePermission('sync:trigger')
|
|
async importDingTalkUsers(@Body() body: ImportUsersDto) {
|
|
const result = await this.syncService.importDingTalkUsers(body.users);
|
|
return { success: true, ...result };
|
|
}
|
|
|
|
@Get('logs')
|
|
@RequirePermission('sync:read')
|
|
async getLogs(
|
|
@Query('platform') platform?: SyncPlatform,
|
|
@Query('limit') limit?: number,
|
|
) {
|
|
return this.syncService.getLogs(platform, limit ? Number(limit) : 50);
|
|
}
|
|
|
|
// ── 排班同步 ──
|
|
|
|
/** 触发排班同步到钉钉考勤排班 */
|
|
@Post('schedule/sync')
|
|
@RequirePermission('sync:trigger')
|
|
async syncSchedule(
|
|
@Query('dateFrom') dateFrom?: string,
|
|
@Query('days') days?: string,
|
|
) {
|
|
const result = await this.syncService.syncScheduleToDingTalk(
|
|
dateFrom,
|
|
days ? parseInt(days, 10) : 30,
|
|
);
|
|
return { success: true, data: result };
|
|
}
|
|
|
|
/** 查询钉钉排班状态(核对本地 vs 钉钉) */
|
|
@Get('schedule/status')
|
|
@RequirePermission('sync:read')
|
|
async getScheduleStatus(
|
|
@Query('date') date?: string,
|
|
) {
|
|
const status = await this.syncService.getScheduleSyncStatus(date);
|
|
return { success: true, data: status };
|
|
}
|
|
|
|
private parseRootDeptId(rootDeptId: string): number {
|
|
const parsed = parseInt(rootDeptId, 10);
|
|
if (isNaN(parsed)) {
|
|
throw new BadRequestException('rootDeptId must be a valid integer');
|
|
}
|
|
return parsed;
|
|
}
|
|
}
|