feat: 集成 AI 对话与只读查询工具

This commit is contained in:
2026-07-23 14:24:40 +08:00
parent 302dbe0621
commit f3b59935d6
52 changed files with 4942 additions and 4 deletions

View File

@@ -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('宿舍不存在');