Update docker-compose and Nginx configuration for sample data path; enhance MasterDataMapper and MasterDataService with methods for managing legal entities and FX rates, including normalization and validation logic.
This commit is contained in:
parent
3400eb8067
commit
bbaa6f3e0b
|
|
@ -92,7 +92,7 @@ services:
|
|||
volumes:
|
||||
- ./ops/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
- ./client/nexacro-deploy:/usr/share/nginx/html:ro
|
||||
- ./sample-data:/usr/share/nginx/html/sample-data:ro
|
||||
- ./sample-data:/opt/hanwha/sample-data:ro
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1/healthz || exit 1"]
|
||||
interval: 10s
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ server {
|
|||
}
|
||||
|
||||
location /sample-data/ {
|
||||
alias /usr/share/nginx/html/sample-data/;
|
||||
alias /opt/hanwha/sample-data/;
|
||||
autoindex on;
|
||||
}
|
||||
|
||||
|
|
@ -34,4 +34,3 @@ server {
|
|||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package com.hanwha.nexacrodemo.master;
|
||||
|
||||
import com.hanwha.nexacrodemo.auth.AuthService;
|
||||
import jakarta.servlet.http.HttpSession;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.Map;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/master")
|
||||
public class MasterDataController {
|
||||
|
||||
private final AuthService authService;
|
||||
private final MasterDataService masterDataService;
|
||||
|
||||
public MasterDataController(AuthService authService, MasterDataService masterDataService) {
|
||||
this.authService = authService;
|
||||
this.masterDataService = masterDataService;
|
||||
}
|
||||
|
||||
@PutMapping("/entities")
|
||||
public Map<String, Object> saveEntities(@Valid @RequestBody MasterEntitiesUpdateRequest request, HttpSession session) {
|
||||
authService.requireRole(session, "ADMIN");
|
||||
int savedCount = masterDataService.replaceEntities(request);
|
||||
return Map.of("ok", true, "savedCount", savedCount, "message", "법인정보가 저장되었습니다.");
|
||||
}
|
||||
|
||||
@PutMapping("/fx-rates")
|
||||
public Map<String, Object> saveFxRates(@Valid @RequestBody MasterFxRatesUpdateRequest request, HttpSession session) {
|
||||
authService.requireRole(session, "ADMIN");
|
||||
int savedCount = masterDataService.replaceFxRates(request);
|
||||
return Map.of("ok", true, "savedCount", savedCount, "message", "환율정보가 저장되었습니다.");
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package com.hanwha.nexacrodemo.master;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.ibatis.annotations.Delete;
|
||||
import org.apache.ibatis.annotations.Insert;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
|
|
@ -49,5 +51,40 @@ public interface MasterDataMapper {
|
|||
order by username
|
||||
""")
|
||||
List<Map<String, Object>> findUsers();
|
||||
}
|
||||
|
||||
@Delete("""
|
||||
delete from legal_entity
|
||||
""")
|
||||
void deleteAllEntities();
|
||||
|
||||
@Insert("""
|
||||
insert into legal_entity (
|
||||
entity_code,
|
||||
entity_name,
|
||||
base_currency
|
||||
) values (
|
||||
#{entityCode},
|
||||
#{entityName},
|
||||
#{baseCurrency}
|
||||
)
|
||||
""")
|
||||
void insertEntity(MasterEntityRowRequest row);
|
||||
|
||||
@Delete("""
|
||||
delete from fx_rate
|
||||
""")
|
||||
void deleteAllFxRates();
|
||||
|
||||
@Insert("""
|
||||
insert into fx_rate (
|
||||
fiscal_period,
|
||||
currency_code,
|
||||
rate_to_krw
|
||||
) values (
|
||||
#{fiscalPeriod},
|
||||
#{currencyCode},
|
||||
#{rateToKrw}
|
||||
)
|
||||
""")
|
||||
void insertFxRate(MasterFxRateRowRequest row);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,16 @@ package com.hanwha.nexacrodemo.master;
|
|||
|
||||
import com.hanwha.nexacrodemo.common.TxResponse;
|
||||
import com.hanwha.nexacrodemo.common.MapKeyUtils;
|
||||
import com.hanwha.nexacrodemo.common.ApiException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
public class MasterDataService {
|
||||
|
|
@ -48,6 +53,79 @@ public class MasterDataService {
|
|||
);
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public int replaceEntities(MasterEntitiesUpdateRequest request) {
|
||||
List<MasterEntityRowRequest> rows = request.getRows().stream()
|
||||
.map(this::normalizeEntity)
|
||||
.toList();
|
||||
|
||||
validateDuplicateEntityCodes(rows);
|
||||
|
||||
masterDataMapper.deleteAllEntities();
|
||||
rows.forEach(masterDataMapper::insertEntity);
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public int replaceFxRates(MasterFxRatesUpdateRequest request) {
|
||||
List<MasterFxRateRowRequest> rows = request.getRows().stream()
|
||||
.map(this::normalizeFxRate)
|
||||
.toList();
|
||||
|
||||
validateDuplicateFxRateKeys(rows);
|
||||
|
||||
masterDataMapper.deleteAllFxRates();
|
||||
rows.forEach(masterDataMapper::insertFxRate);
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
private MasterEntityRowRequest normalizeEntity(MasterEntityRowRequest source) {
|
||||
MasterEntityRowRequest row = new MasterEntityRowRequest();
|
||||
row.setEntityCode(trimToNull(source.getEntityCode()));
|
||||
row.setEntityName(trimToNull(source.getEntityName()));
|
||||
row.setBaseCurrency(upper(trimToNull(source.getBaseCurrency())));
|
||||
return row;
|
||||
}
|
||||
|
||||
private MasterFxRateRowRequest normalizeFxRate(MasterFxRateRowRequest source) {
|
||||
MasterFxRateRowRequest row = new MasterFxRateRowRequest();
|
||||
row.setFiscalPeriod(trimToNull(source.getFiscalPeriod()));
|
||||
row.setCurrencyCode(upper(trimToNull(source.getCurrencyCode())));
|
||||
row.setRateToKrw(source.getRateToKrw());
|
||||
return row;
|
||||
}
|
||||
|
||||
private void validateDuplicateEntityCodes(List<MasterEntityRowRequest> rows) {
|
||||
Set<String> codes = new LinkedHashSet<>();
|
||||
for (MasterEntityRowRequest row : rows) {
|
||||
if (!codes.add(row.getEntityCode())) {
|
||||
throw new ApiException(HttpStatus.BAD_REQUEST, "중복된 법인코드는 저장할 수 없습니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void validateDuplicateFxRateKeys(List<MasterFxRateRowRequest> rows) {
|
||||
Set<String> keys = new LinkedHashSet<>();
|
||||
for (MasterFxRateRowRequest row : rows) {
|
||||
String key = row.getFiscalPeriod() + "|" + row.getCurrencyCode();
|
||||
if (!keys.add(key)) {
|
||||
throw new ApiException(HttpStatus.BAD_REQUEST, "중복된 회계기간/통화 조합은 저장할 수 없습니다.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String trimToNull(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
private String upper(String value) {
|
||||
return value == null ? null : value.toUpperCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
public record ReferenceSnapshot(
|
||||
List<Map<String, Object>> entities,
|
||||
List<Map<String, Object>> accounts,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
package com.hanwha.nexacrodemo.master;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MasterEntitiesUpdateRequest {
|
||||
|
||||
@NotNull(message = "rows는 필수입니다.")
|
||||
private List<@Valid MasterEntityRowRequest> rows = new ArrayList<>();
|
||||
|
||||
public List<MasterEntityRowRequest> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void setRows(List<MasterEntityRowRequest> rows) {
|
||||
this.rows = rows;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package com.hanwha.nexacrodemo.master;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
|
||||
public class MasterEntityRowRequest {
|
||||
|
||||
@NotBlank(message = "법인코드는 필수입니다.")
|
||||
@Size(max = 20, message = "법인코드는 20자 이하여야 합니다.")
|
||||
private String entityCode;
|
||||
|
||||
@NotBlank(message = "법인명은 필수입니다.")
|
||||
@Size(max = 100, message = "법인명은 100자 이하여야 합니다.")
|
||||
private String entityName;
|
||||
|
||||
@NotBlank(message = "통화는 필수입니다.")
|
||||
@Pattern(regexp = "^[A-Z]{3}$", message = "통화는 영문 대문자 3자리여야 합니다.")
|
||||
private String baseCurrency;
|
||||
|
||||
public String getEntityCode() {
|
||||
return entityCode;
|
||||
}
|
||||
|
||||
public void setEntityCode(String entityCode) {
|
||||
this.entityCode = entityCode;
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
public void setEntityName(String entityName) {
|
||||
this.entityName = entityName;
|
||||
}
|
||||
|
||||
public String getBaseCurrency() {
|
||||
return baseCurrency;
|
||||
}
|
||||
|
||||
public void setBaseCurrency(String baseCurrency) {
|
||||
this.baseCurrency = baseCurrency;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.hanwha.nexacrodemo.master;
|
||||
|
||||
import jakarta.validation.constraints.DecimalMin;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
public class MasterFxRateRowRequest {
|
||||
|
||||
@NotBlank(message = "회계기간은 필수입니다.")
|
||||
@Pattern(regexp = "^\\d{4}-\\d{2}$", message = "회계기간은 YYYY-MM 형식이어야 합니다.")
|
||||
private String fiscalPeriod;
|
||||
|
||||
@NotBlank(message = "통화는 필수입니다.")
|
||||
@Size(max = 10, message = "통화 코드는 10자 이하여야 합니다.")
|
||||
private String currencyCode;
|
||||
|
||||
@NotNull(message = "환산율은 필수입니다.")
|
||||
@DecimalMin(value = "0.0", inclusive = false, message = "환산율은 0보다 커야 합니다.")
|
||||
private BigDecimal rateToKrw;
|
||||
|
||||
public String getFiscalPeriod() {
|
||||
return fiscalPeriod;
|
||||
}
|
||||
|
||||
public void setFiscalPeriod(String fiscalPeriod) {
|
||||
this.fiscalPeriod = fiscalPeriod;
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return currencyCode;
|
||||
}
|
||||
|
||||
public void setCurrencyCode(String currencyCode) {
|
||||
this.currencyCode = currencyCode;
|
||||
}
|
||||
|
||||
public BigDecimal getRateToKrw() {
|
||||
return rateToKrw;
|
||||
}
|
||||
|
||||
public void setRateToKrw(BigDecimal rateToKrw) {
|
||||
this.rateToKrw = rateToKrw;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.hanwha.nexacrodemo.master;
|
||||
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MasterFxRatesUpdateRequest {
|
||||
|
||||
@NotNull(message = "rows는 필수입니다.")
|
||||
private List<@Valid MasterFxRateRowRequest> rows = new ArrayList<>();
|
||||
|
||||
public List<MasterFxRateRowRequest> getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
public void setRows(List<MasterFxRateRowRequest> rows) {
|
||||
this.rows = rows;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue