Skip to content

Commit a96fbb8

Browse files
authored
Merge pull request #308 from TaskFlow-CLAP/CLAP-269
CLAP-269 csv 데이터 유효성 검증 추가 및 회원 저장 로직 개선
2 parents 1059dff + 6adf9de commit a96fbb8

File tree

13 files changed

+106
-52
lines changed

13 files changed

+106
-52
lines changed

src/main/java/clap/server/adapter/inbound/security/handler/JwtAccessDeniedHandler.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ public void handle(
2323
) throws IOException, ServletException {
2424
AuthErrorCode errorCode = AuthErrorCode.FORBIDDEN;
2525

26-
response.setContentType("application/json;charset=UTF-8");
2726
response.setStatus(errorCode.getHttpStatus().value());
2827
response.getWriter().write(errorCode.getCustomCode());
2928
}

src/main/java/clap/server/adapter/inbound/web/admin/RegisterMemberCsvController.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
import clap.server.adapter.inbound.security.service.SecurityUserDetails;
44
import clap.server.application.port.inbound.admin.RegisterMemberCSVUsecase;
55
import clap.server.common.annotation.architecture.WebAdapter;
6+
import clap.server.common.utils.FileTypeValidator;
7+
import clap.server.exception.AdapterException;
8+
import clap.server.exception.ApplicationException;
9+
import clap.server.exception.code.FileErrorcode;
610
import io.swagger.v3.oas.annotations.Operation;
711
import io.swagger.v3.oas.annotations.tags.Tag;
812
import org.springframework.http.ResponseEntity;
@@ -13,6 +17,8 @@
1317
import org.springframework.web.bind.annotation.RequestParam;
1418
import org.springframework.web.multipart.MultipartFile;
1519

20+
import java.io.IOException;
21+
1622
@Tag(name = "05. Admin")
1723
@WebAdapter
1824
@RequestMapping("/api/managements")
@@ -28,7 +34,10 @@ public RegisterMemberCsvController(RegisterMemberCSVUsecase registerMemberCSVUse
2834
@Secured("ROLE_ADMIN")
2935
public ResponseEntity<String> registerMembersFromCsv(
3036
@AuthenticationPrincipal SecurityUserDetails userInfo,
31-
@RequestParam("file") MultipartFile file) {
37+
@RequestParam("file") MultipartFile file) throws IOException {
38+
if (!FileTypeValidator.validCSVFile(file.getInputStream())) {
39+
throw new AdapterException(FileErrorcode.UNSUPPORTED_FILE_TYPE);
40+
}
3241
int addedCount = registerMemberCSVUsecase.registerMembersFromCsv(userInfo.getUserId(), file);
3342
return ResponseEntity.ok(addedCount + "명의 회원이 등록되었습니다.");
3443
}

src/main/java/clap/server/adapter/outbound/persistense/DepartmentPersistentAdapter.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package clap.server.adapter.outbound.persistense;
22

33
import clap.server.adapter.outbound.persistense.entity.member.DepartmentEntity;
4+
import clap.server.adapter.outbound.persistense.entity.member.constant.DepartmentStatus;
45
import clap.server.adapter.outbound.persistense.mapper.DepartmentPersistenceMapper;
56
import clap.server.adapter.outbound.persistense.repository.member.DepartmentRepository;
67
import clap.server.application.port.outbound.member.CommandDepartmentPort;
@@ -9,6 +10,7 @@
910
import clap.server.domain.model.member.Department;
1011
import lombok.RequiredArgsConstructor;
1112

13+
import java.util.List;
1214
import java.util.Optional;
1315

1416
@PersistenceAdapter
@@ -22,4 +24,11 @@ public Optional<Department> findById(final Long id) {
2224
Optional<DepartmentEntity> departmentEntity = departmentRepository.findById(id);
2325
return departmentEntity.map(departmentPersistenceMapper::toDomain);
2426
}
27+
28+
@Override
29+
public List<Department> findActiveDepartments() {
30+
return departmentRepository.findAllByStatusIs(DepartmentStatus.ACTIVE).stream()
31+
.map(departmentPersistenceMapper::toDomain).toList();
32+
}
33+
2534
}

src/main/java/clap/server/adapter/outbound/persistense/MemberPersistenceAdapter.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ public void save(final Member member) {
7373
memberRepository.save(memberEntity);
7474
}
7575

76+
@Override
77+
public void saveAll(List<Member> members) {
78+
List<MemberEntity> memberEntities = members.stream().map(memberPersistenceMapper::toEntity).toList();
79+
memberRepository.saveAll(memberEntities);
80+
}
81+
7682
@Override
7783
public List<Member> findActiveManagers() {
7884
List<MemberEntity> memberEntities = memberRepository.findByRoleAndStatus(MemberRole.valueOf("ROLE_MANAGER"), MemberStatus.ACTIVE);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package clap.server.adapter.outbound.persistense.repository.member;
22

33
import clap.server.adapter.outbound.persistense.entity.member.DepartmentEntity;
4+
import clap.server.adapter.outbound.persistense.entity.member.constant.DepartmentStatus;
45
import org.springframework.data.jpa.repository.JpaRepository;
56
import org.springframework.stereotype.Repository;
67

8+
import java.util.List;
9+
710
@Repository
811
public interface DepartmentRepository extends JpaRepository<DepartmentEntity, Long> {
12+
List<DepartmentEntity> findAllByStatusIs(DepartmentStatus status);
913
}

src/main/java/clap/server/application/port/outbound/member/CommandMemberPort.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
import clap.server.domain.model.member.Member;
44

5+
import java.util.List;
6+
57
public interface CommandMemberPort {
68
void save(Member member);
9+
void saveAll(List<Member> members);
710
}

src/main/java/clap/server/application/port/outbound/member/LoadDepartmentPort.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import clap.server.domain.model.member.Department;
44

5+
import java.util.List;
56
import java.util.Optional;
67

78
public interface LoadDepartmentPort {
89
Optional<Department> findById(Long id);
10+
List<Department> findActiveDepartments();
911
}

src/main/java/clap/server/application/service/admin/CsvParseService.java

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
package clap.server.application.service.admin;
22

3+
import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole;
34
import clap.server.application.port.outbound.member.LoadDepartmentPort;
4-
import clap.server.common.annotation.architecture.ApplicationService;
55
import clap.server.domain.model.member.Department;
66
import clap.server.domain.model.member.Member;
77
import clap.server.domain.model.member.MemberInfo;
8-
import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole;
98
import clap.server.exception.ApplicationException;
109
import clap.server.exception.code.DepartmentErrorCode;
1110
import clap.server.exception.code.MemberErrorCode;
1211
import lombok.RequiredArgsConstructor;
1312
import lombok.extern.slf4j.Slf4j;
13+
import org.springframework.stereotype.Service;
1414
import org.springframework.web.multipart.MultipartFile;
1515

1616
import java.io.BufferedReader;
@@ -24,38 +24,41 @@
2424

2525

2626
@Slf4j
27-
@ApplicationService
27+
@Service
2828
@RequiredArgsConstructor
2929
public class CsvParseService {
3030

3131
private final LoadDepartmentPort loadDepartmentPort;
3232

33-
public List<Member> parse(MultipartFile file) {
33+
public List<Member> parseDataAndMapToMember(MultipartFile file) {
3434
List<Member> members = new ArrayList<>();
35+
List<Department> departments = loadDepartmentPort.findActiveDepartments();
36+
3537
try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream()))) {
3638
// 첫 번째 줄은 헤더로 간주하고 다음 줄부터 파싱
3739
String headerLine = reader.readLine();
3840
if (headerLine == null) {
39-
throw ApplicationException.from(MemberErrorCode.INVALID_CSV_FORMAT);
41+
throw new ApplicationException(MemberErrorCode.INVALID_CSV_FORMAT);
4042
}
4143
String line;
4244
while ((line = reader.readLine()) != null) {
4345
String[] fields = line.split(",");
4446
if (fields.length != 7) {
45-
throw ApplicationException.from(MemberErrorCode.INVALID_CSV_FORMAT);
47+
throw new ApplicationException(MemberErrorCode.INVALID_CSV_FORMAT);
4648
}
47-
members.add(mapToMember(fields));
49+
members.add(mapToMember(fields, departments));
4850
}
4951
} catch (IOException e) {
50-
throw ApplicationException.from(MemberErrorCode.CSV_PARSING_ERROR);
52+
throw new ApplicationException(MemberErrorCode.CSV_PARSING_ERROR);
5153
}
5254
return members;
5355
}
5456

55-
private Member mapToMember(String[] fields) {
56-
// 부서 ID로 Department 객체 조회
57+
private Member mapToMember(String[] fields, List<Department> departments) {
5758
Long departmentId = Long.parseLong(fields[2].trim());
58-
Department department = loadDepartmentPort.findById(departmentId)
59+
Department department = departments.stream()
60+
.filter(dept -> dept.getDepartmentId().equals(departmentId))
61+
.findFirst()
5962
.orElseThrow(() -> new ApplicationException(DepartmentErrorCode.DEPARTMENT_NOT_FOUND));
6063

6164
MemberInfo memberInfo = toMemberInfo(

src/main/java/clap/server/application/service/admin/RegisterMemberCSVService.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,11 @@ public class RegisterMemberCSVService implements RegisterMemberCSVUsecase {
2121
@Override
2222
@Transactional
2323
public int registerMembersFromCsv(Long adminId, MultipartFile file) {
24-
List<Member> members = csvParser.parse(file);
24+
List<Member> members = csvParser.parseDataAndMapToMember(file);
2525
Member admin = memberService.findActiveMember(adminId);
26-
members.forEach(member -> {
27-
member.register(admin);
28-
commandMemberPort.save(member);
29-
});
26+
members.forEach(member -> {member.register(admin);});
27+
28+
commandMemberPort.saveAll(members);
3029
return members.size();
3130
}
3231
}

src/main/java/clap/server/application/service/member/UpdateMemberInfoService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import clap.server.application.port.outbound.s3.S3UploadPort;
77
import clap.server.common.annotation.architecture.ApplicationService;
88
import clap.server.domain.policy.attachment.FilePathPolicy;
9-
import clap.server.common.utils.FileUtils;
9+
import clap.server.common.utils.FileTypeValidator;
1010
import clap.server.domain.model.member.Member;
1111
import clap.server.exception.ApplicationException;
1212
import clap.server.exception.code.FileErrorcode;
@@ -25,7 +25,7 @@ class UpdateMemberInfoService implements UpdateMemberInfoUsecase {
2525

2626
@Override
2727
public void updateMemberInfo(Long memberId, UpdateMemberInfoRequest request, MultipartFile profileImage) throws IOException {
28-
if (!FileUtils.validImageFile(profileImage.getInputStream())) {
28+
if (!FileTypeValidator.validImageFile(profileImage.getInputStream())) {
2929
throw new ApplicationException(FileErrorcode.UNSUPPORTED_FILE_TYPE);
3030
}
3131
Member member = memberService.findActiveMember(memberId);

0 commit comments

Comments
 (0)