refactor(rooms): remove dorm gender restrictions

This commit is contained in:
2026-07-13 15:19:09 +08:00
parent aa1ed7db56
commit f98c0ccaa8
12 changed files with 61 additions and 166 deletions

View File

@@ -1,21 +0,0 @@
import { ValidationPipe } from '@nestjs/common';
import { CreateRoomDto, UpdateRoomDto } from './room.dto';
const pipe = new ValidationPipe({ transform: true, whitelist: true });
const transform = <T extends object>(metatype: new () => T, value: unknown) =>
pipe.transform(value, { type: 'body', metatype });
describe('room gender DTO', () => {
it.each(['男', '女', null])('accepts %p', async (gender) => {
await expect(
transform(CreateRoomDto, { roomNumber: '1-101', capacity: 4, gender }),
).resolves.toMatchObject({ gender });
await expect(transform(UpdateRoomDto, { gender })).resolves.toMatchObject({ gender });
});
it.each(['不限', 'male', '女生宿舍'])('rejects unsupported value %p', async (gender) => {
await expect(
transform(CreateRoomDto, { roomNumber: '1-101', capacity: 4, gender }),
).rejects.toThrow();
});
});

View File

@@ -1,4 +1,4 @@
import { IsString, IsOptional, IsInt, IsEnum, IsIn, Min, IsNumber } from 'class-validator';
import { IsString, IsOptional, IsInt, IsEnum, Min, IsNumber } from 'class-validator';
export class CreateRoomDto {
@IsString()
@@ -20,10 +20,6 @@ export class CreateRoomDto {
@IsString()
roomType?: string;
@IsOptional()
@IsIn(['男', '女'])
gender?: '男' | '女' | null;
@IsOptional()
@IsString()
rentalCategory?: string;
@@ -55,10 +51,6 @@ export class UpdateRoomDto {
@IsString()
roomType?: string;
@IsOptional()
@IsIn(['男', '女'])
gender?: '男' | '女' | null;
@IsOptional()
@IsEnum(['available', 'full', 'maintenance'])
status?: string;

View File

@@ -67,7 +67,6 @@ export class RoomsController {
{ header: '宿舍类型', key: 'roomType', width: 12 },
{ header: '租赁类型(long/short)', key: 'rentalCategory', width: 18 },
{ header: '月租金', key: 'monthlyRate', width: 10 },
{ header: '宿舍性别(男/女)', key: 'gender', width: 18 },
];
ws.addRow({
roomNumber: '4-102',
@@ -77,7 +76,6 @@ export class RoomsController {
roomType: '四人间',
rentalCategory: 'long',
monthlyRate: 800,
gender: '男',
});
ws.addRow({
roomNumber: '2-201',
@@ -87,7 +85,6 @@ export class RoomsController {
roomType: '单人间',
rentalCategory: 'short',
monthlyRate: 0,
gender: '女',
});
res.setHeader(
'Content-Type',
@@ -113,7 +110,6 @@ export class RoomsController {
{ header: '宿舍类型', key: 'roomType', width: 12 },
{ header: '额定人数', key: 'capacity', width: 10 },
{ header: '当前入住', key: 'currentCount', width: 10 },
{ header: '性别', key: 'gender', width: 8 },
{ header: '状态', key: 'status', width: 10 },
{ header: '租赁类型', key: 'rentalCategory', width: 12 },
{ header: '月租金', key: 'monthlyRate', width: 10 },
@@ -134,7 +130,6 @@ export class RoomsController {
roomType: r.roomType || '',
capacity: r.capacity,
currentCount: r.currentCount,
gender: r.gender || '',
status: statusMap[r.status] || r.status,
rentalCategory: r.rentalCategory === 'long' ? '长租' : '短租',
monthlyRate: r.monthlyRate ?? '',
@@ -339,7 +334,6 @@ export class RoomsController {
roomType?: string;
rentalCategory?: string;
monthlyRate?: number;
gender?: '男' | '女';
}[] = [];
ws.eachRow((row, idx) => {
if (idx === 1) return;
@@ -352,13 +346,6 @@ export class RoomsController {
: undefined;
const monthlyRateRaw = Number(row.getCell(7).value);
const monthlyRate = isNaN(monthlyRateRaw) ? undefined : monthlyRateRaw;
const genderRaw = String(row.getCell(8).value || '').trim();
const gender =
genderRaw === '男' || genderRaw === '男生'
? '男'
: genderRaw === '女' || genderRaw === '女生'
? '女'
: undefined;
rows.push({
roomNumber: String(row.getCell(1).value || ''),
building: String(row.getCell(2).value || '') || undefined,
@@ -367,7 +354,6 @@ export class RoomsController {
roomType: String(row.getCell(5).value || '').trim() || undefined,
rentalCategory,
monthlyRate,
gender,
});
});
const result = await this.service.batchImport(rows);

View File

@@ -1,43 +0,0 @@
import { BadRequestException } from '@nestjs/common';
import { RoomsService } from './rooms.service';
function createService(room: { id: number; gender: '男' | '女' | null }, activeCount: number) {
const repo = {
findOne: jest.fn().mockResolvedValue(room),
update: jest.fn(),
};
const occRepo = { count: jest.fn().mockResolvedValue(activeCount) };
const service = new RoomsService(
repo as never,
occRepo as never,
{} as never,
{} as never,
{} as never,
);
return { service, repo, occRepo };
}
describe('RoomsService — room gender maintenance', () => {
it('allows an empty room gender to be changed or cleared', async () => {
const { service, repo } = createService({ id: 1, gender: '男' }, 0);
await service.update(1, { gender: null });
expect(repo.update).toHaveBeenCalledWith(1, { gender: null });
});
it('rejects changing gender while students are living in the room', async () => {
const { service, repo } = createService({ id: 1, gender: '男' }, 1);
await expect(service.update(1, { gender: '女' })).rejects.toBeInstanceOf(BadRequestException);
expect(repo.update).not.toHaveBeenCalled();
});
it('does not query occupants when gender is unchanged or omitted', async () => {
const { service, occRepo } = createService({ id: 1, gender: '男' }, 1);
await service.update(1, { roomType: '四人间' });
expect(occRepo.count).not.toHaveBeenCalled();
});
});

View File

@@ -114,13 +114,7 @@ export class RoomsService {
}
async update(id: number, dto: UpdateRoomDto) {
const room = await this.findOne(id);
if (Object.prototype.hasOwnProperty.call(dto, 'gender') && dto.gender !== room.gender) {
const activeCount = await this.occRepo.count({
where: { roomId: id, checkOutDate: IsNull() },
});
if (activeCount > 0) throw new BadRequestException('该宿舍有在住人员,无法修改宿舍性别');
}
await this.findOne(id);
await this.repo.update(id, dto);
return this.repo.findOne({ where: { id } });
}
@@ -302,7 +296,6 @@ export class RoomsService {
roomType?: string;
rentalCategory?: string;
monthlyRate?: number;
gender?: '男' | '女';
}[],
) {
let imported = 0;
@@ -328,7 +321,6 @@ export class RoomsService {
roomType: row.roomType || parsed.roomType || undefined,
rentalCategory: row.rentalCategory || undefined,
monthlyRate: row.monthlyRate ?? undefined,
gender: row.gender ?? undefined,
}),
);
imported++;