Commit 40447147 by 宋毅

tj

parent 50caa15f
node_modules/
localsettings.js
\ No newline at end of file
......@@ -27,7 +27,7 @@ class APIBase {
pobj.actionBody.resultInfo = result;
pobj.actionBody.requestId = result.requestId;
pobj.actionBody.opTitle = "reqPath:" + req.path;
this.esUtils.addEsLogs(settings.queuedName + "-request", pobj.actionBody);
this.esUtils.addEsLogs(settings.queuedName + "api-req", pobj);
}
return result;
} catch (error) {
......@@ -37,7 +37,7 @@ class APIBase {
pobj.actionBody.requestId = await this.getBusUid("err");
pobj.actionBody.errorInfo = stackStr;
pobj.actionBody.opTitle = ",reqPath:" + req.path;
this.esUtils.addEsLogs(settings.queuedName + "-error", pobj.actionBody);
this.esUtils.addEsLogs(settings.queuedName + "apidoexec-error", pobj);
return rtnerror;
}
}
......
var APIBase = require("../../api.base");
var system = require("../../../system");
const settings = require("../../../../config/settings");
class IcQueryAPI extends APIBase {
constructor() {
......@@ -20,9 +19,6 @@ class IcQueryAPI extends APIBase {
return result;
}
async opActionType(pobj, actionType, req) {
if (settings.queuedName != "SYTXPUBLIC-MSGQ") {
return system.getResult(null, "请求地址有误!");
}
var opResult = null;
switch (actionType) {
case "getListByLikeCompanyName":
......
......@@ -23,9 +23,6 @@ class ProducerAPI extends APIBase {
var opResult = null;
switch (actionType) {
case "produceData":
if (settings.queuedName != "SYTXPUBLIC-MSGQ") {
return system.getResult(null, "请求地址有误!");
}
opResult = await this.utilsProduceSve.produceData(pobj, req);
break;
case "produceLogsData":
......
......@@ -21,10 +21,10 @@ class ConsumerBase {
counter = 1;
}
var self = this;
this.duplicateInstance.brpop(queuedName, 3600, async function (err, repl) {
console.log(JSON.stringify(repl), "..............repl.....info........." + queuedName);
this.duplicateInstance.brpop(queuedName, 0, async function (err, repl) {
console.log(JSON.stringify(repl), "..........repl...........queuedName=" + queuedName);
if (err) {
return new Error("queuedName=" + queuedName + "doConsumer brpop error :" + err);
return new Error(queuedName + '-->doConsumer brpop error :' + err);
}
else {
if (repl[1]) {
......@@ -47,7 +47,7 @@ class ConsumerBase {
const notifyQueuedName = "NOTIFY-SYTXPUBLIC-MSGQ";
this.subBeforeConsumer(queuedName, actionBody);
actionBody.requestId = await this.getBusUid("PUB-");
if ("SYTXFAIL-SYTXPUBLIC-MSGQ" != queuedName && "LOGSFAIL-SYTXPUBLIC-MSGQ" != queuedName) {
if ("SYTXFAIL-SYTXPUBLIC-MSGQ" != queuedName) {
execResult = await this.subDoConsumer(queuedName, actionBody);
if (notifyQueuedName != queuedName) {
actionBody.resultInfo = execResult;
......@@ -66,9 +66,6 @@ class ConsumerBase {
return;
}
var failQueuedName = "SYTXFAIL-SYTXPUBLIC-MSGQ";
if ("LOGS-SYTXPUBLIC-MSGQ" === queuedName) {
failQueuedName = "LOGSFAIL-SYTXPUBLIC-MSGQ";
}
actionBody.queuedName = queuedName;
actionBody.counter = actionBody.counter ? actionBody.counter : 1;
await this.redisClient.lpushData(failQueuedName, actionBody);
......@@ -84,14 +81,11 @@ class ConsumerBase {
}
execResult = await this.subDoConsumer(queuedName, actionBody);
} catch (error) {
if (execResult && execResult.status == 1) {
await this.pushSuccessLogDao.addOpSuccessLogs("推送成功", actionBody, execResult);
} else {
//日志记录
var stackStr = error.stack ? error.stack : JSON.stringify(error);
console.log(stackStr, ",队列执行execSubDoConsumer存在异常queuedName=" + queuedName);
this.errorLogDao.addOpErrorLogs("队列执行execSubDoConsumer存在异常", actionBody, execResult, stackStr, 3);
}
//日志记录
var stackStr = error.stack ? error.stack : JSON.stringify(error);
console.log(stackStr, ",队列执行execSubDoConsumer存在异常queuedName=" + queuedName);
this.errorLogDao.addOpErrorLogs("队列执行execSubDoConsumer存在异常", actionBody, execResult, stackStr, 3);
}
}
......@@ -159,5 +153,3 @@ class ConsumerBase {
}
}
module.exports = ConsumerBase;
var stackStr = error.stack ? error.stack : JSON.stringify(error);
\ No newline at end of file
......@@ -7,7 +7,7 @@ class PublicFailConsumer extends ConsumerBase {
super(ConsumerBase.getServiceName(PublicFailConsumer));
}
async subBeforeConsumer(queuedName, actionBody) {
console.log("前置操作......", this.serviceName);
console.log("publicFailConsumer前置操作......", this.serviceName);
}
async subDoConsumer(queuedName, actionBody) {
actionBody.counter = actionBody.counter + 1;
......
......@@ -12,6 +12,12 @@ class PublicLogsConsumer extends ConsumerBase {
console.log("前置操作......", this.serviceName);
}
async subDoConsumer(queuedName, actionBody) {
var params = {
opTitle: actionBody.opTitle,
identifyCode: actionBody.identifyCode,
messageBody: actionBody.messageBody,
requestId: actionBody.requestId
}
var execResult = await this.esUtils.addEsLogs(queuedName, actionBody);;
return execResult;
}
......
const system = require("../../../system");
const ConsumerBase = require("../../consumer.base");
const system = require("../../../system");
class PublicConsumer extends ConsumerBase {
constructor() {
......@@ -7,7 +7,7 @@ class PublicConsumer extends ConsumerBase {
this.esUtils = system.getObject("util.esUtils");
}
async subBeforeConsumer(queuedName, actionBody) {
console.log("publicConsumer前置操作......", this.serviceName);
console.log("publicConsumer-->subDoConsumer前置操作......", this.serviceName);
}
async subDoConsumer(queuedName, actionBody) {
var params = {
......@@ -16,9 +16,9 @@ class PublicConsumer extends ConsumerBase {
actionBody: actionBody.messageBody,
requestId: actionBody.requestId
}
console.log(JSON.stringify(params), "publicConsumer推送信息.....actionBody.pushUrl=" + actionBody.pushUrl)
console.log(JSON.stringify(params), "publicConsumer-->subDoConsumer...pushurl=" + actionBody.pushUrl);
var execResult = await this.execPostByTimeOut(params, actionBody.pushUrl);
console.log(JSON.stringify(execResult), "publicConsumer推送结果.....actionBody.pushUrl=" + actionBody.pushUrl)
console.log(JSON.stringify(execResult), "publicConsumer-->subDoConsumer.....结果......pushurl=" + actionBody.pushUrl);
var tmpBody = {
"opTitle": "队列推送数据记录,推送地址:" + actionBody.pushUrl,// N 操作的业务标题
"identifyCode": actionBody.identifyCode || "",// Y 操作的业务标识
......
......@@ -26,7 +26,7 @@ class UtilsIcService extends AppServiceBase {
from = 0;
}
var esIndexName = "tx_ic_bigdata_business_heming_index/_search";
var esIndexName = "tx_ic_bigdata_business_index/_search";
var params = {
"query": {
"bool": {
......@@ -44,7 +44,31 @@ class UtilsIcService extends AppServiceBase {
"size": pageSize,
"_source": [
"company_name"//公司名称
// "company_org_type",//公司类型
// "credit_code",//统一社会信用代码
// "legal_person",//法人姓名
// "from_time",//营业期限开始日期
// "to_time",//营业期限结束日期
// "estiblish_time",//成立时间
// "reg_status",//公司状态
// "reg_number",//注册号
// "org_number",//组织机构代码
// "reg_location",//公司地址
// "reg_capital",//注册资本
// "business_scope",//公司经营范围
// "reg_institute",//登记机关
// "company_province",//公司省份
// "company_city",//公司二级市
// "company_cate_1",//行业分类一级分类
// "company_cate_2",//行业分类二级分类
// "company_cate_3"//行业分类三级分类
]
// ,
// "sort": [
// {
// "reg_capital": "desc"
// }
// ]
}
......@@ -67,6 +91,25 @@ class UtilsIcService extends AppServiceBase {
resultData.data.hits.hits.forEach(function (c) {
var source = {
"companyName": c._source.company_name//公司名称
// "companyOrgType": c._source.company_org_type || "",//公司类型
// "creditCode": c._source.credit_code || "",//统一社会信用代码
// "legalPerson": c._source.legal_person,//法人姓名
// "fromTime": c._source.from_time ? moment(c._source.from_time * 1000).format("YYYY-MM-DD") : "",//营业期限开始日期
// "toTime": c._source.to_time ? moment(c._source.to_time * 1000).format("YYYY-MM-DD") : "",//营业期限结束日期
// "estiblishTime": c._source.estiblish_time ? moment(c._source.estiblish_time * 1000).format("YYYY-MM-DD") : "",//成立时间
// "regStatus": c._source.reg_status || "",//公司状态
// "regNumber": c._source.reg_number || "",//注册号
// "orgNumber": c._source.org_number || "",//组织机构代码
// "regLocation": c._source.reg_location || "",//公司地址
// "regCapital": c._source.reg_capital || "",//注册资本
// "businessScope": c._source.business_scope || "",//公司经营范围
// "regInstitute": c._source.reg_institute || "",//登记机关
// "companyProvince": c._source.company_province || "",//公司省份
// "companyCity": c._source.company_city || "",//公司二级市
// "companyCate1": c._source.company_cate_1 || "",//行业分类一级分类
// "companyCate2": c._source.company_cate_2 || "",//行业分类二级分类
// "companyCate3": c._source.company_cate_3 || ""//行业分类三级分类
};
sources.push(source);
});
......@@ -111,10 +154,18 @@ class UtilsIcService extends AppServiceBase {
"from_time",//营业期限开始日期
"to_time",//营业期限结束日期
"estiblish_time",//成立时间
"reg_status",//公司状态
"reg_number",//注册号
"org_number",//组织机构代码
"reg_location",//公司地址
"reg_capital",//注册资本
"reg_unit",//资本单位
"business_scope"//公司经营范围
"business_scope",//公司经营范围
"reg_institute",//登记机关
"company_province",//公司省份
"company_city",//公司二级市
"company_cate_1",//行业分类一级分类
"company_cate_2",//行业分类二级分类
"company_cate_3"//行业分类三级分类
]
// ,
// "sort": [
......@@ -134,21 +185,26 @@ class UtilsIcService extends AppServiceBase {
if (!resultData.data.hits.hits || resultData.data.hits.hits.length === 0) {
return system.getResult(null, "data is empty!");
}
var fromTime = resultData.data.hits.hits[0]._source.from_time ? moment(resultData.data.hits.hits[0]._source.from_time * 1000).format("YYYY-MM-DD") : "";//营业期限开始日期
var toTime = resultData.data.hits.hits[0]._source.to_time ? moment(resultData.data.hits.hits[0]._source.to_time * 1000).format("YYYY-MM-DD") : "";//营业期限结束日期
var item = {
companyName: resultData.data.hits.hits[0]._source.company_name,//公司名称
companyOrgType: resultData.data.hits.hits[0]._source.company_org_type || "",//公司类型
creditCode: resultData.data.hits.hits[0]._source.credit_code || "",//统一社会信用代码
legalPerson: resultData.data.hits.hits[0]._source.legal_person,//法人姓名
fromTime: fromTime,//营业期限开始日期
toTime: toTime,//营业期限结束日期
operatingPeriod: (fromTime || "---") + " 至 " + (toTime || "---"),
estiblishTime: resultData.data.hits.hits[0]._source.estiblish_time ? moment(resultData.data.hits.hits[0]._source.estiblish_time * 1000).format("YYYY-MM-DD") : "",//成立时间
regLocation: resultData.data.hits.hits[0]._source.reg_location || "",//公司地址
regCapital: resultData.data.hits.hits[0]._source.reg_capital || "",//注册资本
regUnit: resultData.data.hits.hits[0]._source.reg_unit || "",//资本单位
businessScope: resultData.data.hits.hits[0]._source.business_scope || ""//公司经营范围
"companyName": resultData.data.hits.hits[0]._source.company_name,//公司名称
"companyOrgType": resultData.data.hits.hits[0]._source.company_org_type || "",//公司类型
"creditCode": resultData.data.hits.hits[0]._source.credit_code || "",//统一社会信用代码
"legalPerson": resultData.data.hits.hits[0]._source.legal_person,//法人姓名
"fromTime": resultData.data.hits.hits[0]._source.from_time ? moment(resultData.data.hits.hits[0]._source.from_time * 1000).format("YYYY-MM-DD") : "",//营业期限开始日期
"toTime": resultData.data.hits.hits[0]._source.to_time ? moment(resultData.data.hits.hits[0]._source.to_time * 1000).format("YYYY-MM-DD") : "",//营业期限结束日期
"estiblishTime": resultData.data.hits.hits[0]._source.estiblish_time ? moment(resultData.data.hits.hits[0]._source.estiblish_time * 1000).format("YYYY-MM-DD") : "",//成立时间
"regStatus": resultData.data.hits.hits[0]._source.reg_status || "",//公司状态
"regNumber": resultData.data.hits.hits[0]._source.reg_number || "",//注册号
"orgNumber": resultData.data.hits.hits[0]._source.org_number || "",//组织机构代码
"regLocation": resultData.data.hits.hits[0]._source.reg_location || "",//公司地址
"regCapital": resultData.data.hits.hits[0]._source.reg_capital || "",//注册资本
"businessScope": resultData.data.hits.hits[0]._source.business_scope || "",//公司经营范围
"regInstitute": resultData.data.hits.hits[0]._source.reg_institute || "",//登记机关
"companyProvince": resultData.data.hits.hits[0]._source.company_province || "",//公司省份
"companyCity": resultData.data.hits.hits[0]._source.company_city || "",//公司二级市
"companyCate1": resultData.data.hits.hits[0]._source.company_cate_1 || "",//行业分类一级分类
"companyCate2": resultData.data.hits.hits[0]._source.company_cate_2 || "",//行业分类二级分类
"companyCate3": resultData.data.hits.hits[0]._source.company_cate_3 || ""//行业分类三级分类
};
return system.getResultSuccess(item);
} catch (error) {
......
......@@ -48,9 +48,6 @@ class UtilsProduceService extends AppServiceBase {
if (keyCount === 0) {
return system.getResult(null, "actionBody参数不能为空");
}
if (!pobj.actionBody.indexName) {
return system.getResult(null, "actionBody.indexName参数不能为空");
}
if (!pobj.actionBody.identifyCode) {
return system.getResult(null, "actionBody.identifyCode参数不能为空");
}
......
......@@ -15,14 +15,10 @@ class EsUtils {
* @param {*} actionBody 日志信息
*/
async addEsLogs(queuedName, actionBody) {
var esIndexName = queuedName;
if (["SYTXPUBLIC-MSGQ-request", "SYTXPUBLIC-MSGQ-error"].indexOf(esIndexName) < 0) {
esIndexName = queuedName + (actionBody.indexName ? "-" + actionBody.indexName : "");
}
var esIndexName = queuedName + (actionBody.identifyCode ? "-" + actionBody.identifyCode : "");
esIndexName = esIndexName.toLocaleLowerCase() + "/_doc?pretty";
actionBody.resultInfo = typeof actionBody.resultInfo === 'object' ? JSON.stringify(actionBody.resultInfo) : actionBody.resultInfo || "";
actionBody.errorInfo = typeof actionBody.errorInfo === 'object' ? JSON.stringify(actionBody.errorInfo) : actionBody.errorInfo || "";
actionBody.messageBody = typeof actionBody.messageBody === 'object' ? JSON.stringify(actionBody.messageBody) : actionBody.messageBody || "";
actionBody.messageBody = (actionBody.messageBody || "") + ";actionType=" + (actionBody.actionType || "") + ";pushUrl=" + (actionBody.pushUrl || "");
var params = {
opTitle: moment().format("YYYY-MM-DD HH:mm:ss:SSS") + "," + actionBody.opTitle || "",
......@@ -30,8 +26,7 @@ class EsUtils {
messageBody: actionBody.messageBody || "",
resultInfo: actionBody.resultInfo || "",
errorInfo: actionBody.errorInfo || "",
requestId: actionBody.requestId,
created_at: new Date()
requestId: actionBody.requestId
}
var execResult = await this.execPostEs(queuedName, params, esIndexName);
return execResult;
......
......@@ -28,7 +28,7 @@ class RedisClient {
if (!opResult || opResult.length == 0) {
return;
}
if ("SYTXFAIL-SYTXPUBLIC-MSGQ" != opResult[0] && "LOGSFAIL-SYTXPUBLIC-MSGQ" != opResult[0]) {
if ("SYTXFAIL-SYTXPUBLIC-MSGQ" != opResult[0]) {
return;
}
var lock = await self.enter(opResult[1]);
......@@ -87,7 +87,7 @@ class RedisClient {
if (err) {
return new Error("lpush message error :" + err + ",queuedName:" + queuedName + ",messageBody:" + messageBody);
} else {
console.log("lpush message success....");
console.log("lpush message success");
}
});
}
......
......@@ -3,7 +3,7 @@ var settings = {
host: "43.247.184.32",
port: 8967,
password: "Gongsibao2018",
db: 1,
db: 5,
},
database: {
dbname: "tx-queue-log",
......@@ -15,14 +15,11 @@ var settings = {
dialect: 'mysql',
operatorsAliases: false,
pool: {
max: 5,
min: 0,
max: 50,
idle: 30000,//断开连接后,连接实例在连接池保持的时间
acquire: 30000,//请求超时时间
evict: 30000,
handleDisconnects: true
acquire: 90000000,
idle: 1000000
},
timezone: '+08:00',
debug: false,
dialectOptions: {
requestTimeout: 999999,
......
......@@ -10,8 +10,8 @@ var ENVINPUT = {
REDIS_DB: process.env.QUEUE_REDIS_DB,
DB_NAME: process.env.QUEUE_DB_NAME,
APP_ENV: process.env.APP_ENV ? process.env.APP_ENV : "test",//运行环境
CONSUMER_NAME: process.env.CONSUMER_NAME || "publicLogs.publicLogsConsumer",//消费者名称
QUEUED_NAME: process.env.QUEUED_NAME || "LOGS-SYTXPUBLIC-MSGQ",//队列名称,FAIL-失败队列(队列和失败队列一对存在进行部署)
CONSUMER_NAME: process.env.CONSUMER_NAME || "publicServiceAllocation.publicConsumer",//消费者名称
QUEUED_NAME: process.env.QUEUED_NAME || "SYTXPUBLIC-MSGQ",//队列名称,FAIL-失败队列(队列和失败队列一对存在进行部署)
};
var settings = {
env: ENVINPUT.APP_ENV,
......@@ -43,23 +43,20 @@ var settings = {
password: ENVINPUT.DB_PWD,
config: {
host: ENVINPUT.DB_HOST,
port: ENVINPUT.DB_PORT,
dialect: 'mysql',
operatorsAliases: false,
pool: {
max: 5,
min: 0,
max: 50,
idle: 30000,//断开连接后,连接实例在连接池保持的时间
acquire: 30000,//请求超时时间
evict: 30000,
handleDisconnects: true
acquire: 90000000,
idle: 1000000
},
timezone: '+08:00',
debug: false,
// dialectOptions: {
// requestTimeout: 999999,
// // instanceName:'DEV'
// } //设置MSSQL超时时间
dialectOptions: {
requestTimeout: 999999,
// instanceName:'DEV'
} //设置MSSQL超时时间
},
};
}
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
......@@ -9,15 +9,12 @@
队列名称 (QUEUED_NAME):SYTXFAIL-SYTXPUBLIC-MSGQ
公共通知配置:
通知者名称(CONSUMER_NAME):publicNotify.publicNotifyConsumer
队列名称 (QUEUED_NAME):NOTIFY-SYTXPUBLIC-MSGQ
队列名称 (QUEUED_NAME):NOTIFY-SYTXPUBLIC-MSGQ
公共日志配置:
消费者名称(CONSUMER_NAME):publicLogs.publicLogsConsumer
队列名称 (QUEUED_NAME):LOGS-SYTXPUBLIC-MSGQ
公共日志失败配置:
消费者名称(CONSUMER_NAME):publicFail.publicFailConsumer
队列名称 (QUEUED_NAME):LOGSFAIL-SYTXPUBLIC-MSGQ
二.公共队列接口
1.生产业务数据供消费者消费
地址:http://192.168.1.128:4018/api/queueAction/producer/springBoard
......@@ -87,7 +84,6 @@
"actionBody": {
"opTitle": "",// N 操作的业务标题
"identifyCode": "logs001",// Y 操作的业务标识
"indexName":"brg-user-center",// Y es索引值,同一个项目用一个值
"messageBody": {//日志的描述信息
"opUrl": "http://192.168.1.189:4012/api/test/testApi/springBoard",// N 操作的业务Url
"message": ""// Y 日志消息描述
......@@ -170,28 +166,35 @@
{
"actionType":"getItemByCompanyName",
"actionBody":{
"companyName":"西安广发彩钢结构有限公司"
"companyName":"上海汉玉实业有限公司"
}
}
返回参数:
{
{
"status": 1,
"message": "success",
"data": {
"companyName": "西安广发彩钢结构有限公司",
"companyName": "上海汉玉实业有限公司",
"companyOrgType": "有限责任公司",
"creditCode": "916111050810200881",
"legalPerson": "陈鑫宇",
"fromTime": "2013-11-22",
"toTime": "",
"operatingPeriod ": "2013-11-22 至 ---",
"estiblishTime": "2013-11-22",
"regLocation": "陕西省西安市未央区六村堡街道(北徐寨)石化大道88号",
"regCapital": "108.000",
"regUnit": "万人民币",
"businessScope": "钢结构、彩钢板、彩钢夹心板、铝型材的设计、加工、销售、安装;五金交电、电线电缆、金属材料、建筑材料的销售。(依法须经批准的项目,经相关部门批准后方可开展经营活动)"
"creditCode": "",
"legalPerson": "曹雪静",
"fromTime": "2008-03-20",
"toTime": "2018-03-19",
"estiblishTime": "2008-03-20",
"regStatus": "注销",
"regNumber": "310228001075786",
"orgNumber": " ",
"regLocation": "上海市金山区卫昌路229号2幢220室",
"regCapital": "3.000",
"businessScope": "化妆品,日用百货,服装鞋帽,健身器材销售,投资咨询、商务咨询(除经纪)(涉及行政许可的凭许可证经营)。",
"regInstitute": "金山区市场监管局",
"companyProvince": "上海",
"companyCity": "金山区",
"companyCate1": "批发和零售业",
"companyCate2": "批发业",
"companyCate3": "纺织、服装及家庭用品批发"
},
"requestId": "SCZ202006251651HBUiV"
"requestId": "SCZ202006181604MJBns"
}
参数说明:
......@@ -202,9 +205,16 @@
"legalPerson": "曹雪静",//法人姓名
"fromTime": "2008-03-20",//营业期限开始日期
"toTime": "2018-03-19",//营业期限结束日期
"operatingPeriod ": "2008-03-20 至 2018-03-19",//营业期限范围
"estiblishTime": "2008-03-20",//成立时间
"regStatus": "注销",//公司状态
"regNumber": "310228001075786",//注册号
"orgNumber": " ",//组织机构代码
"regLocation": "上海市金山区卫昌路229号2幢220室",//公司地址
"regCapital": "3.000",//注册资本
"regUnit": "万人民币",//资本单位
"businessScope": "化妆品,日用百货,服装鞋帽,健身器材销售,投资咨询、商务咨询(除经纪)(涉及行政许可的凭许可证经营)。",//公司经营范围
\ No newline at end of file
"businessScope": "化妆品,日用百货,服装鞋帽,健身器材销售,投资咨询、商务咨询(除经纪)(涉及行政许可的凭许可证经营)。",//公司经营范围
"regInstitute": "金山区市场监管局",//登记机关
"companyProvince": "上海",//公司省份
"companyCity": "金山区",//公司二级市
"companyCate1": "批发和零售业",//行业分类一级分类
"companyCate2": "批发业",//行业分类二级分类
"companyCate3": "纺织、服装及家庭用品批发"//行业分类三级分类
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment