package cn.iocoder.yudao.module.bpm.service.oa.expense;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.InfoSourceEnum;
import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
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.controller.admin.oa.expense.vo.OaExpensePageReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.expense.vo.OaExpenseRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.oa.expense.vo.OaExpenseSaveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskReturnReqVO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.expense.OaExpenseDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.expense.OaExpenseObjDO;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.expense.OaExpenseMapper;
import cn.iocoder.yudao.module.bpm.dal.mysql.oa.expense.OaExpenseObjMapper;
import cn.iocoder.yudao.module.bpm.enums.DictDataConstants;
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.bpm.enums.ProcessKeyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants;
import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
import cn.iocoder.yudao.module.bpm.util.OANotifyContentUtil;
import cn.iocoder.yudao.module.employee.api.EmployeeApi;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeRespDTO;
import cn.iocoder.yudao.module.expense.api.expenseinfo.ExpenseInfoApi;
import cn.iocoder.yudao.module.expense.api.expenseinfo.dto.ExpenseInfoCreateReqDTO;
import cn.iocoder.yudao.module.expense.api.expenseinfo.dto.ExpenseInfoObjCreateReqDTO;
import cn.iocoder.yudao.module.expense.api.expenseitem.ExpenseItemApi;
import cn.iocoder.yudao.module.expense.api.expenseitem.dto.ExpenseItemRespDTO;
import cn.iocoder.yudao.module.expense.api.expensetype.ExpenseTypeApi;
import cn.iocoder.yudao.module.expense.api.expensetype.dto.ExpenseTypeRespDTO;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.infra.api.file.dto.FileDTO;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.notify.NotifyMessageSendApi;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.commons.lang3.StringUtils;
import org.flowable.engine.TaskService;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_EXPENSE_NOT_EXISTS;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_EXPENSE_OBJ_END_DATE_NULL;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_EXPENSE_OBJ_MONEY_ZERO;
import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.OA_EXPENSE_OBJ_START_DATE_NULL;

/**
 * 报销流程信息 Service 实现类
 *
 * @author dp
 */
@Service
@Validated
public class OaExpenseServiceImpl implements OaExpenseService {


    /**
     * 对应的流程定义 KEY
     */
    public static final String PROCESS_KEY = ProcessKeyEnum.OA_EXPENSE.getProcessKey();


    @Resource
    private OaExpenseMapper oaExpenseMapper;

    @Resource
    private OaExpenseObjMapper oaExpenseObjMapper;

    @Resource
    private BpmProcessInstanceApi processInstanceApi;

    @Resource
    private BpmTaskService bpmTaskService;

    @Resource
    private TaskService taskService;

    @Resource
    private FileApi fileApi;

    @Resource
    private EmployeeApi employeeApi;

    @Resource
    private DeptApi deptApi;

    @Resource
    private ExpenseTypeApi expenseTypeApi;

    @Resource
    private ExpenseItemApi expenseItemApi;

    @Resource
    private ExpenseInfoApi expenseInfoApi;

    @Resource
    private NotifyMessageSendApi notifyMessageSendApi;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long stagingOaExpense(OaExpenseSaveReqVO stagingReqVO) {
        // 登录人信息
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 根据登录人查询出对应的员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        if (loginEmployee == null) {
            throw exception(ErrorCodeConstants.OA_EMPLOYEE_NOT_EXISTS);
        }

        OaExpenseDO oaExpense = BeanUtils.toBean(stagingReqVO, OaExpenseDO.class);
        if (StringUtils.isBlank(oaExpense.getExpenseUuid())) {
            // 创建单据uuid
            String uuid = IdUtil.fastSimpleUUID();
            oaExpense.setExpenseUuid(uuid);
        }
        oaExpense.setEmployeeId(loginEmployee.getId());
        oaExpense.setEmployeeName(loginEmployee.getName());
        oaExpense.setEmployeePhone(loginEmployee.getPhone());
        oaExpense.setDeptId(loginEmployee.getDeptId());
        oaExpense.setPostId(loginEmployee.getPostId());
        oaExpense.setPosition(loginEmployee.getPosition());
        oaExpense.setUserId(loginUserId);
        oaExpense.setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_STAGING);
        oaExpense.setInfoSource("0");
        oaExpense.setApplyEmployeeId(loginEmployee.getId());
        oaExpense.setApplyEmployeeName(loginEmployee.getName());
        oaExpense.setCreator(String.valueOf(loginEmployee.getId()));
        // 暂存不保存审批人信息
        oaExpense.setStartUserSelectAssignees(null);

        // 保存或更新表单信息
        if (oaExpense.getId() == null) {
            oaExpenseMapper.insert(oaExpense);
        } else {
            validateOaExpenseExists(oaExpense.getId());
            oaExpenseMapper.updateById(oaExpense);
        }
        // 保存子表信息
        List<OaExpenseObjDO> oaExpenseObjs = stagingReqVO.getOaExpenseObjs();
        if (CollectionUtil.isNotEmpty(oaExpenseObjs)) {
            updateOaExpenseObjList(oaExpense.getId(), oaExpenseObjs);
        }

        // 保存业务uuid到附件中
        fileApi.updateFileBiz(stagingReqVO.getFileIdList(), oaExpense.getExpenseUuid());

        return oaExpense.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long commitOaExpense(OaExpenseSaveReqVO commitReqVO) {
        if (CollectionUtil.isEmpty(commitReqVO.getStartUserSelectAssignees())) {
            throw exception(ErrorCodeConstants.TASK_CREATE_FAIL_NO_START_SELECT_ASSIGNEE);
        }
        // 登录人信息
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 根据登录人查询出对应的员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        if (loginEmployee == null) {
            throw exception(ErrorCodeConstants.OA_EMPLOYEE_NOT_EXISTS);
        }


        OaExpenseDO oaExpense = BeanUtils.toBean(commitReqVO, OaExpenseDO.class);
        if (StringUtils.isBlank(oaExpense.getExpenseUuid())) {
            // 创建单据uuid
            String uuid = IdUtil.fastSimpleUUID();
            oaExpense.setExpenseUuid(uuid);
        }
        oaExpense.setEmployeeId(loginEmployee.getId());
        oaExpense.setEmployeeName(loginEmployee.getName());
        oaExpense.setEmployeePhone(loginEmployee.getPhone());
        oaExpense.setDeptId(loginEmployee.getDeptId());
        oaExpense.setPostId(loginEmployee.getPostId());
        oaExpense.setPosition(loginEmployee.getPosition());
        oaExpense.setUserId(loginUserId);
        oaExpense.setInfoSource("0");
        oaExpense.setApplyEmployeeId(loginEmployee.getId());
        oaExpense.setApplyEmployeeName(loginEmployee.getName());
        oaExpense.setCreator(String.valueOf(loginEmployee.getId()));

        // 子表信息
        List<OaExpenseObjDO> oaExpenseObjs = commitReqVO.getOaExpenseObjs();
        for (OaExpenseObjDO oaExpenseObj : oaExpenseObjs) {
            if (oaExpenseObj.getExpenseItemId() == null) {
                throw exception(ErrorCodeConstants.OA_EXPENSE_OBJ_ITEM_BLANK);
            }
            if (oaExpenseObj.getMoney() == null || oaExpenseObj.getMoney().compareTo(BigDecimal.ZERO) < 1) {
                throw exception(OA_EXPENSE_OBJ_MONEY_ZERO);
            }
            if (StrUtil.isBlank(oaExpenseObj.getStartDate())) {
                throw exception(OA_EXPENSE_OBJ_START_DATE_NULL);
            }
            if (StrUtil.isBlank(oaExpenseObj.getEndDate())) {
                throw exception(OA_EXPENSE_OBJ_END_DATE_NULL);
            }
        }

        // 保存或更新表单信息
        if (oaExpense.getId() == null) {
            oaExpenseMapper.insert(oaExpense);
        } else {
            OaExpenseDO oaExpenseDO = validateOaExpenseExists(oaExpense.getId());
            if (StrUtil.isNotBlank(oaExpenseDO.getProcInstId())) {
                throw exception(ErrorCodeConstants.PROCESS_INSTANCE_CREATE_FAIL_HAS_PROCESS);
            }
            oaExpenseMapper.updateById(oaExpense);
        }
        // 保存子表
        if (CollectionUtil.isNotEmpty(oaExpenseObjs)) {
            updateOaExpenseObjList(oaExpense.getId(), oaExpenseObjs);
        }

        // 发起流程
        Map<String, Object> processInstanceVariables = new HashMap<>();
        processInstanceVariables.put("auditPass", "true");
        // 添加审批人信息
        Map<String, List<Long>> startUserSelectAssignees = new HashMap<>();
        List<Long> selectAssignees = commitReqVO.getStartUserSelectAssignees();
        startUserSelectAssignees.put("approver", selectAssignees);
        String processInstanceId = processInstanceApi
                .createProcessInstance(loginEmployee.getId(),
                        new BpmProcessInstanceCreateReqDTO()
                                .setProcessDefinitionKey(PROCESS_KEY)
                                .setVariables(processInstanceVariables)
                                .setBusinessKey(String.valueOf(oaExpense.getId()))
                                .setStartUserSelectAssignees(startUserSelectAssignees));

        //获取下一个审批人
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();

        // 审批同意
        BpmTaskApproveReqVO agreeReqVO = new BpmTaskApproveReqVO();
        agreeReqVO.setId(task.getId());
        agreeReqVO.setReason("[首次提交]");
        agreeReqVO.setTaskStatus(Integer.valueOf(DictDataConstants.OA_AUDIT_STATUS_COMMITTED));
        taskService.setVariable(task.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, DictDataConstants.OA_AUDIT_STATUS_COMMITTED);
        taskService.setVariable(task.getId(), "auditPass", "true");
        bpmTaskService.approveTask(loginEmployee.getId(), agreeReqVO);

        Task nextTask = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        if (nextTask == null) {
            throw exception(ErrorCodeConstants.TASK_CREATE_FAIL_NO_START_SELECT_ASSIGNEE);
        }
        Long currentAuditEmployeeId = Long.valueOf(nextTask.getAssignee());
        EmployeeRespDTO currentAuditEmployee = employeeApi.getEmployeeById(currentAuditEmployeeId);


        // 将工作流的流程实例ID、单据状态、最后一次审批时间、当前审批人更新到单据信息中
        oaExpenseMapper.updateById(new OaExpenseDO()
                .setId(oaExpense.getId())
                .setProcInstId(processInstanceId)
                .setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_COMMITTED)
                .setFinalAuditDate(LocalDateTime.now())
                .setCurrentAuditEmployeeId(currentAuditEmployeeId)
                .setCurrentAuditEmployeeName(currentAuditEmployee.getName())
                .setStartUserSelectAssignees(selectAssignees.stream().map(String::valueOf).collect(Collectors.joining(","))));
        // 保存业务uuid到附件中
        fileApi.updateFileBiz(commitReqVO.getFileIdList(), oaExpense.getExpenseUuid());

        // 给审批人发送审批站内信
        Long assigneeId = -1L;
        String assigneeName = "";
        String applyName = loginEmployee.getName();
        String processName = ProcessKeyEnum.getByProcessKey(PROCESS_KEY);

        String assignee = nextTask.getAssignee();
        if (StrUtil.isNotBlank(assignee)) {
            EmployeeRespDTO employeeAssignee = employeeApi.getEmployeeById(Long.valueOf(assignee));
            if (employeeAssignee != null) {
                assigneeId = employeeAssignee.getId();
                assigneeName = employeeAssignee.getName();
            }
        }
        StringBuilder content = OANotifyContentUtil.buildContentSendToAssignee(assigneeName, applyName, processName);
        notifyMessageSendApi.sendSingleMessageToMemberForOA(assigneeId, content.toString());

        return oaExpense.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long agreeOaExpense(BpmTaskApproveReqVO agreeReqVO) {
        // 登录人信息
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 根据登录人查询出对应的员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        if (loginEmployee == null) {
            throw exception(ErrorCodeConstants.OA_EMPLOYEE_NOT_EXISTS);
        }

        Task currentTask = bpmTaskService.getTask(agreeReqVO.getId());
        if (currentTask == null) {
            throw exception(ErrorCodeConstants.TASK_NOT_EXISTS);
        }
        // 先更新为审批中
        taskService.setVariable(currentTask.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, DictDataConstants.OA_AUDIT_STATUS_AUDITING);
        LambdaQueryWrapper<OaExpenseDO> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(OaExpenseDO::getProcInstId, currentTask.getProcessInstanceId());
        OaExpenseDO oaExpense = oaExpenseMapper.selectOne(lambdaQueryWrapper);
        // 如果是最后一个人审批同意，设置流程审批状态为已审核
        String[] auditPersons = oaExpense.getStartUserSelectAssignees().split(",");
        String lastAuditPerson = Arrays.stream(auditPersons)
                .reduce((first, second) -> second)
                .orElse(null);
        if (currentTask.getAssignee().equals(lastAuditPerson)) {
            // 更新为已审核(暂时未生效，未找到原因，先不处理)
            taskService.setVariable(agreeReqVO.getId(),BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, DictDataConstants.OA_AUDIT_STATUS_AUDITED);
        }
        // 审批同意
        String reason = "[同意]";
        if (StrUtil.isNotBlank(agreeReqVO.getReason())) {
            reason = reason + agreeReqVO.getReason();
        }
        agreeReqVO.setReason(reason);
        agreeReqVO.setTaskStatus(Integer.valueOf(DictDataConstants.OA_AUDIT_STATUS_AUDITING));
        taskService.setVariable(agreeReqVO.getId(), "auditPass", "true");
        bpmTaskService.approveTask(loginEmployee.getId(), agreeReqVO);

        Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
        OaExpenseDO oaExpenseDO = new OaExpenseDO();
        if (nextTask != null) {
            Long currentAuditEmployeeId = Long.valueOf(nextTask.getAssignee());
            EmployeeRespDTO currentAuditEmployee = employeeApi.getEmployeeById(currentAuditEmployeeId);

            // 如果审批人重复，实际未结束，再次更新为审核中
            taskService.setVariable(nextTask.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, DictDataConstants.OA_AUDIT_STATUS_AUDITING);
            oaExpenseDO.setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_AUDITING)
                    .setCurrentAuditEmployeeId(currentAuditEmployeeId)
                    .setCurrentAuditEmployeeName(currentAuditEmployee.getName())
                    .setFinalAuditDate(LocalDateTime.now())
                    .setId(oaExpense.getId());

        } else {
            oaExpenseDO.setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_AUDITED)
                    .setCurrentAuditEmployeeId(null)
                    .setCurrentAuditEmployeeName(null)
                    .setFinalAuditDate(LocalDateTime.now())
                    .setId(oaExpense.getId());


        }
        oaExpenseMapper.updateById(oaExpenseDO);
        // 复制业务单据信息到业务模块单据表
        if (Objects.isNull(nextTask)) {
            // 根据ID查询费用报销单
            OaExpenseDO expenseDO = oaExpenseMapper.selectById(oaExpense.getId());
            // 将费用报销单转换为费用信息创建请求对象
            ExpenseInfoCreateReqDTO expenseInfoCreateReqDTO = BeanUtils.toBean(expenseDO, ExpenseInfoCreateReqDTO.class);
            // 设置费用信息状态为已完成
            expenseInfoCreateReqDTO.setStatus("0");
            expenseInfoCreateReqDTO.setInfoSource(InfoSourceEnum.FLOW.getSource());

            // 查询费用报销单关联的费用明细
            List<OaExpenseObjDO> oaExpenseObjListByExpenseId = getOaExpenseObjListByExpenseId(expenseDO.getId());
            // 将费用明细转换为费用信息明细创建请求对象列表
            List<ExpenseInfoObjCreateReqDTO> expenseInfoObjCreateReqDTOs = BeanUtils.toBean(oaExpenseObjListByExpenseId, ExpenseInfoObjCreateReqDTO.class);

            // 设置费用信息明细列表
            expenseInfoCreateReqDTO.setExpenseInfoObjs(expenseInfoObjCreateReqDTOs);

            // 调用接口创建费用信息
            expenseInfoApi.createExpenseInfo(expenseInfoCreateReqDTO);
        }

        // 发送站内信
        Long assignId = -1L;
        String assigneeName = "";
        Long applyId = -1L;
        String applyName = "";
        String processName = ProcessKeyEnum.getByProcessKey(PROCESS_KEY);

        EmployeeRespDTO applyEmployee = employeeApi.getEmployeeById(oaExpense.getApplyEmployeeId());
        if (applyEmployee != null) {
            applyId = applyEmployee.getId();
            applyName = applyEmployee.getName();
        }
        if (nextTask != null) {
            EmployeeRespDTO employeeAssignee = employeeApi.getEmployeeById(Long.valueOf(nextTask.getAssignee()));
            if (employeeAssignee != null) {
                assignId = employeeAssignee.getId();
                assigneeName = employeeAssignee.getName();
            }
            // 给审批人发送审批站内信
            StringBuilder content = OANotifyContentUtil.buildContentSendToAssignee(assigneeName, applyName, processName);
            notifyMessageSendApi.sendSingleMessageToMemberForOA(assignId, content.toString());
        } else {
            // 给申请人发送审批完成站内信
            StringBuilder content = OANotifyContentUtil.buildContentSendToApplyForComplete(applyName, processName);
            notifyMessageSendApi.sendSingleMessageToMemberForOA(applyId, content.toString());
        }


        // 返回
        return 1L;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long disagreeOaExpense(BpmTaskReturnReqVO disagreeReqVO) {
        // 登录人信息
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 根据登录人查询出对应的员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        if (loginEmployee == null) {
            throw exception(ErrorCodeConstants.OA_EMPLOYEE_NOT_EXISTS);
        }

        Task currentTask = bpmTaskService.getTask(disagreeReqVO.getId());

        disagreeReqVO.setTargetTaskDefinitionKey("modifyApply");
        disagreeReqVO.setReason("[驳回]" + disagreeReqVO.getReason());
        disagreeReqVO.setTaskStatus(Integer.valueOf(DictDataConstants.OA_AUDIT_STATUS_RETURNED));
        taskService.setVariable(disagreeReqVO.getId(), "auditPass", "false");
        taskService.setVariable(disagreeReqVO.getId(),BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, DictDataConstants.OA_AUDIT_STATUS_RETURNED);
        bpmTaskService.returnTask(loginEmployee.getId(), disagreeReqVO);

        Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
        Long currentAuditEmployeeId = Long.valueOf(nextTask.getAssignee());
        EmployeeRespDTO currentAuditEmployee = employeeApi.getEmployeeById(currentAuditEmployeeId);

        // 更新单据状态，当前处理人，最后处理时间
        LambdaUpdateWrapper<OaExpenseDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(OaExpenseDO::getCurrentAuditEmployeeId, currentAuditEmployeeId)
                .set(OaExpenseDO::getCurrentAuditEmployeeName, currentAuditEmployee.getName())
                .set(OaExpenseDO::getFinalAuditDate, LocalDateTime.now())
                .set(OaExpenseDO::getAuditStatus, DictDataConstants.OA_AUDIT_STATUS_RETURNED)
                .eq(OaExpenseDO::getProcInstId, currentTask.getProcessInstanceId());
        oaExpenseMapper.update(updateWrapper);

        // 给申请人发送驳回站内信
        String assigneeName = loginEmployee.getName();
        Long applyId = -1L;
        String applyName = "";
        String processName = ProcessKeyEnum.getByProcessKey(PROCESS_KEY);
        OaExpenseDO oaExpenseDO = oaExpenseMapper.selectOne(OaExpenseDO::getProcInstId, currentTask.getProcessInstanceId());
        if (oaExpenseDO != null) {
            EmployeeRespDTO applyEmployee = employeeApi.getEmployeeById(oaExpenseDO.getApplyEmployeeId());
            if (applyEmployee != null) {
                applyId = applyEmployee.getId();
                applyName = applyEmployee.getName();
            }
        }
        StringBuilder content = OANotifyContentUtil.buildContentSendToApplyForDisagree(assigneeName, applyName, processName);
        notifyMessageSendApi.sendSingleMessageToMemberForOA(applyId, content.toString());


        return 1L;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long revocationOaExpense(BpmTaskApproveReqVO revocationReqVO) {
        // 登录人信息
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 根据登录人查询出对应的员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        if (loginEmployee == null) {
            throw exception(ErrorCodeConstants.OA_EMPLOYEE_NOT_EXISTS);
        }

        Task currentTask = bpmTaskService.getTask(revocationReqVO.getId());
        if (currentTask == null) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.TASK_NOT_EXISTS);
        }
        LambdaQueryWrapper<OaExpenseDO> lambdaQueryWrapper = new LambdaQueryWrapper<OaExpenseDO>()
                .eq(OaExpenseDO::getProcInstId, currentTask.getProcessInstanceId());
        OaExpenseDO oaExpenseDO = oaExpenseMapper.selectOne(lambdaQueryWrapper);
        if (!DictDataConstants.OA_AUDIT_STATUS_COMMITTED.equals(oaExpenseDO.getAuditStatus())
                || !Objects.equals(String.valueOf(loginEmployee.getId()), oaExpenseDO.getCreator())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.TASK_REVOCATION_NOT_ALLOWED);
        }

        BpmTaskReturnReqVO returnReqVO = BeanUtils.toBean(revocationReqVO, BpmTaskReturnReqVO.class);
        returnReqVO.setTargetTaskDefinitionKey("modifyApply");
        String reason = "[撤回]";
        if (StrUtil.isNotBlank(returnReqVO.getReason())) {
            reason = reason + returnReqVO.getReason();
        }
        returnReqVO.setReason(reason);
        returnReqVO.setTaskStatus(Integer.valueOf(DictDataConstants.OA_AUDIT_STATUS_RECALLED));
        taskService.setVariable(currentTask.getId(),BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, DictDataConstants.OA_AUDIT_STATUS_RECALLED);
        bpmTaskService.revocationTask(loginEmployee.getId(), returnReqVO);

        Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
        Long currentAuditEmployeeId = Long.valueOf(nextTask.getAssignee());
        EmployeeRespDTO currentAuditEmployee = employeeApi.getEmployeeById(currentAuditEmployeeId);

        // 更新单据状态，当前处理人，最后处理时间
        LambdaUpdateWrapper<OaExpenseDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.set(OaExpenseDO::getCurrentAuditEmployeeId, currentAuditEmployeeId)
                .set(OaExpenseDO::getCurrentAuditEmployeeName, currentAuditEmployee.getName())
                .set(OaExpenseDO::getFinalAuditDate, LocalDateTime.now())
                .set(OaExpenseDO::getAuditStatus, DictDataConstants.OA_AUDIT_STATUS_RECALLED)
                .eq(OaExpenseDO::getProcInstId, currentTask.getProcessInstanceId());
        oaExpenseMapper.update(updateWrapper);

        return 1L;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long reCommitOaExpense(OaExpenseSaveReqVO reCommitReqVO) {
        if (reCommitReqVO.getId() == null) {
            throw exception(ErrorCodeConstants.SERVICE_ID_NOT_EXISTS);
        }
        // 登录人信息
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 根据登录人查询出对应的员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        if (loginEmployee == null) {
            throw exception(ErrorCodeConstants.OA_EMPLOYEE_NOT_EXISTS);
        }

        OaExpenseDO oaExpenseNew = BeanUtils.toBean(reCommitReqVO, OaExpenseDO.class);
        OaExpenseDO oaExpenseOld = validateOaExpenseExists(oaExpenseNew.getId());
        oaExpenseNew.setExpenseUuid(oaExpenseOld.getExpenseUuid())
                .setProcInstId(oaExpenseOld.getProcInstId());

        oaExpenseNew.setEmployeeId(loginEmployee.getId());
        oaExpenseNew.setEmployeeName(loginEmployee.getName());
        oaExpenseNew.setEmployeePhone(loginEmployee.getPhone());
        oaExpenseNew.setDeptId(loginEmployee.getDeptId());
        oaExpenseNew.setPostId(loginEmployee.getPostId());
        oaExpenseNew.setPosition(loginEmployee.getPosition());
        oaExpenseNew.setUserId(loginUserId);
        oaExpenseNew.setApplyEmployeeId(loginEmployee.getId());
        oaExpenseNew.setApplyEmployeeName(loginEmployee.getName());

        // 子表信息
        List<OaExpenseObjDO> oaExpenseObjs = reCommitReqVO.getOaExpenseObjs();
        for (OaExpenseObjDO oaExpenseObj : oaExpenseObjs) {
            if (oaExpenseObj.getExpenseItemId() == null) {
                throw exception(ErrorCodeConstants.OA_EXPENSE_OBJ_ITEM_BLANK);
            }
            if (oaExpenseObj.getMoney() == null || oaExpenseObj.getMoney().compareTo(BigDecimal.ZERO) < 1) {
                throw exception(OA_EXPENSE_OBJ_MONEY_ZERO);
            }
            if (StrUtil.isBlank(oaExpenseObj.getStartDate())) {
                throw exception(OA_EXPENSE_OBJ_START_DATE_NULL);
            }
            if (StrUtil.isBlank(oaExpenseObj.getEndDate())) {
                throw exception(OA_EXPENSE_OBJ_END_DATE_NULL);
            }
        }

        Task currentTask = taskService.createTaskQuery().processInstanceId(oaExpenseNew.getProcInstId()).singleResult();
        BpmTaskApproveReqVO approveReqVO = new BpmTaskApproveReqVO();
        approveReqVO.setId(currentTask.getId())
                .setReason("[再次提交]");
        approveReqVO.setTaskStatus(Integer.valueOf(DictDataConstants.OA_AUDIT_STATUS_COMMITTED));
        if (CollectionUtil.isNotEmpty(reCommitReqVO.getStartUserSelectAssignees())) {
            // 添加审批人信息到流程参数中
            Map<String, List<Long>> startUserSelectAssignees = new HashMap<>();
            List<Long> selectAssignees = reCommitReqVO.getStartUserSelectAssignees();
            startUserSelectAssignees.put("approver", selectAssignees);
            taskService.setVariable(currentTask.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees);
            // 保存审批人信息
            oaExpenseNew.setStartUserSelectAssignees(selectAssignees.stream().map(String::valueOf).collect(Collectors.joining(",")));
        }
        taskService.setVariable(currentTask.getId(), "auditPass", "true");
        taskService.setVariable(currentTask.getId(),BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, DictDataConstants.OA_AUDIT_STATUS_COMMITTED);
        // 再次提交，和审批通过逻辑相同
        bpmTaskService.approveTask(loginEmployee.getId(), approveReqVO);

        Task nextTask = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).singleResult();
        Long currentAuditEmployeeId = Long.valueOf(nextTask.getAssignee());
        EmployeeRespDTO currentAuditEmployee = employeeApi.getEmployeeById(currentAuditEmployeeId);
        oaExpenseNew.setAuditStatus(DictDataConstants.OA_AUDIT_STATUS_COMMITTED)
                .setCurrentAuditEmployeeId(currentAuditEmployeeId)
                .setCurrentAuditEmployeeName(currentAuditEmployee.getName())
                .setFinalAuditDate(LocalDateTime.now());
        oaExpenseMapper.updateById(oaExpenseNew);

        // 保存子表信息
        if (CollectionUtil.isNotEmpty(oaExpenseObjs)) {
            updateOaExpenseObjList(oaExpenseNew.getId(), oaExpenseObjs);
        }

        // 保存业务uuid到附件中
        fileApi.updateFileBiz(reCommitReqVO.getFileIdList(), oaExpenseNew.getExpenseUuid());

        // 给审批人发送审批站内信
        Long assigneeId = -1L;
        String assigneeName = "";
        String applyName = loginEmployee.getName();
        String processName = ProcessKeyEnum.getByProcessKey(PROCESS_KEY);

        String assignee = nextTask.getAssignee();
        if (StrUtil.isNotBlank(assignee)) {
            EmployeeRespDTO assigneeEmployee = employeeApi.getEmployeeById(Long.valueOf(assignee));
            if (assigneeEmployee != null) {
                assigneeId = assigneeEmployee.getId();
                assigneeName = assigneeEmployee.getName();
            }
        }
        StringBuilder content = OANotifyContentUtil.buildContentSendToAssignee(assigneeName, applyName, processName);
        notifyMessageSendApi.sendSingleMessageToMemberForOA(assigneeId, content.toString());


        return 1L;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long closeOaExpense(Long id) {
        if (id == null) {
            throw exception(ErrorCodeConstants.SERVICE_ID_NOT_EXISTS);
        }
        // 登录人信息
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 根据登录人查询出对应的员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        if (loginEmployee == null) {
            throw exception(ErrorCodeConstants.OA_EMPLOYEE_NOT_EXISTS);
        }

        OaExpenseDO oaExpenseDO = oaExpenseMapper.selectById(id);
        if (!DictDataConstants.OA_AUDIT_STATUS_RETURNED.equals(oaExpenseDO.getAuditStatus())
                && !DictDataConstants.OA_AUDIT_STATUS_RECALLED.equals(oaExpenseDO.getAuditStatus())) {
            throw exception(ErrorCodeConstants.TASK_CLOSE_NOT_ALLOWED);
        }

        Task currentTask = taskService.createTaskQuery().processInstanceId(oaExpenseDO.getProcInstId()).singleResult();
        BpmTaskApproveReqVO approveReqVO = new BpmTaskApproveReqVO();
        approveReqVO.setId(currentTask.getId())
                .setReason("[关闭]");
        approveReqVO.setTaskStatus(Integer.valueOf(DictDataConstants.OA_AUDIT_STATUS_CLOSED));
        taskService.setVariable(currentTask.getId(), "auditPass", "false");
        taskService.setVariable(currentTask.getId(),BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, DictDataConstants.OA_AUDIT_STATUS_CLOSED);
        // 关闭，使用审批通过的方法实现
        bpmTaskService.approveTask(loginEmployee.getId(), approveReqVO);

        LambdaUpdateWrapper<OaExpenseDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
        lambdaUpdateWrapper.set(OaExpenseDO::getAuditStatus, DictDataConstants.OA_AUDIT_STATUS_CLOSED)
                .set(OaExpenseDO::getCurrentAuditEmployeeId, null)
                .set(OaExpenseDO::getCurrentAuditEmployeeName, null)
                .set(OaExpenseDO::getFinalAuditDate, LocalDateTime.now())
                .eq(OaExpenseDO::getId, id);
        oaExpenseMapper.update(lambdaUpdateWrapper);
        return 1L;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteOaExpense(Long id) {
        // 校验存在
        validateOaExpenseDelete(id);
        // 删除
        oaExpenseMapper.deleteById(id);
        // 删除子表数据
        deleteOaExpenseObjByExpenseId(id);
    }

    @Override
    public OaExpenseRespVO getOaExpense(Long id) {
        // 登录人信息
        Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
        // 根据登录人查询出对应的员工信息
        EmployeeRespDTO loginEmployee = employeeApi.getEmployeeByUserId(loginUserId);
        if (loginEmployee == null) {
            throw exception(ErrorCodeConstants.OA_EMPLOYEE_NOT_EXISTS);
        }

        OaExpenseDO oaExpenseDO = validateOaExpenseExists(id);
        OaExpenseRespVO oaExpenseRespVO = BeanUtils.toBean(oaExpenseDO, OaExpenseRespVO.class);
        if (oaExpenseDO.getDeptId() != null) {
            DeptRespDTO dept = deptApi.getDept(oaExpenseDO.getDeptId());
            if (dept != null) {
                oaExpenseRespVO.setDeptName(dept.getName());
            }
        }

        // 报销类型名称
        Long expenseTypeId = oaExpenseDO.getExpenseTypeId();
        if (expenseTypeId != null) {
            ExpenseTypeRespDTO expenseType = expenseTypeApi.getExpenseType(expenseTypeId);
            if (expenseType != null) {
                oaExpenseRespVO.setExpenseTypeName(expenseType.getName());
            }
        }

        // 子表数据
        List<OaExpenseObjDO> oaExpenseObjDOS = getOaExpenseObjListByExpenseId(oaExpenseDO.getId());
        if (CollectionUtil.isNotEmpty(oaExpenseObjDOS)) {
            Set<Long> expenseItemIdSet = oaExpenseObjDOS.stream().map(OaExpenseObjDO::getExpenseItemId).collect(Collectors.toSet());
            List<ExpenseItemRespDTO> expenseItemList = expenseItemApi.getExpenseItemList(expenseItemIdSet);
            if (CollectionUtil.isNotEmpty(expenseItemList)) {
                Map<Long, ExpenseItemRespDTO> expenseItemRespDTOMap = CollectionUtils.convertMap(expenseItemList, ExpenseItemRespDTO::getId);
                oaExpenseObjDOS.forEach(oaExpenseObjDO -> {
                    ExpenseItemRespDTO expenseItemRespDTO = expenseItemRespDTOMap.get(oaExpenseObjDO.getExpenseItemId());
                    oaExpenseObjDO.setExpenseItemName(expenseItemRespDTO.getName());
                });
            }
        }
        oaExpenseRespVO.setOaExpenseObjs(oaExpenseObjDOS);

        String procInstId = oaExpenseDO.getProcInstId();
        if (StrUtil.isNotBlank(procInstId)) {
            Task task = taskService.createTaskQuery()
                    .processInstanceId(procInstId)
                    .taskAssignee(String.valueOf(loginEmployee.getId()))
                    .singleResult();
            if (DictDataConstants.OA_AUDIT_STATUS_COMMITTED.equals(oaExpenseDO.getAuditStatus())) {
                // 如果是已提交，不限制任务处理人是当前登录人，用于直接撤回
                task = taskService.createTaskQuery()
                        .processInstanceId(procInstId)
                        .singleResult();
            }
            if (task != null) {
                oaExpenseRespVO.setTaskId(task.getId());
            }
        }

        // 附件列表
        List<FileDTO> fileList = fileApi.getFileDTOListByBiz(oaExpenseDO.getExpenseUuid());
        oaExpenseRespVO.setFileList(fileList);
        List<Long> fileIdList = fileList.stream().map(FileDTO::getId).collect(Collectors.toList());
        oaExpenseRespVO.setFileIdList(fileIdList);

        // 审批记录
        if (StrUtil.isNotBlank(oaExpenseDO.getProcInstId())) {
            List<BpmTaskRespVO> auditRecordList = bpmTaskService.getAuditRecordListByProcessInstanceId(oaExpenseDO.getProcInstId());
            oaExpenseRespVO.setAuditRecordList(auditRecordList);
        }

        // 审批人
        String startUserSelectAssignees = oaExpenseDO.getStartUserSelectAssignees();
        if (StrUtil.isNotEmpty(startUserSelectAssignees)) {
            List<Long> employeeIdList = Arrays.stream(startUserSelectAssignees.split(","))
                    .map(Long::valueOf).collect(Collectors.toList());
            List<EmployeeRespDTO> employeeRespDTOList = employeeApi.getEmployeeListByIds(employeeIdList);
            Map<Long, EmployeeRespDTO> longEmployeeRespDTOMap = CollectionUtils.convertMap(employeeRespDTOList, EmployeeRespDTO::getId);
            List<EmployeeRespDTO> auditUserList = new ArrayList<>();
            for (Long employeeId : employeeIdList) {
                EmployeeRespDTO employeeRespDTO = longEmployeeRespDTOMap.get(employeeId);
                if (employeeRespDTO != null) {
                    employeeRespDTO.setNickname(employeeRespDTO.getName());
                    auditUserList.add(employeeRespDTO);
                }
            }
            oaExpenseRespVO.setAuditUserList(auditUserList);
        }

        return oaExpenseRespVO;
    }

    @Override
    public OaExpenseRespVO getOaExpenseByProcInstId(String procInstId) {
        OaExpenseDO oaExpenseDO = oaExpenseMapper.selectOne(OaExpenseDO::getProcInstId, procInstId);
        OaExpenseRespVO oaExpenseRespVO = BeanUtils.toBean(oaExpenseDO, OaExpenseRespVO.class);
        if (oaExpenseDO.getDeptId() != null) {
            DeptRespDTO dept = deptApi.getDept(oaExpenseDO.getDeptId());
            if (dept != null) {
                oaExpenseRespVO.setDeptName(dept.getName());
            }
        }

        // 附件列表
        List<FileDTO> fileList = fileApi.getFileDTOListByBiz(oaExpenseDO.getExpenseUuid());
        oaExpenseRespVO.setFileList(fileList);
        List<Long> fileIdList = fileList.stream().map(FileDTO::getId).collect(Collectors.toList());
        oaExpenseRespVO.setFileIdList(fileIdList);

        // 审批记录
        if (StrUtil.isNotBlank(oaExpenseDO.getProcInstId())) {
            List<BpmTaskRespVO> auditRecordList = bpmTaskService.getAuditRecordListByProcessInstanceId(oaExpenseDO.getProcInstId());
            oaExpenseRespVO.setAuditRecordList(auditRecordList);
        }

        // 审批人
        String startUserSelectAssignees = oaExpenseDO.getStartUserSelectAssignees();
        if (StrUtil.isNotEmpty(startUserSelectAssignees)) {
            List<Long> employeeIdList = Arrays.stream(startUserSelectAssignees.split(","))
                    .map(Long::valueOf).collect(Collectors.toList());
            List<EmployeeRespDTO> employeeRespDTOList = employeeApi.getEmployeeListByIds(employeeIdList);
            Map<Long, EmployeeRespDTO> longEmployeeRespDTOMap = CollectionUtils.convertMap(employeeRespDTOList, EmployeeRespDTO::getId);
            List<EmployeeRespDTO> auditUserList = new ArrayList<>();
            for (Long employeeId : employeeIdList) {
                EmployeeRespDTO employeeRespDTO = longEmployeeRespDTOMap.get(employeeId);
                if (employeeRespDTO != null) {
                    employeeRespDTO.setNickname(employeeRespDTO.getName());
                    auditUserList.add(employeeRespDTO);
                }
            }
            oaExpenseRespVO.setAuditUserList(auditUserList);
        }

        return oaExpenseRespVO;
    }

    @Override
    public PageResult<OaExpenseRespVO> getOaExpensePage(OaExpensePageReqVO pageReqVO) {
        PageResult<OaExpenseDO> oaExpenseDOPageResult = oaExpenseMapper.selectPage(pageReqVO);
        PageResult<OaExpenseRespVO> oaExpenseRespVOPageResult = BeanUtils.toBean(oaExpenseDOPageResult, OaExpenseRespVO.class);

        List<OaExpenseRespVO> oaExpenseRespVOList = oaExpenseRespVOPageResult.getList();
        if (CollectionUtil.isNotEmpty(oaExpenseRespVOList)) {
            // 当前审核人名称
            List<Long> employeeIdList = oaExpenseRespVOList.stream().map(OaExpenseRespVO::getCurrentAuditEmployeeId).collect(Collectors.toList());
            List<EmployeeRespDTO> employeeRespDTOList = employeeApi.getEmployeeListByIds(employeeIdList);
            Map<Long, EmployeeRespDTO> longEmployeeRespDTOMap = CollectionUtils.convertMap(employeeRespDTOList, EmployeeRespDTO::getId);
            for (OaExpenseRespVO respVO : oaExpenseRespVOList) {
                EmployeeRespDTO employeeRespDTO = longEmployeeRespDTOMap.get(respVO.getCurrentAuditEmployeeId());
                if (employeeRespDTO != null) {
                    respVO.setCurrentAuditEmployeeName(employeeRespDTO.getName());
                }
            }

            // 部门名称
            List<Long> deptIdList = oaExpenseRespVOList.stream().map(OaExpenseRespVO::getDeptId).collect(Collectors.toList());
            List<DeptRespDTO> deptList = deptApi.getDeptList(deptIdList);

            // 报销类型名称
            List<Long> expenseTypeIdList = oaExpenseRespVOList.stream().map(OaExpenseRespVO::getExpenseTypeId).collect(Collectors.toList());
            List<ExpenseTypeRespDTO> expenseTypeList = expenseTypeApi.getExpenseTypeList(expenseTypeIdList);

            for (OaExpenseRespVO respVO : oaExpenseRespVOList) {

                for (DeptRespDTO dept : deptList) {
                    if (dept.getId() != null && dept.getId().equals(respVO.getDeptId())) {
                        respVO.setDeptName(dept.getName());
                        break;
                    }
                }

                for (ExpenseTypeRespDTO expenseType : expenseTypeList) {
                    if (expenseType.getId() != null && expenseType.getId().equals(respVO.getExpenseTypeId())) {
                        respVO.setExpenseTypeName(expenseType.getName());
                        break;
                    }
                }
            }
        }

        return oaExpenseRespVOPageResult;
    }

    @Override
    public PageResult<OaExpenseRespVO> exportOaExpenseExcel(OaExpensePageReqVO pageReqVO) {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        return getOaExpensePage(pageReqVO);
    }

    private void validateOaExpenseDelete(Long id) {
        OaExpenseDO oaExpenseDO = validateOaExpenseExists(id);
        if (!DictDataConstants.OA_AUDIT_STATUS_STAGING.equals(oaExpenseDO.getAuditStatus())) {
            throw exception(ErrorCodeConstants.DELETE_FAIL_NOT_STAGING);
        }
    }

    private OaExpenseDO validateOaExpenseExists(Long id) {
        OaExpenseDO oaExpenseDO = oaExpenseMapper.selectById(id);
        if (oaExpenseDO == null) {
            throw exception(OA_EXPENSE_NOT_EXISTS);
        }
        return oaExpenseDO;
    }

    // @Resource
    // private OaExpenseMapper oaExpenseMapper;
    // @Resource
    // private OaExpenseObjMapper oaExpenseObjMapper;
    //
    // @Override
    // @Transactional(rollbackFor = Exception.class)(rollbackFor = Exception.class)
    // public Long createOaExpense(OaExpenseSaveReqVO createReqVO) {
    //     // 插入
    //     OaExpenseDO oaExpense = BeanUtils.toBean(createReqVO, OaExpenseDO.class);
    //     oaExpenseMapper.insert(oaExpense);
    //
    //     // 插入子表
    //     createOaExpenseObjList(oaExpense.getId(), createReqVO.getOaExpenseObjs());
    //     // 返回
    //     return oaExpense.getId();
    // }
    //
    // @Override
    // @Transactional(rollbackFor = Exception.class)(rollbackFor = Exception.class)
    // public void updateOaExpense(OaExpenseSaveReqVO updateReqVO) {
    //     // 校验存在
    //     validateOaExpenseExists(updateReqVO.getId());
    //     // 更新
    //     OaExpenseDO updateObj = BeanUtils.toBean(updateReqVO, OaExpenseDO.class);
    //     oaExpenseMapper.updateById(updateObj);
    //
    //     // 更新子表
    //     updateOaExpenseObjList(updateReqVO.getId(), updateReqVO.getOaExpenseObjs());
    // }
    //
    // @Override
    // @Transactional(rollbackFor = Exception.class)(rollbackFor = Exception.class)
    // public void deleteOaExpense(Long id) {
    //     // 校验存在
    //     validateOaExpenseExists(id);
    //     // 删除
    //     oaExpenseMapper.deleteById(id);
    //
    //     // 删除子表
    //     deleteOaExpenseObjByExpenseId(id);
    // }
    //
    // private void validateOaExpenseExists(Long id) {
    //     if (oaExpenseMapper.selectById(id) == null) {
    //         throw exception(OA_EXPENSE_NOT_EXISTS);
    //     }
    // }
    //
    // @Override
    // public OaExpenseDO getOaExpense(Long id) {
    //     return oaExpenseMapper.selectById(id);
    // }
    //
    // @Override
    // public PageResult<OaExpenseDO> getOaExpensePage(OaExpensePageReqVO pageReqVO) {
    //     return oaExpenseMapper.selectPage(pageReqVO);
    // }

    // ==================== 子表（报销流程信息子） ====================

    @Override
    public List<OaExpenseObjDO> getOaExpenseObjListByExpenseId(Long expenseId) {
        return oaExpenseObjMapper.selectListByExpenseId(expenseId);
    }

    private void createOaExpenseObjList(Long expenseId, List<OaExpenseObjDO> list) {
        list.forEach(o -> o.setExpenseId(expenseId));
        oaExpenseObjMapper.insertBatch(list);
    }

    private void updateOaExpenseObjList(Long expenseId, List<OaExpenseObjDO> list) {
        deleteOaExpenseObjByExpenseId(expenseId);
		list.forEach(o -> o.setId(null).setCreator(null).setCreateTime(null).setUpdater(null).setUpdateTime(null).setDeleted(null)); // 解决更新情况下：1）id 冲突；2）updateTime 不更新
        createOaExpenseObjList(expenseId, list);
    }

    private void deleteOaExpenseObjByExpenseId(Long expenseId) {
        oaExpenseObjMapper.deleteByExpenseId(expenseId);
    }

}