Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.dreamteam.alter.adapter.inbound.admin.workspace.controller;

import java.util.List;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.dreamteam.alter.adapter.inbound.admin.workspace.dto.AdminBusinessTypeRequestDto;
import com.dreamteam.alter.adapter.inbound.admin.workspace.dto.AdminBusinessTypeResponseDto;
import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse;
import com.dreamteam.alter.domain.workspace.command.AdminCreateBusinessTypeCommand;
import com.dreamteam.alter.domain.workspace.command.AdminUpdateBusinessTypeCommand;
import com.dreamteam.alter.domain.workspace.port.inbound.AdminCreateBusinessTypeUseCase;
import com.dreamteam.alter.domain.workspace.port.inbound.AdminDeleteBusinessTypeUseCase;
import com.dreamteam.alter.domain.workspace.port.inbound.AdminGetBusinessTypeListUseCase;
import com.dreamteam.alter.domain.workspace.port.inbound.AdminUpdateBusinessTypeUseCase;

import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/admin/business-types")
@PreAuthorize("hasAnyRole('ADMIN')")
@RequiredArgsConstructor
@Validated
public class AdminBusinessTypeController implements AdminBusinessTypeControllerSpec {

@Resource(name = "adminGetBusinessTypeList")
private final AdminGetBusinessTypeListUseCase adminGetBusinessTypeList;

@Resource(name = "adminCreateBusinessType")
private final AdminCreateBusinessTypeUseCase adminCreateBusinessType;

@Resource(name = "adminUpdateBusinessType")
private final AdminUpdateBusinessTypeUseCase adminUpdateBusinessType;

@Resource(name = "adminDeleteBusinessType")
private final AdminDeleteBusinessTypeUseCase adminDeleteBusinessType;

@Override
@GetMapping
public ResponseEntity<CommonApiResponse<List<AdminBusinessTypeResponseDto>>> getBusinessTypeList() {
List<AdminBusinessTypeResponseDto> result = adminGetBusinessTypeList.execute().stream()
.map(AdminBusinessTypeResponseDto::from)
.toList();
return ResponseEntity.ok(CommonApiResponse.of(result));
}

@Override
@PostMapping
public ResponseEntity<CommonApiResponse<AdminBusinessTypeResponseDto>> createBusinessType(
@Valid @RequestBody AdminBusinessTypeRequestDto request
) {
Long id = adminCreateBusinessType.execute(
new AdminCreateBusinessTypeCommand(request.getName(), request.getDescription())
);
return ResponseEntity.status(HttpStatus.CREATED)
.body(CommonApiResponse.of(
AdminBusinessTypeResponseDto.of(id, request.getName(), request.getDescription())
));
}

@Override
@PutMapping("/{id}")
public ResponseEntity<CommonApiResponse<Void>> updateBusinessType(
@PathVariable Long id,
@Valid @RequestBody AdminBusinessTypeRequestDto request
) {
adminUpdateBusinessType.execute(
id, new AdminUpdateBusinessTypeCommand(request.getName(), request.getDescription())
);
return ResponseEntity.ok(CommonApiResponse.empty());
}

@Override
@DeleteMapping("/{id}")
public ResponseEntity<CommonApiResponse<Void>> deleteBusinessType(
@PathVariable Long id
) {
adminDeleteBusinessType.execute(id);
return ResponseEntity.ok(CommonApiResponse.empty());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.dreamteam.alter.adapter.inbound.admin.workspace.controller;

import java.util.List;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;

import com.dreamteam.alter.adapter.inbound.admin.workspace.dto.AdminBusinessTypeRequestDto;
import com.dreamteam.alter.adapter.inbound.admin.workspace.dto.AdminBusinessTypeResponseDto;
import com.dreamteam.alter.adapter.inbound.common.dto.CommonApiResponse;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;

@Tag(name = "ADMIN - 업종(BusinessType) 관리 API")
public interface AdminBusinessTypeControllerSpec {

@Operation(summary = "업종 목록 조회", description = "관리자가 등록된 업종 전체 목록을 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "업종 목록 조회 성공")
})
ResponseEntity<CommonApiResponse<List<AdminBusinessTypeResponseDto>>> getBusinessTypeList();

@Operation(summary = "업종 생성", description = "관리자가 새 업종을 등록합니다. 이름은 중복될 수 없습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "201", description = "업종 생성 성공"),
@ApiResponse(responseCode = "409", description = "이미 존재하는 업종 (CONFLICT)")
})
ResponseEntity<CommonApiResponse<AdminBusinessTypeResponseDto>> createBusinessType(
@Valid @RequestBody AdminBusinessTypeRequestDto request
);

@Operation(summary = "업종 수정", description = "관리자가 업종의 이름/설명을 수정합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "업종 수정 성공"),
@ApiResponse(responseCode = "404", description = "존재하지 않는 업종 (NOT_FOUND)"),
@ApiResponse(responseCode = "409", description = "이미 존재하는 업종 (CONFLICT)")
})
ResponseEntity<CommonApiResponse<Void>> updateBusinessType(
@PathVariable Long id,
@Valid @RequestBody AdminBusinessTypeRequestDto request
);

@Operation(summary = "업종 삭제", description = "관리자가 업종을 삭제합니다. '기타' 업종 또는 업장/신청에서 사용 중인 업종은 삭제할 수 없습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "업종 삭제 성공"),
@ApiResponse(responseCode = "404", description = "존재하지 않는 업종 (NOT_FOUND)"),
@ApiResponse(responseCode = "409", description = "'기타' 업종 또는 사용 중인 업종 (CONFLICT)")
})
ResponseEntity<CommonApiResponse<Void>> deleteBusinessType(
@PathVariable Long id
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.dreamteam.alter.adapter.inbound.admin.workspace.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Schema(description = "업종(BusinessType) 생성/수정 요청 DTO")
public class AdminBusinessTypeRequestDto {

@NotBlank
@Size(max = 64)
@Schema(description = "업종명", example = "카페")
private String name;

@Size(max = 255)
@Schema(description = "업종 설명", example = "카페/디저트 매장")
private String description;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.dreamteam.alter.adapter.inbound.admin.workspace.dto;

import com.dreamteam.alter.domain.workspace.entity.BusinessType;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(access = AccessLevel.PRIVATE)
@Schema(description = "업종(BusinessType) 응답 DTO")
public class AdminBusinessTypeResponseDto {

@Schema(description = "업종 ID", example = "1")
private Long id;

@Schema(description = "업종명", example = "카페")
private String name;

@Schema(description = "업종 설명", example = "카페/디저트 매장")
private String description;

@Schema(description = "상세 입력 요구 여부 ('기타' 업종이면 true, 삭제 불가)", example = "false")
private boolean requiresDetail;

public static AdminBusinessTypeResponseDto from(BusinessType businessType) {
return AdminBusinessTypeResponseDto.builder()
.id(businessType.getId())
.name(businessType.getName())
.description(businessType.getDescription())
.requiresDetail(businessType.isRequiresDetail())
.build();
}

// 관리자 생성 업종은 항상 requiresDetail=false 이므로 재조회 없이 생성 결과를 구성한다.
public static AdminBusinessTypeResponseDto of(Long id, String name, String description) {
return AdminBusinessTypeResponseDto.builder()
.id(id)
.name(name)
.description(description)
.requiresDetail(false)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public class AdminWorkspaceRequestResponseDto {
@Schema(description = "업종", example = "카페")
private String businessType;

@Schema(description = "업종 상세 (업종이 '기타'인 경우)", example = "떡볶이 전문점")
private String businessTypeDetail;

@Schema(description = "연락처", example = "01012345678")
private String contact;

Expand Down Expand Up @@ -74,6 +77,7 @@ public static AdminWorkspaceRequestResponseDto from(
.businessRegistrationNo(entity.getBusinessRegistrationNo())
.businessName(entity.getBusinessName())
.businessType(entity.getBusinessType())
.businessTypeDetail(entity.getBusinessTypeDetail())
.contact(entity.getContact())
.status(DescribedEnumDto.of(entity.getStatus(), WorkspaceRequestStatus.describe()))
.fullAddress(entity.getFullAddress())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.dreamteam.alter.adapter.inbound.common.dto;

import com.dreamteam.alter.domain.workspace.entity.BusinessType;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Schema(title = "업종(BusinessType) 응답 DTO")
public class BusinessTypeResponseDto {

@NotNull
@Schema(title = "업종 ID", example = "1")
private Long id;

@NotBlank
@Schema(title = "업종 이름", example = "카페")
private String name;

@Schema(title = "상세 입력 요구 여부 ('기타' 업종이면 true)", example = "false")
private boolean requiresDetail;

@Schema(title = "업종 설명", example = "마스터에 없는 업종")
private String description;

public static BusinessTypeResponseDto from(BusinessType businessType) {
return new BusinessTypeResponseDto(
businessType.getId(),
businessType.getName(),
businessType.isRequiresDetail(),
businessType.getDescription()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Positive;
import lombok.*;

Expand Down Expand Up @@ -39,10 +38,6 @@ public class CreatePostingRequestDto {
@NotNull
private PaymentType paymentType;

@Schema(description = "키워드", example = "[2, 3, 1]")
@NotEmpty
private List<Long> keywords;

@Schema(description = "공고 스케줄", example = "[" +
"{" +
"\"workingDays\": [\"MONDAY\", \"WEDNESDAY\"], \"startTime\": \"09:00\", \"endTime\": \"18:00\", \"positionsNeeded\": 3, \"position\": \"설거지\"" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,19 @@ public class ManagerPostingListWorkspaceResponseDto {
@Schema(description = "업장 이름", example = "카페 알터")
private String businessName;

@NotBlank
@Schema(description = "업종", example = "카페")
private String businessType;

@Schema(description = "업종 상세 (업종이 '기타'인 경우)", example = "떡볶이 전문점")
private String businessTypeDetail;

public static ManagerPostingListWorkspaceResponseDto from(Workspace entity) {
return ManagerPostingListWorkspaceResponseDto.builder()
.id(entity.getId())
.businessName(entity.getBusinessName())
.businessType(entity.getBusinessType().getName())
.businessTypeDetail(entity.getBusinessTypeDetail())
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,6 @@ public class PostingDetailResponseDto {
@Schema(description = "생성일", example = "2023-10-01T12:00:00")
private LocalDateTime createdAt;

@NotNull
@Schema(description = "키워드", example = "[{\"id\":1,\"name\":\"카페\"},{\"id\":4,\"name\":\"분식\"}]")
private List<PostingKeywordListResponseDto> keywords;

@NotNull
@Schema(description = "공고 스케줄", example = "[{\"id\":1,\"workingDays\":[\"MONDAY\",\"WEDNESDAY\"],\"startTime\":\"09:00\",\"endTime\":\"18:00\",\"positionsNeeded\":3,\"positionsAvailable\":2,\"position\":\"홀서빙\"}]")
private List<PostingScheduleResponseDto> schedules;
Expand All @@ -74,9 +70,6 @@ public static PostingDetailResponseDto from(
.schedules(entity.getSchedules().stream()
.map(PostingScheduleResponseDto::from)
.toList())
.keywords(entity.getPostingKeywords().stream()
.map(PostingKeywordListResponseDto::from)
.toList())
.scrapped(entity.isScrapped())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.dreamteam.alter.adapter.inbound.general.posting.dto;

import com.dreamteam.alter.adapter.inbound.common.dto.BusinessTypeResponseDto;
import com.dreamteam.alter.adapter.inbound.common.dto.DescribedEnumDto;
import com.dreamteam.alter.domain.posting.type.PostingSortType;
import com.dreamteam.alter.domain.workspace.entity.BusinessType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;

Expand All @@ -26,11 +28,15 @@ public class PostingFilterOptionsResponseDto {
@Schema(description = "정렬 옵션 목록")
private List<DescribedEnumDto<PostingSortType>> sortOptions;

@Schema(description = "업종 목록")
private List<BusinessTypeResponseDto> businessTypes;

public static PostingFilterOptionsResponseDto of(
List<String> provinces,
List<String> districts,
List<String> towns,
List<PostingSortType> sortOptions
List<PostingSortType> sortOptions,
List<BusinessType> businessTypes
) {
return PostingFilterOptionsResponseDto.builder()
.provinces(provinces)
Expand All @@ -40,6 +46,10 @@ public static PostingFilterOptionsResponseDto of(
.map(sortType -> DescribedEnumDto.of(sortType, PostingSortType.describe()))
.toList()
)
.businessTypes(businessTypes.stream()
.map(BusinessTypeResponseDto::from)
.toList()
)
.build();
}

Expand Down
Loading
Loading