feat: 集成 AI 对话与只读查询工具
This commit is contained in:
@@ -21,6 +21,25 @@ import { CreateLockerDto, UpdateLockerDto, BatchCreateLockerDto } from './dto/lo
|
||||
import { RoomInspectionsService } from './room-inspections.service';
|
||||
import { occupancyWhereOnDate } from './room-occupancy-date';
|
||||
|
||||
interface AgentRoomRow {
|
||||
id: string | number;
|
||||
roomNumber: string;
|
||||
building: string | null;
|
||||
floor: string | number | null;
|
||||
capacity: string | number;
|
||||
roomType: string | null;
|
||||
status: string;
|
||||
occupiedBeds: string | number;
|
||||
}
|
||||
|
||||
interface AgentRoomOccupancyRow {
|
||||
roomId: string | number;
|
||||
roomNumber: string;
|
||||
building: string | null;
|
||||
capacity: string | number;
|
||||
occupiedBeds: string | number;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class RoomsService {
|
||||
constructor(
|
||||
@@ -85,6 +104,59 @@ export class RoomsService {
|
||||
return this.repo.find({ where, order: { roomNumber: 'ASC' } });
|
||||
}
|
||||
|
||||
async agentSearchRooms(query: { keyword?: string; building?: string; status?: string; limit?: number }) {
|
||||
const qb = this.repo
|
||||
.createQueryBuilder('room')
|
||||
.leftJoin(
|
||||
Occupancy,
|
||||
'occupancy',
|
||||
'occupancy.roomId = room.id AND occupancy.checkOutDate IS NULL',
|
||||
)
|
||||
.select('room.id', 'id')
|
||||
.addSelect('room.roomNumber', 'roomNumber')
|
||||
.addSelect('room.building', 'building')
|
||||
.addSelect('room.floor', 'floor')
|
||||
.addSelect('room.capacity', 'capacity')
|
||||
.addSelect('room.roomType', 'roomType')
|
||||
.addSelect('room.status', 'status')
|
||||
.addSelect('COUNT(occupancy.id)', 'occupiedBeds')
|
||||
.where('room.status != :archived', { archived: 'archived' });
|
||||
if (query.keyword) qb.andWhere('room.roomNumber LIKE :keyword', { keyword: `%${query.keyword}%` });
|
||||
if (query.building) qb.andWhere('room.building = :building', { building: query.building });
|
||||
if (query.status) qb.andWhere('room.status = :status', { status: query.status });
|
||||
const rows = await qb.groupBy('room.id').orderBy('room.roomNumber', 'ASC').limit(query.limit ?? 20).getRawMany<AgentRoomRow>();
|
||||
return rows.map((row) => ({
|
||||
...row,
|
||||
id: Number(row.id), floor: row.floor == null ? null : Number(row.floor),
|
||||
capacity: Number(row.capacity), occupiedBeds: Number(row.occupiedBeds || 0),
|
||||
}));
|
||||
}
|
||||
|
||||
async agentGetRoomOccupancySummary(query: { date?: string; building?: string; limit?: number }) {
|
||||
const targetDate = query.date || this.getChinaDate(new Date());
|
||||
const qb = this.repo
|
||||
.createQueryBuilder('room')
|
||||
.leftJoin(
|
||||
Occupancy,
|
||||
'occupancy',
|
||||
'occupancy.roomId = room.id AND occupancy.checkInDate <= :targetDate AND (occupancy.checkOutDate IS NULL OR occupancy.checkOutDate > :targetDate)',
|
||||
{ targetDate },
|
||||
)
|
||||
.select('room.id', 'roomId')
|
||||
.addSelect('room.roomNumber', 'roomNumber')
|
||||
.addSelect('room.building', 'building')
|
||||
.addSelect('room.capacity', 'capacity')
|
||||
.addSelect('COUNT(occupancy.id)', 'occupiedBeds')
|
||||
.where('room.status != :archived', { archived: 'archived' });
|
||||
if (query.building) qb.andWhere('room.building = :building', { building: query.building });
|
||||
const rows = await qb.groupBy('room.id').orderBy('room.roomNumber', 'ASC').limit(query.limit ?? 50).getRawMany<AgentRoomOccupancyRow>();
|
||||
return rows.map((row) => {
|
||||
const capacity = Number(row.capacity || 0);
|
||||
const occupiedBeds = Number(row.occupiedBeds || 0);
|
||||
return { date: targetDate, roomId: Number(row.roomId), roomNumber: row.roomNumber, building: row.building, capacity, occupiedBeds, availableBeds: Math.max(0, capacity - occupiedBeds) };
|
||||
});
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const room = await this.repo.findOne({ where: { id } });
|
||||
if (!room) throw new NotFoundException('宿舍不存在');
|
||||
|
||||
Reference in New Issue
Block a user