feat: replace tenants with organization management

This commit is contained in:
2026-07-10 21:27:26 +08:00
parent 8ed1682b90
commit 8f0991a51f
49 changed files with 1292 additions and 698 deletions

View File

@@ -6,6 +6,7 @@ import { Class } from '../entities/class.entity';
import { ClassStudent } from '../entities/class-student.entity';
import { ClassTeacher } from '../entities/class-teacher.entity';
import { AttendanceRecord } from '../entities/attendance-record.entity';
import { Organization } from '../entities/organization.entity';
import { CreateStudentDto, UpdateStudentDto } from './dto/student.dto';
@Injectable()
@@ -16,6 +17,7 @@ export class StudentsService {
@InjectRepository(Class) private classRepo: Repository<Class>,
@InjectRepository(AttendanceRecord) private attendanceRepo: Repository<AttendanceRecord>,
@InjectRepository(ClassTeacher) private classTeacherRepo: Repository<ClassTeacher>,
@InjectRepository(Organization) private organizationRepo: Repository<Organization>,
) {}
async getAccessibleClassIds(userId: number, canManageAll = false): Promise<number[] | undefined> {
@@ -29,13 +31,13 @@ export class StudentsService {
name?: string;
status?: string;
includeArchived?: boolean;
tenantId?: number | string;
organizationId?: number | string;
},
accessibleClassIds?: number[],
) {
const where: FindOptionsWhere<Student> = {};
if (query?.name) where.name = Like(`%${query.name}%`);
if (query?.tenantId) where.tenantId = Number(query.tenantId);
if (query?.organizationId) where.organizationId = Number(query.organizationId);
if (query?.status) {
where.status = query.status;
} else if (!query?.includeArchived) {
@@ -50,7 +52,7 @@ export class StudentsService {
if (studentIds.length === 0) return [];
where.id = In(studentIds);
}
return this.repo.find({ where, order: { createdAt: 'DESC' }, relations: ['tenant'] });
return this.repo.find({ where, order: { createdAt: 'DESC' }, relations: ['organization'] });
}
async findOne(id: number) {
@@ -63,11 +65,13 @@ export class StudentsService {
}
async create(dto: CreateStudentDto) {
await this.assertActiveOrganization(dto.organizationId);
return this.repo.save(this.repo.create(dto));
}
async update(id: number, dto: UpdateStudentDto) {
await this.findOne(id);
if (dto.organizationId) await this.assertActiveOrganization(dto.organizationId);
await this.repo.update(id, dto);
return this.repo.findOne({ where: { id } });
}
@@ -127,7 +131,7 @@ export class StudentsService {
emergencyPhone?: string;
organization?: string;
supervisor?: string;
tenantId?: number;
organizationId?: number;
}[],
) {
let imported = 0;
@@ -151,9 +155,8 @@ export class StudentsService {
ethnicity: row.ethnicity || undefined,
emergencyContact: row.emergencyContact || undefined,
emergencyPhone: row.emergencyPhone || undefined,
organization: row.organization || undefined,
supervisor: row.supervisor || undefined,
tenantId: row.tenantId || undefined,
organizationId: row.organizationId || (await this.getHostOrganizationId()),
}),
);
imported++;
@@ -176,7 +179,7 @@ export class StudentsService {
emergencyPhone?: string;
organization?: string;
supervisor?: string;
tenantId?: number;
organizationId?: number;
}[],
) {
let matched = 0;
@@ -208,9 +211,8 @@ export class StudentsService {
| 'ethnicity'
| 'emergencyContact'
| 'emergencyPhone'
| 'organization'
| 'supervisor'
| 'tenantId'
| 'organizationId'
>
> = {};
if (row.name?.trim()) updates.name = row.name.trim();
@@ -220,9 +222,8 @@ export class StudentsService {
if (row.ethnicity) updates.ethnicity = row.ethnicity;
if (row.emergencyContact) updates.emergencyContact = row.emergencyContact;
if (row.emergencyPhone) updates.emergencyPhone = row.emergencyPhone;
if (row.organization) updates.organization = row.organization;
if (row.supervisor) updates.supervisor = row.supervisor;
if (row.tenantId) updates.tenantId = row.tenantId;
if (row.organizationId) updates.organizationId = row.organizationId;
await this.repo.update(student.id, updates as Partial<Student>);
matched++;
}
@@ -233,6 +234,19 @@ export class StudentsService {
};
}
private async assertActiveOrganization(id: number) {
const organization = await this.organizationRepo.findOne({ where: { id, status: 'active' } });
if (!organization) throw new BadRequestException('所属机构不存在或已归档');
}
private async getHostOrganizationId() {
const organization = await this.organizationRepo.findOne({
where: { isHost: true, status: 'active' },
});
if (!organization) throw new BadRequestException('尚未配置本机构');
return organization.id;
}
async compareClasses(studentId: number) {
const student = await this.repo.findOne({ where: { id: studentId } });
if (!student) throw new NotFoundException('学生不存在');