요일별 주단위 데이터 조회 prisma 예시 nestjs

agnusdei·2024년 7월 9일
async searchMySalesStatistics(
    userId: string,
    option: SearchTrainerSaleManagementDTO
  ) {
    return await this.prismaService.$transaction(async (tx) => {
      /** 트레이너 무결성 검사 */
      const user = await tx.user.findUnique({
        include: {
          trainerProfile: true,
        },
        where: { id: userId },
      });

      if (!user || !user.trainerProfile || user.type !== UserType.TRAINER)
        throw new BadRequestException('회원 정보가 존재하지 않습니다.');

      const { timeRangeUnit, year, month } = option;

      if (timeRangeUnit === TimeRangeUnit.YEAR) {
        /** 연간 통계 조회 */
        const list = await tx.trainerSaleManagement.findMany({
          where: {
            trainerId: user.trainerProfile.id,
            createdAt: {
              gte: new Date(`${year}-01-01`),
              lt: new Date(`${year}-12-31`),
            },
          },
          orderBy: { createdAt: 'asc' },
        });

        /** 월별 amount 계산하여 12개의 배열로 이루어진 데이터 반환
         * [{amount, month, year}...]
         */
        const monthlyList = Array.from({ length: 12 }, (_, i) => {
          const month = (i + 1).toString().padStart(2, '0');
          const amount = list
            .filter((item) => item.createdAt.getMonth() + 1 === i + 1)
            .reduce((acc, cur) => acc + cur.amount, 0);
          return { amount, month, year };
        });
        return monthlyList;
      } else if (timeRangeUnit === TimeRangeUnit.MONTH) {
        /**
         * 일별 amount 를 계산하여 해당 연도 월의 일별 데이터 반환
         * [{amount, day, month, year}...]
         */
        const list = await tx.trainerSaleManagement.findMany({
          where: {
            trainerId: user.trainerProfile.id,
            createdAt: {
              gte: new Date(`${year}-${month}-01`),
              lt: new Date(`${year}-${month}-31`),
            },
          },
          orderBy: { createdAt: 'asc' },
        });

        /** 해당 연도 월의 일 개수를 측정하여 조회 */
        const days = new Date(+year, +month, 0).getDate();
        const dailyList = Array.from({ length: days }, (_, i) => {
          const day = (i + 1).toString().padStart(2, '0');
          const amount = list
            .filter((item) => item.createdAt.getDate() === i + 1)
            .reduce((acc, cur) => acc + cur.amount, 0);
          return { amount, day, month, year };
        });
        return dailyList;
      } else if (timeRangeUnit === TimeRangeUnit.WEEK) {
        /** 현재를 기준으로 한 주 내의 요일별 데이터 반환
         * [{amount, dayOfWeek, year, month, day}...]
         */
        const today = dayjs().subtract(7, 'day');
        const _year = today.year().toString();
        const _month = today.month() + 1; // dayjs의 month()는 0부터 시작하므로 +1

        const firstDayOfMonth = dayjs(`${_year}-${_month}-01`);
        const lastDayOfMonth = firstDayOfMonth.endOf('month');

        const firstDayOfWeek = firstDayOfMonth.day();
        const lastDayOfWeek = lastDayOfMonth.day();

        const isSameWeek =
          firstDayOfWeek <= lastDayOfWeek ||
          lastDayOfWeek - firstDayOfWeek <= 3;

        let startDate, endDate;
        if (isSameWeek) {
          /** 같은 주에 있는 경우 */
          startDate = firstDayOfMonth;
          endDate = lastDayOfMonth;
        } else {
          /** 같은 주에 없는 경우 */
          startDate = firstDayOfMonth;
          endDate = dayjs(new Date(Number(_year), Number(_month), 1)).subtract(
            1,
            'day'
          );
        }

        const list = await tx.trainerSaleManagement.findMany({
          where: {
            trainerId: user.trainerProfile.id,
            createdAt: {
              gte: startDate.toDate(),
              lt: endDate.toDate(),
            },
          },
          orderBy: { createdAt: 'asc' },
        });

        const weeklyList = Array.from({ length: 7 }, (_, i) => {
          const dayOfWeek = i;
          const items = list.filter(
            (item) => dayjs(item.createdAt).day() === i
          );
          const amount = items.reduce((acc, cur) => acc + cur.amount, 0);

          let date;
          let _month;

          if (items.length > 0) {
            const firstItem = items[0];
            const firstItemDay = dayjs(firstItem.createdAt);
            const diff = (dayOfWeek - firstItemDay.day() + 7) % 7;
            date = firstItemDay.subtract(diff, 'day');
            _month = date.month() + 1; // dayjs의 month()는 0부터 시작하므로 +1
          } else {
            const targetDayOfWeek = (today.day() + 7 - dayOfWeek) % 7;
            date = today.subtract(targetDayOfWeek, 'day');
            _month = date.month() + 1; // dayjs의 month()는 0부터 시작하므로 +1
          }

          return {
            amount,
            dayOfWeek: DayOfWeekWithNumber[dayOfWeek],
            year: date.year().toString(),
            month: _month.toString().padStart(2, '0'),
            day: `${date.date()}`,
          };
        });

        weeklyList.sort((a, b) => {
          const dateA = dayjs(`${a.year}-${a.month}-${a.day}`);
          const dateB = dayjs(`${b.year}-${b.month}-${b.day}`);
          return dateA.valueOf() - dateB.valueOf(); // Unix 타임스탬프를 사용하여 비교
        });

        return weeklyList;
      }
    });
  }

0개의 댓글