feat: add batch-import students to class endpoint
This commit is contained in:
@@ -21,6 +21,7 @@ import {
|
||||
AddTeacherDto,
|
||||
QueryClassScheduleDto,
|
||||
QueryClassAttendanceSummaryDto,
|
||||
BatchImportStudentsDto,
|
||||
} from './dto/class.dto';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||||
import { OperationLogsService } from '../operation-logs/operation-logs.service';
|
||||
@@ -107,6 +108,16 @@ export class ClassesController {
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 批量导入学生到班级(通过钉钉用户ID) */
|
||||
@Post(':id/students/import')
|
||||
@RequirePermission('class:edit')
|
||||
async batchImportStudents(
|
||||
@Param('id') id: string,
|
||||
@Body() dto: BatchImportStudentsDto,
|
||||
) {
|
||||
return this.service.batchImportStudents(+id, dto.dingUserIds);
|
||||
}
|
||||
|
||||
/** 归档班级 */
|
||||
@Put(':id/archive')
|
||||
@RequirePermission('class:edit')
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { CommonModule } from '../common/common.module';
|
||||
import { Class, ClassStudent, ClassTeacher, ClassSchedule, AttendanceRecord, Department } from '../entities';
|
||||
import { Class, ClassStudent, ClassTeacher, ClassSchedule, AttendanceRecord, Department, Student, StudentDingMapping } from '../entities';
|
||||
import { ClassesService } from './classes.service';
|
||||
import { ClassesController } from './classes.controller';
|
||||
import { OperationLogsModule } from '../operation-logs/operation-logs.module';
|
||||
import { NotificationsModule } from '../notifications/notifications.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Class, ClassStudent, ClassTeacher, ClassSchedule, AttendanceRecord, Department]), OperationLogsModule, NotificationsModule, CommonModule],
|
||||
imports: [TypeOrmModule.forFeature([Class, ClassStudent, ClassTeacher, ClassSchedule, AttendanceRecord, Department, Student, StudentDingMapping]), OperationLogsModule, NotificationsModule, CommonModule],
|
||||
controllers: [ClassesController],
|
||||
providers: [ClassesService],
|
||||
exports: [ClassesService],
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, In, Like } from 'typeorm';
|
||||
import { Class, ClassStudent, ClassTeacher, ClassSchedule, AttendanceRecord, Department, Classroom } from '../entities';
|
||||
import { CreateClassDto, UpdateClassDto, QueryClassDto, AddTeacherDto, QueryClassScheduleDto, QueryClassAttendanceSummaryDto } from './dto/class.dto';
|
||||
import { Class, ClassStudent, ClassTeacher, ClassSchedule, AttendanceRecord, Department, Classroom, Student, StudentDingMapping } from '../entities';
|
||||
import { CreateClassDto, UpdateClassDto, QueryClassDto, AddTeacherDto, QueryClassScheduleDto, QueryClassAttendanceSummaryDto, BatchImportStudentsDto } from './dto/class.dto';
|
||||
|
||||
interface RawStudentCount {
|
||||
classId: string;
|
||||
@@ -24,6 +24,10 @@ export class ClassesService {
|
||||
private attendanceRepo: Repository<AttendanceRecord>,
|
||||
@InjectRepository(Department)
|
||||
private deptRepo: Repository<Department>,
|
||||
@InjectRepository(Student)
|
||||
private studentRepo: Repository<Student>,
|
||||
@InjectRepository(StudentDingMapping)
|
||||
private studentDingMappingRepo: Repository<StudentDingMapping>,
|
||||
) {}
|
||||
|
||||
async findAll(query: QueryClassDto) {
|
||||
@@ -93,7 +97,7 @@ export class ClassesService {
|
||||
}
|
||||
|
||||
async create(dto: CreateClassDto) {
|
||||
const { studentIds, teachers, ...classData } = dto;
|
||||
const { studentIds, teachers, dingUserIds, ...classData } = dto;
|
||||
|
||||
const cls = this.classRepo.create(classData);
|
||||
const saved = await this.classRepo.save(cls);
|
||||
@@ -117,9 +121,56 @@ export class ClassesService {
|
||||
await this.syncClassTeacherIds(saved.id);
|
||||
}
|
||||
|
||||
// batch import students by dingUserIds
|
||||
if (dingUserIds?.length) {
|
||||
await this.batchImportStudents(saved.id, dingUserIds);
|
||||
}
|
||||
|
||||
return this.findOne(saved.id);
|
||||
}
|
||||
|
||||
|
||||
async batchImportStudents(classId: number, dingUserIds: string[]): Promise<{ imported: number; skipped: number }> {
|
||||
const classEntity = await this.classRepo.findOne({ where: { id: classId } });
|
||||
if (!classEntity) throw new NotFoundException('班级不存在');
|
||||
|
||||
let imported = 0;
|
||||
let skipped = 0;
|
||||
|
||||
for (const dingUserId of dingUserIds) {
|
||||
let mapping = await this.studentDingMappingRepo.findOne({ where: { dingUserId } });
|
||||
let studentId: number;
|
||||
|
||||
if (mapping) {
|
||||
studentId = mapping.studentId;
|
||||
} else {
|
||||
const student = this.studentRepo.create({
|
||||
name: `dd_${dingUserId}`,
|
||||
status: 'active',
|
||||
departmentId: classEntity.departmentId ?? undefined,
|
||||
});
|
||||
const saved = await this.studentRepo.save(student);
|
||||
studentId = saved.id;
|
||||
mapping = this.studentDingMappingRepo.create({ dingUserId, studentId });
|
||||
await this.studentDingMappingRepo.save(mapping);
|
||||
}
|
||||
|
||||
const existing = await this.classStudentRepo.findOne({
|
||||
where: { classId, studentId },
|
||||
});
|
||||
if (existing) { skipped++; continue; }
|
||||
|
||||
await this.classStudentRepo.save(
|
||||
this.classStudentRepo.create({
|
||||
classId, studentId, status: 'active',
|
||||
joinDate: new Date().toISOString().slice(0, 10),
|
||||
}),
|
||||
);
|
||||
imported++;
|
||||
}
|
||||
|
||||
return { imported, skipped };
|
||||
}
|
||||
async update(id: number, dto: UpdateClassDto) {
|
||||
const cls = await this.classRepo.findOne({ where: { id } });
|
||||
if (!cls) throw new NotFoundException('班级不存在');
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IsOptional, IsString, IsNotEmpty, IsInt, IsArray, IsDateString, IsEnum } from 'class-validator';
|
||||
import { IsOptional, IsString, IsNotEmpty, IsInt, IsArray, IsDateString, IsEnum, ArrayNotEmpty } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ClassType, ClassStatus, TeacherRoleType } from '../../entities';
|
||||
|
||||
@@ -43,6 +43,11 @@ export class CreateClassDto {
|
||||
studentIds?: number[];
|
||||
|
||||
@IsOptional() @IsArray()
|
||||
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@IsOptional()
|
||||
dingUserIds?: string[];
|
||||
teachers?: Array<{ userId: number; roleType: string; subject?: string }>;
|
||||
}
|
||||
|
||||
@@ -135,3 +140,10 @@ export class QueryClassAttendanceSummaryDto {
|
||||
@IsOptional() @IsDateString()
|
||||
endDate?: string;
|
||||
}
|
||||
|
||||
export class BatchImportStudentsDto {
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@ArrayNotEmpty()
|
||||
dingUserIds: string[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user