Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
W
wangxiaolu-sfa-module-operation
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
sfa
wangxiaolu-sfa-module-operation
Commits
31c41f3b
提交
31c41f3b
authored
12月 16, 2025
作者:
douxy
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
增加店内执行计划导出/入功能:修复更新功能
上级
03bcdf82
隐藏空白字符变更
内嵌
并排
正在显示
4 个修改的文件
包含
168 行增加
和
154 行删除
+168
-154
SalesApDisplayVo.java
...ava/com/sfa/operation/pojo/sales/vo/SalesApDisplayVo.java
+0
-93
ImportExcelServiceImpl.java
...ion/service/sales/export/impl/ImportExcelServiceImpl.java
+55
-15
IImportApExcelStrategy.java
...va/com/sfa/operation/strategy/IImportApExcelStrategy.java
+6
-0
NormalDisplayImportStrategyImpl.java
...trategy/impl/imports/NormalDisplayImportStrategyImpl.java
+107
-46
没有找到文件。
src/main/java/com/sfa/operation/pojo/sales/vo/SalesApDisplayVo.java
deleted
100644 → 0
浏览文件 @
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
;
}
src/main/java/com/sfa/operation/service/sales/export/impl/ImportExcelServiceImpl.java
浏览文件 @
31c41f3b
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
,
re
ques
t
);
return
R
.
fail
(
0
,
re
sul
t
);
}
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
);
}
}
src/main/java/com/sfa/operation/strategy/IImportApExcelStrategy.java
浏览文件 @
31c41f3b
...
...
@@ -58,6 +58,12 @@ public interface IImportApExcelStrategy<T> {
*/
Map
<
String
,
Object
>
execute
(
String
flePathUrl
);
/**
* 获取导入目标DTO类
* @return 目标DTO类
*/
Class
<?>
getTargetDtoClass
();
/**
* 获取导入Sheet名称(默认方法)
*/
...
...
src/main/java/com/sfa/operation/strategy/impl/imports/NormalDisplayImportStrategyImpl.java
浏览文件 @
31c41f3b
...
...
@@ -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
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论