diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/common/config/GovDingProperties.java b/pmapi/src/main/java/com/ningdatech/pmapi/common/config/GovDingProperties.java index 34ccd97..a107a7a 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/common/config/GovDingProperties.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/common/config/GovDingProperties.java @@ -44,15 +44,15 @@ public class GovDingProperties { GovDingProperties.appAuthsecret = appAuthsecret; } - @Value("${ding.app-sso-auth-key}") - public void setAppSsoAuthKey(String appSsoAuthkey) { - GovDingProperties.appSsoAuthkey = appSsoAuthkey; - } - - @Value("${ding.app-sso-auth-secret}") - public void setAppSsoAuthsecret(String appSsoAuthsecret) { - GovDingProperties.appSsoAuthsecret = appSsoAuthsecret; - } +// @Value("${ding.app-sso-auth-key}") +// public void setAppSsoAuthKey(String appSsoAuthkey) { +// GovDingProperties.appSsoAuthkey = appSsoAuthkey; +// } +// +// @Value("${ding.app-sso-auth-secret}") +// public void setAppSsoAuthsecret(String appSsoAuthsecret) { +// GovDingProperties.appSsoAuthsecret = appSsoAuthsecret; +// } @Value("${ding.tenantId}") public void setTenantId(Long tenantId) { diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/ding/model/DingOrgInfoTreeDTO.java b/pmapi/src/main/java/com/ningdatech/pmapi/ding/model/DingOrgInfoTreeDTO.java new file mode 100644 index 0000000..7040ce1 --- /dev/null +++ b/pmapi/src/main/java/com/ningdatech/pmapi/ding/model/DingOrgInfoTreeDTO.java @@ -0,0 +1,29 @@ +package com.ningdatech.pmapi.ding.model; + +import com.ningdatech.zwdd.model.dto.DingOrgInfoDTO; +import lombok.Data; + +import java.util.List; + +/** + * @author liuxinxin + * @date 2022/8/24 上午11:06 + * 钉钉组织结构树状结构 + */ +@Data +public class DingOrgInfoTreeDTO { + + /** + * 钉钉code码 + */ + private String code; + + /** + * 组织信息 + */ + private DingOrgInfoDTO dingOrgInfoDTO; + /** + * 子节点code + */ + private List childCodes; +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/ding/task/OrganizationBatchGetTask.java b/pmapi/src/main/java/com/ningdatech/pmapi/ding/task/OrganizationBatchGetTask.java index defd658..3888c2c 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/ding/task/OrganizationBatchGetTask.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/ding/task/OrganizationBatchGetTask.java @@ -1,154 +1,167 @@ -//package com.ningdatech.pmapi.ding.task; -// -//import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; -//import com.google.common.collect.Lists; -//import com.ningdatech.basic.model.GenericResult; -//import com.ningdatech.zwdd.client.ZwddAuthClient; -//import com.ningdatech.zwdd.client.ZwddClient; -//import com.ningdatech.zwdd.model.dto.DingOrgInfoDTO; -//import com.ningdatech.zwdd.model.dto.DingScopesV2DTO; -//import com.ningdatech.zwdd.model.dto.PageSubOrganizationCodeDTO; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.stereotype.Component; -// -//import java.util.ArrayList; -//import java.util.List; -//import java.util.Objects; -//import java.util.stream.Collectors; -// -///** -// * @author liuxinxin -// * @date 2023/2/7 上午10:15 -// */ -// -//@Component -//public class OrganizationBatchGetTask { -// -// @Autowired -// private ZwddClient zwddClient; -// -// @Autowired -// private ZwddAuthClient zwddAuthClient; -// -// public OrganizationBatchGetTask() { -// // 获取通讯录权限范围 获取顶级组织code -// GenericResult scopesV2Result = zwddClient.getScopesV2(); -// DingScopesV2DTO data = scopesV2Result.getData(); -// -// if (Objects.nonNull(data)) { -// -// } -// -//// // 根据组织code查询详情获取部门信息 -//// List deptVisibleScopes = data.getDeptVisibleScopes(); -//// for (String deptVisibleScope : deptVisibleScopes) { -//// GenericResult organizationByCodeResult = zwddClient.getOrganizationByCode(deptVisibleScope); -//// DingOrgInfoDTO dingOrgInfoDTO = organizationByCodeResult.getData(); -////// dingOrgInfoDTO -//// } -// List currentAllOrganizationCodeList = new ArrayList<>(); -// -// -// List deptVisibleScopes = data.getDeptVisibleScopes(); -// for (String orgCode : deptVisibleScopes) { -// if (currentAllOrganizationCodeList.contains(orgCode)) { -//// log.info("已存在组织架构---{}", orgCode); -// continue; -// } -// List treeDTOList = new ArrayList<>(); -// -// DingOrgInfoTreeDTO childDingOrgInfoTreeDTO = new DingOrgInfoTreeDTO(); -// //设置节点详情 -// if (dingOrgInfoDtos != null && !dingOrgInfoDtos.isEmpty()) { -// for (DingOrgInfoDTO orgInfo : dingOrgInfoDtos) { -// if (orgInfo.getOrganizationCode().equals(orgCode)) { -// childDingOrgInfoTreeDTO.setDingOrgInfoDTO(orgInfo); -// } +package com.ningdatech.pmapi.ding.task; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.google.common.collect.Lists; +import com.ningdatech.basic.model.GenericResult; +import com.ningdatech.pmapi.ding.model.DingOrgInfoTreeDTO; +import com.ningdatech.pmapi.organization.entity.DingOrganization; +import com.ningdatech.pmapi.organization.service.IDingOrganizationService; +import com.ningdatech.zwdd.client.ZwddAuthClient; +import com.ningdatech.zwdd.client.ZwddClient; +import com.ningdatech.zwdd.model.dto.DingOrgInfoDTO; +import com.ningdatech.zwdd.model.dto.DingScopesV2DTO; +import com.ningdatech.zwdd.model.dto.PageSubOrganizationCodeDTO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author liuxinxin + * @date 2023/2/7 上午10:15 + */ + +@Slf4j +@Component +public class OrganizationBatchGetTask { + + @Autowired + private ZwddClient zwddClient; + + @Autowired + private ZwddAuthClient zwddAuthClient; + + private static final Integer GROUP_SIZE = 100; + + @Autowired + private IDingOrganizationService iDingOrganizationService; + + /** + * 获取浙政钉组织架构 + */ + @Transactional(rollbackFor = Exception.class) + public void OrganizationBatchGetTask() { +// List allList = iDingOrganizationService.list(); +// List currentAllOrganizationCodeList = allList.stream().map(DingOrganization::getOrganizationCode).collect(Collectors.toList()); + // 全量删除 + // iDingOrganizationService.remove(Wrappers.lambdaQuery(DingOrganization.class).isNotNull(DingOrganization::getId)); + // 获取顶级组织code + GenericResult scopesV2Result = zwddClient.getScopesV2(); + DingScopesV2DTO scopesV2 = scopesV2Result.getData(); + + if (Objects.nonNull(scopesV2)) { + // 顶级组织code + List deptVisibleScopes = scopesV2.getDeptVisibleScopes(); + log.info("顶级组织code: size = " + deptVisibleScopes.size() + "列表:" + JSONObject.toJSONString(deptVisibleScopes)); + // 获取顶级节点信息 + GenericResult> listGenericResult = zwddClient.listOrganizationsByCodes(deptVisibleScopes); + List dingOrgInfoDtos = listGenericResult.getData(); + for (String orgCode : deptVisibleScopes) { +// if (currentAllOrganizationCodeList.contains(orgCode)) { +// log.info("已存在组织架构---{}", orgCode); +// continue; // } -// } -// childDingOrgInfoTreeDTO.setCode(orgCode); -// childDingOrgInfoTreeDTO.setChildCodes(new ArrayList<>()); -// getDingOrgChild(childDingOrgInfoTreeDTO); -// treeDTOList.add(childDingOrgInfoTreeDTO); -// -// if (CollectionUtils.isNotEmpty(treeDTOList)) { -// List saveRecordList = new ArrayList<>(); -// buildSaveRecordList(treeDTOList, saveRecordList); -// -// // 批量保存 -// if (saveRecordList.size() <= GROUP_SIZE) { -// iDingOrganizationService.saveBatch(saveRecordList); -// } else { -// List> split = Lists.partition(saveRecordList, GROUP_SIZE); -// for (List segment : split) { -// iDingOrganizationService.saveBatch(segment); -// } -// } -// } -// log.info("----拉取浙政钉组织结构结束---,顶级code:" + orgCode); -// } -// } -// -// -// private void getDingOrgChild(DingOrgInfoTreeDTO parentDingOrgInfoTreeDTO) { -// String parentOrgCode = parentDingOrgInfoTreeDTO.getCode(); -// DingOrgInfoDTO orgInfoDTO = parentDingOrgInfoTreeDTO.getDingOrgInfoDTO(); -// boolean leaf = orgInfoDTO.getLeaf(); -// if (!leaf) { -// int currentPage = 1; -// int pageSize = 100; -// PageSubOrganizationCodeDTO pageSubOrganizationCodeDTO = -// dingApiClient.pageSubOrganizationCodes(currentPage++, pageSize, parentOrgCode); -// List subOrganizationCodeList = new ArrayList<>(pageSubOrganizationCodeDTO.getSubOrganizationCodeList()); -// Long totalSize = pageSubOrganizationCodeDTO.getTotalSize(); -// -// while (totalSize > (long) currentPage * pageSize) { -// PageSubOrganizationCodeDTO subOrganizationCodeDTO = dingApiClient -// .pageSubOrganizationCodes(currentPage++, pageSize, parentOrgCode); -// if (CollectionUtils.isNotEmpty(subOrganizationCodeDTO.getSubOrganizationCodeList())) { -// subOrganizationCodeList.addAll(subOrganizationCodeDTO.getSubOrganizationCodeList()); -// } -// } -// -// if (CollectionUtils.isNotEmpty(subOrganizationCodeList)) { -// List dingOrgInfoDtos = dingApiClient -// .listOrganizationsByCodes(subOrganizationCodeList); -// List dingOrgInfoTreeDTOList = dingOrgInfoDtos.stream().map(r -> { -// DingOrgInfoTreeDTO dingOrgInfoTreeDTO = new DingOrgInfoTreeDTO(); -// dingOrgInfoTreeDTO.setCode(r.getOrganizationCode()); -// dingOrgInfoTreeDTO.setDingOrgInfoDTO(r); -// dingOrgInfoTreeDTO.setChildCodes(new ArrayList<>()); -// getDingOrgChild(dingOrgInfoTreeDTO); -// return dingOrgInfoTreeDTO; -// }).collect(Collectors.toList()); -// parentDingOrgInfoTreeDTO.setChildCodes(dingOrgInfoTreeDTOList); -// } -// -// } -// } -// -// -// private void buildSaveRecordList(List treeDTOList, List saveRecordList) { -// if (CollectionUtils.isEmpty(treeDTOList)) { -// return; -// } -// for (DingOrgInfoTreeDTO dingOrgInfoTreeDTO : treeDTOList) { -// DingOrganization saveRecord = new DingOrganization(); -// DingOrgInfoDTO dingOrgInfoDTO = dingOrgInfoTreeDTO.getDingOrgInfoDTO(); -// List childCodes = dingOrgInfoTreeDTO.getChildCodes(); -// saveRecord.setDeptSort(dingOrgInfoDTO.getDisplayOrder()); + List treeDTOList = new ArrayList<>(); + + DingOrgInfoTreeDTO childDingOrgInfoTreeDTO = new DingOrgInfoTreeDTO(); + //设置节点详情 + if (dingOrgInfoDtos != null && !dingOrgInfoDtos.isEmpty()) { + for (DingOrgInfoDTO orgInfo : dingOrgInfoDtos) { + if (orgInfo.getOrganizationCode().equals(orgCode)) { + childDingOrgInfoTreeDTO.setDingOrgInfoDTO(orgInfo); + } + } + } + childDingOrgInfoTreeDTO.setCode(orgCode); + childDingOrgInfoTreeDTO.setChildCodes(new ArrayList<>()); + getDingOrgChild(childDingOrgInfoTreeDTO); + treeDTOList.add(childDingOrgInfoTreeDTO); + + if (CollectionUtils.isNotEmpty(treeDTOList)) { + List saveRecordList = new ArrayList<>(); + buildSaveRecordList(treeDTOList, saveRecordList); + + // 批量保存 + if (saveRecordList.size() <= GROUP_SIZE) { + iDingOrganizationService.saveBatch(saveRecordList); + } else { + List> split = Lists.partition(saveRecordList, GROUP_SIZE); + for (List segment : split) { + iDingOrganizationService.saveBatch(segment); + } + } + } + log.info("----拉取浙政钉组织结构结束---,顶级code:" + orgCode); + } + } + } + + private void buildSaveRecordList(List treeDTOList, List saveRecordList) { + if (CollectionUtils.isEmpty(treeDTOList)) { + return; + } + for (DingOrgInfoTreeDTO dingOrgInfoTreeDTO : treeDTOList) { + DingOrganization saveRecord = new DingOrganization(); + DingOrgInfoDTO dingOrgInfoDTO = dingOrgInfoTreeDTO.getDingOrgInfoDTO(); + List childCodes = dingOrgInfoTreeDTO.getChildCodes(); + saveRecord.setDisplayOrder(dingOrgInfoDTO.getDisplayOrder()); // saveRecord.setEnabled("1"); -// saveRecord.setParentCode(dingOrgInfoDTO.getParentCode()); -// saveRecord.setOrganizationCode(dingOrgInfoDTO.getOrganizationCode()); + saveRecord.setParentCode(dingOrgInfoDTO.getParentCode()); + saveRecord.setOrganizationCode(dingOrgInfoDTO.getOrganizationCode()); // saveRecord.setSubCount((long) dingOrgInfoTreeDTO.getChildCodes().size()); -// saveRecord.setName(dingOrgInfoDTO.getOrganizationName()); -// saveRecordList.add(saveRecord); -// if (CollectionUtils.isNotEmpty(childCodes)) { -// buildSaveRecordList(childCodes, saveRecordList); -// } -// } -// -// } -// -// -//} + saveRecord.setOrganizationName(dingOrgInfoDTO.getOrganizationName()); + saveRecordList.add(saveRecord); + if (CollectionUtils.isNotEmpty(childCodes)) { + buildSaveRecordList(childCodes, saveRecordList); + } + } + + } + + private void getDingOrgChild(DingOrgInfoTreeDTO parentDingOrgInfoTreeDTO) { + String parentOrgCode = parentDingOrgInfoTreeDTO.getCode(); + DingOrgInfoDTO orgInfoDTO = parentDingOrgInfoTreeDTO.getDingOrgInfoDTO(); + boolean leaf = orgInfoDTO.getLeaf(); + if (!leaf) { + int currentPage = 1; + int pageSize = 100; + GenericResult pageSubOrganizationCodeDTOGenericResult = zwddClient.pageSubOrganizationCodes(currentPage++, pageSize, parentOrgCode); + PageSubOrganizationCodeDTO pageSubOrganizationCodeDTO = pageSubOrganizationCodeDTOGenericResult.getData(); + + List subOrganizationCodeList = new ArrayList<>(pageSubOrganizationCodeDTO.getSubOrganizationCodeList()); + Long totalSize = pageSubOrganizationCodeDTO.getTotalSize(); + + while (totalSize > (long) currentPage * pageSize) { + GenericResult subPageSubOrganizationCodeDTOGenericResult = zwddClient + .pageSubOrganizationCodes(currentPage++, pageSize, parentOrgCode); + PageSubOrganizationCodeDTO subOrganizationCodeDTO = subPageSubOrganizationCodeDTOGenericResult.getData(); + if (CollectionUtils.isNotEmpty(subOrganizationCodeDTO.getSubOrganizationCodeList())) { + subOrganizationCodeList.addAll(subOrganizationCodeDTO.getSubOrganizationCodeList()); + } + } + + if (CollectionUtils.isNotEmpty(subOrganizationCodeList)) { + GenericResult> listGenericResult = zwddClient + .listOrganizationsByCodes(subOrganizationCodeList); + List dingOrgInfoDtos = listGenericResult.getData(); + List dingOrgInfoTreeDTOList = dingOrgInfoDtos.stream().map(r -> { + DingOrgInfoTreeDTO dingOrgInfoTreeDTO = new DingOrgInfoTreeDTO(); + dingOrgInfoTreeDTO.setCode(r.getOrganizationCode()); + dingOrgInfoTreeDTO.setDingOrgInfoDTO(r); + dingOrgInfoTreeDTO.setChildCodes(new ArrayList<>()); + getDingOrgChild(dingOrgInfoTreeDTO); + return dingOrgInfoTreeDTO; + }).collect(Collectors.toList()); + parentDingOrgInfoTreeDTO.setChildCodes(dingOrgInfoTreeDTOList); + } + + } + } + +} diff --git a/pmapi/src/main/java/com/ningdatech/pmapi/organization/entity/DingOrganization.java b/pmapi/src/main/java/com/ningdatech/pmapi/organization/entity/DingOrganization.java index 2060ba9..25296fd 100644 --- a/pmapi/src/main/java/com/ningdatech/pmapi/organization/entity/DingOrganization.java +++ b/pmapi/src/main/java/com/ningdatech/pmapi/organization/entity/DingOrganization.java @@ -1,14 +1,15 @@ package com.ningdatech.pmapi.organization.entity; import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import lombok.Data; + import java.io.Serializable; import java.time.LocalDateTime; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; /** *

- * + * *

* * @author Lierbao @@ -16,6 +17,7 @@ import io.swagger.annotations.ApiModelProperty; */ @TableName("ding_organization") @ApiModel(value = "DingOrganization对象", description = "") +@Data public class DingOrganization implements Serializable { private static final long serialVersionUID = 1L; @@ -50,130 +52,4 @@ public class DingOrganization implements Serializable { private String status; - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - public String getInstitutionLevelCode() { - return institutionLevelCode; - } - - public void setInstitutionLevelCode(String institutionLevelCode) { - this.institutionLevelCode = institutionLevelCode; - } - public String getAddress() { - return address; - } - - public void setAddress(String address) { - this.address = address; - } - public String getOrganizationName() { - return organizationName; - } - - public void setOrganizationName(String organizationName) { - this.organizationName = organizationName; - } - public Long getDisplayOrder() { - return displayOrder; - } - - public void setDisplayOrder(Long displayOrder) { - this.displayOrder = displayOrder; - } - public Long getTypeName() { - return typeName; - } - - public void setTypeName(Long typeName) { - this.typeName = typeName; - } - public Integer getLeaf() { - return leaf; - } - - public void setLeaf(Integer leaf) { - this.leaf = leaf; - } - public LocalDateTime getGmtCreate() { - return gmtCreate; - } - - public void setGmtCreate(LocalDateTime gmtCreate) { - this.gmtCreate = gmtCreate; - } - public String getTypeCode() { - return typeCode; - } - - public void setTypeCode(String typeCode) { - this.typeCode = typeCode; - } - public String getDivisionCode() { - return divisionCode; - } - - public void setDivisionCode(String divisionCode) { - this.divisionCode = divisionCode; - } - public String getParentName() { - return parentName; - } - - public void setParentName(String parentName) { - this.parentName = parentName; - } - public String getParentCode() { - return parentCode; - } - - public void setParentCode(String parentCode) { - this.parentCode = parentCode; - } - public String getOrganizationCode() { - return organizationCode; - } - - public void setOrganizationCode(String organizationCode) { - this.organizationCode = organizationCode; - } - public String getBusinessStripCodes() { - return businessStripCodes; - } - - public void setBusinessStripCodes(String businessStripCodes) { - this.businessStripCodes = businessStripCodes; - } - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - @Override - public String toString() { - return "DingOrganization{" + - "id=" + id + - ", institutionLevelCode=" + institutionLevelCode + - ", address=" + address + - ", organizationName=" + organizationName + - ", displayOrder=" + displayOrder + - ", typeName=" + typeName + - ", leaf=" + leaf + - ", gmtCreate=" + gmtCreate + - ", typeCode=" + typeCode + - ", divisionCode=" + divisionCode + - ", parentName=" + parentName + - ", parentCode=" + parentCode + - ", organizationCode=" + organizationCode + - ", businessStripCodes=" + businessStripCodes + - ", status=" + status + - "}"; - } } diff --git a/pmapi/src/main/resources/application-dev.yml b/pmapi/src/main/resources/application-dev.yml index 96fe00f..7727198 100644 --- a/pmapi/src/main/resources/application-dev.yml +++ b/pmapi/src/main/resources/application-dev.yml @@ -158,12 +158,9 @@ sa-token: #专有钉钉 ding: #扫码 - app-auth-key: file-manage_dingoa-zte2LbiAfIj - app-auth-secret: H794aFZf271QbfUr50pbBpBTlXSrWIP71q9RTR34 - #扫码 - app-sso-auth-key: fgdn_wjlzjkxt_hz - app-sso-auth-secret: dafe1e6f7d424032acb81f5c2a797a1f - #免登/获取信息 + app-auth-key: expert-base_dingoa-c5nnefYVnie + app-auth-secret: nm8qtST8uK431HYrjr7srcE23sT4889QgMcYFM3L + # #免登/获取信息 app-key: file-manage-4Mjx9358wuxjyYFjY3 app-secret: hE41938wqyQ5LOpc1QDRA9e7gb5YugoClWD3nY4O #专有钉钉在开发管理工作台,右键查看网页源码realmId: '31141',浙政钉固定196729 diff --git a/pmapi/src/main/resources/integration/zwdd-dev.yml b/pmapi/src/main/resources/integration/zwdd-dev.yml new file mode 100644 index 0000000..0073994 --- /dev/null +++ b/pmapi/src/main/resources/integration/zwdd-dev.yml @@ -0,0 +1,11 @@ +#专有钉钉 +ding: + #扫码 + app-auth-key: expert-base_dingoa-c5nnefYVnie + app-auth-secret: nm8qtST8uK431HYrjr7srcE23sT4889QgMcYFM3L + # #免登/获取信息 + app-key: file-manage-4Mjx9358wuxjyYFjY3 + app-secret: hE41938wqyQ5LOpc1QDRA9e7gb5YugoClWD3nY4O + #专有钉钉在开发管理工作台,右键查看网页源码realmId: '31141',浙政钉固定196729 + tenantId: 31141 + domain: openplatform.dg-work.cn \ No newline at end of file diff --git a/pmapi/src/test/java/com/ningdatech/pmapi/organization/OrganizationTest.java b/pmapi/src/test/java/com/ningdatech/pmapi/organization/OrganizationTest.java new file mode 100644 index 0000000..1f08428 --- /dev/null +++ b/pmapi/src/test/java/com/ningdatech/pmapi/organization/OrganizationTest.java @@ -0,0 +1,30 @@ +package com.ningdatech.pmapi.organization; + +import com.ningdatech.pmapi.AppTests; +import com.ningdatech.pmapi.ding.task.OrganizationBatchGetTask; +import com.ningdatech.zwdd.client.ZwddAuthClient; +import com.ningdatech.zwdd.client.ZwddClient; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author liuxinxin + * @date 2023/2/9 下午3:54 + */ + +public class OrganizationTest extends AppTests { + + @Autowired + private ZwddClient zwddClient; + + @Autowired + private ZwddAuthClient zwddAuthClient; + + @Autowired + private OrganizationBatchGetTask organizationBatchGetTask; + + public void testBatchGetOrganization() { + + + } + +}