forked from wangziqi/gongxue-base
feat: enhance dashboard with comprehensive stats and charts
This commit is contained in:
@@ -8,11 +8,15 @@ import { RoomExpense } from '../entities/room-expense.entity';
|
||||
import { Classroom } from '../entities/classroom.entity';
|
||||
import { ClassSchedule } from '../entities/class-schedule.entity';
|
||||
import { AttendanceRecord } from '../entities/attendance-record.entity';
|
||||
import { Class } from '../entities/class.entity';
|
||||
import { Deposit } from '../entities/deposit.entity';
|
||||
import { ClassroomRental } from '../entities/classroom-rental.entity';
|
||||
import { ClassTeacher } from '../entities/class-teacher.entity';
|
||||
import { DashboardService } from './dashboard.service';
|
||||
import { DashboardController } from './dashboard.controller';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Room, Student, Occupancy, Bill, RoomExpense, Classroom, ClassSchedule, AttendanceRecord])],
|
||||
imports: [TypeOrmModule.forFeature([Room, Student, Occupancy, Bill, RoomExpense, Classroom, ClassSchedule, AttendanceRecord, Class, Deposit, ClassroomRental, ClassTeacher])],
|
||||
controllers: [DashboardController],
|
||||
providers: [DashboardService],
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, IsNull, Not } from 'typeorm';
|
||||
import { Repository, IsNull, Not, MoreThanOrEqual } from 'typeorm';
|
||||
import { Room } from '../entities/room.entity';
|
||||
import { Student } from '../entities/student.entity';
|
||||
import { Occupancy } from '../entities/occupancy.entity';
|
||||
@@ -9,6 +9,10 @@ import { RoomExpense } from '../entities/room-expense.entity';
|
||||
import { Classroom } from '../entities/classroom.entity';
|
||||
import { ClassSchedule } from '../entities/class-schedule.entity';
|
||||
import { AttendanceRecord } from '../entities/attendance-record.entity';
|
||||
import { Class } from '../entities/class.entity';
|
||||
import { Deposit } from '../entities/deposit.entity';
|
||||
import { ClassroomRental } from '../entities/classroom-rental.entity';
|
||||
import { ClassTeacher } from '../entities/class-teacher.entity';
|
||||
|
||||
@Injectable()
|
||||
export class DashboardService {
|
||||
@@ -21,6 +25,10 @@ export class DashboardService {
|
||||
@InjectRepository(Classroom) private classroomRepo: Repository<Classroom>,
|
||||
@InjectRepository(ClassSchedule) private scheduleRepo: Repository<ClassSchedule>,
|
||||
@InjectRepository(AttendanceRecord) private attendanceRepo: Repository<AttendanceRecord>,
|
||||
@InjectRepository(Class) private classRepo: Repository<Class>,
|
||||
@InjectRepository(Deposit) private depositRepo: Repository<Deposit>,
|
||||
@InjectRepository(ClassroomRental) private rentalRepo: Repository<ClassroomRental>,
|
||||
@InjectRepository(ClassTeacher) private classTeacherRepo: Repository<ClassTeacher>,
|
||||
) {}
|
||||
|
||||
async getStats() {
|
||||
@@ -89,6 +97,48 @@ export class DashboardService {
|
||||
const attendanceTrend = await this.getAttendanceTrend(todayStr);
|
||||
const incomeTrend = await this.getIncomeTrend(currentMonth);
|
||||
|
||||
// --- New stats ---
|
||||
const classCount = await this.classRepo.count();
|
||||
|
||||
const teacherResult = await this.classTeacherRepo
|
||||
.createQueryBuilder('ct')
|
||||
.select('COUNT(DISTINCT ct.userId)', 'cnt')
|
||||
.getRawOne();
|
||||
const teacherCount = parseInt(teacherResult?.cnt || '0', 10);
|
||||
|
||||
const pendingResult = await this.depositRepo
|
||||
.createQueryBuilder('d')
|
||||
.select('SUM(d.amount)', 'total')
|
||||
.where('d.status = :paid', { paid: 'paid' })
|
||||
.andWhere('d.refundStatus IS NULL')
|
||||
.getRawOne();
|
||||
const pendingDeposits = parseFloat(pendingResult?.total || '0');
|
||||
|
||||
const activeRentals = await this.rentalRepo.count({ where: { endDate: MoreThanOrEqual(todayStr) } });
|
||||
|
||||
const occupancyByBuilding = await this.occRepo
|
||||
.createQueryBuilder('o')
|
||||
.leftJoin('o.room', 'r')
|
||||
.select('r.building', 'building')
|
||||
.addSelect('COUNT(*)', 'count')
|
||||
.where('o.checkOutDate IS NULL')
|
||||
.groupBy('r.building')
|
||||
.getRawMany();
|
||||
|
||||
const attendanceByStatus = attTodayStats.reduce((acc, r) => {
|
||||
acc[r.status] = parseInt(r.count, 10);
|
||||
return acc;
|
||||
}, {} as Record<string, number>);
|
||||
|
||||
const expenseByType = await this.expRepo
|
||||
.createQueryBuilder('e')
|
||||
.select('e.expenseType', 'type')
|
||||
.addSelect('SUM(e.amount)', 'total')
|
||||
.where('e.periodStart >= :start', { start: `${currentMonth}-01` })
|
||||
.andWhere('e.periodEnd <= :end', { end: this.nextMonth(currentMonth) })
|
||||
.groupBy('e.expenseType')
|
||||
.getRawMany();
|
||||
|
||||
return {
|
||||
totalRooms,
|
||||
totalStudents,
|
||||
@@ -100,6 +150,13 @@ export class DashboardService {
|
||||
classroomOccupancyRate,
|
||||
todayAttendanceRate,
|
||||
monthlyIncome,
|
||||
classCount,
|
||||
teacherCount,
|
||||
pendingDeposits,
|
||||
activeRentals,
|
||||
occupancyByBuilding,
|
||||
attendanceByStatus,
|
||||
expenseByType,
|
||||
attendanceTrend,
|
||||
incomeTrend,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user