Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
H
HYH.APSJ
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
佟礼
HYH.APSJ
Commits
18808dc2
Commit
18808dc2
authored
Sep 24, 2026
by
DESKTOP-VKRD9QF\Administration
Browse files
Options
Browse Files
Download
Plain Diff
Merge origin/master and preserve KPI output changes
parents
a61e73b3
9e6f7171
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
228 additions
and
138 deletions
+228
-138
MacroPlannerResultController.java
...java/com/aps/controller/MacroPlannerResultController.java
+31
-43
MacroPlannerDataConverterRunner.java
...com/aps/macroplanner/MacroPlannerDataConverterRunner.java
+2
-2
MacroPlannerOptimizer.java
...main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
+19
-9
KpiAggregator.java
...n/java/com/aps/macroplanner/constraint/KpiAggregator.java
+11
-1
MacroPlannerModel.java
...in/java/com/aps/macroplanner/model/MacroPlannerModel.java
+20
-0
KpiLib.java
src/main/java/com/aps/macroplanner/objective/KpiLib.java
+20
-11
ObjectiveBuilder.java
...java/com/aps/macroplanner/objective/ObjectiveBuilder.java
+5
-5
StrategyLevel.java
...in/java/com/aps/macroplanner/objective/StrategyLevel.java
+9
-9
ResultWriter.java
src/main/java/com/aps/macroplanner/output/ResultWriter.java
+54
-24
SolutionPrinter.java
...ain/java/com/aps/macroplanner/output/SolutionPrinter.java
+25
-25
KpiResult.java
src/main/java/com/aps/macroplanner/output/dto/KpiResult.java
+3
-0
OptimizationResult.java
...a/com/aps/macroplanner/output/dto/OptimizationResult.java
+6
-0
RiskItem.java
src/main/java/com/aps/macroplanner/output/dto/RiskItem.java
+18
-6
SalesDemandResult.java
...va/com/aps/macroplanner/output/dto/SalesDemandResult.java
+5
-3
No files found.
src/main/java/com/aps/controller/MacroPlannerResultController.java
View file @
18808dc2
...
@@ -165,12 +165,30 @@ public class MacroPlannerResultController {
...
@@ -165,12 +165,30 @@ public class MacroPlannerResultController {
.
skip
((
long
)(
pageNumber
-
1
)*
pageSize
)
.
skip
((
long
)(
pageNumber
-
1
)*
pageSize
)
.
limit
(
pageSize
)
.
limit
(
pageSize
)
.
collect
(
Collectors
.
toList
());
.
collect
(
Collectors
.
toList
());
if
(
SalesDemandSummaries
!=
null
&&
SalesDemandSummaries
.
size
()>
0
)
{
List
<
String
>
SalesDemandids
=
SalesDemandSummaries
.
stream
()
.
map
(
SalesDemandResult:
:
getSalesDemandId
)
.
collect
(
Collectors
.
toList
());
List
<
RiskItem
>
Risks
=
getSalesDemandReason
(
sceneId
,
SalesDemandids
);
if
(
Risks
!=
null
&&
Risks
.
size
()>
0
)
{
for
(
SalesDemandResult
entry
:
SalesDemandSummaries
)
{
List
<
RiskItem
>
Risks1
=
Risks
.
stream
()
.
filter
(
t
->
t
.
getSalesDemandId
().
equals
(
entry
.
getSalesDemandId
())&&
t
.
getType
()==
1
)
.
collect
(
Collectors
.
toList
());
entry
.
setUnmetReasons
(
Risks1
);
List
<
RiskItem
>
Risks2
=
Risks
.
stream
()
.
filter
(
t
->
t
.
getSalesDemandId
().
equals
(
entry
.
getSalesDemandId
())&&
t
.
getType
()==
2
)
.
collect
(
Collectors
.
toList
());
entry
.
setRisks
(
Risks2
);
}
}
}
// List<Map<String, Object>> summaries = new ArrayList<>();
// for (SalesDemandResult entry : SalesDemandSummaries) {
//
// }
double
totalDemand
=
lists
.
stream
().
mapToDouble
(
SalesDemandResult:
:
getDemandQty
).
sum
();
double
totalDemand
=
lists
.
stream
().
mapToDouble
(
SalesDemandResult:
:
getDemandQty
).
sum
();
double
totalFulfilled
=
lists
.
stream
().
mapToDouble
(
SalesDemandResult:
:
getFulfilledQty
).
sum
();
double
totalFulfilled
=
lists
.
stream
().
mapToDouble
(
SalesDemandResult:
:
getFulfilledQty
).
sum
();
...
@@ -188,32 +206,19 @@ public class MacroPlannerResultController {
...
@@ -188,32 +206,19 @@ public class MacroPlannerResultController {
return
null
;
return
null
;
}
}
}
}
private
List
<
SalesDemandResult
>
getSalesDemandsResult
(
String
sceneId
,
String
salesDemandId
)
{
private
List
<
RiskItem
>
getSalesDemandReason
(
String
sceneId
,
List
<
String
>
salesDemandIds
)
{
try
{
try
{
FlatParquetUtil
parquetUtil
=
new
FlatParquetUtil
();
FlatParquetUtil
parquetUtil
=
new
FlatParquetUtil
();
ResultWriter
rw
=
new
ResultWriter
();
ResultWriter
rw
=
new
ResultWriter
();
List
<
FlatParquetUtil
.
FilterCondition
<?>>
filters
=
new
ArrayList
<>();
filters
.
add
(
FlatParquetUtil
.
FilterCondition
.
eq
(
"salesDemandId"
,
salesDemandId
));
List
<
FlatParquetUtil
.
SortCondition
>
sorts
=
Arrays
.
asList
(
String
salesDemandReasonPath
=
rw
.
getOptimizationSalesDemandReasons
(
sceneId
);
FlatParquetUtil
.
SortCondition
.
asc
(
"periodIndex"
)
);
List
<
RiskItem
>
list
=
parquetUtil
.
readAll
(
salesDemandReasonPath
,
RiskItem
.
class
,
oi
->
salesDemandIds
.
contains
(
oi
.
getSalesDemandId
()));
FlatParquetUtil
.
QueryRequest
request
=
new
FlatParquetUtil
.
QueryRequest
(
FlatParquetUtil
.
PageRequest
.
of
(
1
,
500
),
filters
,
return
list
;
sorts
);
FlatParquetUtil
.
PageResult
<
SalesDemandResult
>
result
=
parquetUtil
.
queryPage
(
rw
.
getOptimizationSalesDemand
(
sceneId
),
SalesDemandResult
.
class
,
request
);
List
<
SalesDemandResult
>
pispips
=
result
.
getRecords
();
return
pispips
;
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
return
null
;
return
null
;
}
}
...
@@ -678,25 +683,7 @@ public class MacroPlannerResultController {
...
@@ -678,25 +683,7 @@ public class MacroPlannerResultController {
}
}
}
}
private
void
normalizeUnmetReasons
(
List
<
SalesDemandResult
>
demands
)
{
for
(
SalesDemandResult
demand
:
demands
)
{
List
<
String
>
reasons
=
demand
.
getUnmetReasons
();
if
(
reasons
==
null
||
reasons
.
isEmpty
())
{
continue
;
}
List
<
String
>
normalized
=
new
ArrayList
<>();
for
(
String
reason
:
reasons
)
{
if
(
reason
!=
null
&&
reason
.
startsWith
(
"库存耗尽("
)
&&
reason
.
endsWith
(
")"
))
{
normalized
.
add
(
"库存耗尽"
);
normalized
.
add
(
reason
.
substring
(
"库存耗尽"
.
length
()));
}
else
{
normalized
.
add
(
reason
);
}
}
reasons
.
clear
();
reasons
.
addAll
(
normalized
);
}
}
private
void
enrichPispipProductCodes
(
List
<
PispipResult
>
pispips
)
{
private
void
enrichPispipProductCodes
(
List
<
PispipResult
>
pispips
)
{
if
(
pispips
==
null
||
pispips
.
isEmpty
())
{
if
(
pispips
==
null
||
pispips
.
isEmpty
())
{
...
@@ -1025,6 +1012,7 @@ public class MacroPlannerResultController {
...
@@ -1025,6 +1012,7 @@ public class MacroPlannerResultController {
entryMap
.
put
(
"rawValue"
,
String
.
format
(
"%.2f"
,
e
.
rawValue
));
entryMap
.
put
(
"rawValue"
,
String
.
format
(
"%.2f"
,
e
.
rawValue
));
entryMap
.
put
(
"weight"
,
e
.
weight
==
null
?
null
:
String
.
format
(
"%.1f"
,
e
.
weight
));
entryMap
.
put
(
"weight"
,
e
.
weight
==
null
?
null
:
String
.
format
(
"%.1f"
,
e
.
weight
));
entryMap
.
put
(
"penalty"
,
e
.
penalty
==
null
?
null
:
String
.
format
(
"%.2f"
,
e
.
penalty
));
entryMap
.
put
(
"penalty"
,
e
.
penalty
==
null
?
null
:
String
.
format
(
"%.2f"
,
e
.
penalty
));
entryMap
.
put
(
"contribution"
,
e
.
contribution
==
null
?
null
:
String
.
format
(
"%.2f"
,
e
.
contribution
));
kpiList
.
add
(
entryMap
);
kpiList
.
add
(
entryMap
);
}
}
kpiData
.
put
(
"entries"
,
kpiList
);
kpiData
.
put
(
"entries"
,
kpiList
);
...
...
src/main/java/com/aps/macroplanner/MacroPlannerDataConverterRunner.java
View file @
18808dc2
...
@@ -80,8 +80,8 @@ public class MacroPlannerDataConverterRunner {
...
@@ -80,8 +80,8 @@ public class MacroPlannerDataConverterRunner {
optimizer
.
solve
();
optimizer
.
solve
();
ResultWriter
writer
=
new
ResultWriter
(
optimizer
.
getModel
(),
optimizer
.
getData
(),
1
);
ResultWriter
writer
=
new
ResultWriter
(
optimizer
.
getModel
(),
optimizer
.
getData
(),
1
);
OptimizationResult
result
=
writer
.
buildResult
(
"Default"
);
OptimizationResult
result
=
writer
.
buildResult
(
sceneId
);
boolean
jsonPath
=
writer
.
saveResultToFile
(
"Default"
,
result
);
boolean
jsonPath
=
writer
.
saveResultToFile
(
sceneId
,
result
);
System
.
out
.
println
(
"===== MACROPLANNER DATA CONVERTER RUNNER END ====="
);
System
.
out
.
println
(
"===== MACROPLANNER DATA CONVERTER RUNNER END ====="
);
}
finally
{
}
finally
{
...
...
src/main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
View file @
18808dc2
...
@@ -286,7 +286,7 @@ public class MacroPlannerOptimizer {
...
@@ -286,7 +286,7 @@ public class MacroPlannerOptimizer {
// ========== 1. 前置参数校验 ==========
// ========== 1. 前置参数校验 ==========
List
<
KpiSetting
>
w
=
data
.
getKpiSettings
();
List
<
KpiSetting
>
w
=
data
.
getKpiSettings
();
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
if
(
w
==
null
)
{
if
(
w
==
null
||
w
.
size
()==
0
)
{
levels
=
defineLevels
(
data
.
getKpiWeights
());
levels
=
defineLevels
(
data
.
getKpiWeights
());
}
else
{
}
else
{
levels
=
getKpiLevels
(
w
);
levels
=
getKpiLevels
(
w
);
...
@@ -342,16 +342,18 @@ public class MacroPlannerOptimizer {
...
@@ -342,16 +342,18 @@ public class MacroPlannerOptimizer {
double
bestBound
=
model
.
getSolver
().
objective
().
bestBound
();
double
bestBound
=
model
.
getSolver
().
objective
().
bestBound
();
writeLog
(
" SCIP Status : %s%n"
,
status
);
writeLog
(
" SCIP Status : %s%n"
,
status
);
writeLog
(
" Solving Time (sec) : %.2f%n"
,
levelElapsedMs
/
1000.0
);
writeLog
(
" Solving Time (sec) : %.2f%n"
,
levelElapsedMs
/
1000.0
);
writeLog
(
"
Primal Bound
: %+.6e%n"
,
optimalValue
);
writeLog
(
"
Goal Score
: %+.6e%n"
,
optimalValue
);
writeLog
(
" Best Bound : %+.6e%n"
,
bestBound
);
writeLog
(
" Best Bound : %+.6e%n"
,
bestBound
);
double
gap
=
Math
.
abs
(
optimalValue
-
bestBound
)
/
Math
.
abs
(
optimalValue
)
*
100
;
double
gap
=
Math
.
abs
(
optimalValue
-
bestBound
)
/
Math
.
abs
(
optimalValue
)
*
100
;
writeLog
(
"gap:%.2f"
,
gap
);
writeLog
(
"gap:%.2f"
,
gap
);
// 输出当前层各KPI值
// 输出当前层各KPI值
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
double
kpiValue
=
kpi
.
variable
.
solutionValue
();
double
kpiValue
=
kpi
.
variable
.
solutionValue
();
double
penalty
=
kpi
.
effectiveCoefficient
()
*
kpiValue
;
double
contribution
=
kpi
.
effectiveCoefficient
()
*
kpiValue
;
writeLog
(
" %s: %.2f (系数=%.1f, 惩罚=%.2f)%n"
,
kpi
.
name
,
kpiValue
,
kpi
.
effectiveCoefficient
(),
penalty
);
System
.
out
.
printf
(
" %s: %.2f (系数=%+.1f, 得分贡献=%+.2f)%n"
,
kpi
.
name
,
kpiValue
,
kpi
.
effectiveCoefficient
(),
contribution
);
}
}
levelResults
.
add
(
new
LevelResult
(
level
,
optimalValue
,
status
));
levelResults
.
add
(
new
LevelResult
(
level
,
optimalValue
,
status
));
...
@@ -431,14 +433,19 @@ public class MacroPlannerOptimizer {
...
@@ -431,14 +433,19 @@ public class MacroPlannerOptimizer {
private
List
<
StrategyLevel
>
defineLevels
(
KPIWeights
w
)
{
private
List
<
StrategyLevel
>
defineLevels
(
KPIWeights
w
)
{
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
List
<
StrategyLevel
>
levels
=
new
ArrayList
<>();
// === Level 0: 需求满足 (最高优先级, 严格分层, slack=0%) ===
StrategyLevel
l0
=
new
StrategyLevel
(
0
,
"需求松弛"
,
0.0
);
l0
.
addKPI
(
"TotalSlack"
,
"需求松弛"
,
model
.
getTotalSlack
(),
1
);
levels
.
add
(
l0
);
// === Level 1: 需求满足 (最高优先级, 严格分层, slack=0%) ===
// === Level 1: 需求满足 (最高优先级, 严格分层, slack=0%) ===
StrategyLevel
l1
=
new
StrategyLevel
(
1
,
"需求满足"
,
0.0
);
StrategyLevel
l1
=
new
StrategyLevel
(
1
,
"需求满足"
,
0.0
);
l1
.
addKPI
(
KpiLib
.
Fulfillment
.
getEn
(),
KpiLib
.
Fulfillment
.
getCn
(),
model
.
getTotalFulfillment
(),
w
.
getFulfillmentWeight
());
l1
.
addKPI
(
KpiLib
.
Fulfillment
.
getEn
(),
KpiLib
.
Fulfillment
.
getCn
(),
model
.
getTotalFulfillment
(),
w
.
getFulfillmentWeight
()
,
true
);
levels
.
add
(
l1
);
levels
.
add
(
l1
);
// === Level 2: 产能约束 (物理硬约束, 严格分层, slack=0%) ===
// === Level 2: 产能约束 (物理硬约束, 严格分层, slack=0%) ===
StrategyLevel
l2
=
new
StrategyLevel
(
2
,
"产能约束"
,
0.0
);
StrategyLevel
l2
=
new
StrategyLevel
(
2
,
"产能约束"
,
0.0
);
l2
.
addKPI
(
KpiLib
.
UnitCapacity
.
getEn
(),
KpiLib
.
Fulfillment
.
getCn
(),
model
.
getTotalUnitCapacity
(),
w
.
getUnitCapacityWeight
());
l2
.
addKPI
(
KpiLib
.
UnitCapacity
.
getEn
(),
KpiLib
.
UnitCapacity
.
getCn
(),
model
.
getTotalUnitCapacity
(),
w
.
getUnitCapacityWeight
());
levels
.
add
(
l2
);
levels
.
add
(
l2
);
// === Level 3: 业务KPI (允许 5% 退化, slack=5%) ===
// === Level 3: 业务KPI (允许 5% 退化, slack=5%) ===
...
@@ -472,7 +479,10 @@ public class MacroPlannerOptimizer {
...
@@ -472,7 +479,10 @@ public class MacroPlannerOptimizer {
TreeMap:
:
new
,
TreeMap:
:
new
,
Collectors
.
toList
()
Collectors
.
toList
()
));
));
// === Level 0: 需求满足 (最高优先级, 严格分层, slack=0%) ===
StrategyLevel
l0
=
new
StrategyLevel
(
0
,
"需求松弛"
,
0.0
);
l0
.
addKPI
(
"TotalSlack"
,
"需求松弛"
,
model
.
getTotalSlack
(),
1
);
levels
.
add
(
l0
);
for
(
Map
.
Entry
<
Integer
,
List
<
KpiSetting
>>
entry
:
groupByLevelMap
.
entrySet
())
{
for
(
Map
.
Entry
<
Integer
,
List
<
KpiSetting
>>
entry
:
groupByLevelMap
.
entrySet
())
{
Integer
currentLevel
=
entry
.
getKey
();
Integer
currentLevel
=
entry
.
getKey
();
List
<
KpiSetting
>
kpiGroup
=
entry
.
getValue
();
List
<
KpiSetting
>
kpiGroup
=
entry
.
getValue
();
...
@@ -480,7 +490,7 @@ public class MacroPlannerOptimizer {
...
@@ -480,7 +490,7 @@ public class MacroPlannerOptimizer {
// 遍历当前分组里面每一个KPI
// 遍历当前分组里面每一个KPI
for
(
KpiSetting
kpi
:
kpiGroup
)
{
for
(
KpiSetting
kpi
:
kpiGroup
)
{
level
.
addKPI
(
kpi
.
code
,
kpi
.
name
,
model
.
getKpi
(
kpi
.
code
),
kpi
.
weight
);
level
.
addKPI
(
kpi
.
code
,
kpi
.
name
,
model
.
getKpi
(
kpi
.
code
),
kpi
.
weight
,
KpiLib
.
ofEn
(
kpi
.
code
).
getIsBenefit
()
);
}
}
...
...
src/main/java/com/aps/macroplanner/constraint/KpiAggregator.java
View file @
18808dc2
...
@@ -38,8 +38,12 @@ public class KpiAggregator {
...
@@ -38,8 +38,12 @@ public class KpiAggregator {
public
static
void
build
(
MacroPlannerModel
model
,
TestDataBuilder
data
)
{
public
static
void
build
(
MacroPlannerModel
model
,
TestDataBuilder
data
)
{
double
inf
=
MPSolver
.
infinity
();
double
inf
=
MPSolver
.
infinity
();
// TotalFulfillment = Σ
DemandSlack
// TotalFulfillment = Σ
SalesDemandQty (Quintiq fulfillment bonus KPI)
model
.
setTotalFulfillment
(
createSumKpi
(
model
,
"TotalFulfillment"
,
model
.
setTotalFulfillment
(
createSumKpi
(
model
,
"TotalFulfillment"
,
model
.
getSalesDemandQtyVars
()));
// TotalSlack = Σ DemandSlack (Quintiq Slack KPI, 高优先级惩罚)
model
.
setTotalSlack
(
createSumKpi
(
model
,
"TotalSlack"
,
model
.
getDemandSlackVars
()));
model
.
getDemandSlackVars
()));
// TotalLotSize = Σ PTLotSizeOver + Σ PTLotSizeUnder
// TotalLotSize = Σ PTLotSizeOver + Σ PTLotSizeUnder
...
@@ -66,10 +70,16 @@ public class KpiAggregator {
...
@@ -66,10 +70,16 @@ public class KpiAggregator {
model
.
setTotalUnitCapacity
(
createSumKpi
(
model
,
"TotalUnitCapacity"
,
model
.
setTotalUnitCapacity
(
createSumKpi
(
model
,
"TotalUnitCapacity"
,
model
.
getCapacityOverloadedVars
()));
model
.
getCapacityOverloadedVars
()));
// TotalMinimumUnitCapacity = Σ CapacityNotMet (Quintiq 最小产能未满足 KPI)
model
.
setTotalMinimumUnitCapacity
(
createSumKpi
(
model
,
"TotalMinUnitCapacity"
,
model
.
getCapacityNotMetVars
()));
// TotalSupplyTarget = Σ SupplyTargetQtyUnder
// TotalSupplyTarget = Σ SupplyTargetQtyUnder
model
.
setTotalSupplyTarget
(
createSumKpi
(
model
,
"TotalSupplyTarget"
,
model
.
setTotalSupplyTarget
(
createSumKpi
(
model
,
"TotalSupplyTarget"
,
model
.
getSupplyTargetQtyUnderVars
()));
model
.
getSupplyTargetQtyUnderVars
()));
// TotalMinSupply = Σ MinSupplyQtyUnder
// TotalMinSupply = Σ MinSupplyQtyUnder
model
.
setTotalMinSupply
(
createSumKpi
(
model
,
"TotalMinSupply"
,
model
.
setTotalMinSupply
(
createSumKpi
(
model
,
"TotalMinSupply"
,
model
.
getMinSupplyQtyUnderVars
()));
model
.
getMinSupplyQtyUnderVars
()));
...
...
src/main/java/com/aps/macroplanner/model/MacroPlannerModel.java
View file @
18808dc2
...
@@ -80,12 +80,19 @@ public class MacroPlannerModel {
...
@@ -80,12 +80,19 @@ public class MacroPlannerModel {
private
final
Map
<
String
,
MPVariable
>
ptLotSizeUnderVars
=
new
HashMap
<>();
private
final
Map
<
String
,
MPVariable
>
ptLotSizeUnderVars
=
new
HashMap
<>();
// ==================== KPI 汇总变量 ====================
// ==================== KPI 汇总变量 ====================
private
MPVariable
totalSlack
;
private
MPVariable
totalFulfillment
;
private
MPVariable
totalFulfillment
;
private
MPVariable
totalLotSize
;
private
MPVariable
totalLotSize
;
private
MPVariable
totalMaxInventoryLevel
;
private
MPVariable
totalMaxInventoryLevel
;
private
MPVariable
totalMinInventoryLevel
;
private
MPVariable
totalMinInventoryLevel
;
private
MPVariable
totalTargetInvLevel
;
private
MPVariable
totalTargetInvLevel
;
private
MPVariable
totalUnitCapacity
;
private
MPVariable
totalUnitCapacity
;
private
MPVariable
totalMinimumUnitCapacity
;
private
MPVariable
totalSupplyTarget
;
private
MPVariable
totalSupplyTarget
;
private
MPVariable
totalMinSupply
;
private
MPVariable
totalMinSupply
;
private
MPVariable
totalMaxSupply
;
private
MPVariable
totalMaxSupply
;
...
@@ -123,6 +130,7 @@ public class MacroPlannerModel {
...
@@ -123,6 +130,7 @@ public class MacroPlannerModel {
}
}
// ==================== KPI 变量 setter (由 KpiAggregator 调用) ====================
// ==================== KPI 变量 setter (由 KpiAggregator 调用) ====================
public
void
setTotalSlack
(
MPVariable
v
)
{
this
.
totalSlack
=
v
;
}
public
void
setTotalFulfillment
(
MPVariable
v
)
{
this
.
totalFulfillment
=
v
;
}
public
void
setTotalFulfillment
(
MPVariable
v
)
{
this
.
totalFulfillment
=
v
;
}
public
void
setTotalLotSize
(
MPVariable
v
)
{
this
.
totalLotSize
=
v
;
}
public
void
setTotalLotSize
(
MPVariable
v
)
{
this
.
totalLotSize
=
v
;
}
...
@@ -130,6 +138,9 @@ public class MacroPlannerModel {
...
@@ -130,6 +138,9 @@ public class MacroPlannerModel {
public
void
setTotalMinInventoryLevel
(
MPVariable
v
)
{
this
.
totalMinInventoryLevel
=
v
;
}
public
void
setTotalMinInventoryLevel
(
MPVariable
v
)
{
this
.
totalMinInventoryLevel
=
v
;
}
public
void
setTotalTargetInvLevel
(
MPVariable
v
)
{
this
.
totalTargetInvLevel
=
v
;
}
public
void
setTotalTargetInvLevel
(
MPVariable
v
)
{
this
.
totalTargetInvLevel
=
v
;
}
public
void
setTotalUnitCapacity
(
MPVariable
v
)
{
this
.
totalUnitCapacity
=
v
;
}
public
void
setTotalUnitCapacity
(
MPVariable
v
)
{
this
.
totalUnitCapacity
=
v
;
}
public
void
setTotalMinimumUnitCapacity
(
MPVariable
v
)
{
this
.
totalMinimumUnitCapacity
=
v
;
}
public
void
setTotalSupplyTarget
(
MPVariable
v
)
{
this
.
totalSupplyTarget
=
v
;
}
public
void
setTotalSupplyTarget
(
MPVariable
v
)
{
this
.
totalSupplyTarget
=
v
;
}
public
void
setTotalMinSupply
(
MPVariable
v
)
{
this
.
totalMinSupply
=
v
;
}
public
void
setTotalMinSupply
(
MPVariable
v
)
{
this
.
totalMinSupply
=
v
;
}
public
void
setTotalMaxSupply
(
MPVariable
v
)
{
this
.
totalMaxSupply
=
v
;
}
public
void
setTotalMaxSupply
(
MPVariable
v
)
{
this
.
totalMaxSupply
=
v
;
}
...
@@ -150,6 +161,8 @@ public class MacroPlannerModel {
...
@@ -150,6 +161,8 @@ public class MacroPlannerModel {
public
Map
<
String
,
MPVariable
>
getDemandFulfillmentVars
()
{
return
demandFulfillmentVars
;
}
public
Map
<
String
,
MPVariable
>
getDemandFulfillmentVars
()
{
return
demandFulfillmentVars
;
}
public
Map
<
String
,
MPVariable
>
getCapacityOverloadedVars
()
{
return
capacityOverloadedVars
;
}
public
Map
<
String
,
MPVariable
>
getCapacityOverloadedVars
()
{
return
capacityOverloadedVars
;
}
public
Map
<
String
,
MPVariable
>
getCapacityNotMetVars
()
{
return
capacityNotMetVars
;
}
public
Map
<
String
,
MPVariable
>
getCapacityNotMetVars
()
{
return
capacityNotMetVars
;
}
public
Map
<
String
,
MPVariable
>
getMinInvQtyUnderVars
()
{
return
minInvQtyUnderVars
;
}
public
Map
<
String
,
MPVariable
>
getMinInvQtyUnderVars
()
{
return
minInvQtyUnderVars
;
}
public
Map
<
String
,
MPVariable
>
getMaxInvQtyOverVars
()
{
return
maxInvQtyOverVars
;
}
public
Map
<
String
,
MPVariable
>
getMaxInvQtyOverVars
()
{
return
maxInvQtyOverVars
;
}
public
Map
<
String
,
MPVariable
>
getInvQtyUnderTargetVars
()
{
return
invQtyUnderTargetVars
;
}
public
Map
<
String
,
MPVariable
>
getInvQtyUnderTargetVars
()
{
return
invQtyUnderTargetVars
;
}
...
@@ -159,12 +172,16 @@ public class MacroPlannerModel {
...
@@ -159,12 +172,16 @@ public class MacroPlannerModel {
public
Map
<
String
,
MPVariable
>
getPtLotSizeOverVars
()
{
return
ptLotSizeOverVars
;
}
public
Map
<
String
,
MPVariable
>
getPtLotSizeOverVars
()
{
return
ptLotSizeOverVars
;
}
public
Map
<
String
,
MPVariable
>
getPtLotSizeUnderVars
()
{
return
ptLotSizeUnderVars
;
}
public
Map
<
String
,
MPVariable
>
getPtLotSizeUnderVars
()
{
return
ptLotSizeUnderVars
;
}
public
MPVariable
getTotalSlack
()
{
return
totalSlack
;
}
public
MPVariable
getTotalFulfillment
()
{
return
totalFulfillment
;
}
public
MPVariable
getTotalFulfillment
()
{
return
totalFulfillment
;
}
public
MPVariable
getTotalLotSize
()
{
return
totalLotSize
;
}
public
MPVariable
getTotalLotSize
()
{
return
totalLotSize
;
}
public
MPVariable
getTotalMaxInventoryLevel
()
{
return
totalMaxInventoryLevel
;
}
public
MPVariable
getTotalMaxInventoryLevel
()
{
return
totalMaxInventoryLevel
;
}
public
MPVariable
getTotalMinInventoryLevel
()
{
return
totalMinInventoryLevel
;
}
public
MPVariable
getTotalMinInventoryLevel
()
{
return
totalMinInventoryLevel
;
}
public
MPVariable
getTotalTargetInvLevel
()
{
return
totalTargetInvLevel
;
}
public
MPVariable
getTotalTargetInvLevel
()
{
return
totalTargetInvLevel
;
}
public
MPVariable
getTotalUnitCapacity
()
{
return
totalUnitCapacity
;
}
public
MPVariable
getTotalUnitCapacity
()
{
return
totalUnitCapacity
;
}
public
MPVariable
getTotalMinimumUnitCapacity
()
{
return
totalMinimumUnitCapacity
;
}
public
MPVariable
getTotalSupplyTarget
()
{
return
totalSupplyTarget
;
}
public
MPVariable
getTotalSupplyTarget
()
{
return
totalSupplyTarget
;
}
public
MPVariable
getTotalMinSupply
()
{
return
totalMinSupply
;
}
public
MPVariable
getTotalMinSupply
()
{
return
totalMinSupply
;
}
public
MPVariable
getTotalMaxSupply
()
{
return
totalMaxSupply
;
}
public
MPVariable
getTotalMaxSupply
()
{
return
totalMaxSupply
;
}
...
@@ -183,6 +200,9 @@ public class MacroPlannerModel {
...
@@ -183,6 +200,9 @@ public class MacroPlannerModel {
case
UnitCapacity:
case
UnitCapacity:
kpivar
=
totalUnitCapacity
;
kpivar
=
totalUnitCapacity
;
break
;
break
;
case
MinUnitCapacity:
kpivar
=
totalMinimumUnitCapacity
;
break
;
case
LotSize:
case
LotSize:
kpivar
=
totalLotSize
;
kpivar
=
totalLotSize
;
break
;
break
;
...
...
src/main/java/com/aps/macroplanner/objective/KpiLib.java
View file @
18808dc2
...
@@ -9,23 +9,28 @@ import java.util.stream.Collectors;
...
@@ -9,23 +9,28 @@ import java.util.stream.Collectors;
* 时间:2026-09-22
* 时间:2026-09-22
*/
*/
public
enum
KpiLib
{
public
enum
KpiLib
{
Fulfillment
(
"需求满足"
),
Fulfillment
(
"需求满足"
,
true
),
UnitCapacity
(
"产能"
),
UnitCapacity
(
"产能"
,
false
),
LotSize
(
"批次"
),
MinUnitCapacity
(
"最小化产能"
,
false
),
TargetInvLevel
(
"目标库存"
),
LotSize
(
"批次"
,
false
),
SupplyTarget
(
"供应目标"
),
TargetInvLevel
(
"目标库存"
,
false
),
SalesDemandPriority
(
"销售优先级"
),
SupplyTarget
(
"供应目标"
,
false
),
MaxInventoryLevel
(
"最大库存"
),
SalesDemandPriority
(
"销售优先级"
,
false
),
MinInventoryLevel
(
"最小库存"
),
MaxInventoryLevel
(
"最大库存"
,
false
),
MinSupply
(
"最小供应"
),
MinInventoryLevel
(
"最小库存"
,
false
),
MaxSupply
(
"最大供应"
);
MinSupply
(
"最小供应"
,
false
),
MaxSupply
(
"最大供应"
,
false
);
private
final
String
en
;
private
final
String
en
;
private
final
String
cn
;
private
final
String
cn
;
KpiLib
(
String
cn
)
{
/** 是否为收益 KPI(越大越好)。 */
public
final
boolean
isBenefit
;
KpiLib
(
String
cn
,
boolean
isBenefit
)
{
this
.
en
=
this
.
name
();
this
.
en
=
this
.
name
();
this
.
cn
=
cn
;
this
.
cn
=
cn
;
this
.
isBenefit
=
isBenefit
;
}
}
public
String
getEn
()
{
public
String
getEn
()
{
...
@@ -36,6 +41,10 @@ public enum KpiLib {
...
@@ -36,6 +41,10 @@ public enum KpiLib {
return
cn
;
return
cn
;
}
}
public
boolean
getIsBenefit
()
{
return
isBenefit
;
}
/**
/**
* 转为 Map<英文编码,中文名称>,用于批量翻译、表头转换
* 转为 Map<英文编码,中文名称>,用于批量翻译、表头转换
*/
*/
...
...
src/main/java/com/aps/macroplanner/objective/ObjectiveBuilder.java
View file @
18808dc2
...
@@ -168,18 +168,18 @@ public class ObjectiveBuilder {
...
@@ -168,18 +168,18 @@ public class ObjectiveBuilder {
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
objective
.
setCoefficient
(
kpi
.
variable
,
kpi
.
effectiveCoefficient
());
objective
.
setCoefficient
(
kpi
.
variable
,
kpi
.
effectiveCoefficient
());
}
}
objective
.
setM
in
imization
();
objective
.
setM
ax
imization
();
}
}
/**
/**
* 添加层级边界约束 — 限制上层
目标值不超过最优值 × (1 + slack)
。
* 添加层级边界约束 — 限制上层
得分不低于最优值减去允许松弛
。
*
*
* <p>该约束确保在求解下层 KPI 时, 上层 KPI 不会退化超过允许范围。
* <p>该约束确保在求解下层 KPI 时, 上层 KPI 不会退化超过允许范围。
* 对应 Quintiq 中 StrategyLevel 的 HierarchicalSolver 约束。</p>
* 对应 Quintiq 中 StrategyLevel 的 HierarchicalSolver 约束。</p>
*
*
* <h3>数学公式</h3>
* <h3>数学公式</h3>
* <pre>
* <pre>
* Σ (effectiveCoeff × KPI_variable)
≤ optimalValue + |optimalValue| × relativeGoalSlack
* Σ (effectiveCoeff × KPI_variable)
≥ optimalValue - |optimalValue × relativeGoalSlack|
* </pre>
* </pre>
*
*
* @param model 模型容器
* @param model 模型容器
...
@@ -192,10 +192,10 @@ public class ObjectiveBuilder {
...
@@ -192,10 +192,10 @@ public class ObjectiveBuilder {
if
(
level
.
getRelativeGoalSlack
()
<
0.0
)
return
;
// 负松弛表示不约束
if
(
level
.
getRelativeGoalSlack
()
<
0.0
)
return
;
// 负松弛表示不约束
MPSolver
solver
=
model
.
getSolver
();
MPSolver
solver
=
model
.
getSolver
();
double
upperBound
=
computeUpperBound
(
optimalValue
,
level
.
getRelativeGoalSlack
());
double
lowerBound
=
optimalValue
-
Math
.
abs
(
optimalValue
*
level
.
getRelativeGoalSlack
());
MPConstraint
bound
=
solver
.
makeConstraint
(
MPConstraint
bound
=
solver
.
makeConstraint
(
-
MPSolver
.
infinity
(),
upperBound
,
lowerBound
,
MPSolver
.
infinity
()
,
"HierLevel"
+
level
.
getLevel
()
+
"_Bound"
);
"HierLevel"
+
level
.
getLevel
()
+
"_Bound"
);
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
for
(
StrategyLevel
.
KPIEntry
kpi
:
level
.
getKpis
())
{
...
...
src/main/java/com/aps/macroplanner/objective/StrategyLevel.java
View file @
18808dc2
...
@@ -60,8 +60,8 @@ public class StrategyLevel {
...
@@ -60,8 +60,8 @@ public class StrategyLevel {
/** 该 KPI 在当前层级内的权重 */
/** 该 KPI 在当前层级内的权重 */
public
final
double
weight
;
public
final
double
weight
;
/** 是否为
负向 KPI (越小越好 = 正常惩罚项; false = 正常惩罚项)
*/
/** 是否为
收益 KPI(越大越好)。
*/
public
final
boolean
is
Negative
;
public
final
boolean
is
Benefit
;
/** 编号 */
/** 编号 */
public
final
String
code
;
public
final
String
code
;
...
@@ -69,20 +69,20 @@ public class StrategyLevel {
...
@@ -69,20 +69,20 @@ public class StrategyLevel {
/** KPI 名称 (用于日志) */
/** KPI 名称 (用于日志) */
public
final
String
name
;
public
final
String
name
;
public
KPIEntry
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
is
Negative
)
{
public
KPIEntry
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
is
Benefit
)
{
this
.
code
=
code
;
this
.
code
=
code
;
this
.
name
=
name
;
this
.
name
=
name
;
this
.
variable
=
variable
;
this
.
variable
=
variable
;
this
.
weight
=
weight
;
this
.
weight
=
weight
;
this
.
is
Negative
=
isNegative
;
this
.
is
Benefit
=
isBenefit
;
}
}
/**
/**
* 计算该 KPI 在目标函数中的实际系数。
* 计算该 KPI 在目标函数中的实际系数。
*
负向 KPI (如 SalesDemandPriority) 使用负系数实现最大化
。
*
收益 KPI 使用正系数;惩罚 KPI 使用负系数
。
*/
*/
public
double
effectiveCoefficient
()
{
public
double
effectiveCoefficient
()
{
return
is
Negative
?
-
weight
:
weight
;
return
is
Benefit
?
weight
:
-
weight
;
}
}
}
}
...
@@ -116,11 +116,11 @@ public class StrategyLevel {
...
@@ -116,11 +116,11 @@ public class StrategyLevel {
* @param name KPI 名称
* @param name KPI 名称
* @param variable KPI 汇总变量
* @param variable KPI 汇总变量
* @param weight 权重 (0 = 跳过)
* @param weight 权重 (0 = 跳过)
* @param is
Negative 是否为负向 KPI (true = 最大化, 使用负系数)
* @param is
Benefit 是否为收益 KPI(true = 最大化得分中的正系数)
*/
*/
public
void
addKPI
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
isNegative
)
{
public
void
addKPI
(
String
code
,
String
name
,
MPVariable
variable
,
double
weight
,
boolean
isBenefit
)
{
if
(
weight
>
0.0
&&
variable
!=
null
)
{
if
(
weight
>
0.0
&&
variable
!=
null
)
{
kpis
.
add
(
new
KPIEntry
(
code
,
name
,
variable
,
weight
,
is
Negative
));
kpis
.
add
(
new
KPIEntry
(
code
,
name
,
variable
,
weight
,
is
Benefit
));
}
}
}
}
...
...
src/main/java/com/aps/macroplanner/output/ResultWriter.java
View file @
18808dc2
...
@@ -143,6 +143,12 @@ public class ResultWriter {
...
@@ -143,6 +143,12 @@ public class ResultWriter {
String
fileName
=
resultDir
.
getAbsolutePath
()+
"\\salesdemand.parquet"
;
String
fileName
=
resultDir
.
getAbsolutePath
()+
"\\salesdemand.parquet"
;
return
fileName
;
return
fileName
;
}
}
public
String
getOptimizationSalesDemandReasons
(
String
sceneId
)
{
File
resultDir
=
getResultDirectory
(
sceneId
);
String
fileName
=
resultDir
.
getAbsolutePath
()+
"\\salesdemandReasons.parquet"
;
return
fileName
;
}
public
String
getOptimizationSalesDemandSummarie
(
String
sceneId
)
{
public
String
getOptimizationSalesDemandSummarie
(
String
sceneId
)
{
File
resultDir
=
getResultDirectory
(
sceneId
);
File
resultDir
=
getResultDirectory
(
sceneId
);
String
fileName
=
resultDir
.
getAbsolutePath
()+
"\\salesdemandsummarie.parquet"
;
String
fileName
=
resultDir
.
getAbsolutePath
()+
"\\salesdemandsummarie.parquet"
;
...
@@ -213,6 +219,11 @@ public class ResultWriter {
...
@@ -213,6 +219,11 @@ public class ResultWriter {
writeLog
(
"writesalesDemand"
);
writeLog
(
"writesalesDemand"
);
String
salesDemandPath
=
getOptimizationSalesDemand
(
sceneId
);
String
salesDemandPath
=
getOptimizationSalesDemand
(
sceneId
);
parquetUtil
.
write
(
result
.
getSalesDemands
(),
salesDemandPath
,
SalesDemandResult
.
class
);
parquetUtil
.
write
(
result
.
getSalesDemands
(),
salesDemandPath
,
SalesDemandResult
.
class
);
String
salesDemandReasonPath
=
getOptimizationSalesDemandReasons
(
sceneId
);
parquetUtil
.
write
(
result
.
getSaleDemandReasons
(),
salesDemandReasonPath
,
RiskItem
.
class
);
// List<PispipResult> pispips = parquetUtil.readAll(pispiPath, PispipResult.class);
// List<PispipResult> pispips = parquetUtil.readAll(pispiPath, PispipResult.class);
String
salesDemandSummariePath
=
getOptimizationSalesDemandSummarie
(
sceneId
);
String
salesDemandSummariePath
=
getOptimizationSalesDemandSummarie
(
sceneId
);
parquetUtil
.
write
(
result
.
getSaleSummarieDemands
(),
salesDemandSummariePath
,
SalesDemandResult
.
class
);
parquetUtil
.
write
(
result
.
getSaleSummarieDemands
(),
salesDemandSummariePath
,
SalesDemandResult
.
class
);
...
@@ -529,7 +540,9 @@ public class ResultWriter {
...
@@ -529,7 +540,9 @@ public class ResultWriter {
writeLog
(
"PeriodTasks"
);
writeLog
(
"PeriodTasks"
);
buildPeriodTasks
(
sceneId
,
result
);
buildPeriodTasks
(
sceneId
,
result
);
writeLog
(
"SalesDemands"
);
writeLog
(
"SalesDemands"
);
buildSalesDemands
(
result
);
List
<
RiskItem
>
unmetReasons
=
new
ArrayList
<>();
buildSalesDemands
(
result
,
unmetReasons
);
result
.
setSaleDemandReasons
(
unmetReasons
);
writeLog
(
"Pispips"
);
writeLog
(
"Pispips"
);
buildPispips
(
result
);
buildPispips
(
result
);
writeLog
(
"UnitCapacitie"
);
writeLog
(
"UnitCapacitie"
);
...
@@ -713,7 +726,7 @@ public class ResultWriter {
...
@@ -713,7 +726,7 @@ public class ResultWriter {
// ==================== SalesDemandResult ====================
// ==================== SalesDemandResult ====================
private
void
buildSalesDemands
(
OptimizationResult
result
)
{
private
void
buildSalesDemands
(
OptimizationResult
result
,
List
<
RiskItem
>
unmetReasons
)
{
for
(
SalesDemand
sd
:
data
.
getSalesDemands
())
{
for
(
SalesDemand
sd
:
data
.
getSalesDemands
())
{
SalesDemandResult
sr
=
new
SalesDemandResult
();
SalesDemandResult
sr
=
new
SalesDemandResult
();
sr
.
setSalesDemandId
(
sd
.
getDemandOrderId
()==
null
?
sd
.
getKey
():
sd
.
getDemandOrderId
());
sr
.
setSalesDemandId
(
sd
.
getDemandOrderId
()==
null
?
sd
.
getKey
():
sd
.
getDemandOrderId
());
...
@@ -740,10 +753,10 @@ public class ResultWriter {
...
@@ -740,10 +753,10 @@ public class ResultWriter {
sr
.
setDemandSlack
(
solutionValue
(
model
.
getDemandSlackVars
(),
invKey
).
setScale
(
3
,
RoundingMode
.
HALF_UP
).
doubleValue
());
sr
.
setDemandSlack
(
solutionValue
(
model
.
getDemandSlackVars
(),
invKey
).
setScale
(
3
,
RoundingMode
.
HALF_UP
).
doubleValue
());
// 未完成原因分析
// 未完成原因分析
if
(
unmet
>
0.001
)
{
if
(
unmet
>
0.001
)
{
// analyzeUnmetReasons(sd, sr.getUnmetReasons()
);
analyzeUnmetReasons
(
sd
,
unmetReasons
);
}
}
// 风险分析
// 风险分析
// analyzeRisks(sd, sr.getRisks()
);
analyzeRisks
(
sd
,
unmetReasons
);
result
.
getSalesDemands
().
add
(
sr
);
result
.
getSalesDemands
().
add
(
sr
);
}
}
...
@@ -782,7 +795,7 @@ public class ResultWriter {
...
@@ -782,7 +795,7 @@ public class ResultWriter {
* <li>无生产工序: 纯采购品, 依赖库存/在途</li>
* <li>无生产工序: 纯采购品, 依赖库存/在途</li>
* </ol>
* </ol>
*/
*/
private
void
analyzeUnmetReasons
(
SalesDemand
sd
,
java
.
util
.
List
<
String
>
reasons
)
{
private
void
analyzeUnmetReasons
(
SalesDemand
sd
,
List
<
RiskItem
>
reasons
)
{
Product
prod
=
sd
.
getProduct
();
Product
prod
=
sd
.
getProduct
();
StockingPoint
sp
=
sd
.
getStockingPoint
();
StockingPoint
sp
=
sd
.
getStockingPoint
();
Period
p
=
sd
.
getPeriod
();
Period
p
=
sd
.
getPeriod
();
...
@@ -804,22 +817,25 @@ public class ResultWriter {
...
@@ -804,22 +817,25 @@ public class ResultWriter {
}
}
double
available
=
openingInv
+
totalArrived
.
setScale
(
2
,
RoundingMode
.
HALF_UP
).
doubleValue
();
double
available
=
openingInv
+
totalArrived
.
setScale
(
2
,
RoundingMode
.
HALF_UP
).
doubleValue
();
if
(
available
<
demandQty
)
{
if
(
available
<
demandQty
)
{
addReasonWithDetail
(
reasons
,
"库存耗尽"
,
addReasonWithDetail
(
reasons
,
sd
,
"库存耗尽"
,
"(可用"
+
fmt
(
available
)
+
"件, 需求"
+
fmt
(
demandQty
)
+
"件)"
);
"(可用"
+
fmt
(
available
)
+
"件, 需求"
+
fmt
(
demandQty
)
+
"件)"
);
}
}
// 2. 产能瓶颈: 所有生产该产品的单元满负荷 (利用率 > 95%)
// 2. 产能瓶颈: 所有生产该产品的单元满负荷 (利用率 > 95%)
boolean
hasAnyUnit
=
false
;
boolean
hasAnyUnit
=
false
;
boolean
allUnitsAtMax
=
true
;
boolean
allUnitsAtMax
=
true
;
String
unitnames
=
""
;
for
(
Operation
op
:
data
.
getOperationsProducing
(
prod
.
getId
(),
sp
.
getId
()))
{
for
(
Operation
op
:
data
.
getOperationsProducing
(
prod
.
getId
(),
sp
.
getId
()))
{
for
(
UnitOperation
uo
:
op
.
getUnitOperations
())
{
for
(
UnitOperation
uo
:
op
.
getUnitOperations
())
{
hasAnyUnit
=
true
;
hasAnyUnit
=
true
;
UnitPeriod
up
=
data
.
getUnitPeriod
(
uo
.
getUnitId
(),
p
);
UnitPeriod
up
=
data
.
getUnitPeriod
(
uo
.
getUnitId
(),
p
);
if
(
up
==
null
||
up
.
isUnlimited
())
continue
;
if
(
up
==
null
||
up
.
isUnlimited
())
continue
;
BigDecimal
totalUsed
=
BigDecimal
.
ZERO
;
BigDecimal
totalUsed
=
BigDecimal
.
ZERO
;
for
(
Operation
unitOp
:
data
.
getOperationsByUnitId
(
uo
.
getUnitId
()))
{
for
(
Operation
unitOp
:
data
.
getOperationsByUnitId
(
uo
.
getUnitId
()))
{
for
(
UnitOperation
unitUo
:
unitOp
.
getUnitOperations
())
{
for
(
UnitOperation
unitUo
:
unitOp
.
getUnitOperations
())
{
if
(!
unitUo
.
getUnitId
().
equals
(
uo
.
getUnitId
()))
continue
;
if
(!
unitUo
.
getUnitId
().
equals
(
uo
.
getUnitId
()))
continue
;
unitnames
+=
unitUo
.
getUnitName
()+
";"
;
totalUsed
=
totalUsed
.
add
(
solutionValue
(
model
.
getPtQtyVars
(),
totalUsed
=
totalUsed
.
add
(
solutionValue
(
model
.
getPtQtyVars
(),
unitOp
.
ptQtyKey
(
unitUo
,
p
.
getIndex
())).
multiply
(
BigDecimal
.
valueOf
(
unitUo
.
getCapacityCoeff
())
)
);
unitOp
.
ptQtyKey
(
unitUo
,
p
.
getIndex
())).
multiply
(
BigDecimal
.
valueOf
(
unitUo
.
getCapacityCoeff
())
)
);
}
}
...
@@ -830,7 +846,8 @@ public class ResultWriter {
...
@@ -830,7 +846,8 @@ public class ResultWriter {
if
(!
allUnitsAtMax
)
break
;
if
(!
allUnitsAtMax
)
break
;
}
}
if
(
hasAnyUnit
&&
allUnitsAtMax
)
{
if
(
hasAnyUnit
&&
allUnitsAtMax
)
{
reasons
.
add
(
"产能瓶颈(所有产线满负荷)"
);
addReasonWithDetail
(
reasons
,
sd
,
"产能瓶颈(所有产线满负荷)"
,
unitnames
);
}
}
// 3. 原材料短缺: BOM 输入物料上期库存见底
// 3. 原材料短缺: BOM 输入物料上期库存见底
...
@@ -845,8 +862,12 @@ public class ResultWriter {
...
@@ -845,8 +862,12 @@ public class ResultWriter {
InventorySpec
inputSpec
=
data
.
getInventorySpecFor
(
inputProd
,
inputSp
,
p
);
InventorySpec
inputSpec
=
data
.
getInventorySpecFor
(
inputProd
,
inputSp
,
p
);
double
minInv
=
inputSpec
!=
null
?
inputSpec
.
getMinLevel
()
:
0
;
double
minInv
=
inputSpec
!=
null
?
inputSpec
.
getMinLevel
()
:
0
;
if
(
prevInputInv
<=
minInv
+
0.001
)
{
if
(
prevInputInv
<=
minInv
+
0.001
)
{
String
r
=
"原材料短缺("
+
inputProd
.
getId
()
+
"库存见底)"
;
String
r
=
"原材料短缺("
+
inputProd
.
getCode
()
+
"库存见底)"
;
if
(!
reasons
.
contains
(
r
))
reasons
.
add
(
r
);
if
(!
reasons
.
contains
(
r
))
{
addReasonWithDetail
(
reasons
,
sd
,
"原材料短缺"
,
r
);
}
}
}
}
}
}
}
...
@@ -861,8 +882,10 @@ public class ResultWriter {
...
@@ -861,8 +882,10 @@ public class ResultWriter {
totalProduced
=
totalProduced
.
add
(
solutionValue
(
model
.
getPtQtyVars
(),
op
.
ptQtyKey
(
uo
,
p
.
getIndex
())));
totalProduced
=
totalProduced
.
add
(
solutionValue
(
model
.
getPtQtyVars
(),
op
.
ptQtyKey
(
uo
,
p
.
getIndex
())));
}
}
if
(
totalProduced
.
doubleValue
()
>
0.001
)
{
if
(
totalProduced
.
doubleValue
()
>
0.001
)
{
reasons
.
add
(
"提前期限制(生产"
+
fmt
(
totalProduced
.
doubleValue
())
+
"件, 需"
String
r
=
"提前期限制(生产"
+
fmt
(
totalProduced
.
doubleValue
())
+
"件, 需"
+
op
.
getLeadTimeDays
()
+
"天后到货)"
);
+
op
.
getLeadTimeDays
()
+
"天后到货)"
;
addReasonWithDetail
(
reasons
,
sd
,
"提前期限制"
,
r
);
}
}
}
}
}
}
...
@@ -870,17 +893,23 @@ public class ResultWriter {
...
@@ -870,17 +893,23 @@ public class ResultWriter {
// 5. 无生产工序
// 5. 无生产工序
boolean
hasOperation
=
!
data
.
getOperationsProducingProduct
(
prod
.
getId
()).
isEmpty
();
boolean
hasOperation
=
!
data
.
getOperationsProducingProduct
(
prod
.
getId
()).
isEmpty
();
if
(!
hasOperation
)
{
if
(!
hasOperation
)
{
reasons
.
add
(
"无生产工序(纯采购品, 依赖库存/在途)"
);
addReasonWithDetail
(
reasons
,
sd
,
"无生产工序"
,
"无生产工序(纯采购品, 依赖库存/在途)"
);
}
}
if
(
reasons
.
isEmpty
())
{
if
(
reasons
.
isEmpty
())
{
reasons
.
add
(
"供需缺口(可用库存不足)"
);
addReasonWithDetail
(
reasons
,
sd
,
"供需缺口"
,
"供需缺口(可用库存不足)"
);
}
}
}
}
private
void
addReasonWithDetail
(
java
.
util
.
List
<
String
>
reasons
,
String
title
,
String
detail
)
{
reasons
.
add
(
title
);
reasons
.
add
(
detail
);
private
void
addReasonWithDetail
(
java
.
util
.
List
<
RiskItem
>
reasons
,
SalesDemand
sd
,
String
title
,
String
detail
)
{
addReasonWithDetail
(
reasons
,
sd
,
1
,
""
,
title
,
detail
);
}
private
void
addReasonWithDetail
(
java
.
util
.
List
<
RiskItem
>
reasons
,
SalesDemand
sd
,
int
type
,
String
level
,
String
title
,
String
detail
)
{
RiskItem
reason
=
new
RiskItem
(
sd
.
getDemandOrderId
(),
sd
.
getPeriod
().
getIndex
(),
type
,
level
,
title
,
detail
);
reasons
.
add
(
reason
);
}
}
/**
/**
...
@@ -905,7 +934,7 @@ public class ResultWriter {
...
@@ -905,7 +934,7 @@ public class ResultWriter {
double
endingInv
=
solutionValue
(
model
.
getInvQtyVars
(),
invKey
).
setScale
(
3
,
RoundingMode
.
HALF_UP
).
doubleValue
();
double
endingInv
=
solutionValue
(
model
.
getInvQtyVars
(),
invKey
).
setScale
(
3
,
RoundingMode
.
HALF_UP
).
doubleValue
();
InventorySpec
spec
=
data
.
getInventorySpecFor
(
prod
,
sp
,
p
);
InventorySpec
spec
=
data
.
getInventorySpecFor
(
prod
,
sp
,
p
);
if
(
spec
!=
null
&&
spec
.
hasMinLevel
()
&&
endingInv
<
spec
.
getMinLevel
()
-
0.001
)
{
if
(
spec
!=
null
&&
spec
.
hasMinLevel
()
&&
endingInv
<
spec
.
getMinLevel
()
-
0.001
)
{
addRisk
(
risks
,
"高"
,
"安全库存被消耗(期末"
+
fmt
(
endingInv
)
+
"件 < 最小"
+
fmt
(
spec
.
getMinLevel
())
+
"件)"
);
addRisk
(
risks
,
sd
,
"高"
,
"安全库存被消耗(期末"
+
fmt
(
endingInv
)
+
"件 < 最小"
+
fmt
(
spec
.
getMinLevel
())
+
"件)"
);
}
}
// 2. 产线高负荷 (> 90%)
// 2. 产线高负荷 (> 90%)
...
@@ -926,7 +955,7 @@ public class ResultWriter {
...
@@ -926,7 +955,7 @@ public class ResultWriter {
.
doubleValue
()
.
doubleValue
()
:
0
D
;
if
(
util
>
0.90
)
{
:
0
D
;
if
(
util
>
0.90
)
{
String
level
=
util
>
0.95
?
"高"
:
"低"
;
String
level
=
util
>
0.95
?
"高"
:
"低"
;
addRisk
(
risks
,
level
,
"产线高负荷("
+
uo
.
getUnitId
()
+
"利用率"
addRisk
(
risks
,
sd
,
level
,
"产线高负荷("
+
uo
.
getUnitId
()
+
"利用率"
+
String
.
format
(
"%.0f%%"
,
util
*
100
)
+
")"
);
+
String
.
format
(
"%.0f%%"
,
util
*
100
)
+
")"
);
}
}
}
}
...
@@ -938,7 +967,7 @@ public class ResultWriter {
...
@@ -938,7 +967,7 @@ public class ResultWriter {
unitCount
+=
op
.
getUnitOperations
().
size
();
unitCount
+=
op
.
getUnitOperations
().
size
();
}
}
if
(
unitCount
==
1
)
{
if
(
unitCount
==
1
)
{
addRisk
(
risks
,
"低"
,
"单一供应源(仅1条产线可生产)"
);
addRisk
(
risks
,
sd
,
"低"
,
"单一供应源(仅1条产线可生产)"
);
}
}
// 4. 原材料提前期长: 关键 BOM 物料 leadTime 长
// 4. 原材料提前期长: 关键 BOM 物料 leadTime 长
...
@@ -948,7 +977,7 @@ public class ResultWriter {
...
@@ -948,7 +977,7 @@ public class ResultWriter {
Product
inputProd
=
input
.
getInputProduct
();
Product
inputProd
=
input
.
getInputProduct
();
for
(
Operation
upOp
:
data
.
getOperationsProducingProduct
(
inputProd
.
getId
()))
{
for
(
Operation
upOp
:
data
.
getOperationsProducingProduct
(
inputProd
.
getId
()))
{
if
(
upOp
.
getLeadTimeDays
()
>=
2
)
{
if
(
upOp
.
getLeadTimeDays
()
>=
2
)
{
addRisk
(
risks
,
"低"
,
"原材料提前期长("
+
inputProd
.
getId
()
+
"采购需"
addRisk
(
risks
,
sd
,
"低"
,
"原材料提前期长("
+
inputProd
.
getId
()
+
"采购需"
+
upOp
.
getLeadTimeDays
()
+
"天)"
);
+
upOp
.
getLeadTimeDays
()
+
"天)"
);
}
}
}
}
...
@@ -959,16 +988,16 @@ public class ResultWriter {
...
@@ -959,16 +988,16 @@ public class ResultWriter {
double
fulfilled
=
solutionValue
(
model
.
getSalesDemandQtyVars
(),
sd
.
getKey
()).
setScale
(
3
,
RoundingMode
.
HALF_UP
).
doubleValue
();
double
fulfilled
=
solutionValue
(
model
.
getSalesDemandQtyVars
(),
sd
.
getKey
()).
setScale
(
3
,
RoundingMode
.
HALF_UP
).
doubleValue
();
double
rate
=
sd
.
getQuantity
()
>
0
?
fulfilled
/
sd
.
getQuantity
()
:
1.0
;
double
rate
=
sd
.
getQuantity
()
>
0
?
fulfilled
/
sd
.
getQuantity
()
:
1.0
;
if
(
sd
.
getPriority
()
>
1.0
&&
rate
<
0.999
)
{
if
(
sd
.
getPriority
()
>
1.0
&&
rate
<
0.999
)
{
addRisk
(
risks
,
"高"
,
"高优先级需求满足率"
+
String
.
format
(
"%.0f%%"
,
rate
*
100
));
addRisk
(
risks
,
sd
,
"高"
,
"高优先级需求满足率"
+
String
.
format
(
"%.0f%%"
,
rate
*
100
));
}
}
}
}
/** 按描述去重后添加风险项 */
/** 按描述去重后添加风险项 */
private
void
addRisk
(
java
.
util
.
List
<
RiskItem
>
risks
,
String
level
,
String
description
)
{
private
void
addRisk
(
java
.
util
.
List
<
RiskItem
>
risks
,
SalesDemand
sd
,
String
level
,
String
description
)
{
for
(
RiskItem
r
:
risks
)
{
for
(
RiskItem
r
:
risks
)
{
if
(
r
.
getDescription
().
equals
(
description
))
return
;
if
(
r
.
getDescription
().
equals
(
description
))
return
;
}
}
risks
.
add
(
new
RiskItem
(
level
,
description
)
);
addReasonWithDetail
(
risks
,
sd
,
2
,
level
,
""
,
description
);
}
}
/** 格式化数值: 去掉多余小数位 */
/** 格式化数值: 去掉多余小数位 */
private
String
fmt
(
double
v
)
{
private
String
fmt
(
double
v
)
{
...
@@ -1606,7 +1635,7 @@ public class ResultWriter {
...
@@ -1606,7 +1635,7 @@ public class ResultWriter {
jb
.
key
(
"fulfillmentRate"
).
val
(
sr
.
getFulfillmentRate
());
jb
.
key
(
"fulfillmentRate"
).
val
(
sr
.
getFulfillmentRate
());
jb
.
key
(
"priority"
).
val
(
sr
.
getPriority
());
jb
.
key
(
"priority"
).
val
(
sr
.
getPriority
());
jb
.
key
(
"unmetReasons"
).
arr
();
jb
.
key
(
"unmetReasons"
).
arr
();
for
(
String
r
:
sr
.
getUnmetReasons
())
jb
.
val
(
r
);
//
for (String r : sr.getUnmetReasons()) jb.val(r);
jb
.
endArr
();
jb
.
endArr
();
jb
.
key
(
"risks"
).
arr
();
jb
.
key
(
"risks"
).
arr
();
for
(
RiskItem
r
:
sr
.
getRisks
())
{
for
(
RiskItem
r
:
sr
.
getRisks
())
{
...
@@ -1698,6 +1727,7 @@ public class ResultWriter {
...
@@ -1698,6 +1727,7 @@ public class ResultWriter {
.
key
(
"rawValue"
).
val
(
e
.
rawValue
)
.
key
(
"rawValue"
).
val
(
e
.
rawValue
)
.
key
(
"weight"
).
valOpt
(
e
.
weight
)
.
key
(
"weight"
).
valOpt
(
e
.
weight
)
.
key
(
"penalty"
).
valOpt
(
e
.
penalty
)
.
key
(
"penalty"
).
valOpt
(
e
.
penalty
)
.
key
(
"contribution"
).
valOpt
(
e
.
contribution
)
.
key
(
"isBenefit"
).
val
(
e
.
isBenefit
)
.
key
(
"isBenefit"
).
val
(
e
.
isBenefit
)
.
endObj
();
.
endObj
();
}
}
...
...
src/main/java/com/aps/macroplanner/output/SolutionPrinter.java
View file @
18808dc2
...
@@ -693,37 +693,37 @@ public class SolutionPrinter {
...
@@ -693,37 +693,37 @@ public class SolutionPrinter {
// 计算并输出加权总惩罚 (等价于单目标函数值)
// 计算并输出加权总惩罚 (等价于单目标函数值)
writeLog
(
""
);
writeLog
(
""
);
writeLog
(
"--- 最终 KPI 值 (加权总惩罚) ---"
);
//
writeLog("--- 最终 KPI 值 (加权总惩罚) ---");
double
fulfillment
=
model
.
getTotalFulfillment
().
solutionValue
();
//
double fulfillment = model.getTotalFulfillment().solutionValue();
double
lotSize
=
model
.
getTotalLotSize
().
solutionValue
();
//
double lotSize = model.getTotalLotSize().solutionValue();
double
maxInv
=
model
.
getTotalMaxInventoryLevel
().
solutionValue
();
//
double maxInv = model.getTotalMaxInventoryLevel().solutionValue();
double
minInv
=
model
.
getTotalMinInventoryLevel
().
solutionValue
();
//
double minInv = model.getTotalMinInventoryLevel().solutionValue();
double
targetInv
=
model
.
getTotalTargetInvLevel
().
solutionValue
();
//
double targetInv = model.getTotalTargetInvLevel().solutionValue();
double
capacity
=
model
.
getTotalUnitCapacity
().
solutionValue
();
//
double capacity = model.getTotalUnitCapacity().solutionValue();
double
supplyTarget
=
model
.
getTotalSupplyTarget
().
solutionValue
();
//
double supplyTarget = model.getTotalSupplyTarget().solutionValue();
double
minSupply
=
model
.
getTotalMinSupply
().
solutionValue
();
//
double minSupply = model.getTotalMinSupply().solutionValue();
double
maxSupply
=
model
.
getTotalMaxSupply
().
solutionValue
();
//
double maxSupply = model.getTotalMaxSupply().solutionValue();
double
salesPriority
=
model
.
getTotalSalesDemandPriority
().
solutionValue
();
//
double salesPriority = model.getTotalSalesDemandPriority().solutionValue();
double
totalPenalty
=
fulfillment
*
weights
.
getOrDefault
(
KpiLib
.
Fulfillment
.
getEn
(),
0
d
)
//
double totalPenalty = fulfillment * weights.getOrDefault(KpiLib.Fulfillment.getEn(),0d)
+
lotSize
*
weights
.
getOrDefault
(
KpiLib
.
LotSize
.
getEn
(),
0
d
)
//
+ lotSize * weights.getOrDefault(KpiLib.LotSize.getEn(),0d)
+
maxInv
*
weights
.
getOrDefault
(
KpiLib
.
MinInventoryLevel
.
getEn
(),
0
d
)
//
+ maxInv * weights.getOrDefault(KpiLib.MinInventoryLevel.getEn(),0d)
+
minInv
*
weights
.
getOrDefault
(
KpiLib
.
MinInventoryLevel
.
getEn
(),
0
d
)
//
+ minInv * weights.getOrDefault(KpiLib.MinInventoryLevel.getEn(),0d)
+
targetInv
*
weights
.
getOrDefault
(
KpiLib
.
TargetInvLevel
.
getEn
(),
0
d
)
//
+ targetInv * weights.getOrDefault(KpiLib.TargetInvLevel.getEn(),0d)
+
capacity
*
weights
.
getOrDefault
(
KpiLib
.
UnitCapacity
.
getEn
(),
0
d
)
//
+ capacity * weights.getOrDefault(KpiLib.UnitCapacity.getEn(),0d)
+
supplyTarget
*
weights
.
getOrDefault
(
KpiLib
.
SupplyTarget
.
getEn
(),
0
d
)
//
+ supplyTarget * weights.getOrDefault(KpiLib.SupplyTarget.getEn(),0d)
+
minSupply
*
weights
.
getOrDefault
(
KpiLib
.
MinSupply
.
getEn
(),
0
d
)
//
+ minSupply * weights.getOrDefault(KpiLib.MinSupply.getEn(),0d)
+
maxSupply
*
weights
.
getOrDefault
(
KpiLib
.
MaxSupply
.
getEn
(),
0
d
)
//
+ maxSupply * weights.getOrDefault(KpiLib.MaxSupply.getEn(),0d)
-
salesPriority
*
weights
.
getOrDefault
(
KpiLib
.
SalesDemandPriority
.
getEn
(),
0
d
);
//
- salesPriority * weights.getOrDefault(KpiLib.SalesDemandPriority.getEn(),0d);
//
writeLog
(
" 加权总惩罚
: %.2f"
,
totalPenalty
);
// writeLog(" 总kpi
: %.2f", totalPenalty);
for
(
Map
.
Entry
<
String
,
Double
>
entry
:
weights
.
entrySet
())
{
for
(
Map
.
Entry
<
String
,
Double
>
entry
:
weights
.
entrySet
())
{
String
key
=
entry
.
getKey
();
String
key
=
entry
.
getKey
();
Double
weight
=
entry
.
getValue
();
Double
weight
=
entry
.
getValue
();
KpiLib
kpi
=
KpiLib
.
ofEn
(
key
);
KpiLib
kpi
=
KpiLib
.
ofEn
(
key
);
double
val
=
model
.
getKpi
(
key
).
solutionValue
();
double
val
=
model
.
getKpi
(
key
).
solutionValue
();
writeLog
(
" %s: %.2f (
权重
%.0f × %.2f)"
,
writeLog
(
" %s: %.2f (%.0f × %.2f)"
,
kpi
.
getCn
(),
val
*
weight
,
val
,
weight
);
kpi
.
getCn
(),
val
*
weight
,
val
,
weight
);
}
}
...
...
src/main/java/com/aps/macroplanner/output/dto/KpiResult.java
View file @
18808dc2
...
@@ -26,6 +26,8 @@ public class KpiResult {
...
@@ -26,6 +26,8 @@ public class KpiResult {
public
Double
weight
;
public
Double
weight
;
/** 加权惩罚;权重为空时为 null */
/** 加权惩罚;权重为空时为 null */
public
Double
penalty
;
public
Double
penalty
;
/** 得分贡献 = rawValue × weight(收益项为正, 惩罚项为负);无权重时为 null */
public
Double
contribution
;
/** 是否为收益项 (越大越好, 正系数) */
/** 是否为收益项 (越大越好, 正系数) */
public
boolean
isBenefit
;
public
boolean
isBenefit
;
}
}
...
@@ -44,6 +46,7 @@ public class KpiResult {
...
@@ -44,6 +46,7 @@ public class KpiResult {
e
.
weight
=
weight
;
e
.
weight
=
weight
;
e
.
isBenefit
=
isBenefit
;
e
.
isBenefit
=
isBenefit
;
e
.
penalty
=
weight
==
null
?
null
:
(
isBenefit
?
-
rawValue
*
weight
:
rawValue
*
weight
);
e
.
penalty
=
weight
==
null
?
null
:
(
isBenefit
?
-
rawValue
*
weight
:
rawValue
*
weight
);
e
.
contribution
=
weight
==
null
?
null
:
(
isBenefit
?
rawValue
*
weight
:
-
rawValue
*
weight
);
entries
.
add
(
e
);
entries
.
add
(
e
);
}
}
}
}
src/main/java/com/aps/macroplanner/output/dto/OptimizationResult.java
View file @
18808dc2
...
@@ -33,6 +33,8 @@ public class OptimizationResult {
...
@@ -33,6 +33,8 @@ public class OptimizationResult {
private
final
List
<
SalesDemandResult
>
salesDemands
=
new
ArrayList
<>();
private
final
List
<
SalesDemandResult
>
salesDemands
=
new
ArrayList
<>();
private
List
<
SalesDemandResult
>
saleDemandSummaries
=
new
ArrayList
<>();
private
List
<
SalesDemandResult
>
saleDemandSummaries
=
new
ArrayList
<>();
private
List
<
RiskItem
>
saleDemandReasons
=
new
ArrayList
<>();
private
final
List
<
PispipResult
>
pispips
=
new
ArrayList
<>();
private
final
List
<
PispipResult
>
pispips
=
new
ArrayList
<>();
...
@@ -76,8 +78,12 @@ public class OptimizationResult {
...
@@ -76,8 +78,12 @@ public class OptimizationResult {
public
List
<
SalesDemandResult
>
getSaleSummarieDemands
()
{
return
saleDemandSummaries
;
}
public
List
<
SalesDemandResult
>
getSaleSummarieDemands
()
{
return
saleDemandSummaries
;
}
public
List
<
RiskItem
>
getSaleDemandReasons
()
{
return
saleDemandReasons
;
}
public
void
setSaleSummarieDemands
(
List
<
SalesDemandResult
>
v
)
{
saleDemandSummaries
=
v
;
}
public
void
setSaleSummarieDemands
(
List
<
SalesDemandResult
>
v
)
{
saleDemandSummaries
=
v
;
}
public
void
setSaleDemandReasons
(
List
<
RiskItem
>
v
)
{
saleDemandReasons
=
v
;
}
public
List
<
PispipResult
>
getPispips
()
{
return
pispips
;
}
public
List
<
PispipResult
>
getPispips
()
{
return
pispips
;
}
...
...
src/main/java/com/aps/macroplanner/output/dto/RiskItem.java
View file @
18808dc2
...
@@ -2,6 +2,7 @@ package com.aps.macroplanner.output.dto;
...
@@ -2,6 +2,7 @@ package com.aps.macroplanner.output.dto;
import
com.fasterxml.jackson.annotation.JsonCreator
;
import
com.fasterxml.jackson.annotation.JsonCreator
;
import
com.fasterxml.jackson.annotation.JsonProperty
;
import
com.fasterxml.jackson.annotation.JsonProperty
;
import
lombok.Data
;
/**
/**
* 作者:佟礼
* 作者:佟礼
...
@@ -12,16 +13,27 @@ import com.fasterxml.jackson.annotation.JsonProperty;
...
@@ -12,16 +13,27 @@ import com.fasterxml.jackson.annotation.JsonProperty;
*
*
* <p>level 取值: "高" / "低"。</p>
* <p>level 取值: "高" / "低"。</p>
*/
*/
@Data
public
class
RiskItem
{
public
class
RiskItem
{
private
final
String
level
;
private
final
String
description
;
@JsonCreator
private
String
salesDemandId
;
public
RiskItem
(
@JsonProperty
(
"level"
)
String
level
,
@JsonProperty
(
"description"
)
String
description
)
{
private
Integer
periodIndex
;
private
Integer
type
;
private
String
level
;
private
String
title
;
private
String
description
;
public
RiskItem
()
{}
public
RiskItem
(
String
salesDemandId
,
int
periodIndex
,
int
type
,
String
level
,
String
title
,
String
description
)
{
this
.
salesDemandId
=
salesDemandId
;
this
.
periodIndex
=
periodIndex
;
this
.
type
=
type
;
this
.
level
=
level
;
this
.
level
=
level
;
this
.
title
=
title
;
this
.
description
=
description
;
this
.
description
=
description
;
}
}
public
String
getLevel
()
{
return
level
;
}
public
String
getDescription
()
{
return
description
;
}
}
}
src/main/java/com/aps/macroplanner/output/dto/SalesDemandResult.java
View file @
18808dc2
...
@@ -39,9 +39,9 @@ public class SalesDemandResult {
...
@@ -39,9 +39,9 @@ public class SalesDemandResult {
private
Long
categoryId
;
private
Long
categoryId
;
/** 未完成原因分析 (仅当 unmetQty > 0 时有内容) */
/** 未完成原因分析 (仅当 unmetQty > 0 时有内容) */
private
final
List
<
String
>
unmetReasons
=
new
ArrayList
<>();
private
List
<
RiskItem
>
unmetReasons
=
new
ArrayList
<>();
/** 风险分析 (即使满足也可能存在的供应链风险) */
/** 风险分析 (即使满足也可能存在的供应链风险) */
private
final
List
<
RiskItem
>
risks
=
new
ArrayList
<>();
private
List
<
RiskItem
>
risks
=
new
ArrayList
<>();
// ==================== Getters / Setters ====================
// ==================== Getters / Setters ====================
...
@@ -88,8 +88,10 @@ public class SalesDemandResult {
...
@@ -88,8 +88,10 @@ public class SalesDemandResult {
public
double
getPriority
()
{
return
priority
;
}
public
double
getPriority
()
{
return
priority
;
}
public
void
setPriority
(
double
v
)
{
this
.
priority
=
v
;
}
public
void
setPriority
(
double
v
)
{
this
.
priority
=
v
;
}
public
List
<
String
>
getUnmetReasons
()
{
return
unmetReasons
;
}
public
List
<
RiskItem
>
getUnmetReasons
()
{
return
unmetReasons
;
}
public
List
<
RiskItem
>
getRisks
()
{
return
risks
;
}
public
List
<
RiskItem
>
getRisks
()
{
return
risks
;
}
public
void
setUnmetReasons
(
List
<
RiskItem
>
v
)
{
unmetReasons
=
v
;
}
public
void
setRisks
(
List
<
RiskItem
>
v
)
{
risks
=
v
;
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment