Commit 96e321fd by zhangtw

导入新建类与模板

parent 9d95b93c
package com.scpyun.platform.jilinsscgsdp.bean.entity;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
@Data
public class InboundImp {
@ExcelProperty(value = "入库单号", index = 0)
private String inbound_no;
@ExcelProperty(value = "批次号", index = 1)
private String batch_no;
@ExcelProperty(value = "入库日期", index = 2)
private String inbound_date;
@ExcelProperty(value = "存放位置", index = 3)
private String storage_location;
@ExcelProperty(value = "备注", index = 4)
private String remark;
@ExcelProperty(value = "物料编号", index = 5)
private String material_code;
@ExcelProperty(value = "物料名称", index = 6)
private String material_name;
@ExcelProperty(value = "入库数量", index = 7)
private String inbound_quantity;
@ExcelProperty(value = "单价", index = 8)
private String unit_price;
@ExcelProperty(value = "生产日期", index = 9)
private String production_date;
@ExcelProperty(value = "有效期至", index = 10)
private String expiry_date;
}
package com.scpyun.platform.jilinsscgsdp.utils;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.scpyun.base.core.utils.StringUtils;
import com.scpyun.base.core.utils.UUIDUtil;
import com.scpyun.base.db.service.CommonService;
import com.scpyun.platform.jilinsscgsdp.bean.entity.InboundImp;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
public class InboundFileListener extends AnalysisEventListener<InboundImp> {
private static final Logger log = LoggerFactory.getLogger(InboundFileListener.class);
private static final int BATCH_COUNT = 200;
private static final int GROUP_BATCH_SIZE = 50; // 每批处理50个入库单
private List<InboundImp> cached = new ArrayList<>(BATCH_COUNT);
private AtomicInteger insertCount = new AtomicInteger(0);
private AtomicInteger errorCount = new AtomicInteger(0);
private AtomicInteger inboundCount = new AtomicInteger(0); // 入库单计数
private CommonService commonService;
private String namespace;
private Map<String,Object> param;
private List<String> errInfo = new ArrayList<>();
private Map<String,Object> materialMap;
// 用于存储按入库单号分组的明细数据
private Map<String, List<InboundImp>> inboundGroupMap = new HashMap<>();
private Map<String, Object> user = null;
private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
public InboundFileListener(CommonService commonService, String namespace, Map<String,Object> param) {
this.commonService = commonService;
this.namespace = namespace;
this.param = param;
user = (Map<String, Object>) param.get("_user");
// 初始化日期格式
dateFormat.setLenient(false);
// 初始化物料映射
initMaterialMap();
}
/**
* 初始化物料映射
*/
private void initMaterialMap() {
try {
List<Map<String,Object>> materialList = commonService.findList(namespace + "getMaterialMap", null);
materialMap = new HashMap<>();
for (Map<String, Object> material : materialList) {
String code = String.valueOf(material.get("material_code"));
if (StringUtils.isNotEmpty(code)) {
materialMap.put(code, material);
}
}
log.info("初始化物料映射完成,共 {} 条记录", materialMap.size());
} catch (Exception e) {
log.error("初始化物料映射异常", e);
materialMap = new HashMap<>();
}
}
@Override
public void invoke(InboundImp data, AnalysisContext context) {
try {
// 数据校验
if (isValidInbound(data)) {
cached.add(data);
// 按入库单号和批次号分组
String groupKey = getGroupKey(data);
// 初始化分组列表
if (!inboundGroupMap.containsKey(groupKey)) {
inboundGroupMap.put(groupKey, new ArrayList<>());
}
inboundGroupMap.get(groupKey).add(data);
} else {
errorCount.incrementAndGet();
errInfo.add("行数据校验失败: " + data.getInbound_no() + "-" + data.getMaterial_code());
}
if (cached.size() >= BATCH_COUNT) {
saveBatch();
cached.clear();
}
} catch (Exception e) {
errorCount.incrementAndGet();
errInfo.add("解析异常: " + data.getInbound_no() + "-" + data.getMaterial_code() + ":" + e.getMessage());
log.error("解析入库导入行异常", e);
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
if (!cached.isEmpty()) {
saveBatch();
cached.clear();
}
// 处理所有分组的数据
processInboundGroupsTransactional();
}
/**
* 获取分组键(入库单号 + 批次号)
*/
private String getGroupKey(InboundImp data) {
String inboundNo = StringUtils.isNotEmpty(data.getInbound_no()) ? data.getInbound_no().trim() : "";
String batchNo = StringUtils.isNotEmpty(data.getBatch_no()) ? data.getBatch_no().trim() : "DEFAULT";
return inboundNo + "|" + batchNo;
}
/**
* 事务性批量处理入库单
*/
private void processInboundGroupsTransactional() {
if (inboundGroupMap.isEmpty()) {
return;
}
List<String> groupKeys = new ArrayList<>(inboundGroupMap.keySet());
// 按批次处理,避免内存溢出
for (int i = 0; i < groupKeys.size(); i += GROUP_BATCH_SIZE) {
int end = Math.min(i + GROUP_BATCH_SIZE, groupKeys.size());
List<String> batchGroupKeys = groupKeys.subList(i, end);
try {
// 开启事务处理一批入库单
processBatchTransactional(batchGroupKeys);
} catch (Exception e) {
log.error("批量处理入库单异常", e);
// 当前批次失败,尝试单个处理
for (String groupKey : batchGroupKeys) {
try {
processSingleInboundGroup(groupKey);
} catch (Exception ex) {
errorCount.incrementAndGet();
errInfo.add("入库单处理失败: " + groupKey + ":" + ex.getMessage());
}
}
}
}
}
/**
* 批量处理事务
*/
private void processBatchTransactional(List<String> groupKeys) {
List<Map<String, Object>> masterList = new ArrayList<>();
Map<String, String> inboundIdMap = new HashMap<>(); // groupKey -> inboundId
List<Map<String, Object>> detailList = new ArrayList<>();
// 第一步:创建入库单主表和明细数据
for (String groupKey : groupKeys) {
List<InboundImp> inboundList = inboundGroupMap.get(groupKey);
if (inboundList == null || inboundList.isEmpty()) {
continue;
}
InboundImp firstRecord = inboundList.get(0);
Map<String, Object> inboundMaster = createInboundMaster(firstRecord);
if (inboundMaster == null) {
throw new RuntimeException("创建入库单主表失败: " + groupKey);
}
String inboundId = (String) inboundMaster.get("id");
inboundIdMap.put(groupKey, inboundId);
masterList.add(inboundMaster);
// 创建明细数据
for (InboundImp detail : inboundList) {
Map<String, Object> inboundDetail = createInboundDetail(detail, inboundId);
if (inboundDetail != null) {
detailList.add(inboundDetail);
}
}
}
if (masterList.isEmpty()) {
return;
}
// 第二步:批量插入入库单主表
batchInsertInboundMasters(masterList);
inboundCount.addAndGet(masterList.size());
// 第三步:批量插入入库明细
if (!detailList.isEmpty()) {
batchInsertInboundDetails(detailList);
insertCount.addAndGet(detailList.size());
}
// 第四步:批量更新物料库存
batchUpdateMaterialStock(detailList);
}
/**
* 单个入库单组处理
*/
private void processSingleInboundGroup(String groupKey) {
List<InboundImp> inboundList = inboundGroupMap.get(groupKey);
if (inboundList == null || inboundList.isEmpty()) {
return;
}
InboundImp firstRecord = inboundList.get(0);
Map<String, Object> inboundMaster = createInboundMaster(firstRecord);
if (inboundMaster == null) {
throw new RuntimeException("创建入库单主表失败: " + groupKey);
}
// 插入主表
String inboundId = insertInboundMaster(inboundMaster);
if (inboundId == null) {
throw new RuntimeException("插入入库单主表失败: " + groupKey);
}
inboundCount.incrementAndGet();
// 插入明细
for (InboundImp detail : inboundList) {
Map<String, Object> inboundDetail = createInboundDetail(detail, inboundId);
if (inboundDetail != null) {
insertInboundDetail(inboundDetail);
insertCount.incrementAndGet();
}
}
}
/**
* 批量插入入库单主表
*/
private void batchInsertInboundMasters(List<Map<String, Object>> inboundMasters) {
if (inboundMasters == null || inboundMasters.isEmpty()) {
return;
}
try {
Map<String, Object> batchParam = new HashMap<>();
batchParam.put("list", inboundMasters);
batchParam.put("_user", user);
commonService.insert(namespace + "batchInsertInboundMasters", batchParam);
} catch (Exception e) {
log.error("批量插入入库单主表异常", e);
throw new RuntimeException("批量插入入库单主表失败", e);
}
}
/**
* 批量插入入库明细
*/
private void batchInsertInboundDetails(List<Map<String, Object>> inboundDetails) {
if (inboundDetails == null || inboundDetails.isEmpty()) {
return;
}
try {
Map<String, Object> batchParam = new HashMap<>();
batchParam.put("list", inboundDetails);
batchParam.put("_user", user);
commonService.insert(namespace + "batchInsertInboundDetails", batchParam);
} catch (Exception e) {
log.error("批量插入入库明细异常", e);
throw new RuntimeException("批量插入入库明细失败", e);
}
}
/**
* 批量更新物料库存
*/
private void batchUpdateMaterialStock(List<Map<String, Object>> inboundDetails) {
if (inboundDetails == null || inboundDetails.isEmpty()) {
return;
}
try {
// 按物料ID分组汇总数量
Map<String, BigDecimal> materialQuantityMap = new HashMap<>();
for (Map<String, Object> detail : inboundDetails) {
String materialId = (String) detail.get("material_id");
BigDecimal quantity = (BigDecimal) detail.get("inbound_quantity");
if (materialId != null && quantity != null) {
materialQuantityMap.merge(materialId, quantity, BigDecimal::add);
}
}
// 批量更新
List<Map<String, Object>> updateList = new ArrayList<>();
for (Map.Entry<String, BigDecimal> entry : materialQuantityMap.entrySet()) {
Map<String, Object> updateParam = new HashMap<>();
updateParam.put("material_id", entry.getKey());
updateParam.put("quantity", entry.getValue());
updateParam.put("update_time", new Date());
updateParam.put("_user", user);
updateList.add(updateParam);
}
if (!updateList.isEmpty()) {
Map<String, Object> batchParam = new HashMap<>();
batchParam.put("list", updateList);
batchParam.put("_user", user);
commonService.update(namespace + "batchUpdateMaterialStock", batchParam);
}
} catch (Exception e) {
log.error("批量更新物料库存异常", e);
throw new RuntimeException("批量更新物料库存失败", e);
}
}
/**
* 创建入库单主表记录
*/
private Map<String, Object> createInboundMaster(InboundImp data) {
try {
Map<String, Object> master = new HashMap<>();
master.put("id", UUIDUtil.getUUID());
master.put("inbound_no", data.getInbound_no());
master.put("batch_no", data.getBatch_no());
master.put("inbound_type", 1);
master.put("inbound_status", 1); // 1-已入库
// 处理入库日期
Date inboundDate = null;
if (StringUtils.isNotEmpty(data.getInbound_date())) {
try {
inboundDate = dateFormat.parse(data.getInbound_date().trim());
} catch (ParseException e) {
log.warn("日期格式解析失败: {}", data.getInbound_date());
}
}
if (inboundDate == null) {
inboundDate = new Date();
}
master.put("inbound_date", inboundDate);
master.put("is_used", 1);
master.put("storage_location", data.getStorage_location());
master.put("remark", data.getRemark());
master.put("create_by", user.get("id"));
master.put("create_time", new Date());
master.put("_user", user);
return master;
} catch (Exception e) {
log.error("创建入库单主表异常", e);
return null;
}
}
/**
* 创建入库明细记录
*/
private Map<String, Object> createInboundDetail(InboundImp data, String inboundId) {
try {
// 检查物料是否存在
Map<String, Object> materialTempMap = (Map<String, Object>) materialMap.get(data.getMaterial_code());
if (materialTempMap == null) {
log.warn("物料 {} 不存在,跳过该明细", data.getMaterial_code());
errInfo.add("物料不存在: " + data.getMaterial_code());
return null;
}
Map<String, Object> detail = new HashMap<>();
detail.put("id", UUIDUtil.getUUID());
detail.put("inbound_id", inboundId);
detail.put("material_id", materialTempMap.get("id"));
// 物料名称优先使用Excel中的,否则使用系统已有的
String materialName = StringUtils.isNotEmpty(data.getMaterial_name()) ?
data.getMaterial_name().trim() : (String) materialTempMap.get("material_name");
detail.put("material_name", materialName);
detail.put("material_code", data.getMaterial_code());
detail.put("inbound_type", 1);
detail.put("is_used", 1);
// 转换入库数量
BigDecimal inboundQuantity = BigDecimal.ZERO;
try {
if (StringUtils.isNotEmpty(data.getInbound_quantity())) {
inboundQuantity = new BigDecimal(data.getInbound_quantity().trim());
}
} catch (Exception e) {
log.warn("数量格式转换失败: {}", data.getInbound_quantity());
inboundQuantity = BigDecimal.ZERO;
}
detail.put("inbound_quantity", inboundQuantity);
// 转换单价
BigDecimal unitPrice = BigDecimal.ZERO;
try {
if (StringUtils.isNotEmpty(data.getUnit_price())) {
unitPrice = new BigDecimal(data.getUnit_price().trim());
}
} catch (Exception e) {
log.warn("单价格式转换失败: {}", data.getUnit_price());
unitPrice = BigDecimal.ZERO;
}
detail.put("unit_price", unitPrice);
// 计算总金额
BigDecimal totalAmount = inboundQuantity.multiply(unitPrice);
detail.put("total_amount", totalAmount);
// 处理生产日期
Date productionDate = null;
if (StringUtils.isNotEmpty(data.getProduction_date())) {
try {
productionDate = dateFormat.parse(data.getProduction_date().trim());
} catch (ParseException e) {
log.warn("生产日期格式解析失败: {}", data.getProduction_date());
}
}
detail.put("production_date", productionDate);
// 处理有效期
Date expiryDate = null;
if (StringUtils.isNotEmpty(data.getExpiry_date())) {
try {
expiryDate = dateFormat.parse(data.getExpiry_date().trim());
} catch (ParseException e) {
log.warn("有效期格式解析失败: {}", data.getExpiry_date());
}
}
detail.put("expiry_date", expiryDate);
detail.put("create_by", user.get("id"));
detail.put("create_time", new Date());
detail.put("_user", user);
return detail;
} catch (Exception e) {
log.error("创建入库明细异常", e);
return null;
}
}
/**
* 插入入库单主表
*/
private String insertInboundMaster(Map<String, Object> inboundMaster) {
try {
commonService.insert(namespace + "insertInboundMaster", inboundMaster);
return (String) inboundMaster.get("id");
} catch (Exception e) {
log.error("插入入库单主表异常", e);
return null;
}
}
/**
* 插入入库明细
*/
private void insertInboundDetail(Map<String, Object> inboundDetail) {
try {
commonService.insert(namespace + "insertInboundDetail", inboundDetail);
} catch (Exception e) {
log.error("插入入库明细异常", e);
throw e;
}
}
/**
* 数据校验方法
*/
private boolean isValidInbound(InboundImp data) {
// 必需字段校验
if (StringUtils.isEmpty(data.getInbound_no()) ||
StringUtils.isEmpty(data.getMaterial_code()) ||
StringUtils.isEmpty(data.getInbound_quantity())) {
return false;
}
// 数量校验
try {
BigDecimal quantity = new BigDecimal(data.getInbound_quantity().trim());
if (quantity.compareTo(BigDecimal.ZERO) <= 0) {
return false;
}
} catch (Exception e) {
return false;
}
// 单价校验(可选)
if (StringUtils.isNotEmpty(data.getUnit_price())) {
try {
new BigDecimal(data.getUnit_price().trim());
} catch (Exception e) {
return false;
}
}
return true;
}
public Map<String,Object> getResult() {
Map<String,Object> r = new HashMap<>();
r.put("insert", insertCount.get()); // 明细条数
r.put("inbound_count", inboundCount.get()); // 入库单数量
r.put("error", errorCount.get());
r.put("errInfo", errInfo.size() > 0 ? String.join(", ", errInfo) : "无");
return r;
}
// 批量保存方法(如果需要保留原来的逻辑)
private void saveBatch() {
// 这里可以根据需要保留原有的批量处理逻辑
}
}
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论