chore: commit oxfmt formatting changes and verify artifacts

This commit is contained in:
2026-07-02 15:55:09 +08:00
parent 34726cc46d
commit 4f4cee157a
77 changed files with 5576 additions and 1400 deletions

View File

@@ -1,4 +1,18 @@
import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards, Request, Res, UseInterceptors, UploadedFile } from '@nestjs/common';
import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
UseGuards,
Request,
Res,
UseInterceptors,
UploadedFile,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import type { Response } from 'express';
import { StudentsService } from './students.service';
@@ -12,11 +26,18 @@ import * as ExcelJS from 'exceljs';
@UseGuards(JwtAuthGuard)
@Controller('students')
export class StudentsController {
constructor(private service: StudentsService, private logService: OperationLogsService) {}
constructor(
private service: StudentsService,
private logService: OperationLogsService,
) {}
@Get()
@RequirePermission('student:view')
findAll(@Query('name') name?: string, @Query('status') status?: string, @Query('includeArchived') includeArchived?: string) {
findAll(
@Query('name') name?: string,
@Query('status') status?: string,
@Query('includeArchived') includeArchived?: string,
) {
return this.service.findAll({ name, status, includeArchived: includeArchived === 'true' });
}
@@ -40,11 +61,30 @@ export class StudentsController {
];
ws.getRow(1).font = { bold: true };
ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
const statusMap: Record<string, string> = { active: '在读', graduated: '已毕业', withdrawn: '已退训', archived: '已归档' };
const statusMap: Record<string, string> = {
active: '在读',
graduated: '已毕业',
withdrawn: '已退训',
archived: '已归档',
};
for (const s of students) {
ws.addRow({ name: s.name, gender: s.gender || '', phone: s.phone || '', idNumber: s.idNumber || '', ethnicity: s.ethnicity || '', emergencyContact: s.emergencyContact || '', emergencyPhone: s.emergencyPhone || '', organization: s.organization || '', supervisor: s.supervisor || '', status: statusMap[s.status] || s.status });
ws.addRow({
name: s.name,
gender: s.gender || '',
phone: s.phone || '',
idNumber: s.idNumber || '',
ethnicity: s.ethnicity || '',
emergencyContact: s.emergencyContact || '',
emergencyPhone: s.emergencyPhone || '',
organization: s.organization || '',
supervisor: s.supervisor || '',
status: statusMap[s.status] || s.status,
});
}
res!.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res!.setHeader(
'Content-Type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
res!.setHeader('Content-Disposition', 'attachment; filename=students.xlsx');
await workbook.xlsx.write(res!);
res!.end();
@@ -68,8 +108,21 @@ export class StudentsController {
];
ws.getRow(1).font = { bold: true };
ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
ws.addRow({ name: '张三', phone: '13800138000', idNumber: '2024001', gender: '男', ethnicity: '汉族', emergencyContact: '张父', emergencyPhone: '13900000000', organization: '', supervisor: '' });
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
ws.addRow({
name: '张三',
phone: '13800138000',
idNumber: '2024001',
gender: '男',
ethnicity: '汉族',
emergencyContact: '张父',
emergencyPhone: '13900000000',
organization: '',
supervisor: '',
});
res.setHeader(
'Content-Type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
res.setHeader('Content-Disposition', 'attachment; filename=student_template.xlsx');
await workbook.xlsx.write(res);
res.end();
@@ -86,7 +139,17 @@ export class StudentsController {
async create(@Body() dto: CreateStudentDto, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.create(dto);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生', action: '添加学生', targetId: result.id, targetType: 'student', detail: `姓名: ${dto.name}, 电话: ${dto.phone || '无'}, 学号: ${dto.idNumber || '无'}`, ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '学生',
action: '添加学生',
targetId: result.id,
targetType: 'student',
detail: `姓名: ${dto.name}, 电话: ${dto.phone || '无'}, 学号: ${dto.idNumber || '无'}`,
ipAddress,
userAgent,
});
return result;
}
@@ -95,7 +158,17 @@ export class StudentsController {
async update(@Param('id') id: string, @Body() dto: UpdateStudentDto, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.update(+id, dto);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生', action: '编辑学生', targetId: +id, targetType: 'student', detail: JSON.stringify(dto), ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '学生',
action: '编辑学生',
targetId: +id,
targetType: 'student',
detail: JSON.stringify(dto),
ipAddress,
userAgent,
});
return result;
}
@@ -104,7 +177,16 @@ export class StudentsController {
async remove(@Param('id') id: string, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.remove(+id);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生', action: '归档学生', targetId: +id, targetType: 'student', ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '学生',
action: '归档学生',
targetId: +id,
targetType: 'student',
ipAddress,
userAgent,
});
return result;
}
@@ -113,7 +195,15 @@ export class StudentsController {
async batchRemove(@Body() body: { ids: number[] }, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.batchRemove(body.ids || []);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生', action: '批量归档学生', detail: `IDs: ${(body.ids || []).join(',')}`, ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '学生',
action: '批量归档学生',
detail: `IDs: ${(body.ids || []).join(',')}`,
ipAddress,
userAgent,
});
return result;
}
@@ -122,7 +212,16 @@ export class StudentsController {
async restore(@Param('id') id: string, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.restore(+id);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生', action: '恢复学生', targetId: +id, targetType: 'student', ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '学生',
action: '恢复学生',
targetId: +id,
targetType: 'student',
ipAddress,
userAgent,
});
return result;
}
@@ -134,7 +233,17 @@ export class StudentsController {
const workbook = new ExcelJS.Workbook();
await workbook.xlsx.load(file.buffer as any);
const ws = workbook.worksheets[0];
const rows: { name: string; phone?: string; idNumber?: string; gender?: string; ethnicity?: string; emergencyContact?: string; emergencyPhone?: string; organization?: string; supervisor?: string }[] = [];
const rows: {
name: string;
phone?: string;
idNumber?: string;
gender?: string;
ethnicity?: string;
emergencyContact?: string;
emergencyPhone?: string;
organization?: string;
supervisor?: string;
}[] = [];
ws.eachRow((row, idx) => {
if (idx === 1) return;
rows.push({
@@ -150,7 +259,15 @@ export class StudentsController {
});
});
const result = await this.service.batchImport(rows);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '学生', action: '批量导入', detail: result.message, ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '学生',
action: '批量导入',
detail: result.message,
ipAddress,
userAgent,
});
return result;
}
}

View File

@@ -20,7 +20,10 @@ export class StudentsService {
}
async findOne(id: number) {
const student = await this.repo.findOne({ where: { id }, relations: ['occupancies', 'occupancies.room'] });
const student = await this.repo.findOne({
where: { id },
relations: ['occupancies', 'occupancies.room'],
});
if (!student) throw new NotFoundException('学生不存在');
return student;
}
@@ -56,16 +59,18 @@ export class StudentsService {
}
let affected = 0;
if (targetIds.length > 0) {
const result = await this.repo.createQueryBuilder()
const result = await this.repo
.createQueryBuilder()
.update()
.set({ status: 'archived' })
.where('id IN (:...ids)', { ids: targetIds })
.execute();
affected = result.affected || 0;
}
const message = skipped.length > 0
? `成功归档 ${affected} 人;${skipped.length} 人已是归档状态被跳过(${skipped.slice(0, 5).join('、')}${skipped.length > 5 ? '…' : ''}`
: `已批量归档 ${affected}(数据已保留,可随时恢复`;
const message =
skipped.length > 0
? `成功归档 ${affected}${skipped.length} 人已是归档状态被跳过(${skipped.slice(0, 5).join('、')}${skipped.length > 5 ? '…' : ''}`
: `已批量归档 ${affected} 人(数据已保留,可随时恢复)`;
return { message, archived: affected, skipped: skipped.length };
}
@@ -78,26 +83,50 @@ export class StudentsService {
return { message: '已恢复' };
}
async batchImport(rows: { name: string; phone?: string; idNumber?: string; gender?: string; ethnicity?: string; emergencyContact?: string; emergencyPhone?: string; organization?: string; supervisor?: string }[]) {
async batchImport(
rows: {
name: string;
phone?: string;
idNumber?: string;
gender?: string;
ethnicity?: string;
emergencyContact?: string;
emergencyPhone?: string;
organization?: string;
supervisor?: string;
}[],
) {
let imported = 0;
let skipped = 0;
for (const row of rows) {
if (!row.name || !row.name.trim()) { skipped++; continue; }
if (!row.name || !row.name.trim()) {
skipped++;
continue;
}
const exists = await this.repo.findOne({ where: { name: row.name.trim() } });
if (exists) { skipped++; continue; }
await this.repo.save(this.repo.create({
name: row.name.trim(),
phone: row.phone?.trim() || undefined,
idNumber: row.idNumber?.trim() || undefined,
gender: row.gender || undefined,
ethnicity: row.ethnicity || undefined,
emergencyContact: row.emergencyContact || undefined,
emergencyPhone: row.emergencyPhone || undefined,
organization: row.organization || undefined,
supervisor: row.supervisor || undefined,
}));
if (exists) {
skipped++;
continue;
}
await this.repo.save(
this.repo.create({
name: row.name.trim(),
phone: row.phone?.trim() || undefined,
idNumber: row.idNumber?.trim() || undefined,
gender: row.gender || undefined,
ethnicity: row.ethnicity || undefined,
emergencyContact: row.emergencyContact || undefined,
emergencyPhone: row.emergencyPhone || undefined,
organization: row.organization || undefined,
supervisor: row.supervisor || undefined,
}),
);
imported++;
}
return { message: `成功导入 ${imported} 名学生,跳过 ${skipped} 条(重复或空行)`, imported, skipped };
return {
message: `成功导入 ${imported} 名学生,跳过 ${skipped} 条(重复或空行)`,
imported,
skipped,
};
}
}