HouseworkTaskGenerateService.java

package com.hwhub.batch.application.service;

import com.hwhub.batch.domain.enums.ProgramType;
import com.hwhub.batch.domain.enums.RecurrenceType;
import com.hwhub.batch.domain.model.Housework;
import com.hwhub.batch.domain.model.HouseworkTaskCreateParam;
import com.hwhub.batch.domain.repository.HouseworkRepository;
import com.hwhub.batch.domain.repository.HouseworkTaskRepository;
import com.hwhub.batch.domain.service.HouseworkScheduleCalculator;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class HouseworkTaskGenerateService {

  private static final Logger log = LoggerFactory.getLogger(HouseworkTaskGenerateService.class);

  private static final long SYSTEM_USER_ID = 2;

  private final HouseworkScheduleCalculator calculator;
  private final HouseworkRepository houseworkRepository;
  private final HouseworkTaskRepository houseworkTaskRepository;

  /**
   * 家事マスタから t_housework_task を生成するメイン処理
   *
   * @return 生成したタスク件数
   */
  @Transactional
  public int generateTasks() {

    LocalDate today = LocalDate.now(ZoneId.of("Asia/Tokyo"));
    // Weekly は today〜today+7日
    LocalDate weeklyTo = today.plusDays(7);
    // Monthly / NthWeekday は today〜today+1ヶ月+2日
    LocalDate monthlyTo = today.plusMonths(1).plusDays(2);
    log.info(
        "HouseworkTaskGenerateService started. today={}, weeklyTo={}, monthlyTo={}",
        today,
        weeklyTo,
        monthlyTo);

    // 有効期間が today〜monthlyTo と交差している housework を取得
    // Monthlyの期間でまとめて取得後、各処理で絞り込む
    List<Housework> houseworks = houseworkRepository.findActiveHouseworks(today, monthlyTo);
    if (houseworks.isEmpty()) {
      log.info("No active houseworks found in range. Nothing to do.");
      return 0;
    } else {
      log.info("Found {} active houseworks", houseworks.size());
    }

    int totalInserted = 0;
    for (Housework hw : houseworks) {

      // RecurrenceTypeによりtoを変更
      RecurrenceType type = RecurrenceType.fromCode(hw.getRecurrenceTypeCode());
      LocalDate rangeTo =
          switch (type) {
            case WEEKLY -> weeklyTo;
            case MONTHLY, NTH_WEEKDAY -> monthlyTo;
          };

      LocalDate from = max(today, hw.getStartDate());
      LocalDate to = min(rangeTo, hw.getEndDate());
      if (from.isAfter(to)) {
        continue;
      }

      // タスクの日付を生成
      List<LocalDate> candidateDates = calculator.calculateDates(hw, from, to);

      // 既存タスクのtarget_dateを取得し重複削除
      List<LocalDate> existingDates =
          houseworkTaskRepository.findExistingTaskDates(hw.getHouseworkId(), from);
      Set<LocalDate> existingSet = new HashSet<>(existingDates);

      List<LocalDate> newDates =
          candidateDates.stream().filter(d -> !existingSet.contains(d)).sorted().toList();

      if (!newDates.isEmpty()) {
        List<HouseworkTaskCreateParam> params =
            newDates.stream()
                .map(
                    d ->
                        HouseworkTaskCreateParam.from(
                            hw, d, SYSTEM_USER_ID, ProgramType.BTC_TSK_GEN.getCode()))
                .toList();
        int inserted = houseworkTaskRepository.bulkInsertTasks(params);
        totalInserted += inserted;

        log.debug(
            "Generated tasks for houseworkId={}, type={}, newDatesCount={}, inserted={}",
            hw.getHouseworkId(),
            type,
            newDates.size(),
            inserted);
      }
    }

    log.info("HouseworkTaskGenerateService finished. totalInserted={}", totalInserted);
    return totalInserted;
  }

  /**
   * 指定された日付のうち大きい日付を返す。
   *
   * @param a 日付
   * @param b 日付
   * @return 指定された日付のうち大きい日付
   */
  private static LocalDate max(LocalDate a, LocalDate b) {
    return a.isAfter(b) ? a : b;
  }

  /**
   * 指定された日付のうち小さい日付を返す。
   *
   * @param a 日付
   * @param b 日付
   * @return 指定された日付のうち小さい日付
   */
  private static LocalDate min(LocalDate a, LocalDate b) {
    return a.isBefore(b) ? a : b;
  }
}