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

@@ -15,10 +15,11 @@ import { Student } from '../entities/student.entity';
import { Bed } from '../entities/bed.entity';
import { Locker } from '../entities/locker.entity';
import { Deposit } from '../entities/deposit.entity';
import { Organization } from '../entities/organization.entity';
import { uuidV7 } from '../common/uuid-v7';
import { CheckInDto, CheckOutDto, TransferRoomDto } from './dto/occupancy.dto';
import { RoomsService } from '../rooms/rooms.service';
@Injectable()
export class OccupanciesService {
constructor(
@@ -28,6 +29,7 @@ export class OccupanciesService {
@InjectRepository(Deposit) private depositRepo: Repository<Deposit>,
@InjectRepository(Bed) private bedRepo: Repository<Bed>,
@InjectRepository(Locker) private lockerRepo: Repository<Locker>,
@InjectRepository(Organization) private organizationRepo: Repository<Organization>,
private dataSource: DataSource,
) {}
@@ -38,6 +40,7 @@ export class OccupanciesService {
.leftJoinAndSelect('o.room', 'room')
.leftJoinAndSelect('o.bed', 'bed')
.leftJoinAndSelect('o.locker', 'locker')
.leftJoinAndSelect('o.responsibleOrganization', 'responsibleOrganization')
.orderBy('o.checkInDate', 'DESC');
if (query?.roomId) qb.andWhere('o.roomId = :roomId', { roomId: query.roomId });
if (query?.studentId) qb.andWhere('o.studentId = :studentId', { studentId: query.studentId });
@@ -76,7 +79,9 @@ export class OccupanciesService {
// 柜子校验
if (dto.lockerId) {
const locker = await this.lockerRepo.findOne({ where: { id: dto.lockerId, roomId: dto.roomId } });
const locker = await this.lockerRepo.findOne({
where: { id: dto.lockerId, roomId: dto.roomId },
});
if (!locker) throw new BadRequestException('柜子不存在或不属于该宿舍');
if (locker.status !== 'available') throw new BadRequestException('该柜子已被占用或维修中');
}
@@ -86,8 +91,8 @@ export class OccupanciesService {
roomId: dto.roomId,
checkInDate: dto.checkInDate,
billingStartDate: dto.billingStartDate || dto.checkInDate,
rentalType: dto.rentalType,
tenantId: dto.tenantId,
stayType: dto.stayType,
responsibleOrganizationId: dto.responsibleOrganizationId ?? student.organizationId,
notes: dto.notes,
bedId: dto.bedId,
lockerId: dto.lockerId,
@@ -192,12 +197,16 @@ export class OccupanciesService {
// 新床位校验
if (dto.newBedId) {
const newBed = await runner.manager.findOne(Bed, { where: { id: dto.newBedId, roomId: dto.newRoomId } });
const newBed = await runner.manager.findOne(Bed, {
where: { id: dto.newBedId, roomId: dto.newRoomId },
});
if (!newBed) throw new BadRequestException('目标床位不存在或不属于目标宿舍');
if (newBed.status !== 'available') throw new BadRequestException('目标床位已被占用');
}
if (dto.newLockerId) {
const newLocker = await runner.manager.findOne(Locker, { where: { id: dto.newLockerId, roomId: dto.newRoomId } });
const newLocker = await runner.manager.findOne(Locker, {
where: { id: dto.newLockerId, roomId: dto.newRoomId },
});
if (!newLocker) throw new BadRequestException('目标柜子不存在或不属于目标宿舍');
if (newLocker.status !== 'available') throw new BadRequestException('目标柜子已被占用');
}
@@ -214,8 +223,8 @@ export class OccupanciesService {
roomId: dto.newRoomId,
checkInDate: dto.transferDate,
billingStartDate: dto.newBillingStartDate || defaultBillingStart,
rentalType: oldOcc.rentalType,
tenantId: oldOcc.tenantId,
stayType: oldOcc.stayType,
responsibleOrganizationId: oldOcc.responsibleOrganizationId,
notes: `${oldOcc.roomId}号房换入`,
bedId: dto.newBedId,
lockerId: dto.newLockerId,
@@ -339,7 +348,8 @@ export class OccupanciesService {
}
// 释放床位/柜子
if (occ.bedId) await runner.manager.update(Bed, occ.bedId, { status: 'available' });
if (occ.lockerId) await runner.manager.update(Locker, occ.lockerId, { status: 'available' });
if (occ.lockerId)
await runner.manager.update(Locker, occ.lockerId, { status: 'available' });
success++;
}
await runner.commitTransaction();
@@ -395,7 +405,23 @@ export class OccupanciesService {
}
try {
// 1. 查找或创建学生
// 1. 解析所属机构;未填写时默认本机构
let organization = row.organization?.trim()
? await this.organizationRepo.findOne({ where: { name: row.organization.trim() } })
: await this.organizationRepo.findOne({ where: { isHost: true, status: 'active' } });
if (!organization && row.organization?.trim()) {
organization = await this.organizationRepo.save(
this.organizationRepo.create({
publicId: uuidV7(),
code: `ORG_${Date.now()}_${i}`,
name: row.organization.trim(),
isHost: false,
status: 'active',
}),
);
}
if (!organization) throw new BadRequestException('尚未配置本机构');
let student = await this.studentRepo.findOne({ where: { name: row.name.trim() } });
if (!student) {
student = await this.studentRepo.save(
@@ -407,7 +433,7 @@ export class OccupanciesService {
ethnicity: row.ethnicity?.trim() || undefined,
emergencyContact: row.emergencyContact?.trim() || undefined,
emergencyPhone: row.emergencyPhone?.trim() || undefined,
organization: row.organization?.trim() || undefined,
organizationId: organization.id,
supervisor: row.supervisor?.trim() || undefined,
}),
);
@@ -422,8 +448,7 @@ export class OccupanciesService {
updates.emergencyContact = row.emergencyContact.trim();
if (!student.emergencyPhone && row.emergencyPhone?.trim())
updates.emergencyPhone = row.emergencyPhone.trim();
if (!student.organization && row.organization?.trim())
updates.organization = row.organization.trim();
if (!student.organizationId) updates.organizationId = organization.id;
if (!student.supervisor && row.supervisor?.trim())
updates.supervisor = row.supervisor.trim();
if (Object.keys(updates).length > 0) {
@@ -486,6 +511,7 @@ export class OccupanciesService {
roomId: room.id,
checkInDate,
billingStartDate: checkInDate,
responsibleOrganizationId: student.organizationId || organization.id,
};
// 如果有退宿日期,直接记录
if (row.checkOutDate?.trim()) {