diff --git a/apps/server/src/occupancies/dto/occupancy.dto.ts b/apps/server/src/occupancies/dto/occupancy.dto.ts index d430610..55cd56a 100644 --- a/apps/server/src/occupancies/dto/occupancy.dto.ts +++ b/apps/server/src/occupancies/dto/occupancy.dto.ts @@ -25,6 +25,13 @@ export class CheckInDto { @IsOptional() @IsInt() tenantId?: number; + @IsOptional() + @IsInt() + bedId?: number; // 后续改 required + + @IsOptional() + @IsInt() + lockerId?: number; } export class CheckOutDto { @@ -51,6 +58,13 @@ export class TransferRoomDto { @IsString() oldBillingEndDate?: string; // 旧房计费截止日,默认=transferDate + @IsOptional() + @IsInt() + newBedId?: number; + + @IsOptional() + @IsInt() + newLockerId?: number; @IsOptional() @IsString() newBillingStartDate?: string; // 新房计费起始日,默认=transferDate次日 diff --git a/apps/server/src/occupancies/occupancies.module.ts b/apps/server/src/occupancies/occupancies.module.ts index efad34f..a8f1bb5 100644 --- a/apps/server/src/occupancies/occupancies.module.ts +++ b/apps/server/src/occupancies/occupancies.module.ts @@ -4,6 +4,8 @@ import { Occupancy } from '../entities/occupancy.entity'; import { Room } from '../entities/room.entity'; import { Student } from '../entities/student.entity'; import { Deposit } from '../entities/deposit.entity'; +import { Bed } from '../entities/bed.entity'; +import { Locker } from '../entities/locker.entity'; import { OccupanciesService } from './occupancies.service'; import { OccupanciesController } from './occupancies.controller'; import { OperationLogsModule } from '../operation-logs/operation-logs.module'; @@ -11,7 +13,7 @@ import { NotificationsModule } from '../notifications/notifications.module'; import { CommonModule } from '../common/common.module'; @Module({ - imports: [TypeOrmModule.forFeature([Occupancy, Room, Student, Deposit]), OperationLogsModule, NotificationsModule, CommonModule], + imports: [TypeOrmModule.forFeature([Occupancy, Room, Student, Deposit, Bed, Locker]), OperationLogsModule, NotificationsModule, CommonModule], controllers: [OccupanciesController], providers: [OccupanciesService], exports: [OccupanciesService], diff --git a/apps/server/src/occupancies/occupancies.service.ts b/apps/server/src/occupancies/occupancies.service.ts index 83aed7d..08e7914 100644 --- a/apps/server/src/occupancies/occupancies.service.ts +++ b/apps/server/src/occupancies/occupancies.service.ts @@ -12,6 +12,8 @@ import { import { Occupancy } from '../entities/occupancy.entity'; import { Room } from '../entities/room.entity'; 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 { CheckInDto, CheckOutDto, TransferRoomDto } from './dto/occupancy.dto'; import { RoomsService } from '../rooms/rooms.service'; @@ -24,6 +26,8 @@ export class OccupanciesService { @InjectRepository(Room) private roomRepo: Repository, @InjectRepository(Student) private studentRepo: Repository, @InjectRepository(Deposit) private depositRepo: Repository, + @InjectRepository(Bed) private bedRepo: Repository, + @InjectRepository(Locker) private lockerRepo: Repository, private dataSource: DataSource, private readonly scope: CampusScope, ) {} @@ -33,6 +37,8 @@ export class OccupanciesService { .createQueryBuilder('o') .leftJoinAndSelect('o.student', 'student') .leftJoinAndSelect('o.room', 'room') + .leftJoinAndSelect('o.bed', 'bed') + .leftJoinAndSelect('o.locker', 'locker') .orderBy('o.checkInDate', 'DESC'); const scopeIds = await this.scope.getScopeDepartmentIds(); if (scopeIds) { @@ -66,6 +72,20 @@ export class OccupanciesService { ); } + // 床位校验 + if (dto.bedId) { + const bed = await this.bedRepo.findOne({ where: { id: dto.bedId, roomId: dto.roomId } }); + if (!bed) throw new BadRequestException('床位不存在或不属于该宿舍'); + if (bed.status !== 'available') throw new BadRequestException('该床位已被占用或维修中'); + } + + // 柜子校验 + if (dto.lockerId) { + 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('该柜子已被占用或维修中'); + } + const occ = this.repo.create({ studentId: dto.studentId, roomId: dto.roomId, @@ -74,10 +94,20 @@ export class OccupanciesService { rentalType: dto.rentalType, tenantId: dto.tenantId, notes: dto.notes, + bedId: dto.bedId, + lockerId: dto.lockerId, }); occ.departmentId = room.departmentId; const saved = await this.repo.save(occ); + // 更新床位/柜子状态 + if (dto.bedId) { + await this.bedRepo.update(dto.bedId, { status: 'occupied' }); + } + if (dto.lockerId) { + await this.lockerRepo.update(dto.lockerId, { status: 'occupied' }); + } + // 首位入住者确定房间性别 if (student.gender && !room.gender) { await this.roomRepo.update(room.id, { gender: student.gender }); @@ -100,6 +130,14 @@ export class OccupanciesService { occ.checkOutReason = dto.checkOutReason || ''; await this.repo.save(occ); + // 释放床位/柜子 + if (occ.bedId) { + await this.bedRepo.update(occ.bedId, { status: 'available' }); + } + if (occ.lockerId) { + await this.lockerRepo.update(occ.lockerId, { status: 'available' }); + } + // 更新宿舍状态 await this.roomRepo.update(occ.roomId, { status: 'available' }); @@ -128,6 +166,13 @@ export class OccupanciesService { oldOcc.billingEndDate = dto.oldBillingEndDate || dto.transferDate; oldOcc.checkOutReason = dto.reason || '换房'; await runner.manager.save(oldOcc); + // 释放旧床位/柜子 + if (oldOcc.bedId) { + await runner.manager.update(Bed, oldOcc.bedId, { status: 'available' }); + } + if (oldOcc.lockerId) { + await runner.manager.update(Locker, oldOcc.lockerId, { status: 'available' }); + } await runner.manager.update(Room, oldOcc.roomId, { status: 'available' }); // 旧房如果已无在住人员,重置性别 const oldRemaining = await runner.manager.count(Occupancy, { @@ -151,6 +196,18 @@ export class OccupanciesService { throw new BadRequestException(`目标宿舍为${newRoom.gender}生寝室,无法换入`); } + // 新床位校验 + if (dto.newBedId) { + 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 } }); + if (!newLocker) throw new BadRequestException('目标柜子不存在或不属于目标宿舍'); + if (newLocker.status !== 'available') throw new BadRequestException('目标柜子已被占用'); + } + // 计算新房计费起始日:默认为换房日期次日 const transferDate = new Date(dto.transferDate); const nextDay = new Date(transferDate); @@ -166,9 +223,19 @@ export class OccupanciesService { rentalType: oldOcc.rentalType, tenantId: oldOcc.tenantId, notes: `从${oldOcc.roomId}号房换入`, + bedId: dto.newBedId, + lockerId: dto.newLockerId, }); await runner.manager.save(newOcc); + // 更新新床位/柜子状态 + if (dto.newBedId) { + await runner.manager.update(Bed, dto.newBedId, { status: 'occupied' }); + } + if (dto.newLockerId) { + await runner.manager.update(Locker, dto.newLockerId, { status: 'occupied' }); + } + // 首位入住者确定新房性别 if (student?.gender && !newRoom.gender) { await runner.manager.update(Room, newRoom.id, { gender: student.gender });