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

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


没有新的实体,只是对已有数据的统计。
在 echarts 官网可以看到柱形图和饼图都只需要一个二维的数据,也就是 [[a, b], [a, b], [a, b]] 这样的形式的数据:


我们先在数据库里写下这个 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 表,过滤出在这段日期内的预定记录,根据用户分组,统计每组的预定数量。
这样查询出来的就是这段时间内每个用户预定了多少次会议室。
同理,也可以很轻松的统计处会议室被预定的频率:
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 里实现下上面两个统计。
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

await get(StatisticService).userBookingCount();
仔细观察下这个打印的 sql,其实和我们前面在 mysql workbench 里写的是一样的。
用 typeorm 的 query buidler 的 api 可以写各种 sql。
然后我们加上参数,并且改下别名:

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;
}
再跑下:
await get(StatisticService).userBookingCount("2023-09-23", "2023-09-30");

没啥问题。
然后在 controller 里加个接口:
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 就可以实现饼图、柱形图了:

然后,我们再写另一个接口。
和用户预定次数的统计差不多:
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:
@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 展示饼图或者柱形图了。
