Skip to content
切换导航条
切换导航条
当前项目
正在载入...
登录
孙克
/
smf-core
转到一个项目
切换导航栏
切换导航栏固定状态
项目
群组
代码片段
帮助
项目
活动
版本库
流水线
图表
问题
0
合并请求
0
维基
网络
创建新的问题
作业
提交
问题看板
文件
提交
网络
比较
分支
标签
Commit 2d97e6be
由
zshaohui
编写于
2023-05-31 10:02:57 +0800
浏览文件
选项
浏览文件
标签
下载
电子邮件补丁
差异文件
1.外仓过期报表和过期看板
2.外仓每次出库 不删除物料信息
1 个父辈
421e1815
全部展开
隐藏空白字符变更
内嵌
并排
正在显示
9 个修改的文件
包含
142 行增加
和
29 行删除
src/main/java/com/neotel/smfcore/core/barcode/service/manager/IBarcodeManager.java
src/main/java/com/neotel/smfcore/core/barcode/service/manager/impl/BarcodeManagerImpl.java
src/main/java/com/neotel/smfcore/core/barcode/utils/CodeResolve.java
src/main/java/com/neotel/smfcore/custom/lizhen/LizhenApi.java
src/main/java/com/neotel/smfcore/custom/lizhen/agvBox/rest/OutLineController.java
src/main/java/com/neotel/smfcore/custom/lizhen/agvBox/rest/WarehouseController.java
src/main/java/com/neotel/smfcore/custom/lizhen/kanban/outer/OuterKanbanController.java
src/main/java/com/neotel/smfcore/custom/lizhen/report/bean/dto/ExpireDto.java
src/main/java/com/neotel/smfcore/custom/lizhen/report/outer/OuterReportController.java
src/main/java/com/neotel/smfcore/core/barcode/service/manager/IBarcodeManager.java
查看文件 @
2d97e6b
...
...
@@ -2,6 +2,7 @@ package com.neotel.smfcore.core.barcode.service.manager;
import
com.neotel.smfcore.common.base.IBaseManager
;
import
com.neotel.smfcore.core.barcode.service.po.Barcode
;
import
org.springframework.data.domain.Pageable
;
import
org.springframework.data.mongodb.core.query.Query
;
import
org.springframework.data.mongodb.core.query.Update
;
...
...
@@ -25,4 +26,6 @@ public interface IBarcodeManager extends IBaseManager<Barcode> {
void
updateBarcode
(
Query
query
,
Update
update
);
List
<
Barcode
>
findByPosName
(
String
barcodeStr
);
List
<
Barcode
>
findByQuery
(
Query
query
,
Pageable
pageable
);
}
src/main/java/com/neotel/smfcore/core/barcode/service/manager/impl/BarcodeManagerImpl.java
查看文件 @
2d97e6b
...
...
@@ -190,6 +190,11 @@ public class BarcodeManagerImpl implements IBarcodeManager {
return
barcodeDao
.
findByQuery
(
q
);
}
@Override
public
List
<
Barcode
>
findByQuery
(
Query
query
,
Pageable
pageable
)
{
return
barcodeDao
.
findByQuery
(
query
,
pageable
);
}
protected
boolean
validateComponent
(
Barcode
barcode
)
{
return
componentManager
.
findOneByPN
(
barcode
.
getPartNumber
())
!=
null
;
}
...
...
src/main/java/com/neotel/smfcore/core/barcode/utils/CodeResolve.java
查看文件 @
2d97e6b
...
...
@@ -5,6 +5,7 @@ import com.google.common.collect.Lists;
import
com.neotel.smfcore.common.exception.ValidateException
;
import
com.neotel.smfcore.common.utils.DateUtil
;
import
com.neotel.smfcore.common.utils.StringUtils
;
import
com.neotel.smfcore.core.api.SmfApi
;
import
com.neotel.smfcore.core.barcode.bean.BarcodeRule
;
import
com.neotel.smfcore.core.barcode.bean.CodeBean
;
import
com.neotel.smfcore.core.barcode.enums.COMPONENT_TYPE
;
...
...
@@ -12,6 +13,7 @@ import com.neotel.smfcore.core.barcode.service.manager.IBarcodeManager;
import
com.neotel.smfcore.core.barcode.service.manager.IComponentManager
;
import
com.neotel.smfcore.core.barcode.service.po.Barcode
;
import
com.neotel.smfcore.core.barcode.service.po.Component
;
import
com.neotel.smfcore.custom.lizhen.LizhenApi
;
import
com.neotel.smfcore.custom.lizhen.agvBox.bean.GrLabel
;
import
com.neotel.smfcore.custom.lizhen.setting.bean.ExpiredSetting
;
import
com.neotel.smfcore.custom.lizhen.setting.service.manager.ExpiredSettingManager
;
...
...
@@ -20,10 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.stereotype.Service
;
import
org.yaml.snakeyaml.comments.CommentType
;
import
java.util.Collection
;
import
java.util.Date
;
import
java.util.List
;
import
java.util.Locale
;
import
java.util.*
;
/**
* Created by sunke on 2021/7/13.
...
...
@@ -41,6 +40,7 @@ public class CodeResolve {
@Autowired
private
ExpiredSettingManager
expiredSettingManager
;
private
List
<
BarcodeRule
>
barcodeRuleList
;
public
void
updateBarcodeRuleList
(
List
<
String
>
ruleList
){
...
...
@@ -139,15 +139,14 @@ public class CodeResolve {
Barcode
barcodeFromRule
=
codeBeanFromRule
.
getBarcode
();
if
(
barcodeFromRule
!=
null
){
boolean
needUpdate
=
false
;
/*int lastLabelAmount = barcode.getLabelAmount
();
int currentLabelAmount = barcodeFromRule.getAmount
();
if
(currentLabelAmount != lastLabelAmount)
{
String
lastLabelPartNumber
=
barcode
.
getPartNumber
();
String
currentLabelPartNumber
=
barcodeFromRule
.
getPartNumber
();
if
(!
lastLabelPartNumber
.
equals
(
currentLabelPartNumber
))
{
//本次解析出来的数量与上次条码解析出来的数量不一样,重新设置数量
log.info("重新设置"+codeBeanFromRule.getCodeStr()+"数量为:"+currentLabelAmount);
barcode.setAmount(currentLabelAmount);
barcode.setLabelAmount(currentLabelAmount);
log
.
info
(
"重新设置"
+
codeBeanFromRule
.
getCodeStr
()
+
"partNumber为:"
+
currentLabelPartNumber
);
barcode
.
setPartNumber
(
currentLabelPartNumber
);
needUpdate
=
true
;
}
*/
}
Date
produceDate
=
barcodeFromRule
.
getProduceDate
();
if
(
produceDate
!=
null
){
//抓取到了生产日期,未抓取到过期日期,重新设置过期日期
...
...
@@ -197,7 +196,6 @@ public class CodeResolve {
}
}
}
codeBeanFromRule
.
setError
(
null
,
null
);
codeBeanFromRule
.
setCodeStr
(
barcode
.
getBarcode
());
codeBeanFromRule
.
setBarcode
(
barcode
);
...
...
src/main/java/com/neotel/smfcore/custom/lizhen/LizhenApi.java
查看文件 @
2d97e6b
...
...
@@ -24,6 +24,7 @@ import com.neotel.smfcore.custom.lizhen.innerBox.util.PreWarningItemCache;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Component
;
import
org.springframework.stereotype.Service
;
import
javax.annotation.PostConstruct
;
...
...
@@ -32,7 +33,7 @@ import java.util.*;
import
java.util.stream.Collectors
;
@Slf4j
@
Service
@
Component
public
class
LizhenApi
extends
DefaultSmfApiListener
{
@Autowired
...
...
src/main/java/com/neotel/smfcore/custom/lizhen/agvBox/rest/OutLineController.java
查看文件 @
2d97e6b
...
...
@@ -429,7 +429,6 @@ public class OutLineController {
//生成任务
int
amount
=
subCode
.
getAmount
();
subCode
.
setAmount
(
0
);
subCode
=
barcodeManager
.
save
(
subCode
);
pidBarcode
.
UpdateSubCode
(
subCode
);
pidBarcode
.
setReelAmount
(
pidBarcode
.
getReelAmount
()
-
1
);
pidBarcode
.
setAmount
(
pidBarcode
.
getAmount
()
-
amount
);
...
...
@@ -439,7 +438,14 @@ public class OutLineController {
dataLogList
.
add
(
dataLog
);
log
.
info
(
"整箱出库,生成出库任务,barcode:"
+
subCode
.
getBarcode
()
+
",料箱号为:"
+
pidBarcode
.
getBarcode
());
if
(
subCode
.
getAmount
()
<=
0
)
{
barcodeManager
.
delete
(
subCode
);
subCode
.
setAmount
(
amount
);
subCode
.
setSelectMsg
(
null
);
subCode
.
setOut
(
false
);
subCode
.
setOrderItemId
(
null
);
subCode
.
setPosName
(
null
);
subCode
.
setHostBarcodeId
(
null
);
subCode
.
setStorageId
(
null
);
barcodeManager
.
save
(
subCode
);
}
}
if
(
dataLogList
!=
null
&&
!
dataLogList
.
isEmpty
())
{
...
...
@@ -500,7 +506,6 @@ public class OutLineController {
}
int
amount
=
subCode
.
getAmount
();
subCode
.
setAmount
(
0
);
subCode
=
barcodeManager
.
save
(
subCode
);
pidBarcode
.
UpdateSubCode
(
subCode
);
pidBarcode
.
setReelAmount
(
pidBarcode
.
getReelAmount
()
-
1
);
pidBarcode
.
setAmount
(
pidBarcode
.
getAmount
()
-
amount
);
...
...
@@ -511,7 +516,14 @@ public class OutLineController {
log
.
info
(
"隔口出库,生成出库任务,barcode:"
+
subCode
.
getBarcode
()
+
",料箱号为:"
+
subCode
.
getPosName
());
log
.
info
(
"箱子数量为:"
+
pidBarcode
.
getAmount
()
+
",物料数量为:"
+
pidBarcode
.
getReelAmount
()
+
"箱号为:"
+
pidBarcode
.
getBarcode
());
if
(
subCode
.
getAmount
()
<=
0
)
{
barcodeManager
.
delete
(
subCode
);
subCode
.
setAmount
(
amount
);
subCode
.
setSelectMsg
(
null
);
subCode
.
setOut
(
false
);
subCode
.
setOrderItemId
(
null
);
subCode
.
setPosName
(
null
);
subCode
.
setHostBarcodeId
(
null
);
subCode
.
setStorageId
(
null
);
barcodeManager
.
save
(
subCode
);
}
//}
}
...
...
@@ -599,7 +611,6 @@ public class OutLineController {
}
int
amount
=
barcode
.
getAmount
();
barcode
.
setAmount
(
0
);
barcode
=
barcodeManager
.
save
(
barcode
);
pidBarcode
.
UpdateSubCode
(
barcode
);
pidBarcode
.
setReelAmount
(
pidBarcode
.
getReelAmount
()
-
1
);
pidBarcode
.
setAmount
(
pidBarcode
.
getAmount
()
-
amount
);
...
...
@@ -608,7 +619,14 @@ public class OutLineController {
generateTask
(
barcode
,
opStatus
,
amount
,
opType
,
orderItemId
,
extendType
,
false
);
log
.
info
(
"物料出库,生成出库任务,barcode:"
+
barcode
.
getBarcode
()
+
",隔口号为:"
+
barcode
.
getPosName
());
if
(
barcode
.
getAmount
()
<=
0
)
{
barcodeManager
.
delete
(
barcode
);
barcode
.
setAmount
(
amount
);
barcode
.
setSelectMsg
(
null
);
barcode
.
setOut
(
false
);
barcode
.
setOrderItemId
(
null
);
barcode
.
setPosName
(
null
);
barcode
.
setHostBarcodeId
(
null
);
barcode
.
setStorageId
(
null
);
barcodeManager
.
save
(
barcode
);
}
}
else
{
if
(
subCodes
!=
null
&&
!
subCodes
.
isEmpty
())
{
...
...
@@ -636,7 +654,6 @@ public class OutLineController {
int
amount
=
barcode
.
getAmount
();
barcode
.
setAmount
(
0
);
barcode
.
setOrderItemId
(
orderItemId
);
barcode
=
barcodeManager
.
save
(
barcode
);
pidBarcode
.
setReelAmount
(
pidBarcode
.
getReelAmount
()
-
1
);
pidBarcode
.
setAmount
(
pidBarcode
.
getAmount
()
-
amount
);
pidBarcode
.
UpdateSubCode
(
barcode
);
...
...
@@ -646,7 +663,14 @@ public class OutLineController {
log
.
info
(
barcode
.
getBarcode
()
+
"不是出库任务,"
+
barcodeByOut
.
getBarcode
()
+
"需更改out为false"
);
log
.
info
(
"物料出库,生成出库任务,barcode:"
+
barcode
.
getBarcode
()
+
",隔口号为:"
+
barcode
.
getPosName
());
if
(
barcode
.
getAmount
()
<=
0
)
{
barcodeManager
.
delete
(
barcode
);
barcode
.
setAmount
(
amount
);
barcode
.
setSelectMsg
(
null
);
barcode
.
setOut
(
false
);
barcode
.
setOrderItemId
(
null
);
barcode
.
setPosName
(
null
);
barcode
.
setHostBarcodeId
(
null
);
barcode
.
setStorageId
(
null
);
barcodeManager
.
save
(
barcode
);
}
isOut
=
true
;
...
...
src/main/java/com/neotel/smfcore/custom/lizhen/agvBox/rest/WarehouseController.java
查看文件 @
2d97e6b
...
...
@@ -960,7 +960,6 @@ public class WarehouseController {
//生成任务
int
amount
=
subCode
.
getAmount
();
subCode
.
setAmount
(
0
);
subCode
=
barcodeManager
.
save
(
subCode
);
pidBarcode
.
UpdateSubCode
(
subCode
);
pidBarcode
.
setReelAmount
(
pidBarcode
.
getReelAmount
()
-
1
);
pidBarcode
.
setAmount
(
pidBarcode
.
getAmount
()
-
amount
);
...
...
@@ -971,7 +970,14 @@ public class WarehouseController {
dataLogList
.
add
(
dataLog
);
log
.
info
(
"整箱出库,生成出库任务,barcode:"
+
subCode
.
getBarcode
()
+
",料箱号为:"
+
pidBarcode
.
getBarcode
());
if
(
subCode
.
getAmount
()
<=
0
)
{
barcodeManager
.
delete
(
subCode
);
subCode
.
setAmount
(
amount
);
subCode
.
setSelectMsg
(
null
);
subCode
.
setOut
(
false
);
subCode
.
setOrderItemId
(
null
);
subCode
.
setPosName
(
null
);
subCode
.
setHostBarcodeId
(
null
);
subCode
.
setStorageId
(
null
);
barcodeManager
.
save
(
subCode
);
}
}
if
(
dataLogList
!=
null
&&
!
dataLogList
.
isEmpty
())
{
...
...
@@ -1031,7 +1037,6 @@ public class WarehouseController {
}
int
amount
=
subCode
.
getAmount
();
subCode
.
setAmount
(
0
);
subCode
=
barcodeManager
.
save
(
subCode
);
pidBarcode
.
UpdateSubCode
(
subCode
);
pidBarcode
.
setReelAmount
(
pidBarcode
.
getReelAmount
()
-
1
);
pidBarcode
.
setAmount
(
pidBarcode
.
getAmount
()
-
amount
);
...
...
@@ -1042,7 +1047,14 @@ public class WarehouseController {
log
.
info
(
"隔口出库,生成出库任务,barcode:"
+
subCode
.
getBarcode
()
+
",料箱号为:"
+
subCode
.
getPosName
());
log
.
info
(
"箱子数量为:"
+
pidBarcode
.
getAmount
()
+
",物料数量为:"
+
pidBarcode
.
getReelAmount
()
+
"箱号为:"
+
pidBarcode
.
getBarcode
());
if
(
subCode
.
getAmount
()
<=
0
)
{
barcodeManager
.
delete
(
subCode
);
subCode
.
setAmount
(
amount
);
subCode
.
setSelectMsg
(
null
);
subCode
.
setOut
(
false
);
subCode
.
setOrderItemId
(
null
);
subCode
.
setPosName
(
null
);
subCode
.
setHostBarcodeId
(
null
);
subCode
.
setStorageId
(
null
);
barcodeManager
.
save
(
subCode
);
}
//}
}
...
...
@@ -1124,7 +1136,6 @@ public class WarehouseController {
}
int
amount
=
barcode
.
getAmount
();
barcode
.
setAmount
(
0
);
barcode
=
barcodeManager
.
save
(
barcode
);
pidBarcode
.
UpdateSubCode
(
barcode
);
pidBarcode
.
setReelAmount
(
pidBarcode
.
getReelAmount
()
-
1
);
pidBarcode
.
setAmount
(
pidBarcode
.
getAmount
()
-
amount
);
...
...
@@ -1133,7 +1144,14 @@ public class WarehouseController {
generateTask
(
barcode
,
opStatus
,
amount
,
opType
,
orderItemId
,
name
,
extendType
,
false
);
log
.
info
(
"物料出库,生成出库任务,barcode:"
+
barcode
.
getBarcode
()
+
",隔口号为:"
+
barcode
.
getPosName
());
if
(
barcode
.
getAmount
()
<=
0
)
{
barcodeManager
.
delete
(
barcode
);
barcode
.
setAmount
(
amount
);
barcode
.
setSelectMsg
(
null
);
barcode
.
setOut
(
false
);
barcode
.
setOrderItemId
(
null
);
barcode
.
setPosName
(
null
);
barcode
.
setHostBarcodeId
(
null
);
barcode
.
setStorageId
(
null
);
barcodeManager
.
save
(
barcode
);
}
}
else
{
if
(
subCodes
!=
null
&&
!
subCodes
.
isEmpty
())
{
...
...
@@ -1161,7 +1179,7 @@ public class WarehouseController {
int
amount
=
barcode
.
getAmount
();
barcode
.
setAmount
(
0
);
barcode
.
setOrderItemId
(
orderItemId
);
barcode
=
barcodeManager
.
save
(
barcode
);
//
barcode = barcodeManager.save(barcode);
pidBarcode
.
setReelAmount
(
pidBarcode
.
getReelAmount
()
-
1
);
pidBarcode
.
setAmount
(
pidBarcode
.
getAmount
()
-
amount
);
pidBarcode
.
UpdateSubCode
(
barcode
);
...
...
@@ -1171,7 +1189,14 @@ public class WarehouseController {
log
.
info
(
barcode
.
getBarcode
()
+
"不是出库任务,"
+
barcodeByOut
.
getBarcode
()
+
"需更改out为false"
);
log
.
info
(
"物料出库,生成出库任务,barcode:"
+
barcode
.
getBarcode
()
+
",隔口号为:"
+
barcode
.
getPosName
());
if
(
barcode
.
getAmount
()
<=
0
)
{
barcodeManager
.
delete
(
barcode
);
barcode
.
setAmount
(
amount
);
barcode
.
setSelectMsg
(
null
);
barcode
.
setOut
(
false
);
barcode
.
setOrderItemId
(
null
);
barcode
.
setPosName
(
null
);
barcode
.
setHostBarcodeId
(
null
);
barcode
.
setStorageId
(
null
);
barcodeManager
.
save
(
barcode
);
}
isOut
=
true
;
...
...
src/main/java/com/neotel/smfcore/custom/lizhen/kanban/outer/OuterKanbanController.java
查看文件 @
2d97e6b
package
com
.
neotel
.
smfcore
.
custom
.
lizhen
.
kanban
.
outer
;
import
cn.hutool.core.date.DateUnit
;
import
com.google.common.collect.Maps
;
import
com.neotel.smfcore.common.bean.ResultBean
;
import
com.neotel.smfcore.common.utils.DateUtil
;
import
com.neotel.smfcore.core.barcode.service.manager.IBarcodeManager
;
import
com.neotel.smfcore.core.barcode.service.po.Barcode
;
import
com.neotel.smfcore.core.device.enums.OP
;
import
com.neotel.smfcore.core.device.util.DataCache
;
import
com.neotel.smfcore.core.order.LiteOrderCache
;
import
com.neotel.smfcore.core.order.service.po.LiteOrder
;
import
com.neotel.smfcore.core.storage.service.po.Storage
;
import
com.neotel.smfcore.core.storage.service.po.StoragePos
;
import
com.neotel.smfcore.core.system.service.manager.IDataLogManager
;
import
com.neotel.smfcore.core.system.service.po.DataLog
;
import
com.neotel.smfcore.core.system.util.TaskService
;
...
...
@@ -16,6 +21,8 @@ import com.neotel.smfcore.custom.lizhen.kanban.outer.bean.dto.PickingProgressDto
import
com.neotel.smfcore.custom.lizhen.kanban.outer.bean.dto.StationInOutDto
;
import
com.neotel.smfcore.security.annotation.AnonymousAccess
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.data.mongodb.core.query.Criteria
;
import
org.springframework.data.mongodb.core.query.Query
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
...
...
@@ -38,6 +45,11 @@ public class OuterKanbanController {
@Autowired
private
TaskService
taskService
;
@Autowired
private
IBarcodeManager
barcodeManager
;
public
static
final
Map
<
String
,
Long
>
expireMap
=
Maps
.
newConcurrentMap
();
/**
* 获取近7天出入库统计
*
...
...
@@ -152,4 +164,44 @@ public class OuterKanbanController {
}
return
ResultBean
.
newOkResult
(
dataLogList
);
}
/**
* 过期时间信息
*
* @return
*/
@RequestMapping
(
"/getExpireInfo"
)
@AnonymousAccess
public
ResultBean
getExpireInfo
()
{
Long
lastSaveTime
=
expireMap
.
get
(
"lastSaveTime"
);
if
(
lastSaveTime
==
null
||
System
.
currentTimeMillis
()
-
lastSaveTime
>=
1000
*
60
*
60
)
{
expireMap
.
put
(
"zeroToServen"
,
0
l
);
expireMap
.
put
(
"servenToThirty"
,
0
l
);
expireMap
.
put
(
"expire"
,
0
l
);
Query
query
=
new
Query
().
addCriteria
(
Criteria
.
where
(
"posName"
).
exists
(
true
).
ne
(
""
).
and
(
"expireDate"
).
lt
(
new
Date
()));
List
<
Barcode
>
barcodeList
=
barcodeManager
.
findByQuery
(
query
);
long
zeroToServen
=
barcodeList
.
stream
().
filter
(
item
->
{
Date
expireDate
=
item
.
getExpireDate
();
long
days
=
cn
.
hutool
.
core
.
date
.
DateUtil
.
between
(
expireDate
,
new
Date
(),
DateUnit
.
DAY
)
+
1
;
if
(
days
>=
0
&&
days
<
7
)
{
return
true
;
}
return
false
;
}).
count
();
long
servenToThirty
=
barcodeList
.
stream
().
filter
(
item
->
{
Date
expireDate
=
item
.
getExpireDate
();
long
days
=
cn
.
hutool
.
core
.
date
.
DateUtil
.
between
(
expireDate
,
new
Date
(),
DateUnit
.
DAY
)
+
1
;
if
(
days
>=
7
&&
days
<
30
)
{
return
true
;
}
return
false
;
}).
count
();
expireMap
.
put
(
"zeroToServen"
,
zeroToServen
);
expireMap
.
put
(
"servenToThirty"
,
servenToThirty
);
expireMap
.
put
(
"expire"
,
barcodeList
==
null
?
0
l
:
(
long
)
barcodeList
.
size
());
expireMap
.
put
(
"lastSaveTime"
,
System
.
currentTimeMillis
());
}
return
ResultBean
.
newOkResult
(
expireMap
);
}
}
src/main/java/com/neotel/smfcore/custom/lizhen/report/bean/dto/ExpireDto.java
查看文件 @
2d97e6b
...
...
@@ -38,4 +38,9 @@ public class ExpireDto extends Barcode {
* 来源
*/
private
String
source
;
/**
* 隔口数量
*/
private
Integer
partitionCount
;
}
src/main/java/com/neotel/smfcore/custom/lizhen/report/outer/OuterReportController.java
查看文件 @
2d97e6b
此文件的差异被折叠,
点击展开。
编写
预览
支持
Markdown
格式
附加文件
你添加了
0
人
到此讨论。请谨慎行事。
Finish editing this message first!
Cancel
请
注册
或
登录
后发表评论