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'; @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: { users: Array<{ dingUserId: string; name: string; mobile: string; roleId: number | null }> }) { 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; } }