提交 31c41f3b authored 作者: douxy's avatar douxy

增加店内执行计划导出/入功能:修复更新功能

上级 03bcdf82
package com.sfa.operation.pojo.sales.vo;
import lombok.Data;
/**
* @Author: DouXinYu
* @Date: 2025-12-10 15:31
* @Description: 店内执行-填报要更新到表的字段
*/
@Data
public class SalesApDisplayVo {
/**
* 主键ID
*/
private Long sadId;
/**
* 主货架形式(实际)
*/
private String actualMainShelfType;
/**
* 主货架数量(实际)
*/
private Integer actualMainShelfQty;
/**
* 实际-主货架是否执行
* 执行主货架形式 >= 计划主货架形式 && 执行主货架数量 >= 计划主货架数量"
* 执行/未执行
*/
private String actualMainShelfExecuted;
/**
* 端架数量(实际)
*/
private Integer actualEndCapQty;
/**
* 实际-架是否执行
* 执行端架数量 >= 计划端架数量
*/
private String actualEndCapExecuted;
/**
* 地堆平米数(实际)
*/
private Double actualFloorStackArea;
/**
* 地堆数量(实际)
*/
private Integer actualFloorStackQty;
/**
* 实际-地堆是否执行
* 执行平米数 >= 计划平米数 && 执行数量 >= 计划数量"
*/
private String actualFloorStackExecuted;
/**
* 多点陈列数量+形式(实际)
*/
private String actualMultiDisplay;
/**
* 实际-多点陈列是否执行
*
* actualMultiDisplay的值如下时:
* 执行与计划一致:执行
* 执行与计划不一致:未执行
*/
private String actualMultiDisplayExecuted;
/**
* 挂条数量+形式(实际)
*/
private String actualHangingStripQuantityForm;
/**
* 实际-挂条是否执行
*
* actualHangingStripQuantityForm的值如下时:
* 执行与计划一致:执行
* 执行与计划不一致:未执行
*/
private String hangingStripExecuted;
/**
* 备注信息
*
*/
private String remark ;
}
package com.sfa.operation.service.sales.export.impl;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.sfa.common.core.domain.R;
import com.sfa.operation.factory.ApImportExcelStrategyFactory;
......@@ -13,6 +14,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
......@@ -61,9 +63,9 @@ public class ImportExcelServiceImpl implements IImportExcelService {
//failCount>0 时 返回错误信息
if (failCount>0 ) {
if (failCount > 0 ) {
log.error("导入失败,失败条数:{}",failCount);
return R.fail(0, request);
return R.fail(0, result);
} else {
String uuid = (String) result.getOrDefault("uuid", "");
String redisKey = REDIS_KEY_PREFIX + uuid;
......@@ -83,32 +85,70 @@ public class ImportExcelServiceImpl implements IImportExcelService {
*/
@Override
public R updateApEntity(ImportApExcelRequest request) {
if (request.getImportApType() == null || request.getImportApType().trim().isEmpty()){
if (request.getImportApType() == null || request.getImportApType().trim().isEmpty()) {
return R.fail("导入类型不能为空!");
}
if (request.getUuid() == null || request.getUuid().trim().isEmpty()){
if (request.getUuid() == null || request.getUuid().trim().isEmpty()) {
return R.fail("导入数据标识不能为空!");
}
// 获取策略
IImportApExcelStrategy strategy = apImportExcelStrategyFactory.getStrategy(request.getImportApType());
if (strategy == null){
if (strategy == null) {
return R.fail("未找到对应的导入策略!");
}
//从redis获取数据
String redisKey = REDIS_KEY_PREFIX+ request.getUuid();
String redisValue = stringRedisTemplate.opsForValue().get(redisKey);
//解析jsonToDtoList
List list = strategy.getTransactionJsonToObject(redisValue);
// 从redis获取数据
String redisKey = REDIS_KEY_PREFIX + request.getUuid();
String redisDataJson = stringRedisTemplate.opsForValue().get(redisKey);
log.info("从Redis获取数据,redisKey={}, redisDataJson={}", redisKey, redisDataJson);
//批量更新
String result = strategy.updateDisplay(list);
// 1校验Redis数据是否存在
if (redisDataJson == null || redisDataJson.trim().isEmpty()) {
log.error("Redis中无有效数据,key={}", redisKey);
return R.fail("导入数据已过期或不存在,请重新导入!");
}
if ("更新失败".equals(result)) {
return R.fail(result);
// 解析Redis数据为JSONObject,仅提取table数组(核心修改点,适配JDK8)
List<?> tableList;
try {
// 先解析整个Redis数据为JSONObject
JSONObject redisDataObj = JSONObject.parseObject(redisDataJson);
// 提取table字段对应的JSON数组
JSONArray tableArray = redisDataObj.getJSONArray("table");
if (tableArray == null) {
tableList = Collections.emptyList();
} else {
// 根据策略指定的DTO类型解析table数组
Class<?> dtoClass = strategy.getTargetDtoClass();
// 解析为策略对应的DTO类型列表(而非Object)
tableList = tableArray.toList(dtoClass);
}
} catch (Exception e) {
log.error("解析Redis中的table数组失败,redisDataJson={}", redisDataJson, e);
return R.fail("数据解析失败,请重新导入!");
}
return R.ok(result);
// 校验table数组非空
if (CollectionUtils.isEmpty(tableList)) {
log.error("解析出的table数组为空,redisDataJson={}", redisDataJson);
return R.fail("更新数据为空,请重新导入!");
}
for (Object o : tableList) {
System.out.println( o.toString());
}
// 批量更新(传入table数组,适配不同策略处理)
String updateResult = strategy.updateDisplay(tableList);
log.info("批量更新结果:{}", updateResult);
if ("更新失败".equals(updateResult)) {
return R.fail(updateResult);
}
return R.ok(updateResult);
}
}
......@@ -58,6 +58,12 @@ public interface IImportApExcelStrategy<T> {
*/
Map<String, Object> execute(String flePathUrl);
/**
* 获取导入目标DTO类
* @return 目标DTO类
*/
Class<?> getTargetDtoClass();
/**
* 获取导入Sheet名称(默认方法)
*/
......
......@@ -8,7 +8,6 @@ import com.google.gson.JsonParser;
import com.sfa.operation.config.ExportColumnConfig;
import com.sfa.operation.domain.sales.entity.SalesApDisplay;
import com.sfa.operation.pojo.sales.excel.SalesApDisplayImportExcelDto;
import com.sfa.operation.pojo.sales.vo.SalesApDisplayVo;
import com.sfa.operation.service.sales.impl.ApDisplayCoreServiceImpl;
import com.sfa.operation.service.sales.impl.ApDisplayQueryServiceImpl;
import com.sfa.operation.strategy.IImportApExcelStrategy;
......@@ -20,7 +19,6 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.io.InputStream;
import java.util.*;
......@@ -166,74 +164,126 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S
List<SalesApDisplay> salesApDisplayList = queryData(dtoList);
Map<String, SalesApDisplay> displayMap = new HashMap<>();
for (SalesApDisplay display : salesApDisplayList) {
String matchKey = buildMatchKey(
display.getSadId(),
display.getRegionName(),
display.getDealerName(),
display.getLineName()
);
// 优化点:仅用主键sadId作为匹配键
String matchKey = buildMatchKey(display.getSadId());
displayMap.put(matchKey, display);
}
for (SalesApDisplayImportExcelDto dto : dtoList) {
SalesApDisplayVo salesApDisplayVo = new SalesApDisplayVo();
BeanUtils.copyProperties(dto, salesApDisplayVo);
// 跳过sadId为空的数据(兜底防护)
if (dto.getSadId() == null) {
log.warn("DTO序号为空,跳过处理");
continue;
}
// 从Map中获取当前DTO对应的计划数据
SalesApDisplay salesApDisplay = displayMap.get(buildMatchKey(
dto.getSadId(),
dto.getRegionName(),
dto.getDealerName(),
dto.getLineName()
));
if (salesApDisplay == null) {
String dtoMatchKey = buildMatchKey(dto.getSadId());
SalesApDisplay dbDisplay = displayMap.get(dtoMatchKey);
if (dbDisplay == null) {
log.warn("序号为{}的DTO无匹配计划数据,跳过处理", dto.getSadId());
continue;
}
// 主货架执行状态计算
if (salesApDisplay.getPlannedMainShelfType() != null && salesApDisplay.getPlannedMainShelfQty() != null
&& salesApDisplayVo.getActualMainShelfType() != null && salesApDisplayVo.getActualMainShelfQty() != null) {
boolean mainShelfTypeMatch = salesApDisplayVo.getActualMainShelfType().equals(salesApDisplay.getPlannedMainShelfType());
boolean mainShelfQtySufficient = salesApDisplayVo.getActualMainShelfQty() >= salesApDisplay.getPlannedMainShelfQty();
salesApDisplayVo.setActualMainShelfExecuted((mainShelfTypeMatch && mainShelfQtySufficient) ? "执行" : "未执行");
// 优化点:复制新实体,避免ORM会话关联的并发/只读异常
SalesApDisplay updateDisplay = new SalesApDisplay();
BeanUtils.copyProperties(dbDisplay, updateDisplay);
// ========== 核心优化:赋值DTO中的基础实际值到新实体 ==========
// 1. 主货架基础值(实际)
if (dto.getActualMainShelfType() != null) {
updateDisplay.setActualMainShelfType(dto.getActualMainShelfType());
}
if (dto.getActualMainShelfQty() != null) {
updateDisplay.setActualMainShelfQty(dto.getActualMainShelfQty());
}
// 2. 端架基础值(实际)
if (dto.getActualEndCapQty() != null) {
updateDisplay.setActualEndCapQty(dto.getActualEndCapQty());
}
// 端架执行状态计算
if (salesApDisplayVo.getActualEndCapQty() != null && salesApDisplay.getPlannedEndCapQty() != null) {
salesApDisplayVo.setActualEndCapExecuted(
salesApDisplayVo.getActualEndCapQty() >= salesApDisplay.getPlannedEndCapQty() ? "执行" : "未执行"
// 3. 地堆基础值(实际)
if (dto.getActualFloorStackArea() != null) {
updateDisplay.setActualFloorStackArea(dto.getActualFloorStackArea());
}
if (dto.getActualFloorStackQty() != null) {
updateDisplay.setActualFloorStackQty(dto.getActualFloorStackQty());
}
// 4. 多点陈列基础值(实际)
String actualMultiDisplay = StringUtils.trimToNull(dto.getActualMultiDisplay());
if (actualMultiDisplay != null) {
updateDisplay.setActualMultiDisplay(actualMultiDisplay);
}
// 5. 挂条基础值(实际)
String actualHangingStrip = StringUtils.trimToNull(dto.getActualHangingStripQuantityForm());
if (actualHangingStrip != null) {
updateDisplay.setActualHangingStripQuantityForm(actualHangingStrip);
}
// 6. 备注
if (dto.getRemark() != null) {
updateDisplay.setRemark(dto.getRemark());
}
// ========== 执行状态计算(按业务规则处理空值) ==========
// 1. 主货架执行状态计算
if (updateDisplay.getPlannedMainShelfType() != null && updateDisplay.getPlannedMainShelfQty() != null
&& dto.getActualMainShelfType() != null && dto.getActualMainShelfQty() != null) {
boolean mainShelfTypeMatch = dto.getActualMainShelfType().equals(updateDisplay.getPlannedMainShelfType());
boolean mainShelfQtySufficient = dto.getActualMainShelfQty() >= updateDisplay.getPlannedMainShelfQty();
updateDisplay.setActualMainShelfExecuted((mainShelfTypeMatch && mainShelfQtySufficient) ? "执行" : "未执行");
} else {
// 业务规则:计划/实际值不全时,清空执行状态
updateDisplay.setActualMainShelfExecuted(null);
}
// 2. 端架执行状态计算
if (dto.getActualEndCapQty() != null && updateDisplay.getPlannedEndCapQty() != null) {
updateDisplay.setActualEndCapExecuted(
dto.getActualEndCapQty() >= updateDisplay.getPlannedEndCapQty() ? "执行" : "未执行"
);
} else {
updateDisplay.setActualEndCapExecuted(null);
}
// 地堆执行状态计算
if (salesApDisplay.getPlannedFloorStackArea() != null && salesApDisplay.getPlannedFloorStackQty() != null
&& salesApDisplayVo.getActualFloorStackArea() != null && salesApDisplayVo.getActualFloorStackQty() != null) {
boolean areaSufficient = salesApDisplayVo.getActualFloorStackArea() >= salesApDisplay.getPlannedFloorStackArea();
boolean qtySufficient = salesApDisplayVo.getActualFloorStackQty() >= salesApDisplay.getPlannedFloorStackQty();
salesApDisplayVo.setActualFloorStackExecuted((areaSufficient && qtySufficient) ? "执行" : "未执行");
// 3. 地堆执行状态计算
if (updateDisplay.getPlannedFloorStackArea() != null && updateDisplay.getPlannedFloorStackQty() != null
&& dto.getActualFloorStackArea() != null && dto.getActualFloorStackQty() != null) {
boolean areaSufficient = dto.getActualFloorStackArea() >= updateDisplay.getPlannedFloorStackArea();
boolean qtySufficient = dto.getActualFloorStackQty() >= updateDisplay.getPlannedFloorStackQty();
updateDisplay.setActualFloorStackExecuted((areaSufficient && qtySufficient) ? "执行" : "未执行");
} else {
updateDisplay.setActualFloorStackExecuted(null);
}
// 多点陈列执行状态
if (salesApDisplay.getPlannedMultiDisplay() != null && salesApDisplayVo.getActualMultiDisplay() != null) {
salesApDisplayVo.setActualMultiDisplayExecuted(
StringUtils.equals("执行与计划一致", salesApDisplayVo.getActualMultiDisplay()) ? "执行" : "未执行"
// 4. 多点陈列执行状态(处理空字符串)
if (updateDisplay.getPlannedMultiDisplay() != null && actualMultiDisplay != null) {
updateDisplay.setActualMultiDisplayExecuted(
StringUtils.equals("执行与计划一致", actualMultiDisplay) ? "执行" : "未执行"
);
} else {
updateDisplay.setActualMultiDisplayExecuted(null);
}
// 挂条执行状态字段赋值错误
if (salesApDisplay.getPlannedHangingStripQuantityForm() != null && salesApDisplayVo.getActualHangingStripQuantityForm() != null) {
salesApDisplayVo.setHangingStripExecuted(
StringUtils.equals("执行与计划一致", salesApDisplayVo.getActualHangingStripQuantityForm()) ? "执行" : "未执行"
// 5. 挂条执行状态(处理空字符串)
if (updateDisplay.getPlannedHangingStripQuantityForm() != null && actualHangingStrip != null) {
updateDisplay.setHangingStripExecuted(
StringUtils.equals("执行与计划一致", actualHangingStrip) ? "执行" : "未执行"
);
} else {
updateDisplay.setHangingStripExecuted(null);
}
// VO转实体逻辑
SalesApDisplay updatedEntity = new SalesApDisplay();
BeanUtils.copyProperties(salesApDisplayVo, updatedEntity);
updateEntityList.add(updatedEntity);
// 添加到更新列表
updateEntityList.add(updateDisplay);
}
// 日志打印更新实体(调试用,生产可注释)
for (SalesApDisplay salesApDisplay : updateEntityList) {
log.info("待更新实体:{}", salesApDisplay.toString());
}
return updateEntityList;
}
......@@ -267,7 +317,7 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S
//批量验证/操作)
int successCount = queryAndBatchOperate(dtoList);
// 5封装返回结果
// 封装返回结果
resultMap.put("uuid", UUID.randomUUID().toString());
resultMap.put("table", dtoList);
resultMap.put("successCount", successCount);
......@@ -343,6 +393,10 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S
}
}
@Override
public Class<?> getTargetDtoClass() {
return SalesApDisplayImportExcelDto.class;
}
/**
* 统一构建组合匹配键(保证DTO和查询结果的键规则一致)
......@@ -354,6 +408,13 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S
+ (lineName == null ? "" : lineName.trim());
}
/**
* 统一构建组合匹配键(保证DTO和查询结果的键规则一致)
*/
private String buildMatchKey(Long sadId) {
return sadId == null ? "" : sadId.toString();
}
/**
* 构建错误DTO(仅用于参数异常场景)
*/
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论