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
2ec4b057
Commit
2ec4b057
authored
Sep 14, 2026
by
Tong Li
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MP
parent
7d6de509
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
580 additions
and
275 deletions
+580
-275
pom.xml
pom.xml
+65
-0
FlatParquetUtil.java
src/main/java/com/aps/common/util/FlatParquetUtil.java
+126
-0
MacroPlannerOptimizer.java
...main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
+3
-3
MultiLevelBomTestRunner.java
...in/java/com/aps/macroplanner/MultiLevelBomTestRunner.java
+2
-1
BalanceConstraint.java
...va/com/aps/macroplanner/constraint/BalanceConstraint.java
+5
-8
BomConstraint.java
...n/java/com/aps/macroplanner/constraint/BomConstraint.java
+1
-3
CapacityConstraint.java
...a/com/aps/macroplanner/constraint/CapacityConstraint.java
+2
-2
DemandFulfillmentConstraint.java
.../macroplanner/constraint/DemandFulfillmentConstraint.java
+7
-14
DemandSlackLinkageConstraint.java
...macroplanner/constraint/DemandSlackLinkageConstraint.java
+7
-9
BenchmarkDataBuilder.java
.../java/com/aps/macroplanner/data/BenchmarkDataBuilder.java
+2
-2
DataValidator.java
src/main/java/com/aps/macroplanner/data/DataValidator.java
+5
-5
TestDataBuilder.java
src/main/java/com/aps/macroplanner/data/TestDataBuilder.java
+124
-23
UnitOperation.java
src/main/java/com/aps/macroplanner/data/UnitOperation.java
+1
-1
MacroPlannerModel.java
...in/java/com/aps/macroplanner/model/MacroPlannerModel.java
+1
-1
ResultWriter.java
src/main/java/com/aps/macroplanner/output/ResultWriter.java
+201
-150
SolutionPrinter.java
...ain/java/com/aps/macroplanner/output/SolutionPrinter.java
+5
-9
PeriodTaskResult.java
...ava/com/aps/macroplanner/output/dto/PeriodTaskResult.java
+13
-44
PispipResult.java
...in/java/com/aps/macroplanner/output/dto/PispipResult.java
+3
-0
logback-spring.xml
src/main/resources/logback-spring.xml
+7
-0
No files found.
pom.xml
View file @
2ec4b057
...
@@ -128,6 +128,71 @@
...
@@ -128,6 +128,71 @@
<artifactId>
httpclient
</artifactId>
<artifactId>
httpclient
</artifactId>
<version>
4.5.14
</version>
<version>
4.5.14
</version>
</dependency>
</dependency>
<!-- parquet (用于存储运算结果) -->
<dependency>
<groupId>
org.apache.parquet
</groupId>
<artifactId>
parquet-avro
</artifactId>
<version>
1.14.4
</version>
</dependency>
<dependency>
<groupId>
org.apache.hadoop
</groupId>
<artifactId>
hadoop-common
</artifactId>
<version>
3.3.6
</version>
<exclusions>
<!-- 排除 Hadoop 自带的 slf4j-reload4j 绑定,避免与 Spring Boot 的 logback 冲突(SLF4J multiple bindings) -->
<exclusion>
<groupId>
org.slf4j
</groupId>
<artifactId>
slf4j-reload4j
</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>
org.apache.hadoop
</groupId>
<artifactId>
hadoop-mapreduce-client-core
</artifactId>
<version>
3.3.6
</version>
<exclusions>
<!-- 排除日志绑定,避免再次出现 SLF4J multiple bindings -->
<exclusion>
<groupId>
org.slf4j
</groupId>
<artifactId>
slf4j-reload4j
</artifactId>
</exclusion>
<exclusion>
<groupId>
org.slf4j
</groupId>
<artifactId>
slf4j-api
</artifactId>
</exclusion>
<exclusion>
<groupId>
log4j
</groupId>
<artifactId>
log4j
</artifactId>
</exclusion>
<!-- 排除YARN相关,完全用不到 -->
<exclusion>
<groupId>
org.apache.hadoop
</groupId>
<artifactId>
hadoop-yarn-api
</artifactId>
</exclusion>
<exclusion>
<groupId>
org.apache.hadoop
</groupId>
<artifactId>
hadoop-yarn-common
</artifactId>
</exclusion>
<exclusion>
<groupId>
org.apache.hadoop
</groupId>
<artifactId>
hadoop-yarn-client
</artifactId>
</exclusion>
<!-- 排除其他不需要的模块 -->
<exclusion>
<groupId>
org.apache.hadoop
</groupId>
<artifactId>
hadoop-hdfs-client
</artifactId>
</exclusion>
<exclusion>
<groupId>
com.google.protobuf
</groupId>
<artifactId>
protobuf-java
</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>
org.xerial.snappy
</groupId>
<artifactId>
snappy-java
</artifactId>
<version>
1.1.10.5
</version>
</dependency>
</dependencies>
</dependencies>
<build>
<build>
...
...
src/main/java/com/aps/common/util/FlatParquetUtil.java
0 → 100644
View file @
2ec4b057
package
com
.
aps
.
common
.
util
;
/**
* 作者:佟礼
* 时间:2026-09-08
*/
import
org.apache.avro.Schema
;
import
org.apache.avro.reflect.ReflectData
;
import
org.apache.hadoop.conf.Configuration
;
import
org.apache.hadoop.fs.Path
;
import
org.apache.parquet.avro.AvroParquetReader
;
import
org.apache.parquet.avro.AvroParquetWriter
;
import
org.apache.parquet.hadoop.ParquetReader
;
import
org.apache.parquet.hadoop.ParquetWriter
;
import
org.apache.parquet.hadoop.metadata.CompressionCodecName
;
import
org.apache.parquet.io.LocalInputFile
;
import
org.apache.parquet.io.LocalOutputFile
;
import
java.io.File
;
import
java.io.IOException
;
import
java.net.URI
;
import
java.nio.file.Paths
;
import
java.util.ArrayList
;
import
java.util.List
;
/**
* 范式化扁平Row POJO读写Parquet
* 要求:POJO不要包含嵌套List集合,子List拆成独立子parquet
*/
public
class
FlatParquetUtil
{
static
{
// Windows 下 Hadoop 需要 winutils.exe/hadoop.dll。
// 优先复用 HADOOP_HOME,避免把 hadoop.home.dir 指到当前目录(无 bin/winutils.exe)导致写入失败。
if
(
System
.
getProperty
(
"hadoop.home.dir"
)
==
null
)
{
String
hadoopHome
=
System
.
getenv
(
"HADOOP_HOME"
);
if
(
hadoopHome
!=
null
&&
!
hadoopHome
.
trim
().
isEmpty
())
{
System
.
setProperty
(
"hadoop.home.dir"
,
hadoopHome
);
}
}
}
private
final
Configuration
conf
;
public
FlatParquetUtil
()
{
conf
=
new
Configuration
();
// 使用本地文件系统,不依赖hadoop集群
conf
.
set
(
"fs.file.impl"
,
org
.
apache
.
hadoop
.
fs
.
LocalFileSystem
.
class
.
getName
());
// 关闭hadoop Shell的部分本地命令调用(Windows不需要winutils.exe)
conf
.
setBoolean
(
"hadoop.native.lib"
,
false
);
}
/**
* 写入扁平对象列表到parquet(覆盖)
*/
public
<
T
>
void
write
(
List
<
T
>
rows
,
String
filePath
,
Class
<
T
>
clazz
)
throws
IOException
{
if
(
rows
==
null
||
rows
.
isEmpty
())
{
return
;
}
File
file
=
resolvePath
(
filePath
);
File
parent
=
file
.
getParentFile
();
if
(
parent
!=
null
&&
!
parent
.
exists
())
{
parent
.
mkdirs
();
}
if
(
file
.
exists
())
{
file
.
delete
();
}
Schema
schema
=
ReflectData
.
AllowNull
.
get
().
getSchema
(
clazz
);
LocalOutputFile
outputFile
=
new
LocalOutputFile
(
file
.
toPath
());
try
(
ParquetWriter
<
T
>
writer
=
AvroParquetWriter
.<
T
>
builder
(
outputFile
)
.
withSchema
(
schema
)
.
withDataModel
(
ReflectData
.
AllowNull
.
get
())
.
withConf
(
conf
)
.
withCompressionCodec
(
CompressionCodecName
.
SNAPPY
)
.
withRowGroupSize
(
128
*
1024
*
1024
)
.
build
()){
for
(
T
row
:
rows
){
writer
.
write
(
row
);
}
}
}
private
File
resolvePath
(
String
filePath
)
throws
IOException
{
try
{
if
(
filePath
.
startsWith
(
"file:"
))
{
// 是URI格式,用标准URI解析,自动处理Windows盘符、空格、中文
return
Paths
.
get
(
new
URI
(
filePath
)).
toFile
();
}
else
{
// 纯本地路径
return
new
File
(
filePath
);
}
}
catch
(
Exception
e
)
{
throw
new
IOException
(
"路径解析失败: "
+
filePath
,
e
);
}
}
/**
* 读取全部行
*/
public
<
T
>
List
<
T
>
readAll
(
String
filePath
,
Class
<
T
>
clazz
)
throws
IOException
{
List
<
T
>
res
=
new
ArrayList
<>();
File
file
=
resolvePath
(
filePath
);
LocalInputFile
inputFile
=
new
LocalInputFile
(
file
.
toPath
());
try
(
ParquetReader
<
T
>
reader
=
AvroParquetReader
.<
T
>
builder
(
inputFile
)
.
withDataModel
(
ReflectData
.
AllowNull
.
get
())
.
withConf
(
conf
)
.
build
()){
T
t
;
while
((
t
=
reader
.
read
())
!=
null
)
{
res
.
add
(
t
);
}
}
return
res
;
}
/**
* 获取迭代器流式读取,不一次性全加载内存
*/
public
<
T
>
ParquetReader
<
T
>
getReader
(
String
filePath
,
Class
<
T
>
clazz
)
throws
IOException
{
Path
path
=
new
Path
(
filePath
);
return
AvroParquetReader
.<
T
>
builder
(
path
)
.
withDataModel
(
ReflectData
.
AllowNull
.
get
())
.
withConf
(
conf
)
.
build
();
}
}
src/main/java/com/aps/macroplanner/MacroPlannerOptimizer.java
View file @
2ec4b057
...
@@ -196,7 +196,7 @@ public class MacroPlannerOptimizer {
...
@@ -196,7 +196,7 @@ public class MacroPlannerOptimizer {
writeLog
(
" [OK] 目标函数创建完成"
);
writeLog
(
" [OK] 目标函数创建完成"
);
// 4. 导出 LP 模型文件
// 4. 导出 LP 模型文件
exportLpModel
();
//
exportLpModel();
writeLog
(
"\n模型统计: 变量="
+
model
.
getSolver
().
numVariables
()
writeLog
(
"\n模型统计: 变量="
+
model
.
getSolver
().
numVariables
()
+
", 约束="
+
model
.
getSolver
().
numConstraints
()
+
"\n"
);
+
", 约束="
+
model
.
getSolver
().
numConstraints
()
+
"\n"
);
...
@@ -432,7 +432,7 @@ public class MacroPlannerOptimizer {
...
@@ -432,7 +432,7 @@ public class MacroPlannerOptimizer {
l3
.
addKPI
(
"供应目标偏差"
,
model
.
getTotalSupplyTarget
(),
w
.
getSupplyTargetWeight
());
l3
.
addKPI
(
"供应目标偏差"
,
model
.
getTotalSupplyTarget
(),
w
.
getSupplyTargetWeight
());
l3
.
addKPI
(
"销售优先级"
,
model
.
getTotalSalesDemandPriority
(),
l3
.
addKPI
(
"销售优先级"
,
model
.
getTotalSalesDemandPriority
(),
w
.
getSalesDemandPriorityWeight
(),
true
);
// 负系数 = 最大化
w
.
getSalesDemandPriorityWeight
(),
true
);
// 负系数 = 最大化
levels
.
add
(
l3
);
//
levels.add(l3);
// === Level 4: 软约束 (允许 10% 退化, slack=10%) ===
// === Level 4: 软约束 (允许 10% 退化, slack=10%) ===
StrategyLevel
l4
=
new
StrategyLevel
(
4
,
"软约束"
,
0.10
);
StrategyLevel
l4
=
new
StrategyLevel
(
4
,
"软约束"
,
0.10
);
...
@@ -440,7 +440,7 @@ public class MacroPlannerOptimizer {
...
@@ -440,7 +440,7 @@ public class MacroPlannerOptimizer {
l4
.
addKPI
(
"欠库存"
,
model
.
getTotalMinInventoryLevel
(),
w
.
getMinInventoryLevelWeight
());
l4
.
addKPI
(
"欠库存"
,
model
.
getTotalMinInventoryLevel
(),
w
.
getMinInventoryLevelWeight
());
l4
.
addKPI
(
"最小供应不足"
,
model
.
getTotalMinSupply
(),
w
.
getMinSupplyWeight
());
l4
.
addKPI
(
"最小供应不足"
,
model
.
getTotalMinSupply
(),
w
.
getMinSupplyWeight
());
l4
.
addKPI
(
"最大供应超出"
,
model
.
getTotalMaxSupply
(),
w
.
getMaxSupplyWeight
());
l4
.
addKPI
(
"最大供应超出"
,
model
.
getTotalMaxSupply
(),
w
.
getMaxSupplyWeight
());
levels
.
add
(
l4
);
//
levels.add(l4);
return
levels
;
return
levels
;
}
}
...
...
src/main/java/com/aps/macroplanner/MultiLevelBomTestRunner.java
View file @
2ec4b057
...
@@ -55,10 +55,11 @@ public class MultiLevelBomTestRunner {
...
@@ -55,10 +55,11 @@ public class MultiLevelBomTestRunner {
// for (int scale : BenchmarkDataBuilder.SUPPORTED_SCALES) {
// for (int scale : BenchmarkDataBuilder.SUPPORTED_SCALES) {
//
//
// int scale=10000;
// BenchmarkDataBuilder data =
// BenchmarkDataBuilder data =
// BenchmarkDataBuilder.forScale(scale);
// BenchmarkDataBuilder.forScale(scale);
// writeLog("===== TEST RUNNER START "+scale+"=====");
// writeLog("===== TEST RUNNER START "+scale+"=====");
//
//
//
// data.init();
// data.init();
// writeLog("Data loaded: " + data.getProducts().size() + " products, "
// writeLog("Data loaded: " + data.getProducts().size() + " products, "
// + data.getOperations().size() + " operations");
// + data.getOperations().size() + " operations");
...
...
src/main/java/com/aps/macroplanner/constraint/BalanceConstraint.java
View file @
2ec4b057
...
@@ -42,8 +42,7 @@ public class BalanceConstraint {
...
@@ -42,8 +42,7 @@ public class BalanceConstraint {
// === 流入 ===
// === 流入 ===
// PTQty 产出 (考虑提前期: productionDate 生产, productionDate+leadTimeDays 到货)
// PTQty 产出 (考虑提前期: productionDate 生产, productionDate+leadTimeDays 到货)
// 关键: 只统计产出到当前库存点(sp)的操作, 避免多工序路由中产出到错误的库存点
// 关键: 只统计产出到当前库存点(sp)的操作, 避免多工序路由中产出到错误的库存点
for
(
Operation
op
:
data
.
getOperations
())
{
for
(
Operation
op
:
data
.
getOperationsProducing
(
prod
.
getId
(),
sp
.
getId
()))
{
if
(!
op
.
producesProductAtSp
(
prod
.
getId
(),
sp
.
getId
()))
continue
;
int
leadTimeDays
=
op
.
getLeadTimeDays
();
int
leadTimeDays
=
op
.
getLeadTimeDays
();
Period
productionPeriod
=
data
.
getPeriodOffsetByDays
(
p
,
leadTimeDays
);
Period
productionPeriod
=
data
.
getPeriodOffsetByDays
(
p
,
leadTimeDays
);
if
(
productionPeriod
==
null
)
continue
;
if
(
productionPeriod
==
null
)
continue
;
...
@@ -53,8 +52,8 @@ public class BalanceConstraint {
...
@@ -53,8 +52,8 @@ public class BalanceConstraint {
}
}
}
}
// DemandSlack
// DemandSlack
// MPVariable slackVar = demandSlackVars.get(invKey);
// MPVariable slackVar = demandSlackVars.get(invKey);
// if (slackVar != null) balance.setCoefficient(slackVar, 1.0);
// if (slackVar != null) balance.setCoefficient(slackVar, 1.0);
// 上一周期库存 (t>0)
// 上一周期库存 (t>0)
if
(
p
.
getIndex
()
>
0
)
{
if
(
p
.
getIndex
()
>
0
)
{
String
prevKey
=
prod
.
getId
()
+
"_"
+
sp
.
getId
()
+
"_"
+
(
p
.
getIndex
()
-
1
);
String
prevKey
=
prod
.
getId
()
+
"_"
+
sp
.
getId
()
+
"_"
+
(
p
.
getIndex
()
-
1
);
...
@@ -84,10 +83,8 @@ public class BalanceConstraint {
...
@@ -84,10 +83,8 @@ public class BalanceConstraint {
}
}
// 在途供应 (供应商已发货, 固定到货量, 按日期匹配周期)
// 在途供应 (供应商已发货, 固定到货量, 按日期匹配周期)
for
(
InTransitSupply
its
:
data
.
getInTransitSupplies
())
{
for
(
InTransitSupply
its
:
data
.
getInTransitSuppliesFor
(
prod
.
getId
(),
sp
.
getId
()))
{
if
(
its
.
getProduct
().
getId
().
equals
(
prod
.
getId
())
if
(
p
.
equals
(
data
.
getPeriodByDate
(
its
.
getArrivalDate
())))
{
&&
its
.
getStockingPoint
().
getId
().
equals
(
sp
.
getId
())
&&
p
.
equals
(
data
.
getPeriodByDate
(
its
.
getArrivalDate
())))
{
rhs
-=
its
.
getQuantity
();
rhs
-=
its
.
getQuantity
();
}
}
}
}
...
...
src/main/java/com/aps/macroplanner/constraint/BomConstraint.java
View file @
2ec4b057
...
@@ -57,9 +57,7 @@ public class BomConstraint {
...
@@ -57,9 +57,7 @@ public class BomConstraint {
"DepDemandDef_"
+
ddKey
);
"DepDemandDef_"
+
ddKey
);
ddCon
.
setCoefficient
(
ddVar
,
-
1.0
);
ddCon
.
setCoefficient
(
ddVar
,
-
1.0
);
for
(
OperationInput
input
:
data
.
getOperationInputs
())
{
for
(
OperationInput
input
:
data
.
getOperationInputsFor
(
prod
.
getId
(),
sp
.
getId
()))
{
if
(!
input
.
getInputProduct
().
getId
().
equals
(
prod
.
getId
()))
continue
;
if
(!
input
.
getInputSp
().
getId
().
equals
(
sp
.
getId
()))
continue
;
MPVariable
odVar
=
operationDemandQtyVars
.
get
(
MPVariable
odVar
=
operationDemandQtyVars
.
get
(
input
.
getKey
()
+
"_"
+
p
.
getIndex
());
input
.
getKey
()
+
"_"
+
p
.
getIndex
());
if
(
odVar
!=
null
)
ddCon
.
setCoefficient
(
odVar
,
1.0
);
if
(
odVar
!=
null
)
ddCon
.
setCoefficient
(
odVar
,
1.0
);
...
...
src/main/java/com/aps/macroplanner/constraint/CapacityConstraint.java
View file @
2ec4b057
...
@@ -38,7 +38,7 @@ public class CapacityConstraint {
...
@@ -38,7 +38,7 @@ public class CapacityConstraint {
// --- 最大产能 ---
// --- 最大产能 ---
MPConstraint
maxCap
=
model
.
getSolver
().
makeConstraint
(
MPConstraint
maxCap
=
model
.
getSolver
().
makeConstraint
(
-
MPSolver
.
infinity
(),
up
.
getMaxCapacity
(),
"MaxCap_"
+
capKey
);
-
MPSolver
.
infinity
(),
up
.
getMaxCapacity
(),
"MaxCap_"
+
capKey
);
for
(
Operation
op
:
data
.
getOperations
(
))
{
for
(
Operation
op
:
data
.
getOperations
ByUnitId
(
up
.
getUnitId
()
))
{
for
(
UnitOperation
uo
:
op
.
getUnitOperations
())
{
for
(
UnitOperation
uo
:
op
.
getUnitOperations
())
{
if
(!
uo
.
getUnitId
().
equals
(
up
.
getUnitId
()))
continue
;
if
(!
uo
.
getUnitId
().
equals
(
up
.
getUnitId
()))
continue
;
MPVariable
ptVar
=
ptQtyVars
.
get
(
op
.
ptQtyKey
(
uo
,
up
.
getPeriod
().
getIndex
()));
MPVariable
ptVar
=
ptQtyVars
.
get
(
op
.
ptQtyKey
(
uo
,
up
.
getPeriod
().
getIndex
()));
...
@@ -52,7 +52,7 @@ public class CapacityConstraint {
...
@@ -52,7 +52,7 @@ public class CapacityConstraint {
if
(
up
.
hasMinCapacity
())
{
if
(
up
.
hasMinCapacity
())
{
MPConstraint
minCap
=
model
.
getSolver
().
makeConstraint
(
MPConstraint
minCap
=
model
.
getSolver
().
makeConstraint
(
up
.
getMinCapacity
(),
MPSolver
.
infinity
(),
"MinCap_"
+
capKey
);
up
.
getMinCapacity
(),
MPSolver
.
infinity
(),
"MinCap_"
+
capKey
);
for
(
Operation
op
:
data
.
getOperations
(
))
{
for
(
Operation
op
:
data
.
getOperations
ByUnitId
(
up
.
getUnitId
()
))
{
for
(
UnitOperation
uo
:
op
.
getUnitOperations
())
{
for
(
UnitOperation
uo
:
op
.
getUnitOperations
())
{
if
(!
uo
.
getUnitId
().
equals
(
up
.
getUnitId
()))
continue
;
if
(!
uo
.
getUnitId
().
equals
(
up
.
getUnitId
()))
continue
;
MPVariable
ptVar
=
ptQtyVars
.
get
(
op
.
ptQtyKey
(
uo
,
up
.
getPeriod
().
getIndex
()));
MPVariable
ptVar
=
ptQtyVars
.
get
(
op
.
ptQtyKey
(
uo
,
up
.
getPeriod
().
getIndex
()));
...
...
src/main/java/com/aps/macroplanner/constraint/DemandFulfillmentConstraint.java
View file @
2ec4b057
...
@@ -49,23 +49,16 @@ public class DemandFulfillmentConstraint {
...
@@ -49,23 +49,16 @@ public class DemandFulfillmentConstraint {
if
(
dfVar
!=
null
)
c
.
setCoefficient
(
dfVar
,
-
1.0
);
if
(
dfVar
!=
null
)
c
.
setCoefficient
(
dfVar
,
-
1.0
);
// + SalesDemandQty
// + SalesDemandQty
for
(
SalesDemand
sd
:
data
.
getSalesDemands
())
{
for
(
SalesDemand
sd
:
data
.
getSalesDemandsFor
(
prod
,
sp
,
p
))
{
if
(
sd
.
getProduct
().
getId
().
equals
(
prod
.
getId
())
MPVariable
sdVar
=
sdVars
.
get
(
sd
.
getKey
());
&&
sd
.
getStockingPoint
().
getId
().
equals
(
sp
.
getId
())
if
(
sdVar
!=
null
)
c
.
setCoefficient
(
sdVar
,
1.0
);
&&
sd
.
getPeriod
().
getIndex
()
==
p
.
getIndex
())
{
MPVariable
sdVar
=
sdVars
.
get
(
sd
.
getKey
());
if
(
sdVar
!=
null
)
c
.
setCoefficient
(
sdVar
,
1.0
);
}
}
}
// + OperationDemandQty (BOM 消耗)
// + OperationDemandQty (BOM 消耗)
for
(
OperationInput
input
:
data
.
getOperationInputs
())
{
for
(
OperationInput
input
:
data
.
getOperationInputsFor
(
prod
.
getId
(),
sp
.
getId
()))
{
if
(
input
.
getInputProduct
().
getId
().
equals
(
prod
.
getId
())
String
opKey
=
input
.
getKey
()
+
"_"
+
p
.
getIndex
();
&&
input
.
getInputSp
().
getId
().
equals
(
sp
.
getId
()))
{
MPVariable
odVar
=
opDemandVars
.
get
(
opKey
);
String
opKey
=
input
.
getKey
()
+
"_"
+
p
.
getIndex
();
if
(
odVar
!=
null
)
c
.
setCoefficient
(
odVar
,
1.0
);
MPVariable
odVar
=
opDemandVars
.
get
(
opKey
);
if
(
odVar
!=
null
)
c
.
setCoefficient
(
odVar
,
1.0
);
}
}
}
}
}
}
}
...
...
src/main/java/com/aps/macroplanner/constraint/DemandSlackLinkageConstraint.java
View file @
2ec4b057
...
@@ -6,6 +6,7 @@ import com.google.ortools.linearsolver.MPVariable;
...
@@ -6,6 +6,7 @@ import com.google.ortools.linearsolver.MPVariable;
import
com.aps.macroplanner.data.*
;
import
com.aps.macroplanner.data.*
;
import
com.aps.macroplanner.model.MacroPlannerModel
;
import
com.aps.macroplanner.model.MacroPlannerModel
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
/**
/**
...
@@ -58,25 +59,22 @@ public class DemandSlackLinkageConstraint {
...
@@ -58,25 +59,22 @@ public class DemandSlackLinkageConstraint {
for
(
Period
p
:
data
.
getPeriods
())
{
for
(
Period
p
:
data
.
getPeriods
())
{
String
pispipKey
=
prod
.
getId
()
+
"_"
+
sp
.
getId
()
+
"_"
+
p
.
getIndex
();
String
pispipKey
=
prod
.
getId
()
+
"_"
+
sp
.
getId
()
+
"_"
+
p
.
getIndex
();
double
totalDemandQty
=
0.0
;
boolean
hasDemand
=
false
;
// 汇总该 PISPIP 的所有销售需求
// 汇总该 PISPIP 的所有销售需求
for
(
SalesDemand
sd
:
data
.
getSalesDemandsFor
(
prod
,
sp
,
p
))
{
List
<
SalesDemand
>
demands
=
data
.
getSalesDemandsFor
(
prod
,
sp
,
p
);
if
(
demands
.
isEmpty
())
continue
;
double
totalDemandQty
=
0.0
;
for
(
SalesDemand
sd
:
demands
)
{
totalDemandQty
+=
sd
.
getQuantity
();
totalDemandQty
+=
sd
.
getQuantity
();
hasDemand
=
true
;
}
}
// 只对存在销售需求的 PISPIP 创建约束
if
(!
hasDemand
)
continue
;
// 约束: Σ SalesDemandQty + DemandSlack >= totalDemandQty
// 约束: Σ SalesDemandQty + DemandSlack >= totalDemandQty
MPConstraint
c
=
model
.
getSolver
().
makeConstraint
(
MPConstraint
c
=
model
.
getSolver
().
makeConstraint
(
totalDemandQty
,
MPSolver
.
infinity
(),
totalDemandQty
,
MPSolver
.
infinity
(),
"DSLink_"
+
pispipKey
);
"DSLink_"
+
pispipKey
);
// + Σ SalesDemandQty
// + Σ SalesDemandQty
for
(
SalesDemand
sd
:
d
ata
.
getSalesDemandsFor
(
prod
,
sp
,
p
)
)
{
for
(
SalesDemand
sd
:
d
emands
)
{
MPVariable
sdVar
=
sdVars
.
get
(
sd
.
getKey
());
MPVariable
sdVar
=
sdVars
.
get
(
sd
.
getKey
());
if
(
sdVar
!=
null
)
c
.
setCoefficient
(
sdVar
,
1.0
);
if
(
sdVar
!=
null
)
c
.
setCoefficient
(
sdVar
,
1.0
);
}
}
...
...
src/main/java/com/aps/macroplanner/data/BenchmarkDataBuilder.java
View file @
2ec4b057
...
@@ -19,9 +19,9 @@ import java.util.Random;
...
@@ -19,9 +19,9 @@ import java.util.Random;
*/
*/
public
class
BenchmarkDataBuilder
extends
TestDataBuilder
{
public
class
BenchmarkDataBuilder
extends
TestDataBuilder
{
public
static
final
int
[]
SUPPORTED_SCALES
=
{
100
,
200
,
500
,
1000
,
2000
,
5000
};
public
static
final
int
[]
SUPPORTED_SCALES
=
{
100
,
200
,
500
,
1000
,
2000
,
5000
,
10000
};
public
static
final
long
RANDOM_SEED
=
20260826L
;
public
static
final
long
RANDOM_SEED
=
20260826L
;
public
static
final
int
PERIOD_COUNT
=
15
;
public
static
final
int
PERIOD_COUNT
=
30
;
private
static
final
int
SEMI_DIVISOR
=
5
;
private
static
final
int
SEMI_DIVISOR
=
5
;
private
static
final
int
RAW_MULTIPLIER
=
2
;
private
static
final
int
RAW_MULTIPLIER
=
2
;
...
...
src/main/java/com/aps/macroplanner/data/DataValidator.java
View file @
2ec4b057
...
@@ -179,6 +179,7 @@ public class DataValidator {
...
@@ -179,6 +179,7 @@ public class DataValidator {
// 循环依赖检测 (含库存点: 仅当产品+库存点都匹配时才构成循环)
// 循环依赖检测 (含库存点: 仅当产品+库存点都匹配时才构成循环)
// 多工序路由中同一产品经不同库存点流转不构成循环
// 多工序路由中同一产品经不同库存点流转不构成循环
// 优化: 通过 getOperationInputsFor 索引查找反向边, 避免 O(Inputs²) 全表扫描
for
(
OperationInput
input
:
data
.
getOperationInputs
())
{
for
(
OperationInput
input
:
data
.
getOperationInputs
())
{
String
consumedProd
=
input
.
getInputProduct
().
getId
();
String
consumedProd
=
input
.
getInputProduct
().
getId
();
String
consumedSp
=
input
.
getInputSp
().
getId
();
String
consumedSp
=
input
.
getInputSp
().
getId
();
...
@@ -188,11 +189,10 @@ public class DataValidator {
...
@@ -188,11 +189,10 @@ public class DataValidator {
String
consumerProd
=
consumerOutput
.
getProductId
();
String
consumerProd
=
consumerOutput
.
getProductId
();
String
consumerSp
=
consumerOutput
.
getSpId
();
String
consumerSp
=
consumerOutput
.
getSpId
();
for
(
OperationInput
other
:
data
.
getOperationInputs
())
{
// 反向边: 消耗 consumerProd@consumerSp 的输入
// 检查 other 是否产出 consumedProd@consumedSp 且消耗 consumerProd@consumerSp
for
(
OperationInput
other
:
data
.
getOperationInputsFor
(
consumerProd
,
consumerSp
))
{
if
(
other
.
getOperation
().
producesProductAtSp
(
consumedProd
,
consumedSp
)
// 检查 other 是否产出 consumedProd@consumedSp (形成 A→B→A)
&&
other
.
getInputProduct
().
getId
().
equals
(
consumerProd
)
if
(
other
.
getOperation
().
producesProductAtSp
(
consumedProd
,
consumedSp
))
{
&&
other
.
getInputSp
().
getId
().
equals
(
consumerSp
))
{
errors
.
add
(
"BOM 循环依赖: "
+
consumerProd
+
"@"
+
consumerSp
errors
.
add
(
"BOM 循环依赖: "
+
consumerProd
+
"@"
+
consumerSp
+
" → "
+
consumedProd
+
"@"
+
consumedSp
+
" → "
+
consumedProd
+
"@"
+
consumedSp
+
" → "
+
consumerProd
+
"@"
+
consumerSp
);
+
" → "
+
consumerProd
+
"@"
+
consumerSp
);
...
...
src/main/java/com/aps/macroplanner/data/TestDataBuilder.java
View file @
2ec4b057
...
@@ -63,6 +63,17 @@ public class TestDataBuilder {
...
@@ -63,6 +63,17 @@ public class TestDataBuilder {
/** 初始库存列表 (子类可访问) */
/** 初始库存列表 (子类可访问) */
protected
final
List
<
InitialInventory
>
initialInventories
=
new
ArrayList
<>();
protected
final
List
<
InitialInventory
>
initialInventories
=
new
ArrayList
<>();
// ==================== 查询索引 (懒加载, 避免约束构建中的 O(N²) 线性扫描) ====================
private
Map
<
String
,
List
<
StockingPoint
>>
stockingPointsByProductIndex
;
private
Map
<
String
,
List
<
SalesDemand
>>
salesDemandsIndex
;
private
Map
<
String
,
List
<
OperationInput
>>
operationInputsIndex
;
private
Map
<
String
,
List
<
Operation
>>
operationsByOutputIndex
;
private
Map
<
String
,
List
<
Operation
>>
operationsByProductIndex
;
private
Map
<
String
,
List
<
Operation
>>
operationsByUnitIndex
;
private
Map
<
String
,
Double
>
initialInventoryIndex
;
private
Map
<
String
,
InventorySpec
>
inventorySpecIndex
;
private
Map
<
String
,
List
<
InTransitSupply
>>
inTransitSuppliesByProductSpIndex
;
/** 公开构造器 — 自动调用 build() 初始化默认测试数据 */
/** 公开构造器 — 自动调用 build() 初始化默认测试数据 */
public
TestDataBuilder
()
{
public
TestDataBuilder
()
{
this
(
false
);
this
(
false
);
...
@@ -295,13 +306,83 @@ public class TestDataBuilder {
...
@@ -295,13 +306,83 @@ public class TestDataBuilder {
* @return 库存点列表, 不存在则返回空列表
* @return 库存点列表, 不存在则返回空列表
*/
*/
public
List
<
StockingPoint
>
getStockingPointsForProduct
(
String
productId
)
{
public
List
<
StockingPoint
>
getStockingPointsForProduct
(
String
productId
)
{
List
<
StockingPoint
>
result
=
new
ArrayList
<>();
if
(
stockingPointsByProductIndex
==
null
)
{
for
(
ProductSpMapping
m
:
productSpMappings
)
{
stockingPointsByProductIndex
=
new
HashMap
<>();
if
(
m
.
getProduct
().
getId
().
equals
(
productId
))
{
for
(
ProductSpMapping
m
:
productSpMappings
)
{
result
.
add
(
m
.
getStockingPoint
());
stockingPointsByProductIndex
.
computeIfAbsent
(
m
.
getProduct
().
getId
(),
k
->
new
ArrayList
<>())
.
add
(
m
.
getStockingPoint
());
}
}
return
stockingPointsByProductIndex
.
getOrDefault
(
productId
,
Collections
.
emptyList
());
}
/**
* 获取产出到指定产品/库存点的所有操作 (索引查询, O(1))。
*/
public
List
<
Operation
>
getOperationsProducing
(
String
productId
,
String
spId
)
{
if
(
operationsByOutputIndex
==
null
)
{
operationsByOutputIndex
=
new
HashMap
<>();
for
(
Operation
op
:
operations
)
{
for
(
OperationOutput
oo
:
op
.
getOutputs
())
{
operationsByOutputIndex
.
computeIfAbsent
(
oo
.
getProductId
()
+
"_"
+
oo
.
getSpId
(),
k
->
new
ArrayList
<>())
.
add
(
op
);
}
}
}
return
operationsByOutputIndex
.
getOrDefault
(
productId
+
"_"
+
spId
,
Collections
.
emptyList
());
}
/**
* 获取产出指定产品 (任意库存点) 的所有操作 (索引查询, O(1))。
*/
public
List
<
Operation
>
getOperationsProducingProduct
(
String
productId
)
{
if
(
operationsByProductIndex
==
null
)
{
operationsByProductIndex
=
new
HashMap
<>();
for
(
Operation
op
:
operations
)
{
for
(
OperationOutput
oo
:
op
.
getOutputs
())
{
operationsByProductIndex
.
computeIfAbsent
(
oo
.
getProductId
(),
k
->
new
ArrayList
<>())
.
add
(
op
);
}
}
}
return
operationsByProductIndex
.
getOrDefault
(
productId
,
Collections
.
emptyList
());
}
/**
* 获取在指定单元上执行的所有操作 (索引查询, O(1))。
*/
public
List
<
Operation
>
getOperationsByUnitId
(
String
unitId
)
{
if
(
operationsByUnitIndex
==
null
)
{
operationsByUnitIndex
=
new
HashMap
<>();
for
(
Operation
op
:
operations
)
{
for
(
UnitOperation
uo
:
op
.
getUnitOperations
())
{
operationsByUnitIndex
.
computeIfAbsent
(
uo
.
getUnitId
(),
k
->
new
ArrayList
<>())
.
add
(
op
);
}
}
}
return
operationsByUnitIndex
.
getOrDefault
(
unitId
,
Collections
.
emptyList
());
}
/**
* 获取消耗指定产品/库存点的所有 BOM 输入 (索引查询, O(1))。
*/
public
List
<
OperationInput
>
getOperationInputsFor
(
String
productId
,
String
spId
)
{
if
(
operationInputsIndex
==
null
)
{
operationInputsIndex
=
new
HashMap
<>();
for
(
OperationInput
input
:
operationInputs
)
{
operationInputsIndex
.
computeIfAbsent
(
input
.
getInputProduct
().
getId
()
+
"_"
+
input
.
getInputSp
().
getId
(),
k
->
new
ArrayList
<>())
.
add
(
input
);
}
}
}
}
return
result
;
return
operationInputsIndex
.
getOrDefault
(
productId
+
"_"
+
spId
,
Collections
.
emptyList
())
;
}
}
public
List
<
Operation
>
getOperations
()
{
return
operations
;
}
public
List
<
Operation
>
getOperations
()
{
return
operations
;
}
public
List
<
OperationInput
>
getOperationInputs
()
{
return
operationInputs
;
}
public
List
<
OperationInput
>
getOperationInputs
()
{
return
operationInputs
;
}
...
@@ -323,42 +404,62 @@ public class TestDataBuilder {
...
@@ -323,42 +404,62 @@ public class TestDataBuilder {
* @return 初始库存量,不存在则返回 0
* @return 初始库存量,不存在则返回 0
*/
*/
public
double
getInitialInventory
(
String
productId
,
String
spId
)
{
public
double
getInitialInventory
(
String
productId
,
String
spId
)
{
for
(
InitialInventory
inv
:
initialInventories
)
{
if
(
initialInventoryIndex
==
null
)
{
if
(
inv
.
getProduct
().
getId
().
equals
(
productId
)
initialInventoryIndex
=
new
HashMap
<>();
&&
inv
.
getStockingPoint
().
getId
().
equals
(
spId
))
{
for
(
InitialInventory
inv
:
initialInventories
)
{
return
inv
.
getQuantity
();
initialInventoryIndex
.
put
(
inv
.
getProduct
().
getId
()
+
"_"
+
inv
.
getStockingPoint
().
getId
(),
inv
.
getQuantity
());
}
}
}
}
return
0.0
;
return
initialInventoryIndex
.
getOrDefault
(
productId
+
"_"
+
spId
,
0.0
)
;
}
}
/**
/**
* 获取指定产品/库存点/周期的销售需求
* 获取指定产品/库存点/周期的销售需求
*/
*/
public
List
<
SalesDemand
>
getSalesDemandsFor
(
Product
product
,
StockingPoint
sp
,
Period
period
)
{
public
List
<
SalesDemand
>
getSalesDemandsFor
(
Product
product
,
StockingPoint
sp
,
Period
period
)
{
List
<
SalesDemand
>
result
=
new
ArrayList
<>();
if
(
salesDemandsIndex
==
null
)
{
for
(
SalesDemand
sd
:
salesDemands
)
{
salesDemandsIndex
=
new
HashMap
<>();
if
(
sd
.
getProduct
().
getId
().
equals
(
product
.
getId
())
for
(
SalesDemand
sd
:
salesDemands
)
{
&&
sd
.
getStockingPoint
().
getId
().
equals
(
sp
.
getId
())
salesDemandsIndex
&&
sd
.
getPeriod
().
getIndex
()
==
period
.
getIndex
())
{
.
computeIfAbsent
(
sd
.
getKey
(),
k
->
new
ArrayList
<>())
result
.
add
(
sd
);
.
add
(
sd
);
}
}
}
}
return
result
;
return
salesDemandsIndex
.
getOrDefault
(
product
.
getId
()
+
"_"
+
sp
.
getId
()
+
"_"
+
period
.
getIndex
(),
Collections
.
emptyList
());
}
}
/**
/**
* 获取指定产品/库存点/周期的库存规格
* 获取指定产品/库存点/周期的库存规格
*/
*/
public
InventorySpec
getInventorySpecFor
(
Product
product
,
StockingPoint
sp
,
Period
period
)
{
public
InventorySpec
getInventorySpecFor
(
Product
product
,
StockingPoint
sp
,
Period
period
)
{
for
(
InventorySpec
spec
:
inventorySpecs
)
{
if
(
inventorySpecIndex
==
null
)
{
if
(
spec
.
getProduct
().
getId
().
equals
(
product
.
getId
())
inventorySpecIndex
=
new
HashMap
<>();
&&
spec
.
getStockingPoint
().
getId
().
equals
(
sp
.
getId
())
for
(
InventorySpec
spec
:
inventorySpecs
)
{
&&
spec
.
getPeriod
().
getIndex
()
==
period
.
getIndex
())
{
inventorySpecIndex
.
put
(
spec
.
getKey
(),
spec
);
return
spec
;
}
}
}
}
return
null
;
return
inventorySpecIndex
.
get
(
product
.
getId
()
+
"_"
+
sp
.
getId
()
+
"_"
+
period
.
getIndex
());
}
/**
* 获取指定产品/库存点的所有在途供应 (索引查询, O(1))。
*/
public
List
<
InTransitSupply
>
getInTransitSuppliesFor
(
String
productId
,
String
spId
)
{
if
(
inTransitSuppliesByProductSpIndex
==
null
)
{
inTransitSuppliesByProductSpIndex
=
new
HashMap
<>();
for
(
InTransitSupply
its
:
inTransitSupplies
)
{
inTransitSuppliesByProductSpIndex
.
computeIfAbsent
(
its
.
getProduct
().
getId
()
+
"_"
+
its
.
getStockingPoint
().
getId
(),
k
->
new
ArrayList
<>())
.
add
(
its
);
}
}
return
inTransitSuppliesByProductSpIndex
.
getOrDefault
(
productId
+
"_"
+
spId
,
Collections
.
emptyList
());
}
}
/**
/**
...
...
src/main/java/com/aps/macroplanner/data/UnitOperation.java
View file @
2ec4b057
...
@@ -12,7 +12,7 @@ public class UnitOperation {
...
@@ -12,7 +12,7 @@ public class UnitOperation {
private
final
String
unitId
;
// 所属单元ID
private
final
String
unitId
;
// 所属单元ID
private
final
String
unitName
;
// 所属单元Name
private
final
String
unitName
;
// 所属单元Name
private
final
double
capacityCoeff
;
// 产能消耗系数 (单件
耗
时)
private
final
double
capacityCoeff
;
// 产能消耗系数 (单件
工
时)
private
final
boolean
hasLotSize
;
// 是否有批次大小
private
final
boolean
hasLotSize
;
// 是否有批次大小
private
final
double
lotSize
;
// 批次大小
private
final
double
lotSize
;
// 批次大小
private
final
double
qtpfactor
;
// QuantityToProcessFactor
private
final
double
qtpfactor
;
// QuantityToProcessFactor
...
...
src/main/java/com/aps/macroplanner/model/MacroPlannerModel.java
View file @
2ec4b057
...
@@ -29,7 +29,7 @@ public class MacroPlannerModel {
...
@@ -29,7 +29,7 @@ public class MacroPlannerModel {
private
final
MPSolver
solver
;
private
final
MPSolver
solver
;
// ==================== 生产变量 ====================
// ==================== 生产变量 ====================
/** PTQty[operationId_periodIndex] — 生产量, 范围 [0, +∞) */
/** PTQty[operationId_
Uintid_
periodIndex] — 生产量, 范围 [0, +∞) */
private
final
Map
<
String
,
MPVariable
>
ptQtyVars
=
new
HashMap
<>();
private
final
Map
<
String
,
MPVariable
>
ptQtyVars
=
new
HashMap
<>();
// ==================== 库存变量 ====================
// ==================== 库存变量 ====================
...
...
src/main/java/com/aps/macroplanner/output/ResultWriter.java
View file @
2ec4b057
This diff is collapsed.
Click to expand it.
src/main/java/com/aps/macroplanner/output/SolutionPrinter.java
View file @
2ec4b057
...
@@ -58,7 +58,7 @@ public class SolutionPrinter {
...
@@ -58,7 +58,7 @@ public class SolutionPrinter {
// printBomStructure();
// printBomStructure();
// printHeader();
// printHeader();
// printDailyViews();
// printDailyViews();
// printKpiSummary();
// printKpiSummary();
printStatistics
();
printStatistics
();
}
}
...
@@ -318,10 +318,8 @@ public class SolutionPrinter {
...
@@ -318,10 +318,8 @@ public class SolutionPrinter {
// 在途到货
// 在途到货
double
inTransitQty
=
0
;
double
inTransitQty
=
0
;
for
(
InTransitSupply
its
:
data
.
getInTransitSupplies
())
{
for
(
InTransitSupply
its
:
data
.
getInTransitSuppliesFor
(
prod
.
getId
(),
sp
.
getId
()))
{
if
(
its
.
getProduct
().
getId
().
equals
(
prod
.
getId
())
if
(
p
.
equals
(
data
.
getPeriodByDate
(
its
.
getArrivalDate
())))
{
&&
its
.
getStockingPoint
().
getId
().
equals
(
sp
.
getId
())
&&
p
.
equals
(
data
.
getPeriodByDate
(
its
.
getArrivalDate
())))
{
inTransitQty
+=
its
.
getQuantity
();
inTransitQty
+=
its
.
getQuantity
();
}
}
}
}
...
@@ -540,10 +538,8 @@ public class SolutionPrinter {
...
@@ -540,10 +538,8 @@ public class SolutionPrinter {
// 在途到货
// 在途到货
double
inTransit
=
0
;
double
inTransit
=
0
;
for
(
InTransitSupply
its
:
data
.
getInTransitSupplies
())
{
for
(
InTransitSupply
its
:
data
.
getInTransitSuppliesFor
(
inputProd
.
getId
(),
inputSp
.
getId
()))
{
if
(
its
.
getProduct
().
getId
().
equals
(
inputProd
.
getId
())
if
(
p
.
equals
(
data
.
getPeriodByDate
(
its
.
getArrivalDate
())))
{
&&
its
.
getStockingPoint
().
getId
().
equals
(
inputSp
.
getId
())
&&
p
.
equals
(
data
.
getPeriodByDate
(
its
.
getArrivalDate
())))
{
inTransit
+=
its
.
getQuantity
();
inTransit
+=
its
.
getQuantity
();
}
}
}
}
...
...
src/main/java/com/aps/macroplanner/output/dto/PeriodTaskResult.java
View file @
2ec4b057
package
com
.
aps
.
macroplanner
.
output
.
dto
;
package
com
.
aps
.
macroplanner
.
output
.
dto
;
import
lombok.Data
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
...
@@ -8,8 +10,10 @@ import java.util.List;
...
@@ -8,8 +10,10 @@ import java.util.List;
*
*
* <p>每个操作在每个周期的生产量、产能消耗、批次偏差等。</p>
* <p>每个操作在每个周期的生产量、产能消耗、批次偏差等。</p>
*/
*/
@Data
public
class
PeriodTaskResult
{
public
class
PeriodTaskResult
{
private
String
snapshotId
;
private
String
dataKey
;
private
String
operationId
;
private
String
operationId
;
private
String
operationName
;
private
String
operationName
;
private
String
unitId
;
private
String
unitId
;
...
@@ -18,9 +22,9 @@ public class PeriodTaskResult {
...
@@ -18,9 +22,9 @@ public class PeriodTaskResult {
/** PTQty — 生产量 */
/** PTQty — 生产量 */
private
double
productionQty
;
private
double
productionQty
;
/** 产能消耗 = productionQty × coefficient */
/** 产能消耗
总工时
= productionQty × coefficient */
private
double
capacityUsed
;
private
double
capacityUsed
;
/**
产能消耗系数
*/
/**
单件工时
*/
private
double
capacityCoeff
;
private
double
capacityCoeff
;
/** 批次大小 (如有) */
/** 批次大小 (如有) */
...
@@ -31,51 +35,16 @@ public class PeriodTaskResult {
...
@@ -31,51 +35,16 @@ public class PeriodTaskResult {
private
double
lotSizeUnder
;
private
double
lotSizeUnder
;
/** 产出: 产品 → 库存点 */
/** 产出: 产品 → 库存点 */
private
final
List
<
OutputInfo
>
outputs
=
new
ArrayList
<>();
//
private final List<OutputInfo> outputs = new ArrayList<>();
// ==================== 内嵌类 ====================
// ==================== 内嵌类 ====================
/** 产出信息 */
/** 产出信息 */
public
static
class
OutputInfo
{
// public static class OutputInfo {
public
String
productId
;
// public String productId;
public
String
spId
;
// public String spId;
public
double
factor
;
// public double factor;
}
// }
// ==================== Getters / Setters ====================
public
String
getOperationId
()
{
return
operationId
;
}
public
void
setOperationId
(
String
v
)
{
this
.
operationId
=
v
;
}
public
String
getOperationName
()
{
return
operationName
;
}
public
void
setOperationName
(
String
v
)
{
this
.
operationName
=
v
;
}
public
String
getUnitId
()
{
return
unitId
;
}
public
void
setUnitId
(
String
v
)
{
this
.
unitId
=
v
;
}
public
int
getPeriodIndex
()
{
return
periodIndex
;
}
public
void
setPeriodIndex
(
int
v
)
{
this
.
periodIndex
=
v
;
}
public
String
getPeriodStartDate
()
{
return
periodStartDate
;
}
public
void
setPeriodStartDate
(
String
v
)
{
this
.
periodStartDate
=
v
;
}
public
double
getProductionQty
()
{
return
productionQty
;
}
public
void
setProductionQty
(
double
v
)
{
this
.
productionQty
=
v
;
}
public
double
getCapacityUsed
()
{
return
capacityUsed
;
}
public
void
setCapacityUsed
(
double
v
)
{
this
.
capacityUsed
=
v
;
}
public
double
getCapacityCoeff
()
{
return
capacityCoeff
;
}
public
void
setCapacityCoeff
(
double
v
)
{
this
.
capacityCoeff
=
v
;
}
public
Double
getLotSize
()
{
return
lotSize
;
}
public
void
setLotSize
(
Double
v
)
{
this
.
lotSize
=
v
;
}
public
double
getLotSizeOver
()
{
return
lotSizeOver
;
}
public
void
setLotSizeOver
(
double
v
)
{
this
.
lotSizeOver
=
v
;
}
public
double
getLotSizeUnder
()
{
return
lotSizeUnder
;
}
public
void
setLotSizeUnder
(
double
v
)
{
this
.
lotSizeUnder
=
v
;
}
public
List
<
OutputInfo
>
getOutputs
()
{
return
outputs
;
}
}
}
\ No newline at end of file
src/main/java/com/aps/macroplanner/output/dto/PispipResult.java
View file @
2ec4b057
package
com
.
aps
.
macroplanner
.
output
.
dto
;
package
com
.
aps
.
macroplanner
.
output
.
dto
;
import
lombok.Data
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
...
@@ -8,6 +10,7 @@ import java.util.List;
...
@@ -8,6 +10,7 @@ import java.util.List;
*
*
* <p>每个产品在每个库存点每个周期的库存状态、流入流出、规格偏差。</p>
* <p>每个产品在每个库存点每个周期的库存状态、流入流出、规格偏差。</p>
*/
*/
@Data
public
class
PispipResult
{
public
class
PispipResult
{
private
String
productId
;
private
String
productId
;
...
...
src/main/resources/logback-spring.xml
View file @
2ec4b057
...
@@ -95,6 +95,13 @@
...
@@ -95,6 +95,13 @@
<logger
name=
"org.mybatis"
level=
"INFO"
/>
<logger
name=
"org.mybatis"
level=
"INFO"
/>
<logger
name=
"com.zaxxer.hikari"
level=
"INFO"
/>
<logger
name=
"com.zaxxer.hikari"
level=
"INFO"
/>
<!-- 关闭 Parquet 内部 DEBUG 刷屏日志 -->
<logger
name=
"org.apache.parquet"
level=
"ERROR"
additivity=
"false"
>
<appender-ref
ref=
"CONSOLE"
/>
<!-- 替换为你项目实际的控制台/文件 appender 名称 -->
</logger>
<!-- 屏蔽 Hadoop Shell 的冗余警告 -->
<logger
name=
"org.apache.hadoop.util.Shell"
level=
"ERROR"
additivity=
"false"
/>
<!-- 根据不同环境调整 -->
<!-- 根据不同环境调整 -->
<springProfile
name=
"dev"
>
<springProfile
name=
"dev"
>
<root
level=
"INFO"
>
<root
level=
"INFO"
>
...
...
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