package com.ruoyi.system.service.impl;
|
|
import cn.hutool.core.bean.BeanUtil;
|
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.AssetInventoryUserUpdateDTO;
|
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;
|
|
/**
|
* <p>
|
* 盘点任务表 服务实现类
|
* </p>
|
*
|
* @author WuGuanFengYue
|
* @since 2025-09-15
|
*/
|
@Slf4j
|
@Service
|
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
|
public class AssetInventoryTaskServiceImpl extends ServiceImpl<AssetInventoryTaskMapper, AssetInventoryTask> 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<AssetInventoryTaskVO> getInventoryTaskPage(AssertInventoryQuery query) {
|
// 1. 构建Lambda查询条件
|
LambdaQueryWrapper<AssetInventoryTask> wrapper = new LambdaQueryWrapper<>();
|
wrapper.like(StringUtils.isNotBlank(query.getTaskName()),
|
AssetInventoryTask::getTaskName, query.getTaskName())
|
.eq(AssetInventoryTask::getDisabled, false)
|
.orderByDesc(AssetInventoryTask::getCreateTime);
|
|
// 2. 构建分页对象
|
Page<AssetInventoryTask> page = new Page<>(query.getPageNum(), query.getPageSize());
|
|
// 3. 执行分页查询
|
IPage<AssetInventoryTask> taskPage = this.page(page, wrapper);
|
|
// 4. 收集部门ID并去重
|
List<Integer> deptIds = taskPage.getRecords().stream()
|
.map(AssetInventoryTask::getDeptId)
|
.filter(Objects::nonNull)
|
.distinct()
|
.collect(Collectors.toList());
|
|
// 5. 批量查询部门信息
|
List<TDept> depts = deptIds.isEmpty() ?
|
Collections.emptyList() :
|
deptService.listByIds(deptIds);
|
|
// 6. 构建部门ID->名称的映射Map
|
Map<Integer, String> deptNameMap = depts.stream()
|
.collect(Collectors.toMap(TDept::getId, TDept::getDeptName));
|
|
// 7. 使用BeanUtil.copyToList转换VO并填充部门名称
|
List<AssetInventoryTaskVO> voList = BeanUtil.copyToList(taskPage.getRecords(), AssetInventoryTaskVO.class);
|
voList.forEach(vo -> {
|
if (vo.getDeptId() != null) {
|
vo.setDeptName(deptNameMap.get(vo.getDeptId()));
|
}
|
});
|
|
// 8. 构建返回的分页结果
|
IPage<AssetInventoryTaskVO> 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<AssetInventoryTaskItem> items = buildTaskItems(task.getId(), dto.getAssetMains(), currentUserId);
|
return assetInventoryTaskItemService.saveBatch(items);
|
}
|
|
/**
|
* 构建盘点任务资产明细列表
|
*
|
* @param taskId 盘点任务ID
|
* @param assetMains 资产盘点项列表
|
* @param currentUserId 当前用户ID
|
* @return 资产明细列表
|
*/
|
private List<AssetInventoryTaskItem> buildTaskItems(Integer taskId,
|
List<AssetInventoryTaskDTO.AssetMainDTO> 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<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);
|
}
|
}
|