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 @@ -19,6 +19,7 @@
import java.util.UUID;
import org.springframework.http.MediaType;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
Expand Down Expand Up @@ -88,4 +89,31 @@ public DocumentRequestDraftResponse upsert(
);
return DocumentRequestDraftResponse.from(draft);
}

@Operation(
operationId = "getDocumentRequestDraft",
summary = "문서 요청 초안 조회",
description = "저장된 요청 안내문과 최신 version을 조회하여 화면을 복구합니다."
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
description = "조회 성공",
content = @Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = @Schema(implementation = DocumentRequestDraftResponse.class)
)
),
@ApiResponse(responseCode = "401", ref = "#/components/responses/Unauthorized"),
@ApiResponse(responseCode = "403", ref = "#/components/responses/Forbidden"),
@ApiResponse(responseCode = "404", ref = "#/components/responses/NotFound")
})
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
@PreAuthorize("hasAnyRole('ADMIN', 'HR')")
public DocumentRequestDraftResponse find(
@Parameter(description = "업무 ID") @PathVariable UUID taskId
) {
ActorContext actor = actorContextProvider.requireCurrentActor();
return DocumentRequestDraftResponse.from(documentRequestDraftService.find(taskId, actor));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fowoco.server.document.domain.DocumentRequestDraft;
import com.fowoco.server.document.domain.DocumentRequestReviewStatus;
import com.fowoco.server.worker.domain.DocumentType;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.Instant;
import java.util.List;
import java.util.UUID;

@Schema(name = "DocumentRequestDraftResponse", description = "업무별 문서 요청 초안 저장 결과")
Expand All @@ -13,6 +16,18 @@ public final class DocumentRequestDraftResponse {
@Schema(name = "draft_id", format = "uuid", requiredMode = Schema.RequiredMode.REQUIRED)
private final UUID draftId;

@JsonProperty("language")
@Schema(requiredMode = Schema.RequiredMode.REQUIRED, example = "vi")
private final String language;

@JsonProperty("document_types")
@Schema(name = "document_types", requiredMode = Schema.RequiredMode.REQUIRED)
private final List<DocumentType> documentTypes;

@JsonProperty("message")
@Schema(description = "근로자에게 표시할 요청 안내문", nullable = true)
private final String message;

@JsonProperty("version")
@Schema(requiredMode = Schema.RequiredMode.REQUIRED)
private final long version;
Expand All @@ -21,25 +36,65 @@ public final class DocumentRequestDraftResponse {
@Schema(name = "review_status", requiredMode = Schema.RequiredMode.REQUIRED)
private final DocumentRequestReviewStatus reviewStatus;

private DocumentRequestDraftResponse(UUID draftId, long version, DocumentRequestReviewStatus reviewStatus) {
@JsonProperty("updated_at")
@Schema(name = "updated_at", format = "date-time", requiredMode = Schema.RequiredMode.REQUIRED)
private final Instant updatedAt;

private DocumentRequestDraftResponse(
UUID draftId,
String language,
List<DocumentType> documentTypes,
String message,
long version,
DocumentRequestReviewStatus reviewStatus,
Instant updatedAt
) {
this.draftId = draftId;
this.language = language;
this.documentTypes = List.copyOf(documentTypes);
this.message = message;
this.version = version;
this.reviewStatus = reviewStatus;
this.updatedAt = updatedAt;
}

public static DocumentRequestDraftResponse from(DocumentRequestDraft draft) {
return new DocumentRequestDraftResponse(draft.draftId(), draft.version(), draft.reviewStatus());
return new DocumentRequestDraftResponse(
draft.draftId(),
draft.language(),
draft.documentTypes(),
draft.message(),
draft.version(),
draft.reviewStatus(),
draft.updatedAt()
);
}

public UUID getDraftId() {
return draftId;
}

public String getLanguage() {
return language;
}

public List<DocumentType> getDocumentTypes() {
return documentTypes;
}

public String getMessage() {
return message;
}

public long getVersion() {
return version;
}

public DocumentRequestReviewStatus getReviewStatus() {
return reviewStatus;
}

public Instant getUpdatedAt() {
return updatedAt;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.time.Instant;
import java.util.Comparator;
import java.util.Optional;
import java.util.UUID;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -106,6 +107,15 @@ public DocumentRequestDraft upsert(
return saved;
}

@Transactional(readOnly = true)
public DocumentRequestDraft find(UUID taskId, ActorContext actor) {
tenantDatabaseContext.setCompanyIdForCurrentTransaction(actor.companyId());
taskRepository.findByIdAndCompanyId(taskId, actor.companyId())
.orElseThrow(() -> new ApiException(TaskErrorCode.TASK_NOT_FOUND));
return documentRequestDraftRepository.findByTaskIdAndCompanyId(taskId, actor.companyId())
.orElseThrow(() -> new ApiException(DocumentErrorCode.DOCUMENT_REQUEST_DRAFT_NOT_FOUND));
}

private void appendAudit(
ActorContext actor,
AuditAction action,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ public enum DocumentErrorCode implements ApiErrorCode {
HttpStatus.CONFLICT,
"다른 사용자가 먼저 수정했습니다. 새로고침 후 다시 시도해 주세요."
),
DOCUMENT_REQUEST_DRAFT_NOT_FOUND(
HttpStatus.NOT_FOUND,
"문서 요청 초안을 찾을 수 없습니다."
),
DOCUMENT_NOT_FOUND(
HttpStatus.NOT_FOUND,
"문서를 찾을 수 없습니다."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public WorkerLinkViewController(WorkerLinkViewService workerLinkViewService) {
schema = @Schema(implementation = WorkerLinkViewResponse.class)
)
),
@ApiResponse(responseCode = "409", description = "근로자 안내 초안이 아직 준비되지 않음"),
@ApiResponse(responseCode = "410", description = "링크를 찾을 수 없거나 더 이상 사용할 수 없음"),
@ApiResponse(responseCode = "429", description = "요청 과다")
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.fowoco.server.workerlink.api;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fowoco.server.worker.domain.DocumentType;
import com.fowoco.server.workerlink.application.WorkerLinkViewResult;
import com.fowoco.server.workerlink.domain.WorkerResponseType;
import io.swagger.v3.oas.annotations.media.Schema;
Expand All @@ -14,32 +15,62 @@ public final class WorkerLinkViewResponse {
@Schema(description = "번역된 안내 내용")
private final String guidance;

@JsonProperty("language")
@Schema(description = "안내문 언어 코드", example = "vi")
private final String language;

@JsonProperty("due_date")
@Schema(name = "due_date", description = "제출 마감일")
private final LocalDate dueDate;

@JsonProperty("requested_document_types")
@Schema(name = "requested_document_types", description = "근로자에게 요청한 서류 유형")
private final List<DocumentType> requestedDocumentTypes;

@JsonProperty("allowed_responses")
@Schema(name = "allowed_responses", description = "이 링크에서 허용되는 응답 유형")
private final List<WorkerResponseType> allowedResponses;

private WorkerLinkViewResponse(String guidance, LocalDate dueDate, List<WorkerResponseType> allowedResponses) {
private WorkerLinkViewResponse(
String guidance,
String language,
LocalDate dueDate,
List<DocumentType> requestedDocumentTypes,
List<WorkerResponseType> allowedResponses
) {
this.guidance = guidance;
this.language = language;
this.dueDate = dueDate;
this.requestedDocumentTypes = List.copyOf(requestedDocumentTypes);
this.allowedResponses = allowedResponses;
}

public static WorkerLinkViewResponse from(WorkerLinkViewResult result) {
return new WorkerLinkViewResponse(result.guidance(), result.dueDate(), result.allowedResponses());
return new WorkerLinkViewResponse(
result.guidance(),
result.language(),
result.dueDate(),
result.requestedDocumentTypes(),
result.allowedResponses()
);
}

public String getGuidance() {
return guidance;
}

public String getLanguage() {
return language;
}

public LocalDate getDueDate() {
return dueDate;
}

public List<DocumentType> getRequestedDocumentTypes() {
return requestedDocumentTypes;
}

public List<WorkerResponseType> getAllowedResponses() {
return allowedResponses;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.fowoco.server.workerlink.application;

import com.fowoco.server.worker.domain.DocumentType;
import com.fowoco.server.workerlink.domain.WorkerResponseType;
import java.time.LocalDate;
import java.util.List;

public record WorkerLinkViewResult(
String guidance,
String language,
LocalDate dueDate,
List<DocumentType> requestedDocumentTypes,
List<WorkerResponseType> allowedResponses
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
import com.fowoco.server.common.id.UuidGenerator;
import com.fowoco.server.common.security.TenantDatabaseContext;
import com.fowoco.server.common.web.RequestMetadata;
import com.fowoco.server.document.application.port.DocumentRequestDraftRepository;
import com.fowoco.server.document.domain.DocumentRequestDraft;
import com.fowoco.server.task.application.port.TaskRepository;
import com.fowoco.server.task.domain.Task;
import com.fowoco.server.workerlink.application.error.WorkerLinkErrorCode;
import com.fowoco.server.workerlink.application.port.WorkerLinkRepository;
import com.fowoco.server.workerlink.application.port.WorkerLinkTenantBootstrap;
Expand All @@ -30,6 +34,8 @@ public class WorkerLinkViewService {
private final WorkerLinkTenantBootstrap workerLinkTenantBootstrap;
private final TenantDatabaseContext tenantDatabaseContext;
private final WorkerLinkRepository workerLinkRepository;
private final TaskRepository taskRepository;
private final DocumentRequestDraftRepository documentRequestDraftRepository;
private final WorkerLinkHasher workerLinkHasher;
private final AuditEventRepository auditRepository;
private final UuidGenerator uuidGenerator;
Expand All @@ -39,6 +45,8 @@ public WorkerLinkViewService(
WorkerLinkTenantBootstrap workerLinkTenantBootstrap,
TenantDatabaseContext tenantDatabaseContext,
WorkerLinkRepository workerLinkRepository,
TaskRepository taskRepository,
DocumentRequestDraftRepository documentRequestDraftRepository,
WorkerLinkHasher workerLinkHasher,
AuditEventRepository auditRepository,
UuidGenerator uuidGenerator,
Expand All @@ -47,6 +55,8 @@ public WorkerLinkViewService(
this.workerLinkTenantBootstrap = workerLinkTenantBootstrap;
this.tenantDatabaseContext = tenantDatabaseContext;
this.workerLinkRepository = workerLinkRepository;
this.taskRepository = taskRepository;
this.documentRequestDraftRepository = documentRequestDraftRepository;
this.workerLinkHasher = workerLinkHasher;
this.auditRepository = auditRepository;
this.uuidGenerator = uuidGenerator;
Expand All @@ -71,6 +81,13 @@ public WorkerLinkViewResult view(String rawToken, RequestMetadata metadata) {
throw new ApiException(WorkerLinkErrorCode.WORKER_LINK_NOT_FOUND);
}

Task task = taskRepository.findByIdAndCompanyId(link.taskId(), companyId)
.orElseThrow(() -> new ApiException(WorkerLinkErrorCode.WORKER_LINK_NOT_FOUND));
DocumentRequestDraft draft = documentRequestDraftRepository
.findByTaskIdAndCompanyId(link.taskId(), companyId)
.filter(value -> value.message() != null && !value.message().isBlank())
.orElseThrow(() -> new ApiException(WorkerLinkErrorCode.WORKER_LINK_CONTENT_NOT_READY));

auditRepository.append(new AuditEvent(
uuidGenerator.generate(),
companyId,
Expand All @@ -87,11 +104,11 @@ public WorkerLinkViewResult view(String rawToken, RequestMetadata metadata) {
now
));

// document-request-draft 연동해서 guidance/dueDate 실제 값 채우기.
// AI Agent(Language Agent) 다음에 다시 확인
return new WorkerLinkViewResult(
"document-request-draft 연동 전",
null,
draft.message(),
draft.language(),
task.dueDate(),
draft.documentTypes(),
List.of(WorkerResponseType.values())
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ public enum WorkerLinkErrorCode implements ApiErrorCode {
HttpStatus.UNPROCESSABLE_CONTENT,
"현재 사용할 수 있는 근로자 링크만 전달 완료로 기록할 수 있습니다."
),
WORKER_LINK_CONTENT_NOT_READY(
HttpStatus.CONFLICT,
"근로자에게 표시할 요청 안내가 아직 준비되지 않았습니다."
),

WORKER_LINK_NOT_FOUND(HttpStatus.GONE, "링크를 찾을 수 없거나 더 이상 사용할 수 없습니다."),
UPLOAD_NOT_AVAILABLE(
Expand Down
Loading
Loading