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, ) {} @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 }, ) { 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 }, ) { 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 }, ) { 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 { 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; } }