HouseworkTaskService.java

package com.hwhub.backend.application.service;

import com.hwhub.backend.application.service.notification.NotificationPublisher;
import com.hwhub.backend.domain.enums.ProgramType;
import com.hwhub.backend.domain.enums.TaskStatus;
import com.hwhub.backend.domain.model.HouseworkTask4AssignModel;
import com.hwhub.backend.domain.model.HouseworkTaskModel;
import com.hwhub.backend.domain.repository.HouseworkTaskRepository;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
public class HouseworkTaskService {

  private final HouseworkTaskRepository taskRepository;
  private final HouseholdAuthorizationService authService;
  private final NotificationPublisher notificationPublisher;

  @Transactional(readOnly = true)
  public List<HouseworkTask4AssignModel> findForAssign(
      Long householdId, String status, Long userId) {

    // 認可チェック
    if (!authService.canAccessHousehold(householdId, userId)) {
      throw new AccessDeniedException(
          "User does not belong to household: userId=" + userId + ", householdId=" + householdId);
    }

    LocalDate lowerDate = LocalDate.now().minusDays(7);
    return taskRepository.findForAssign(householdId, status, lowerDate);
  }

  @Transactional
  public void updateAssignee(
      Long houseworkTaskId, Long assigneeUserId, String assignResonType, Long loginUserId) {

    HouseworkTaskModel model = taskRepository.findById(houseworkTaskId);
    if (model == null) {
      throw new IllegalArgumentException("HouseworkTask not found. id=" + houseworkTaskId);
    }

    // 認可チェック
    if (!authService.canAccessHousehold(model.getHouseholdId(), loginUserId)) {
      throw new AccessDeniedException(
          "User does not belong to household: userId="
              + loginUserId
              + ", householdId="
              + model.getHouseholdId());
    }
    if (assigneeUserId != null
        && !authService.canAccessHousehold(model.getHouseholdId(), assigneeUserId)) {
      throw new AccessDeniedException(
          "User does not belong to household: userId="
              + assigneeUserId
              + ", householdId="
              + model.getHouseholdId());
    }

    Long beforeAssigneeUserId = model.getAssigneeUserId();
    model.changeAssignee(assigneeUserId, assignResonType, loginUserId);
    taskRepository.update(model, loginUserId, ProgramType.ONL_HWRTSK.getCode());

    // 通知
    notificationPublisher.publishTaskAssignedEvent(
        model, beforeAssigneeUserId, loginUserId, ProgramType.ONL_HWRTSK.getCode());
  }

  @Transactional
  public void updateStatus(
      Long houseworkTaskId, String status, String skippedReason, Long loginUserId) {

    HouseworkTaskModel model = taskRepository.findById(houseworkTaskId);
    if (model == null) {
      throw new IllegalArgumentException("HouseworkTask not found. id=" + houseworkTaskId);
    }

    // 認可チェック
    if (!authService.canAccessHousehold(model.getHouseholdId(), loginUserId)) {
      throw new AccessDeniedException(
          "User does not belong to household: userId="
              + loginUserId
              + ", householdId="
              + model.getHouseholdId());
    }

    TaskStatus taskStatus = TaskStatus.fromCode(status);
    switch (taskStatus) {
      case DONE -> model.complete();
      case SKIPPED -> model.skip(skippedReason);
      default -> throw new IllegalArgumentException("Unprocessable status: " + status);
    }
    taskRepository.update(model, loginUserId, ProgramType.ONL_HWRTSK.getCode());
  }

  @Transactional
  public void bulkUpdateStatus(
      List<Long> taskIds, String status, String skippedReason, Long loginUserId) {

    if (taskIds.isEmpty()) {
      return;
    }

    // 先頭タスクの世帯IDで認可チェック
    HouseworkTaskModel first = taskRepository.findById(taskIds.get(0));
    if (first == null) {
      throw new IllegalArgumentException("HouseworkTask not found. id=" + taskIds.get(0));
    }
    if (!authService.canAccessHousehold(first.getHouseholdId(), loginUserId)) {
      throw new AccessDeniedException(
          "User does not belong to household: userId="
              + loginUserId
              + ", householdId="
              + first.getHouseholdId());
    }

    // 全タスクIDが同一世帯に属することを検証
    long count = taskRepository.countByIdsAndHouseholdId(taskIds, first.getHouseholdId());
    if (count != taskIds.size()) {
      throw new AccessDeniedException(
          "Some taskIds do not belong to household: householdId=" + first.getHouseholdId());
    }

    TaskStatus taskStatus = TaskStatus.fromCode(status);
    LocalDate doneAt =
        (taskStatus == TaskStatus.DONE || taskStatus == TaskStatus.SKIPPED)
            ? LocalDate.now()
            : null;

    taskRepository.bulkUpdateStatus(
        taskIds, status, skippedReason, doneAt, loginUserId, ProgramType.ONL_HWRTSK.getCode());
  }
}