- 全模块类型化:controller 的 req: any → AuthenticatedRequest/RequestUser, 聚合查询 getRawMany 泛型标注、导入行/响应体定义具体 interface、 catch (e: any) → unknown + 收窄、no-base-to-string 用 String() 显式转换 - 第三方无类型库边界(pdfkit/exceljs)文件级或单行 disable 并注明理由 - 顺带修复:get-business-context.tool 两个 require-await error、 bills.controller 参数顺序隐患、main.ts compression 调用 - 运行时逻辑零改动;测试 142 套件 / 1065 用例全部通过
82 lines
2.9 KiB
TypeScript
82 lines
2.9 KiB
TypeScript
import { Body, Controller, Get, Post, Query, Request, UseGuards } from '@nestjs/common';
|
||
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
||
import { RequirePermission } from '../auth/decorators/permission.decorator';
|
||
import { OperationLogsService } from '../operation-logs/operation-logs.service';
|
||
import { extractRequestInfo } from '../common/request-utils';
|
||
import type { AuthenticatedUser } from '../authorization';
|
||
import { BatchChangeWalletBalanceDto, ChangeWalletBalanceDto } from './dto/wallet.dto';
|
||
import { WalletsService } from './wallets.service';
|
||
|
||
interface AuthenticatedRequest {
|
||
user: AuthenticatedUser;
|
||
ip?: string;
|
||
headers?: Record<string, string | string[] | undefined>;
|
||
}
|
||
|
||
@UseGuards(JwtAuthGuard)
|
||
@Controller('wallets')
|
||
export class WalletsController {
|
||
constructor(private service: WalletsService, private logService: OperationLogsService) {}
|
||
|
||
@Get()
|
||
@RequirePermission('wallet:view')
|
||
findAll(
|
||
@Query('keyword') keyword?: string,
|
||
@Query('debtOnly') debtOnly?: string,
|
||
@Query('roomType') roomType?: string,
|
||
) {
|
||
return this.service.findAll({ keyword, debtOnly: debtOnly === 'true', roomType });
|
||
}
|
||
|
||
@Get('room-types')
|
||
@RequirePermission('wallet:view')
|
||
findRoomTypes() {
|
||
return this.service.findRoomTypes();
|
||
}
|
||
|
||
@Get('transactions')
|
||
@RequirePermission('wallet:view')
|
||
findTransactions(@Query('studentId') studentId: string) {
|
||
return this.service.findTransactions(Number(studentId));
|
||
}
|
||
|
||
@Post('change-balance')
|
||
@RequirePermission('wallet:edit')
|
||
async changeBalance(@Body() dto: ChangeWalletBalanceDto, @Request() req: AuthenticatedRequest) {
|
||
const result = await this.service.changeBalance(dto, req.user?.id);
|
||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||
await this.logService.log({
|
||
userId: req.user?.id,
|
||
username: req.user?.username,
|
||
module: '学生余额',
|
||
action: dto.type === 'recharge' ? '余额充值' : '余额调账',
|
||
targetId: dto.studentId,
|
||
targetType: 'student_wallet',
|
||
detail: `金额 ¥${dto.amount}${dto.description ? `,${dto.description}` : ''}`,
|
||
ipAddress,
|
||
userAgent,
|
||
});
|
||
return result;
|
||
}
|
||
|
||
|
||
@Post('batch-change-balance')
|
||
@RequirePermission('wallet:edit')
|
||
async batchChangeBalance(@Body() dto: BatchChangeWalletBalanceDto, @Request() req: AuthenticatedRequest) {
|
||
const result = await this.service.batchChangeBalance(dto, req.user?.id);
|
||
const { ipAddress, userAgent } = extractRequestInfo(req);
|
||
await this.logService.log({
|
||
userId: req.user?.id,
|
||
username: req.user?.username,
|
||
module: '学生余额',
|
||
action: dto.type === 'recharge' ? '批量余额充值' : '批量余额调账',
|
||
targetId: undefined,
|
||
targetType: 'student_wallet',
|
||
detail: `学生${result.count}人,金额 ¥${dto.amount}${dto.description ? `,${dto.description}` : ''}`,
|
||
ipAddress,
|
||
userAgent,
|
||
});
|
||
return result;
|
||
}
|
||
}
|