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
@@ -0,0 +1,24 @@
package repit.repit_api_server.domain.company;

/**
* 분석 서버 기업 계약(00 §6-1)의 길이·개수 상한. 분석 서버가 같은 규칙으로 422를 내므로,
* 패키지를 가져올 때와 스냅샷을 만들 때 같은 값으로 막는다.
*/
public final class CompanyLimits {

public static final int DISPLAY_NAME = 30;
public static final int FACT_TITLE = 20;
public static final int FACT_CONTENT = 200;
public static final int SIGNALS_PER_FACT = 3;
public static final int SIGNAL = 100;
public static final int INTERVIEWER_BRIEF = 600;
public static final int FACTS = 20;

private CompanyLimits() {
}

/** 분석 서버(Python)가 세는 글자 수와 맞춘다. String.length()는 이모지 하나를 둘로 센다. */
public static int length(String value) {
return value.codePointCount(0, value.length());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package repit.repit_api_server.domain.company.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import repit.repit_api_server.domain.company.dto.CompanySummaryResponse;
import repit.repit_api_server.domain.company.entity.enums.CompanyCategory;
import repit.repit_api_server.domain.company.service.CompanyQueryService;
import repit.repit_api_server.global.common.ApiResponse;

import java.util.List;

@RestController
@RequestMapping("/api/companies")
@RequiredArgsConstructor
public class CompanyController {

private final CompanyQueryService companyQueryService;

// 공개 기업 목록. 공개 기업이 없으면 빈 목록이고, 웹은 그때 기업 선택 섹션을 숨긴다.
@GetMapping
public ApiResponse<List<CompanySummaryResponse>> getCompanies(
@RequestParam(required = false) String q,
@RequestParam(required = false) CompanyCategory category) {
return ApiResponse.success(companyQueryService.search(q, category));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package repit.repit_api_server.domain.company.dto;

/** 채팅 서버 준비 요청의 company(00 §6-3). 꼬리질문은 이 요약만으로 기업을 반영한다. */
public record ChatCompany(String displayName, String interviewerBrief) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package repit.repit_api_server.domain.company.dto;

/** 면접·피드백 응답에 붙는 기업 표시. 기업을 고르지 않은 면접은 null이다. */
public record CompanyBadge(Long companyId, String displayName) {

// id는 면접의 company_id 컬럼에서 받는다. 스냅샷의 문자열 id를 다시 숫자로 바꾸지 않는다.
public static CompanyBadge of(Long companyId, CompanySnapshot snapshot) {
return snapshot == null ? null : new CompanyBadge(companyId, snapshot.displayName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package repit.repit_api_server.domain.company.dto;

import java.util.List;

/**
* 면접을 만들 때 복사해 두는 기업 정보. interview.company_snapshot에 이 모양 그대로 저장되고,
* 분석 서버 요청의 company(00 §6-1)에도 그대로 실린다 — 변환 코드를 따로 두지 않는다.
*
* <p>규모·별칭·출처·signalsOrigin은 프롬프트에 필요 없어 넣지 않는다. 기업 정보가 나중에 바뀌어도
* 지난 면접과 피드백은 이 스냅샷 기준으로 남는다.
*/
public record CompanySnapshot(
String companyId, String displayName, String category,
List<Fact> facts, String interviewerBrief) {

// 신호는 TALENT에만 있다. 나머지는 빈 목록이고, 분석 서버는 그것을 "신호 없음"으로 받는다.
public record Fact(String factId, String kind, String title, String content,
List<String> positiveSignals, List<String> concernSignals) {
}

// 채팅 서버는 실시간 경로라 요약만 받는다.
public ChatCompany toChat() {
return new ChatCompany(displayName, interviewerBrief);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package repit.repit_api_server.domain.company.dto;

import repit.repit_api_server.domain.company.entity.enums.CompanyCategory;
import repit.repit_api_server.domain.company.entity.enums.LegalSize;

import java.time.LocalDate;
import java.util.List;

/** GET /api/companies 한 건. */
public record CompanySummaryResponse(
Long companyId,
String displayName,
String legalName,
String parentDisplayName,
CompanyCategory category,
// 미확인이면 null이다.
LegalSize legalSize,
// TALENT fact의 title. 표시 순서대로 최대 3개.
List<String> talentKeywords,
LocalDate dataAsOf) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package repit.repit_api_server.domain.company.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.UpdateTimestamp;
import org.hibernate.type.SqlTypes;
import repit.repit_api_server.domain.company.entity.enums.CompanyCategory;
import repit.repit_api_server.domain.company.entity.enums.CompanyStatus;
import repit.repit_api_server.domain.company.entity.enums.LegalSize;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

/**
* 면접에서 고를 수 있는 기업. 기동할 때 기업 데이터 패키지에서 가져온다 — {@code CompanyDataImporter} 참고.
*
* <p>행은 지우지 않는다. 면접이 company_id로 가리키고 있어, 패키지에서 빠진 기업은 HIDDEN으로만 바꾼다.
*/
@Entity
@Table(name = "company")
@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class CompanyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "company_id")
private Long companyId;

// 패키지의 영구 키. 가져올 때 이 값으로 기존 행을 찾는다.
@Column(nullable = false, unique = true, length = 40)
private String code;

@Column(nullable = false, length = 30)
private String displayName;

@Column(nullable = false, length = 100)
private String legalName;

// 검색용 다른 이름.
@JdbcTypeCode(SqlTypes.JSON)
@Column(nullable = false, columnDefinition = "jsonb")
private List<String> aliases;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_company_id")
private CompanyEntity parent;

@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
private CompanyCategory category;

// 비어 있으면 미확인이다.
@Enumerated(EnumType.STRING)
@Column(length = 10)
private LegalSize legalSize;

@Column(columnDefinition = "TEXT")
private String legalSizeSource;

@Column(length = 8, unique = true)
private String dartCorpCode;

@Column(length = 10, unique = true)
private String businessRegNo;

@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 10)
private CompanyStatus status;

// 채팅 서버 꼬리질문이 받는 유일한 기업 정보.
@Column(nullable = false, columnDefinition = "TEXT")
private String interviewerBrief;

@Column(nullable = false)
private LocalDate dataAsOf;

@Column(length = 50)
private String reviewedBy;

private LocalDate reviewedAt;

// 가져온 패키지 파일 바이트의 SHA-256. 같으면 다시 가져오지 않는다.
@Column(nullable = false, length = 64)
private String contentHash;

@CreationTimestamp
@Column(nullable = false, updatable = false)
private LocalDateTime createdAt;

@UpdateTimestamp
@Column(nullable = false)
private LocalDateTime updatedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package repit.repit_api_server.domain.company.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;
import repit.repit_api_server.domain.company.entity.enums.CompanyFactKind;
import repit.repit_api_server.domain.company.entity.enums.SignalsOrigin;

import java.util.List;

/**
* 기업 정보 한 줄. 패키지를 다시 가져오면 지우고 다시 넣으므로 fact_id가 바뀐다.
* 이미 만든 면접은 스냅샷 안의 factId를 쓰므로 영향이 없다.
*/
@Entity
@Table(name = "company_fact")
@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class CompanyFactEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "fact_id")
private Long factId;

@Column(nullable = false)
private Long companyId;

@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
private CompanyFactKind kind;

// 짧은 이름. 목록 카드 키워드와 피드백 태그에 쓴다.
@Column(nullable = false, length = 20)
private String title;

@Column(nullable = false, length = 200)
private String content;

// 인재상(TALENT)에만 있다. 나머지는 빈 목록이다.
@JdbcTypeCode(SqlTypes.JSON)
@Column(nullable = false, columnDefinition = "jsonb")
private List<String> positiveSignals;

@JdbcTypeCode(SqlTypes.JSON)
@Column(nullable = false, columnDefinition = "jsonb")
private List<String> concernSignals;

@Enumerated(EnumType.STRING)
@Column(length = 10)
private SignalsOrigin signalsOrigin;

@Column(nullable = false)
private Long sourceId;

// 패키지 파일 안 순서(0부터). 표시·전달 순서다.
@Column(nullable = false)
private int sortOrder;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package repit.repit_api_server.domain.company.entity;

import jakarta.persistence.*;
import lombok.*;
import repit.repit_api_server.domain.company.entity.enums.CompanySourceType;

import java.time.LocalDate;

/** 기업 정보의 출처. 화면·프롬프트에는 나가지 않고 DB에만 둔다. */
@Entity
@Table(name = "company_source")
@Getter
@Setter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class CompanySourceEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "source_id")
private Long sourceId;

@Column(nullable = false)
private Long companyId;

// 패키지 안에서 fact가 출처를 가리키는 키.
@Column(nullable = false, length = 40)
private String sourceKey;

@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
private CompanySourceType sourceType;

@Column(nullable = false, columnDefinition = "TEXT")
private String url;

@Column(length = 200)
private String title;

@Column(length = 100)
private String scope;

private LocalDate publishedAt;

@Column(nullable = false)
private LocalDate collectedAt;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package repit.repit_api_server.domain.company.entity.enums;

/** 화면 분류. 선언 순서가 목록 정렬 순서다(00 §5). */
public enum CompanyCategory {
IT_SERVICE,
PLATFORM,
FINTECH,
GAME,
CONSUMER,
SECURITY
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package repit.repit_api_server.domain.company.entity.enums;

/** 기업 정보 종류. 선언 순서가 프롬프트 블록의 표시 순서다(00 §5). */
public enum CompanyFactKind {
BUSINESS,
PROBLEM,
TALENT,
MISSION_VISION,
TECH_STACK
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package repit.repit_api_server.domain.company.entity.enums;

public enum CompanySourceType {
CAREER_PAGE,
ABOUT_PAGE,
TECH_BLOG,
DART,
FTC,
MID_SIZE_REGISTRY,
SME_REGISTRY,
GITHUB,
JOB_API,
SAMPLE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package repit.repit_api_server.domain.company.entity.enums;

/** 기업 행은 지우지 않는다. 패키지에서 빠지면 HIDDEN이 되고 목록·면접 생성에서 보이지 않는다. */
public enum CompanyStatus {
PUBLISHED,
HIDDEN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package repit.repit_api_server.domain.company.entity.enums;

/** 법적 규모. 확인하지 못했으면 값이 없다 — 모른다고 SMALL로 채우지 않는다. */
public enum LegalSize {
LARGE,
MID,
SMALL
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package repit.repit_api_server.domain.company.entity.enums;

/** 인재상 신호의 출처. SOURCE는 출처에 행동 기준이 있는 것, DERIVED는 가치 문구에서 서비스가 만든 연습용 해석이다. */
public enum SignalsOrigin {
SOURCE,
DERIVED
}
Loading
Loading