| | |
| | | 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.dto.asset.AddInventoryTaskDTO; |
| | | 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.AssetInventoryUserUpdateDTO; |
| | | import com.ruoyi.system.mapper.AssetInventoryTaskMapper; |
| | | import com.ruoyi.system.mapper.TDeptMapper; |
| | | 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; |
| | |
| | | * @author WuGuanFengYue |
| | | * @since 2025-09-15 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | @RequiredArgsConstructor(onConstructor_ = {@Lazy}) |
| | | public class AssetInventoryTaskServiceImpl extends ServiceImpl<AssetInventoryTaskMapper, AssetInventoryTask> implements AssetInventoryTaskService { |
| | | |
| | | private final TDeptMapper tDeptMapper; |
| | | |
| | | 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<AssetInventoryTaskVO> getInventoryTaskPage(AssertInventoryQuery query) { |
| | |
| | | // 5. 批量查询部门信息 |
| | | List<TDept> depts = deptIds.isEmpty() ? |
| | | Collections.emptyList() : |
| | | tDeptMapper.selectBatchIds(deptIds); |
| | | deptService.listByIds(deptIds); |
| | | |
| | | // 6. 构建部门ID->名称的映射Map |
| | | Map<Integer, String> deptNameMap = depts.stream() |
| | |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean createInventoryTask(AddInventoryTaskDTO dto) { |
| | | // 1. DTO转Entity |
| | | 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); |
| | | |
| | | // 2. 设置默认值 |
| | | SysUser currentUser = SecurityUtils.getLoginUser().getUser(); |
| | | task.setUserId(Math.toIntExact(currentUser.getUserId())); |
| | | // 3. 设置默认值 |
| | | task.setUserId(currentUserId); |
| | | task.setStatus(0); // 未开始状态 |
| | | task.setCreateTime(LocalDateTime.now()); |
| | | task.setCreateBy(currentUser.getUserName()); |
| | | task.setDisabled(false); |
| | | |
| | | // 3. 保存主任务 |
| | | // 4. 保存主任务 |
| | | boolean saved = this.save(task); |
| | | if (!saved) { |
| | | throw new RuntimeException("保存盘点任务失败"); |
| | | } |
| | | |
| | | // 4. 批量创建资产明细 |
| | | List<AssetInventoryTaskItem> items = buildTaskItems(task.getId(), dto.getAssetMainIds()); |
| | | // 5. 批量创建资产明细 |
| | | List<AssetInventoryTaskItem> items = buildTaskItems(task.getId(), dto.getAssetMains(), currentUserId); |
| | | return assetInventoryTaskItemService.saveBatch(items); |
| | | } |
| | | |
| | |
| | | * 构建盘点任务资产明细列表 |
| | | * |
| | | * @param taskId 盘点任务ID |
| | | * @param assetMainIds 资产ID列表 |
| | | * @param assetMains 资产盘点项列表 |
| | | * @param currentUserId 当前用户ID |
| | | * @return 资产明细列表 |
| | | */ |
| | | private List<AssetInventoryTaskItem> buildTaskItems(Integer taskId, List<Integer> assetMainIds) { |
| | | SysUser currentUser = SecurityUtils.getLoginUser().getUser(); |
| | | Integer currentUserId = Math.toIntExact(currentUser.getUserId()); |
| | | private List<AssetInventoryTaskItem> buildTaskItems(Integer taskId, |
| | | List<AssetInventoryTaskDTO.AssetMainDTO> assetMains, |
| | | Integer currentUserId) { |
| | | // 参数验证 |
| | | if (taskId == null) { |
| | | throw new IllegalArgumentException("盘点任务ID不能为空"); |
| | | } |
| | | |
| | | return assetMainIds.stream() |
| | | .map(assetId -> { |
| | | 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(assetId); |
| | | item.setUserId(currentUserId); |
| | | 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<InventoryTaskTabVO> 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<AssetInventoryTask> taskWrapper = new LambdaQueryWrapper<>(); |
| | | taskWrapper.eq(AssetInventoryTask::getDisabled, false) |
| | | .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<AssetInventoryTask> tasks = this.list(taskWrapper); |
| | | if (tasks.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // 2. 收集任务ID并查询相关的任务项 |
| | | List<Integer> taskIds = tasks.stream() |
| | | .map(AssetInventoryTask::getId) |
| | | .collect(Collectors.toList()); |
| | | |
| | | LambdaQueryWrapper<AssetInventoryTaskItem> itemWrapper = new LambdaQueryWrapper<>(); |
| | | itemWrapper.in(AssetInventoryTaskItem::getInventoryTaskId, taskIds); |
| | | List<AssetInventoryTaskItem> items = assetInventoryTaskItemService.list(itemWrapper); |
| | | |
| | | // 3. 按任务ID分组统计任务项 |
| | | Map<Integer, List<AssetInventoryTaskItem>> itemGroupMap = items.stream() |
| | | .collect(Collectors.groupingBy(AssetInventoryTaskItem::getInventoryTaskId)); |
| | | |
| | | // 4. 批量查询部门信息 |
| | | List<Integer> deptIds = tasks.stream() |
| | | .map(AssetInventoryTask::getDeptId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | |
| | | List<TDept> depts = deptIds.isEmpty() ? |
| | | Collections.emptyList() : |
| | | deptService.listByIds(deptIds); |
| | | |
| | | Map<Integer, String> 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<AssetInventoryTaskItem> taskItems = itemGroupMap.getOrDefault(task.getId(), Collections.emptyList()); |
| | | calculateTaskStatistics(taskItems,vo); |
| | | return vo; |
| | | }).collect(Collectors.toList()); |
| | | } |
| | | |
| | | /** |
| | | * 计算任务统计数据 |
| | | * |
| | | * @param taskItems 任务项列表 |
| | | * @return 统计数据 |
| | | */ |
| | | private void calculateTaskStatistics(List<AssetInventoryTaskItem> 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<AssetMainInventoryVO> assetList = baseMapper.getAssetMainInventoryList(id); |
| | | |
| | | // 5. 查询盘点人姓名并拼接 |
| | | List<String> 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<AssetMainInventoryVO> 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<AssetInventoryTaskItem> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(AssetInventoryTaskItem::getInventoryTaskId, dto.getTaskId()) |
| | | .eq(AssetInventoryTaskItem::getAssetMainId, dto.getAssetId()); |
| | | |
| | | AssetInventoryTaskItem taskItem = assetInventoryTaskItemService.getOne(wrapper); |
| | | if (taskItem == null) { |
| | | throw new ServiceException("未找到对应的盘点资产项"); |
| | | } |
| | | |
| | | // 4. 更新盘点人 |
| | | String currentUserName = SecurityUtils.getLoginUser().getUser().getNickName(); |
| | | taskItem.setUserId(dto.getUserId()); |
| | | boolean updated = assetInventoryTaskItemService.updateById(taskItem); |
| | | |
| | | if (!updated) { |
| | | throw new ServiceException("修改盘点人失败"); |
| | | } |
| | | |
| | | log.info("成功修改盘点人,任务ID:{},资产ID:{},新盘点人ID:{},操作人:{}", |
| | | dto.getTaskId(), dto.getAssetId(), 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 |
| | | public void handleResult(List<AssetInventoryTaskItemDTO> dtoList) { |
| | | Map<Integer, Integer> taskItemMap = dtoList.stream() |
| | | .collect(Collectors.toMap(AssetInventoryTaskItemDTO::getAssetInventoryTaskItemId, AssetInventoryTaskItemDTO::getDeal)); |
| | | List<AssetInventoryTaskItem> 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); |
| | | } |
| | | } |