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
f76aaece
Commit
f76aaece
authored
Sep 14, 2026
by
DESKTOP-VKRD9QF\Administration
Browse files
Options
Browse Files
Download
Plain Diff
merge: integrate remote macro planner changes
parents
7017fffd
2ec4b057
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
580 additions
and
271 deletions
+580
-271
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
-146
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 @
f76aaece
...
@@ -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 @
f76aaece
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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
...
@@ -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 @
f76aaece
This diff is collapsed.
Click to expand it.
src/main/java/com/aps/macroplanner/output/SolutionPrinter.java
View file @
f76aaece
...
@@ -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 @
f76aaece
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 @
f76aaece
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 @
f76aaece
...
@@ -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