Commit 4066ad3e1ae8b08cedb13d713dfef7ea5c3be6c4

Authored by YunaiV
1 parent d604e76d

✨ CRM:完善合同的审批逻辑

Showing 13 changed files with 35 additions and 143 deletions
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/BpmResultListenerApi.java deleted
1 -package cn.iocoder.yudao.module.bpm.api.listener;  
2 -  
3 -import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;  
4 -  
5 -// TODO @芋艿:后续改成支持 RPC  
6 -/**  
7 - * 业务流程实例的结果发生变化的监听器 Api  
8 - *  
9 - * @author HUIHUI  
10 - */  
11 -public interface BpmResultListenerApi {  
12 -  
13 - /**  
14 - * 监听的流程定义 Key  
15 - *  
16 - * @return 返回监听的流程定义 Key  
17 - */  
18 - String getProcessDefinitionKey();  
19 -  
20 - /**  
21 - * 处理事件  
22 - *  
23 - * @param event 事件  
24 - */  
25 - void onEvent(BpmResultListenerRespDTO event);  
26 -  
27 -}  
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/listener/dto/BpmResultListenerRespDTO.java deleted
1 -package cn.iocoder.yudao.module.bpm.api.listener.dto;  
2 -  
3 -import lombok.Data;  
4 -  
5 -// TODO @芋艿:后续改成支持 RPC  
6 -/**  
7 - * 业务流程实例的结果 Response DTO  
8 - *  
9 - * @author HUIHUI  
10 - */  
11 -@Data  
12 -public class BpmResultListenerRespDTO {  
13 -  
14 - /**  
15 - * 流程实例的编号  
16 - */  
17 - private String id;  
18 - /**  
19 - * 流程实例的 key  
20 - */  
21 - private String processDefinitionKey;  
22 - /**  
23 - * 流程实例的结果  
24 - */  
25 - private Integer result;  
26 - /**  
27 - * 流程实例对应的业务标识  
28 - * 例如说,请假  
29 - */  
30 - private String businessKey;  
31 -  
32 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/core/event/BpmProcessInstanceResultEvent.java renamed to yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/event/BpmProcessInstanceResultEvent.java
1 -package cn.iocoder.yudao.module.bpm.framework.bpm.core.event; 1 +package cn.iocoder.yudao.module.bpm.event;
2 2
3 -import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; 3 +import jakarta.validation.constraints.NotNull;
4 import lombok.Data; 4 import lombok.Data;
5 import org.springframework.context.ApplicationEvent; 5 import org.springframework.context.ApplicationEvent;
6 6
7 -import jakarta.validation.constraints.NotNull;  
8 -  
9 /** 7 /**
10 * 流程实例的结果发生变化的 Event 8 * 流程实例的结果发生变化的 Event
11 * 定位:由于额外增加了 {@link BpmProcessInstanceExtDO#getResult()} 结果,所以增加该事件 9 * 定位:由于额外增加了 {@link BpmProcessInstanceExtDO#getResult()} 结果,所以增加该事件
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/core/event/BpmProcessInstanceResultEventListener.java renamed to yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/event/BpmProcessInstanceResultEventListener.java
1 -package cn.iocoder.yudao.module.bpm.framework.bpm.core.event; 1 +package cn.iocoder.yudao.module.bpm.event;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 import org.springframework.context.ApplicationListener; 4 import org.springframework.context.ApplicationListener;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI @@ -7,7 +7,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessI
7 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; 7 import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO;
8 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; 8 import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO;
9 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; 9 import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO;
10 -import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent; 10 +import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEvent;
11 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; 11 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO;
12 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; 12 import cn.iocoder.yudao.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO;
13 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 13 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/core/event/BpmProcessInstanceResultEventPublisher.java
1 package cn.iocoder.yudao.module.bpm.framework.bpm.core.event; 1 package cn.iocoder.yudao.module.bpm.framework.bpm.core.event;
2 2
  3 +import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEvent;
3 import lombok.AllArgsConstructor; 4 import lombok.AllArgsConstructor;
4 import org.springframework.context.ApplicationEventPublisher; 5 import org.springframework.context.ApplicationEventPublisher;
5 import org.springframework.validation.annotation.Validated; 6 import org.springframework.validation.annotation.Validated;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/bpm/listener/BpmServiceResultListener.java deleted
1 -package cn.iocoder.yudao.module.bpm.framework.bpm.listener;  
2 -  
3 -import cn.hutool.core.util.StrUtil;  
4 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
5 -import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi;  
6 -import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;  
7 -import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;  
8 -import jakarta.annotation.Resource;  
9 -import org.springframework.context.ApplicationListener;  
10 -import org.springframework.stereotype.Component;  
11 -  
12 -import java.util.List;  
13 -  
14 -// TODO @芋艿:后续改成支持 RPC  
15 -/**  
16 - * 业务流程结果监听器实现类  
17 - *  
18 - * @author HUIHUI  
19 - */  
20 -@Component  
21 -public class BpmServiceResultListener implements ApplicationListener<BpmProcessInstanceResultEvent> {  
22 -  
23 - @Resource  
24 - private List<BpmResultListenerApi> bpmResultListenerApis;  
25 -  
26 - @Override  
27 - public final void onApplicationEvent(BpmProcessInstanceResultEvent event) {  
28 - bpmResultListenerApis.forEach(bpmResultListenerApi -> {  
29 - if (!StrUtil.equals(event.getProcessDefinitionKey(), bpmResultListenerApi.getProcessDefinitionKey())) {  
30 - return;  
31 - }  
32 - bpmResultListenerApi.onEvent(BeanUtils.toBean(event, BpmResultListenerRespDTO.class));  
33 - });  
34 - }  
35 -  
36 -}  
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/listener/BpmOALeaveResultListener.java
1 package cn.iocoder.yudao.module.bpm.service.oa.listener; 1 package cn.iocoder.yudao.module.bpm.service.oa.listener;
2 2
3 -import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEvent;  
4 -import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventListener; 3 +import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEvent;
  4 +import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEventListener;
5 import cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveService; 5 import cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveService;
6 import cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveServiceImpl; 6 import cn.iocoder.yudao.module.bpm.service.oa.BpmOALeaveServiceImpl;
7 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -11,8 +11,9 @@ public interface ErrorCodeConstants { @@ -11,8 +11,9 @@ public interface ErrorCodeConstants {
11 11
12 // ========== 合同管理 1-020-000-000 ========== 12 // ========== 合同管理 1-020-000-000 ==========
13 ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在"); 13 ErrorCode CONTRACT_NOT_EXISTS = new ErrorCode(1_020_000_000, "合同不存在");
14 - ErrorCode CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_020_000_001, "更新合同失败,原因:禁止编辑"); 14 + ErrorCode CONTRACT_UPDATE_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_001, "合同更新失败,原因:合同不是草稿状态");
15 ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态"); 15 ErrorCode CONTRACT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_000_002, "合同提交审核失败,原因:合同没处在未提交状态");
  16 + ErrorCode CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_000_003, "更新合同审核状态失败,原因:合同不是处理中状态");
16 17
17 // ========== 线索管理 1-020-001-000 ========== 18 // ========== 线索管理 1-020-001-000 ==========
18 ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); 19 ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在");
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contract/vo/CrmContractRespVO.java
@@ -58,7 +58,7 @@ public class CrmContractRespVO { @@ -58,7 +58,7 @@ public class CrmContractRespVO {
58 58
59 @Schema(description = "工作流编号", example = "1043") 59 @Schema(description = "工作流编号", example = "1043")
60 @ExcelProperty("工作流编号") 60 @ExcelProperty("工作流编号")
61 - private Long processInstanceId; 61 + private String processInstanceId;
62 62
63 @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") 63 @Schema(description = "审批状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
64 @ExcelProperty("审批状态") 64 @ExcelProperty("审批状态")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractService.java
1 package cn.iocoder.yudao.module.crm.service.contract; 1 package cn.iocoder.yudao.module.crm.service.contract;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;  
5 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO; 4 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO; 5 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
@@ -72,9 +71,10 @@ public interface CrmContractService { @@ -72,9 +71,10 @@ public interface CrmContractService {
72 /** 71 /**
73 * 更新合同流程审批结果 72 * 更新合同流程审批结果
74 * 73 *
75 - * @param event 审批结果 74 + * @param id 合同编号
  75 + * @param bpmResult BPM 审批结果
76 */ 76 */
77 - void updateContractAuditStatus(BpmResultListenerRespDTO event); 77 + void updateContractAuditStatus(Long id, Integer bpmResult);
78 78
79 /** 79 /**
80 * 获得合同 80 * 获得合同
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; 8 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; 10 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
11 -import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO;  
12 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; 11 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
13 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; 12 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
14 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; 13 import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum;
@@ -35,6 +34,7 @@ import com.mzt.logapi.context.LogRecordContext; @@ -35,6 +34,7 @@ import com.mzt.logapi.context.LogRecordContext;
35 import com.mzt.logapi.service.impl.DiffParseFunction; 34 import com.mzt.logapi.service.impl.DiffParseFunction;
36 import com.mzt.logapi.starter.annotation.LogRecord; 35 import com.mzt.logapi.starter.annotation.LogRecord;
37 import jakarta.annotation.Resource; 36 import jakarta.annotation.Resource;
  37 +import lombok.extern.slf4j.Slf4j;
38 import org.springframework.stereotype.Service; 38 import org.springframework.stereotype.Service;
39 import org.springframework.transaction.annotation.Transactional; 39 import org.springframework.transaction.annotation.Transactional;
40 import org.springframework.validation.annotation.Validated; 40 import org.springframework.validation.annotation.Validated;
@@ -56,6 +56,7 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; @@ -56,6 +56,7 @@ import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
56 */ 56 */
57 @Service 57 @Service
58 @Validated 58 @Validated
  59 +@Slf4j
59 public class CrmContractServiceImpl implements CrmContractService { 60 public class CrmContractServiceImpl implements CrmContractService {
60 61
61 /** 62 /**
@@ -129,7 +130,7 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -129,7 +130,7 @@ public class CrmContractServiceImpl implements CrmContractService {
129 // 1.2 只有草稿、审批中,可以编辑; 130 // 1.2 只有草稿、审批中,可以编辑;
130 if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), 131 if (!ObjectUtils.equalsAny(contract.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(),
131 CrmAuditStatusEnum.PROCESS.getStatus())) { 132 CrmAuditStatusEnum.PROCESS.getStatus())) {
132 - throw exception(CONTRACT_UPDATE_FAIL_EDITING_PROHIBITED); 133 + throw exception(CONTRACT_UPDATE_FAIL_NOT_DRAFT);
133 } 134 }
134 // 1.3 校验产品项的有效性 135 // 1.3 校验产品项的有效性
135 List<CrmContractProductDO> contractProducts = validateContractProducts(updateReqVO.getProducts()); 136 List<CrmContractProductDO> contractProducts = validateContractProducts(updateReqVO.getProducts());
@@ -291,35 +292,22 @@ public class CrmContractServiceImpl implements CrmContractService { @@ -291,35 +292,22 @@ public class CrmContractServiceImpl implements CrmContractService {
291 } 292 }
292 293
293 @Override 294 @Override
294 - public void updateContractAuditStatus(BpmResultListenerRespDTO event) {  
295 - // 判断下状态是否符合预期  
296 - if (!isEndResult(event.getResult())) {  
297 - return;  
298 - }  
299 - // 状态转换  
300 - if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.APPROVE.getResult())) {  
301 - event.setResult(CrmAuditStatusEnum.APPROVE.getStatus());  
302 - }  
303 - if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.REJECT.getResult())) {  
304 - event.setResult(CrmAuditStatusEnum.REJECT.getStatus());  
305 - }  
306 - if (ObjUtil.equal(event.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult())) {  
307 - event.setResult(CrmAuditStatusEnum.CANCEL.getStatus()); 295 + public void updateContractAuditStatus(Long id, Integer bpmResult) {
  296 + // 1.1 校验合同是否存在
  297 + CrmContractDO contract = validateContractExists(id);
  298 + // 1.2 只有审批中,可以更新审批结果
  299 + if (ObjUtil.notEqual(contract.getAuditStatus(), CrmAuditStatusEnum.PROCESS.getStatus())) {
  300 + log.error("[updateContractAuditStatus][contract({}) 不处于审批中,无法更新审批结果({})]",
  301 + contract.getId(), bpmResult);
  302 + throw exception(CONTRACT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
308 } 303 }
309 - // 更新合同状态  
310 - contractMapper.updateById(new CrmContractDO().setId(Long.parseLong(event.getBusinessKey()))  
311 - .setAuditStatus(event.getResult()));  
312 - }  
313 304
314 - /**  
315 - * 判断该结果是否处于 End 最终结果  
316 - *  
317 - * @param result 结果  
318 - * @return 是否  
319 - */  
320 - public static boolean isEndResult(Integer result) {  
321 - return ObjectUtils.equalsAny(result, BpmProcessInstanceResultEnum.APPROVE.getResult(),  
322 - BpmProcessInstanceResultEnum.REJECT.getResult(), BpmProcessInstanceResultEnum.CANCEL.getResult()); 305 + // 2. 更新合同审批结果
  306 + Integer auditStatus = BpmProcessInstanceResultEnum.APPROVE.getResult().equals(bpmResult) ? CrmAuditStatusEnum.APPROVE.getStatus()
  307 + : BpmProcessInstanceResultEnum.REJECT.getResult().equals(bpmResult) ? CrmAuditStatusEnum.REJECT.getStatus()
  308 + : BpmProcessInstanceResultEnum.CANCEL.getResult();
  309 + Assert.notNull(auditStatus, "BPM 审批结果({}) 转换失败", bpmResult);
  310 + contractMapper.updateById(new CrmContractDO().setId(id).setAuditStatus(auditStatus));
323 } 311 }
324 312
325 //======================= 查询相关 ======================= 313 //======================= 查询相关 =======================
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contract/listener/CrmContractResultListener.java
1 package cn.iocoder.yudao.module.crm.service.contract.listener; 1 package cn.iocoder.yudao.module.crm.service.contract.listener;
2 2
3 -import cn.iocoder.yudao.module.bpm.api.listener.BpmResultListenerApi;  
4 -import cn.iocoder.yudao.module.bpm.api.listener.dto.BpmResultListenerRespDTO; 3 +import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEvent;
  4 +import cn.iocoder.yudao.module.bpm.event.BpmProcessInstanceResultEventListener;
5 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; 5 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
6 import cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl; 6 import cn.iocoder.yudao.module.crm.service.contract.CrmContractServiceImpl;
7 import jakarta.annotation.Resource; 7 import jakarta.annotation.Resource;
8 import org.springframework.stereotype.Component; 8 import org.springframework.stereotype.Component;
9 9
10 -// TODO @芋艿:后续改成支持 RPC  
11 /** 10 /**
12 * 合同审批的结果的监听器实现类 11 * 合同审批的结果的监听器实现类
13 * 12 *
14 * @author HUIHUI 13 * @author HUIHUI
15 */ 14 */
16 @Component 15 @Component
17 -public class CrmContractResultListener implements BpmResultListenerApi { 16 +public class CrmContractResultListener extends BpmProcessInstanceResultEventListener {
18 17
19 @Resource 18 @Resource
20 private CrmContractService contractService; 19 private CrmContractService contractService;
@@ -25,8 +24,8 @@ public class CrmContractResultListener implements BpmResultListenerApi { @@ -25,8 +24,8 @@ public class CrmContractResultListener implements BpmResultListenerApi {
25 } 24 }
26 25
27 @Override 26 @Override
28 - public void onEvent(BpmResultListenerRespDTO event) {  
29 - contractService.updateContractAuditStatus(event); 27 + protected void onEvent(BpmProcessInstanceResultEvent event) {
  28 + contractService.updateContractAuditStatus(Long.parseLong(event.getBusinessKey()), event.getResult());
30 } 29 }
31 30
32 } 31 }