feat: 增加宿舍查寝功能
This commit is contained in:
@@ -19,6 +19,7 @@ import { Locker } from '../entities/locker.entity';
|
||||
import { CreateRoomDto, UpdateRoomDto } from './dto/room.dto';
|
||||
import { CreateBedDto, UpdateBedDto, BatchCreateBedDto } from './dto/bed.dto';
|
||||
import { CreateLockerDto, UpdateLockerDto, BatchCreateLockerDto } from './dto/locker.dto';
|
||||
import { RoomInspectionsService } from './room-inspections.service';
|
||||
|
||||
@Injectable()
|
||||
export class RoomsService {
|
||||
@@ -29,6 +30,7 @@ export class RoomsService {
|
||||
@InjectRepository(Bed) private bedRepo: Repository<Bed>,
|
||||
@InjectRepository(Locker) private lockerRepo: Repository<Locker>,
|
||||
private dataSource: DataSource,
|
||||
private readonly inspectionsService: RoomInspectionsService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -235,7 +237,7 @@ export class RoomsService {
|
||||
async getRoomVisual(asOf?: string) {
|
||||
// asOf 为空 = 实时(今天)。带 asOf = 还原该日期结束时的历史入住快照。
|
||||
const isHistorical = !!asOf;
|
||||
const targetDate = asOf || new Date().toISOString().slice(0, 10);
|
||||
const targetDate = asOf || this.getChinaDate(new Date());
|
||||
|
||||
// 实时视图排除已归档房间;历史视图不排除——当时有人住的房间即使现在已归档也应显示。
|
||||
const rooms = await this.repo.find({
|
||||
@@ -244,13 +246,11 @@ export class RoomsService {
|
||||
});
|
||||
|
||||
const occupancies = await this.occRepo.find({
|
||||
where: isHistorical
|
||||
? [
|
||||
{ checkInDate: LessThanOrEqual(targetDate), checkOutDate: IsNull() },
|
||||
{ checkInDate: LessThanOrEqual(targetDate), checkOutDate: MoreThanOrEqual(targetDate) },
|
||||
]
|
||||
: { checkOutDate: IsNull() },
|
||||
relations: ['student', 'student.organization', 'responsibleOrganization'],
|
||||
where: [
|
||||
{ checkInDate: LessThanOrEqual(targetDate), checkOutDate: IsNull() },
|
||||
{ checkInDate: LessThanOrEqual(targetDate), checkOutDate: MoreThanOrEqual(targetDate) },
|
||||
],
|
||||
relations: ['student', 'student.organization', 'responsibleOrganization', 'bed'],
|
||||
order: { checkInDate: 'ASC' },
|
||||
});
|
||||
|
||||
@@ -264,7 +264,10 @@ export class RoomsService {
|
||||
const days = Math.max(1, Math.ceil((refTime - checkIn.getTime()) / (1000 * 60 * 60 * 24)));
|
||||
occMap.get(occ.roomId)!.push({
|
||||
studentId: occ.studentId,
|
||||
occupancyId: occ.id,
|
||||
studentName: occ.student?.name || '未知',
|
||||
bedId: occ.bedId ?? null,
|
||||
bedNumber: occ.bed?.bedNumber || null,
|
||||
checkInDate: occ.checkInDate,
|
||||
billingStartDate: occ.billingStartDate,
|
||||
days,
|
||||
@@ -296,10 +299,19 @@ export class RoomsService {
|
||||
if (bed.status === 'occupied') entry.occupied++;
|
||||
}
|
||||
|
||||
const inspectionMap = await this.inspectionsService.getByRoomsAndDate(
|
||||
visibleRooms.map((room) => room.id),
|
||||
targetDate,
|
||||
);
|
||||
|
||||
return {
|
||||
buildings,
|
||||
rooms: visibleRooms.map((room) => {
|
||||
const occ = occMap.get(room.id) || [];
|
||||
const inspection = inspectionMap.get(room.id);
|
||||
const inspectionByOccupancyId = new Map(
|
||||
(inspection?.details || []).map((detail) => [detail.occupancyId, detail.status]),
|
||||
);
|
||||
const orgs = [...new Set(occ.map((o: any) => o.organization).filter(Boolean))];
|
||||
let orgLabel: string | null = null;
|
||||
if (orgs.length > 0 && occ.length > 0) {
|
||||
@@ -322,7 +334,19 @@ export class RoomsService {
|
||||
currentCount: occ.length,
|
||||
totalBeds: bedMap.get(room.id)?.total ?? 0,
|
||||
occupiedBeds: bedMap.get(room.id)?.occupied ?? 0,
|
||||
occupants: occ,
|
||||
occupants: occ.map((occupant) => ({
|
||||
...occupant,
|
||||
inspectionStatus: inspectionByOccupancyId.get(occupant.occupancyId) || null,
|
||||
})),
|
||||
inspection: inspection
|
||||
? {
|
||||
submitted: true,
|
||||
inspectorId: inspection.inspectorId,
|
||||
inspectorName: inspection.inspectorName,
|
||||
source: inspection.source,
|
||||
submittedAt: inspection.submittedAt,
|
||||
}
|
||||
: { submitted: false },
|
||||
orgLabel,
|
||||
organizationColor,
|
||||
organizationIds,
|
||||
@@ -346,6 +370,15 @@ export class RoomsService {
|
||||
};
|
||||
}
|
||||
|
||||
private getChinaDate(now: Date): string {
|
||||
return new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone: 'Asia/Shanghai',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
}).format(now);
|
||||
}
|
||||
|
||||
async batchImport(
|
||||
rows: {
|
||||
roomNumber: string;
|
||||
|
||||
Reference in New Issue
Block a user