Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -234,17 +234,17 @@ ResponseEntity<SearchProjectResponse> searchProject(
@RequestParam(required = false) String keyword
);

@Operation(summary = "외부교수 목록 조회", description = "사용자가 연구 생성/수정할 때 외부교수 목록을 조회하는 GET API")
@Operation(summary = "외부교수 목록 조회", description = "사용자가 연구 생성/수정할 때 외부교수 목록을 조회하는 GET API. 이름, 기관, 부서로 통합 검색 가능")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "내 연구 조회 성공"
description = "외부교수 조회 성공"
)
}
)
ResponseEntity<ExternalProfessorFindAllResponse> getExternalProfessors(
@RequestParam(required = false) String name
@RequestParam(required = false) String keyword
);

@Operation(summary = "사용자 연구 조회", description = "사용자 ID로 사용자가 참여하는 연구 목록을 조회하는 GET API")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,9 @@ public ResponseEntity<SearchProjectResponse> searchProject(

@GetMapping("/external-professors")
public ResponseEntity<ExternalProfessorFindAllResponse> getExternalProfessors(
@RequestParam(required = false) String name
@RequestParam(required = false) String keyword
) {

return ResponseEntity.ok(projectService.getExternalProfessorsByName(name));
return ResponseEntity.ok(projectService.getExternalProfessorsByKeyword(keyword));
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ public interface ExternalProfessorRepository extends JpaRepository<ExternalProfe

List<ExternalProfessor> findAllByOrderByNameAsc();

@Query("select ex from ExternalProfessor ex where :name is null "
+ "or :name = '' "
+ "or ex.name like concat('%', :name, '%')")
List<ExternalProfessor> findAllByNameContaining(String name);
@Query("select ex from ExternalProfessor ex where :keyword is null "
+ "or :keyword = '' "
+ "or ex.name like concat('%', :keyword, '%') "
+ "or ex.organization like concat('%', :keyword, '%') "
+ "or ex.department like concat('%', :keyword, '%')")
List<ExternalProfessor> findAllByKeyword(String keyword);

boolean existsByNameAndOrganizationAndDepartmentAndPosition(String name, String organization, String department, String position);
}
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,8 @@ public void deleteExternalProfessor(Long professorId) {
externalProfessorRepository.delete(externalProfessor);
}

public ExternalProfessorFindAllResponse getExternalProfessorsByName(String name) {
List<ExternalProfessor> externalProfessors = externalProfessorRepository.findAllByNameContaining(name);
public ExternalProfessorFindAllResponse getExternalProfessorsByKeyword(String keyword) {
List<ExternalProfessor> externalProfessors = externalProfessorRepository.findAllByKeyword(keyword);

return ExternalProfessorFindAllResponse.of(externalProfessors);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public record CreateJournalRequest(
@NotBlank(message = "저널명은 필수입니다.")
String journalName,

@Schema(description = "년도", example = "2025")
@NotNull(message = "년도는 필수입니다.")
Integer year,

@Schema(description = "저널 분류", example = "SCI")
@NotNull(message = "저널 분류는 필수입니다.")
JournalCategory category,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.bmilab.backend.domain.research.paper.enums.ProfessorRole;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

import java.time.LocalDate;
import java.util.List;
Expand All @@ -11,6 +12,7 @@ public record CreatePaperRequest(
@Schema(description = "Accept 날짜", example = "2025-10-20")
LocalDate acceptDate,
@Schema(description = "Publish 날짜", example = "2025-12-05")
@NotNull(message = "Publish 날짜는 필수입니다.")
LocalDate publishDate,
@Schema(description = "저널 ID", example = "1")
Long journalId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public record UpdateJournalRequest(
@NotBlank(message = "저널명은 필수입니다.")
String journalName,

@Schema(description = "년도", example = "2025")
@NotNull(message = "년도는 필수입니다.")
Integer year,

@Schema(description = "저널 분류", example = "SCI")
@NotNull(message = "저널 분류는 필수입니다.")
JournalCategory category,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.bmilab.backend.domain.research.paper.enums.ProfessorRole;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

import java.time.LocalDate;
import java.util.List;
Expand All @@ -11,6 +12,7 @@ public record UpdatePaperRequest(
@Schema(description = "Accept 날짜", example = "2025-10-21")
LocalDate acceptDate,
@Schema(description = "Publish 날짜", example = "2025-12-06")
@NotNull(message = "Publish 날짜는 필수입니다.")
LocalDate publishDate,
@Schema(description = "저널 ID", example = "1")
Long journalId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public record JournalResponse(
@Schema(description = "저널명")
String journalName,

@Schema(description = "년도")
Integer year,

@Schema(description = "저널 분류")
JournalCategory category,

Expand Down Expand Up @@ -39,9 +42,13 @@ public record JournalResponse(
String issue
) {
public static JournalResponse from(Journal journal) {
if (journal == null) {
return null;
}
return new JournalResponse(
journal.getId(),
journal.getJournalName(),
journal.getYear(),
journal.getCategory(),
journal.getPublisher(),
journal.getPublishCountry(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ public record JournalSummaryResponse(
@Schema(description = "저널명")
String journalName,

@Schema(description = "년도")
Integer year,

@Schema(description = "저널 분류")
JournalCategory category,

Expand Down Expand Up @@ -42,6 +45,7 @@ public static JournalSummaryResponse from(Journal journal) {
return new JournalSummaryResponse(
journal.getId(),
journal.getJournalName(),
journal.getYear(),
journal.getCategory(),
journal.getPublisher(),
journal.getPublishCountry(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import lombok.NoArgsConstructor;

@Entity
@Table(name = "research_journals")
@Table(name = "research_journals", uniqueConstraints = {
@UniqueConstraint(columnNames = {"journal_name", "year"})
})
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Journal extends BaseTimeEntity {
Expand All @@ -18,9 +20,12 @@ public class Journal extends BaseTimeEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
@Column(name = "journal_name", nullable = false)
private String journalName;

@Column(nullable = false)
private Integer year;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
private JournalCategory category;
Expand Down Expand Up @@ -49,10 +54,11 @@ public class Journal extends BaseTimeEntity {
private String issue;

@Builder
public Journal(String journalName, JournalCategory category, String publisher,
public Journal(String journalName, Integer year, JournalCategory category, String publisher,
String publishCountry, String isbn, String issn, String eissn,
String jif, String jcrRank, String issue) {
this.journalName = journalName;
this.year = year;
this.category = category;
this.publisher = publisher;
this.publishCountry = publishCountry;
Expand All @@ -64,10 +70,11 @@ public Journal(String journalName, JournalCategory category, String publisher,
this.issue = issue;
}

public void update(String journalName, JournalCategory category, String publisher,
public void update(String journalName, Integer year, JournalCategory category, String publisher,
String publishCountry, String isbn, String issn, String eissn,
String jif, String jcrRank, String issue) {
this.journalName = journalName;
this.year = year;
this.category = category;
this.publisher = publisher;
this.publishCountry = publishCountry;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ public class Paper extends BaseTimeEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private LocalDate acceptDate;

private LocalDate publishDate;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "journal_id", nullable = false)
@JoinColumn(name = "journal_id")
private Journal journal;

@Column(nullable = false)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public enum PaperErrorCode implements ErrorCode {
PAPER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 논문을 찾을 수 없습니다."),
JOURNAL_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 저널을 찾을 수 없습니다."),
PAPER_ACCESS_DENIED(HttpStatus.FORBIDDEN, "해당 논문에 접근할 권한이 없습니다."),
DUPLICATE_CORRESPONDING_AUTHOR(HttpStatus.BAD_REQUEST, "중복된 교신저자가 존재합니다."),
;

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class JournalService {
public JournalResponse createJournal(CreateJournalRequest dto) {
Journal newJournal = Journal.builder()
.journalName(dto.journalName())
.year(dto.year())
.category(dto.category())
.publisher(dto.publisher())
.publishCountry(dto.publishCountry())
Expand Down Expand Up @@ -60,6 +61,7 @@ public JournalResponse updateJournal(Long journalId, UpdateJournalRequest dto) {
.orElseThrow(() -> new ApiException(PaperErrorCode.JOURNAL_NOT_FOUND));
journal.update(
dto.journalName(),
dto.year(),
dto.category(),
dto.publisher(),
dto.publishCountry(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ public class PaperService {
private final AuthorSyncService authorSyncService;

public PaperResponse createPaper(CreatePaperRequest dto) {
Journal journal = journalRepository.findById(dto.journalId())
.orElseThrow(() -> new ApiException(PaperErrorCode.JOURNAL_NOT_FOUND));
Journal journal = dto.journalId() != null
? journalRepository.findById(dto.journalId()).orElseThrow(() -> new ApiException(PaperErrorCode.JOURNAL_NOT_FOUND))
: null;
Task task = dto.taskId() != null
? taskRepository.findById(dto.taskId()).orElseThrow(() -> new ApiException(TaskErrorCode.TASK_NOT_FOUND))
: null;
Expand Down Expand Up @@ -109,6 +110,12 @@ public PaperResponse createPaper(CreatePaperRequest dto) {
List<Long> externalProfessorIds = dto.correspondingAuthors().stream()
.map(CreatePaperRequest.PaperCorrespondingAuthorRequest::externalProfessorId)
.collect(Collectors.toList());

// 교신저자 중복 체크
if (externalProfessorIds.size() != externalProfessorIds.stream().distinct().count()) {
throw new ApiException(PaperErrorCode.DUPLICATE_CORRESPONDING_AUTHOR);
}

List<ExternalProfessor> externalProfessors = externalProfessorRepository.findAllById(externalProfessorIds);
if (externalProfessors.size() != externalProfessorIds.size()) {
throw new ApiException(GlobalErrorCode.GLOBAL_NOT_FOUND);
Expand Down Expand Up @@ -157,8 +164,9 @@ public PaperResponse getPaper(Long paperId) {
public PaperResponse updatePaper(Long paperId, UpdatePaperRequest dto) {
Paper paper = paperRepository.findById(paperId)
.orElseThrow(() -> new ApiException(PaperErrorCode.PAPER_NOT_FOUND));
Journal journal = journalRepository.findById(dto.journalId())
.orElseThrow(() -> new ApiException(PaperErrorCode.JOURNAL_NOT_FOUND));
Journal journal = dto.journalId() != null
? journalRepository.findById(dto.journalId()).orElseThrow(() -> new ApiException(PaperErrorCode.JOURNAL_NOT_FOUND))
: null;
Task task = dto.taskId() != null
? taskRepository.findById(dto.taskId()).orElseThrow(() -> new ApiException(TaskErrorCode.TASK_NOT_FOUND))
: null;
Expand All @@ -174,6 +182,12 @@ public PaperResponse updatePaper(Long paperId, UpdatePaperRequest dto) {
List<Long> externalProfessorIds = dto.correspondingAuthors().stream()
.map(UpdatePaperRequest.PaperCorrespondingAuthorRequest::externalProfessorId)
.collect(Collectors.toList());

// 교신저자 중복 체크
if (externalProfessorIds.size() != externalProfessorIds.stream().distinct().count()) {
throw new ApiException(PaperErrorCode.DUPLICATE_CORRESPONDING_AUTHOR);
}

List<ExternalProfessor> externalProfessors = externalProfessorRepository.findAllById(externalProfessorIds);
if (externalProfessors.size() != externalProfessorIds.size()) {
throw new ApiException(GlobalErrorCode.GLOBAL_NOT_FOUND);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ public record CreatePatentRequest(
List<PatentAuthorRequest> patentAuthors,
@Schema(description = "비고", example = "국내 특허")
String remarks,
@Schema(description = "연계 프로젝트 ID", example = "1")
@Schema(description = "연계 프로젝트 ID (선택적)", example = "1")
Long projectId,
@Schema(description = "연계 과제 ID (선택적)", example = "1")
Long taskId,
@Schema(description = "첨부 파일 ID 목록", example = "[\"a1b2c3d4-e5f6-7890-1234-567890abcdef\"]")
List<java.util.UUID> fileIds
) {
@Schema(description = "특허 저자 요청 DTO")
@Schema(description = "특허 저자 요청 DTO (userId 또는 externalProfessorId 중 하나만 입력)")
public record PatentAuthorRequest(
@Schema(description = "사용자 ID", example = "1")
@Schema(description = "내부 사용자 ID (선택)", example = "1")
Long userId,
@Schema(description = "외부 인사 ID (선택)", example = "1")
Long externalProfessorId,
@Schema(description = "저자 역할", example = "발명자")
String role
) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,19 @@ public record UpdatePatentRequest(
List<PatentAuthorRequest> patentAuthors,
@Schema(description = "비고", example = "국내/미국 특허")
String remarks,
@Schema(description = "연계 프로젝트 ID", example = "2")
@Schema(description = "연계 프로젝트 ID (선택적)", example = "2")
Long projectId,
@Schema(description = "연계 과제 ID (선택적)", example = "1")
Long taskId,
@Schema(description = "첨부 파일 ID 목록", example = "[\"a1b2c3d4-e5f6-7890-1234-567890abcdef\"]")
List<java.util.UUID> fileIds
) {
@Schema(description = "특허 저자 요청 DTO")
@Schema(description = "특허 저자 요청 DTO (userId 또는 externalProfessorId 중 하나만 입력)")
public record PatentAuthorRequest(
@Schema(description = "사용자 ID", example = "1")
@Schema(description = "내부 사용자 ID (선택)", example = "1")
Long userId,
@Schema(description = "외부 인사 ID (선택)", example = "1")
Long externalProfessorId,
@Schema(description = "저자 역할", example = "발명자")
String role
) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ public PatentResponse(Patent patent, List<PatentAuthor> patentAuthors, List<File
patent.getApplicationNumber(),
patent.getPatentName(),
patent.getApplicantsAll(),
patentAuthors.stream().map(PatentAuthorResponse::new).collect(Collectors.toList()),
patentAuthors.stream().map(PatentAuthorResponse::from).collect(Collectors.toList()),
patent.getRemarks(),
patent.getProject().getId(),
patent.getProject().getTitle(),
patent.getProject() != null ? patent.getProject().getId() : null,
patent.getProject() != null ? patent.getProject().getTitle() : null,
patent.getTask() != null ? patent.getTask().getId() : null,
patent.getTask() != null ? patent.getTask().getTitle() : null,
files
Expand All @@ -42,13 +42,19 @@ public PatentResponse(Patent patent, List<PatentAuthor> patentAuthors, List<File
public record PatentAuthorResponse(
Long userId,
String userName,
String role
Long externalProfessorId,
String externalProfessorName,
String role,
boolean isInternal
) {
public PatentAuthorResponse(PatentAuthor patentAuthor) {
this(
patentAuthor.getUser().getId(),
patentAuthor.getUser().getName(),
patentAuthor.getRole()
public static PatentAuthorResponse from(PatentAuthor patentAuthor) {
return new PatentAuthorResponse(
patentAuthor.getUser() != null ? patentAuthor.getUser().getId() : null,
patentAuthor.getUser() != null ? patentAuthor.getUser().getName() : null,
patentAuthor.getExternalProfessor() != null ? patentAuthor.getExternalProfessor().getId() : null,
patentAuthor.getExternalProfessor() != null ? patentAuthor.getExternalProfessor().getName() : null,
patentAuthor.getRole(),
patentAuthor.isInternal()
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ public static PatentSummaryResponse from(Patent patent, List<PatentAuthor> paten
patent.getApplicationNumber(),
patent.getPatentName(),
patent.getApplicantsAll(),
patentAuthors.stream().map(PatentResponse.PatentAuthorResponse::new).collect(Collectors.toList()),
patentAuthors.stream().map(PatentResponse.PatentAuthorResponse::from).collect(Collectors.toList()),
patent.getRemarks(),
patent.getProject().getId(),
patent.getProject().getTitle(),
patent.getProject() != null ? patent.getProject().getId() : null,
patent.getProject() != null ? patent.getProject().getTitle() : null,
patent.getTask() != null ? patent.getTask().getId() : null,
patent.getTask() != null ? patent.getTask().getTitle() : null,
files
Expand Down
Loading
Loading