Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import com.workhub.projectNode.dto.ProjectNodeCategoryCount;
import com.workhub.projectNode.entity.NodeCategory;
import com.workhub.projectNode.service.ProjectNodeService;
import com.workhub.userTable.service.company.CompanyService;
import com.workhub.userTable.service.company.ReadCompanyService;
import com.workhub.userTable.service.user.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -36,7 +36,7 @@ public class DashBoardAdminService {
private static final String PROJECT_CRITERIA = "계약 기간이 월과 겹치는 프로젝트";

private final UserService userService;
private final CompanyService companyService;
private final ReadCompanyService readCompanyService;
private final ProjectService projectService;
private final ProjectNodeService projectNodeService;

Expand All @@ -53,7 +53,7 @@ public UserCountResponse getUserCount() {
*/
public CompanyCountResponse getCompanyCount() {

return CompanyCountResponse.from(companyService.countActiveCompanies());
return CompanyCountResponse.from(readCompanyService.countActiveCompanies());
}

/**
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/workhub/project/service/ProjectService.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,4 +171,14 @@ public List<Project> findProjectsWithPaging(List<Long> projectIds, LocalDate sta
return projectRepository.findProjectsWithPaging(projectIds, startDate, endDate,
status, sortOrder, cursor, size);
}

/**
* 특정 회사의 모든 프로젝트 조회
*
* @param clientCompanyId 클라이언트 회사 ID
* @return 해당 회사의 프로젝트 리스트
*/
public List<Project> findAllByClientCompanyId(Long clientCompanyId) {
return projectRepository.findAllByClientCompanyId(clientCompanyId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import com.workhub.userTable.entity.UserRole;
import com.workhub.userTable.entity.UserTable;
import com.workhub.userTable.service.company.CompanyService;
import com.workhub.userTable.service.company.ReadCompanyService;
import com.workhub.userTable.service.user.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -41,6 +42,7 @@ public class ReadProjectService {
private final ProjectService projectService;
private final ProjectNodeService projectNodeService;
private final UserService userService;
private final ReadCompanyService readCompanyService;
private final CompanyService companyService;

/**
Expand Down Expand Up @@ -156,7 +158,7 @@ private BatchData loadBatchData(List<Long> projectIds, List<Project> projects) {
.map(Project::getClientCompanyId)
.distinct()
.toList();
Map<Long, Company> companyMap = companyService.getCompanyMapByCompanyIdIn(companyIds);
Map<Long, Company> companyMap = readCompanyService.getCompanyMapByCompanyIdIn(companyIds);

Map<Long, List<ProjectClientMember>> clientMemberMap = groupClientMembersByProjectId(allClientMembers);
Map<Long, List<ProjectDevMember>> devMemberMap = groupDevMembersByProjectId(allDevMembers);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
import com.workhub.userTable.dto.company.response.CompanyResponse;
import com.workhub.userTable.dto.company.response.CompanyTitleResponse;
import com.workhub.userTable.dto.user.response.UserNameResponse;
import com.workhub.userTable.service.company.CompanyService;
import com.workhub.userTable.service.company.CreateCompanyService;
import com.workhub.userTable.service.company.DeleteCompanyService;
import com.workhub.userTable.service.company.ReadCompanyService;
import com.workhub.userTable.service.company.UpdateCompanyService;
import com.workhub.userTable.service.user.UserService;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,13 +30,16 @@
@RequiredArgsConstructor
public class CompanyController implements CompanyApi {

private final CompanyService companyService;
private final CreateCompanyService createCompanyService;
private final ReadCompanyService readCompanyService;
private final UpdateCompanyService updateCompanyService;
private final DeleteCompanyService deleteCompanyService;
private final UserService userService;

@PostMapping("/add")
@Override
public ResponseEntity<ApiResponse<CompanyResponse>> registerCompany(@RequestBody @Valid CompanyRegisterRequest request) {
CompanyResponse response = companyService.registerCompany(request);
CompanyResponse response = createCompanyService.registerCompany(request);
return ApiResponse.created(response, "고객사가 등록되었습니다.");
}

Expand All @@ -42,29 +48,29 @@ public ResponseEntity<ApiResponse<CompanyResponse>> registerCompany(@RequestBody
public ResponseEntity<ApiResponse<Page<CompanyListResponse>>> getCompanies(
@PageableDefault(size = 20, sort = "createdAt", direction = Sort.Direction.DESC) Pageable pageable
) {
Page<CompanyListResponse> companies = companyService.getCompanies(pageable);
Page<CompanyListResponse> companies = readCompanyService.getCompanies(pageable);
return ApiResponse.success(companies);
}

@GetMapping("/list")
@Override
public ResponseEntity<ApiResponse<List<CompanyTitleResponse>>> getCompanyNames() {

List<CompanyTitleResponse> response = companyService.getCompanyNameList();
List<CompanyTitleResponse> response = readCompanyService.getCompanyNameList();
return ApiResponse.success(response);
}

@GetMapping("/detail/{companyId}")
@Override
public ResponseEntity<ApiResponse<CompanyDetailResponse>> getCompany(@PathVariable("companyId") Long companyId) {

CompanyDetailResponse company = companyService.getCompany(companyId);
CompanyDetailResponse company = readCompanyService.getCompany(companyId);
return ApiResponse.success(company);
}
@DeleteMapping("/{companyId}")
@Override
public ResponseEntity<ApiResponse<Object>> deleteCompany(@PathVariable("companyId") Long companyId) {
companyService.deleteCompany(companyId);
deleteCompanyService.deleteCompany(companyId);
return ApiResponse.success(null, "고객사가 비활성화되었습니다.");
}

Expand All @@ -74,7 +80,7 @@ public ResponseEntity<ApiResponse<CompanyResponse>> updateCompanyStatus(
@PathVariable("companyId") Long companyId,
@RequestBody @Valid CompanyStatusUpdateRequest request
) {
CompanyResponse response = companyService.updateCompanyStatus(companyId, request.status());
CompanyResponse response = updateCompanyService.updateCompanyStatus(companyId, request.status());
return ApiResponse.success(response, "고객사 상태가 변경되었습니다.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,9 @@

import com.workhub.global.error.ErrorCode;
import com.workhub.global.error.exception.BusinessException;
import com.workhub.project.entity.Project;
import com.workhub.project.entity.Status;
import com.workhub.project.repository.ProjectRepository;
import com.workhub.userTable.dto.company.request.CompanyRegisterRequest;
import com.workhub.userTable.dto.company.response.CompanyDetailResponse;
import com.workhub.userTable.dto.company.response.CompanyListResponse;
import com.workhub.userTable.dto.company.response.CompanyResponse;
import com.workhub.userTable.dto.company.response.CompanyTitleResponse;
import com.workhub.userTable.entity.Company;
import com.workhub.userTable.entity.CompanyStatus;
import com.workhub.userTable.entity.UserRole;
import com.workhub.userTable.repository.CompanyRepository;
import com.workhub.userTable.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
Expand All @@ -30,94 +20,72 @@
public class CompanyService {

private final CompanyRepository companyRepository;
private final ProjectRepository projectRepository;
private final UserRepository userRepository;

@Transactional
public CompanyResponse registerCompany(CompanyRegisterRequest request) {

validateDuplicateCompanyNumber(request.companyNumber());

Company company = Company.of(request);
Company savedCompany = companyRepository.save(company);

return CompanyResponse.from(savedCompany);
/**
* Company ID로 활성 회사 조회 (Core 메서드)
* 다른 도메인 서비스에서 사용
*
* @param companyId 회사 ID
* @return 활성 회사 엔티티
*/
@Transactional(readOnly = true)
public Company findById(Long companyId) {
return companyRepository.findByCompanyIdAndCompanystatus(companyId, CompanyStatus.ACTIVE)
.orElseThrow(() -> new BusinessException(ErrorCode.Company_NOT_EXISTS));
}

private void validateDuplicateCompanyNumber(String companyNumber) {
/**
* 사업자번호 중복 검증 (Core 유틸)
*
* @param companyNumber 사업자번호
* @throws BusinessException 중복된 사업자번호가 존재할 경우
*/
public void validateDuplicateCompanyNumber(String companyNumber) {
if (companyRepository.existsByCompanyNumber(companyNumber)) {
throw new BusinessException(ErrorCode.COMPANY_ALREADY_EXISTS);
}
}
@Transactional(readOnly = true)
public Page<CompanyListResponse> getCompanies(Pageable pageable) {
Page<Company> companies = companyRepository.findAllByCompanystatus(CompanyStatus.ACTIVE, pageable);

return companies.map(company -> {
// 해당 회사의 프로젝트 조회
List<Project> projects = projectRepository.findAllByClientCompanyId(company.getCompanyId());

// IN_PROGRESS와 총 프로젝트 카운트
long inProgressCount = projects.stream()
.filter(p -> p.getStatus() == Status.IN_PROGRESS)
.count();
long totalProjectCount = projects.size();

// 활성 클라이언트 멤버 수 조회 (company_id 기준)
long clientMemberCount = userRepository.countByCompanyIdAndRoleAndStatus(
company.getCompanyId(),
UserRole.CLIENT,
com.workhub.userTable.entity.Status.ACTIVE
);

// CompanyListResponse 생성
return CompanyListResponse.from(company, inProgressCount, totalProjectCount, clientMemberCount);
});
}

@Transactional(readOnly = true)
public List<CompanyTitleResponse> getCompanyNameList() {
return companyRepository.findAllByCompanystatus(CompanyStatus.ACTIVE).stream()
.map(CompanyTitleResponse::from)
.toList();
/**
* Company 저장
*
* @param company 저장할 회사 엔티티
* @return 저장된 회사 엔티티
*/
@Transactional
public Company save(Company company) {
return companyRepository.save(company);
}

/**
* 활성 회사 페이징 조회
*
* @param pageable 페이징 정보
* @return 활성 회사 페이지
*/
@Transactional(readOnly = true)
public CompanyDetailResponse getCompany(Long companyId) {
Company company = companyRepository.findByCompanyIdAndCompanystatus(companyId, CompanyStatus.ACTIVE)
.orElseThrow(() -> new BusinessException(ErrorCode.Company_NOT_EXISTS));
return CompanyDetailResponse.from(company);
public Page<Company> findAllByCompanystatus(Pageable pageable) {
return companyRepository.findAllByCompanystatus(CompanyStatus.ACTIVE, pageable);
}

/**
* 모든 활성 회사 조회
*
* @return 활성 회사 리스트
*/
@Transactional(readOnly = true)
public Company findById(Long companyId) {
return companyRepository.findByCompanyIdAndCompanystatus(companyId, CompanyStatus.ACTIVE)
.orElseThrow(() -> new BusinessException(ErrorCode.Company_NOT_EXISTS));
}

@Transactional
public void deleteCompany(Long companyId) {
Company company = companyRepository.findById(companyId)
.orElseThrow(() -> new BusinessException(ErrorCode.Company_NOT_EXISTS));
company.markDeleted();
}

@Transactional
public CompanyResponse updateCompanyStatus(Long companyId, CompanyStatus status) {
Company company = companyRepository.findById(companyId)
.orElseThrow(() -> new BusinessException(ErrorCode.Company_NOT_EXISTS));
company.updateStatus(status);
return CompanyResponse.from(company);
public List<Company> findAllActiveCompanies() {
return companyRepository.findAllByCompanystatus(CompanyStatus.ACTIVE);
}

/**
* 여러 companyId로 Company 맵을 배치 조회
* 여러 Company ID로 활성 회사 배치 조회
*
* @param companyIds 조회할 Company ID 리스트
* @return companyId를 키로 하는 Company
* @param companyIds 회사 ID 리스트
* @return 회사 ID를 키로 하는 회사
*/
@Transactional(readOnly = true)
public Map<Long, Company> getCompanyMapByCompanyIdIn(List<Long> companyIds) {
public Map<Long, Company> findAllByCompanyIdInAndCompanystatus(List<Long> companyIds) {
if (companyIds == null || companyIds.isEmpty()) {
return Map.of();
}
Expand All @@ -127,8 +95,25 @@ public Map<Long, Company> getCompanyMapByCompanyIdIn(List<Long> companyIds) {
.collect(Collectors.toMap(Company::getCompanyId, company -> company));
}

/**
* 활성 회사 수 조회
*
* @return 활성 회사 개수
*/
@Transactional(readOnly = true)
public Long countActiveCompanies() {
return companyRepository.countByCompanystatus(CompanyStatus.ACTIVE);
}

/**
* Company ID로 활성 회사 상세 조회 (DTO 반환용)
*
* @param companyId 회사 ID
* @return 활성 회사 엔티티
*/
@Transactional(readOnly = true)
public Company findByCompanyIdAndCompanystatus(Long companyId) {
return companyRepository.findByCompanyIdAndCompanystatus(companyId, CompanyStatus.ACTIVE)
.orElseThrow(() -> new BusinessException(ErrorCode.Company_NOT_EXISTS));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.workhub.userTable.service.company;

import com.workhub.userTable.dto.company.request.CompanyRegisterRequest;
import com.workhub.userTable.dto.company.response.CompanyResponse;
import com.workhub.userTable.entity.Company;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class CreateCompanyService {

private final CompanyService companyService;

/**
* 새로운 회사 등록
* 사업자번호 중복 검증 후 회사 정보를 저장
*
* @param request 회사 등록 요청 정보 (회사명, 사업자번호 등)
* @return 등록된 회사 정보
* @throws BusinessException 사업자번호가 이미 존재할 경우
*/
public CompanyResponse registerCompany(CompanyRegisterRequest request) {

companyService.validateDuplicateCompanyNumber(request.companyNumber());

Company company = Company.of(request);
Company savedCompany = companyService.save(company);

return CompanyResponse.from(savedCompany);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.workhub.userTable.service.company;

import com.workhub.userTable.entity.Company;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional
public class DeleteCompanyService {

private final CompanyService companyService;

/**
* 회사 소프트 삭제
* 실제 데이터를 삭제하지 않고 deletedAt 타임스탬프를 기록
*
* @param companyId 삭제할 회사 ID
* @throws BusinessException 회사가 존재하지 않을 경우
*/
public void deleteCompany(Long companyId) {
Company company = companyService.findById(companyId);
company.markDeleted();
}
}
Loading