fix(成本分析): 采购类型F和E50逻辑调整

This commit is contained in:
曹鹏飞 2025-03-03 19:43:25 +08:00
parent 42cfcb449a
commit d8eecd5e53
8 changed files with 237 additions and 59 deletions

View File

@ -1,22 +1,17 @@
package com.nflg.product.material.api.user.authority; package com.nflg.product.material.api.user.authority;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mysql.cj.log.Log;
import com.nflg.product.base.core.api.BaseApi; import com.nflg.product.base.core.api.BaseApi;
import com.nflg.product.material.mapper.master.AuthorityRolePowerMapper; import com.nflg.product.material.mapper.master.AuthorityRolePowerMapper;
import com.nflg.product.material.pojo.dto.AuthorityPowerDTO; import com.nflg.product.material.pojo.dto.AuthorityPowerDTO;
import com.nflg.product.material.pojo.dto.SaveMemuPermissionDTO; import com.nflg.product.material.pojo.dto.SaveMemuPermissionDTO;
import com.nflg.product.material.pojo.entity.AuthorityPowerEntity;
import com.nflg.product.material.pojo.query.AuthorityPowerQuery; import com.nflg.product.material.pojo.query.AuthorityPowerQuery;
import com.nflg.product.material.pojo.vo.AuthorityMenuVO; import com.nflg.product.material.pojo.vo.AuthorityMenuVO;
import com.nflg.product.material.pojo.vo.AuthorityPowerVO; import com.nflg.product.material.pojo.vo.AuthorityPowerVO;
import com.nflg.product.material.service.AuthorityPowerService; import com.nflg.product.material.service.AuthorityPowerService;
import com.nflg.product.material.service.AuthorityRolePowerService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import nflg.product.common.constant.STATE;
import nflg.product.common.vo.ResultVO; import nflg.product.common.vo.ResultVO;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;

View File

@ -1,7 +1,6 @@
package com.nflg.product.material.pojo.vo; package com.nflg.product.material.pojo.vo;
import lombok.Data; import lombok.Data;
import org.omg.CORBA.PRIVATE_MEMBER;
/** /**
* @decription * @decription

View File

@ -5,6 +5,7 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration @Configuration
@ -40,4 +41,21 @@ public class RedisTemplateConfig {
return template; return template;
} }
@Bean
public RedisTemplate<String, Object> redisObjectTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 使用GenericJackson2JsonRedisSerializer来序列化和反序列化redis的value值
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}
} }

View File

@ -0,0 +1,7 @@
package com.nflg.product.technology.mapper.master;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nflg.product.technology.pojo.entity.MaterialMainAttrEntity;
public interface MaterialMainAttrMapper extends BaseMapper<MaterialMainAttrEntity> {
}

View File

@ -0,0 +1,77 @@
package com.nflg.product.technology.pojo.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
@Data
@Accessors(chain = true)
@ApiModel(value = "com-nflg-product-technology-pojo-main-entity-MaterialMainAttrEntity")
@TableName(value = "t_material_main_attr")
public class MaterialMainAttrEntity implements Serializable {
/**
* 行ID 雪花
*/
@TableId(value = "row_id", type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "行ID 雪花")
private Long rowId;
/**
* 物料编码
*/
@TableField(value = "material_no")
@ApiModelProperty(value = "物料编码")
private String materialNo;
@TableField(value = "factory")
@ApiModelProperty(value = "工厂")
private String factory;
@TableField(value = "purchase_type")
@ApiModelProperty(value = "采购类型")
private String purchaseType;
@TableField(value = "special_purchase_type")
@ApiModelProperty(value = "特殊采购类")
private String specialPurchaseType;
@TableField(value = "plan_delivery_time")
@ApiModelProperty(value = "计划交货时间(天数)")
private Integer planDeliveryTime;
/**
* 创建人
*/
@TableField(value = "created_by", fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建人")
private String createdBy;
/**
* 创建时间
*/
@TableField(value = "created_time", fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private LocalDateTime createdTime;
/**
* 更新人
*/
@TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "更新人")
private String updatedBy;
/**
* 更新时间
*/
@TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "更新时间")
private LocalDateTime updatedTime;
private static final long serialVersionUID = 1L;
}

View File

@ -56,6 +56,9 @@ public class BomCostCalculateService {
@Resource @Resource
private ProcessRouteTaskProcessesService processRouteTaskProcessesService; private ProcessRouteTaskProcessesService processRouteTaskProcessesService;
@Resource
private MaterialMainAttrService materialMainAttrService;
/** /**
* 缓存时长 * 缓存时长
*/ */
@ -80,17 +83,18 @@ public class BomCostCalculateService {
config.setManday(manday); config.setManday(manday);
config.setVirtualWorkings(virtualWorkingService.listDTO()); config.setVirtualWorkings(virtualWorkingService.listDTO());
List<EBomCostCacheDTO> result = Collections.synchronizedList(new ArrayList<>(50)); List<EBomCostCacheDTO> result = Collections.synchronizedList(new ArrayList<>(50));
calculateBom(pDto, datas, config, result); calculateBom(true, pDto, datas, config, result);
return result; return result;
} }
private EBomCostCacheDTO calculateBom(EBomDTO dto, List<EBomDTO> datas, BomCostCalculateConfig config, List<EBomCostCacheDTO> result) { private EBomCostCacheDTO calculateBom(boolean isRoot, EBomDTO dto, List<EBomDTO> datas, BomCostCalculateConfig config, List<EBomCostCacheDTO> result) {
String cdata = redisTemplate.opsForValue().get(buildKey(dto)); String cdata = redisTemplate.opsForValue().get(buildKey(dto));
List<EBomDTO> children = datas.stream() boolean purchaseTypeIsF = materialMainAttrService.purchaseTypeIsF(dto.getMaterialNo());
.filter(d -> StrUtil.equals(d.getParentMaterialNo(), dto.getMaterialNo())) boolean purchaseTypeIsE50 = materialMainAttrService.purchaseTypeIsE50(dto.getMaterialNo());
.collect(Collectors.toList()); if ((isRoot && purchaseTypeIsE50) || StrUtil.isBlank(cdata)) {
if (StrUtil.isBlank(cdata)) { List<WorkingHourDTO> workingHours = isRoot
final List<WorkingHourDTO> workingHours = processRouteTaskProcessesService.getWorkingHour(dto.getMaterialNo()); ? processRouteTaskProcessesService.getWorkingHour(dto.getParentMaterialNo())
: (purchaseTypeIsE50 ? new ArrayList<>() : processRouteTaskProcessesService.getWorkingHour(dto.getMaterialNo()));
log.debug(StrUtil.format("BOM成本计算 {} 物料工时: {}", dto.getMaterialNo(), JsonUtil.toJson(workingHours))); log.debug(StrUtil.format("BOM成本计算 {} 物料工时: {}", dto.getMaterialNo(), JsonUtil.toJson(workingHours)));
EBomCostCacheDTO cdto = new EBomCostCacheDTO(); EBomCostCacheDTO cdto = new EBomCostCacheDTO();
cdto.setMaterialNo(dto.getMaterialNo()); cdto.setMaterialNo(dto.getMaterialNo());
@ -105,54 +109,63 @@ public class BomCostCalculateService {
cdto.setAuxiliaryDepartmentExpenses(calculateAuxiliaryDepartmentExpenses(dto, config, workingHours)); cdto.setAuxiliaryDepartmentExpenses(calculateAuxiliaryDepartmentExpenses(dto, config, workingHours));
cdto.setPaintCost(calculatePaintCost(dto)); cdto.setPaintCost(calculatePaintCost(dto));
cdto.setProductionCosts(calculateProductionCosts(dto, config, workingHours)); cdto.setProductionCosts(calculateProductionCosts(dto, config, workingHours));
if (CollectionUtil.isEmpty(children)) { if (purchaseTypeIsF) {
cdto.setHasChildren(false); cdto.setHasChildren(false);
// 计算自身 cdto.setPurchasedParts(true);
cdto.setPurchasedParts(!BomConstant.STEELS_CATEGORY_CODE.contains(dto.getMaterialCategoryCode())); cdto.setPurchasedPartsCost(calculatePurchasedPartsCost(dto));
if (cdto.isPurchasedParts()) {
// 外购件只计算采购成本
cdto.setPurchasedPartsCost(calculatePurchasedPartsCost(dto));
} else {
// 非外购件计算材料成本和制作成本
// 计算材料成本
cdto.setSteelsCost(calculateSteelsCost(dto));
// 计算损耗成本
cdto.setWasteCost(calculateWasteCost(dto));
}
} else { } else {
cdto.setHasChildren(true); List<EBomDTO> children = datas.stream()
cdto.setPurchasedParts(false); .filter(d -> StrUtil.equals(d.getParentMaterialNo(), dto.getMaterialNo()))
//计算子级成本
List<EBomCostCacheDTO> childrenCost = children.parallelStream()
.map(c -> calculateBom(c, datas, config, result))
.collect(Collectors.toList()); .collect(Collectors.toList());
for (EBomCostCacheDTO c : childrenCost) { if (CollectionUtil.isEmpty(children)) {
EBomDTO dto1 = children.stream().filter(cc -> StrUtil.equals(cc.getMaterialNo(), c.getMaterialNo())).findFirst().orElse(null); cdto.setHasChildren(false);
cdto.setSteelsCost(cdto.getSteelsCost().add(c.getSteelsCost().multiply(dto1.getNum()))); // 计算自身
cdto.setSteelsCost(cdto.getSteelsCost().add(c.getWasteCost().multiply(dto1.getNum()))); cdto.setPurchasedParts(!BomConstant.STEELS_CATEGORY_CODE.contains(dto.getMaterialCategoryCode()));
cdto.setPurchasedPartsCost(cdto.getPurchasedPartsCost().add(c.getPurchasedPartsCost().multiply(dto1.getNum()))); if (cdto.isPurchasedParts()) {
cdto.setPieceRateSalary(cdto.getPieceRateSalary().add(c.getPieceRateSalary().multiply(dto1.getNum()))); // 外购件只计算采购成本
cdto.setWelfareExpenses(cdto.getWelfareExpenses().add(c.getWelfareExpenses().multiply(dto1.getNum()))); cdto.setPurchasedPartsCost(calculatePurchasedPartsCost(dto));
cdto.setHydropowerCost(cdto.getHydropowerCost().add(c.getHydropowerCost().multiply(dto1.getNum()))); } else {
cdto.setDepreciationCost(cdto.getDepreciationCost().add(c.getDepreciationCost().multiply(dto1.getNum()))); // 非外购件计算材料成本和制作成本
cdto.setWorkshopManagementLaborCost(cdto.getWorkshopManagementLaborCost().add(c.getWorkshopManagementLaborCost().multiply(dto1.getNum()))); // 计算材料成本
cdto.setWorkshopOfficeExpenses(cdto.getWorkshopOfficeExpenses().add(c.getWorkshopOfficeExpenses().multiply(dto1.getNum()))); cdto.setSteelsCost(calculateSteelsCost(dto));
cdto.setAuxiliaryDepartmentLaborCost(cdto.getAuxiliaryDepartmentLaborCost().add(c.getAuxiliaryDepartmentLaborCost().multiply(dto1.getNum()))); // 计算损耗成本
cdto.setAuxiliaryDepartmentExpenses(cdto.getAuxiliaryDepartmentExpenses().add(c.getAuxiliaryDepartmentExpenses().multiply(dto1.getNum()))); cdto.setWasteCost(calculateWasteCost(dto));
cdto.setPaintCost(cdto.getPaintCost().add(c.getPaintCost().multiply(dto1.getNum()))); }
if (CollectionUtil.isNotEmpty(c.getProductionCosts())) { } else {
c.getProductionCosts().forEach(pc -> { cdto.setHasChildren(true);
ProductionCostDTO pdto = cdto.getProductionCosts().parallelStream() cdto.setPurchasedParts(false);
.filter(p -> StrUtil.equals(p.getName(), pc.getName())) //计算子级成本
.findFirst() List<EBomCostCacheDTO> childrenCost = children.parallelStream()
.orElse(null); .map(c -> calculateBom(false, c, datas, config, result))
if (Objects.isNull(pdto)) { .collect(Collectors.toList());
pdto = new ProductionCostDTO(); for (EBomCostCacheDTO c : childrenCost) {
pdto.setName(pc.getName()); EBomDTO dto1 = children.stream().filter(cc -> StrUtil.equals(cc.getMaterialNo(), c.getMaterialNo())).findFirst().orElse(null);
cdto.getProductionCosts().add(pdto); cdto.setSteelsCost(cdto.getSteelsCost().add(c.getSteelsCost().multiply(dto1.getNum())));
} cdto.setSteelsCost(cdto.getSteelsCost().add(c.getWasteCost().multiply(dto1.getNum())));
pdto.setCost(pdto.getCost().add(pc.getCost().multiply(dto1.getNum()))); cdto.setPurchasedPartsCost(cdto.getPurchasedPartsCost().add(c.getPurchasedPartsCost().multiply(dto1.getNum())));
}); cdto.setPieceRateSalary(cdto.getPieceRateSalary().add(c.getPieceRateSalary().multiply(dto1.getNum())));
cdto.setWelfareExpenses(cdto.getWelfareExpenses().add(c.getWelfareExpenses().multiply(dto1.getNum())));
cdto.setHydropowerCost(cdto.getHydropowerCost().add(c.getHydropowerCost().multiply(dto1.getNum())));
cdto.setDepreciationCost(cdto.getDepreciationCost().add(c.getDepreciationCost().multiply(dto1.getNum())));
cdto.setWorkshopManagementLaborCost(cdto.getWorkshopManagementLaborCost().add(c.getWorkshopManagementLaborCost().multiply(dto1.getNum())));
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 (CollectionUtil.isNotEmpty(c.getProductionCosts())) {
c.getProductionCosts().forEach(pc -> {
ProductionCostDTO pdto = cdto.getProductionCosts().parallelStream()
.filter(p -> StrUtil.equals(p.getName(), pc.getName()))
.findFirst()
.orElse(null);
if (Objects.isNull(pdto)) {
pdto = new ProductionCostDTO();
pdto.setName(pc.getName());
cdto.getProductionCosts().add(pdto);
}
pdto.setCost(pdto.getCost().add(pc.getCost().multiply(dto1.getNum())));
});
}
} }
} }
} }
@ -167,7 +180,12 @@ public class BomCostCalculateService {
//log.debug(cdata); //log.debug(cdata);
EBomCostCacheDTO cdto = JsonUtil.fromJson(cdata, EBomCostCacheDTO.class); EBomCostCacheDTO cdto = JsonUtil.fromJson(cdata, EBomCostCacheDTO.class);
result.add(cdto); result.add(cdto);
children.parallelStream().forEach(c -> calculateBom(c, datas, config, result)); if (!purchaseTypeIsF) {
List<EBomDTO> children = datas.stream()
.filter(d -> StrUtil.equals(d.getParentMaterialNo(), dto.getMaterialNo()))
.collect(Collectors.toList());
children.parallelStream().forEach(c -> calculateBom(false, c, datas, config, result));
}
return cdto; return cdto;
} }
} }

View File

@ -0,0 +1,60 @@
package com.nflg.product.technology.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nflg.product.technology.mapper.master.MaterialMainAttrMapper;
import com.nflg.product.technology.pojo.entity.MaterialMainAttrEntity;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
@Service
public class MaterialMainAttrService extends ServiceImpl<MaterialMainAttrMapper, MaterialMainAttrEntity> {
@Resource
private RedisTemplate<String, Object> redisTemplate;
public boolean purchaseTypeIsF(String materialNo) {
String key = "technology:materialNo:purchaseTypeIsF";
HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash();
Object data = hashOps.get(key, materialNo);
if (Objects.isNull(data)) {
data = lambdaQuery()
.eq(MaterialMainAttrEntity::getMaterialNo, materialNo)
.eq(MaterialMainAttrEntity::getPurchaseType, "F")
.eq(MaterialMainAttrEntity::getFactory, "1010")
.exists();
if (hashOps.size(key) == 0) {
hashOps.put(key, materialNo, data);
redisTemplate.expire(key, 8, TimeUnit.HOURS);
} else {
hashOps.put(key, materialNo, data);
}
}
return (boolean) data;
}
public boolean purchaseTypeIsE50(String materialNo) {
String key = "technology:materialNo:purchaseTypeIsE50";
HashOperations<String, String, Object> hashOps = redisTemplate.opsForHash();
Object data = hashOps.get(key, materialNo);
if (Objects.isNull(data)) {
data = lambdaQuery()
.eq(MaterialMainAttrEntity::getMaterialNo, materialNo)
.eq(MaterialMainAttrEntity::getFactory, "1010")
.eq(MaterialMainAttrEntity::getPurchaseType, "E")
.eq(MaterialMainAttrEntity::getSpecialPurchaseType, "50")
.exists();
if (hashOps.size(key) == 0) {
hashOps.put(key, materialNo, data);
redisTemplate.expire(key, 8, TimeUnit.HOURS);
} else {
hashOps.put(key, materialNo, data);
}
}
return (boolean) data;
}
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nflg.product.technology.mapper.master.MaterialMainAttrMapper">
</mapper>