Skip to content

写完用户模块、会议室模块、预定模块之后,就只剩下统计模块了,这节我们就来写下这个模块。

这个模块只有 2 个统计的功能:

分别是统计会议室的使用频率、用户的预定频率:

没有新的实体,只是对已有数据的统计。

echarts 官网可以看到柱形图和饼图都只需要一个二维的数据,也就是 [[a, b], [a, b], [a, b]] 这样的形式的数据:

我们先在数据库里写下这个 sql:

sql
select u.username 用户名, count(*) 预定次数
from booking b
left join users u
on b.userId = u.id
where b.startTime between '2023-09-24' and '2023-09-30'
group by b.userId;

关联 users 和 booking 表,过滤出在这段日期内的预定记录,根据用户分组,统计每组的预定数量。

这样查询出来的就是这段时间内每个用户预定了多少次会议室。

同理,也可以很轻松的统计处会议室被预定的频率:

sql
select m.name 会议室名字, count(*) 预定次数
from booking b
left join meeting_room m
on b.roomId = m.id
where b.startTime between '2023-09-24' and '2023-09-30'
group by b.roomId;

把关联的表换成 meeting_room 就好了。

当然,现在的数据不是很多,我们添加一些数据:

直接通过 mysql workbench 的 copy row 和 paste row 快速复制一些数据就好了。

复制出来的数据要改下 id,以及其他一些信息。

我添加了 4 条数据,并且指定了不同的 userId 和 roomId,点击 apply 应用修改。

然后再跑下那两个统计 sql

没啥问题。

接下来在 nest 里把这个统计 sql 实现就好了。

nest g module statistic

生成一个新的 module。

nest g service statistic
nest g controller statistic

之后生成 controller 和 service。

然后在 service 里实现下上面两个统计。

javascript
import { Injectable } from '@nestjs/common';
import { InjectEntityManager } from '@nestjs/typeorm';
import { Booking } from 'src/booking/entities/booking.entity';
import { User } from 'src/user/entities/user.entity';
import { EntityManager } from 'typeorm';

@Injectable()
export class StatisticService {

    @InjectEntityManager()
    private entityManager: EntityManager;

    async userBookingCount() {
        const res = await this.entityManager
            .createQueryBuilder(Booking, 'b')
            .select('u.id', '用户id')
            .addSelect('u.username', '用户名')
            .leftJoin(User, 'u', 'b.userId = u.id')
            .addSelect('count(1)', '预定次数')
            .where('b.startTime between :time1 and :time2', {
                time1: '2023-09-24', 
                time2: '2023-09-30'
            })
            .addGroupBy('b.user')
            .getRawMany();
        return res;
    }


    async meetingRoomUsedCount() {

    }
}

注入 entityManager 来查询。

统计相关的 sql 比较复杂,我们使用 queryBuilder 的 api。

queryBuilder 的 api 和写 sql 的体验差不多。

我们用 repl 的方式跑下试试:

npm run repl

javascript
await get(StatisticService).userBookingCount()

仔细观察下这个打印的 sql,其实和我们前面在 mysql workbench 里写的是一样的。

用 typeorm 的 query buidler 的 api 可以写各种 sql。

然后我们加上参数,并且改下别名:

javascript
async userBookingCount(startTime: string, endTime: string) {
    const res = await this.entityManager
        .createQueryBuilder(Booking, 'b')
        .select('u.id', 'userId')
        .addSelect('u.username', 'username')
        .leftJoin(User, 'u', 'b.userId = u.id')
        .addSelect('count(1)', 'bookingCount')
        .where('b.startTime between :time1 and :time2', {
            time1: startTime, 
            time2: endTime
        })
        .addGroupBy('b.user')
        .getRawMany();
    return res;
}

再跑下:

javascript
await get(StatisticService).userBookingCount('2023-09-23', '2023-09-30')

没啥问题。

然后在 controller 里加个接口:

javascript
import { Controller, Get, Inject, Query } from '@nestjs/common';
import { StatisticService } from './statistic.service';

@Controller('statistic')
export class StatisticController {

    @Inject(StatisticService)
    private statisticService: StatisticService;

    @Get('userBookingCount')
    async userBookignCount(@Query('startTime') startTime: string, @Query('endTime') endTime) {
        return this.statisticService.userBookingCount(startTime, endTime);
    }
}

把 repl 的模式停掉,重新跑服务:

npm run start:dev

用 postman 访问下:

http://localhost:3005/statistic/userBookingCount?startTime=2023-09-23&endTime=2023-09-30

可以看到,返回了这段时间的统计数据。

这样,加个 echarts 就可以实现饼图、柱形图了:

然后,我们再写另一个接口。

和用户预定次数的统计差不多:

javascript
async meetingRoomUsedCount(startTime: string, endTime: string) {
    const res = await this.entityManager
        .createQueryBuilder(Booking, 'b')
        .select('m.id', 'meetingRoomId')
        .addSelect('m.name', 'meetingRoomName')
        .leftJoin(MeetingRoom, 'm', 'b.roomId = m.id')
        .addSelect('count(1)', 'usedCount')
        .where('b.startTime between :time1 and :time2', {
            time1: startTime, 
            time2: endTime
        })
        .addGroupBy('b.roomId')
        .getRawMany();
    return res;
}

上面是 service 部分。

然后是 controller:

javascript
@Get('meetingRoomUsedCount')
async meetingRoomUsedCount(@Query('startTime') startTime: string, @Query('endTime') endTime) {
    return this.statisticService.meetingRoomUsedCount(startTime, endTime);
}

postman 里测试下:

http://localhost:3005/statistic/meetingRoomUsedCount?startTime=2023-09-23&endTime=2023-09-30

也没啥问题,和我们在 mysql workbench 里自己写 sql 统计的结果一样。

这样,统计模块的后端部分就完成了。

代码在小册仓库

总结

这节我们实现了统计模块的后端代码。

就两个统计 sql,我们先在 mysql workbench 里写了这个统计 sql,然后在 typeorm 里用 query builder 的方式实现。

query builder 的 api 和直接写 sql 差不多。

前端部分拿到统计的数据,就可以用 echarts 展示饼图或者柱形图了。