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,8 +1,26 @@
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 { ExpensesService } from './expenses.service';
import { CreateRoomExpenseDto, CreatePersonalExpenseDto, BatchRoomExpenseDto } from './dto/expense.dto';
import {
CreateRoomExpenseDto,
CreatePersonalExpenseDto,
BatchRoomExpenseDto,
} from './dto/expense.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { OperationLogsService } from '../operation-logs/operation-logs.service';
import { extractRequestInfo } from '../common/request-utils';
@@ -15,13 +33,13 @@ function readCell(cell: ExcelJS.Cell): any {
if (v == null) return '';
if (typeof v === 'object') {
// 公式单元格:{ formula, result }
if ('result' in v) v = (v as any).result;
if ('result' in v) v = v.result;
// 富文本:{ richText: [...] }
else if ('richText' in v && Array.isArray((v as any).richText)) {
return (v as any).richText.map((r: any) => r.text || '').join('');
else if ('richText' in v && Array.isArray(v.richText)) {
return v.richText.map((r: any) => r.text || '').join('');
}
// 超链接:{ text, hyperlink }
else if ('text' in v) v = (v as any).text;
else if ('text' in v) v = v.text;
// 错误值:{ error: '#DIV/0!' }
else if ('error' in v) return '';
}
@@ -49,14 +67,27 @@ function readCellStr(cell: ExcelJS.Cell): string {
@UseGuards(JwtAuthGuard)
@Controller('expenses')
export class ExpensesController {
constructor(private service: ExpensesService, private logService: OperationLogsService) {}
constructor(
private service: ExpensesService,
private logService: OperationLogsService,
) {}
@Post('room')
@RequirePermission('expense:create')
async createRoomExpense(@Body() dto: CreateRoomExpenseDto, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.createRoomExpense(dto, req.user?.id);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '费用', action: '录入宿舍费用', targetId: result.id, targetType: 'room_expense', detail: `房间${dto.roomId} ¥${dto.amount} ${dto.expenseType}`, ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '费用',
action: '录入宿舍费用',
targetId: result.id,
targetType: 'room_expense',
detail: `房间${dto.roomId} ¥${dto.amount} ${dto.expenseType}`,
ipAddress,
userAgent,
});
return result;
}
@@ -65,7 +96,15 @@ export class ExpensesController {
async batchCreateRoomExpenses(@Body() dto: BatchRoomExpenseDto, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.batchCreateRoomExpenses(dto, req.user?.id);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '费用', action: '批量录入费用', detail: JSON.stringify(dto), ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '费用',
action: '批量录入费用',
detail: JSON.stringify(dto),
ipAddress,
userAgent,
});
return result;
}
@@ -78,7 +117,8 @@ export class ExpensesController {
) {
return this.service.findRoomExpenses({
roomId: roomId ? +roomId : undefined,
periodStart, periodEnd,
periodStart,
periodEnd,
});
}
@@ -87,7 +127,16 @@ export class ExpensesController {
async deleteRoomExpense(@Param('id') id: string, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.deleteRoomExpense(+id);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '费用', action: '删除宿舍费用', targetId: +id, targetType: 'room_expense', ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '费用',
action: '删除宿舍费用',
targetId: +id,
targetType: 'room_expense',
ipAddress,
userAgent,
});
return result;
}
@@ -96,16 +145,38 @@ export class ExpensesController {
async batchDeleteRoomExpenses(@Body() body: { ids: number[] }, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.batchDeleteRoomExpenses(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;
}
@Put('room/:id')
@RequirePermission('expense:edit')
async updateRoomExpense(@Param('id') id: string, @Body() dto: CreateRoomExpenseDto, @Request() req: any) {
async updateRoomExpense(
@Param('id') id: string,
@Body() dto: CreateRoomExpenseDto,
@Request() req: any,
) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.updateRoomExpense(+id, dto);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '费用', action: '编辑宿舍费用', targetId: +id, targetType: 'room_expense', detail: `¥${dto.amount} ${dto.expenseType}`, ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '费用',
action: '编辑宿舍费用',
targetId: +id,
targetType: 'room_expense',
detail: `¥${dto.amount} ${dto.expenseType}`,
ipAddress,
userAgent,
});
return result;
}
@@ -114,7 +185,15 @@ export class ExpensesController {
async createPersonalExpense(@Body() dto: CreatePersonalExpenseDto, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.createPersonalExpense(dto, req.user?.id);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '费用', action: '录入个人费用', detail: `学生${dto.studentId} ¥${dto.amount} ${dto.expenseType}`, ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '费用',
action: '录入个人费用',
detail: `学生${dto.studentId} ¥${dto.amount} ${dto.expenseType}`,
ipAddress,
userAgent,
});
return result;
}
@@ -129,7 +208,15 @@ export class ExpensesController {
async deletePersonalExpense(@Param('id') id: string, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.deletePersonalExpense(+id);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '费用', action: '删除个人费用', targetId: +id, ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '费用',
action: '删除个人费用',
targetId: +id,
ipAddress,
userAgent,
});
return result;
}
@@ -138,16 +225,37 @@ export class ExpensesController {
async batchDeletePersonalExpenses(@Body() body: { ids: number[] }, @Request() req: any) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.batchDeletePersonalExpenses(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;
}
@Put('personal/:id')
@RequirePermission('expense:edit')
async updatePersonalExpense(@Param('id') id: string, @Body() dto: CreatePersonalExpenseDto, @Request() req: any) {
async updatePersonalExpense(
@Param('id') id: string,
@Body() dto: CreatePersonalExpenseDto,
@Request() req: any,
) {
const { ipAddress, userAgent } = extractRequestInfo(req);
const result = await this.service.updatePersonalExpense(+id, dto);
await this.logService.log({ userId: req.user?.id, username: req.user?.username, module: '费用', action: '编辑个人费用', targetId: +id, detail: `¥${dto.amount} ${dto.expenseType}`, ipAddress, userAgent });
await this.logService.log({
userId: req.user?.id,
username: req.user?.username,
module: '费用',
action: '编辑个人费用',
targetId: +id,
detail: `¥${dto.amount} ${dto.expenseType}`,
ipAddress,
userAgent,
});
return result;
}
@@ -168,8 +276,20 @@ export class ExpensesController {
];
ws.getRow(1).font = { bold: true };
ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
ws.addRow({ seq: 1, period: '2026-01-21 - 2026-02-08', roomNumber: '4-102', electricity: 50, electricityFee: 25.5, water: 3, waterFee: 14.7, total: 40.2 });
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
ws.addRow({
seq: 1,
period: '2026-01-21 - 2026-02-08',
roomNumber: '4-102',
electricity: 50,
electricityFee: 25.5,
water: 3,
waterFee: 14.7,
total: 40.2,
});
res.setHeader(
'Content-Type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
res.setHeader('Content-Disposition', 'attachment; filename=utility_template.xlsx');
await workbook.xlsx.write(res);
res.end();
@@ -199,7 +319,15 @@ export class ExpensesController {
});
});
const result = await this.service.batchImportUtilityExpenses(rows, req.user?.id);
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;
}
@@ -217,12 +345,23 @@ export class ExpensesController {
];
ws.getRow(1).font = { bold: true };
ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
ws.addRow({ studentName: '张三', expenseType: '钥匙费', amount: 30, expenseDate: '2026-01-15', description: '丢失宿舍钥匙' });
ws.addRow({
studentName: '张三',
expenseType: '钥匙费',
amount: 30,
expenseDate: '2026-01-15',
description: '丢失宿舍钥匙',
});
// 添加费用类型说明
const noteSheet = workbook.addWorksheet('费用类型说明');
noteSheet.columns = [{ header: '费用类型可用值', key: 'type', width: 25 }];
['物品损坏', '保洁费', '罚款', '钥匙费', '空调遥控器', '押金扣除', '其他'].forEach(t => noteSheet.addRow({ type: t }));
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
['物品损坏', '保洁费', '罚款', '钥匙费', '空调遥控器', '押金扣除', '其他'].forEach((t) =>
noteSheet.addRow({ type: t }),
);
res.setHeader(
'Content-Type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
res.setHeader('Content-Disposition', 'attachment; filename=personal_expense_template.xlsx');
await workbook.xlsx.write(res);
res.end();
@@ -250,7 +389,15 @@ export class ExpensesController {
});
});
const result = await this.service.batchImportPersonalExpenses(rows, req.user?.id);
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;
}
@@ -258,7 +405,15 @@ 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 typeMap: Record<string, string> = {
damage: '物品损坏',
cleaning: '保洁费',
penalty: '罚款',
key: '钥匙费',
remote: '空调遥控器',
deposit_deduction: '押金扣除',
other: '其他',
};
const workbook = new ExcelJS.Workbook();
const ws = workbook.addWorksheet('个人附加费');
ws.columns = [
@@ -278,7 +433,10 @@ export class ExpensesController {
description: d.description || '',
});
});
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=personal_expenses_export.xlsx');
await workbook.xlsx.write(res);
res.end();