提交 e2903b83 authored 作者: douxy's avatar douxy

增加店内执行计划导出/入功能: 1优化更新速度

上级 8e4e0065
...@@ -5,6 +5,7 @@ import com.sfa.operation.domain.sales.entity.SalesApDisplay; ...@@ -5,6 +5,7 @@ import com.sfa.operation.domain.sales.entity.SalesApDisplay;
import com.sfa.operation.domain.sales.wq.SalesApWq; import com.sfa.operation.domain.sales.wq.SalesApWq;
import com.sfa.operation.pojo.sales.excel.SalesApDisplayImportExcelDto; import com.sfa.operation.pojo.sales.excel.SalesApDisplayImportExcelDto;
import com.sfa.operation.pojo.sales.response.SalesApDisplayDto; import com.sfa.operation.pojo.sales.response.SalesApDisplayDto;
import org.springframework.data.repository.query.Param;
import java.util.List; import java.util.List;
...@@ -26,6 +27,6 @@ public interface ISalesApDisplayDao { ...@@ -26,6 +27,6 @@ public interface ISalesApDisplayDao {
List<SalesApDisplay> queryByCondition(List<SalesApDisplayImportExcelDto> validDtoList); List<SalesApDisplay> queryByCondition(List<SalesApDisplayImportExcelDto> validDtoList);
int batchUpdate(List<SalesApDisplay> updateEntityList); boolean batchUpdate( List<SalesApDisplay> updateEntityList);
} }
...@@ -3,7 +3,9 @@ package com.sfa.operation.domain.sales.dao.impl; ...@@ -3,7 +3,9 @@ package com.sfa.operation.domain.sales.dao.impl;
import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sfa.common.core.enums.ECode; import com.sfa.common.core.enums.ECode;
import com.sfa.common.core.exception.CheckedException; import com.sfa.common.core.exception.CheckedException;
import com.sfa.common.core.utils.DateUtils; import com.sfa.common.core.utils.DateUtils;
...@@ -16,6 +18,7 @@ import com.sfa.operation.domain.sales.mapper.SalesApDisplayMapper; ...@@ -16,6 +18,7 @@ import com.sfa.operation.domain.sales.mapper.SalesApDisplayMapper;
import com.sfa.operation.domain.sales.wq.SalesApWq; import com.sfa.operation.domain.sales.wq.SalesApWq;
import com.sfa.operation.pojo.sales.excel.SalesApDisplayImportExcelDto; import com.sfa.operation.pojo.sales.excel.SalesApDisplayImportExcelDto;
import com.sfa.operation.pojo.sales.response.SalesApDisplayDto; import com.sfa.operation.pojo.sales.response.SalesApDisplayDto;
import com.sfa.operation.util.excel.EntityUpdateCheckUtil;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -24,6 +27,7 @@ import org.springframework.util.CollectionUtils; ...@@ -24,6 +27,7 @@ import org.springframework.util.CollectionUtils;
import java.util.*; import java.util.*;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Collectors;
/** /**
* @author : liqiulin * @author : liqiulin
...@@ -112,15 +116,14 @@ public class SalesApDisplayDaoImpl implements ISalesApDisplayDao { ...@@ -112,15 +116,14 @@ public class SalesApDisplayDaoImpl implements ISalesApDisplayDao {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public int batchUpdate(List<SalesApDisplay> updateEntityList) { public boolean batchUpdate(List<SalesApDisplay> updateEntityList) {
if (CollectionUtils.isEmpty(updateEntityList)){ int affectRows = 0;
return 0; List<List<SalesApDisplay>> batchList = EntityUpdateCheckUtil.splitList(updateEntityList, 1000);
} for (List<SalesApDisplay> batch : batchList) {
int count = 0; // 批量更新
for (SalesApDisplay salesApDisplay : updateEntityList) { affectRows = salesapdisMapper.batchUpdate(batch);
count += salesapdisMapper.updateById(salesApDisplay); }
} return affectRows > 0;
return count;
} }
......
...@@ -3,6 +3,7 @@ package com.sfa.operation.domain.sales.mapper; ...@@ -3,6 +3,7 @@ package com.sfa.operation.domain.sales.mapper;
import com.sfa.operation.domain.sales.entity.SalesApDisplay; import com.sfa.operation.domain.sales.entity.SalesApDisplay;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.sfa.operation.domain.sales.wq.SalesApWq; import com.sfa.operation.domain.sales.wq.SalesApWq;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
import java.util.List; import java.util.List;
...@@ -22,6 +23,8 @@ public interface SalesApDisplayMapper extends BaseMapper<SalesApDisplay> { ...@@ -22,6 +23,8 @@ public interface SalesApDisplayMapper extends BaseMapper<SalesApDisplay> {
List<Map<String,Object>> queryDeptAPReportZQ(SalesApWq build); List<Map<String,Object>> queryDeptAPReportZQ(SalesApWq build);
List<Map<String, Object>> queryDeptAPReportDQ(SalesApWq build); List<Map<String, Object>> queryDeptAPReportDQ(SalesApWq build);
int batchUpdate(@Param("list") List<SalesApDisplay> validEntities);
} }
......
...@@ -88,7 +88,6 @@ public class SalesApDisplayVo { ...@@ -88,7 +88,6 @@ public class SalesApDisplayVo {
/** /**
* 备注信息 * 备注信息
* *
* 计划与执行不一致时的原因(不必要填写)
*/ */
private String remark ; private String remark ;
} }
...@@ -23,5 +23,5 @@ public interface IApDisplayCoreService { ...@@ -23,5 +23,5 @@ public interface IApDisplayCoreService {
void putPromotionDetail(SalesApRequest request); void putPromotionDetail(SalesApRequest request);
int batchUpdate(List<SalesApDisplay> updateEntityList); boolean batchUpdate(List<SalesApDisplay> updateEntityList);
} }
...@@ -86,7 +86,7 @@ public class ApDisplayCoreServiceImpl implements IApDisplayCoreService { ...@@ -86,7 +86,7 @@ public class ApDisplayCoreServiceImpl implements IApDisplayCoreService {
* @return * @return
*/ */
@Override @Override
public int batchUpdate(List<SalesApDisplay> updateEntityList) { public boolean batchUpdate(List<SalesApDisplay> updateEntityList) {
return salesApDisplayDao.batchUpdate(updateEntityList); return salesApDisplayDao.batchUpdate(updateEntityList);
} }
......
...@@ -109,7 +109,7 @@ public class NormalDisplayExportStrategyImpl implements IExportApExcelStrategy { ...@@ -109,7 +109,7 @@ public class NormalDisplayExportStrategyImpl implements IExportApExcelStrategy {
column.add(actualMultiDisplay); column.add(actualMultiDisplay);
column.add(new ExportColumnConfig("plannedHangingStripQuantityForm", "挂条数量形式(计划)", "", ExcelStyleUtils.ExcelStyle.UNMODIFIABLE)); column.add(new ExportColumnConfig("plannedHangingStripQuantityForm", "挂条数量形式(计划)", "", ExcelStyleUtils.ExcelStyle.UNMODIFIABLE));
column.add(actualHangingStripQuantityForm); column.add(actualHangingStripQuantityForm);
column.add(new ExportColumnConfig("remark", "备注-未执行原因", "", ExcelStyleUtils.ExcelStyle.DEFAULT_STYLE)); column.add(new ExportColumnConfig("remark", "备注", "", ExcelStyleUtils.ExcelStyle.DEFAULT_STYLE));
for (ExportColumnConfig exportColumnConfig : column) { for (ExportColumnConfig exportColumnConfig : column) {
System.out.println(exportColumnConfig.toString()); System.out.println(exportColumnConfig.toString());
......
package com.sfa.operation.strategy.impl.imports; package com.sfa.operation.strategy.impl.imports;
import com.alibaba.nacos.shaded.com.google.gson.reflect.TypeToken; import com.alibaba.nacos.shaded.com.google.gson.reflect.TypeToken;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser; import com.google.gson.JsonParser;
...@@ -32,6 +33,7 @@ import java.util.stream.Collectors; ...@@ -32,6 +33,7 @@ import java.util.stream.Collectors;
*/ */
@Slf4j @Slf4j
@DS("bi")
@Component("normalDisplayImportStrategy") @Component("normalDisplayImportStrategy")
public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<SalesApDisplayImportExcelDto> { public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<SalesApDisplayImportExcelDto> {
...@@ -241,7 +243,7 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S ...@@ -241,7 +243,7 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S
InputStream inputStream = null; InputStream inputStream = null;
try { try {
// 获取文件流(修改:文件流为空时构建错误DTO) // 获取文件流
inputStream = ExcelUtils.getOssFileInputStream(filePathUrl); inputStream = ExcelUtils.getOssFileInputStream(filePathUrl);
if (inputStream == null) { if (inputStream == null) {
SalesApDisplayImportExcelDto errorDto = buildSimpleErrorDto("文件流获取失败:OSS文件不存在或权限不足"); SalesApDisplayImportExcelDto errorDto = buildSimpleErrorDto("文件流获取失败:OSS文件不存在或权限不足");
...@@ -305,7 +307,6 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S ...@@ -305,7 +307,6 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S
} }
@Override @Override
@Transactional(rollbackFor = Exception.class)
public String updateDisplay(List<SalesApDisplayImportExcelDto> dtoList) { public String updateDisplay(List<SalesApDisplayImportExcelDto> dtoList) {
if (dtoList == null || dtoList.isEmpty()) { if (dtoList == null || dtoList.isEmpty()) {
...@@ -315,8 +316,7 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S ...@@ -315,8 +316,7 @@ public class NormalDisplayImportStrategyImpl implements IImportApExcelStrategy<S
if (salesApDisplayList.isEmpty()) { if (salesApDisplayList.isEmpty()) {
return "更新失败"; return "更新失败";
} }
int i = salesApDisplayCoreService.batchUpdate(salesApDisplayList); return salesApDisplayCoreService.batchUpdate(salesApDisplayList) ? "更新成功" : "更新失败";
return i > 0 ? "更新成功" : "更新失败";
} }
......
package com.sfa.operation.util.excel;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
/**
* 实体更新字段校验工具
*/
public class EntityUpdateCheckUtil {
private static final Logger log = LoggerFactory.getLogger(EntityUpdateCheckUtil.class);
/**
* 校验实体是否有「非主键的有效更新字段」
* @param entity 待校验实体
* @return true=有有效字段,false=无有效字段
*/
public static <T> boolean hasValidUpdateField(T entity) {
if (entity == null) {
return false;
}
Class<?> clazz = entity.getClass();
Field[] fields = clazz.getDeclaredFields();
boolean hasUpdateField = false;
for (Field field : fields) {
field.setAccessible(true);
String fieldName = field.getName();
try {
Object fieldValue = field.get(entity);
// 跳过主键字段(带@TableId注解)
if (field.isAnnotationPresent(TableId.class)) {
continue;
}
// 跳过不参与更新的字段(如@TableField(updateStrategy = FieldStrategy.NEVER))
TableField tableField = field.getAnnotation(TableField.class);
if (tableField != null && tableField.updateStrategy().equals(FieldStrategy.NEVER)) {
continue;
}
// 存在非空的更新字段
if (fieldValue != null) {
hasUpdateField = true;
break;
}
} catch (IllegalAccessException e) {
log.error("反射校验实体字段失败:fieldName={}", fieldName, e);
}
}
return hasUpdateField;
}
public static <T> List<List<T>> splitList(List<T> list, int batchSize) {
List<List<T>> batches = new ArrayList<>();
for (int i = 0; i < list.size(); i += batchSize) {
int end = Math.min(i + batchSize, list.size());
batches.add(list.subList(i, end));
}
return batches;
}
}
\ No newline at end of file
...@@ -11,6 +11,9 @@ import com.aliyuncs.exceptions.ClientException; ...@@ -11,6 +11,9 @@ import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType; import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile; import com.aliyuncs.profile.IClientProfile;
import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.sfa.operation.config.ExportColumnConfig; import com.sfa.operation.config.ExportColumnConfig;
import com.sfa.operation.config.OssConfigProperties; import com.sfa.operation.config.OssConfigProperties;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
...@@ -58,6 +61,47 @@ public class ExcelUtils { ...@@ -58,6 +61,47 @@ public class ExcelUtils {
@Autowired @Autowired
private OssConfigProperties ossConfig; private OssConfigProperties ossConfig;
public static String extractFileNameFromUrl(String filePathUrl) {
if (StringUtils.isBlank(filePathUrl)) {
return "未知文件";
}
try {
// 先尝试URL解码,处理包含特殊字符或中文的文件名
String decodedUrl = URLDecoder.decode(filePathUrl, StandardCharsets.UTF_8.name());
// 方案1:标准文件路径(file:///C:/Users/xxx/Desktop/data.xlsx)
if (decodedUrl.startsWith("file:")) {
return new File(decodedUrl.substring(6)).getName();
}
// 方案2:普通绝对/相对路径(如"C:\\Users\\xxx\\Desktop\\data.xlsx"或"./data.xlsx")
if (decodedUrl.contains(".") || decodedUrl.contains("/")) {
return new File(decodedUrl).getName();
}
// 方案3:URL路径(如"http://example.com/path/data.xlsx?version=1")
if (decodedUrl.contains("://")) {
String pathPart = decodedUrl.substring(decodedUrl.indexOf("://") + 3);
if (pathPart.contains("/")) {
pathPart = pathPart.substring(pathPart.indexOf("/") + 1);
}
// 移除查询参数(?之后的部分)
if (pathPart.contains("?")) {
pathPart = pathPart.substring(0, pathPart.indexOf("?"));
}
return new File(pathPart).getName();
}
// 默认兜底方案:直接当作文件名处理
return decodedUrl;
} catch (Exception e) {
log.warn("解析文件名异常,使用原始名称:{},错误:{}", filePathUrl, e.getMessage());
return filePathUrl;
}
}
@PostConstruct @PostConstruct
public void init() { public void init() {
staticOssConfig = this.ossConfig; staticOssConfig = this.ossConfig;
...@@ -936,8 +980,5 @@ public static <T> List<T> readApExcelWithColumnConfig(InputStream inputStream, ...@@ -936,8 +980,5 @@ public static <T> List<T> readApExcelWithColumnConfig(InputStream inputStream,
log.info("从OSS URL解析通用文件名完成:{} → {}", ossUrl, fileName); log.info("从OSS URL解析通用文件名完成:{} → {}", ossUrl, fileName);
return fileName; return fileName;
} }
} }
...@@ -712,4 +712,94 @@ ...@@ -712,4 +712,94 @@
group by region_name) sp group by region_name) sp
on ar.region_name = sp.region_name order by ar.region_name on ar.region_name = sp.region_name order by ar.region_name
</select> </select>
<!-- 批量更新SQL -->
<update id="batchUpdate">
UPDATE sales_ap_display
<trim prefix="SET" suffixOverrides=",">
<!-- 实际主货架类型 -->
actual_main_shelf_type = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualMainShelfType}
</foreach>
END,
<!-- 实际主货架数量 -->
actual_main_shelf_qty = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualMainShelfQty}
</foreach>
END,
<!-- 实际主货架执行状态 -->
actual_main_shelf_executed = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualMainShelfExecuted}
</foreach>
END,
<!-- 实际端架数量 -->
actual_end_cap_qty = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualEndCapQty}
</foreach>
END,
<!-- 实际端架执行状态 -->
actual_end_cap_executed = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualEndCapExecuted}
</foreach>
END,
<!-- 实际堆头面积 -->
actual_floor_stack_area = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualFloorStackArea}
</foreach>
END,
<!-- 实际堆头数量 -->
actual_floor_stack_qty = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualFloorStackQty}
</foreach>
END,
<!-- 实际堆头执行状态 -->
actual_floor_stack_executed = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualFloorStackExecuted}
</foreach>
END,
<!-- 实际多点陈列 -->
actual_multi_display = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualMultiDisplay}
</foreach>
END,
<!-- 实际多点陈列执行状态 -->
actual_multi_display_executed = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualMultiDisplayExecuted}
</foreach>
END,
<!-- 实际挂条数量 -->
actual_hanging_strip_quantity_form = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.actualHangingStripQuantityForm}
</foreach>
END,
<!-- 挂条执行状态 -->
hanging_strip_executed = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.hangingStripExecuted}
</foreach>
END,
<!-- 备注 -->
remark = CASE
<foreach collection="list" item="item" separator="">
WHEN sad_id = #{item.sadId} THEN #{item.remark}
</foreach>
END
</trim>
<!-- 只更新传入的主键范围(避免全表更新) -->
WHERE sad_id IN
<foreach collection="list" item="item" open="(" separator="," close=")">
#{item.sadId}
</foreach>
</update>
</mapper> </mapper>
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论