Merge pull request 'feat/infer_dev_260107' (#303) from feat/infer_dev_260107 into develop

Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/303
This commit is contained in:
2026-01-21 14:38:45 +09:00
7 changed files with 163 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic; import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.members.service.AdminService; import com.kamco.cd.kamcoback.members.service.AdminService;
import com.kamco.cd.kamcoback.members.service.MembersService; import com.kamco.cd.kamcoback.members.service.MembersService;
import com.kamco.cd.kamcoback.scheduler.service.MemberInactiveJobService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
@@ -34,6 +35,7 @@ public class MembersApiController {
private final MembersService membersService; private final MembersService membersService;
private final AdminService adminService; private final AdminService adminService;
private final MemberInactiveJobService memberInactiveJobService;
@Operation(summary = "회원정보 목록", description = "회원정보 조회") @Operation(summary = "회원정보 목록", description = "회원정보 조회")
@ApiResponses( @ApiResponses(
@@ -157,4 +159,13 @@ public class MembersApiController {
String employeeNo) { String employeeNo) {
return ApiResponseDto.ok(adminService.existsByEmployeeNo(employeeNo)); return ApiResponseDto.ok(adminService.existsByEmployeeNo(employeeNo));
} }
@Operation(
summary = "라벨러/검수자 최종로그인 28일 경과 이후 사용중지(스케줄링 실행)",
description = "라벨러/검수자 최종로그인 28일 경과 이후 사용중지 처리")
@GetMapping("/member-inactive-job")
public ApiResponseDto<Void> memberInactiveJob() {
memberInactiveJobService.memberActive28daysToInactive();
return ApiResponseDto.ok(null);
}
} }

View File

@@ -0,0 +1,22 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.postgres.repository.members.MemberInactiveJobRepository;
import com.kamco.cd.kamcoback.scheduler.dto.MemberInactiveJobDto.MemberInfo;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class MemberInactiveJobCoreService {
private final MemberInactiveJobRepository memberInactiveJobRepository;
public List<MemberInfo> findInactiveLabelerReviewer() {
return memberInactiveJobRepository.findInactiveLabelerReviewer();
}
public void updateMemberStatusInactive(MemberInfo memberInfo) {
memberInactiveJobRepository.updateMemberStatusInactive(memberInfo);
}
}

View File

@@ -0,0 +1,7 @@
package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MemberInactiveJobRepository
extends JpaRepository<MemberEntity, Long>, MemberInactiveJobRepositoryCustom {}

View File

@@ -0,0 +1,11 @@
package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.scheduler.dto.MemberInactiveJobDto.MemberInfo;
import java.util.List;
public interface MemberInactiveJobRepositoryCustom {
List<MemberInfo> findInactiveLabelerReviewer();
void updateMemberStatusInactive(MemberInfo memberInfo);
}

View File

@@ -0,0 +1,44 @@
package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.common.enums.RoleType;
import com.kamco.cd.kamcoback.common.enums.StatusType;
import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity;
import com.kamco.cd.kamcoback.scheduler.dto.MemberInactiveJobDto.MemberInfo;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class MemberInactiveJobRepositoryImpl implements MemberInactiveJobRepositoryCustom {
private final JPAQueryFactory queryFactory;
private final QMemberEntity memberEntity = QMemberEntity.memberEntity;
@Override
public List<MemberInfo> findInactiveLabelerReviewer() {
ZonedDateTime checkTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul")).minusDays(14);
return queryFactory
.select(Projections.constructor(MemberInfo.class, memberEntity.id, memberEntity.employeeNo))
.from(memberEntity)
.where(
memberEntity.userRole.in(RoleType.LABELER.getId(), RoleType.REVIEWER.getId()),
memberEntity.status.eq(StatusType.ACTIVE.getId()),
memberEntity.lastLoginDttm.lt(checkTime))
.fetch();
}
@Override
public void updateMemberStatusInactive(MemberInfo memberInfo) {
queryFactory
.update(memberEntity)
.set(memberEntity.status, StatusType.INACTIVE.getId())
.set(memberEntity.statusChgDttm, ZonedDateTime.now())
.where(memberEntity.id.eq(memberInfo.getId()))
.execute();
}
}

View File

@@ -0,0 +1,19 @@
package com.kamco.cd.kamcoback.scheduler.dto;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
public class MemberInactiveJobDto {
@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor
public static class MemberInfo {
private Long id;
private String employeeNo;
}
}

View File

@@ -0,0 +1,49 @@
package com.kamco.cd.kamcoback.scheduler.service;
import com.kamco.cd.kamcoback.postgres.core.MemberInactiveJobCoreService;
import com.kamco.cd.kamcoback.scheduler.dto.MemberInactiveJobDto.MemberInfo;
import jakarta.transaction.Transactional;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
@Log4j2
@Service
@RequiredArgsConstructor
public class MemberInactiveJobService {
private final MemberInactiveJobCoreService memberInactiveJobCoreService;
@Value("${spring.profiles.active}")
private String profile;
private boolean isLocalProfile() {
return "local".equalsIgnoreCase(profile);
}
@Transactional
@Scheduled(cron = "0 0 1 * * *")
public void memberActive28daysToInactive() {
if (isLocalProfile()) {
return;
}
try {
List<MemberInfo> list = memberInactiveJobCoreService.findInactiveLabelerReviewer();
if (list.isEmpty()) {
return;
}
for (MemberInfo memberInfo : list) {
memberInactiveJobCoreService.updateMemberStatusInactive(memberInfo);
}
} catch (Exception e) {
log.error("배치 처리 중 예외", e);
}
}
}