package cn.iocoder.yudao.module.hrm.service.supply;
|
|
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.StrUtil;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyAlertRespVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyIssuePageReqVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyIssueRespVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyIssueSaveReqVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyPageReqVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyPurchasePageReqVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyPurchaseRespVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyPurchaseSaveReqVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplyRespVO;
|
import cn.iocoder.yudao.module.hrm.controller.admin.supply.vo.HrmSupplySaveReqVO;
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.supply.HrmSupplyDO;
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.supply.HrmSupplyIssueDO;
|
import cn.iocoder.yudao.module.hrm.dal.dataobject.supply.HrmSupplyPurchaseDO;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.supply.HrmSupplyIssueMapper;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.supply.HrmSupplyMapper;
|
import cn.iocoder.yudao.module.hrm.dal.mysql.supply.HrmSupplyPurchaseMapper;
|
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.user.AdminUserApi;
|
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
import jakarta.annotation.Resource;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.validation.annotation.Validated;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDateTime;
|
import java.util.ArrayList;
|
import java.util.Collection;
|
import java.util.Collections;
|
import java.util.HashMap;
|
import java.util.HashSet;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.Set;
|
import java.util.function.Function;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*;
|
|
/**
|
* 办公用品管理 Service 实现类
|
*
|
* @author 超级管理员
|
*/
|
@Service
|
@Validated
|
public class HrmSupplyServiceImpl implements HrmSupplyService {
|
|
/** 领用状态:待审批 */
|
private static final Integer ISSUE_STATUS_PENDING = 1;
|
/** 领用状态:已通过 */
|
private static final Integer ISSUE_STATUS_APPROVED = 2;
|
/** 领用状态:已驳回 */
|
private static final Integer ISSUE_STATUS_REJECTED = 3;
|
/** 领用状态:已发放 */
|
private static final Integer ISSUE_STATUS_ISSUED = 4;
|
|
/** 预警类型:低于库存下限 */
|
private static final Integer WARNING_BELOW_MIN = 1;
|
/** 预警类型:高于库存上限 */
|
private static final Integer WARNING_ABOVE_MAX = 2;
|
|
@Resource
|
private HrmSupplyMapper supplyMapper;
|
@Resource
|
private HrmSupplyPurchaseMapper supplyPurchaseMapper;
|
@Resource
|
private HrmSupplyIssueMapper supplyIssueMapper;
|
@Resource
|
private AdminUserApi adminUserApi;
|
@Resource
|
private DeptApi deptApi;
|
|
@Override
|
public Long createSupply(HrmSupplySaveReqVO createReqVO) {
|
validateCodeUnique(null, createReqVO.getCode());
|
HrmSupplyDO supply = BeanUtils.toBean(createReqVO, HrmSupplyDO.class);
|
if (supply.getStockQuantity() == null) {
|
supply.setStockQuantity(0);
|
}
|
supplyMapper.insert(supply);
|
return supply.getId();
|
}
|
|
@Override
|
public void updateSupply(HrmSupplySaveReqVO updateReqVO) {
|
validateSupplyExists(updateReqVO.getId());
|
validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
|
HrmSupplyDO updateObj = BeanUtils.toBean(updateReqVO, HrmSupplyDO.class);
|
// 当前库存只能通过「采购入库」「领用发放」变更,档案编辑不覆盖库存
|
updateObj.setStockQuantity(null);
|
supplyMapper.updateById(updateObj);
|
}
|
|
@Override
|
public void deleteSupply(Long id) {
|
HrmSupplyDO supply = validateSupplyExists(id);
|
Long purchaseCount = supplyPurchaseMapper.selectCountBySupplyId(id);
|
if (purchaseCount != null && purchaseCount > 0) {
|
throw exception(SUPPLY_DELETE_FAIL_HAS_PURCHASE, supply.getName(), purchaseCount);
|
}
|
Long issueCount = supplyIssueMapper.selectCountBySupplyId(id);
|
if (issueCount != null && issueCount > 0) {
|
throw exception(SUPPLY_DELETE_FAIL_HAS_ISSUE, supply.getName(), issueCount);
|
}
|
supplyMapper.deleteById(id);
|
}
|
|
@Override
|
public HrmSupplyDO getSupply(Long id) {
|
return supplyMapper.selectById(id);
|
}
|
|
@Override
|
public PageResult<HrmSupplyRespVO> getSupplyPage(HrmSupplyPageReqVO pageReqVO) {
|
PageResult<HrmSupplyDO> pageResult = supplyMapper.selectPage(pageReqVO);
|
return BeanUtils.toBean(pageResult, HrmSupplyRespVO.class);
|
}
|
|
@Override
|
public List<HrmSupplyDO> getSupplyList() {
|
return supplyMapper.selectEnabledList();
|
}
|
|
@Override
|
public List<HrmSupplyAlertRespVO> getSupplyAlertList() {
|
List<HrmSupplyDO> list = supplyMapper.selectEnabledList();
|
if (CollUtil.isEmpty(list)) {
|
return Collections.emptyList();
|
}
|
List<HrmSupplyAlertRespVO> alertList = new ArrayList<>();
|
for (HrmSupplyDO supply : list) {
|
Integer stock = supply.getStockQuantity() != null ? supply.getStockQuantity() : 0;
|
Integer warningType = null;
|
if (supply.getMinQuantity() != null && stock <= supply.getMinQuantity()) {
|
warningType = WARNING_BELOW_MIN;
|
} else if (supply.getMaxQuantity() != null && stock >= supply.getMaxQuantity()) {
|
warningType = WARNING_ABOVE_MAX;
|
}
|
if (warningType == null) {
|
continue;
|
}
|
HrmSupplyAlertRespVO alert = BeanUtils.toBean(supply, HrmSupplyAlertRespVO.class);
|
alert.setWarningType(warningType);
|
alertList.add(alert);
|
}
|
// 先低于下限、再高于上限,便于优先处理缺货
|
alertList.sort((a, b) -> {
|
int byType = Integer.compare(a.getWarningType(), b.getWarningType());
|
if (byType != 0) {
|
return byType;
|
}
|
return StrUtil.compare(a.getName(), b.getName(), true);
|
});
|
return alertList;
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public Long createSupplyPurchase(HrmSupplyPurchaseSaveReqVO createReqVO) {
|
HrmSupplyDO supply = validateSupplyExists(createReqVO.getSupplyId());
|
Integer quantity = createReqVO.getQuantity();
|
if (quantity == null || quantity <= 0) {
|
throw exception(SUPPLY_PURCHASE_QUANTITY_INVALID, quantity);
|
}
|
if (createReqVO.getPrice() != null && createReqVO.getPrice().signum() < 0) {
|
throw exception(SUPPLY_PURCHASE_PRICE_INVALID, createReqVO.getPrice());
|
}
|
// 1. 写入采购记录:金额 = 数量 × 单价
|
HrmSupplyPurchaseDO purchase = BeanUtils.toBean(createReqVO, HrmSupplyPurchaseDO.class);
|
if (purchase.getPurchaseTime() == null) {
|
purchase.setPurchaseTime(LocalDateTime.now());
|
}
|
if (createReqVO.getPrice() != null) {
|
purchase.setTotalPrice(createReqVO.getPrice().multiply(BigDecimal.valueOf(quantity)));
|
}
|
supplyPurchaseMapper.insert(purchase);
|
// 2. 累加当前库存
|
int newStock = (supply.getStockQuantity() != null ? supply.getStockQuantity() : 0) + quantity;
|
supplyMapper.update(null, new LambdaUpdateWrapper<HrmSupplyDO>()
|
.eq(HrmSupplyDO::getId, supply.getId())
|
.set(HrmSupplyDO::getStockQuantity, newStock)
|
.set(HrmSupplyDO::getUpdateTime, LocalDateTime.now()));
|
return purchase.getId();
|
}
|
|
@Override
|
public PageResult<HrmSupplyPurchaseRespVO> getSupplyPurchasePage(HrmSupplyPurchasePageReqVO pageReqVO) {
|
PageResult<HrmSupplyPurchaseDO> pageResult = supplyPurchaseMapper.selectPage(pageReqVO);
|
if (CollUtil.isEmpty(pageResult.getList())) {
|
return PageResult.empty(pageResult.getTotal());
|
}
|
List<HrmSupplyPurchaseRespVO> list = BeanUtils.toBean(pageResult.getList(), HrmSupplyPurchaseRespVO.class);
|
Map<Long, HrmSupplyDO> supplyMap = getSupplyMap(collectIds(list, HrmSupplyPurchaseRespVO::getSupplyId));
|
list.forEach(vo -> {
|
HrmSupplyDO supply = supplyMap.get(vo.getSupplyId());
|
if (supply != null) {
|
vo.setSupplyCode(supply.getCode());
|
vo.setSupplyName(supply.getName());
|
vo.setUnit(supply.getUnit());
|
}
|
});
|
return new PageResult<>(list, pageResult.getTotal());
|
}
|
|
@Override
|
public Long createSupplyIssue(HrmSupplyIssueSaveReqVO createReqVO) {
|
validateSupplyExists(createReqVO.getSupplyId());
|
Integer quantity = createReqVO.getQuantity();
|
if (quantity == null || quantity <= 0) {
|
throw exception(SUPPLY_ISSUE_QUANTITY_INVALID, quantity);
|
}
|
HrmSupplyIssueDO issue = BeanUtils.toBean(createReqVO, HrmSupplyIssueDO.class);
|
issue.setStatus(ISSUE_STATUS_PENDING);
|
issue.setApproveUserId(null);
|
issue.setApproveTime(null);
|
issue.setApproveRemark(null);
|
issue.setIssueTime(null);
|
supplyIssueMapper.insert(issue);
|
return issue.getId();
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void approveSupplyIssue(Long id, Boolean approve, String remark) {
|
HrmSupplyIssueDO issue = validateSupplyIssueExists(id);
|
HrmSupplyDO supply = supplyMapper.selectById(issue.getSupplyId());
|
if (!ISSUE_STATUS_PENDING.equals(issue.getStatus())) {
|
throw exception(SUPPLY_ISSUE_APPROVE_FAIL,
|
supply != null ? supply.getName() : "ID=" + issue.getSupplyId(),
|
issueStatusLabel(issue.getStatus()));
|
}
|
HrmSupplyIssueDO updateObj = new HrmSupplyIssueDO();
|
updateObj.setId(id);
|
updateObj.setStatus(Boolean.TRUE.equals(approve) ? ISSUE_STATUS_APPROVED : ISSUE_STATUS_REJECTED);
|
updateObj.setApproveUserId(SecurityFrameworkUtils.getLoginUserId());
|
updateObj.setApproveTime(LocalDateTime.now());
|
updateObj.setApproveRemark(remark);
|
supplyIssueMapper.updateById(updateObj);
|
}
|
|
@Override
|
@Transactional(rollbackFor = Exception.class)
|
public void issueSupply(Long id) {
|
HrmSupplyIssueDO issue = validateSupplyIssueExists(id);
|
HrmSupplyDO supply = validateSupplyExists(issue.getSupplyId());
|
if (!ISSUE_STATUS_APPROVED.equals(issue.getStatus())) {
|
throw exception(SUPPLY_ISSUE_ISSUE_FAIL,
|
supply.getName(), issueStatusLabel(issue.getStatus()));
|
}
|
// 1. 校验库存
|
int stock = supply.getStockQuantity() != null ? supply.getStockQuantity() : 0;
|
int quantity = issue.getQuantity() != null ? issue.getQuantity() : 0;
|
String unit = StrUtil.blankToDefault(supply.getUnit(), "件");
|
if (stock < quantity) {
|
throw exception(SUPPLY_ISSUE_STOCK_NOT_ENOUGH,
|
supply.getName(), quantity, unit, stock, unit);
|
}
|
// 2. 扣减库存
|
supplyMapper.update(null, new LambdaUpdateWrapper<HrmSupplyDO>()
|
.eq(HrmSupplyDO::getId, supply.getId())
|
.set(HrmSupplyDO::getStockQuantity, stock - quantity)
|
.set(HrmSupplyDO::getUpdateTime, LocalDateTime.now()));
|
// 3. 更新领用申请为已发放
|
HrmSupplyIssueDO updateObj = new HrmSupplyIssueDO();
|
updateObj.setId(id);
|
updateObj.setStatus(ISSUE_STATUS_ISSUED);
|
updateObj.setIssueTime(LocalDateTime.now());
|
supplyIssueMapper.updateById(updateObj);
|
}
|
|
@Override
|
public PageResult<HrmSupplyIssueRespVO> getSupplyIssuePage(HrmSupplyIssuePageReqVO pageReqVO) {
|
PageResult<HrmSupplyIssueDO> pageResult = supplyIssueMapper.selectPage(pageReqVO);
|
if (CollUtil.isEmpty(pageResult.getList())) {
|
return PageResult.empty(pageResult.getTotal());
|
}
|
List<HrmSupplyIssueRespVO> list = BeanUtils.toBean(pageResult.getList(), HrmSupplyIssueRespVO.class);
|
Set<Long> userIds = new HashSet<>();
|
userIds.addAll(collectIds(list, HrmSupplyIssueRespVO::getUserId));
|
userIds.addAll(collectIds(list, HrmSupplyIssueRespVO::getApproveUserId));
|
Map<Long, AdminUserRespDTO> userMap = getUserMap(userIds);
|
Map<Long, DeptRespDTO> deptMap = getDeptMap(collectIds(list, HrmSupplyIssueRespVO::getDeptId));
|
Map<Long, HrmSupplyDO> supplyMap = getSupplyMap(collectIds(list, HrmSupplyIssueRespVO::getSupplyId));
|
list.forEach(vo -> {
|
HrmSupplyDO supply = supplyMap.get(vo.getSupplyId());
|
if (supply != null) {
|
vo.setSupplyCode(supply.getCode());
|
vo.setSupplyName(supply.getName());
|
vo.setUnit(supply.getUnit());
|
}
|
AdminUserRespDTO user = userMap.get(vo.getUserId());
|
if (user != null) {
|
vo.setUserName(user.getNickname());
|
}
|
AdminUserRespDTO approveUser = userMap.get(vo.getApproveUserId());
|
if (approveUser != null) {
|
vo.setApproveUserName(approveUser.getNickname());
|
}
|
DeptRespDTO dept = deptMap.get(vo.getDeptId());
|
if (dept != null) {
|
vo.setDeptName(dept.getName());
|
}
|
});
|
return new PageResult<>(list, pageResult.getTotal());
|
}
|
|
// ==================== 校验与辅助方法 ====================
|
|
private HrmSupplyDO validateSupplyExists(Long id) {
|
HrmSupplyDO supply = id != null ? supplyMapper.selectById(id) : null;
|
if (supply == null) {
|
throw exception(SUPPLY_NOT_EXISTS, id);
|
}
|
return supply;
|
}
|
|
private HrmSupplyIssueDO validateSupplyIssueExists(Long id) {
|
HrmSupplyIssueDO issue = id != null ? supplyIssueMapper.selectById(id) : null;
|
if (issue == null) {
|
throw exception(SUPPLY_ISSUE_NOT_EXISTS, id);
|
}
|
return issue;
|
}
|
|
private void validateCodeUnique(Long id, String code) {
|
HrmSupplyDO supply = supplyMapper.selectByCode(code);
|
if (supply != null && !Objects.equals(supply.getId(), id)) {
|
throw exception(SUPPLY_CODE_DUPLICATED, code);
|
}
|
}
|
|
private Map<Long, HrmSupplyDO> getSupplyMap(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return Collections.emptyMap();
|
}
|
List<HrmSupplyDO> list = supplyMapper.selectBatchIds(ids);
|
Map<Long, HrmSupplyDO> map = new HashMap<>(list.size());
|
list.forEach(item -> map.put(item.getId(), item));
|
return map;
|
}
|
|
private Map<Long, AdminUserRespDTO> getUserMap(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return Collections.emptyMap();
|
}
|
return adminUserApi.getUserMap(ids);
|
}
|
|
private Map<Long, DeptRespDTO> getDeptMap(Collection<Long> ids) {
|
if (CollUtil.isEmpty(ids)) {
|
return Collections.emptyMap();
|
}
|
return deptApi.getDeptMap(ids);
|
}
|
|
private static <T> Set<Long> collectIds(Collection<T> list, Function<T, Long> getter) {
|
Set<Long> ids = new HashSet<>();
|
list.forEach(item -> {
|
Long id = getter.apply(item);
|
if (id != null) {
|
ids.add(id);
|
}
|
});
|
return ids;
|
}
|
|
private static String issueStatusLabel(Integer status) {
|
if (ISSUE_STATUS_PENDING.equals(status)) {
|
return "待审批";
|
}
|
if (ISSUE_STATUS_APPROVED.equals(status)) {
|
return "已通过";
|
}
|
if (ISSUE_STATUS_REJECTED.equals(status)) {
|
return "已驳回";
|
}
|
if (ISSUE_STATUS_ISSUED.equals(status)) {
|
return "已发放";
|
}
|
return "未知状态(ID=" + status + ")";
|
}
|
|
}
|