From fb005a8139b6f1a07e06d6e05d02462fef02b494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=92=B1=E6=B6=9B?= <15850646081@163.com> Date: Wed, 26 Feb 2025 17:25:51 +0800 Subject: [PATCH] =?UTF-8?q?=E7=A5=A8=E6=8D=AE=E6=B1=A0=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resource/WEB-INF/CBS2ECConfig.xml | 18 +- .../cbs8/client/AccountManagementClient.java | 4 +- .../cbs8/client/BillManagementClient.java | 4 +- .../engine/salary/timer/CBS8GetDtaJob.java | 130 ------------- ...Job.java => SyncCBSAccountDetailsJob.java} | 26 ++- .../salary/timer/SyncCBSPayDetailsJob.java | 183 ++++++++++++++++++ 6 files changed, 213 insertions(+), 152 deletions(-) delete mode 100644 src/com/engine/salary/timer/CBS8GetDtaJob.java rename src/com/engine/salary/timer/{CBS8GetTransactionDetailJob.java => SyncCBSAccountDetailsJob.java} (89%) create mode 100644 src/com/engine/salary/timer/SyncCBSPayDetailsJob.java diff --git a/resource/WEB-INF/CBS2ECConfig.xml b/resource/WEB-INF/CBS2ECConfig.xml index 7dc238ed3..563605ccb 100644 --- a/resource/WEB-INF/CBS2ECConfig.xml +++ b/resource/WEB-INF/CBS2ECConfig.xml @@ -3,7 +3,6 @@ - @@ -12,15 +11,14 @@
- - - - - - - - - + + + + + + + +
diff --git a/src/com/engine/salary/remote/cbs8/client/AccountManagementClient.java b/src/com/engine/salary/remote/cbs8/client/AccountManagementClient.java index 579c68574..40041bc9c 100644 --- a/src/com/engine/salary/remote/cbs8/client/AccountManagementClient.java +++ b/src/com/engine/salary/remote/cbs8/client/AccountManagementClient.java @@ -28,14 +28,14 @@ public class AccountManagementClient extends CBS8BaseClient { .build(); String requestData = JsonUtil.toJsonString(requestParam); - log.info(url + "\n" + requestData); + log.info("鑾峰彇cbs浜ゆ槗鍙傛暟锛" + url + "\n" + requestData); HttpPost httpPost = setupRequest(url, requestData); try (CloseableHttpResponse response = client.execute(httpPost)) { byte[] finalResponseData = handleResponse(response); String req = new String(finalResponseData, StandardCharsets.UTF_8); GetTransactionDetailResponse getTransactionDetailResponse = JsonUtil.parseBean(req, GetTransactionDetailResponse.class); - log.info(url + "\n" + JsonUtil.toJsonString(getTransactionDetailResponse)); + log.info("鑾峰彇cbs浜ゆ槗缁撴灉锛" + "\n" + JsonUtil.toJsonString(getTransactionDetailResponse)); if (Objects.isNull(getTransactionDetailResponse)) { throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(156449, "鏈嶅姟寮傚父")); diff --git a/src/com/engine/salary/remote/cbs8/client/BillManagementClient.java b/src/com/engine/salary/remote/cbs8/client/BillManagementClient.java index f0b30a97c..069c3c4e4 100644 --- a/src/com/engine/salary/remote/cbs8/client/BillManagementClient.java +++ b/src/com/engine/salary/remote/cbs8/client/BillManagementClient.java @@ -27,14 +27,14 @@ public class BillManagementClient extends CBS8BaseClient { .build(); String requestData = JsonUtil.toJsonString(requestParam); - log.info(url + "\n" + requestData); + log.info("鑾峰彇cbs绁ㄦ嵁鍙傛暟锛" + url + "\n" + requestData); HttpPost httpPost = setupRequest(url, requestData); try (CloseableHttpResponse response = client.execute(httpPost)) { byte[] finalResponseData = handleResponse(response); String req = new String(finalResponseData, StandardCharsets.UTF_8); GetDtaResponse getDtaResponse = JsonUtil.parseBean(req, GetDtaResponse.class); - log.info(url + "\n" + JsonUtil.toJsonString(getDtaResponse)); + log.info("鑾峰彇cbs绁ㄦ嵁缁撴灉锛" + "\n" + JsonUtil.toJsonString(getDtaResponse)); if (Objects.isNull(getDtaResponse)) { throw new SalaryRunTimeException(SalaryI18nUtil.getI18nLabel(156449, "鏈嶅姟寮傚父")); diff --git a/src/com/engine/salary/timer/CBS8GetDtaJob.java b/src/com/engine/salary/timer/CBS8GetDtaJob.java deleted file mode 100644 index 044d02c79..000000000 --- a/src/com/engine/salary/timer/CBS8GetDtaJob.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.engine.salary.timer; - -import cn.hutool.core.util.StrUtil; -import com.engine.salary.exception.SalaryRunTimeException; -import com.engine.salary.mapper.hrm.HrmSnapshotMapper; -import com.engine.salary.remote.cbs8.client.BillManagementClient; -import com.engine.salary.remote.cbs8.request.GetDtaRequest; -import com.engine.salary.remote.cbs8.response.GetDtaResponse; -import com.engine.salary.util.db.MapperProxyFactory; -import lombok.extern.slf4j.Slf4j; -import weaver.hrm.User; -import weaver.interfaces.schedule.BaseCronJob; - -import java.time.LocalDate; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -/** - * 鑾峰彇CBS绁ㄦ嵁淇℃伅 - *

Copyright: Copyright (c) 2024

- *

Company: 娉涘井杞欢

- * - * @author qiantao - * @version 1.0 - **/ -@Slf4j -public class CBS8GetDtaJob extends BaseCronJob { - - /** - * 鍑虹エ鏃ユ湡璧 鏍煎紡涓簓yyy-mm-dd锛屽嚭绁ㄦ棩鏈熻捣姝笉鍏佽涓杈规湁鍊间竴杈规棤鍊硷紝鏃ユ湡闂撮殧鏈澶т负涓骞 - */ - private String issueDateStart; - /** - * 鍑虹エ鏃ユ湡姝 鏍煎紡涓簓yyy-mm-dd锛屽嚭绁ㄦ棩鏈熻捣姝笉鍏佽涓杈规湁鍊间竴杈规棤鍊硷紝鏃ユ湡闂撮殧鏈澶т负涓骞 - */ - private String issueDateEnd; - - /** - * 鎸佺エ浜哄崟浣嶇紪鐮 澶氶夈傚~鍦╟bs绯荤粺鍏叡璁剧疆>鍩虹淇℃伅>缁勭粐鏈烘瀯缁存姢鐨勫崟浣嶇紪鐮 - */ - private String displayHoldOrganizationCodeList; - /** - * 鎸佺エ浜鸿处鍙 澶氶夈 - */ - private String holdAccountList; - - - private HrmSnapshotMapper getHrmSnapshotMapper() { - return MapperProxyFactory.getProxy(HrmSnapshotMapper.class); - } - - @Override - public void execute() { - User user = new User(); - user.setUid(1); - user.setLoginid("sysadmin"); - - try { - GetDtaRequest requestParam = new GetDtaRequest(); - requestParam.setCurrentPage(1); - requestParam.setPageSize(1000); - - if (StrUtil.isNotBlank(issueDateStart) && StrUtil.isNotBlank(issueDateEnd)) { - requestParam.setIssueDateStart(issueDateStart); - requestParam.setIssueDateEnd(issueDateEnd); - } else { - String nowDate = LocalDate.now().toString(); - requestParam.setIssueDateStart(nowDate); - requestParam.setIssueDateEnd(nowDate); - } - requestParam.setDisplayHoldOrganizationCodeList(Arrays.stream(displayHoldOrganizationCodeList.split(",")).collect(Collectors.toList())); - requestParam.setHoldAccountList(Arrays.stream(holdAccountList.split(",")).collect(Collectors.toList())); - - BillManagementClient bailManagementClient = new BillManagementClient(); - GetDtaResponse getDtaResponse = bailManagementClient.dtaQuery(requestParam); - - List list = getDtaResponse.getData().getList(); - - - Integer modeId = 1; - List fields = new ArrayList() {{ - add("formmodeid"); - add("modedatacreater"); - add("modedatacreatertype"); - add("modedatacreatedate"); - add("modedatacreatetime"); - }}; -// String currDate = TimeUtil.getCurrentDateString(); -// SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); -// String currTime = sdf.format(new Date()); -// List values = new ArrayList() {{ -// add(modeId); -// add(1); -// add(0); -// add(String.format("'%s'", currDate)); -// add(String.format("'%s'", currTime)); -// }}; -// for (PushSettingItemPO item : pushSettingItemPOS) { -// //鏁版嵁搴撳瓧娈 -// String fieldName = item.getFieldName(); -// fields.add(fieldName); -// // 鍏紡鍙橀噺鐨勫 -// String field = item.getItem(); -// String value = formulaVarValueMap.getOrDefault(field, StringUtils.EMPTY); -// PushItemFieldEnum pushItemFieldEnum = PushItemFieldEnum.parseByValue(item.getFieldType()); -// values.add(pushItemFieldEnum.convertValue(value)); -// } -// String tableName = setting.getTableName(); -// String sql = String.format("insert into %s (%s) values (%s)", tableName, String.join(",", fields), values.stream().map(Object::toString).collect(Collectors.joining(","))); -// RecordSet rs = new RecordSet(); -// rs.execute(sql); -// -// if (modeId != null) { -// rs.executeQuery("select max(id) from " + tableName); -// int mainId = 0; -// if (rs.next()) { -// mainId = rs.getInt(1); -// } -// ModeRightInfo ModeRightInfo = new ModeRightInfo(); -// ModeRightInfo.setNewRight(true); -// ModeRightInfo.editModeDataShare(1, modeId, mainId); -// } - } catch (Exception e) { - log.error("鑾峰彇CBS绁ㄦ嵁淇℃伅澶辫触", e); - throw new SalaryRunTimeException("鑾峰彇CBS绁ㄦ嵁淇℃伅澶辫触," + e.getMessage(), e); - } - } -} diff --git a/src/com/engine/salary/timer/CBS8GetTransactionDetailJob.java b/src/com/engine/salary/timer/SyncCBSAccountDetailsJob.java similarity index 89% rename from src/com/engine/salary/timer/CBS8GetTransactionDetailJob.java rename to src/com/engine/salary/timer/SyncCBSAccountDetailsJob.java index 07dc0c7bf..75d7a5877 100644 --- a/src/com/engine/salary/timer/CBS8GetTransactionDetailJob.java +++ b/src/com/engine/salary/timer/SyncCBSAccountDetailsJob.java @@ -3,7 +3,6 @@ package com.engine.salary.timer; import cn.hutool.core.util.StrUtil; import com.engine.salary.exception.SalaryRunTimeException; import com.engine.salary.mapper.SQLMapper; -import com.engine.salary.mapper.hrm.HrmSnapshotMapper; import com.engine.salary.remote.cbs8.client.AccountManagementClient; import com.engine.salary.remote.cbs8.config.EBS2ECConfig; import com.engine.salary.remote.cbs8.request.GetTransactionDetailRequest; @@ -27,7 +26,7 @@ import java.util.*; import java.util.stream.Collectors; /** - * 鑾峰彇CBS浜ゆ槗淇℃伅 + * 鑾峰彇澧冨唴璐︽埛鏄庣粏 *

Copyright: Copyright (c) 2024

*

Company: 娉涘井杞欢

* @@ -35,7 +34,7 @@ import java.util.stream.Collectors; * @version 1.0 **/ @Slf4j -public class CBS8GetTransactionDetailJob extends BaseCronJob { +public class SyncCBSAccountDetailsJob extends BaseCronJob { /** * 寮濮嬫棩鏈 鏍煎紡涓簓yyy-mm-dd @@ -54,10 +53,6 @@ public class CBS8GetTransactionDetailJob extends BaseCronJob { */ private String paymentNatureList; - private HrmSnapshotMapper getHrmSnapshotMapper() { - return MapperProxyFactory.getProxy(HrmSnapshotMapper.class); - } - private SQLMapper getSQLMapper() { return MapperProxyFactory.getProxy(SQLMapper.class); } @@ -106,7 +101,7 @@ public class CBS8GetTransactionDetailJob extends BaseCronJob { //鑾峰彇宸插瓨鍦ㄧ殑鏁版嵁 EBS2ECConfig.Table.Field uniqueField = table.getFields().stream().filter(EBS2ECConfig.Table.Field::isUnique).findFirst().orElse(null); - if(uniqueField == null){ + if (uniqueField == null) { throw new SalaryRunTimeException("鏈缃敮涓鏍囪瘑瀛楁"); } String uniqueKey = uniqueField.getKey(); @@ -126,12 +121,16 @@ public class CBS8GetTransactionDetailJob extends BaseCronJob { } List fields = new ArrayList() {{ + //寤烘ā榛樿瀛楁 add("formmodeid"); add("modedatacreater"); add("modedatacreatertype"); add("modedatacreatedate"); add("modedatacreatetime"); + //涓氬姟閫昏緫瀛楁锛屾敹娆剧被鍨嬶紝榛樿鏄摱琛屽瓨娆 + add("sklx"); }}; + String currDate = TimeUtil.getCurrentDateString(); SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); String currTime = sdf.format(new Date()); @@ -141,6 +140,8 @@ public class CBS8GetTransactionDetailJob extends BaseCronJob { add(0); add(String.format("'%s'", currDate)); add(String.format("'%s'", currTime)); + //涓氬姟閫昏緫瀛楁锛屾敹娆剧被鍨嬶紝榛樿鏄摱琛屽瓨娆 + add(0); }}; for (EBS2ECConfig.Table.Field field : table.getFields()) { //鏁版嵁搴撳瓧娈 @@ -150,6 +151,15 @@ public class CBS8GetTransactionDetailJob extends BaseCronJob { values.add(String.format("'%s'", detailMap.getOrDefault(field.getEbsKey(), ""))); } + /* + * 璁ら閫昏緫锛 + * 鏍规嵁鍥炴浜哄幓琛ㄩ噷鍖归厤銆愬洖娆句汉涓庡姙浜嬪鐨勫鐓ц〃銆 + * 濡傛灉鑳藉尮閰嶅埌鏁版嵁锛屽垯鐘舵佹敼涓哄凡鑷姩璁ら锛屽悓鏃舵妸鍔炰簨澶勶紙閮ㄩ棬锛夌殑鍊煎悓姝ュ埌鍔炰簨澶勫瓧娈典腑锛屾槸鍚︾郴缁熻嚜鍔ㄨ棰嗗悓姝ヤ负鏄紱 + * 濡傚鐓ф爣鍑嗘棤鏁版嵁锛屽垯鐘舵佷负锛氭湭璁ら锛 + */ + + + String sql = String.format("insert into %s (%s) values (%s)", tableName, String.join(",", fields), values.stream().map(Object::toString).collect(Collectors.joining(","))); RecordSet rs = new RecordSet(); rs.execute(sql); diff --git a/src/com/engine/salary/timer/SyncCBSPayDetailsJob.java b/src/com/engine/salary/timer/SyncCBSPayDetailsJob.java new file mode 100644 index 000000000..40aaf4fa0 --- /dev/null +++ b/src/com/engine/salary/timer/SyncCBSPayDetailsJob.java @@ -0,0 +1,183 @@ +package com.engine.salary.timer; + +import cn.hutool.core.util.StrUtil; +import com.engine.salary.exception.SalaryRunTimeException; +import com.engine.salary.mapper.SQLMapper; +import com.engine.salary.remote.cbs8.client.BillManagementClient; +import com.engine.salary.remote.cbs8.config.EBS2ECConfig; +import com.engine.salary.remote.cbs8.request.GetDtaRequest; +import com.engine.salary.remote.cbs8.response.GetDtaResponse; +import com.engine.salary.util.JsonUtil; +import com.engine.salary.util.db.MapperProxyFactory; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.security.AnyTypePermission; +import lombok.extern.slf4j.Slf4j; +import weaver.conn.RecordSet; +import weaver.formmode.setup.ModeRightInfo; +import weaver.general.GCONST; +import weaver.general.TimeUtil; +import weaver.hrm.User; +import weaver.interfaces.schedule.BaseCronJob; + +import java.io.File; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 鑾峰彇绁ㄦ嵁鏀舵鏄庣粏 + *

Copyright: Copyright (c) 2024

+ *

Company: 娉涘井杞欢

+ * + * @author qiantao + * @version 1.0 + **/ +@Slf4j +public class SyncCBSPayDetailsJob extends BaseCronJob { + + + /** + * 鎸佺エ浜哄崟浣嶇紪鐮 澶氶夈傚~鍦╟bs绯荤粺鍏叡璁剧疆>鍩虹淇℃伅>缁勭粐鏈烘瀯缁存姢鐨勫崟浣嶇紪鐮 + */ + private String displayHoldOrganizationCodeList; + /** + * 鎸佺エ浜鸿处鍙 澶氶夈 + */ + private String holdAccountList; + + /** + * 鍑虹エ鏃ユ湡璧 鏍煎紡涓簓yyy-mm-dd锛屽嚭绁ㄦ棩鏈熻捣姝笉鍏佽涓杈规湁鍊间竴杈规棤鍊硷紝鏃ユ湡闂撮殧鏈澶т负涓骞 + */ + private String issueDateStart; + /** + * 鍑虹エ鏃ユ湡姝 鏍煎紡涓簓yyy-mm-dd锛屽嚭绁ㄦ棩鏈熻捣姝笉鍏佽涓杈规湁鍊间竴杈规棤鍊硷紝鏃ユ湡闂撮殧鏈澶т负涓骞 + */ + private String issueDateEnd; + + /** + * 绯荤粺绁ㄦ嵁绫诲瀷 澶氶夈傛寜闄勫綍4.1.3绁ㄦ嵁绫诲瀷-绁ㄦ嵁鐘舵-娴侀氭爣蹇楃骇鑱斿叧绯昏〃閫夋嫨鍚庢寜椤哄簭濉啓銆傜郴缁熺エ鎹被鍨嬫灇涓捐闄勫綍4.1.3銆傜エ鎹姸鎬佹灇涓捐闄勫綍4.1.4銆傛祦閫氭爣蹇楁灇涓捐闄勫綍4.1.5銆 + */ + private String billVarietyList; + + private SQLMapper getSQLMapper() { + return MapperProxyFactory.getProxy(SQLMapper.class); + } + + @Override + public void execute() { + User user = new User(); + user.setUid(1); + user.setLoginid("sysadmin"); + + try { + GetDtaRequest requestParam = new GetDtaRequest(); + requestParam.setCurrentPage(1); + requestParam.setPageSize(1000); + + if (StrUtil.isNotBlank(issueDateStart) && StrUtil.isNotBlank(issueDateEnd)) { + requestParam.setIssueDateStart(issueDateStart); + requestParam.setIssueDateEnd(issueDateEnd); + } else { + String nowDate = LocalDate.now().toString(); + requestParam.setIssueDateStart(nowDate); + requestParam.setIssueDateEnd(nowDate); + } + if(StrUtil.isBlank(displayHoldOrganizationCodeList)){ + throw new SalaryRunTimeException("鎸佺エ浜哄崟浣嶇紪鐮佷负绌猴紝璇峰湪璁″垝浠诲姟閰嶇疆姝ら」锛"); + } + requestParam.setDisplayHoldOrganizationCodeList(Arrays.stream(displayHoldOrganizationCodeList.split(",")).collect(Collectors.toList())); + if(StrUtil.isBlank(holdAccountList)){ + throw new SalaryRunTimeException("鎸佺エ浜鸿处鍙蜂负绌猴紝璇峰湪璁″垝浠诲姟閰嶇疆姝ら」锛"); + } + requestParam.setHoldAccountList(Arrays.stream(holdAccountList.split(",")).collect(Collectors.toList())); + requestParam.setBillVarietyList(Arrays.stream(billVarietyList.split(",")).collect(Collectors.toList())); + + BillManagementClient bailManagementClient = new BillManagementClient(); + GetDtaResponse getDtaResponse = bailManagementClient.dtaQuery(requestParam); + + List list = getDtaResponse.getData().getList(); + + //鍔犺浇cbs閰嶇疆 + XStream xStream = new XStream(); + String resource = GCONST.getRootPath() + "WEB-INF" + File.separatorChar + "CBS2ECConfig.xml"; + File file = new File(resource); + xStream.addPermission(AnyTypePermission.ANY); + xStream.processAnnotations(EBS2ECConfig.class); + EBS2ECConfig dto = (EBS2ECConfig) xStream.fromXML(file); + EBS2ECConfig.Table table = dto.getTables().get(1); + Integer modeId = table.getModeId(); + String tableName = table.getKey(); + + //鑾峰彇宸插瓨鍦ㄧ殑鏁版嵁 + EBS2ECConfig.Table.Field uniqueField = table.getFields().stream().filter(EBS2ECConfig.Table.Field::isUnique).findFirst().orElse(null); + if (uniqueField == null) { + throw new SalaryRunTimeException("鏈缃敮涓鏍囪瘑瀛楁"); + } + String uniqueKey = uniqueField.getKey(); + String uniqueEbsKey = uniqueField.getEbsKey(); + List uniqueDataKeys = getSQLMapper().listString(String.format("select %s from %s", uniqueKey, tableName)); + + for (GetDtaResponse.Detail detail : list) { + Map detailMap = JsonUtil.parseMap(detail, String.class); + String uniqueData = detailMap.get(uniqueEbsKey); + if (StrUtil.isBlank(uniqueData)) { + log.warn("璺宠繃cbs绁ㄦ嵁鏁版嵁锛屽敮涓鏍囪瘑杩斿洖绌,uniqueKey:{},uniqueEbsKey:{}", uniqueKey, uniqueEbsKey); + continue; + } + if (uniqueDataKeys.contains(uniqueData)) { + log.warn("璺宠繃cbs绁ㄦ嵁鏁版嵁锛屾暟鎹凡瀛樺湪,uniqueKey:{},uniqueEbsKey:{},鍊納}", uniqueKey, uniqueEbsKey, uniqueData); + continue; + } + + List fields = new ArrayList() {{ + add("formmodeid"); + add("modedatacreater"); + add("modedatacreatertype"); + add("modedatacreatedate"); + add("modedatacreatetime"); + //涓氬姟閫昏緫瀛楁锛屾敹娆剧被鍨 + add("sklx"); + }}; + String currDate = TimeUtil.getCurrentDateString(); + SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss"); + String currTime = sdf.format(new Date()); + List values = new ArrayList() {{ + add(modeId); + add(1); + add(0); + add(String.format("'%s'", currDate)); + add(String.format("'%s'", currTime)); + //涓氬姟閫昏緫瀛楁锛屾敹娆剧被鍨嬶紝閾舵壙锛屽晢鎵 + add("AC01".equals(detailMap.get("billType")) ? 0 : 1); + }}; + + for (EBS2ECConfig.Table.Field field : table.getFields()) { + //鏁版嵁搴撳瓧娈 + String fieldName = field.getKey(); + fields.add(fieldName); + // 鎺ュ彛鍊 + values.add(String.format("'%s'", detailMap.getOrDefault(field.getEbsKey(), ""))); + } + + String sql = String.format("insert into %s (%s) values (%s)", tableName, String.join(",", fields), values.stream().map(Object::toString).collect(Collectors.joining(","))); + RecordSet rs = new RecordSet(); + rs.execute(sql); + + if (modeId != null) { + rs.executeQuery("select max(id) from " + tableName); + int mainId = 0; + if (rs.next()) { + mainId = rs.getInt(1); + } + ModeRightInfo ModeRightInfo = new ModeRightInfo(); + ModeRightInfo.setNewRight(true); + ModeRightInfo.editModeDataShare(1, modeId, mainId); + } + } + } catch (Exception e) { + log.error("鑾峰彇CBS绁ㄦ嵁淇℃伅澶辫触", e); + throw new SalaryRunTimeException("鑾峰彇CBS绁ㄦ嵁淇℃伅澶辫触," + e.getMessage(), e); + } + } +}