forked from wangziqi/gongxue-base
109 lines
4.3 KiB
TypeScript
109 lines
4.3 KiB
TypeScript
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { In, Repository } from 'typeorm';
|
|
import { AttendanceDevice, AttendanceDeviceStatus, Classroom } from '../entities';
|
|
import { CreateAttendanceDeviceDto, UpdateAttendanceDeviceDto } from './dto/attendance-device.dto';
|
|
|
|
@Injectable()
|
|
export class AttendanceDevicesService {
|
|
constructor(
|
|
@InjectRepository(AttendanceDevice)
|
|
private readonly repo: Repository<AttendanceDevice>,
|
|
@InjectRepository(Classroom)
|
|
private readonly classroomRepo: Repository<Classroom>,
|
|
) {}
|
|
|
|
private normalizeSn(sn: string): string {
|
|
return sn.trim();
|
|
}
|
|
|
|
private async assertClassroomExists(classroomId: number): Promise<void> {
|
|
const exists = await this.classroomRepo.exist({ where: { id: classroomId } });
|
|
if (!exists) throw new BadRequestException('绑定教室不存在');
|
|
}
|
|
|
|
async findAll(query?: { classroomId?: number; status?: AttendanceDeviceStatus | 'active' | 'disabled' }) {
|
|
const where: Record<string, unknown> = {};
|
|
if (query?.classroomId) where.classroomId = query.classroomId;
|
|
if (query?.status) where.status = query.status;
|
|
return this.repo.find({
|
|
where,
|
|
relations: ['classroom'],
|
|
order: { classroomId: 'ASC', deviceName: 'ASC' },
|
|
});
|
|
}
|
|
|
|
async findOne(id: number) {
|
|
const device = await this.repo.findOne({ where: { id }, relations: ['classroom'] });
|
|
if (!device) throw new NotFoundException('考勤机不存在');
|
|
return device;
|
|
}
|
|
|
|
async create(dto: CreateAttendanceDeviceDto) {
|
|
const deviceSn = this.normalizeSn(dto.deviceSn);
|
|
await this.assertClassroomExists(dto.classroomId);
|
|
const exists = await this.repo.findOne({ where: { deviceSn } });
|
|
if (exists) throw new BadRequestException(`SN ${deviceSn} 已绑定教室`);
|
|
const saved = await this.repo.save(
|
|
this.repo.create({
|
|
...dto,
|
|
deviceSn,
|
|
deviceName: dto.deviceName.trim(),
|
|
status: dto.status ?? AttendanceDeviceStatus.ACTIVE,
|
|
}),
|
|
);
|
|
return this.findOne(saved.id);
|
|
}
|
|
|
|
async update(id: number, dto: UpdateAttendanceDeviceDto) {
|
|
const device = await this.repo.findOne({ where: { id } });
|
|
if (!device) throw new NotFoundException('考勤机不存在');
|
|
const patch: Partial<AttendanceDevice> = { ...dto };
|
|
if (dto.classroomId != null) await this.assertClassroomExists(dto.classroomId);
|
|
if (dto.deviceSn != null) {
|
|
const deviceSn = this.normalizeSn(dto.deviceSn);
|
|
const exists = await this.repo.findOne({ where: { deviceSn } });
|
|
if (exists && exists.id !== id) throw new BadRequestException(`SN ${deviceSn} 已绑定教室`);
|
|
patch.deviceSn = deviceSn;
|
|
}
|
|
if (dto.deviceName != null) patch.deviceName = dto.deviceName.trim();
|
|
await this.repo.update(id, patch);
|
|
return this.findOne(id);
|
|
}
|
|
|
|
async remove(id: number) {
|
|
const device = await this.repo.findOne({ where: { id } });
|
|
if (!device) throw new NotFoundException('考勤机不存在');
|
|
if (device.status === AttendanceDeviceStatus.DISABLED) {
|
|
throw new BadRequestException('考勤机已停用');
|
|
}
|
|
await this.repo.update(id, { status: AttendanceDeviceStatus.DISABLED });
|
|
return { message: '已停用(绑定数据已保留)' };
|
|
}
|
|
|
|
async findActiveBySn(deviceSns: string[]) {
|
|
const sns = [...new Set(deviceSns.map((sn) => this.normalizeSn(sn)).filter(Boolean))];
|
|
if (sns.length === 0) return new Map<string, AttendanceDevice>();
|
|
const devices = await this.repo.find({
|
|
where: { deviceSn: In(sns), status: AttendanceDeviceStatus.ACTIVE },
|
|
relations: ['classroom'],
|
|
});
|
|
return new Map(devices.map((device) => [device.deviceSn, device]));
|
|
}
|
|
|
|
async findActiveByClassroomIds(classroomIds: number[]) {
|
|
const ids = [...new Set(classroomIds.filter((id) => Number.isFinite(id)))];
|
|
if (ids.length === 0) return new Map<number, AttendanceDevice>();
|
|
const devices = await this.repo.find({
|
|
where: { classroomId: In(ids), status: AttendanceDeviceStatus.ACTIVE },
|
|
relations: ['classroom'],
|
|
order: { id: 'ASC' },
|
|
});
|
|
const result = new Map<number, AttendanceDevice>();
|
|
for (const device of devices) {
|
|
if (!result.has(device.classroomId)) result.set(device.classroomId, device);
|
|
}
|
|
return result;
|
|
}
|
|
}
|