feat: 기관에 문정원 담당자·담당 변호사 연락처 컬럼 추가
신청기관 담당자 1종만 저장하던 기관 테이블에 문정원 담당자(부서/담당자/연락처/이메일)와 담당 변호사(성명/연락처/이메일/배정일) 정보를 추가해, 채널관리 화면에서 세 담당자를 한 번에 저장하고 다른 화면(기관관리·업무메모)에서도 쓸 수 있게 한다. - V3 마이그레이션으로 organization에 컬럼 8개 추가 (upsertBySeed는 그대로 둬서 엑셀 재시드가 새 컬럼을 건드리지 않는다) - ContactRequest/OrgResponse/updateContact에 8개 필드를 선택값으로 반영 - 컨트롤러 테스트 2건(신규 필드 왕복, 신규 필드 없는 기존 페이로드) + 매퍼 테스트 2건(영속화 왕복, 재시드 시 보존) 추가
@435af9eb1359a76b32bf915798f0cc00c0444c83
--- src/main/java/kr/itn/itnhub/org/ContactRequest.java
+++ src/main/java/kr/itn/itnhub/org/ContactRequest.java
... | ... | @@ -2,11 +2,20 @@ |
| 2 | 2 |
|
| 3 | 3 |
import jakarta.validation.constraints.Email; |
| 4 | 4 |
import jakarta.validation.constraints.NotBlank; |
| 5 |
+import jakarta.validation.constraints.Size; |
|
| 5 | 6 |
|
| 6 | 7 |
public record ContactRequest( |
| 7 | 8 |
@NotBlank String deptName, |
| 8 | 9 |
@NotBlank String managerName, |
| 9 | 10 |
String managerTitle, |
| 10 | 11 |
@NotBlank String managerPhone, |
| 11 |
- @NotBlank @Email String managerEmail) {
|
|
| 12 |
+ @NotBlank @Email String managerEmail, |
|
| 13 |
+ @Size(max = 200) String mjDeptName, |
|
| 14 |
+ @Size(max = 100) String mjManagerName, |
|
| 15 |
+ @Size(max = 50) String mjManagerPhone, |
|
| 16 |
+ @Size(max = 200) String mjManagerEmail, |
|
| 17 |
+ @Size(max = 100) String lawyerName, |
|
| 18 |
+ @Size(max = 50) String lawyerPhone, |
|
| 19 |
+ @Size(max = 200) String lawyerEmail, |
|
| 20 |
+ @Size(max = 20) String lawyerAssignedDate) {
|
|
| 12 | 21 |
} |
--- src/main/java/kr/itn/itnhub/org/OrgResponse.java
+++ src/main/java/kr/itn/itnhub/org/OrgResponse.java
... | ... | @@ -10,6 +10,14 @@ |
| 10 | 10 |
String managerTitle, |
| 11 | 11 |
String managerPhone, |
| 12 | 12 |
String managerEmail, |
| 13 |
+ String mjDeptName, |
|
| 14 |
+ String mjManagerName, |
|
| 15 |
+ String mjManagerPhone, |
|
| 16 |
+ String mjManagerEmail, |
|
| 17 |
+ String lawyerName, |
|
| 18 |
+ String lawyerPhone, |
|
| 19 |
+ String lawyerEmail, |
|
| 20 |
+ String lawyerAssignedDate, |
|
| 13 | 21 |
String channelIdMj, |
| 14 | 22 |
String channelIdLaw) {
|
| 15 | 23 |
|
... | ... | @@ -24,6 +32,14 @@ |
| 24 | 32 |
org.getManagerTitle(), |
| 25 | 33 |
org.getManagerPhone(), |
| 26 | 34 |
org.getManagerEmail(), |
| 35 |
+ org.getMjDeptName(), |
|
| 36 |
+ org.getMjManagerName(), |
|
| 37 |
+ org.getMjManagerPhone(), |
|
| 38 |
+ org.getMjManagerEmail(), |
|
| 39 |
+ org.getLawyerName(), |
|
| 40 |
+ org.getLawyerPhone(), |
|
| 41 |
+ org.getLawyerEmail(), |
|
| 42 |
+ org.getLawyerAssignedDate(), |
|
| 27 | 43 |
org.getChannelIdMj(), |
| 28 | 44 |
org.getChannelIdLaw()); |
| 29 | 45 |
} |
--- src/main/java/kr/itn/itnhub/org/Organization.java
+++ src/main/java/kr/itn/itnhub/org/Organization.java
... | ... | @@ -11,6 +11,14 @@ |
| 11 | 11 |
private String managerTitle; |
| 12 | 12 |
private String managerPhone; |
| 13 | 13 |
private String managerEmail; |
| 14 |
+ private String mjDeptName; |
|
| 15 |
+ private String mjManagerName; |
|
| 16 |
+ private String mjManagerPhone; |
|
| 17 |
+ private String mjManagerEmail; |
|
| 18 |
+ private String lawyerName; |
|
| 19 |
+ private String lawyerPhone; |
|
| 20 |
+ private String lawyerEmail; |
|
| 21 |
+ private String lawyerAssignedDate; |
|
| 14 | 22 |
private String channelIdMj; |
| 15 | 23 |
private String channelIdLaw; |
| 16 | 24 |
|
... | ... | @@ -70,6 +78,30 @@ |
| 70 | 78 |
public String getManagerEmail() { return managerEmail; }
|
| 71 | 79 |
public void setManagerEmail(String managerEmail) { this.managerEmail = managerEmail; }
|
| 72 | 80 |
|
| 81 |
+ public String getMjDeptName() { return mjDeptName; }
|
|
| 82 |
+ public void setMjDeptName(String mjDeptName) { this.mjDeptName = mjDeptName; }
|
|
| 83 |
+ |
|
| 84 |
+ public String getMjManagerName() { return mjManagerName; }
|
|
| 85 |
+ public void setMjManagerName(String mjManagerName) { this.mjManagerName = mjManagerName; }
|
|
| 86 |
+ |
|
| 87 |
+ public String getMjManagerPhone() { return mjManagerPhone; }
|
|
| 88 |
+ public void setMjManagerPhone(String mjManagerPhone) { this.mjManagerPhone = mjManagerPhone; }
|
|
| 89 |
+ |
|
| 90 |
+ public String getMjManagerEmail() { return mjManagerEmail; }
|
|
| 91 |
+ public void setMjManagerEmail(String mjManagerEmail) { this.mjManagerEmail = mjManagerEmail; }
|
|
| 92 |
+ |
|
| 93 |
+ public String getLawyerName() { return lawyerName; }
|
|
| 94 |
+ public void setLawyerName(String lawyerName) { this.lawyerName = lawyerName; }
|
|
| 95 |
+ |
|
| 96 |
+ public String getLawyerPhone() { return lawyerPhone; }
|
|
| 97 |
+ public void setLawyerPhone(String lawyerPhone) { this.lawyerPhone = lawyerPhone; }
|
|
| 98 |
+ |
|
| 99 |
+ public String getLawyerEmail() { return lawyerEmail; }
|
|
| 100 |
+ public void setLawyerEmail(String lawyerEmail) { this.lawyerEmail = lawyerEmail; }
|
|
| 101 |
+ |
|
| 102 |
+ public String getLawyerAssignedDate() { return lawyerAssignedDate; }
|
|
| 103 |
+ public void setLawyerAssignedDate(String lawyerAssignedDate) { this.lawyerAssignedDate = lawyerAssignedDate; }
|
|
| 104 |
+ |
|
| 73 | 105 |
public String getChannelIdMj() { return channelIdMj; }
|
| 74 | 106 |
public void setChannelIdMj(String channelIdMj) { this.channelIdMj = channelIdMj; }
|
| 75 | 107 |
|
--- src/main/java/kr/itn/itnhub/org/OrganizationService.java
+++ src/main/java/kr/itn/itnhub/org/OrganizationService.java
... | ... | @@ -29,6 +29,14 @@ |
| 29 | 29 |
org.setManagerTitle(request.managerTitle()); |
| 30 | 30 |
org.setManagerPhone(request.managerPhone()); |
| 31 | 31 |
org.setManagerEmail(request.managerEmail()); |
| 32 |
+ org.setMjDeptName(request.mjDeptName()); |
|
| 33 |
+ org.setMjManagerName(request.mjManagerName()); |
|
| 34 |
+ org.setMjManagerPhone(request.mjManagerPhone()); |
|
| 35 |
+ org.setMjManagerEmail(request.mjManagerEmail()); |
|
| 36 |
+ org.setLawyerName(request.lawyerName()); |
|
| 37 |
+ org.setLawyerPhone(request.lawyerPhone()); |
|
| 38 |
+ org.setLawyerEmail(request.lawyerEmail()); |
|
| 39 |
+ org.setLawyerAssignedDate(request.lawyerAssignedDate()); |
|
| 32 | 40 |
mapper.updateContact(org); |
| 33 | 41 |
|
| 34 | 42 |
return OrgResponse.of(mapper.findById(id)); |
+++ src/main/resources/db/migration/V3__org_contacts.sql
... | ... | @@ -0,0 +1,9 @@ |
| 1 | +alter table organization | |
| 2 | + add column mj_dept_name varchar(200), | |
| 3 | + add column mj_manager_name varchar(100), | |
| 4 | + add column mj_manager_phone varchar(50), | |
| 5 | + add column mj_manager_email varchar(200), | |
| 6 | + add column lawyer_name varchar(100), | |
| 7 | + add column lawyer_phone varchar(50), | |
| 8 | + add column lawyer_email varchar(200), | |
| 9 | + add column lawyer_assigned_date varchar(20); |
--- src/main/resources/mapper/OrganizationMapper.xml
+++ src/main/resources/mapper/OrganizationMapper.xml
... | ... | @@ -6,6 +6,8 @@ |
| 6 | 6 |
<sql id="columns"> |
| 7 | 7 |
id, org_no, org_name, channel_slug, |
| 8 | 8 |
dept_name, manager_name, manager_title, manager_phone, manager_email, |
| 9 |
+ mj_dept_name, mj_manager_name, mj_manager_phone, mj_manager_email, |
|
| 10 |
+ lawyer_name, lawyer_phone, lawyer_email, lawyer_assigned_date, |
|
| 9 | 11 |
channel_id_mj, channel_id_law |
| 10 | 12 |
</sql> |
| 11 | 13 |
|
... | ... | @@ -45,12 +47,20 @@ |
| 45 | 47 |
|
| 46 | 48 |
<update id="updateContact" parameterType="kr.itn.itnhub.org.Organization"> |
| 47 | 49 |
update organization set |
| 48 |
- dept_name = #{deptName},
|
|
| 49 |
- manager_name = #{managerName},
|
|
| 50 |
- manager_title = #{managerTitle},
|
|
| 51 |
- manager_phone = #{managerPhone},
|
|
| 52 |
- manager_email = #{managerEmail},
|
|
| 53 |
- updated_at = now() |
|
| 50 |
+ dept_name = #{deptName},
|
|
| 51 |
+ manager_name = #{managerName},
|
|
| 52 |
+ manager_title = #{managerTitle},
|
|
| 53 |
+ manager_phone = #{managerPhone},
|
|
| 54 |
+ manager_email = #{managerEmail},
|
|
| 55 |
+ mj_dept_name = #{mjDeptName},
|
|
| 56 |
+ mj_manager_name = #{mjManagerName},
|
|
| 57 |
+ mj_manager_phone = #{mjManagerPhone},
|
|
| 58 |
+ mj_manager_email = #{mjManagerEmail},
|
|
| 59 |
+ lawyer_name = #{lawyerName},
|
|
| 60 |
+ lawyer_phone = #{lawyerPhone},
|
|
| 61 |
+ lawyer_email = #{lawyerEmail},
|
|
| 62 |
+ lawyer_assigned_date = #{lawyerAssignedDate},
|
|
| 63 |
+ updated_at = now() |
|
| 54 | 64 |
where id = #{id}
|
| 55 | 65 |
</update> |
| 56 | 66 |
|
--- src/test/java/kr/itn/itnhub/org/OrganizationControllerTest.java
+++ src/test/java/kr/itn/itnhub/org/OrganizationControllerTest.java
... | ... | @@ -17,6 +17,7 @@ |
| 17 | 17 |
|
| 18 | 18 |
import static org.assertj.core.api.Assertions.assertThat; |
| 19 | 19 |
import static org.hamcrest.Matchers.containsString; |
| 20 |
+import static org.hamcrest.Matchers.nullValue; |
|
| 20 | 21 |
import static org.mockito.ArgumentMatchers.anyString; |
| 21 | 22 |
import static org.mockito.ArgumentMatchers.eq; |
| 22 | 23 |
import static org.mockito.Mockito.when; |
... | ... | @@ -88,6 +89,64 @@ |
| 88 | 89 |
} |
| 89 | 90 |
|
| 90 | 91 |
@Test |
| 92 |
+ void 문정원_담당자와_변호사_정보를_포함해_저장하면_응답과_재조회에_모두_반영된다() throws Exception {
|
|
| 93 |
+ mvc.perform(put("/api/orgs/{id}/contact", orgId)
|
|
| 94 |
+ .with(csrf()) |
|
| 95 |
+ .contentType(MediaType.APPLICATION_JSON) |
|
| 96 |
+ .content("""
|
|
| 97 |
+ {
|
|
| 98 |
+ "deptName": "데이터정보화팀", |
|
| 99 |
+ "managerName": "송민지", |
|
| 100 |
+ "managerTitle": "과장", |
|
| 101 |
+ "managerPhone": "02-3475-5434", |
|
| 102 |
+ "managerEmail": "ming@arirang.com", |
|
| 103 |
+ "mjDeptName": "문화체육관광부 저작권정책과", |
|
| 104 |
+ "mjManagerName": "김문정", |
|
| 105 |
+ "mjManagerPhone": "02-1234-5678", |
|
| 106 |
+ "mjManagerEmail": "mj@mcst.go.kr", |
|
| 107 |
+ "lawyerName": "이변호", |
|
| 108 |
+ "lawyerPhone": "02-9876-5432", |
|
| 109 |
+ "lawyerEmail": "lawyer@lawfirm.kr", |
|
| 110 |
+ "lawyerAssignedDate": "2026-07-21" |
|
| 111 |
+ } |
|
| 112 |
+ """)) |
|
| 113 |
+ .andExpect(status().isOk()) |
|
| 114 |
+ .andExpect(jsonPath("$.mjDeptName").value("문화체육관광부 저작권정책과"))
|
|
| 115 |
+ .andExpect(jsonPath("$.mjManagerName").value("김문정"))
|
|
| 116 |
+ .andExpect(jsonPath("$.mjManagerPhone").value("02-1234-5678"))
|
|
| 117 |
+ .andExpect(jsonPath("$.mjManagerEmail").value("mj@mcst.go.kr"))
|
|
| 118 |
+ .andExpect(jsonPath("$.lawyerName").value("이변호"))
|
|
| 119 |
+ .andExpect(jsonPath("$.lawyerPhone").value("02-9876-5432"))
|
|
| 120 |
+ .andExpect(jsonPath("$.lawyerEmail").value("lawyer@lawfirm.kr"))
|
|
| 121 |
+ .andExpect(jsonPath("$.lawyerAssignedDate").value("2026-07-21"));
|
|
| 122 |
+ |
|
| 123 |
+ mvc.perform(get("/api/orgs"))
|
|
| 124 |
+ .andExpect(status().isOk()) |
|
| 125 |
+ .andExpect(jsonPath("$[0].mjManagerName").value("김문정"))
|
|
| 126 |
+ .andExpect(jsonPath("$[0].lawyerName").value("이변호"));
|
|
| 127 |
+ } |
|
| 128 |
+ |
|
| 129 |
+ @Test |
|
| 130 |
+ void 문정원_변호사_정보없이_기존방식_페이로드만으로도_저장에_성공한다() throws Exception {
|
|
| 131 |
+ mvc.perform(put("/api/orgs/{id}/contact", orgId)
|
|
| 132 |
+ .with(csrf()) |
|
| 133 |
+ .contentType(MediaType.APPLICATION_JSON) |
|
| 134 |
+ .content("""
|
|
| 135 |
+ {
|
|
| 136 |
+ "deptName": "데이터정보화팀", |
|
| 137 |
+ "managerName": "송민지", |
|
| 138 |
+ "managerTitle": "과장", |
|
| 139 |
+ "managerPhone": "02-3475-5434", |
|
| 140 |
+ "managerEmail": "ming@arirang.com" |
|
| 141 |
+ } |
|
| 142 |
+ """)) |
|
| 143 |
+ .andExpect(status().isOk()) |
|
| 144 |
+ .andExpect(jsonPath("$.status").value("READY"))
|
|
| 145 |
+ .andExpect(jsonPath("$.mjManagerName").value(nullValue()))
|
|
| 146 |
+ .andExpect(jsonPath("$.lawyerName").value(nullValue()));
|
|
| 147 |
+ } |
|
| 148 |
+ |
|
| 149 |
+ @Test |
|
| 91 | 150 |
void 존재하지_않는_기관ID로_담당자정보를_수정하면_404다() throws Exception {
|
| 92 | 151 |
long missingId = orgId + 999999L; |
| 93 | 152 |
|
--- src/test/java/kr/itn/itnhub/org/OrganizationMapperTest.java
+++ src/test/java/kr/itn/itnhub/org/OrganizationMapperTest.java
... | ... | @@ -114,6 +114,47 @@ |
| 114 | 114 |
} |
| 115 | 115 |
|
| 116 | 116 |
@Test |
| 117 |
+ void 문정원_담당자와_변호사_정보가_저장되고_그대로_읽힌다() {
|
|
| 118 |
+ mapper.upsertBySeed(seedRow("003", "한국문학번역원", "정보관리팀"));
|
|
| 119 |
+ Organization org = mapper.findAll().get(0); |
|
| 120 |
+ |
|
| 121 |
+ org.setMjDeptName("문화체육관광부 저작권정책과");
|
|
| 122 |
+ org.setMjManagerName("김문정");
|
|
| 123 |
+ org.setMjManagerPhone("02-1234-5678");
|
|
| 124 |
+ org.setMjManagerEmail("mj@mcst.go.kr");
|
|
| 125 |
+ org.setLawyerName("이변호");
|
|
| 126 |
+ org.setLawyerPhone("02-9876-5432");
|
|
| 127 |
+ org.setLawyerEmail("lawyer@lawfirm.kr");
|
|
| 128 |
+ org.setLawyerAssignedDate("2026-07-21");
|
|
| 129 |
+ mapper.updateContact(org); |
|
| 130 |
+ |
|
| 131 |
+ Organization after = mapper.findById(org.getId()); |
|
| 132 |
+ assertThat(after.getMjDeptName()).isEqualTo("문화체육관광부 저작권정책과");
|
|
| 133 |
+ assertThat(after.getMjManagerName()).isEqualTo("김문정");
|
|
| 134 |
+ assertThat(after.getMjManagerPhone()).isEqualTo("02-1234-5678");
|
|
| 135 |
+ assertThat(after.getMjManagerEmail()).isEqualTo("mj@mcst.go.kr");
|
|
| 136 |
+ assertThat(after.getLawyerName()).isEqualTo("이변호");
|
|
| 137 |
+ assertThat(after.getLawyerPhone()).isEqualTo("02-9876-5432");
|
|
| 138 |
+ assertThat(after.getLawyerEmail()).isEqualTo("lawyer@lawfirm.kr");
|
|
| 139 |
+ assertThat(after.getLawyerAssignedDate()).isEqualTo("2026-07-21");
|
|
| 140 |
+ } |
|
| 141 |
+ |
|
| 142 |
+ @Test |
|
| 143 |
+ void 시드_재실행은_문정원_변호사_정보를_지우지_않는다() {
|
|
| 144 |
+ mapper.upsertBySeed(seedRow("001", "국제방송교류재단", "데이터정보화팀"));
|
|
| 145 |
+ Organization org = mapper.findAll().get(0); |
|
| 146 |
+ org.setMjManagerName("김문정");
|
|
| 147 |
+ org.setLawyerName("이변호");
|
|
| 148 |
+ mapper.updateContact(org); |
|
| 149 |
+ |
|
| 150 |
+ mapper.upsertBySeed(seedRow("001", "국제방송교류재단", "데이터정보화팀"));
|
|
| 151 |
+ |
|
| 152 |
+ Organization after = mapper.findById(org.getId()); |
|
| 153 |
+ assertThat(after.getMjManagerName()).isEqualTo("김문정");
|
|
| 154 |
+ assertThat(after.getLawyerName()).isEqualTo("이변호");
|
|
| 155 |
+ } |
|
| 156 |
+ |
|
| 157 |
+ @Test |
|
| 117 | 158 |
void 목록은_연번_오름차순이다() {
|
| 118 | 159 |
mapper.upsertBySeed(seedRow("010", "국립아시아문화전당", "기획운영과"));
|
| 119 | 160 |
mapper.upsertBySeed(seedRow("002", "세종학당재단", "콘텐츠개발팀"));
|
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?