diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java index 4370ff82b3fe121f008c0ff67ad5bcffcbe695c3..9e36cd06f053b9fbc38827d16ea9815ef316d53a 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwTaskController.java @@ -18,6 +18,7 @@ import org.dromara.workflow.domain.bo.*; import org.dromara.workflow.domain.vo.FlowHisTaskVo; import org.dromara.workflow.domain.vo.FlowTaskVo; import org.dromara.workflow.service.IFlwTaskService; +import org.dromara.workflow.utils.FlwCommonUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -206,7 +207,7 @@ public class FlwTaskController extends BaseController { */ @GetMapping("/currentTaskAllUser/{taskId}") public R> currentTaskAllUser(@PathVariable Long taskId) { - return R.ok(flwTaskService.currentTaskAllUser(List.of(taskId))); + return R.ok(FlwCommonUtils.currentTaskAllUser(List.of(taskId))); } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 6a18bea6101277d52d391877721bfe8555ecc24e..2cef3a4abae426e8c263eef5f760309ea78762e2 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -8,6 +8,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.utils.StringUtils; +import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.Definition; import org.dromara.warm.flow.core.entity.Instance; @@ -20,9 +21,9 @@ import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.domain.bo.FlowCopyBo; import org.dromara.workflow.handler.FlowProcessEventHandler; -import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwInstanceService; import org.dromara.workflow.service.IFlwTaskService; +import org.dromara.workflow.utils.FlwCommonUtils; import org.springframework.stereotype.Component; import java.util.HashMap; @@ -41,10 +42,8 @@ import java.util.Map; public class WorkflowGlobalListener implements GlobalListener { private final IFlwTaskService flwTaskService; - private final IFlwInstanceService instanceService; + private final IFlwInstanceService flwInstanceService; private final FlowProcessEventHandler flowProcessEventHandler; - private final IFlwCommonService flwCommonService; - private final InsService insService; /** * 创建监听器,任务创建时执行 @@ -77,7 +76,7 @@ public class WorkflowGlobalListener implements GlobalListener { FlowParams flowParams = listenerVariable.getFlowParams(); Definition definition = listenerVariable.getDefinition(); Instance instance = listenerVariable.getInstance(); - String applyNodeCode = flwCommonService.applyNodeCode(definition.getId()); + String applyNodeCode = FlwCommonUtils.applyNodeCode(definition.getId()); for (Task flowTask : nextTasks) { // 如果办理或者退回并行存在需要指定办理人,则直接覆盖办理人 if (variable.containsKey(flowTask.getNodeCode()) && TaskStatusEnum.isPassOrBack(flowParams.getHisStatus())) { @@ -117,6 +116,7 @@ public class WorkflowGlobalListener implements GlobalListener { } //申请人提交事件 Boolean submit = MapUtil.getBool(variable, FlowConstant.SUBMIT); + InsService insService = FlowEngine.insService(); if (submit != null && submit) { flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, instance.getFlowStatus(), variable, true); } else { @@ -127,7 +127,7 @@ public class WorkflowGlobalListener implements GlobalListener { } if (!BusinessStatusEnum.initialState(instance.getFlowStatus())) { if (task != null && CollUtil.isNotEmpty(nextTasks) && nextTasks.size() == 1 - && flwCommonService.applyNodeCode(definition.getId()).equals(nextTasks.get(0).getNodeCode())) { + && FlwCommonUtils.applyNodeCode(definition.getId()).equals(nextTasks.get(0).getNodeCode())) { // 如果为画线指定驳回 线条指定为驳回 驳回得节点为申请人节点 则修改流程状态为退回 flowProcessEventHandler.processHandler(definition.getFlowCode(), instance, BusinessStatusEnum.BACK.getStatus(), params, false); // 修改流程实例状态 @@ -154,17 +154,16 @@ public class WorkflowGlobalListener implements GlobalListener { } if (variable.containsKey(FlowConstant.FLOW_COPY_LIST)) { - List flowCopyList = MapUtil.get(variable, FlowConstant.FLOW_COPY_LIST, new TypeReference<>() {}); + List flowCopyList = MapUtil.get(variable, FlowConstant.FLOW_COPY_LIST, new TypeReference<>() { + }); // 添加抄送人 flwTaskService.setCopy(task, flowCopyList); } if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) { - List messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() {}); + List messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() { + }); String notice = MapUtil.getStr(variable, FlowConstant.MESSAGE_NOTICE); - // 消息通知 - if (CollUtil.isNotEmpty(messageType)) { - flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); - } + FlwCommonUtils.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); } insService.removeVariables(instance.getId(), FlowConstant.FLOW_COPY_LIST, @@ -190,7 +189,7 @@ public class WorkflowGlobalListener implements GlobalListener { if (flwTaskService.isTaskEnd(instanceId)) { String status = BusinessStatusEnum.FINISH.getStatus(); // 更新流程状态为已完成 - instanceService.updateStatus(instanceId, status); + flwInstanceService.updateStatus(instanceId, status); log.info("流程已结束,状态更新为: {}", status); return status; } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java deleted file mode 100644 index f809f47b349f69fbaa5241f462ba24a2bfb63d4d..0000000000000000000000000000000000000000 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwCommonService.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.dromara.workflow.service; - -import org.dromara.common.core.domain.dto.UserDTO; - -import java.util.List; - -/** - * 通用 工作流服务 - * - * @author LionLi - */ -public interface IFlwCommonService { - - /** - * 发送消息 - * - * @param flowName 流程定义名称 - * @param instId 实例id - * @param messageType 消息类型 - * @param message 消息内容,为空则发送默认配置的消息内容 - */ - void sendMessage(String flowName, Long instId, List messageType, String message); - - /** - * 发送消息 - * - * @param messageType 消息类型 - * @param message 消息内容 - * @param subject 邮件标题 - * @param userList 接收用户 - */ - void sendMessage(List messageType, String message, String subject, List userList); - - /** - * 申请人节点编码 - * - * @param definitionId 流程定义id - * @return 申请人节点编码 - */ - String applyNodeCode(Long definitionId); -} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java index f9dc45e1d48ac2f14694bca48bac817fccd665ce..7b2f5f018fd057c5998eb476aee4e57d0685a562 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IFlwTaskService.java @@ -1,7 +1,6 @@ package org.dromara.workflow.service; import org.dromara.common.core.domain.dto.StartProcessReturnDTO; -import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.warm.flow.core.entity.Node; @@ -111,8 +110,8 @@ public interface IFlwTaskService { /** * 获取可驳回的前置节点 * - * @param taskId 任务id - * @param nowNodeCode 当前节点 + * @param taskId 任务id + * @param nowNodeCode 当前节点 * @return 结果 */ List getBackTaskNode(Long taskId, String nowNodeCode); @@ -190,14 +189,6 @@ public interface IFlwTaskService { */ boolean taskOperation(TaskOperationBo bo, String taskOperation); - /** - * 获取当前任务的所有办理人 - * - * @param taskIds 任务id - * @return 结果 - */ - List currentTaskAllUser(List taskIds); - /** * 按照节点编码查询节点 * diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java index b89a369cfb6cf2a4c03b93459102fef3cb430418..61c0bf209dda7d9ed939958bb5a94b2e3fac220b 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java @@ -33,8 +33,8 @@ import org.dromara.workflow.common.constant.FlowConstant; import org.dromara.workflow.domain.FlowCategory; import org.dromara.workflow.domain.vo.FlowDefinitionVo; import org.dromara.workflow.mapper.FlwCategoryMapper; -import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwDefinitionService; +import org.dromara.workflow.utils.FlwCommonUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.multipart.MultipartFile; @@ -64,7 +64,6 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { private final FlowNodeMapper flowNodeMapper; private final FlowSkipMapper flowSkipMapper; private final FlwCategoryMapper flwCategoryMapper; - private final IFlwCommonService flwCommonService; /** * 查询流程定义列表 @@ -121,7 +120,7 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { List flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper().eq(FlowNode::getDefinitionId, id)); List errorMsg = new ArrayList<>(); if (CollUtil.isNotEmpty(flowNodes)) { - String applyNodeCode = flwCommonService.applyNodeCode(id); + String applyNodeCode = FlwCommonUtils.applyNodeCode(id); for (FlowNode flowNode : flowNodes) { if (StringUtils.isBlank(flowNode.getPermissionFlag()) && !applyNodeCode.equals(flowNode.getNodeCode()) && NodeType.BETWEEN.getKey().equals(flowNode.getNodeType())) { errorMsg.add(flowNode.getNodeName()); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 22b78680f4b965876a9fa7c0ff333e08af3dffc1..b9fb871c6e04a503378a1c6a0ae822a2ef1417be 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -16,7 +16,6 @@ import org.dromara.common.core.domain.dto.StartProcessReturnDTO; import org.dromara.common.core.domain.dto.UserDTO; import org.dromara.common.core.enums.BusinessStatusEnum; import org.dromara.common.core.exception.ServiceException; -import org.dromara.common.core.service.UserService; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ValidatorUtils; @@ -51,10 +50,10 @@ import org.dromara.workflow.domain.vo.FlowTaskVo; import org.dromara.workflow.mapper.FlwCategoryMapper; import org.dromara.workflow.mapper.FlwInstanceBizExtMapper; import org.dromara.workflow.mapper.FlwTaskMapper; -import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwNodeExtService; import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.dromara.workflow.service.IFlwTaskService; +import org.dromara.workflow.utils.FlwCommonUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -83,12 +82,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService { private final FlowTaskMapper flowTaskMapper; private final FlowHisTaskMapper flowHisTaskMapper; private final IdentifierGenerator identifierGenerator; - private final UserService userService; private final FlwTaskMapper flwTaskMapper; private final FlwCategoryMapper flwCategoryMapper; private final FlowNodeMapper flowNodeMapper; private final IFlwTaskAssigneeService flwTaskAssigneeService; - private final IFlwCommonService flwCommonService; private final IFlwNodeExtService flwNodeExtService; private final FlwInstanceBizExtMapper flwInstanceBizExtMapper; @@ -479,7 +476,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { Instance inst = insService.getById(task.getInstanceId()); BusinessStatusEnum.checkBackStatus(inst.getFlowStatus()); Long definitionId = task.getDefinitionId(); - String applyNodeCode = flwCommonService.applyNodeCode(definitionId); + String applyNodeCode = FlwCommonUtils.applyNodeCode(definitionId); Map variable = new HashMap<>(); // 消息类型 @@ -600,7 +597,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { //设置按钮权限 flowTaskVo.setButtonList(flwNodeExtService.buildButtonPermissionsFromExt(flowNode.getExt())); flowTaskVo.setNodeRatio(flowNode.getNodeRatio()); - flowTaskVo.setApplyNode(flowNode.getNodeCode().equals(flwCommonService.applyNodeCode(task.getDefinitionId()))); + flowTaskVo.setApplyNode(flowNode.getNodeCode().equals(FlwCommonUtils.applyNodeCode(task.getDefinitionId()))); return flowTaskVo; } @@ -788,21 +785,6 @@ public class FlwTaskServiceImpl implements IFlwTaskService { return true; } - /** - * 获取当前任务的所有办理人 - * - * @param taskIds 任务id - */ - @Override - public List currentTaskAllUser(List taskIds) { - // 获取与当前任务关联的用户列表 - List userList = FlowEngine.userService().getByAssociateds(taskIds); - if (CollUtil.isEmpty(userList)) { - return Collections.emptyList(); - } - return userService.selectListByIds(StreamUtils.toList(userList, e -> Convert.toLong(e.getProcessedBy()))); - } - /** * 按照节点编码查询节点 * @@ -827,13 +809,13 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (CollUtil.isEmpty(bo.getTaskIdList())) { return false; } - List userList = this.currentTaskAllUser(bo.getTaskIdList()); + List userList = FlwCommonUtils.currentTaskAllUser(bo.getTaskIdList()); if (CollUtil.isEmpty(userList)) { return false; } List messageType = bo.getMessageType(); String message = bo.getMessage(); - flwCommonService.sendMessage(messageType, message, "单据审批提醒", userList); + FlwCommonUtils.sendMessage(messageType, message, "单据审批提醒", userList); } catch (Exception e) { log.error(e.getMessage(), e); throw new ServiceException(e.getMessage()); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/FlwCommonUtils.java similarity index 38% rename from ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java rename to ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/FlwCommonUtils.java index efc25a3f8a2e6a5eba0eea59f7ffac75abfc43e3..907f8624b11e9abe60fdeb9716e0ad31487c9d96 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/FlwCommonUtils.java @@ -1,73 +1,82 @@ -package org.dromara.workflow.service.impl; +package org.dromara.workflow.utils; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; import org.dromara.common.core.domain.dto.UserDTO; +import org.dromara.common.core.service.UserService; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mail.utils.MailUtils; import org.dromara.common.sse.dto.SseMessageDto; import org.dromara.common.sse.utils.SseMessageUtils; +import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.entity.Node; -import org.dromara.warm.flow.core.enums.SkipType; -import org.dromara.warm.flow.core.service.NodeService; +import org.dromara.warm.flow.core.entity.User; import org.dromara.warm.flow.orm.entity.FlowTask; -import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.enums.MessageTypeEnum; -import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwTaskService; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.stream.Collectors; +import java.util.*; /** - * 工作流工具 + * 工作流工具类 * - * @author LionLi + * @author AprilWind */ -@ConditionalOnEnable -@Slf4j -@RequiredArgsConstructor -@Service -public class FlwCommonServiceImpl implements IFlwCommonService { - private final NodeService nodeService; +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class FlwCommonUtils { + + private static final IFlwTaskService FLW_TASK_SERVICE = SpringUtils.getBean(IFlwTaskService.class); + private static final UserService USER_SERVICE = SpringUtils.getBean(UserService.class); + + private static final String DEFAULT_SUBJECT = "单据审批提醒"; /** - * 发送消息 + * 根据流程实例发送消息给当前处理人 * * @param flowName 流程定义名称 - * @param messageType 消息类型 - * @param message 消息内容,为空则发送默认配置的消息内容 + * @param instId 流程实例ID + * @param messageType 消息类型列表 + * @param message 消息内容,为空则使用默认消息 */ - @Override - public void sendMessage(String flowName, Long instId, List messageType, String message) { - IFlwTaskService flwTaskService = SpringUtils.getBean(IFlwTaskService.class); - List list = flwTaskService.selectByInstId(instId); + public static void sendMessage(String flowName, Long instId, List messageType, String message) { + if (CollUtil.isEmpty(messageType)) { + return; + } + List taskList = FLW_TASK_SERVICE.selectByInstId(instId); + if (CollUtil.isEmpty(taskList)) { + return; + } + if (StringUtils.isBlank(message)) { - message = "有新的【" + flowName + "】单据已经提交至您,请您及时处理。"; + message = "有新的【" + flowName + "】单据已经提交至您,请及时处理。"; } - List userList = flwTaskService.currentTaskAllUser(StreamUtils.toList(list, FlowTask::getId)); + List userList = currentTaskAllUser(StreamUtils.toList(taskList, FlowTask::getId)); if (CollUtil.isEmpty(userList)) { return; } - sendMessage(messageType, message, "单据审批提醒", userList); + sendMessage(messageType, message, DEFAULT_SUBJECT, userList); } /** - * 发送消息 + * 发送消息给指定用户列表 * - * @param messageType 消息类型 + * @param messageType 消息类型列表 * @param message 消息内容 * @param subject 邮件标题 - * @param userList 接收用户 + * @param userList 接收用户列表 */ - @Override - public void sendMessage(List messageType, String message, String subject, List userList) { + public static void sendMessage(List messageType, String message, String subject, List userList) { + if (CollUtil.isEmpty(messageType) || CollUtil.isEmpty(userList)) { + return; + } + List userIds = new ArrayList<>(StreamUtils.toSet(userList, UserDTO::getUserId)); + Set emails = StreamUtils.toSet(userList, UserDTO::getEmail); + for (String code : messageType) { MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); if (ObjectUtil.isEmpty(messageTypeEnum)) { @@ -76,13 +85,11 @@ public class FlwCommonServiceImpl implements IFlwCommonService { switch (messageTypeEnum) { case SYSTEM_MESSAGE -> { SseMessageDto dto = new SseMessageDto(); - dto.setUserIds(StreamUtils.toList(userList, UserDTO::getUserId).stream().distinct().collect(Collectors.toList())); + dto.setUserIds(userIds); dto.setMessage(message); SseMessageUtils.publishMessage(dto); } - case EMAIL_MESSAGE -> { - MailUtils.sendText(StreamUtils.join(userList, UserDTO::getEmail), subject, message); - } + case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message); case SMS_MESSAGE -> { //todo 短信发送 } @@ -91,17 +98,30 @@ public class FlwCommonServiceImpl implements IFlwCommonService { } } - /** - * 申请人节点编码 + * 获取申请人节点编码 * * @param definitionId 流程定义id * @return 申请人节点编码 */ - @Override - public String applyNodeCode(Long definitionId) { - Node startNode = nodeService.getStartNode(definitionId); - Node nextNode = nodeService.getNextNode(definitionId, startNode.getNodeCode(), null, SkipType.PASS.getKey()); - return nextNode.getNodeCode(); + public static String applyNodeCode(Long definitionId) { + List firstBetweenNode = FlowEngine.nodeService().getFirstBetweenNode(definitionId, new HashMap<>()); + return firstBetweenNode.get(0).getNodeCode(); } + + /** + * 获取当前任务的所有办理人 + * + * @param taskIds 任务id + */ + public static List currentTaskAllUser(List taskIds) { + // 获取与当前任务关联的用户列表 + List userList = FlowEngine.userService().getByAssociateds(taskIds); + if (CollUtil.isEmpty(userList)) { + return Collections.emptyList(); + } + return USER_SERVICE.selectListByIds(StreamUtils.toList(userList, e -> Convert.toLong(e.getProcessedBy()))); + } + } +