package com.ruoyi.system.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.entity.TDept; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.SecurityUtils; import com.ruoyi.system.constants.AssetDeptConstant; import com.ruoyi.system.dto.asset.AssetInventoryTaskDTO; import com.ruoyi.system.dto.asset.AssetInventoryTaskItemDTO; import com.ruoyi.system.dto.asset.AssetInventoryTaskItemUpdateDTO; import com.ruoyi.system.dto.asset.AssetInventoryUserUpdateDTO; import com.ruoyi.system.emums.AssetInventoryTaskStatusEnum; import com.ruoyi.system.mapper.AssetInventoryTaskMapper; import com.ruoyi.system.model.AssetInventoryRecord; import com.ruoyi.system.model.AssetInventoryTask; import com.ruoyi.system.model.AssetInventoryTaskItem; import com.ruoyi.system.query.AssertInventoryQuery; import com.ruoyi.system.query.InventoryTaskQuery; import com.ruoyi.system.service.AssetInventoryRecordService; import com.ruoyi.system.service.AssetInventoryTaskItemService; import com.ruoyi.system.service.AssetInventoryTaskService; import com.ruoyi.system.service.ISysUserService; import com.ruoyi.system.service.TDeptService; import com.ruoyi.system.vo.asset.AssetInventoryTaskDetailVO; import com.ruoyi.system.vo.asset.AssetInventoryTaskVO; import com.ruoyi.system.vo.asset.AssetMainInventoryVO; import com.ruoyi.system.vo.asset.InventoryTaskStatisticsVO; import com.ruoyi.system.vo.asset.InventoryTaskTabVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; /** *

* 盘点任务表 服务实现类 *

* * @author WuGuanFengYue * @since 2025-09-15 */ @Slf4j @Service @RequiredArgsConstructor(onConstructor_ = {@Lazy}) public class AssetInventoryTaskServiceImpl extends ServiceImpl implements AssetInventoryTaskService { private final TDeptService deptService; private final AssetInventoryTaskItemService assetInventoryTaskItemService; private final ISysUserService sysUserService; private final AssetInventoryRecordService inventoryRecordService; // 静态常量,避免重复创建 private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final BigDecimal HUNDRED = BigDecimal.valueOf(100); @Override public IPage getInventoryTaskPage(AssertInventoryQuery query) { // 1. 构建Lambda查询条件 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.isNotBlank(query.getTaskName()), AssetInventoryTask::getTaskName, query.getTaskName()) .eq(AssetInventoryTask::getDisabled, false) .orderByDesc(AssetInventoryTask::getCreateTime); // 2. 构建分页对象 Page page = new Page<>(query.getPageNum(), query.getPageSize()); // 3. 执行分页查询 IPage taskPage = this.page(page, wrapper); // 4. 收集部门ID并去重 List deptIds = taskPage.getRecords().stream() .map(AssetInventoryTask::getDeptId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); // 5. 批量查询部门信息 List depts = deptIds.isEmpty() ? Collections.emptyList() : deptService.listByIds(deptIds); // 6. 构建部门ID->名称的映射Map Map deptNameMap = depts.stream() .collect(Collectors.toMap(TDept::getId, TDept::getDeptName)); // 7. 使用BeanUtil.copyToList转换VO并填充部门名称 List voList = BeanUtil.copyToList(taskPage.getRecords(), AssetInventoryTaskVO.class); voList.forEach(vo -> { if (vo.getDeptId() != null) { vo.setDeptName(deptNameMap.get(vo.getDeptId())); } }); // 8. 构建返回的分页结果 IPage resultPage = new Page<>(taskPage.getCurrent(), taskPage.getSize(), taskPage.getTotal()); resultPage.setRecords(voList); return resultPage; } @Override @Transactional(rollbackFor = Exception.class) public boolean createInventoryTask(AssetInventoryTaskDTO dto) { // 1. 提前获取当前用户信息,避免重复调用 SysUser currentUser = SecurityUtils.getLoginUser().getUser(); Integer currentUserId = Math.toIntExact(currentUser.getUserId()); // 2. DTO转Entity AssetInventoryTask task = new AssetInventoryTask(); BeanUtil.copyProperties(dto, task); // 3. 设置默认值 task.setUserId(currentUserId); task.setStatus(0); // 未开始状态 task.setCreateTime(LocalDateTime.now()); task.setCreateBy(currentUser.getUserName()); task.setDisabled(false); // 4. 保存主任务 boolean saved = this.save(task); if (!saved) { throw new RuntimeException("保存盘点任务失败"); } // 5. 批量创建资产明细 List items = buildTaskItems(task.getId(), dto.getAssetMains(), currentUserId); return assetInventoryTaskItemService.saveBatch(items); } /** * 构建盘点任务资产明细列表 * * @param taskId 盘点任务ID * @param assetMains 资产盘点项列表 * @param currentUserId 当前用户ID * @return 资产明细列表 */ private List buildTaskItems(Integer taskId, List assetMains, Integer currentUserId) { // 参数验证 if (taskId == null) { throw new IllegalArgumentException("盘点任务ID不能为空"); } if (assetMains == null || assetMains.isEmpty()) { return Collections.emptyList(); } return assetMains.stream() .filter(Objects::nonNull) // 过滤掉null元素 .filter(assetMain -> assetMain.getAssetMainId() != null) // 过滤掉assetMainId为null的元素 .map(assetMain -> { AssetInventoryTaskItem item = new AssetInventoryTaskItem(); item.setInventoryTaskId(taskId); item.setAssetMainId(assetMain.getAssetMainId()); // 如果DTO中指定了盘点人ID,使用指定的;否则使用当前用户 item.setUserId(assetMain.getUserId() != null ? assetMain.getUserId() : currentUserId); item.setResultStatus(0); // 未盘点状态 return item; }) .collect(Collectors.toList()); } @Override public void deleteById(Integer id) { this.removeById(id); assetInventoryTaskItemService.lambdaUpdate() .eq(AssetInventoryTaskItem::getInventoryTaskId, id) .remove(); } @Override public List getInventoryTaskTabData() { // 数据权限:超级管理员/资产管理部、董事长、总经理查看所有数据,其他部门查看当前及下级部门的数据 Long userId = SecurityUtils.getUserId(); boolean isAdmin = SecurityUtils.isAdmin(userId); boolean hasNoPermission = isAdmin; if (!isAdmin) { try { // 获取当前用户的部门名称 String deptName = sysUserService.selectUserById(userId).getDept().getDeptName(); // 非超级管理员且非资产管理部,设置部门权限 if (!AssetDeptConstant.ASSET_INVENTORY_DEPT_NAMES.contains(deptName)) { hasNoPermission = true; } } catch (Exception e) { // 如果获取部门信息失败 } } // 1. 查询所有有效的盘点任务,支持筛选条件 LambdaQueryWrapper taskWrapper = new LambdaQueryWrapper<>(); taskWrapper.eq(AssetInventoryTask::getDisabled, false) .ne(AssetInventoryTask::getStatus, AssetInventoryTaskStatusEnum.CANCELED.getCode()) .orderByDesc(AssetInventoryTask::getCreateTime); // 按部门筛选 if (hasNoPermission) { //没有权限,根据盘点人查询任务列表 taskWrapper.apply("EXISTS (SELECT 1 FROM asset_inventory_task_item item WHERE item.inventory_task_id = id AND item.user_id = {0})", Math.toIntExact(userId)); } try { SysUser currentUser = SecurityUtils.getLoginUser().getUser(); taskWrapper.eq(AssetInventoryTask::getUserId, Math.toIntExact(currentUser.getUserId())); } catch (Exception e) { // 如果获取用户信息失败,不应用筛选条件 log.warn("获取当前用户信息失败,不应用'我的任务'筛选条件", e); } List tasks = this.list(taskWrapper); if (tasks.isEmpty()) { return Collections.emptyList(); } // 2. 收集任务ID并查询相关的任务项 List taskIds = tasks.stream() .map(AssetInventoryTask::getId) .collect(Collectors.toList()); LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); itemWrapper.in(AssetInventoryTaskItem::getInventoryTaskId, taskIds); List items = assetInventoryTaskItemService.list(itemWrapper); // 3. 按任务ID分组统计任务项 Map> itemGroupMap = items.stream() .collect(Collectors.groupingBy(AssetInventoryTaskItem::getInventoryTaskId)); // 4. 批量查询部门信息 List deptIds = tasks.stream() .map(AssetInventoryTask::getDeptId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); List depts = deptIds.isEmpty() ? Collections.emptyList() : deptService.listByIds(deptIds); Map deptNameMap = depts.stream() .collect(Collectors.toMap(TDept::getId, TDept::getDeptName)); // 5. 构建返回结果,使用优化的统计方法 return tasks.stream().map(task -> { InventoryTaskTabVO vo = new InventoryTaskTabVO(); vo.setId(task.getId()); vo.setTaskName(task.getTaskName()); vo.setStatus(task.getStatus()); vo.setExecuteDate(task.getExecuteDate() != null ? task.getExecuteDate().format(DATE_FORMATTER) : null); vo.setDeptName(task.getDeptId() != null ? deptNameMap.get(task.getDeptId()) : null); // 使用优化的统计方法计算任务数据 List taskItems = itemGroupMap.getOrDefault(task.getId(), Collections.emptyList()); calculateTaskStatistics(taskItems,vo); return vo; }).collect(Collectors.toList()); } /** * 计算任务统计数据 * * @param taskItems 任务项列表 * @return 统计数据 */ private void calculateTaskStatistics(List taskItems,InventoryTaskTabVO vo) { if (taskItems.isEmpty()) { vo.setCompletedCount(0); vo.setPendingCount(0); vo.setProgressPercentage(BigDecimal.ZERO); } // 已完成数量:resultStatus != 0 (1-正常,2-异常都算已完成) long completedCount = taskItems.stream() .filter(item -> item.getResultStatus() != null && item.getResultStatus() != 0) .count(); long totalCount = taskItems.size(); long pendingCount = totalCount - completedCount; // 计算进度百分比,使用预定义常量 BigDecimal progress = totalCount > 0 ? HUNDRED.multiply(BigDecimal.valueOf(completedCount)) .divide(BigDecimal.valueOf(totalCount), 2, RoundingMode.HALF_UP) : BigDecimal.ZERO; vo.setCompletedCount((int) completedCount); vo.setPendingCount((int) pendingCount); vo.setProgressPercentage(progress); } @Override public AssetInventoryTaskDetailVO getDetail(Integer id) { // 1. 参数验证 if (id == null) { throw new IllegalArgumentException("盘点任务ID不能为空"); } // 2. 查询任务基本信息 AssetInventoryTask task = this.getById(id); if (task == null || task.getDisabled()) { throw new IllegalArgumentException("盘点任务不存在或已被禁用"); } // 3. 查询部门信息 String deptName = null; if (task.getDeptId() != null) { TDept dept = deptService.getById(task.getDeptId()); deptName = dept != null ? dept.getDeptName() : null; } // 4. 查询资产列表 List assetList = baseMapper.getAssetMainInventoryList(id); // 5. 查询盘点人姓名并拼接 List userNames = baseMapper.getInventoryUserNames(id); String userNamesStr = userNames != null && !userNames.isEmpty() ? String.join(",", userNames) : ""; // 6. 构建返回结果 AssetInventoryTaskDetailVO detailVO = new AssetInventoryTaskDetailVO(); detailVO.setId(task.getId()); detailVO.setDeptId(task.getDeptId()); detailVO.setDeptName(deptName); detailVO.setExecuteDate(task.getExecuteDate() ); detailVO.setUserNames(userNamesStr); detailVO.setAssetMainInventoryVOS(assetList); return detailVO; } @Override public IPage getListByTaskId(InventoryTaskQuery query) { return baseMapper.getAssetMainInventoryPageList(new Page<>(query.getPageNum(), query.getPageSize()), query); } @Override public InventoryTaskStatisticsVO getInventoryTaskStatistics(Integer taskId) { // 参数验证 if (taskId == null) { throw new ServiceException("盘点任务ID不能为空"); } // 验证任务是否存在 AssetInventoryTask task = this.getById(taskId); if (task == null || task.getDisabled()) { throw new ServiceException("盘点任务不存在或已被禁用"); } // 查询统计数据 InventoryTaskStatisticsVO statistics = baseMapper.getInventoryTaskStatistics(taskId); // 如果查询结果为null,返回空统计数据对象 return statistics != null ? statistics : InventoryTaskStatisticsVO.empty(); } @Override public void start(Integer id) { AssetInventoryTask assetInventoryTask = this.getById(id); if (Objects.isNull(assetInventoryTask)) { throw new ServiceException("盘点任务不存在"); } assetInventoryTask.setStatus(1); //进行中 assetInventoryTask.setUpdateBy(SecurityUtils.getLoginUser().getUser().getNickName()); this.updateById(assetInventoryTask); } @Override @Transactional(rollbackFor = Exception.class) public void updateInventoryUser(AssetInventoryUserUpdateDTO dto) { log.info("开始修改盘点人,参数:{}", dto); // 1. 检查盘点任务是否存在且状态允许修改 AssetInventoryTask task = this.getById(dto.getTaskId()); if (Objects.isNull(task)) { throw new ServiceException("盘点任务不存在"); } // 2. 检查盘点任务状态,只有未开始状态(0)才允许修改盘点人 if (task.getStatus() != null && task.getStatus() != 0) { String statusText = task.getStatus() == 1 ? "进行中" : "已完成"; throw new ServiceException("盘点任务状态为" + statusText + ",不允许修改盘点人"); } // 3. 查询并更新对应的资产盘点项 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(AssetInventoryTaskItem::getInventoryTaskId, dto.getTaskId()) .in(AssetInventoryTaskItem::getAssetMainId, dto.getAssetIds()); List taskItems = assetInventoryTaskItemService.list(wrapper); if (CollUtil.isEmpty(taskItems)) { throw new ServiceException("未找到对应的盘点资产项"); } // 4. 更新盘点人 String currentUserName = SecurityUtils.getLoginUser().getUser().getNickName(); taskItems.forEach(item->item.setUserId(dto.getUserId())); boolean updated = assetInventoryTaskItemService.updateBatchById(taskItems); if (!updated) { throw new ServiceException("修改盘点人失败"); } log.info("成功修改盘点人,任务ID:{},资产ID:{},新盘点人ID:{},操作人:{}", dto.getTaskId(), dto.getAssetIds(), dto.getUserId(), currentUserName); } /** * 为盘亏资产创建出库记录 * * @param taskItem 盘点任务项 */ private void createInventoryRecordForLoss(AssetInventoryTaskItem taskItem) { try { // 获取当前用户信息 SysUser currentUser = SecurityUtils.getLoginUser().getUser(); // 创建出库记录 AssetInventoryRecord record = new AssetInventoryRecord(); record.setAssetMainId(taskItem.getAssetMainId()); record.setType(1); // 1-出库 record.setRemarks("盘点盘亏出库"); record.setCreateTime(LocalDateTime.now()); record.setCreateBy(currentUser.getUserName()); record.setDisabled(false); // 保存记录 boolean saved = inventoryRecordService.save(record); if (!saved) { log.error("保存盘点盘亏出库记录失败,资产ID:{}", taskItem.getAssetMainId()); throw new ServiceException("保存盘点盘亏出库记录失败"); } log.info("成功创建盘点盘亏出库记录,资产ID:{},盘点项ID:{},操作人:{}", taskItem.getAssetMainId(), taskItem.getId(), currentUser.getUserName()); } catch (Exception e) { log.error("创建盘点盘亏出库记录异常,盘点项ID:{},资产ID:{}", taskItem.getId(), taskItem.getAssetMainId(), e); throw new ServiceException("创建盘点盘亏出库记录失败:" + e.getMessage()); } } @Override @Transactional(rollbackFor = Exception.class) public void handleResult(List dtoList) { Map taskItemMap = dtoList.stream() .collect(Collectors.toMap(AssetInventoryTaskItemDTO::getAssetInventoryTaskItemId, AssetInventoryTaskItemDTO::getDeal)); List assetInventoryTaskItems = assetInventoryTaskItemService.listByIds(taskItemMap.keySet()); for (AssetInventoryTaskItem assetInventoryTaskItem : assetInventoryTaskItems) { Integer deal = taskItemMap.get(assetInventoryTaskItem.getId()); assetInventoryTaskItem.setDeal(deal); // 如果处理方式为盘亏,创建出库记录 if (deal != null && deal == 1) { createInventoryRecordForLoss(assetInventoryTaskItem); } } assetInventoryTaskItemService.updateBatchById(assetInventoryTaskItems); // 更新任务状态为已完成(盘点结果处理完成) if (CollUtil.isNotEmpty(dtoList) && CollUtil.isNotEmpty(assetInventoryTaskItems)) { // 获取任务ID(从第一个盘点项获取) Integer taskId = assetInventoryTaskItems.get(0).getInventoryTaskId(); updateTaskStatusToCompleted(taskId); } } /** * 直接更新任务状态为已完成 * * @param taskId 盘点任务ID */ private void updateTaskStatusToCompleted(Integer taskId) { // 参数验证 if (taskId == null) { log.warn("任务ID为空,无法更新任务状态"); return; } // 检查任务是否存在 AssetInventoryTask task = this.getById(taskId); if (task == null) { log.warn("任务不存在,任务ID:{}", taskId); return; } // 检查任务是否已经是已完成状态 if (task.getStatus() != null && task.getStatus().equals(AssetInventoryTaskStatusEnum.COMPLETED.getCode())) { log.debug("任务已经是已完成状态,任务ID:{}", taskId); return; } // 更新任务状态为已完成 task.setStatus(AssetInventoryTaskStatusEnum.COMPLETED.getCode()); task.setUpdateBy(SecurityUtils.getLoginUser().getUser().getNickName()); this.updateById(task); log.info("盘点任务状态更新为已完成,任务ID:{},任务名称:{},操作人:{}", taskId, task.getTaskName(), task.getUpdateBy()); } @Override public boolean cancel(Integer id) { AssetInventoryTask assetInventoryTask = this.getById(id); if (Objects.nonNull(assetInventoryTask) && !assetInventoryTask.getStatus() .equals(AssetInventoryTaskStatusEnum.COMPLETED.getCode())) { assetInventoryTask.setStatus(AssetInventoryTaskStatusEnum.CANCELED.getCode()); //已取消 assetInventoryTask.setUpdateBy(SecurityUtils.getLoginUser().getUser().getNickName()); return this.updateById(assetInventoryTask); } return false; } @Override @Transactional(rollbackFor = Exception.class) public void saveInventory(List dtoList) { Map dtoMap = dtoList.stream() .collect(Collectors.toMap(AssetInventoryTaskItemUpdateDTO::getAssetInventoryTaskItemId, dto -> dto)); List assetInventoryTaskItems = assetInventoryTaskItemService.listByIds(dtoMap.keySet()); for (AssetInventoryTaskItem assetInventoryTaskItem : assetInventoryTaskItems) { AssetInventoryTaskItemUpdateDTO dto = dtoMap.get(assetInventoryTaskItem.getId()); assetInventoryTaskItem.setResultStatus(dto.getResultStatus()); assetInventoryTaskItem.setRealDeptName(dto.getRealDeptName()); assetInventoryTaskItem.setRealUserName(dto.getRealUserName()); assetInventoryTaskItem.setExceptionExplain(dto.getExceptionExplain()); } assetInventoryTaskItemService.updateBatchById(assetInventoryTaskItems); // 检查并更新任务状态(保存盘点数据完成) if (CollUtil.isNotEmpty(dtoList) && CollUtil.isNotEmpty(assetInventoryTaskItems)) { // 获取任务ID(从第一个盘点项获取) Integer taskId = assetInventoryTaskItems.get(0).getInventoryTaskId(); checkAndUpdateTaskCompletionStatus(taskId); } } /** * 检查并更新任务完成状态 * 如果任务的所有盘点项都为正常状态,则将任务状态更新为已完成 * * @param taskId 盘点任务ID */ private void checkAndUpdateTaskCompletionStatus(Integer taskId) { // 参数验证 if (taskId == null) { log.warn("任务ID为空,无法检查任务完成状态"); return; } // 1. 检查任务是否存在 AssetInventoryTask task = this.getById(taskId); if (task == null) { log.warn("任务不存在,任务ID:{}", taskId); return; } // 2. 查询任务的所有盘点项 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(AssetInventoryTaskItem::getInventoryTaskId, taskId); List allTaskItems = assetInventoryTaskItemService.list(wrapper); // 3. 检查是否所有项都已完成且为正常状态(resultStatus = 1) boolean allNormal = !allTaskItems.isEmpty() && allTaskItems.stream() .allMatch(item -> item.getResultStatus() != null && item.getResultStatus() == 1); // 4. 如果都正常,更新任务状态为已完成 if (allNormal) { // 检查任务是否已经是已完成状态 if (task.getStatus() != null && !task.getStatus().equals(AssetInventoryTaskStatusEnum.COMPLETED.getCode())) { task.setStatus(AssetInventoryTaskStatusEnum.COMPLETED.getCode()); task.setUpdateBy(SecurityUtils.getLoginUser().getUser().getNickName()); this.updateById(task); log.info("盘点任务已完成,任务ID:{},任务名称:{},操作人:{}", taskId, task.getTaskName(), task.getUpdateBy()); } else { log.debug("任务已经是已完成状态,任务ID:{}", taskId); } } else { log.debug("任务未完全完成,跳过状态更新,任务ID:{},盘点项总数:{}", taskId, allTaskItems.size()); } } }