refactor(technology): 优化 BOM 成本计算逻辑

-调整了成本计算的顺序,先计算外购件成本,再计算其他成本
- 优化了缓存逻辑,只在非根节点或非外购件时才进行缓存
- 移除了不必要的注释代码
-重构了部分代码结构,提高了可读性和维护性
This commit is contained in:
曹鹏飞 2025-04-03 17:51:41 +08:00
parent 6beddbd2ac
commit 0fef01e0c8
10 changed files with 90 additions and 43 deletions

View File

@ -1,5 +1,6 @@
package com.nflg.product.technology.api;
import cn.hutool.core.collection.CollectionUtil;
import com.nflg.product.base.core.api.BaseApi;
import com.nflg.product.technology.pojo.query.*;
import com.nflg.product.technology.pojo.vo.*;
@ -104,14 +105,18 @@ public class ProductCostAnalysisApi extends BaseApi {
BomCostMultilayerVO vo = productCostAnalysisService.getBomCostMultilayer(query.getMaterialNo(), query.getMonth());
List<BomCostMultilayerVO> vos = new ArrayList<>();
vos.add(vo);
vo.getChildren().forEach(c -> add(c, vos));
if (CollectionUtil.isNotEmpty(vo.getChildren())) {
vo.getChildren().forEach(c -> add(c, vos));
}
ExcelUtil.export(response, vos, BomCostMultilayerVO.class, query.getMaterialNo() + "组件成本(多层)", query.getMaterialNo());
}
private void add(BomCostMultilayerVO child, List<BomCostMultilayerVO> vos) {
vos.add(child);
child.getChildren().forEach(c -> add(c, vos));
if (CollectionUtil.isNotEmpty(child.getChildren())) {
child.getChildren().forEach(c -> add(c, vos));
}
}
@PostMapping("getMaterialComposition")

View File

@ -5,6 +5,7 @@ import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@ -89,7 +90,7 @@ public class EBomCostCacheDTO implements Serializable {
/**
* 制作成本
*/
private List<ProductionCostDTO> productionCosts;
private List<ProductionCostDTO> productionCosts = new ArrayList<>();
/**
* 是否外购件

View File

@ -1,14 +1,18 @@
package com.nflg.product.technology.pojo.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@Accessors(chain = true)
public class WorkingHourDTO implements Serializable {
private String materialWork;
private BigDecimal workHours = BigDecimal.ZERO;
private String factory;
}

View File

@ -12,7 +12,6 @@ import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
@ -37,5 +36,5 @@ public class BomCostMultilayerVO extends BomCostSingleLayerVO implements Seriali
@ApiModelProperty("子物料列表")
@ExcelIgnore
private List<BomCostMultilayerVO> children = new ArrayList<>();
private List<BomCostMultilayerVO> children;
}

View File

@ -1,5 +1,6 @@
package com.nflg.product.technology.pojo.vo;
import cn.hutool.core.util.IdUtil;
import cn.idev.excel.annotation.ExcelIgnore;
import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.format.NumberFormat;
@ -30,6 +31,10 @@ import java.math.BigDecimal;
@ContentFontStyle(fontHeightInPoints = 10)
public class BomCostSingleLayerVO implements Serializable {
@ApiModelProperty("前端使用的主键")
@ExcelIgnore
private String key = IdUtil.fastUUID();
@ApiModelProperty("物料编码")
@ExcelProperty("物料编码")
@ColumnWidth(15)

View File

@ -109,16 +109,6 @@ public class BomCostCalculateService {
cdto.setMaterialNo(dto.getMaterialNo());
cdto.setMaterialPrice(dto.getMaterialPrice());
cdto.setCategoryCode(dto.getMaterialCategoryCode());
cdto.setPieceRateSalary(calculatePieceRateSalary(dto, config, workingHours));
cdto.setWelfareExpenses(calculateWelfareExpenses(dto, config, workingHours));
cdto.setHydropowerCost(calculateHydropowerCost(dto, config, workingHours));
cdto.setDepreciationCost(calculateDepreciationCost(dto, config, workingHours));
cdto.setWorkshopManagementLaborCost(calculateWorkshopManagementLaborCost(dto, config, workingHours));
cdto.setWorkshopOfficeExpenses(calculateWorkshopOfficeExpenses(dto, config, workingHours));
cdto.setAuxiliaryDepartmentLaborCost(calculateAuxiliaryDepartmentLaborCost(dto, config, workingHours));
cdto.setAuxiliaryDepartmentExpenses(calculateAuxiliaryDepartmentExpenses(dto, config, workingHours));
cdto.setProductionCosts(calculateProductionCosts(dto, config, workingHours));
// cdto.setPaintCost(calculatePaintCost(rootMaterialNo, dto, month));
if (purchaseTypeIsF) {
cdto.setHasChildren(false);
cdto.setPurchasedParts(true);
@ -126,6 +116,15 @@ public class BomCostCalculateService {
// 计算损耗成本
cdto.setPurchasedPartsWasteCost(calculateWasteCost(dto, month));
} else {
cdto.setPieceRateSalary(calculatePieceRateSalary(dto, config, workingHours));
cdto.setWelfareExpenses(calculateWelfareExpenses(dto, config, workingHours));
cdto.setHydropowerCost(calculateHydropowerCost(dto, config, workingHours));
cdto.setDepreciationCost(calculateDepreciationCost(dto, config, workingHours));
cdto.setWorkshopManagementLaborCost(calculateWorkshopManagementLaborCost(dto, config, workingHours));
cdto.setWorkshopOfficeExpenses(calculateWorkshopOfficeExpenses(dto, config, workingHours));
cdto.setAuxiliaryDepartmentLaborCost(calculateAuxiliaryDepartmentLaborCost(dto, config, workingHours));
cdto.setAuxiliaryDepartmentExpenses(calculateAuxiliaryDepartmentExpenses(dto, config, workingHours));
cdto.setProductionCosts(calculateProductionCosts(dto, config, workingHours));
List<EBomDTO> children = datas.stream()
.filter(d -> StrUtil.equals(d.getParentMaterialNo(), dto.getMaterialNo()))
.collect(Collectors.toList());
@ -168,14 +167,6 @@ public class BomCostCalculateService {
cdto.setWorkshopOfficeExpenses(cdto.getWorkshopOfficeExpenses().add(c.getWorkshopOfficeExpenses().multiply(dto1.getNum())));
cdto.setAuxiliaryDepartmentLaborCost(cdto.getAuxiliaryDepartmentLaborCost().add(c.getAuxiliaryDepartmentLaborCost().multiply(dto1.getNum())));
cdto.setAuxiliaryDepartmentExpenses(cdto.getAuxiliaryDepartmentExpenses().add(c.getAuxiliaryDepartmentExpenses().multiply(dto1.getNum())));
// cdto.setPaintCost(cdto.getPaintCost().add(c.getPaintCost().multiply(dto1.getNum())));
// if (c.getPaintCost().compareTo(BigDecimal.ZERO) > 0) {
// if (cdto.getPaintWeight().compareTo(BigDecimal.ZERO) == 0) {
// cdto.setPaintWeight(cdto.getPaintWeight().add(dto1.getNum()));
// } else {
// cdto.setPaintWeight(cdto.getPaintWeight().add(c.getPaintWeight().multiply(dto1.getNum())));
// }
// }
if (StrUtil.equalsIgnoreCase(dto1.getMaterialUnit(), "kg")) {
cdto.setPaintWeight(cdto.getPaintWeight().add(dto1.getNum()));
cdto.setPaintCost(cdto.getPaintCost().add(calculatePaintCost(rootMaterialNo, dto1, month)));
@ -203,9 +194,11 @@ public class BomCostCalculateService {
}
log.debug(StrUtil.format("BOM成本计算 {} 实时计算", dto.getMaterialNo()));
result.add(cdto);
cdata = JsonUtil.toJson(cdto);
//log.debug(cdata);
redisTemplate.opsForValue().set(buildKey(dto), cdata, CACHE_DURATION);
if (!(isRoot && purchaseTypeIsE50)) {
cdata = JsonUtil.toJson(cdto);
//log.debug(cdata);
redisTemplate.opsForValue().set(buildKey(dto), cdata, CACHE_DURATION);
}
return cdto;
} else {
log.debug(StrUtil.format("BOM成本计算 {} 从缓存读取", dto.getMaterialNo()));

View File

@ -7,7 +7,7 @@ import com.nflg.product.technology.pojo.entity.EBomParentEntity;
import com.nflg.product.technology.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.BoundZSetOperations;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SessionCallback;
@ -15,7 +15,10 @@ import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.Duration;
import java.util.*;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Component
@ -74,7 +77,8 @@ public class EBomService {
public List<EBomChildEntity> getChildren(long parentRowId) {
String key = buildChildCacheKey(parentRowId);
Set<String> kvs = redisTemplate.opsForZSet().range(key, 0, -1);
// Set<String> kvs = redisTemplate.opsForZSet().range(key, 0, -1);
List<String> kvs = redisTemplate.opsForList().range(key, 0, -1);
List<EBomChildEntity> entities = null;
if (CollectionUtil.isEmpty(kvs)) {
entities = ebomChildService.lambdaQuery()
@ -82,16 +86,28 @@ public class EBomService {
.orderByAsc(EBomChildEntity::getOrderNumber)
.list();
if (CollectionUtil.isNotEmpty(entities)) {
Map<String, Double> scoreMembers = new HashMap<>();
for (EBomChildEntity entity : entities) {
scoreMembers.put(JsonUtil.toJson(entity), Double.valueOf(entity.getOrderNumber()));
}
// Map<String, Double> scoreMembers = new HashMap<>();
// for (EBomChildEntity entity : entities) {
// scoreMembers.put(JsonUtil.toJson(entity), Double.valueOf(entity.getOrderNumber()));
// }
// redisTemplate.executePipelined(new SessionCallback<Object>() {
// @Override
// public Object execute(RedisOperations operations) throws DataAccessException {
// BoundZSetOperations<String, String> zSetOps = operations.boundZSetOps(key);
// scoreMembers.forEach(zSetOps::add);
// zSetOps.expire(CACHE_DURATION_CHILD);
// return null;
// }
// });
List<EBomChildEntity> finalEntities = entities;
redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
BoundZSetOperations<String, String> zSetOps = operations.boundZSetOps(key);
scoreMembers.forEach(zSetOps::add);
zSetOps.expire(CACHE_DURATION_CHILD);
BoundListOperations<String, String> listOperations = redisTemplate.boundListOps(key);
finalEntities.forEach((v) -> {
listOperations.rightPush(JsonUtil.toJson(v));
});
listOperations.expire(CACHE_DURATION_CHILD);
return null;
}
});

View File

@ -1,14 +1,14 @@
package com.nflg.product.technology.service;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.product.technology.mapper.master.ProcessRouteTaskProcessesMapper;
import com.nflg.product.technology.pojo.dto.WorkingHourDTO;
import com.nflg.product.technology.pojo.entity.ProcessRouteTaskProcessesEntity;
import org.springframework.stereotype.Service;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* <p>
@ -22,6 +22,14 @@ import java.util.Optional;
public class ProcessRouteTaskProcessesService extends ServiceImpl<ProcessRouteTaskProcessesMapper, ProcessRouteTaskProcessesEntity> {
public List<WorkingHourDTO> getWorkingHour(String materialNo) {
return Optional.ofNullable(this.baseMapper.getWorkingHour(materialNo)).orElse(Collections.emptyList());
List<WorkingHourDTO> all = this.baseMapper.getWorkingHour(materialNo);
List<WorkingHourDTO> temps1010 = all.stream().filter(t -> StrUtil.equals("1010", t.getFactory())).collect(Collectors.toList());
all.removeAll(temps1010);
all.forEach(temp -> {
if (temps1010.stream().noneMatch(data -> data.getMaterialWork().equals(temp.getMaterialWork()))) {
temps1010.add(temp);
}
});
return temps1010;
}
}

View File

@ -67,7 +67,11 @@ public class ProductCostAnalysisService {
vo.setHasChildren(true);
buildMultilayerChildren(parent.getRowId(), vo, materials, datas, false);
}
return Convert.toList(BomCostSingleLayerVO.class, vo.getChildren());
if (CollectionUtil.isEmpty(vo.getChildren())) {
return Collections.emptyList();
} else {
return Convert.toList(BomCostSingleLayerVO.class, vo.getChildren());
}
}
public BomCostMultilayerVO getBomCostMultilayer(String materialNo, String month) {
@ -151,6 +155,9 @@ public class ProductCostAnalysisService {
cvo.setTotalCost(cvo.getPrice().add(ccost.getSteelsWasteCost()).add(ccost.getPurchasedPartsWasteCost()).multiply(cvo.getNum()));
}
}
if (CollectionUtil.isEmpty(pvo.getChildren())) {
pvo.setChildren(new ArrayList<>());
}
pvo.getChildren().add(cvo);
EBomParentEntity cparent = ebomService.getParent(child.getMaterialNo());
if (Objects.nonNull(cparent) && !materialMainAttrService.purchaseTypeIsF(cparent.getMaterialNo())) {

View File

@ -11,14 +11,23 @@
<select id="getWorkingHour" resultType="com.nflg.product.technology.pojo.dto.WorkingHourDTO">
SELECT CONCAT_WS('-', rt.material_no, t.`name`) AS 'material_work',
SUM(IFNULL(r.two_work_hours, 0)) work_hours
SUM(IFNULL(r.two_work_hours, 0)) work_hours,
w.factory
FROM t_technology_working_type t
INNER JOIN t_process_workcenter w ON w.working_type = t.`code`
INNER JOIN t_process_route_task_processes r ON r.work_center = w.work_center
INNER JOIN t_process_route_task rt ON r.task_row_id = rt.row_id
WHERE w.factory = '1010'
AND rt.material_no = #{materialNo}
AND r.two_work_hours > 0
GROUP BY rt.material_no, t.`name`
GROUP BY rt.material_no, t.`name`, w.factory
</select>
<!-- <select id="getWorkingHour" resultType="com.nflg.product.technology.pojo.dto.WorkingHourTempDTO">-->
<!-- SELECT rt.material_no, w.factory, t.`name`, IFNULL(r.two_work_hours, 0) AS `workHours`-->
<!-- FROM t_technology_working_type t-->
<!-- INNER JOIN t_process_workcenter w ON w.working_type = t.`code`-->
<!-- INNER JOIN t_process_route_task_processes r ON r.work_center = w.work_center-->
<!-- INNER JOIN t_process_route_task rt ON r.task_row_id = rt.row_id-->
<!-- WHERE rt.material_no = #{materialNo}-->
<!-- </select>-->
</mapper>