feat: integrate CampusScope.filter() into all business services (12 services + 12 modules)

This commit is contained in:
2026-07-05 23:58:51 +08:00
parent eeae06fe30
commit cd6364268d
40 changed files with 949 additions and 172 deletions

View File

@@ -1,10 +1,10 @@
import { IsInt, IsString, IsNumber, IsOptional, IsEnum } from 'class-validator';
import { IsInt, IsString, IsNumber, IsOptional } from 'class-validator';
export class CreateRoomExpenseDto {
@IsInt()
roomId: number;
@IsEnum(['water', 'electricity', 'cleaning', 'damage', 'other'])
@IsString()
expenseType: string;
@IsNumber()
@@ -29,7 +29,7 @@ export class CreatePersonalExpenseDto {
@IsInt()
roomId?: number;
@IsEnum(['damage', 'cleaning', 'penalty', 'key', 'remote', 'deposit_deduction', 'other'])
@IsString()
expenseType: string;
@IsNumber()

View File

@@ -405,15 +405,6 @@ export class ExpensesController {
@RequirePermission('expense:view')
async exportPersonalExpenses(@Res() res: Response) {
const data = await this.service.findPersonalExpenses();
const typeMap: Record<string, string> = {
damage: '物品损坏',
cleaning: '保洁费',
penalty: '罚款',
key: '钥匙费',
remote: '空调遥控器',
deposit_deduction: '押金扣除',
other: '其他',
};
const workbook = new ExcelJS.Workbook();
const ws = workbook.addWorksheet('个人附加费');
ws.columns = [
@@ -427,7 +418,7 @@ export class ExpensesController {
data.forEach((d: any) => {
ws.addRow({
studentName: d.student?.name || '',
expenseType: typeMap[d.expenseType] || d.expenseType,
expenseType: d.expenseType,
amount: Number(d.amount),
expenseDate: d.expenseDate,
description: d.description || '',

View File

@@ -1,5 +1,6 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { DepartmentsModule } from '../departments/departments.module';
import { RoomExpense } from '../entities/room-expense.entity';
import { PersonalExpense } from '../entities/personal-expense.entity';
import { Room } from '../entities/room.entity';
@@ -12,6 +13,7 @@ import { OperationLogsModule } from '../operation-logs/operation-logs.module';
imports: [
TypeOrmModule.forFeature([RoomExpense, PersonalExpense, Room, Student]),
OperationLogsModule,
DepartmentsModule,
],
controllers: [ExpensesController],
providers: [ExpensesService],

View File

@@ -11,6 +11,7 @@ import {
BatchRoomExpenseDto,
} from './dto/expense.dto';
import { RoomsService } from '../rooms/rooms.service';
import { CampusScope } from '../common/campus-scope';
@Injectable()
export class ExpensesService {
@@ -19,6 +20,7 @@ export class ExpensesService {
@InjectRepository(PersonalExpense) private personalExpRepo: Repository<PersonalExpense>,
@InjectRepository(Room) private roomRepo: Repository<Room>,
@InjectRepository(Student) private studentRepo: Repository<Student>,
private readonly scope: CampusScope,
) {}
// 宿舍费用
@@ -42,10 +44,12 @@ export class ExpensesService {
}
async findRoomExpenses(query?: { roomId?: number; periodStart?: string; periodEnd?: string }) {
const scopeIds = await this.scope.getScopeDepartmentIds();
const qb = this.roomExpRepo
.createQueryBuilder('e')
.leftJoinAndSelect('e.room', 'room')
.orderBy('e.createdAt', 'DESC');
if (scopeIds) qb.andWhere('e.departmentId IN (:...scopeIds)', { scopeIds });
if (query?.roomId) qb.andWhere('e.roomId = :roomId', { roomId: query.roomId });
if (query?.periodStart) qb.andWhere('e.periodStart >= :ps', { ps: query.periodStart });
if (query?.periodEnd) qb.andWhere('e.periodEnd <= :pe', { pe: query.periodEnd });
@@ -82,8 +86,9 @@ export class ExpensesService {
}
async findPersonalExpenses(query?: { studentId?: number }) {
const where: any = {};
let where: Record<string, unknown> = {};
if (query?.studentId) where.studentId = query.studentId;
where = await this.scope.filter(where);
return this.personalExpRepo.find({
where,
relations: ['student'],
@@ -286,19 +291,6 @@ export class ExpensesService {
let skipped = 0;
const errors: string[] = [];
const typeMap: Record<string, string> = {
: 'damage',
: 'damage',
: 'cleaning',
: 'cleaning',
: 'penalty',
: 'key',
: 'key',
: 'remote',
: 'remote',
: 'deposit_deduction',
: 'other',
};
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
@@ -319,21 +311,9 @@ export class ExpensesService {
}
// 解析费用类型
let expenseType = row.expenseType?.trim() || '';
if (typeMap[expenseType]) {
expenseType = typeMap[expenseType];
}
const validTypes = [
'damage',
'cleaning',
'penalty',
'key',
'remote',
'deposit_deduction',
'other',
];
if (!validTypes.includes(expenseType)) {
errors.push(`${rowNum}行: 费用类型"${row.expenseType}"无效`);
const expenseType = row.expenseType?.trim() || '';
if (!expenseType) {
errors.push(`${rowNum}行: 费用类型不能为空`);
skipped++;
continue;
}