Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
J
jyzb_platformV2
概览
Overview
Details
Activity
Cycle Analytics
版本库
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
问题
0
Issues
0
列表
Board
标记
里程碑
合并请求
0
Merge Requests
0
CI / CD
CI / CD
流水线
作业
日程表
图表
维基
Wiki
代码片段
Snippets
成员
Members
Collapse sidebar
Close sidebar
活动
图像
聊天
创建新问题
作业
提交
Issue Boards
Open sidebar
赵剑炜
jyzb_platformV2
Commits
816625c1
You need to sign in or sign up before continuing.
Commit
816625c1
authored
Dec 14, 2023
by
李小惠
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
添加报表
parent
582c4715
显示空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
130 行增加
和
37 行删除
+130
-37
UpdateReassignmentReq.java
jyzb-api/src/main/java/com/junmp/jyzb/api/bean/req/UpdateReassignmentReq.java
+1
-1
PolicemanExceptionEnum.java
jyzb-api/src/main/java/com/junmp/jyzb/api/exception/enums/PolicemanExceptionEnum.java
+1
-1
PublicController.java
jyzb-biz/src/main/java/com/junmp/jyzb/controller/PublicController.java
+19
-3
TjController.java
jyzb-biz/src/main/java/com/junmp/jyzb/controller/TjController.java
+24
-0
PolicemanServiceImpl.java
jyzb-biz/src/main/java/com/junmp/jyzb/service/impl/PolicemanServiceImpl.java
+32
-30
EquipmentCountAction.java
jyzb-biz/src/main/java/com/junmp/jyzb/task/EquipmentCountAction.java
+15
-0
FileUtil.java
jyzb-biz/src/main/java/com/junmp/jyzb/utils/FileUtil.java
+36
-0
InventorySummaryMapper.xml
jyzb-biz/src/main/resources/mapper/InventorySummaryMapper.xml
+2
-2
没有找到文件。
jyzb-api/src/main/java/com/junmp/jyzb/api/bean/req/UpdateReassignmentReq.java
View file @
816625c1
...
...
@@ -110,7 +110,7 @@ public class UpdateReassignmentReq extends BaseRequest {
private
BigDecimal
price
;
/**
* 类型(调入警员
0,调出警员1
)
* 类型(调入警员
assign,调出警员reassign
)
*/
private
String
reassignmentType
;
...
...
jyzb-api/src/main/java/com/junmp/jyzb/api/exception/enums/PolicemanExceptionEnum.java
View file @
816625c1
...
...
@@ -36,7 +36,7 @@ public enum PolicemanExceptionEnum implements IExceptionEnum {
/**
* 该警员已经在审核流中,请勿重复操作
*/
POLICE_IS_IN_EXAMINE
(
CommonConstant
.
DEFAULT_USER_ERROR_CODE
,
"该警员已经在审核流中或已经调
走
,请勿重复操作"
)
POLICE_IS_IN_EXAMINE
(
CommonConstant
.
DEFAULT_USER_ERROR_CODE
,
"该警员已经在审核流中或已经调
岗完成
,请勿重复操作"
)
;
/**
* 错误编码
...
...
jyzb-biz/src/main/java/com/junmp/jyzb/controller/PublicController.java
View file @
816625c1
...
...
@@ -4,13 +4,20 @@ import cn.hutool.core.util.ObjectUtil;
import
cn.hutool.core.util.StrUtil
;
import
com.baomidou.mybatisplus.core.toolkit.AES
;
import
com.junmp.jyzb.api.bean.query.DbAccessReq
;
import
com.junmp.jyzb.api.bean.query.PolicemanReq
;
import
com.junmp.jyzb.api.bean.vo.AuthAccessVo
;
import
com.junmp.jyzb.utils.FileUtil
;
import
com.junmp.v2.common.bean.response.ApiRes
;
import
com.junmp.v2.common.exception.enums.DefaultBizExceptionEnum
;
import
io.swagger.annotations.ApiOperation
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.web.bind.annotation.GetMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
org.apache.commons.compress.utils.IOUtils
;
import
org.springframework.web.bind.annotation.*
;
import
java.io.ByteArrayOutputStream
;
import
java.io.File
;
import
java.io.FileInputStream
;
import
java.net.URL
;
/**
* <pre>
...
...
@@ -49,4 +56,13 @@ public class PublicController {
}
}
// @PostMapping("/downloadPhoto")
// @ApiOperation("/下载图片并返回图片内容信息")
// public ApiRes<byte[]> downloadPhoto (@RequestBody PolicemanReq req)throws Exception{
// byte[] bytes = FileUtil.fileToByte(req.getPhoto());
//
// return ApiRes.success(bytes);
// }
}
jyzb-biz/src/main/java/com/junmp/jyzb/controller/TjController.java
View file @
816625c1
...
...
@@ -32,6 +32,10 @@ public class TjController {
private
TjService
tjService
;
/**
* ------------------------------------装备统计报表--------------------------------------------
*/
/**
* 根据组织机构统计装备的报表数据(外层数据)
*/
@PostMapping
(
"/TjOrgEqs"
)
...
...
@@ -56,6 +60,10 @@ public class TjController {
return
ApiRes
.
success
(
tjService
.
TjOrgEqsDetailList
(
req
));
}
/**
*-------------------------------------财务统计报表------------------------------------------
*/
//财务统计报表
@PostMapping
(
"/TjOrgPrice"
)
@ApiOperation
(
"/财务统计报表"
)
...
...
@@ -79,6 +87,22 @@ public class TjController {
public
ApiRes
<
List
<
TjOrgPriceDto
>>
TjOrgPriceDetailList
(
@RequestBody
TjOrgPriceReq
req
){
return
ApiRes
.
success
(
tjService
.
TjOrgPriceDetailList
(
req
));
}
/**
* --------------------------------------使用统计报表-------------------------------------------
*/
/**
* --------------------------------------人员统计报表-------------------------------------------
*/
/**
* --------------------------------------温湿度记录-------------------------------------------
*/
@PostMapping
(
"/test"
)
public
ApiRes
<
FinalTjOrgEqsDto
>
test
(
@RequestBody
TjOrgEqsReq
req
){
return
ApiRes
.
success
(
tjService
.
test
(
req
));
...
...
jyzb-biz/src/main/java/com/junmp/jyzb/service/impl/PolicemanServiceImpl.java
View file @
816625c1
...
...
@@ -618,11 +618,12 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
public
boolean
PoliceBindBox
(
PolicemanReq
req
)
{
Policeman
policeman
=
PoliceExist
(
req
.
getId
());
Cabinet
cabinet
=
new
Cabinet
();
Cabinet
Box
cabinetBox
=
new
CabinetBox
();
//判断该警员是否是调入的警员,如果是调入警员,那么查询log表中是否有记录,如果有记录表示他携带装备
List
<
OrderLog
>
list
=
orderLogService
.
list
(
new
LambdaQueryWrapper
<
OrderLog
>()
.
eq
(
OrderLog:
:
getOrgId
,
req
.
getOrgId
())
.
eq
(
OrderLog:
:
getOrderType
,
policeman
.
getName
()
+
policeman
.
getPoliceCode
())
.
eq
(
OrderLog:
:
getBussinessType
,
"assign"
)
.
orderByDesc
(
OrderLog:
:
getCreateTime
));
List
<
PoliceEquipment
>
policeEqsList
=
new
ArrayList
<>();
String
locationId
=
""
;
...
...
@@ -652,11 +653,11 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
cabinetBoxPoliceService
.
remove
(
new
LambdaQueryWrapper
<
CabinetBoxPolice
>().
eq
(
CabinetBoxPolice:
:
getPoliceId
,
req
.
getId
()));
//公共柜和单警柜一起批量绑定
for
(
String
box
Req
:
boxList
)
{
for
(
String
box
Id
:
boxList
)
{
CabinetBoxPolice
cabinetBoxPolice
=
new
CabinetBoxPolice
();
cabinetBoxPolice
.
setPoliceId
(
req
.
getId
());
// cabinetBoxPolice.setCabinetId(boxReq.getCabinetId());
cabinetBoxPolice
.
setCabinetBoxId
(
box
Req
);
cabinetBoxPolice
.
setCabinetBoxId
(
box
Id
);
cabinetBoxPolice
.
setCreateTime
(
DateTimeUtil
.
getCurrentDateTime
());
// cabinetBoxPolice.setCabinetName(boxReq.getCabinetName());
// cabinetBoxPolice.setNum(boxReq.getNum());
...
...
@@ -665,18 +666,18 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
//调岗警员绑定第一个单警柜以后,不管之后他是否还有单警柜,我都默认是不绑了。
// 判断他下面是否有箱门,如果有则表示他已经帮过单警柜并且调岗携带的装备都已经记录过,那么将不再被记录
//
if (ObjectUtil.isNotNull(orderLog) && list1.size()==0 && !flag && !locationId.trim().isEmpty()){
//
flag=true;
//
// cabinet = cabinetService.getById(boxReq.getCabinetId()
);
//
for (PoliceEquipment equipment:policeEqsList) {
// equipment.setLocationId(boxReq
.getCabinetId());
//
equipment.setOrgId(req.getOrgId());
// Object[] item=new Object[]{req.getOrgId(),boxReq
.getCabinetId(),equipment.getTypeId(),equipment.getSizeId(),equipment.getPrice()};
//
searchItem.add(item);
//
}
//
policeEquipmentService.updateBatchById(policeEqsList);
//
}
if
(
ObjectUtil
.
isNotNull
(
orderLog
)
&&
list1
.
size
()==
0
&&
!
flag
&&
!
locationId
.
trim
().
isEmpty
()){
flag
=
true
;
cabinetBox
=
cabinetBoxService
.
getById
(
boxId
);
for
(
PoliceEquipment
equipment:
policeEqsList
)
{
equipment
.
setLocationId
(
cabinetBox
.
getCabinetId
());
equipment
.
setOrgId
(
req
.
getOrgId
());
Object
[]
item
=
new
Object
[]{
req
.
getOrgId
(),
cabinetBox
.
getCabinetId
(),
equipment
.
getTypeId
(),
equipment
.
getSizeId
(),
equipment
.
getPrice
()};
searchItem
.
add
(
item
);
}
policeEquipmentService
.
updateBatchById
(
policeEqsList
);
}
}
if
(
cabinetBoxPoliceList
.
size
()>
0
){
result
=
cabinetBoxPoliceService
.
saveBatch
(
cabinetBoxPoliceList
);
...
...
@@ -686,17 +687,18 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
}
//判断summary表中是否有
该警员的
数据,如果有,summary表直接更新数据,如果summary表没有,那么直接新增一条数据
//判断summary表中是否有数据,如果有,summary表直接更新数据,如果summary表没有,那么直接新增一条数据
if
(
flag
){
boolean
b
=
updateSumDataAndLog
(
req
.
getOrgId
(),
cabinet
,
policeEqsList
,
searchItem
);
boolean
b
=
updateSumDataAndLog
(
req
.
getOrgId
(),
cabinet
Box
,
policeEqsList
,
searchItem
);
}
return
result
;
}
//添加调岗第一次调入人员绑定箱门时修改装备汇总表的信息以及记录日志
private
boolean
updateSumDataAndLog
(
Long
orgId
,
Cabinet
cabinet
,
List
<
PoliceEquipment
>
policeEqsList
,
List
<
Object
[]>
searchItem
){
private
boolean
updateSumDataAndLog
(
Long
orgId
,
Cabinet
Box
cabinetbox
,
List
<
PoliceEquipment
>
policeEqsList
,
List
<
Object
[]>
searchItem
){
PubOrg
pubOrg
=
pubOrgService
.
PubOrgExist
(
orgId
);
Cabinet
cabinet
=
cabinetService
.
CabinetExist
(
cabinetbox
.
getCabinetId
());
StringBuilder
stringBuilder
=
new
StringBuilder
();
//将警员调岗携带的装备进行查询出来,并对summary表进行计算
List
<
InventorySummary
>
inventorySummaryList
=
inventorySummaryMapper
.
selectSumByItems
(
searchItem
);
...
...
@@ -711,7 +713,7 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
&&
equipment
.
getPrice
().
compareTo
(
is
.
getUnitPrice
())==
0
){
a
=
true
;
Integer
totalNum
=
is
.
getNumber
()+
equipment
.
getNum
();
stringBuilder
.
append
(
"将"
+
cabinet
.
getName
()+
"下的"
+
equipment
.
getTypeName
()+
equipment
.
getSizeName
()+
"装备的在库数量数量从
"
+
is
.
getStockNumber
()+
"改成"
+
totalNum
+
",并且修改了价格和总数;"
);
stringBuilder
.
append
(
cabinet
.
getName
()+
"下的"
+
equipment
.
getTypeName
()+
"【"
+
equipment
.
getSizeName
()+
"】装备的在柜数量:
"
+
is
.
getStockNumber
()+
"改成"
+
totalNum
+
",并且修改了价格和总数;"
);
is
.
setNumber
(
totalNum
);
is
.
setPrice
(
is
.
getPrice
().
add
(
equipment
.
getPrice
().
multiply
(
BigDecimal
.
valueOf
(
equipment
.
getNum
()))));
is
.
setStockNumber
(
is
.
getStockNumber
()+
equipment
.
getNum
());
...
...
@@ -720,7 +722,7 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
}
//没找到,直接新增
if
(!
a
){
stringBuilder
.
append
(
"新增装备"
+
equipment
.
getTypeName
()+
equipment
.
getSizeName
()+
",在库数为
"
+
equipment
.
getNum
()+
",并且修改了价格和总数;"
);
stringBuilder
.
append
(
cabinet
.
getName
()+
"新增装备"
+
equipment
.
getTypeName
()+
"【"
+
equipment
.
getSizeName
()+
"】,在柜数量:
"
+
equipment
.
getNum
()+
",并且修改了价格和总数;"
);
InventorySummary
inventorySummary
=
getInventorySummary
(
pubOrg
,
equipment
,
cabinet
);
addSumList
.
add
(
inventorySummary
);
}
...
...
@@ -728,7 +730,7 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
}
}
else
{
for
(
PoliceEquipment
equipment:
policeEqsList
)
{
stringBuilder
.
append
(
"新增装备"
+
equipment
.
getTypeName
()+
equipment
.
getSizeName
()+
",在库数为
"
+
equipment
.
getNum
()+
",并且修改了价格和总数;"
);
stringBuilder
.
append
(
cabinet
.
getName
()+
"新增装备"
+
equipment
.
getTypeName
()+
"【"
+
equipment
.
getSizeName
()+
"】,在柜数量:
"
+
equipment
.
getNum
()+
",并且修改了价格和总数;"
);
InventorySummary
inventorySummary
=
getInventorySummary
(
pubOrg
,
equipment
,
cabinet
);
addSumList
.
add
(
inventorySummary
);
}
...
...
@@ -827,11 +829,11 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
policeman
.
setOrgId
(
req
.
getOrgId
());
//如果调入的警员携带装备,那么我应该怎么处理呢
stringBuilder
.
append
(
"警员:"
+
req
.
getPoliceName
()+
",警号为:"
+
req
.
getPoliceCode
()+
"因调岗携带以下装备:"
);
stringBuilder
.
append
(
"警员:"
+
req
.
getPoliceName
()+
",警号为:"
+
req
.
getPoliceCode
()+
"
,
因调岗携带以下装备:"
);
if
(
ObjectUtil
.
isNotNull
(
reqEquipments
)){
for
(
PoliceEquipment
equipment:
reqEquipments
)
{
stringBuilder
.
append
(
"装备"
+
equipment
.
getTypeName
()+
","
+
equipment
.
getSizeName
()+
"数量为:"
+
equipment
.
getNum
()+
",
"
);
stringBuilder
.
append
(
equipment
.
getTypeName
()+
"【"
+
equipment
.
getSizeName
()+
"】,数量为:"
+
equipment
.
getNum
()+
"/
"
);
inventoryQuantity
+=
equipment
.
getNum
();
actualQuantity
+=
equipment
.
getNum
();
}
...
...
@@ -842,8 +844,7 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
}
else
if
(
req
.
getReassignmentType
().
equals
(
itemValue1
)
&&
itemValue1
.
equals
(
"reassign"
)){
//将其组织机构号设为空
policeman
.
setOrgId
(
null
);
//将角色关联表进行删除
//角色账号信息也进行删除
//将账号与组织机构关联表进行删除
sysUserOrgService
.
remove
(
new
LambdaQueryWrapper
<
SysUserOrg
>().
eq
(
SysUserOrg:
:
getUserId
,
policeman
.
getUserId
()));
//箱门进行解绑删除
...
...
@@ -858,8 +859,8 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
//判断前端传递的装备列表字段是否为空,如果为空则表示该警员不拿走任何装备信息
if
(
ObjectUtil
.
isNull
(
req
.
getDetailList
())
||
req
.
getDetailList
().
trim
().
isEmpty
()){
if
(
list1
.
size
()>
0
){
policeEquipmentService
.
remove
(
eq
);
police
man
Service
.
removeBatchByIds
(
list1
);
//
policeEquipmentService.remove(eq);
police
Equipment
Service
.
removeBatchByIds
(
list1
);
}
}
else
{
//将该警员拿走的装备的组织机构设置为空,并且其他装备进行跟警员进行解绑删除
...
...
@@ -867,7 +868,7 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
List
<
PoliceEquipment
>
updateList
=
new
ArrayList
<>();
List
<
Object
[]>
searchItem
=
new
ArrayList
<>();
stringBuilder
.
append
(
"原组织机构为"
+
req
.
getOrgId
()+
"的警员"
+
req
.
getPoliceName
()+
",警号为:"
+
req
.
getPoliceCode
()+
"
因调岗拿走以下装备:"
);
stringBuilder
.
append
(
req
.
getOrgName
()+
"的警员"
+
req
.
getPoliceName
()+
",警号为:"
+
req
.
getPoliceCode
()+
",
因调岗拿走以下装备:"
);
for
(
PoliceEquipment
policeEquipment:
list1
)
{
boolean
flag
=
false
;
for
(
PoliceEquipment
one:
reqEquipments
)
{
...
...
@@ -875,7 +876,7 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
&&
policeEquipment
.
getTypeId
().
equals
(
one
.
getTypeId
())){
Object
[]
item
=
new
Object
[]{
policeEquipment
.
getOrgId
(),
policeEquipment
.
getLocationId
(),
policeEquipment
.
getTypeId
(),
policeEquipment
.
getSizeId
(),
policeEquipment
.
getPrice
(),
one
.
getNum
()};
searchItem
.
add
(
item
);
stringBuilder
.
append
(
"原有装备"
+
policeEquipment
.
getTypeName
()+
","
+
policeEquipment
.
getSizeName
()+
"数量为:"
+
policeEquipment
.
getNum
()+
",现拿走该物资,数量为:"
+
one
.
getNum
()+
",
"
);
stringBuilder
.
append
(
policeEquipment
.
getTypeName
()+
"【"
+
policeEquipment
.
getSizeName
()+
"】,原数量:"
+
policeEquipment
.
getNum
()+
",取走:"
+
one
.
getNum
()+
",剩余:"
+(
policeEquipment
.
getNum
()-
one
.
getNum
())+
"/
"
);
inventoryQuantity
+=
policeEquipment
.
getNum
();
actualQuantity
+=
one
.
getNum
();
policeEquipment
.
setNum
(
one
.
getNum
());
...
...
@@ -1009,10 +1010,11 @@ public class PolicemanServiceImpl extends ServiceImpl<PolicemanMapper, Policeman
@Override
public
String
PoliceReassignment
(
UpdateReassignmentReq
req
)
{
//判断该警员是否已经在审核中,如果是的话,那么则提示报错,存草稿没事,但是一旦发起审核流,则需要进行判断
//但是审核通过之后该警员应该将审核中的状态改回正常,
if
(
ObjectUtil
.
isNotNull
(
req
.
getProcessDefinitionId
())){
List
<
Reassignment
>
list
=
reassignmentService
.
list
(
new
LambdaQueryWrapper
<
Reassignment
>()
.
eq
(
Reassignment:
:
getPoliceId
,
req
.
getPoliceId
())
.
isNotNull
(
Reassignment:
:
getReassignmentType
)
.
eq
(
Reassignment:
:
getReassignmentType
,
req
.
getReassignmentType
()
)
.
eq
(
Reassignment:
:
getOrgId
,
req
.
getOrgId
())
.
isNotNull
(
Reassignment:
:
getProcessId
)
.
orderByDesc
(
Reassignment:
:
getCreateTime
));
...
...
jyzb-biz/src/main/java/com/junmp/jyzb/task/EquipmentCountAction.java
0 → 100644
View file @
816625c1
package
com
.
junmp
.
jyzb
.
task
;
import
com.junmp.v2.job.api.JobAction
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.stereotype.Component
;
@Slf4j
@Component
public
class
EquipmentCountAction
implements
JobAction
{
@Override
public
void
action
()
{
}
}
jyzb-biz/src/main/java/com/junmp/jyzb/utils/FileUtil.java
0 → 100644
View file @
816625c1
package
com
.
junmp
.
jyzb
.
utils
;
import
java.io.ByteArrayOutputStream
;
import
java.io.File
;
import
java.io.FileInputStream
;
public
class
FileUtil
{
private
static
final
int
CACHE_SIZE
=
1024
;
/**
* 文件转换为二进制数组
* @param filePath
* @return
* @throws Exception
*/
public
static
byte
[]
fileToByte
(
String
filePath
)
throws
Exception
{
byte
[]
data
=
new
byte
[
0
];
File
file
=
new
File
(
filePath
);
System
.
out
.
println
(
"file.canRead() = "
+
file
.
canRead
());
if
(
file
.
exists
())
{
FileInputStream
in
=
new
FileInputStream
(
file
);
ByteArrayOutputStream
out
=
new
ByteArrayOutputStream
(
2048
);
byte
[]
cache
=
new
byte
[
CACHE_SIZE
];
int
nRead
=
0
;
while
((
nRead
=
in
.
read
(
cache
))
!=
-
1
)
{
out
.
write
(
cache
,
0
,
nRead
);
out
.
flush
();
}
out
.
close
();
in
.
close
();
data
=
out
.
toByteArray
();
}
return
data
;
}
}
jyzb-biz/src/main/resources/mapper/InventorySummaryMapper.xml
View file @
816625c1
...
...
@@ -466,8 +466,8 @@
<select
id=
"selectTotalNum"
resultType=
"com.junmp.jyzb.api.bean.dto.InventorySumDto"
>
<foreach
collection=
"list"
item=
"item"
separator=
"union all"
>
SELECT COALESCE(SUM(stock_number), 0) as stock_number,
COALESCE(
size_id,${item[0]}) as siz
e_id ,
COALESCE(
type_id,'${item[1]}') as typ
e_id
COALESCE(
type_id,${item[0]}) as typ
e_id ,
COALESCE(
size_id,'${item[1]}') as siz
e_id
FROM base_inventory_summary
WHERE org_id_int = #{orgId} and (type_id = ${item[0]} AND size_id = '${item[1]}')
</foreach>
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论