浏览代码

修改
1、外出审批流调整,增加删除和获取外出配置列表
新增
1、新增余额导出功能

liangwenxuan 2 周之前
父节点
当前提交
4ab0322ba3
共有 18 个文件被更改,包括 350 次插入18 次删除
  1. 2 0
      yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java
  2. 41 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java
  3. 3 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java
  4. 12 7
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/business/BpmBusinessFormServiceImpl.java
  5. 1 1
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/business/ModelConfigServiceImpl.java
  6. 53 0
      yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java
  7. 2 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java
  8. 5 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java
  9. 22 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/BalanceController.java
  10. 1 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/ElderlyInfoController.java
  11. 2 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/OutboundRefundConfigController.java
  12. 101 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/vo/excel/BalanceExportExcelVO.java
  13. 3 1
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/BalanceService.java
  14. 35 2
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/BalanceServiceImpl.java
  15. 53 5
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ElderlyAskLeaveServiceImpl.java
  16. 2 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java
  17. 10 0
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java
  18. 2 0
      yudao-server/src/main/resources/application.yaml

+ 2 - 0
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApi.java

@@ -25,6 +25,8 @@ public interface BpmProcessInstanceApi {
 
     String getProcessInstanceId(String businessKey);
 
+    Boolean deleteProcessInstanceCascade(Long userId, String processInstanceId, String reason);
+
     Boolean timedClosureProcess();
 
     Boolean processInstanceComplete();

+ 41 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/api/task/BpmProcessInstanceApiImpl.java

@@ -7,8 +7,10 @@ import cn.iocoder.yudao.module.bpm.dal.dataobject.business.BpmBusinessFormDO;
 import cn.iocoder.yudao.module.bpm.dal.mysql.business.BpmBusinessFormMapper;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
 import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
+import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceCopyService;
 import cn.iocoder.yudao.module.bpm.service.business.BpmBusinessFormService;
 import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
+import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
 import cn.iocoder.yudao.module.system.api.bpm.BpmCheckInApi;
 import cn.iocoder.yudao.module.system.api.bpm.BpmElderlyApi;
 import cn.iocoder.yudao.module.system.api.bpm.BpmElderlyChangeApi;
@@ -16,6 +18,7 @@ import cn.iocoder.yudao.module.system.api.bpm.BpmElderlyExpenseApi;
 import cn.iocoder.yudao.module.system.api.bpm.vo.ElderlyChangeRecordReqVO;
 import cn.iocoder.yudao.module.system.api.bpm.vo.ElderlyRetreatRecordRespVO;
 import cn.iocoder.yudao.module.system.enums.change.BusinessTypeEnum;
+import org.flowable.engine.RuntimeService;
 import org.flowable.engine.HistoryService;
 import org.flowable.engine.TaskService;
 import org.flowable.engine.history.HistoricProcessInstance;
@@ -61,6 +64,15 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi {
     @Resource
     private BpmBusinessFormMapper businessFormMapper;
 
+    @Resource
+    private RuntimeService runtimeService;
+
+    @Resource
+    private BpmProcessInstanceCopyService processInstanceCopyService;
+
+    @Resource
+    private NotifyMessageSendApi notifyMessageSendApi;
+
     @Resource
     private BpmElderlyChangeApi elderlyChangeApi;
 
@@ -102,6 +114,35 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi {
         return processInstanceId;
     }
 
+    @Override
+    public Boolean deleteProcessInstanceCascade(Long userId, String processInstanceId, String reason) {
+        if (processInstanceId == null || processInstanceId.isEmpty()) {
+            return Boolean.FALSE;
+        }
+        try {
+            processInstanceCopyService.deleteProcessInstanceCopy(processInstanceId);
+        } catch (Exception ignored) {
+        }
+        try {
+            notifyMessageSendApi.deleteByPkMessage(processInstanceId);
+        } catch (Exception ignored) {
+        }
+        try {
+            businessFormMapper.delete(new LambdaQueryWrapperX<BpmBusinessFormDO>()
+                    .eq(BpmBusinessFormDO::getProcessInstanceId, processInstanceId));
+        } catch (Exception ignored) {
+        }
+        try {
+            runtimeService.deleteProcessInstance(processInstanceId, reason == null ? "" : reason);
+        } catch (Exception ignored) {
+        }
+        try {
+            historyService.deleteHistoricProcessInstance(processInstanceId);
+        } catch (Exception ignored) {
+        }
+        return Boolean.TRUE;
+    }
+
     @Override
     public Boolean timedClosureProcess() {
 //        LocalDate nowLocalDate = LocalDate.now();

+ 3 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java

@@ -42,6 +42,9 @@ public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEvent
     @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法
     protected void processCancelled(FlowableCancelledEvent event) {
         ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId());
+        if (processInstance == null) {
+            return;
+        }
         processInstanceService.processProcessInstanceCompleted(processInstance);
     }
 

+ 12 - 7
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/business/BpmBusinessFormServiceImpl.java

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
 import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
 import cn.iocoder.yudao.module.bpm.constant.BpmConstant;
@@ -81,7 +82,13 @@ public class BpmBusinessFormServiceImpl implements BpmBusinessFormService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long create(BpmBusinessFormCreateReqVO createReqVO) {
+        if (createReqVO.getTenantId() == null) {
+            return create0(createReqVO);
+        }
+        return TenantUtils.execute(createReqVO.getTenantId(), () -> create0(createReqVO));
+    }
 
+    private Long create0(BpmBusinessFormCreateReqVO createReqVO) {
         // 1. 插入表单
 
         Integer status = modelConfigService.getModelStatus(String.format(BpmConstant.BPM_PROCESS_KEY_FORMAT,
@@ -89,7 +96,7 @@ public class BpmBusinessFormServiceImpl implements BpmBusinessFormService {
                 createReqVO.getTenantId()));
         // 插入业务表单中间表
         BpmBusinessFormDO businessFormDO = BeanUtils.toBean(createReqVO, BpmBusinessFormDO.class);
-        Long userId = getLoginUserId();
+        Long userId = createReqVO.getUserId() != null ? createReqVO.getUserId() : getLoginUserId();
         LocalDate nowLocalDate = LocalDate.now();
         if (status == 1) {
             // 修改为系统审批状态
@@ -206,12 +213,10 @@ public class BpmBusinessFormServiceImpl implements BpmBusinessFormService {
 
     @Override
     public BpmBusinessFormDO getBusinessFormByBusinessId(String businessId) {
-        BpmBusinessFormDO businessFormDO = new BpmBusinessFormDO();
-        List<BpmBusinessFormDO> bpmBusinessFormDOS = bpmBusinessFormMapper.selectList(new LambdaQueryWrapperX<BpmBusinessFormDO>().eq(BpmBusinessFormDO::getBusinessId, businessId));
-        if (null != bpmBusinessFormDOS) {
-            businessFormDO = bpmBusinessFormDOS.get(0);
-        }
-        return businessFormDO;
+        return bpmBusinessFormMapper.selectOne(new LambdaQueryWrapperX<BpmBusinessFormDO>()
+                .eq(BpmBusinessFormDO::getBusinessId, businessId)
+                .orderByDesc(BpmBusinessFormDO::getId)
+                .last("LIMIT 1"));
     }
 
     @Override

+ 1 - 1
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/business/ModelConfigServiceImpl.java

@@ -87,7 +87,7 @@ public class ModelConfigServiceImpl implements ModelConfigService {
                 .eq(ModelConfigDO::getModelKey, modelKey)
                 .eqIfPresent(ModelConfigDO::getTenantId, TenantContextHolder.getTenantId())
                 .eq(ModelConfigDO::getDeleted, 0));
-        if (null == modelConfigDO) {
+        if (modelConfigDO == null) {
             return 1;
         }
         return modelConfigDO.getStatus();

+ 53 - 0
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/message/BpmMessageServiceImpl.java

@@ -300,6 +300,18 @@ public class BpmMessageServiceImpl implements BpmMessageService {
                         NotieBusinessTypeEnum.BPM.getValue(), NotifyTypeEnum.NOTIFY_TODO.getValue(),
                         NotifyPriorityEnum.MEDIUM.getValue(), 0, assigneeUser.getTenantId());
                 break;
+            case OUTBOUND_REGISTRATION:
+                elderlyInfo = bpmElderlyApi.getElderlyInfo(businessFormDO.getBusinessId2());
+                templateParams.put("nickname", user.getNickname());
+                templateParams.put("elderName", elderlyInfo.getElderName());
+                detailUrl = getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId()) + "&businessId=" + businessFormDO.getBusinessId();
+                templateParams.put("detailUrl", detailUrl);
+                notifySendSingleToUserReqDTO = BpmMessageConvert.INSTANCE.messageConvert(reqDTO.getStartUserId(),
+                        reqDTO.getProcessInstanceId(), "", detailUrl,
+                        BpmMessageEnum.PROCESS_INSTANCE_APPROVE.getTemplateCode(), templateParams,
+                        NotieBusinessTypeEnum.BPM.getValue(), NotifyTypeEnum.NOTIFY_TODO.getValue(),
+                        NotifyPriorityEnum.MEDIUM.getValue(), 0, assigneeUser.getTenantId());
+                break;
             case BED_CHANGE:
                 elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
                 templateParams.put("nickname", user.getNickname());
@@ -388,6 +400,9 @@ public class BpmMessageServiceImpl implements BpmMessageService {
                 break;
 
         }
+        if (notifySendSingleToUserReqDTO == null) {
+            return;
+        }
         String corpUserId = assigneeUser.getCorpUserId() == null ? "" : assigneeUser.getCorpUserId().toString();
         CorpWechatTextcard corpTextCard = new CorpWechatTextcard();
         corpTextCard.setTitle(CorpMsgUtil.TITLE_APPROVE);
@@ -525,6 +540,18 @@ public class BpmMessageServiceImpl implements BpmMessageService {
                         NotieBusinessTypeEnum.BPM.getValue(), notifyType,
                         NotifyPriorityEnum.MEDIUM.getValue(), 0, assigneeUser.getTenantId());
                 break;
+            case OUTBOUND_REGISTRATION:
+                elderlyInfo = bpmElderlyApi.getElderlyInfo(businessFormDO.getBusinessId2());
+                templateParams.put("nickname", user.getNickname());
+                templateParams.put("elderName", elderlyInfo.getElderName());
+                detailUrl = getInstanceDetailUrl(reqDTO.getProcessInstanceId()) + "&businessId=" + businessFormDO.getBusinessId();
+                templateParams.put("detailUrl", detailUrl);
+                notifySendSingleToUserReqDTO = BpmMessageConvert.INSTANCE.messageConvert(reqDTO.getStartUserId(),
+                        reqDTO.getProcessInstanceId(), "", detailUrl,
+                        BpmMessageEnum.PROCESS_INSTANCE_REJECT.getTemplateCode(), templateParams,
+                        NotieBusinessTypeEnum.BPM.getValue(), notifyType,
+                        NotifyPriorityEnum.MEDIUM.getValue(), 0, assigneeUser.getTenantId());
+                break;
             case BED_CHANGE:
                 detailUrl = getInstanceDetailUrl(reqDTO.getProcessInstanceId()) + "&businessId=" + businessFormDO.getBusinessId();
                 elderlyInfo = bpmElderlyChangeApi.getElderlyInfoByChangeId(businessFormDO.getBusinessId());
@@ -611,6 +638,9 @@ public class BpmMessageServiceImpl implements BpmMessageService {
             default:
                 break;
         }
+        if (notifySendSingleToUserReqDTO == null) {
+            return;
+        }
         String corpUserId = assigneeUser.getCorpUserId() == null ? "" : assigneeUser.getCorpUserId().toString();
         CorpWechatTextcard corpTextCard = new CorpWechatTextcard();
         corpTextCard.setTitle(CorpMsgUtil.TITLE_REFUSE);
@@ -831,6 +861,26 @@ public class BpmMessageServiceImpl implements BpmMessageService {
             }
         }
 
+        if (BpmBusinessTypeEnum.OUTBOUND_REGISTRATION.getValue().equals(businessFormDO.getType())) {
+            elderlyInfo = bpmElderlyApi.getElderlyInfo(businessFormDO.getBusinessId2());
+            templateParams.put("nickname", user.getNickname());
+            templateParams.put("elderName", elderlyInfo.getElderName());
+            detailUrl = getInstanceDetailUrl(reqDTO.getProcessInstanceId()) + "&businessId=" + businessFormDO.getBusinessId();
+            templateParams.put("detailUrl", detailUrl);
+            notifySendSingleToUserReqDTO = BpmMessageConvert.INSTANCE.messageConvert(reqDTO.getAssigneeUserId(),
+                    reqDTO.getProcessInstanceId(), reqDTO.getTaskId(), detailUrl,
+                    BpmMessageEnum.TASK_ASSIGNED.getTemplateCode(), templateParams,
+                    NotieBusinessTypeEnum.BPM.getValue(), NotifyTypeEnum.NOTIFY_TODO.getValue(),
+                    NotifyPriorityEnum.MEDIUM.getValue(), 0, assigneeUser.getTenantId());
+            if (reqDTO.getStartUserId().equals(loginUserId)) {
+                notifyMessageSendApi.sendSingleMessageToAdminByDTO(BpmMessageConvert.INSTANCE.messageConvert(loginUserId,
+                        reqDTO.getProcessInstanceId(), reqDTO.getTaskId(), detailUrl,
+                        BpmMessageEnum.TASK_ASSIGNED.getTemplateCode(), templateParams,
+                        NotieBusinessTypeEnum.BPM.getValue(), NotifyTypeEnum.NOTIFY_TODO.getValue(),
+                        NotifyPriorityEnum.MEDIUM.getValue(), 1, assigneeUser.getTenantId()));
+            }
+        }
+
         if (null == approveMethod && startNodeFlag) {
             elderlyInfo = bpmElderlyApi.getElderlyInfo(businessFormDO.getBusinessId2());
             templateParams.put("nickname", user.getNickname());
@@ -846,6 +896,9 @@ public class BpmMessageServiceImpl implements BpmMessageService {
             return;
         }
 
+        if (notifySendSingleToUserReqDTO == null) {
+            return;
+        }
         String corpUserId = assigneeUser.getCorpUserId() == null ? "" : assigneeUser.getCorpUserId().toString();
         CorpWechatTextcard corpTextCard = new CorpWechatTextcard();
         corpTextCard.setTitle(CorpMsgUtil.TITLE_HANDLED);

+ 2 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApi.java

@@ -54,4 +54,6 @@ public interface NotifyMessageSendApi {
     Long sendCorpCancelMessage(CorpSendMessage corpSendMessage);
 
     Integer updateMessageByCodeAndPkMessage(String templateCode, Long elderlyId);
+
+    Integer deleteByPkMessage(String pkMessage);
 }

+ 5 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/notify/NotifyMessageSendApiImpl.java

@@ -74,4 +74,9 @@ public class NotifyMessageSendApiImpl implements NotifyMessageSendApi {
     public Integer updateMessageByCodeAndPkMessage(String templateCode, Long elderlyId) {
         return notifySendService.updateMessageByCodeAndPkMessage(templateCode, elderlyId == null ? null : String.valueOf(elderlyId));
     }
+
+    @Override
+    public Integer deleteByPkMessage(String pkMessage) {
+        return notifySendService.deleteByPkMessage(pkMessage);
+    }
 }

+ 22 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/BalanceController.java

@@ -1,20 +1,25 @@
 package cn.iocoder.yudao.module.system.controller.admin.biz;
 
+import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
 import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.module.system.controller.admin.biz.vo.BalancePageReqVO;
+import cn.iocoder.yudao.module.system.controller.admin.biz.vo.excel.BalanceExportExcelVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.biz.BalanceDO;
 import cn.iocoder.yudao.module.system.service.biz.BalanceService;
 import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
-import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
+
 import javax.annotation.Resource;
 import javax.servlet.http.HttpServletResponse;
 import javax.validation.Valid;
+import java.io.IOException;
 
 import org.springframework.validation.annotation.Validated;
 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -23,7 +28,9 @@ import java.math.BigDecimal;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
+import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
 
@@ -70,6 +77,19 @@ public class BalanceController {
         return success(BeanUtils.toBean(pageResult, BalanceDO.class));
     }
 
+    @GetMapping("/exportExcel")
+    @Operation(summary = "导出长者余额 Excel")
+    @ApiAccessLog(operateType = EXPORT)
+    @TenantIgnore
+    public void exportBalanceExcel(@Valid BalancePageReqVO pageReqVO,
+                                   HttpServletResponse response) throws IOException {
+        pageReqVO.setTenantId(Objects.isNull(pageReqVO.getTenantId()) ? TenantContextHolder.getTenantId() : pageReqVO.getTenantId());
+        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<BalanceDO> list = balanceService.getBalanceListForExport(pageReqVO);
+        ExcelUtils.write(response, "长者余额.xls", "数据", BalanceExportExcelVO.class,
+                list.stream().map(BalanceExportExcelVO::fromBalance).collect(Collectors.toList()));
+    }
+
 
     @GetMapping("/getBalanceAmountByElderId")
     @Operation(summary = "获得长者余额")
@@ -90,4 +110,4 @@ public class BalanceController {
 
 
 
-}
+}

+ 1 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/ElderlyInfoController.java

@@ -61,6 +61,7 @@ public class ElderlyInfoController {
     @GetMapping("/findPage")
     @Operation(summary = "长者信息列表分页")
     @TenantIgnore
+    @PermitAll
     //@PreAuthorize("@ss.hasPermission('elderlyInfo:query')")
     public CommonResult<PageResult<ElderlyInfoDO>> findPage(@Valid ElderlyInfoPageReqVO pageVO) {
         pageVO.setTenantIds(Objects.isNull(pageVO.getTenantIds()) ? new Long[]{TenantContextHolder.getTenantId()} : pageVO.getTenantIds());

+ 2 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/OutboundRefundConfigController.java

@@ -12,6 +12,7 @@ import io.swagger.v3.oas.annotations.Operation;
 import io.swagger.v3.oas.annotations.Parameter;
 import org.springframework.web.bind.annotation.*;
 import javax.annotation.Resource;
+import javax.annotation.security.PermitAll;
 import javax.validation.Valid;
 
 import org.springframework.validation.annotation.Validated;
@@ -76,6 +77,7 @@ public class OutboundRefundConfigController {
     @GetMapping("/findList")
     @Operation(summary = "获取外出退费配置列表")
     @TenantIgnore
+    @PermitAll
     public CommonResult<List<OutboundRefundConfigDO>> findList(@RequestParam(value = "tenantIds",required = false) Long[] tenantIds){
         tenantIds = tenantIds == null ? new Long[]{TenantContextHolder.getTenantId()} : tenantIds;
         return success(outboundRefundConfigService.findList(tenantIds));

+ 101 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/biz/vo/excel/BalanceExportExcelVO.java

@@ -0,0 +1,101 @@
+package cn.iocoder.yudao.module.system.controller.admin.biz.vo.excel;
+
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.BalanceDO;
+import cn.iocoder.yudao.module.system.dal.dataobject.biz.BalanceRecordDO;
+import com.alibaba.excel.annotation.ExcelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.Accessors;
+
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Accessors(chain = false)
+public class BalanceExportExcelVO {
+
+    private static final String DATE_TIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+
+    @ExcelProperty("长者姓名")
+    private String elderName;
+
+    @ExcelProperty("床位信息")
+    private String bedName;
+
+    @ExcelProperty("入住状态")
+    private Integer inStatus;
+
+    @ExcelProperty("余额")
+    private BigDecimal amount;
+
+    @ExcelProperty("余额明细")
+    private String balanceDetail;
+
+    public static BalanceExportExcelVO fromBalance(BalanceDO balance) {
+        return BalanceExportExcelVO.builder()
+                .elderName(balance.getElderName())
+                .bedName(balance.getBedName())
+                .inStatus(balance.getInStatus())
+                .amount(balance.getAmount() == null ? BigDecimal.ZERO : balance.getAmount())
+                .balanceDetail(formatBalanceDetail(balance.getBalanceRecord()))
+                .build();
+    }
+
+    private static String formatBalanceDetail(List<BalanceRecordDO> balanceRecords) {
+        if (balanceRecords == null || balanceRecords.isEmpty()) {
+            return "";
+        }
+        return balanceRecords.stream()
+                .map(BalanceExportExcelVO::formatBalanceRecord)
+                .collect(Collectors.joining(System.lineSeparator()));
+    }
+
+    private static String formatBalanceRecord(BalanceRecordDO record) {
+        SimpleDateFormat formatter = new SimpleDateFormat(DATE_TIME_PATTERN);
+        String createdTime = record.getCreatedTime() == null ? "" : formatter.format(record.getCreatedTime());
+        String type = formatType(record.getType());
+        String payType = formatPayType(record.getPayType());
+        String remarks = record.getRemarks() == null ? "" : record.getRemarks();
+        String createdBy = record.getCreatedBy() == null ? "" : record.getCreatedBy();
+        String changeAmount = record.getChangeAmount() == null ? BigDecimal.ZERO.toPlainString() : record.getChangeAmount().toPlainString();
+        return String.format("时间:%s; 类型:%s; 支付方式:%s; 变动金额:%s; 备注:%s; 操作人:%s",
+                createdTime, type, payType, changeAmount, remarks, createdBy);
+    }
+
+    private static String formatType(Integer type) {
+        if (type == null) {
+            return "";
+        }
+        switch (type) {
+            case 1:
+                return "充值";
+            case 2:
+                return "退款";
+            case 3:
+                return "消费";
+            default:
+                return String.valueOf(type);
+        }
+    }
+
+    private static String formatPayType(String payType) {
+        if (payType == null || payType.trim().isEmpty()) {
+            return "";
+        }
+        switch (payType.trim()) {
+            case "1":
+                return "支付宝";
+            case "2":
+                return "微信";
+            default:
+                return payType;
+        }
+    }
+}

+ 3 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/BalanceService.java

@@ -20,6 +20,8 @@ public interface BalanceService {
 
     PageResult<BalanceDO> getBalancePage(BalancePageReqVO pageReqVO);
 
+    List<BalanceDO> getBalanceListForExport(BalancePageReqVO pageReqVO);
+
     void rechargeBalance(BalanceDO createReqVO);
 
     BalanceDO getBalance(Long elderId);
@@ -31,4 +33,4 @@ public interface BalanceService {
     List<Map<String, BigDecimal>> test();
 
     BalanceDO getBalanceAmountByElderId(Long elderId);
-}
+}

+ 35 - 2
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/BalanceServiceImpl.java

@@ -60,6 +60,18 @@ public class BalanceServiceImpl implements BalanceService {
     @Override
     public PageResult<BalanceDO> getBalancePage(BalancePageReqVO pageReqVO) {
         Page<BalanceDO> page = new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize());
+        List<BalanceDO> list = getBalanceList(pageReqVO, page);
+        return new PageResult<>(list, page.getTotal());
+    }
+
+    @Override
+    public List<BalanceDO> getBalanceListForExport(BalancePageReqVO pageReqVO) {
+        List<BalanceDO> list = getBalanceList(pageReqVO, new Page<>(pageReqVO.getPageNo(), pageReqVO.getPageSize()));
+        fillBalanceRecords(list);
+        return list;
+    }
+
+    private List<BalanceDO> getBalanceList(BalancePageReqVO pageReqVO, Page<BalanceDO> page) {
         List<BalanceDO> list = balanceMapper.getBalancePage(page, pageReqVO);
         for (BalanceDO balance : list) {
             balance.setBedName(buildService.getFullBedName(balance.getElderId()));
@@ -67,7 +79,28 @@ public class BalanceServiceImpl implements BalanceService {
                 balance.setAmount(BigDecimal.ZERO);
             }
         }
-        return new PageResult<>(list, page.getTotal());
+        return list;
+    }
+
+    private void fillBalanceRecords(List<BalanceDO> list) {
+        List<Long> elderIds = list.stream()
+                .map(BalanceDO::getElderId)
+                .filter(Objects::nonNull)
+                .distinct()
+                .collect(java.util.stream.Collectors.toList());
+        if (elderIds.isEmpty()) {
+            return;
+        }
+        List<BalanceRecordDO> balanceRecords = balanceRecordMapper.selectList(new LambdaQueryWrapperX<BalanceRecordDO>()
+                .inIfPresent(BalanceRecordDO::getElderId, elderIds)
+                .orderByDesc(BalanceRecordDO::getCreatedTime)
+                .orderByDesc(BalanceRecordDO::getId));
+        Map<Long, List<BalanceRecordDO>> balanceRecordMap = balanceRecords.stream()
+                .collect(java.util.stream.Collectors.groupingBy(BalanceRecordDO::getElderId, LinkedHashMap::new,
+                        java.util.stream.Collectors.toList()));
+        for (BalanceDO balance : list) {
+            balance.setBalanceRecord(balanceRecordMap.getOrDefault(balance.getElderId(), Collections.emptyList()));
+        }
     }
 
     @Override
@@ -219,4 +252,4 @@ public class BalanceServiceImpl implements BalanceService {
     }
 
 
-}
+}

+ 53 - 5
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/biz/ElderlyAskLeaveServiceImpl.java

@@ -5,14 +5,20 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
 import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
 import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
 import cn.iocoder.yudao.module.bpm.api.business.BpmBusinessFormApi;
 import cn.iocoder.yudao.module.bpm.api.business.dto.BpmBusinessFormCreateReqDTO;
+import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
 import cn.iocoder.yudao.module.system.controller.admin.biz.vo.AskLeavePageReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.biz.vo.ElderlyAskLeaveExportExcelVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.biz.*;
+import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
 import cn.iocoder.yudao.module.system.dal.mysql.biz.*;
 import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
+import cn.iocoder.yudao.module.system.dal.dataobject.sys.BusinessParamsConfigDO;
+import cn.iocoder.yudao.module.system.dal.mysql.sys.BusinessParamsConfigMapper;
+import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
 import cn.iocoder.yudao.module.system.enums.change.BusinessConstants;
 import cn.iocoder.yudao.module.system.enums.change.BusinessTypeEnum;
 import cn.iocoder.yudao.module.system.enums.ErrorCodeConstants;
@@ -48,6 +54,7 @@ import static cn.iocoder.yudao.module.system.enums.LogRecordConstants.*;
 public class ElderlyAskLeaveServiceImpl implements ElderlyAskLeaveService {
 
     private static final DateTimeFormatter HH_MM_FORMATTER = DateTimeFormatter.ofPattern("HH:mm");
+    private static final String OUTBOUND_REGISTRATION_START_USER_ID_CONFIG_CODE = "OUTBOUND_REGISTRATION_START_USER_ID";
 
     @Resource
     private ElderlyAskLeaveMapper elderlyAskLeaveMapper;
@@ -98,6 +105,12 @@ public class ElderlyAskLeaveServiceImpl implements ElderlyAskLeaveService {
     @Resource
     private BpmBusinessFormApi bpmBusinessFormApi;
 
+    @Resource
+    private BpmProcessInstanceApi processInstanceApi;
+
+    @Autowired
+    private AdminUserMapper adminUserMapper;
+
 
     @Override
     @LogRecord(type = ELDERLY_ASK_LEAVE_TYPE, subType = ELDERLY_ASK_LEAVE_CREATE_SUB_TYPE, bizNo = "{{#createReqVO.id}}",
@@ -110,6 +123,7 @@ public class ElderlyAskLeaveServiceImpl implements ElderlyAskLeaveService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Long createAskLeaveBpm(ElderlyAskLeaveDO createReqVO) {
+        createReqVO.setOrgType(1);
         createReqVO.setCreateDate(new Date());
         if (createReqVO.getCreateBy() == null) {
             createReqVO.setCreateBy(SecurityFrameworkUtils.getLoginUserNickname());
@@ -119,14 +133,26 @@ public class ElderlyAskLeaveServiceImpl implements ElderlyAskLeaveService {
         }
         elderlyAskLeaveMapper.insert(createReqVO);
 
+        Long startUserId = resolveOutboundRegistrationStartUserId(createReqVO.getTenantId());
+        if (startUserId == null) {
+            startUserId = SecurityFrameworkUtils.getLoginUserId();
+        }
+
         BpmBusinessFormCreateReqDTO bpmReqDTO = new BpmBusinessFormCreateReqDTO();
         bpmReqDTO.setBusinessId(createReqVO.getId());
         bpmReqDTO.setBusinessId2(createReqVO.getElderId());
         bpmReqDTO.setTenantId(createReqVO.getTenantId());
         bpmReqDTO.setType(BusinessTypeEnum.OUTBOUND_REGISTRATION.getValue());
+        bpmReqDTO.setUserId(startUserId);
         return bpmBusinessFormApi.createBusinessForm(bpmReqDTO);
     }
 
+    private Long resolveOutboundRegistrationStartUserId(Long tenantId) {
+        return adminUserMapper.selectOne(new LambdaQueryWrapperX<AdminUserDO>()
+                .eq(AdminUserDO::getTenantId, tenantId)
+                .eq(AdminUserDO::getUsername, "admin")).getId();
+    }
+
     @Override
     @Transactional
     public void editAskLeave(ElderlyAskLeaveDO elderlyAskLeaveDO) {
@@ -727,13 +753,35 @@ public class ElderlyAskLeaveServiceImpl implements ElderlyAskLeaveService {
     }
 
     @Override
+    @Transactional
     public void delete(Long id) {
-        List<ElderlyAskLeaveDailyExpenseRelationDO> elderlyAskLeaveDailyExpenseRelationList = elderlyAskLeaveDailyExpenseRelationMapper.selectList(new LambdaQueryWrapperX<ElderlyAskLeaveDailyExpenseRelationDO>()
-                .eq(ElderlyAskLeaveDailyExpenseRelationDO::getAskLeaveId, id));
-        for (ElderlyAskLeaveDailyExpenseRelationDO elderlyAskLeaveDailyExpenseRelationDO : elderlyAskLeaveDailyExpenseRelationList) {
-            dailyExpensesService.deleteDailyExpenses(elderlyAskLeaveDailyExpenseRelationDO.getDailyExpenseId());
+        ElderlyAskLeaveDO askLeave = elderlyAskLeaveMapper.selectById(id);
+        if (askLeave == null) {
+            return;
         }
-        elderlyAskLeaveMapper.deleteById(id);
+        Long tenantId = askLeave.getTenantId() != null ? askLeave.getTenantId() : TenantContextHolder.getTenantId();
+        TenantUtils.execute(tenantId, () -> {
+            try {
+                String processInstanceId = processInstanceApi.getProcessInstanceId(String.valueOf(id));
+                if (processInstanceId != null && !processInstanceId.isEmpty()) {
+                    Long operatorUserId = SecurityFrameworkUtils.getLoginUserId();
+                    if (operatorUserId == null) {
+                        operatorUserId = resolveOutboundRegistrationStartUserId(tenantId);
+                    }
+                    processInstanceApi.deleteProcessInstanceCascade(operatorUserId, processInstanceId, "外出登记删除");
+                }
+            } catch (Exception e) {
+                log.warn("删除外出登记流程失败,askLeaveId={}, tenantId={}", id, tenantId, e);
+            }
+
+            List<ElderlyAskLeaveDailyExpenseRelationDO> elderlyAskLeaveDailyExpenseRelationList = elderlyAskLeaveDailyExpenseRelationMapper.selectList(
+                    new LambdaQueryWrapperX<ElderlyAskLeaveDailyExpenseRelationDO>()
+                            .eq(ElderlyAskLeaveDailyExpenseRelationDO::getAskLeaveId, id));
+            for (ElderlyAskLeaveDailyExpenseRelationDO elderlyAskLeaveDailyExpenseRelationDO : elderlyAskLeaveDailyExpenseRelationList) {
+                dailyExpensesService.deleteDailyExpenses(elderlyAskLeaveDailyExpenseRelationDO.getDailyExpenseId());
+            }
+            elderlyAskLeaveMapper.deleteById(id);
+        });
     }
 
     @Override

+ 2 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendService.java

@@ -74,4 +74,6 @@ public interface NotifySendService {
     Long sendCorpCancelMessage(CorpSendMessage corpSendMessage);
 
     Integer updateMessageByCodeAndPkMessage(String templateCode, String pkMessage);
+
+    Integer deleteByPkMessage(String pkMessage);
 }

+ 10 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifySendServiceImpl.java

@@ -307,6 +307,16 @@ public class NotifySendServiceImpl implements NotifySendService {
         return update;
     }
 
+    @Override
+    @TenantIgnore
+    public Integer deleteByPkMessage(String pkMessage) {
+        if (pkMessage == null || pkMessage.isEmpty()) {
+            return 0;
+        }
+        return notifyMessageMapper.delete(new LambdaQueryWrapperX<NotifyMessageDO>()
+                .eq(NotifyMessageDO::getPkMessage, pkMessage));
+    }
+
     @VisibleForTesting
     public NotifyTemplateDO validateNotifyTemplate(String templateCode) {
         // 获得站内信模板。考虑到效率,从缓存中获取

+ 2 - 0
yudao-server/src/main/resources/application.yaml

@@ -215,6 +215,7 @@ yudao:
       - /admin-api/elderlyInfo/getTenantHomeData
       - /admin-api/elderlyInfo/getMonitorUrl
       - /admin-api/elderlyInfo/getGroupTenant
+      - /admin-api/elderlyInfo/findPage
       - /admin-api/bpm/elderly/checkInCreate
       - /admin-api/wechat/mp/callback
       - /admin-api/wechat/mp/message/send
@@ -249,6 +250,7 @@ yudao:
       - /admin-api/elderly-safe-check/create
       - /admin-api/food-satisfaction-survey/create
       - /admin-api/elderly/askLeave/createBpm
+      - /admin-api/elderly/outboundRefundConfig/findList
 #      - /admin-api/bpm/check-in-wait/create
     ignore-tables:
       - system_tenant