188 lines
6.7 KiB
TypeScript
188 lines
6.7 KiB
TypeScript
import {
|
|
Controller,
|
|
Get,
|
|
Post,
|
|
Put,
|
|
Delete,
|
|
Body,
|
|
Param,
|
|
Query,
|
|
UseGuards,
|
|
Request,
|
|
ParseIntPipe,
|
|
} from '@nestjs/common';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import { Repository } from 'typeorm';
|
|
import { Student } from '../entities/student.entity';
|
|
import { DepositsService } from './deposits.service';
|
|
import { NotificationsService } from '../notifications/notifications.service';
|
|
import {
|
|
BatchCreateDepositDto,
|
|
CreateDepositDto,
|
|
CreateDepositInstallmentDto,
|
|
RefundDepositDto,
|
|
UpdateDepositInstallmentDto,
|
|
} from './dto/deposit.dto';
|
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
import { OperationLogsService } from '../operation-logs/operation-logs.service';
|
|
import { logAudit } from '../common/with-audit-log';
|
|
import { RequirePermission } from '../auth/decorators/permission.decorator';
|
|
|
|
@UseGuards(JwtAuthGuard)
|
|
@Controller('deposits')
|
|
export class DepositsController {
|
|
constructor(
|
|
private service: DepositsService,
|
|
private logService: OperationLogsService,
|
|
private readonly notificationsService: NotificationsService,
|
|
@InjectRepository(Student) private studentRepo: Repository<Student>,
|
|
) {}
|
|
|
|
@Get('student-lookups')
|
|
@RequirePermission('deposit:create')
|
|
getStudentLookups() {
|
|
return this.service.getStudentLookups();
|
|
}
|
|
|
|
|
|
@Get('eligible-students')
|
|
@RequirePermission('deposit:view')
|
|
getEligibleStudents(@Query('roomType') roomType?: string) {
|
|
return this.service.getEligibleStudents(roomType || undefined);
|
|
}
|
|
|
|
@Get()
|
|
@RequirePermission('deposit:view')
|
|
findAll(
|
|
@Query('studentId', new ParseIntPipe({ optional: true })) studentId?: number,
|
|
@Query('status') status?: string,
|
|
) {
|
|
return this.service.findAll({
|
|
studentId,
|
|
status: status || undefined,
|
|
});
|
|
}
|
|
|
|
@Get('stats')
|
|
@RequirePermission('deposit:view')
|
|
getStats() {
|
|
return this.service.getStats();
|
|
}
|
|
|
|
@Get(':id')
|
|
@RequirePermission('deposit:view')
|
|
findOne(@Param('id', ParseIntPipe) id: number) {
|
|
return this.service.findOne(id);
|
|
}
|
|
|
|
@Post()
|
|
@RequirePermission('deposit:create')
|
|
async create(@Body() dto: CreateDepositDto, @Request() req: any) {
|
|
const result = await this.service.create(dto, req.user?.id);
|
|
await logAudit(this.logService, req, {
|
|
module: '押金管理', action: '收取押金', targetId: result.id, targetType: 'deposit', detail: `学生${dto.studentId} ¥${dto.amount}`,
|
|
});
|
|
await this.notifyDeposit(dto.studentId, 'deposit_due', '押金待缴', `您有一笔押金待缴纳,金额: ¥${dto.amount}`);
|
|
return result;
|
|
}
|
|
|
|
|
|
@Post('batch')
|
|
@RequirePermission('deposit:create')
|
|
async batchCreate(@Body() dto: BatchCreateDepositDto, @Request() req: any) {
|
|
const result = await this.service.batchCreate(dto, req.user?.id);
|
|
await logAudit(this.logService, req, {
|
|
module: '押金管理', action: '批量收取押金', targetType: 'deposit', detail: `批量收取${result.count}人,每人¥${result.amount}${dto.roomType ? `,房型:${dto.roomType}` : ''}${dto.notes ? `,备注:${dto.notes}` : ''}`,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Post(':id/installments')
|
|
@RequirePermission('deposit:edit')
|
|
async addInstallment(
|
|
@Param('id', ParseIntPipe) id: number,
|
|
@Body() body: CreateDepositInstallmentDto,
|
|
@Request() req: { user?: { id: number; username: string }; headers?: Record<string, string> },
|
|
) {
|
|
const result = await this.service.addInstallment(id, body.amount, body.dueDate);
|
|
await logAudit(this.logService, req, {
|
|
module: '押金管理', action: '新增分期', targetId: result.id, targetType: 'deposit-installment', detail: `押金${id} 新增分期 ¥${result.amount}`,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Put('installments/:installmentId')
|
|
@RequirePermission('deposit:edit')
|
|
async updateInstallment(
|
|
@Param('installmentId', ParseIntPipe) installmentId: number,
|
|
@Body() body: UpdateDepositInstallmentDto,
|
|
@Request() req: { user?: { id: number; username: string }; headers?: Record<string, string> },
|
|
) {
|
|
const result = await this.service.updateInstallment(installmentId, body);
|
|
await logAudit(this.logService, req, {
|
|
module: '押金管理', action: '更新分期', targetId: installmentId, targetType: 'deposit-installment', detail: `更新分期${installmentId}, 状态:${result.status ?? '-'}, 实付日:${result.paidDate ?? '-'}`,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Delete('installments/:installmentId')
|
|
@RequirePermission('deposit:delete')
|
|
async deleteInstallment(
|
|
@Param('installmentId', ParseIntPipe) installmentId: number,
|
|
@Request() req: { user?: { id: number; username: string }; headers?: Record<string, string> },
|
|
) {
|
|
const result = await this.service.deleteInstallment(installmentId);
|
|
await logAudit(this.logService, req, {
|
|
module: '押金管理', action: '归档分期', targetId: installmentId, targetType: 'deposit-installment', detail: `归档分期${installmentId}`,
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Put(':id/refund')
|
|
@RequirePermission('deposit:refund')
|
|
async refund(@Param('id', ParseIntPipe) id: number, @Body() dto: RefundDepositDto, @Request() req: any) {
|
|
const result = await this.service.refund(id, dto, req.user?.id);
|
|
await logAudit(this.logService, req, {
|
|
module: '押金管理', action: '退还押金', targetId: id, targetType: 'deposit', detail: `退还全部可用押金 ¥${result.refundAmount}`,
|
|
});
|
|
await this.notifyDeposit(result.studentId, 'deposit_refunded', '押金已退还', `您的剩余押金已全部退还,金额: ¥${result.refundAmount}`);
|
|
return result;
|
|
}
|
|
|
|
private async notifyDeposit(
|
|
studentId: number,
|
|
type: 'deposit_due' | 'deposit_refunded',
|
|
title: string,
|
|
content: string,
|
|
): Promise<void> {
|
|
try {
|
|
const student = await this.studentRepo.findOne({ where: { id: studentId } });
|
|
if (student?.userId) {
|
|
void this.notificationsService.create({ recipientIds: [student.userId], type, title, content });
|
|
}
|
|
} catch {
|
|
// 通知失败不影响主流程
|
|
}
|
|
}
|
|
|
|
@Delete(':id')
|
|
@RequirePermission('deposit:delete')
|
|
async remove(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
|
|
const result = await this.service.remove(id);
|
|
await logAudit(this.logService, req, {
|
|
module: '押金管理', action: '归档押金记录', targetId: id, targetType: 'deposit',
|
|
});
|
|
return result;
|
|
}
|
|
|
|
@Delete(':id/permanent')
|
|
@RequirePermission('deposit:purge')
|
|
async purge(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
|
|
const result = await this.service.purge(id);
|
|
await logAudit(this.logService, req, {
|
|
module: '押金管理', action: '永久删除押金', targetId: id, targetType: 'deposit', detail: '物理删除,不可恢复',
|
|
});
|
|
return result;
|
|
}
|
|
}
|