feat: 重构各业务模块管理页面与服务

This commit is contained in:
2026-08-05 17:12:00 +08:00
parent 80e6fccf05
commit fd39e1686a
163 changed files with 18409 additions and 13449 deletions

View File

@@ -67,16 +67,21 @@ export class DepositsService {
.leftJoin(Deposit, 'deposit', 'deposit.student_id = student.id AND deposit.status != :archived', {
archived: 'archived',
})
.select('student.id', 'studentId')
.addSelect('student.name', 'studentName')
.addSelect('student.studentNo', 'studentNo')
.addSelect('room.id', 'roomId')
.addSelect('room.roomNumber', 'roomNumber')
.addSelect('room.building', 'building')
.addSelect('room.roomType', 'roomType')
.addSelect('room.capacity', 'capacity')
.addSelect('deposit.amount', 'depositAmount')
.where('o.status = :activeStatus', { activeStatus: 'active' })
.select('student.id', 'studentId');
const eligibleSelects = [
['student.name', 'studentName'],
['student.studentNo', 'studentNo'],
['room.id', 'roomId'],
['room.roomNumber', 'roomNumber'],
['room.building', 'building'],
['room.roomType', 'roomType'],
['room.capacity', 'capacity'],
['deposit.amount', 'depositAmount'],
] as const;
for (const [column, alias] of eligibleSelects) {
qb.addSelect(column, alias);
}
qb.where('o.status = :activeStatus', { activeStatus: 'active' })
.andWhere('o.checkOutDate IS NULL')
.andWhere('student.status = :studentStatus', { studentStatus: 'active' })
.orderBy('room.building', 'ASC')
@@ -167,15 +172,20 @@ export class DepositsService {
const qb = this.repo
.createQueryBuilder('d')
.leftJoin('d.student', 'student')
.select('d.id', 'id')
.addSelect('student.name', 'studentName')
.addSelect('student.studentNo', 'studentNo')
.addSelect('d.amount', 'amount')
.addSelect('d.status', 'status')
.addSelect('d.paidDate', 'paidDate')
.addSelect('d.refundAmount', 'refundAmount')
.addSelect('d.refundDate', 'refundDate')
.where('d.status != :archived', { archived: 'archived' });
.select('d.id', 'id');
const depositSelects = [
['student.name', 'studentName'],
['student.studentNo', 'studentNo'],
['d.amount', 'amount'],
['d.status', 'status'],
['d.paidDate', 'paidDate'],
['d.refundAmount', 'refundAmount'],
['d.refundDate', 'refundDate'],
] as const;
for (const [column, alias] of depositSelects) {
qb.addSelect(column, alias);
}
qb.where('d.status != :archived', { archived: 'archived' });
if (query?.keyword) {
qb.andWhere(
'(student.name LIKE :keyword OR student.studentNo LIKE :keyword)',
@@ -226,8 +236,8 @@ export class DepositsService {
existing.paidDate = dto.paidDate;
existing.status = 'paid';
existing.recordedBy = userId ?? null;
existing.refundDate = null as unknown as string;
existing.refundAmount = null as unknown as number;
existing.refundDate = null;
existing.refundAmount = null;
existing.refundedBy = null;
existing.refundedAt = null;
if (dto.notes) existing.notes = dto.notes;
@@ -309,6 +319,28 @@ export class DepositsService {
return { message: '已归档' };
}
async purge(id: number) {
const deposit = await this.repo.findOne({ where: { id } });
if (!deposit) throw new NotFoundException('押金记录不存在');
if (deposit.status !== 'archived') {
throw new BadRequestException('仅已归档押金可以永久删除,请先归档');
}
if (Number(deposit.refundAmount || 0) > 0) {
throw new BadRequestException('该押金已有退款金额,无法永久删除');
}
if (Number(deposit.deductionAmount || 0) > 0) {
throw new BadRequestException('该押金已有抵扣金额,无法永久删除');
}
const paidInstallments = await this.installmentRepo.count({
where: { depositId: id, status: 'paid' },
});
if (paidInstallments > 0) {
throw new BadRequestException('该押金存在已支付分期,无法永久删除');
}
await this.repo.delete(id);
return { message: '已永久删除押金(不可恢复)' };
}
async getStats() {
const qb = this.repo
.createQueryBuilder('d')