mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 11:26:25 +00:00
Feat: 회의록 수정 API 개발
This commit is contained in:
parent
7e2094bcbc
commit
f1e9565d5b
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,9 @@
|
|||||||
package com.unicorn.hgzero.meeting.biz.service;
|
package com.unicorn.hgzero.meeting.biz.service;
|
||||||
|
|
||||||
|
import com.unicorn.hgzero.common.exception.BusinessException;
|
||||||
|
import com.unicorn.hgzero.common.exception.ErrorCode;
|
||||||
import com.unicorn.hgzero.meeting.biz.domain.AgendaSection;
|
import com.unicorn.hgzero.meeting.biz.domain.AgendaSection;
|
||||||
|
import com.unicorn.hgzero.meeting.infra.dto.request.UpdateAgendaSectionsRequest;
|
||||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.AgendaSectionEntity;
|
import com.unicorn.hgzero.meeting.infra.gateway.entity.AgendaSectionEntity;
|
||||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.AgendaSectionRepository;
|
import com.unicorn.hgzero.meeting.infra.gateway.repository.AgendaSectionRepository;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
@ -88,4 +91,50 @@ public class AgendaSectionService {
|
|||||||
.map(AgendaSectionEntity::toDomain)
|
.map(AgendaSectionEntity::toDomain)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 안건 섹션 요약 수정
|
||||||
|
* @param agendaId 안건 ID
|
||||||
|
* @param summary 새로운 요약 내용
|
||||||
|
* @return 수정된 안건 섹션
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public AgendaSection updateAgendaSummary(String agendaId, String summary) {
|
||||||
|
log.info("안건 섹션 요약 수정 - agendaId: {}, summary length: {}",
|
||||||
|
agendaId, summary != null ? summary.length() : 0);
|
||||||
|
|
||||||
|
// 먼저 존재 여부 확인
|
||||||
|
AgendaSectionEntity entity = agendaSectionRepository.findById(agendaId)
|
||||||
|
.orElseThrow(() -> new BusinessException(ErrorCode.AGENDA_SECTION_NOT_FOUND,
|
||||||
|
"안건 섹션을 찾을 수 없습니다: " + agendaId));
|
||||||
|
|
||||||
|
// Native Query로 summary만 업데이트
|
||||||
|
agendaSectionRepository.updateSummaryById(agendaId, summary);
|
||||||
|
|
||||||
|
// 업데이트된 엔티티 다시 조회
|
||||||
|
AgendaSectionEntity updatedEntity = agendaSectionRepository.findById(agendaId)
|
||||||
|
.orElseThrow(() -> new BusinessException(ErrorCode.AGENDA_SECTION_NOT_FOUND,
|
||||||
|
"업데이트된 안건 섹션을 찾을 수 없습니다: " + agendaId));
|
||||||
|
|
||||||
|
log.info("안건 섹션 요약 수정 완료 - agendaId: {}", agendaId);
|
||||||
|
return updatedEntity.toDomain();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 여러 안건 섹션의 요약을 한 번에 수정
|
||||||
|
* @param updateItems 안건 ID와 새로운 요약 내용의 목록
|
||||||
|
* @return 수정된 안건 섹션 목록
|
||||||
|
*/
|
||||||
|
@Transactional
|
||||||
|
public List<AgendaSection> updateMultipleAgendaSummaries(
|
||||||
|
List<UpdateAgendaSectionsRequest.AgendaUpdateItem> updateItems) {
|
||||||
|
log.info("안건 섹션 일괄 수정 - 개수: {}", updateItems.size());
|
||||||
|
|
||||||
|
List<AgendaSection> updatedSections = updateItems.stream()
|
||||||
|
.map(item -> updateAgendaSummary(item.getAgendaId(), item.getContent()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
log.info("안건 섹션 일괄 수정 완료 - 개수: {}", updatedSections.size());
|
||||||
|
return updatedSections;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -14,6 +14,7 @@ import com.unicorn.hgzero.meeting.biz.service.MinutesSectionService;
|
|||||||
import com.unicorn.hgzero.meeting.biz.service.TodoService;
|
import com.unicorn.hgzero.meeting.biz.service.TodoService;
|
||||||
import com.unicorn.hgzero.meeting.biz.service.AgendaSectionService;
|
import com.unicorn.hgzero.meeting.biz.service.AgendaSectionService;
|
||||||
import com.unicorn.hgzero.meeting.infra.dto.request.UpdateMinutesRequest;
|
import com.unicorn.hgzero.meeting.infra.dto.request.UpdateMinutesRequest;
|
||||||
|
import com.unicorn.hgzero.meeting.infra.dto.request.UpdateAgendaSectionsRequest;
|
||||||
import com.unicorn.hgzero.meeting.infra.dto.response.MinutesDetailResponse;
|
import com.unicorn.hgzero.meeting.infra.dto.response.MinutesDetailResponse;
|
||||||
import com.unicorn.hgzero.meeting.infra.dto.response.MinutesListResponse;
|
import com.unicorn.hgzero.meeting.infra.dto.response.MinutesListResponse;
|
||||||
import com.unicorn.hgzero.meeting.infra.cache.CacheService;
|
import com.unicorn.hgzero.meeting.infra.cache.CacheService;
|
||||||
@ -414,6 +415,54 @@ public class MinutesController {
|
|||||||
.body(ApiResponse.errorWithType("섹션 잠금 해제에 실패했습니다"));
|
.body(ApiResponse.errorWithType("섹션 잠금 해제에 실패했습니다"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회의록 안건별 수정
|
||||||
|
* PUT /api/minutes/{minutesId}/agenda-sections
|
||||||
|
*/
|
||||||
|
@PutMapping("/{minutesId}/agenda-sections")
|
||||||
|
@Operation(summary = "회의록 안건별 수정",
|
||||||
|
description = "회의록의 안건별 내용을 수정합니다. 각 안건의 summary 필드만 업데이트됩니다.")
|
||||||
|
@ApiResponses({
|
||||||
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "성공"),
|
||||||
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "400", description = "잘못된 요청"),
|
||||||
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "권한 없음"),
|
||||||
|
@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "회의록을 찾을 수 없음")
|
||||||
|
})
|
||||||
|
public ResponseEntity<ApiResponse<String>> updateAgendaSections(
|
||||||
|
@RequestHeader("X-User-Id") String userId,
|
||||||
|
@RequestHeader(value = "X-User-Name", defaultValue = "System") String userName,
|
||||||
|
@PathVariable String minutesId,
|
||||||
|
@Valid @RequestBody UpdateAgendaSectionsRequest request) {
|
||||||
|
|
||||||
|
log.info("[API] 회의록 안건별 수정 요청 - userId: {}, minutesId: {}, agendaCount: {}",
|
||||||
|
userId, minutesId, request.getAgendas().size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 권한 검증 제거 - 모든 사용자가 수정 가능
|
||||||
|
log.info("회의록 수정 권한 검증 스킵 - 모든 사용자 수정 가능");
|
||||||
|
|
||||||
|
// 안건별 내용 수정
|
||||||
|
List<AgendaSection> updatedSections = agendaSectionService.updateMultipleAgendaSummaries(request.getAgendas());
|
||||||
|
|
||||||
|
// 캐시 무효화
|
||||||
|
cacheService.evictCacheMinutesDetail(minutesId);
|
||||||
|
|
||||||
|
log.info("회의록 안건별 수정 성공 - minutesId: {}, updatedCount: {}",
|
||||||
|
minutesId, updatedSections.size());
|
||||||
|
|
||||||
|
return ResponseEntity.ok(ApiResponse.success("회의록이 성공적으로 수정되었습니다"));
|
||||||
|
|
||||||
|
} catch (BusinessException e) {
|
||||||
|
log.error("회의록 안건별 수정 실패 - 비즈니스 예외 - minutesId: {}", minutesId, e);
|
||||||
|
return ResponseEntity.badRequest()
|
||||||
|
.body(ApiResponse.errorWithType(e.getMessage()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("회의록 안건별 수정 실패 - minutesId: {}", minutesId, e);
|
||||||
|
return ResponseEntity.badRequest()
|
||||||
|
.body(ApiResponse.errorWithType("회의록 수정에 실패했습니다"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Helper methods
|
// Helper methods
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
package com.unicorn.hgzero.meeting.infra.dto.request;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회의록 안건별 수정 요청 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UpdateAgendaSectionsRequest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 수정할 안건 목록
|
||||||
|
*/
|
||||||
|
@NotNull(message = "안건 목록은 필수입니다")
|
||||||
|
private List<AgendaUpdateItem> agendas;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class AgendaUpdateItem {
|
||||||
|
/**
|
||||||
|
* 안건 ID (agenda_sections.id)
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "안건 ID는 필수입니다")
|
||||||
|
private String agendaId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 안건 내용 (agenda_sections.summary)
|
||||||
|
*/
|
||||||
|
@NotBlank(message = "안건 내용은 필수입니다")
|
||||||
|
private String content;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -7,6 +7,8 @@ import lombok.AllArgsConstructor;
|
|||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
|
import org.hibernate.annotations.JdbcTypeCode;
|
||||||
|
import org.hibernate.type.SqlTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 안건별 회의록 섹션 Entity
|
* 안건별 회의록 섹션 Entity
|
||||||
@ -39,9 +41,11 @@ public class AgendaSectionEntity extends BaseTimeEntity {
|
|||||||
@Column(name = "ai_summary_short", columnDefinition = "TEXT")
|
@Column(name = "ai_summary_short", columnDefinition = "TEXT")
|
||||||
private String aiSummaryShort;
|
private String aiSummaryShort;
|
||||||
|
|
||||||
|
@JdbcTypeCode(SqlTypes.JSON)
|
||||||
@Column(name = "pending_items", columnDefinition = "json")
|
@Column(name = "pending_items", columnDefinition = "json")
|
||||||
private String pendingItems;
|
private String pendingItems;
|
||||||
|
|
||||||
|
@JdbcTypeCode(SqlTypes.JSON)
|
||||||
@Column(name = "todos", columnDefinition = "json")
|
@Column(name = "todos", columnDefinition = "json")
|
||||||
private String todos;
|
private String todos;
|
||||||
|
|
||||||
@ -83,5 +87,12 @@ public class AgendaSectionEntity extends BaseTimeEntity {
|
|||||||
.summary(section.getSummary())
|
.summary(section.getSummary())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 요약 내용 업데이트
|
||||||
|
*/
|
||||||
|
public void updateSummary(String summary) {
|
||||||
|
this.summary = summary;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,9 @@ package com.unicorn.hgzero.meeting.infra.gateway.repository;
|
|||||||
|
|
||||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.AgendaSectionEntity;
|
import com.unicorn.hgzero.meeting.infra.gateway.entity.AgendaSectionEntity;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.springframework.data.jpa.repository.Modifying;
|
||||||
|
import org.springframework.data.jpa.repository.Query;
|
||||||
|
import org.springframework.data.repository.query.Param;
|
||||||
import org.springframework.stereotype.Repository;
|
import org.springframework.stereotype.Repository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -25,4 +28,14 @@ public interface AgendaSectionRepository extends JpaRepository<AgendaSectionEnti
|
|||||||
* @return 안건 섹션 목록
|
* @return 안건 섹션 목록
|
||||||
*/
|
*/
|
||||||
List<AgendaSectionEntity> findByMeetingIdOrderByAgendaNumber(String meetingId);
|
List<AgendaSectionEntity> findByMeetingIdOrderByAgendaNumber(String meetingId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 안건 섹션 요약 업데이트 (Native Query 사용)
|
||||||
|
* @param agendaId 안건 ID
|
||||||
|
* @param summary 요약 내용
|
||||||
|
*/
|
||||||
|
@Modifying
|
||||||
|
@Query(value = "UPDATE agenda_sections SET summary = :summary, updated_at = CURRENT_TIMESTAMP WHERE id = :agendaId",
|
||||||
|
nativeQuery = true)
|
||||||
|
void updateSummaryById(@Param("agendaId") String agendaId, @Param("summary") String summary);
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user