From 448c705f86d0bd79c317dbd65e09289a360a866b Mon Sep 17 00:00:00 2001 From: TsMask <340112800@qq.com> Date: Fri, 9 May 2025 14:21:23 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=B0=83=E5=BA=A6=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E5=AF=BC=E5=87=BAcdr/log/=E5=91=8A=E8=AD=A6=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E7=BD=91=E5=85=83cpu/liencse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build/database/lite/install/sys_i18n.sql | 90 +- build/database/lite/install/sys_job.sql | 26 +- build/database/std/install/sys_i18n.sql | 18 +- build/database/std/install/sys_job.sql | 30 +- .../backup_export_cdr/backup_export_cdr.go | 1427 +++++++++++++++++ .../backup_export_log/backup_export_log.go | 358 +++++ .../ne_alarm_state_check.go | 20 +- .../ne_alarm_state_check_cmd.go | 288 ++++ .../ne_alarm_state_check_license.go | 213 +++ src/modules/crontask/processor/processor.go | 13 + 10 files changed, 2440 insertions(+), 43 deletions(-) create mode 100644 src/modules/crontask/processor/backup_export_cdr/backup_export_cdr.go create mode 100644 src/modules/crontask/processor/backup_export_log/backup_export_log.go create mode 100644 src/modules/crontask/processor/ne_alarm_state_check_cmd/ne_alarm_state_check_cmd.go create mode 100644 src/modules/crontask/processor/ne_alarm_state_check_license/ne_alarm_state_check_license.go diff --git a/build/database/lite/install/sys_i18n.sql b/build/database/lite/install/sys_i18n.sql index df2f05bb..2d9c4fe9 100644 --- a/build/database/lite/install/sys_i18n.sql +++ b/build/database/lite/install/sys_i18n.sql @@ -5,8 +5,8 @@ DROP TABLE IF EXISTS "sys_i18n"; CREATE TABLE "sys_i18n" ( "id" integer NOT NULL, "key_lable" text(255) NOT NULL, - "value_zh" text(255), - "value_en" text(255), + "value_zh" text(2048), + "value_en" text(2048), PRIMARY KEY ("id") ); @@ -710,6 +710,12 @@ INSERT INTO "sys_i18n" VALUES (638, 'job.backup_export_table_cdr_event_ims', ' INSERT INTO "sys_i18n" VALUES (639, 'job.backup_export_table_cdr_event_smf', '备份-数据话单表定期导出', 'Backup-Regular Export of data sheet tables'); INSERT INTO "sys_i18n" VALUES (640, 'cache.name.oauth2_codes', '客户端授权码', 'Oauth2 Client Code'); INSERT INTO "sys_i18n" VALUES (641, 'cache.name.oauth2_devices', '客户端令牌', 'Oauth2 Token'); +INSERT INTO "sys_i18n" VALUES (642, 'job.backup_export_cdr', '备份-CDR数据定期导出', 'Backup-Periodic export of CDR Data'); +INSERT INTO "sys_i18n" VALUES (643, 'job.backup_export_cdr_remark', 'dataType: 类型支持 ims/smf/sgwc/smsc +fileType: 文件类型 csv/xlsx +hour: 数据时间从任务执行时间前的小时数', 'Backup-Periodic export of dataType: type support ims/smf/sgwc/smsc +fileType: file type csv/xlsx +hour: data time from the hour before the task execution time'); INSERT INTO "sys_i18n" VALUES (644, 'menu.log.exportFile', '导出文件', 'Exported File'); INSERT INTO "sys_i18n" VALUES (645, 'menu.perf.kpiCReport', '自定义指标数据', 'Custom Indicator Data'); INSERT INTO "sys_i18n" VALUES (646, 'menu.trace.taskHLR', 'HLR 跟踪任务', 'HLR Trace Task'); @@ -821,7 +827,7 @@ storeNum: retention number, default retention 7'); INSERT INTO "sys_i18n" VALUES (726, 'job.backup_export_udm', '备份-UDM数据定期导出', 'Backup-Periodic export of UDM Data'); INSERT INTO "sys_i18n" VALUES (727, 'job.backup_export_udm_remark', 'dataType: 类型支持 auth/sub/voip/volte fileType: 文件类型 csv/txt', 'Backup-Periodic export of dataType: type support auth/sub/voip/volte -fileType: file type csv/txtUDM Data'); +fileType: file type csv/txt'); INSERT INTO "sys_i18n" VALUES (728, 'dictData.cdr_sip_code_cause.0', '因其他原因呼叫失败', 'Call failure for other reason'); INSERT INTO "sys_i18n" VALUES (729, 'dictData.cdr_sip_code_cause.200', '正常通话清除', 'Normal Call Clearing'); INSERT INTO "sys_i18n" VALUES (730, 'dictData.cdr_sip_code_cause.202', '申请已被接受处理,但尚未完成 ', 'The request has been accepted for processing, but it hasn it completed yet'); @@ -844,3 +850,81 @@ INSERT INTO "sys_i18n" VALUES (746, 'dictData.cdr_sip_code_cause.580', '因其 INSERT INTO "sys_i18n" VALUES (747, 'dictData.cdr_sip_code_cause.603', 'MT 明确拒绝通话', 'MT explicitly rejected the call'); INSERT INTO "sys_i18n" VALUES (748, 'dictData.cdr_sip_code_cause.606', '呼叫已到达用户设备,但会话设置的某些部分不可接受', 'The call reached the user’s device, but some parts of the session setup weren it acceptable'); INSERT INTO "sys_i18n" VALUES (749, 'dictType.cdr_sip_code_cause', 'IMS-Voice-SIP响应代码类别类型原因', 'IMS-Voice-SIP Response Code Category Type Cause'); +INSERT INTO "sys_i18n" VALUES (750, 'job.backup_export_log', '备份-日志数据定期导出', 'Backup-Periodic export of Log Data'); +INSERT INTO "sys_i18n" VALUES (751, 'job.backup_export_log_remark', 'dataType: 类型支持 operate/login +fileType: 文件类型 csv/xlsx +hour: 数据时间从任务执行时间前的小时数', 'Backup-Periodic export of dataType: type support operate/login +fileType: file type csv/xlsx +hour: data time from the hour before the task execution time'); +INSERT INTO "sys_i18n" VALUES (752, 'job.ne_alarm_state_check_cmd', '网元告警-内存/CPU/磁盘检查', 'NE Alarm-Memory/CPU/Disk Checks'); +INSERT INTO "sys_i18n" VALUES (753, 'job.ne_alarm_state_check_cmd_remark', '检查网元的内存/CPU/磁盘检查健康状况,在出现过阈值时发出警报。 + +Alarm type: +CommunicationAlarm=1 +EquipmentAlarm=2 +ProcessingFailure=3 +EnvironmentalAlarm=4 +QualityOfServiceAlarm=5 + +Severity: +Critical=1 +Major=2 +Minor=3 +Warning=4 + +AddInfo: 告警补充信息 +cpuUseGt: CPU使用率大于, 范围0~100% +memUseGt: 内存使用率大于, 范围0~100% +diskUseGt: 磁盘使用率大于, 范围0~100%', 'Checks the memory/CPU/disk check health of the network element and sends alerts when thresholds are crossed. + +Alarm type: +CommunicationAlarm=1 +EquipmentAlarm=2 +ProcessingFailure=3 +EnvironmentalAlarm=4 +QualityOfServiceAlarm=5 + +Severity: +Critical=1 +Major=2 +Minor=3 +Warning=4 + +AddInfo: Additional information on alarms +cpuUseGt: CPU utilization greater than, range 0 to 100% +memUseGt: Memory utilization greater than, range 0 to 100% +diskUseGt: Disk utilization greater than, range 0 to 100%'); +INSERT INTO "sys_i18n" VALUES (754, 'job.ne_alarm_state_check_license', '网元告警-License到期检查', 'NE Alarm-License Expire Check'); +INSERT INTO "sys_i18n" VALUES (755, 'job.ne_alarm_state_check_license_remark', '检查网元的License是否即将到期,在出现过阈值时发出警报。 + +Alarm type: +CommunicationAlarm=1 +EquipmentAlarm=2 +ProcessingFailure=3 +EnvironmentalAlarm=4 +QualityOfServiceAlarm=5 + +Severity: +Critical=1 +Major=2 +Minor=3 +Warning=4 + +AddInfo: 告警补充信息 +dayLt: 天数小于,默认30天', 'Checks if the network element is License is about to expire and sends an alert if a threshold is crossed. + +Alarm type: +CommunicationAlarm=1 +EquipmentAlarm=2 +ProcessingFailure=3 +EnvironmentalAlarm=4 +QualityOfServiceAlarm=5 + +Severity: +Critical=1 +Major=2 +Minor=3 +Warning=4 + +AddInfo: Additional information on alarms +dayLt: Days less than, default 30 days'); diff --git a/build/database/lite/install/sys_job.sql b/build/database/lite/install/sys_job.sql index 6a0b9a52..1c9a85b4 100644 --- a/build/database/lite/install/sys_job.sql +++ b/build/database/lite/install/sys_job.sql @@ -34,16 +34,18 @@ ON "sys_job" ( -- Records of sys_job -- ---------------------------- INSERT INTO "sys_job" VALUES (1, 'job.monitor_sys_resource', 'SYSTEM', 'monitor_sys_resource', '{"interval":5}', '0 0/5 * * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.monitor_sys_resource_remark'); -INSERT INTO "sys_job" VALUES (2, 'job.ne_config_backup', 'SYSTEM', 'ne_config_backup', '', '0 30 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_config_backup_remark'); INSERT INTO "sys_job" VALUES (3, 'job.ne_data_udm', 'SYSTEM', 'ne_data_udm', '', '0 0 0/12 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, ''); -INSERT INTO "sys_job" VALUES (4, 'job.ne_alarm_state_check', 'SYSTEM', 'ne_alarm_state_check', '{"alarmTitle":"NE State Check Alarm","alarmType":"2","origSeverity":"2","specificProblem":"alarm cause: the system state of target NE has not been received","specificProblemId":"AC10000","addInfo":""}', '0/30 * * * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_alarm_state_check_remark'); -INSERT INTO "sys_job" VALUES (5, 'job.delete_alarm_record', 'SYSTEM', 'delete_alarm_record', '{"storeDays":7}', '0 10 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_alarm_record_remark'); -INSERT INTO "sys_job" VALUES (6, 'job.delete_kpi_record', 'SYSTEM', 'delete_kpi_record', '{"storeDays":7,"neList":["IMS","AMF","UDM","UPF","MME","SMSC","SMF","MME"]}', '0 20 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_kpi_record_remark'); -INSERT INTO "sys_job" VALUES (7, 'job.delete_ne_config_backup', 'SYSTEM', 'delete_ne_config_backup', '{"storeDays":7,"storeNum":7}', '0 20 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_ne_config_backup_remark'); -INSERT INTO "sys_job" VALUES (21, 'job.backup_export_table_sys_log_operate', 'SYSTEM', 'backup_export_table', '{"hour":1,"columns":["id","title","business_type","opera_by","opera_url_method","opera_url","opera_ip","status","opera_time","cost_time"],"tableName":"sys_log_operate","backupPath":"/log/operate_log"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_sys_log_operate_remark'); -INSERT INTO "sys_job" VALUES (22, 'job.backup_export_table_cdr_event_ims', 'SYSTEM', 'backup_export_table', '{"hour":1,"columns":["id","record_type","call_type","caller_party","called_party","call_duration","cause","seizure_time","release_time"],"tableName":"cdr_event_ims","backupPath":"/cdr/ims_cdr"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_ims_remark'); -INSERT INTO "sys_job" VALUES (23, 'job.backup_export_table_cdr_event_smf', 'SYSTEM', 'backup_export_table', '{"hour":1,"columns":["id","charging_id","subscription_id_data","subscription_id_type","data_volume_uplink","data_volume_downlink","data_total_volume","invocation_timestamp","user_identifier","ssc_mode","dnn_id","pdu_type","rat_type","pdu_ipv4","pdu_ipv6","network_function_ipv4_address","record_nfId","record_type","record_opening_time"],"tableName":"cdr_event_smf","backupPath":"/cdr/smf_cdr"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_smf_remark'); -INSERT INTO "sys_job" VALUES (24, 'job.backup_export_table_cdr_event_smsc', 'SYSTEM', 'backup_export_table', '{"hour":1,"columns":["id","record_type","service_type","caller_party","called_party","result","update_time"],"tableName":"cdr_event_smsc","backupPath":"/cdr/smsc_cdr"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_smsc_remark'); -INSERT INTO "sys_job" VALUES (25, 'job.backup_export_table_cdr_event_sgwc', 'SYSTEM', 'backup_export_table', '{"hour":1,"columns":["charging_id","served_imsi","served_msisdn","data_volume_gprs_uplink","data_volume_gprs_downlink","duration","invocation_timestamp","pgw_address_used","sgw_address","rat_type","pdp_pdn_type","served_pdppdn_address","serving_node_address","serving_node_type","access_point_name_ni","cause_for_rec_closing","record_sequence_number","local_record_sequence_number","record_type","record_opening_time"],"tableName":"cdr_event_sgwc","backupPath":"/cdr/sgwc_cdr"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_sgwc_remark'); -INSERT INTO "sys_job" VALUES (26, 'job.backup_remove_file', 'SYSTEM', 'backup_remove_file', '[{"backupPath":"/log/operate_log","storeDays":30,"storeNum":7},{"backupPath":"/cdr/ims_cdr","storeDays":30},{"backupPath":"/cdr/smf_cdr","storeDays":30},{"backupPath":"/cdr/smsc_cdr","storeDays":30},{"backupPath":"/cdr/sgwc_cdr","storeDays":30},{"backupPath":"/udm_data/auth","storeDays":30},{"backupPath":"/udm_data/sub","storeDays":30},{"backupPath":"/udm_data/voip","storeDays":30},{"backupPath":"/udm_data/volte","storeDays":30}]', '0 10 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_remove_file_remark'); -INSERT INTO "sys_job" VALUES (27, 'job.backup_export_udm', 'SYSTEM', 'backup_export_udm', '{"dataType":["auth","sub","voip","volte"],"fileType":"txt"}', '0 35 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'supervisor', 1745481169354, 'job.backup_export_udm_remark'); +INSERT INTO "sys_job" VALUES (6, 'job.ne_config_backup', 'SYSTEM', 'ne_config_backup', '', '0 30 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_config_backup_remark'); + +INSERT INTO "sys_job" VALUES (10, 'job.delete_ne_config_backup', 'SYSTEM', 'delete_ne_config_backup', '{"storeDays":7,"storeNum":7}', '0 20 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_ne_config_backup_remark'); +INSERT INTO "sys_job" VALUES (11, 'job.delete_alarm_record', 'SYSTEM', 'delete_alarm_record', '{"storeDays":7}', '0 10 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_alarm_record_remark'); +INSERT INTO "sys_job" VALUES (12, 'job.delete_kpi_record', 'SYSTEM', 'delete_kpi_record', '{"storeDays":7,"neList":["IMS","AMF","UDM","UPF","MME","SMSC","SMF","MME"]}', '0 20 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_kpi_record_remark'); + +INSERT INTO "sys_job" VALUES (20, 'job.ne_alarm_state_check', 'SYSTEM', 'ne_alarm_state_check', '{"alarmTitle":"NE State Check Alarm","alarmType":"2","origSeverity":"2","specificProblem":"alarm cause: the system state of target NE has not been received","specificProblemId":"AC10000","addInfo":""}', '0/30 * * * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_alarm_state_check_remark'); +INSERT INTO "sys_job" VALUES (21, 'job.ne_alarm_state_check_cmd', 'SYSTEM', 'ne_alarm_state_check_cmd', '{"alarmTitle":"NE State Check Alarm CPU/Menory/Disk","alarmType":"2","origSeverity":"2","specificProblem":"Alarm Cause: CPU/Menory/Disk status received from target NE reaches the threshold","specificProblemId":"AC10100","addInfo":"","cpuUseGt":70,"memUseGt":70,"diskUseGt":70}', '0/15 * * * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_alarm_state_check_cmd_remark'); +INSERT INTO "sys_job" VALUES (22, 'job.ne_alarm_state_check_license', 'SYSTEM', 'ne_alarm_state_check_license', '{"alarmTitle":"NE State Check Alarm License","alarmType":"2","origSeverity":"2","specificProblem":"Alarm Cause: License received from target NE is about to expire","specificProblemId":"AC10200","addInfo":"","dayLt":7}', '0 5 0 * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_alarm_state_check_license_remark'); + +INSERT INTO "sys_job" VALUES (30, 'job.backup_remove_file', 'SYSTEM', 'backup_remove_file', '[{"backupPath":"/udm_data/auth","storeDays":30},{"backupPath":"/udm_data/sub","storeDays":30},{"backupPath":"/udm_data/voip","storeDays":30},{"backupPath":"/udm_data/volte","storeDays":30},{"backupPath":"/cdr/ims_cdr_event","storeDays":30},{"backupPath":"/cdr/smsc_cdr_event","storeDays":30},{"backupPath":"/cdr/smf_cdr_event","storeDays":30},{"backupPath":"/cdr/sgwc_cdr_event","storeDays":30},{"backupPath":"/log/sys_log_operate","storeDays":30,"storeNum":7},{"backupPath":"/log/sys_log_login","storeDays":30,"storeNum":7}]', '0 10 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_remove_file_remark'); +INSERT INTO "sys_job" VALUES (31, 'job.backup_export_udm', 'SYSTEM', 'backup_export_udm', '{"dataType":["auth","sub","voip","volte"],"fileType":"txt"}', '0 35 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'supervisor', 1745481169354, 'job.backup_export_udm_remark'); +INSERT INTO "sys_job" VALUES (32, 'job.backup_export_cdr', 'SYSTEM', 'backup_export_cdr', '{"dataType":["ims","smf","sgwc","smsc"],"fileType":"xlsx","hour":1}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'supervisor', 1745481169354, 'job.backup_export_cdr_remark'); +INSERT INTO "sys_job" VALUES (33, 'job.backup_export_log', 'SYSTEM', 'backup_export_log', '{"dataType":["operate","login"],"fileType":"xlsx","hour":1}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'supervisor', 1745481169354, 'job.backup_export_log_remark'); diff --git a/build/database/std/install/sys_i18n.sql b/build/database/std/install/sys_i18n.sql index 38e80800..3a82c2f4 100644 --- a/build/database/std/install/sys_i18n.sql +++ b/build/database/std/install/sys_i18n.sql @@ -6,8 +6,8 @@ DROP TABLE IF EXISTS `sys_i18n`; CREATE TABLE `sys_i18n` ( `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ID', `key_lable` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '多语言属性名', - `value_zh` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '-' COMMENT '中文', - `value_en` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '-' COMMENT '英文', + `value_zh` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '-' COMMENT '中文', + `value_en` varchar(2048) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT '-' COMMENT '英文', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统_多语言'; @@ -295,7 +295,7 @@ INSERT INTO `sys_i18n` VALUES (277, 'config..export.key', '参数键名', 'Confi INSERT INTO `sys_i18n` VALUES (278, 'config..export.value', '参数键值', 'Config Value'); INSERT INTO `sys_i18n` VALUES (279, 'config..export.type', '系统内置', 'Built In'); INSERT INTO `sys_i18n` VALUES (280, 'config..export.remark', '参数说明', 'Config Description'); -INSERT INTO `sys_i18n` VALUES (281, 'config.sys.titleValue', '5G Core Network', 'Core Network'); +INSERT INTO `sys_i18n` VALUES (281, 'config.sys.titleValue', 'Core Network', 'Core Network'); INSERT INTO `sys_i18n` VALUES (282, 'config.sys.copyrightValue', 'Copyright ©2025 Core Network', 'Copyright ©2025 Core Network'); INSERT INTO `sys_i18n` VALUES (283, 'config.noData', '没有可访问参数配置数据!', 'No parameter configuration data is accessible!'); INSERT INTO `sys_i18n` VALUES (284, 'config.errKey', '无效 key', 'Invalid key'); @@ -656,8 +656,8 @@ INSERT INTO `sys_i18n` VALUES (638, 'job.backup_export_table_cdr_event_ims', ' INSERT INTO `sys_i18n` VALUES (639, 'job.backup_export_table_cdr_event_smf', '备份-数据话单表定期导出', 'Backup-Regular Export of data sheet tables'); INSERT INTO `sys_i18n` VALUES (640, 'cache.name.oauth2_codes', '客户端授权码', 'Oauth2 Client Code'); INSERT INTO `sys_i18n` VALUES (641, 'cache.name.oauth2_devices', '客户端令牌', 'Oauth2 Token'); --- INSERT INTO `sys_i18n` VALUES (642, 'table.cdr_event_smf', '数据话单', 'Data CDR'); --- INSERT INTO `sys_i18n` VALUES (643, 'table.cdr_event_smsc', '短信话单', 'SMS CDR'); +INSERT INTO `sys_i18n` VALUES (642, 'job.backup_export_cdr', '备份-CDR数据定期导出', 'Backup-Periodic export of CDR Data'); +INSERT INTO `sys_i18n` VALUES (643, 'job.backup_export_cdr_remark', 'dataType: 类型支持 ims/smf/sgwc/smsc\nfileType: 文件类型 csv/xlsx\nhour: 数据时间从任务执行时间前的小时数', 'Backup-Periodic export of dataType: type support ims/smf/sgwc/smsc\nfileType: file type csv/xlsx\nhour: data time from the hour before the task execution time'); INSERT INTO `sys_i18n` VALUES (644, 'menu.log.exportFile', '导出文件', 'Exported File'); INSERT INTO `sys_i18n` VALUES (645, 'menu.perf.kpiCReport', '自定义指标数据', 'Custom Indicator Data'); INSERT INTO `sys_i18n` VALUES (646, 'menu.trace.taskHLR', 'HLR 跟踪任务', 'HLR Trace Task'); @@ -741,7 +741,7 @@ INSERT INTO `sys_i18n` VALUES (723, 'job.backup_export_table_cdr_event_smsc_rema INSERT INTO `sys_i18n` VALUES (724, 'job.backup_export_table_cdr_event_sgwc_remark', 'hour: 数据时间从任务执行时间前的小时数\ntableName: 数据表名\ncolumns: 支持字段\nbackupPath: 备份输出路径 /usr/local/omc/backup/{backupPath}', 'hour: data time from the hour before the task execution time \ntableName: data table name \ncolumns: support fields \nbackupPath: backup output path /usr/local/omc/backup/{backupPath}'); INSERT INTO `sys_i18n` VALUES (725, 'job.backup_remove_file_remark', 'backupPath: 备份路径 /usr/local/omc/backup/{backupPath}\nstoreDays: 保留天数\nstoreNum: 保留数量,默认保留7', 'backupPath: backup path /usr/local/omc/backup/{backupPath}\nstoreDays: retention days\nstoreNum: retention number, default retention 7'); INSERT INTO `sys_i18n` VALUES (726, 'job.backup_export_udm', '备份-UDM数据定期导出', 'Backup-Periodic export of UDM Data'); -INSERT INTO `sys_i18n` VALUES (727, 'job.backup_export_udm_remark', 'dataType: 类型支持 auth/sub/voip/volte\nfileType: 文件类型 csv/txt', 'Backup-Periodic export of dataType: type support auth/sub/voip/volte\nfileType: file type csv/txtUDM Data'); +INSERT INTO `sys_i18n` VALUES (727, 'job.backup_export_udm_remark', 'dataType: 类型支持 auth/sub/voip/volte\nfileType: 文件类型 csv/txt', 'Backup-Periodic export of dataType: type support auth/sub/voip/volte\nfileType: file type csv/txt'); INSERT INTO `sys_i18n` VALUES (728, 'dictData.cdr_sip_code_cause.0', '因其他原因呼叫失败', 'Call failure for other reason'); INSERT INTO `sys_i18n` VALUES (729, 'dictData.cdr_sip_code_cause.200', '正常通话清除', 'Normal Call Clearing'); INSERT INTO `sys_i18n` VALUES (730, 'dictData.cdr_sip_code_cause.202', '申请已被接受处理,但尚未完成 ', 'The request has been accepted for processing, but it hasn it completed yet'); @@ -764,5 +764,11 @@ INSERT INTO `sys_i18n` VALUES (746, 'dictData.cdr_sip_code_cause.580', '因其 INSERT INTO `sys_i18n` VALUES (747, 'dictData.cdr_sip_code_cause.603', 'MT 明确拒绝通话', 'MT explicitly rejected the call'); INSERT INTO `sys_i18n` VALUES (748, 'dictData.cdr_sip_code_cause.606', '呼叫已到达用户设备,但会话设置的某些部分不可接受', 'The call reached the user’s device, but some parts of the session setup weren it acceptable'); INSERT INTO `sys_i18n` VALUES (749, 'dictType.cdr_sip_code_cause', 'IMS-Voice-SIP响应代码类别类型原因', 'IMS-Voice-SIP Response Code Category Type Cause'); +INSERT INTO `sys_i18n` VALUES (750, 'job.backup_export_log', '备份-日志数据定期导出', 'Backup-Periodic export of Log Data'); +INSERT INTO `sys_i18n` VALUES (751, 'job.backup_export_log_remark', 'dataType: 类型支持 operate/login\nfileType: 文件类型 csv/xlsx\nhour: 数据时间从任务执行时间前的小时数', 'Backup-Periodic export of dataType: type support operate/login\nfileType: file type csv/xlsx\nhour: data time from the hour before the task execution time'); +INSERT INTO `sys_i18n` VALUES (752, 'job.ne_alarm_state_check_cmd', '网元告警-内存/CPU/磁盘检查', 'NE Alarm-Memory/CPU/Disk Checks'); +INSERT INTO `sys_i18n` VALUES (753, 'job.ne_alarm_state_check_cmd_remark', '检查网元的内存/CPU/磁盘检查健康状况,在出现过阈值时发出警报。\r\n\r\nAlarm type:\r\nCommunicationAlarm=1\r\nEquipmentAlarm=2\r\nProcessingFailure=3\r\nEnvironmentalAlarm=4\r\nQualityOfServiceAlarm=5\r\n\r\nSeverity:\r\nCritical=1\r\nMajor=2\r\nMinor=3\r\nWarning=4\r\n\r\nAddInfo: 告警补充信息\r\ncpuUseGt: CPU使用率大于, 范围0~100%\r\nmemUseGt: 内存使用率大于, 范围0~100%\r\ndiskUseGt: 磁盘使用率大于, 范围0~100%', 'Checks the memory/CPU/disk check health of the network element and sends alerts when thresholds are crossed.\n\nAlarm type:\nCommunicationAlarm=1\nEquipmentAlarm=2\nProcessingFailure=3\nEnvironmentalAlarm=4\nQualityOfServiceAlarm=5\n\nSeverity:\nCritical=1\nMajor=2\nMinor=3\nWarning=4\r\n\r\nAddInfo: Additional information on alarms\r\ncpuUseGt: CPU utilization greater than, range 0 to 100%\r\nmemUseGt: Memory utilization greater than, range 0 to 100%\r\ndiskUseGt: Disk utilization greater than, range 0 to 100%'); +INSERT INTO `sys_i18n` VALUES (754, 'job.ne_alarm_state_check_license', '网元告警-License到期检查', 'NE Alarm-License Expire Check'); +INSERT INTO `sys_i18n` VALUES (755, 'job.ne_alarm_state_check_license_remark', '检查网元的License是否即将到期,在出现过阈值时发出警报。\r\n\r\nAlarm type:\r\nCommunicationAlarm=1\r\nEquipmentAlarm=2\r\nProcessingFailure=3\r\nEnvironmentalAlarm=4\r\nQualityOfServiceAlarm=5\r\n\r\nSeverity:\r\nCritical=1\r\nMajor=2\r\nMinor=3\r\nWarning=4\r\n\r\nAddInfo: 告警补充信息\r\ndayLt: 天数小于,默认30天', 'Checks if the network element is License is about to expire and sends an alert if a threshold is crossed.\n\nAlarm type:\nCommunicationAlarm=1\nEquipmentAlarm=2\nProcessingFailure=3\nEnvironmentalAlarm=4\nQualityOfServiceAlarm=5\n\nSeverity:\nCritical=1\nMajor=2\nMinor=3\nWarning=4\r\n\r\nAddInfo: Additional information on alarms\r\ndayLt: Days less than, default 30 days'); -- Dump completed on 2025-02-14 15:26:56 diff --git a/build/database/std/install/sys_job.sql b/build/database/std/install/sys_job.sql index aa0f534e..ae856654 100644 --- a/build/database/std/install/sys_job.sql +++ b/build/database/std/install/sys_job.sql @@ -29,20 +29,26 @@ CREATE TABLE `sys_job` ( -- Records of sys_job -- ---------------------------- INSERT INTO `sys_job` VALUES (1, 'job.monitor_sys_resource', 'SYSTEM', 'monitor_sys_resource', '{\"interval\":5}', '0 0/5 * * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.monitor_sys_resource_remark'); -INSERT INTO `sys_job` VALUES (2, 'job.ne_config_backup', 'SYSTEM', 'ne_config_backup', '', '0 30 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_config_backup_remark'); INSERT INTO `sys_job` VALUES (3, 'job.ne_data_udm', 'SYSTEM', 'ne_data_udm', '', '0 0 0/12 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, ''); -INSERT INTO `sys_job` VALUES (4, 'job.ne_alarm_state_check', 'SYSTEM', 'ne_alarm_state_check', '{\"alarmTitle\":\"NE State Check Alarm\",\"alarmType\":\"2\",\"origSeverity\":\"2\",\"specificProblem\":\"alarm cause: the system state of target NE has not been received\",\"specificProblemId\":\"AC10000\",\"addInfo\":\"\"}', '0/30 * * * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_alarm_state_check_remark'); -INSERT INTO `sys_job` VALUES (5, 'job.delete_alarm_record', 'SYSTEM', 'delete_alarm_record', '{\"storeDays\":7}', '0 10 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_alarm_record_remark'); -INSERT INTO `sys_job` VALUES (6, 'job.delete_kpi_record', 'SYSTEM', 'delete_kpi_record', '{\"storeDays\":7,\"neList\":[\"IMS\",\"AMF\",\"UDM\",\"UPF\",\"MME\",\"SMSC\",\"SMF\",\"MME\"]}', '0 20 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_kpi_record_remark'); -INSERT INTO `sys_job` VALUES (7, 'job.delete_ne_config_backup', 'SYSTEM', 'delete_ne_config_backup', '{\"storeDays\":7,\"storeNum\":7}', '0 20 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_ne_config_backup_remark'); +INSERT INTO `sys_job` VALUES (6, 'job.ne_config_backup', 'SYSTEM', 'ne_config_backup', '', '0 30 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_config_backup_remark'); -INSERT INTO `sys_job` VALUES (21, 'job.backup_export_table_sys_log_operate', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"id\",\"title\",\"business_type\",\"opera_by\",\"opera_url_method\",\"opera_url\",\"opera_ip\",\"status\",\"opera_time\",\"cost_time\"],\"tableName\":\"sys_log_operate\",\"backupPath\":\"/log/operate_log\"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_sys_log_operate_remark'); -INSERT INTO `sys_job` VALUES (22, 'job.backup_export_table_cdr_event_ims', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"id\",\"record_type\",\"call_type\",\"caller_party\",\"called_party\",\"call_duration\",\"cause\",\"seizure_time\",\"release_time\"],\"tableName\":\"cdr_event_ims\",\"backupPath\":\"/cdr/ims_cdr\"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_ims_remark'); -INSERT INTO `sys_job` VALUES (23, 'job.backup_export_table_cdr_event_smf', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"id\",\"charging_id\",\"subscription_id_data\",\"subscription_id_type\",\"data_volume_uplink\",\"data_volume_downlink\",\"data_total_volume\",\"invocation_timestamp\",\"user_identifier\",\"ssc_mode\",\"dnn_id\",\"pdu_type\",\"rat_type\",\"pdu_ipv4\",\"pdu_ipv6\",\"network_function_ipv4_address\",\"record_nfId\",\"record_type\",\"record_opening_time\"],\"tableName\":\"cdr_event_smf\",\"backupPath\":\"/cdr/smf_cdr\"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_smf_remark'); -INSERT INTO `sys_job` VALUES (24, 'job.backup_export_table_cdr_event_smsc', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"id\",\"record_type\",\"service_type\",\"caller_party\",\"called_party\",\"result\",\"update_time\"],\"tableName\":\"cdr_event_smsc\",\"backupPath\":\"/cdr/smsc_cdr\"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_smsc_remark'); -INSERT INTO `sys_job` VALUES (25, 'job.backup_export_table_cdr_event_sgwc', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"charging_id\",\"served_imsi\",\"served_msisdn\",\"data_volume_gprs_uplink\",\"data_volume_gprs_downlink\",\"duration\",\"invocation_timestamp\",\"pgw_address_used\",\"sgw_address\",\"rat_type\",\"pdp_pdn_type\",\"served_pdppdn_address\",\"serving_node_address\",\"serving_node_type\",\"access_point_name_ni\",\"cause_for_rec_closing\",\"record_sequence_number\",\"local_record_sequence_number\",\"record_type\",\"record_opening_time\"],\"tableName\":\"cdr_event_sgwc\",\"backupPath\":\"/cdr/sgwc_cdr\"}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_sgwc_remark'); -INSERT INTO `sys_job` VALUES (26, 'job.backup_remove_file', 'SYSTEM', 'backup_remove_file', '[{\"backupPath\":\"/log/operate_log\",\"storeDays\":30,\"storeNum\":7},{\"backupPath\":\"/cdr/ims_cdr\",\"storeDays\":30},{\"backupPath\":\"/cdr/smf_cdr\",\"storeDays\":30},{\"backupPath\":\"/cdr/smsc_cdr\",\"storeDays\":30},{\"backupPath\":\"/cdr/sgwc_cdr\",\"storeDays\":30},{\"backupPath\":\"/udm_data/auth\",\"storeDays\":30},{\"backupPath\":\"/udm_data/sub\",\"storeDays\":30},{\"backupPath\":\"/udm_data/voip\",\"storeDays\":30},{\"backupPath\":\"/udm_data/volte\",\"storeDays\":30}]', '0 10 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839,'job.backup_remove_file_remark'); -INSERT INTO `sys_job` VALUES (27, 'job.backup_export_udm', 'SYSTEM', 'backup_export_udm', '{\"dataType\":[\"auth\",\"sub\",\"voip\",\"volte\"],\"fileType\":\"txt\"}', '0 35 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'supervisor', 1745481169354, 'job.backup_export_udm_remark'); +INSERT INTO `sys_job` VALUES (10, 'job.delete_ne_config_backup', 'SYSTEM', 'delete_ne_config_backup', '{\"storeDays\":7,\"storeNum\":7}', '0 20 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_ne_config_backup_remark'); +INSERT INTO `sys_job` VALUES (11, 'job.delete_alarm_record', 'SYSTEM', 'delete_alarm_record', '{\"storeDays\":7}', '0 10 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_alarm_record_remark'); +INSERT INTO `sys_job` VALUES (12, 'job.delete_kpi_record', 'SYSTEM', 'delete_kpi_record', '{\"storeDays\":7,\"neList\":[\"IMS\",\"AMF\",\"UDM\",\"UPF\",\"MME\",\"SMSC\",\"SMF\",\"MME\"]}', '0 20 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.delete_kpi_record_remark'); + +INSERT INTO `sys_job` VALUES (20, 'job.ne_alarm_state_check', 'SYSTEM', 'ne_alarm_state_check', '{\"alarmTitle\":\"NE State Check Alarm\",\"alarmType\":\"2\",\"origSeverity\":\"2\",\"specificProblem\":\"alarm cause: the system state of target NE has not been received\",\"specificProblemId\":\"AC10000\",\"addInfo\":\"\"}', '0/30 * * * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_alarm_state_check_remark'); +INSERT INTO `sys_job` VALUES (21, 'job.ne_alarm_state_check_cmd', 'SYSTEM', 'ne_alarm_state_check_cmd', '{\"alarmTitle\":\"NE State Check Alarm CPU/Menory/Disk\",\"alarmType\":\"2\",\"origSeverity\":\"2\",\"specificProblem\":\"Alarm Cause: CPU/Menory/Disk status received from target NE reaches the threshold\",\"specificProblemId\":\"AC10100\",\"addInfo\":\"\",\"cpuUseGt\":70,\"memUseGt\":70,\"diskUseGt\":70}', '0/15 * * * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_alarm_state_check_cmd_remark'); +INSERT INTO `sys_job` VALUES (22, 'job.ne_alarm_state_check_license', 'SYSTEM', 'ne_alarm_state_check_license', '{\"alarmTitle\":\"NE State Check Alarm License\",\"alarmType\":\"2\",\"origSeverity\":\"2\",\"specificProblem\":\"Alarm Cause: License received from target NE is about to expire\",\"specificProblemId\":\"AC10200\",\"addInfo\":\"\",\"dayLt\":7}', '0 5 0 * * ?', '3', '0', '1', '0', 'system', 1698478134839, 'system', 1698478134839, 'job.ne_alarm_state_check_license_remark'); + +INSERT INTO `sys_job` VALUES (30, 'job.backup_remove_file', 'SYSTEM', 'backup_remove_file', '[{\"backupPath\":\"/udm_data/auth\",\"storeDays\":30},{\"backupPath\":\"/udm_data/sub\",\"storeDays\":30},{\"backupPath\":\"/udm_data/voip\",\"storeDays\":30},{\"backupPath\":\"/udm_data/volte\",\"storeDays\":30},{\"backupPath\":\"/cdr/ims_cdr_event\",\"storeDays\":30},{\"backupPath\":\"/cdr/smsc_cdr_event\",\"storeDays\":30},{\"backupPath\":\"/cdr/smf_cdr_event\",\"storeDays\":30},{\"backupPath\":\"/cdr/sgwc_cdr_event\",\"storeDays\":30},{\"backupPath\":\"/log/sys_log_operate\",\"storeDays\":30,\"storeNum\":7},{\"backupPath\":\"/log/sys_log_login\",\"storeDays\":30,\"storeNum\":7}]', '0 10 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_remove_file_remark'); +INSERT INTO `sys_job` VALUES (31, 'job.backup_export_udm', 'SYSTEM', 'backup_export_udm', '{\"dataType\":[\"auth\",\"sub\",\"voip\",\"volte\"],\"fileType\":\"txt\"}', '0 35 0 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'supervisor', 1745481169354, 'job.backup_export_udm_remark'); +INSERT INTO `sys_job` VALUES (32, 'job.backup_export_cdr', 'SYSTEM', 'backup_export_cdr', '{\"dataType\":[\"ims\",\"smf\",\"sgwc\",\"smsc\"],\"fileType\":\"xlsx\",\"hour\":1}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'supervisor', 1745481169354, 'job.backup_export_cdr_remark'); +INSERT INTO `sys_job` VALUES (33, 'job.backup_export_log', 'SYSTEM', 'backup_export_log', '{\"dataType\":[\"operate\",\"login\"],\"fileType\":\"xlsx\",\"hour\":1}', '0 0 0/1 * * ?', '3', '0', '1', '1', 'system', 1698478134839, 'supervisor', 1745481169354, 'job.backup_export_log_remark'); +-- INSERT INTO `sys_job` VALUES (34, 'job.backup_export_table_sys_log_operate', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"id\",\"title\",\"business_type\",\"opera_by\",\"opera_url_method\",\"opera_url\",\"opera_ip\",\"status\",\"opera_time\",\"cost_time\"],\"tableName\":\"sys_log_operate\",\"backupPath\":\"/log/operate_log\"}', '0 0 0/1 * * ?', '3', '0', '0', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_sys_log_operate_remark'); +-- INSERT INTO `sys_job` VALUES (35, 'job.backup_export_table_cdr_event_ims', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"id\",\"record_type\",\"call_type\",\"caller_party\",\"called_party\",\"call_duration\",\"cause\",\"seizure_time\",\"release_time\"],\"tableName\":\"cdr_event_ims\",\"backupPath\":\"/cdr/ims_cdr\"}', '0 0 0/1 * * ?', '3', '0', '0', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_ims_remark'); +-- INSERT INTO `sys_job` VALUES (36, 'job.backup_export_table_cdr_event_smf', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"id\",\"charging_id\",\"subscription_id_data\",\"subscription_id_type\",\"data_volume_uplink\",\"data_volume_downlink\",\"data_total_volume\",\"invocation_timestamp\",\"user_identifier\",\"ssc_mode\",\"dnn_id\",\"pdu_type\",\"rat_type\",\"pdu_ipv4\",\"pdu_ipv6\",\"network_function_ipv4_address\",\"record_nfId\",\"record_type\",\"record_opening_time\"],\"tableName\":\"cdr_event_smf\",\"backupPath\":\"/cdr/smf_cdr\"}', '0 0 0/1 * * ?', '3', '0', '0', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_smf_remark'); +-- INSERT INTO `sys_job` VALUES (37, 'job.backup_export_table_cdr_event_smsc', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"id\",\"record_type\",\"service_type\",\"caller_party\",\"called_party\",\"result\",\"update_time\"],\"tableName\":\"cdr_event_smsc\",\"backupPath\":\"/cdr/smsc_cdr\"}', '0 0 0/1 * * ?', '3', '0', '0', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_smsc_remark'); +-- INSERT INTO `sys_job` VALUES (38, 'job.backup_export_table_cdr_event_sgwc', 'SYSTEM', 'backup_export_table', '{\"hour\":1,\"columns\":[\"charging_id\",\"served_imsi\",\"served_msisdn\",\"data_volume_gprs_uplink\",\"data_volume_gprs_downlink\",\"duration\",\"invocation_timestamp\",\"pgw_address_used\",\"sgw_address\",\"rat_type\",\"pdp_pdn_type\",\"served_pdppdn_address\",\"serving_node_address\",\"serving_node_type\",\"access_point_name_ni\",\"cause_for_rec_closing\",\"record_sequence_number\",\"local_record_sequence_number\",\"record_type\",\"record_opening_time\"],\"tableName\":\"cdr_event_sgwc\",\"backupPath\":\"/cdr/sgwc_cdr\"}', '0 0 0/1 * * ?', '3', '0', '0', '1', 'system', 1698478134839, 'system', 1698478134839, 'job.backup_export_table_cdr_event_sgwc_remark'); SET FOREIGN_KEY_CHECKS = 1; diff --git a/src/modules/crontask/processor/backup_export_cdr/backup_export_cdr.go b/src/modules/crontask/processor/backup_export_cdr/backup_export_cdr.go new file mode 100644 index 00000000..c04b6781 --- /dev/null +++ b/src/modules/crontask/processor/backup_export_cdr/backup_export_cdr.go @@ -0,0 +1,1427 @@ +package backup_export_cdr + +import ( + "encoding/json" + "fmt" + "path/filepath" + "runtime" + "strconv" + "strings" + "time" + + "be.ems/src/framework/cron" + "be.ems/src/framework/i18n" + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/date" + "be.ems/src/framework/utils/file" + "be.ems/src/framework/utils/parse" + neDataModel "be.ems/src/modules/network_data/model" + neDataService "be.ems/src/modules/network_data/service" + neModel "be.ems/src/modules/network_element/model" + neService "be.ems/src/modules/network_element/service" + systemService "be.ems/src/modules/system/service" +) + +var NewProcessor = &BackupExportCDRProcessor{ + count: 0, + backupService: neDataService.NewBackup, + sysDictService: systemService.NewSysDictData, + neInfoService: neService.NewNeInfo, + imsCDREventService: neDataService.NewCDREventIMS, + smscCDREventService: neDataService.NewCDREventSMSC, + smfCDREventService: neDataService.NewCDREventSMF, + sgwcCDREventService: neDataService.NewCDREventSGWC, +} + +// BackupExportCDR 队列任务处理 +type BackupExportCDRProcessor struct { + count int // 执行次数 + backupService *neDataService.Backup // 备份相关服务 + sysDictService *systemService.SysDictData // 字典类型数据服务 + neInfoService *neService.NeInfo // 网元信息服务 + imsCDREventService *neDataService.CDREventIMS // IMS-CDR会话事件服务 + smscCDREventService *neDataService.CDREventSMSC // SMSC-CDR会话事件服务 + smfCDREventService *neDataService.CDREventSMF // SMF-CDR会话事件服务 + sgwcCDREventService *neDataService.CDREventSGWC // SGWC-CDR会话事件服务 +} + +func (s *BackupExportCDRProcessor) Execute(data any) (any, error) { + s.count++ // 执行次数加一 + options := data.(cron.JobData) + sysJob := options.SysJob + logger.Infof("重复:%v 任务ID:%d 执行次数:%d", options.Repeat, sysJob.JobId, s.count) + // 返回结果,用于记录执行结果 + result := map[string]any{ + "count": s.count, + } + + var params struct { + DataType []string `json:"dataType"` // 类型支持 ims/smsc/smf/sgwc + FileType string `json:"fileType"` // 文件类型 csv/xlsx + Hour int `json:"hour"` // 数据时间从任务执行时间前的小时数 + } + if err := json.Unmarshal([]byte(sysJob.TargetParams), ¶ms); err != nil { + return nil, err + } + if !(params.FileType == "csv" || params.FileType == "xlsx") { + return nil, fmt.Errorf("file type error, only support csv,xlsx") + } + + for _, v := range params.DataType { + switch v { + case "ims": + neList := s.neInfoService.Find(neModel.NeInfo{NeType: "IMS"}, false, false) + for _, ne := range neList { + result[ne.NeName] = s.exportIMS(params.Hour, ne.RmUID, params.FileType) + } + case "smsc": + neList := s.neInfoService.Find(neModel.NeInfo{NeType: "SMSC"}, false, false) + for _, ne := range neList { + result[ne.NeName] = s.exportSMSC(params.Hour, ne.RmUID, params.FileType) + } + case "smf": + neList := s.neInfoService.Find(neModel.NeInfo{NeType: "SMF"}, false, false) + for _, ne := range neList { + result[ne.NeName] = s.exportSMF(params.Hour, ne.RmUID, params.FileType) + } + case "sgwc": + neList := s.neInfoService.Find(neModel.NeInfo{NeType: "SGWC"}, false, false) + for _, ne := range neList { + result[ne.NeName] = s.exportSGWC(params.Hour, ne.RmUID, params.FileType) + } + } + } + + // 返回结果,用于记录执行结果 + return result, nil +} + +// exportIMS 导出IMS-CDR会话事件数据 +func (s BackupExportCDRProcessor) exportIMS(hour int, rmUID, fileType string) string { + // 前 hour 小时 + now := time.Now() + end := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) + start := end.Add(-time.Duration(hour) * time.Hour) + + language := "en" + query := neDataModel.CDREventIMSQuery{ + SortField: "timestamp", + SortOrder: "asc", + RmUID: rmUID, + BeginTime: start.UnixMilli(), + EndTime: end.UnixMilli(), + PageNum: 1, + PageSize: 30000, + } + rows, total := s.imsCDREventService.FindByPage(query) + if total == 0 { + return "no data" + } + + // 导出文件名称 + fileName := fmt.Sprintf("ims_cdr_event_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType) + filePath := filepath.Join(s.backupService.BACKUP_DIR, "/cdr/ims_cdr_event", fileName) + if runtime.GOOS == "windows" { + filePath = fmt.Sprintf("C:%s", filePath) + } + + if fileType == "csv" { + // 转换数据 + data := [][]string{ + { + "ID", + "NE Name", + "Record Behavior", + "Type", + "Caller", + "Called", + "Duration", + "Result Code", + "Result Cause", + "Call Start Time", + "Hangup Time", + }, + } + // 读取字典数据 CDR SIP响应代码类别类型 + dictCDRSipCode := s.sysDictService.FindByType("cdr_sip_code") + // 读取字典数据 CDR SIP响应代码类别类型原因 + dictCDRSipCodeCause := s.sysDictService.FindByType("cdr_sip_code_cause") + // 读取字典数据 CDR 呼叫类型 + dictCDRCallType := s.sysDictService.FindByType("cdr_call_type") + for _, row := range rows { + // 解析 JSON 字符串为 map + var cdrJSON map[string]any + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("CDRExport Error parsing JSON: %s", err.Error()) + continue + } + // 记录类型 + recordType := "" + if v, ok := cdrJSON["recordType"]; ok && v != nil { + recordType = v.(string) + } + // 呼叫类型 + callType := "sms" + callTypeLable := "SMS" + if v, ok := cdrJSON["callType"]; ok && v != nil { + callType = v.(string) + for _, v := range dictCDRCallType { + if callType == v.DataValue { + callTypeLable = i18n.TKey(language, v.DataLabel) + break + } + } + } + // 被叫 + called := "" + if v, ok := cdrJSON["calledParty"]; ok && v != nil { + called = v.(string) + } + // 主叫 + caller := "" + if v, ok := cdrJSON["callerParty"]; ok && v != nil { + caller = v.(string) + } + // 时长 + duration := "-" + if v, ok := cdrJSON["callDuration"]; ok && v != nil && callType != "sms" { + duration = fmt.Sprintf("%ds", parse.Number(v)) + } + // 呼叫结果 非短信都有code作为结果 sms短信都ok + callResult := "Other" + callCause := "Call failure for other reason" + if callType == "sms" { + callResult = "Success" + callCause = "Normal Send" + } else { + if v, ok := cdrJSON["cause"]; ok && v != nil { + cause := fmt.Sprint(v) + for _, v := range dictCDRSipCode { + if cause == v.DataValue { + callResult = i18n.TKey(language, v.DataLabel) + break + } + } + for _, v := range dictCDRSipCodeCause { + if cause == v.DataValue { + callCause = i18n.TKey(language, v.DataLabel) + break + } + } + } + } + // 呼叫时间 + seizureTimeStr := "" + if v, ok := cdrJSON["seizureTime"]; ok && v != nil { + if seizureTime := parse.Number(v); seizureTime > 0 { + seizureTimeStr = date.ParseDateToStr(seizureTime, date.YYYY_MM_DDTHH_MM_SSZ) + } else { + seizureTimeStr = v.(string) + } + } + // 挂断时间 + releaseTimeStr := "" + if v, ok := cdrJSON["releaseTime"]; ok && v != nil { + if releaseTime := parse.Number(v); releaseTime > 0 { + releaseTimeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ) + } else { + releaseTimeStr = v.(string) + } + } + + data = append(data, []string{ + fmt.Sprint(row.ID), + row.NeName, + recordType, + callTypeLable, + caller, + called, + duration, + callResult, + callCause, + seizureTimeStr, + releaseTimeStr, + }) + } + // 输出到文件 + if err := file.WriterFileCSV(data, filePath); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + if fileType == "xlsx" { + // 第一行表头标题 + headerCells := map[string]string{ + "A1": "ID", + "B1": "NE Name", + "C1": "Record Behavior", + "D1": "Type", + "E1": "Caller", + "F1": "Called", + "G1": "Duration", + "H1": "Result Code", + "I1": "Result Cause", + "J1": "Call Start Time", + "K1": "Hangup Time", + } + // 读取字典数据 CDR SIP响应代码类别类型 + dictCDRSipCode := s.sysDictService.FindByType("cdr_sip_code") + // 读取字典数据 CDR SIP响应代码类别类型原因 + dictCDRSipCodeCause := s.sysDictService.FindByType("cdr_sip_code_cause") + // 读取字典数据 CDR 呼叫类型 + dictCDRCallType := s.sysDictService.FindByType("cdr_call_type") + // 从第二行开始的数据 + dataCells := make([]map[string]any, 0) + for i, row := range rows { + idx := strconv.Itoa(i + 2) + // 解析 JSON 字符串为 map + var cdrJSON map[string]any + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("CDRExport Error parsing JSON: %s", err.Error()) + continue + } + // 记录类型 + recordType := "" + if v, ok := cdrJSON["recordType"]; ok && v != nil { + recordType = v.(string) + } + // 呼叫类型 + callType := "sms" + callTypeLable := "SMS" + if v, ok := cdrJSON["callType"]; ok && v != nil { + callType = v.(string) + for _, v := range dictCDRCallType { + if callType == v.DataValue { + callTypeLable = i18n.TKey(language, v.DataLabel) + break + } + } + } + // 被叫 + called := "" + if v, ok := cdrJSON["calledParty"]; ok && v != nil { + called = v.(string) + } + // 主叫 + caller := "" + if v, ok := cdrJSON["callerParty"]; ok && v != nil { + caller = v.(string) + } + // 时长 + duration := "-" + if v, ok := cdrJSON["callDuration"]; ok && v != nil && callType != "sms" { + duration = fmt.Sprintf("%ds", parse.Number(v)) + } + // 呼叫结果 非短信都有code作为结果 sms短信都ok + callResult := "Other" + callCause := "Call failure for other reason" + if callType == "sms" { + callResult = "Success" + callCause = "Normal Send" + } else { + if v, ok := cdrJSON["cause"]; ok && v != nil { + cause := fmt.Sprint(v) + for _, v := range dictCDRSipCode { + if cause == v.DataValue { + callResult = i18n.TKey(language, v.DataLabel) + break + } + } + for _, v := range dictCDRSipCodeCause { + if cause == v.DataValue { + callCause = i18n.TKey(language, v.DataLabel) + break + } + } + } + } + // 呼叫时间 + seizureTimeStr := "" + if v, ok := cdrJSON["seizureTime"]; ok && v != nil { + if seizureTime := parse.Number(v); seizureTime > 0 { + seizureTimeStr = date.ParseDateToStr(seizureTime, date.YYYY_MM_DDTHH_MM_SSZ) + } else { + seizureTimeStr = v.(string) + } + } + // 挂断时间 + releaseTimeStr := "" + if v, ok := cdrJSON["releaseTime"]; ok && v != nil { + if releaseTime := parse.Number(v); releaseTime > 0 { + releaseTimeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ) + } else { + releaseTimeStr = v.(string) + } + } + + dataCells = append(dataCells, map[string]any{ + "A" + idx: row.ID, + "B" + idx: row.NeName, + "C" + idx: recordType, + "D" + idx: callTypeLable, + "E" + idx: caller, + "F" + idx: called, + "G" + idx: duration, + "H" + idx: callResult, + "I" + idx: callCause, + "J" + idx: seizureTimeStr, + "K" + idx: releaseTimeStr, + }) + } + // 导出数据表格 + if err := file.WriterFileExecl(headerCells, dataCells, filePath, ""); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + // 上传到FTP服务器 + if err := s.backupService.FTPPushFile(filePath, "cdr"); err != nil { + return "ok, ftp err:" + err.Error() + } + return "ok" +} + +// exportSMSC 导出SMSC-CDR会话事件数据 +func (s BackupExportCDRProcessor) exportSMSC(hour int, rmUID, fileType string) string { + // 前 hour 小时 + now := time.Now() + end := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) + start := end.Add(-time.Duration(hour) * time.Hour) + + language := "en" + query := neDataModel.CDREventSMSCQuery{ + SortField: "timestamp", + SortOrder: "asc", + RmUID: rmUID, + BeginTime: start.UnixMilli(), + EndTime: end.UnixMilli(), + PageNum: 1, + PageSize: 30000, + } + rows, total := s.smscCDREventService.FindByPage(query) + if total == 0 { + return "no data" + } + + // 导出文件名称 + fileName := fmt.Sprintf("smsc_cdr_event_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType) + filePath := filepath.Join(s.backupService.BACKUP_DIR, "/cdr/smsc_cdr_event", fileName) + if runtime.GOOS == "windows" { + filePath = fmt.Sprintf("C:%s", filePath) + } + + if fileType == "csv" { + // 转换数据 + data := [][]string{ + { + "ID", + "NE Name", + "Record Behavior", + "Service Type", + "Caller", + "Called", + "Result", + "Time", + }, + } + // 读取字典数据 CDR 原因码 + dictCDRCauseCode := s.sysDictService.FindByType("cdr_cause_code") + for _, row := range rows { + // 解析 JSON 字符串为 map + var cdrJSON map[string]interface{} + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("CDRExport Error parsing JSON: %s", err.Error()) + continue + } + // 记录类型 + recordType := "" + if v, ok := cdrJSON["recordType"]; ok && v != nil { + recordType = v.(string) + } + // 服务类型 + serviceType := "" + if v, ok := cdrJSON["serviceType"]; ok && v != nil { + serviceType = v.(string) + } + // 被叫 + called := "" + if v, ok := cdrJSON["calledParty"]; ok && v != nil { + called = v.(string) + } + // 主叫 + caller := "" + if v, ok := cdrJSON["callerParty"]; ok && v != nil { + caller = v.(string) + } + // 呼叫结果 0失败,1成功 + callResult := "Fail" + if v, ok := cdrJSON["result"]; ok && v != nil { + resultVal := parse.Number(v) + if resultVal == 1 { + callResult = "Success" + } + } + // 结果原因 + if v, ok := cdrJSON["cause"]; ok && v != nil && callResult == "Fail" { + cause := fmt.Sprint(v) + for _, v := range dictCDRCauseCode { + if cause == v.DataValue { + callResult = fmt.Sprintf("%s, %s", callResult, i18n.TKey(language, v.DataLabel)) + break + } + } + } + // 取时间 + timeStr := "" + if v, ok := cdrJSON["updateTime"]; ok && v != nil { + if releaseTime := parse.Number(v); releaseTime > 0 { + timeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ) + } else { + timeStr = v.(string) + } + } + + data = append(data, []string{ + fmt.Sprint(row.ID), + row.NeName, + recordType, + serviceType, + caller, + called, + callResult, + timeStr, + }) + } + // 输出到文件 + if err := file.WriterFileCSV(data, filePath); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + if fileType == "xlsx" { + // 第一行表头标题 + headerCells := map[string]string{ + "A1": "ID", + "B1": "NE Name", + "C1": "Record Behavior", + "D1": "Service Type", + "E1": "Caller", + "F1": "Called", + "G1": "Result", + "H1": "Time", + } + // 读取字典数据 CDR 原因码 + dictCDRCauseCode := s.sysDictService.FindByType("cdr_cause_code") + // 从第二行开始的数据 + dataCells := make([]map[string]any, 0) + for i, row := range rows { + idx := strconv.Itoa(i + 2) + // 解析 JSON 字符串为 map + var cdrJSON map[string]interface{} + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("CDRExport Error parsing JSON: %s", err.Error()) + continue + } + // 记录类型 + recordType := "" + if v, ok := cdrJSON["recordType"]; ok && v != nil { + recordType = v.(string) + } + // 服务类型 + serviceType := "" + if v, ok := cdrJSON["serviceType"]; ok && v != nil { + serviceType = v.(string) + } + // 被叫 + called := "" + if v, ok := cdrJSON["calledParty"]; ok && v != nil { + called = v.(string) + } + // 主叫 + caller := "" + if v, ok := cdrJSON["callerParty"]; ok && v != nil { + caller = v.(string) + } + // 呼叫结果 0失败,1成功 + callResult := "Fail" + if v, ok := cdrJSON["result"]; ok && v != nil { + resultVal := parse.Number(v) + if resultVal == 1 { + callResult = "Success" + } + } + // 结果原因 + if v, ok := cdrJSON["cause"]; ok && v != nil && callResult == "Fail" { + cause := fmt.Sprint(v) + for _, v := range dictCDRCauseCode { + if cause == v.DataValue { + callResult = fmt.Sprintf("%s, %s", callResult, i18n.TKey(language, v.DataLabel)) + break + } + } + } + // 取时间 + timeStr := "" + if v, ok := cdrJSON["updateTime"]; ok && v != nil { + if releaseTime := parse.Number(v); releaseTime > 0 { + timeStr = date.ParseDateToStr(releaseTime, date.YYYY_MM_DDTHH_MM_SSZ) + } else { + timeStr = v.(string) + } + } + + dataCells = append(dataCells, map[string]any{ + "A" + idx: row.ID, + "B" + idx: row.NeName, + "C" + idx: recordType, + "D" + idx: serviceType, + "E" + idx: caller, + "F" + idx: called, + "G" + idx: callResult, + "H" + idx: timeStr, + }) + } + // 导出数据表格 + if err := file.WriterFileExecl(headerCells, dataCells, filePath, ""); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + // 上传到FTP服务器 + if err := s.backupService.FTPPushFile(filePath, "cdr"); err != nil { + return "ok, ftp err:" + err.Error() + } + return "ok" +} + +// exportSMF 导出SMF-CDR会话事件数据 +func (s BackupExportCDRProcessor) exportSMF(hour int, rmUID, fileType string) string { + // 前 hour 小时 + now := time.Now() + end := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) + start := end.Add(-time.Duration(hour) * time.Hour) + + query := neDataModel.CDREventSMFQuery{ + SortField: "timestamp", + SortOrder: "asc", + RmUID: rmUID, + BeginTime: start.UnixMilli(), + EndTime: end.UnixMilli(), + PageNum: 1, + PageSize: 30000, + } + rows, total := s.smfCDREventService.FindByPage(query) + if total == 0 { + return "no data" + } + + // 导出文件名称 + fileName := fmt.Sprintf("smf_cdr_event_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType) + filePath := filepath.Join(s.backupService.BACKUP_DIR, "/cdr/smf_cdr_event", fileName) + if runtime.GOOS == "windows" { + filePath = fmt.Sprintf("C:%s", filePath) + } + + if fileType == "csv" { + // 转换数据 + data := [][]string{ + { + "ID", + "Charging ID", + "NE Name", + "Resource Unique ID", + "Subscriber ID Data", + "Subscriber ID Type", + "Data Volume Uplink", + "Data Volume Downlink", + "Data Total Volume", + "Duration", + "Invocation Time", + "User Identifier", + "SSC Mode", + "DNN ID", + "PDU Type", + "RAT Type", + "PDU IPv4 Address", + "Network Function IPv4", + "PDU IPv6 Address Swith Prefix", + "Record Network Function ID", + "Record Type", + "Record Opening Time", + }, + } + for _, row := range rows { + // 解析 JSON 字符串为 map + var cdrJSON map[string]interface{} + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("CDRExport Error parsing JSON: %s", err.Error()) + continue + } + // 计费ID + chargingID := "" + if v, ok := cdrJSON["chargingID"]; ok && v != nil { + chargingID = fmt.Sprint(parse.Number(v)) + } + // 订阅 ID 类型 + subscriptionIDType := "-" + // 订阅 ID 数据 + subscriptionIDData := "-" + if v, ok := cdrJSON["subscriberIdentifier"]; ok && v != nil { + if sub, subOk := v.(map[string]any); subOk && sub != nil { + subscriptionIDType = sub["subscriptionIDType"].(string) + subscriptionIDData = sub["subscriptionIDData"].(string) + } + } + + // 网络功能 IPv4 地址 + networkFunctionIPv4Address := "" + if v, ok := cdrJSON["nFunctionConsumerInformation"]; ok && v != nil { + if conInfo, conInfoOk := v.(map[string]any); conInfoOk && conInfo != nil { + networkFunctionIPv4Address = conInfo["networkFunctionIPv4Address"].(string) + } + } + + // 数据量上行链路 + var dataVolumeUplink int64 = 0 + // 数据量下行链路 + var dataVolumeDownlink int64 = 0 + // 数据总量 + var dataTotalVolume int64 = 0 + if v, ok := cdrJSON["listOfMultipleUnitUsage"]; ok && v != nil { + usageList := v.([]any) + if len(usageList) > 0 { + for _, used := range usageList { + usedUnit := used.(map[string]any) + usedUnitList := usedUnit["usedUnitContainer"].([]any) + if len(usedUnitList) > 0 { + for _, data := range usedUnitList { + udata := data.(map[string]any) + if dup, dupOk := udata["dataVolumeUplink"]; dupOk { + dataVolumeUplink += parse.Number(dup) + } + if ddown, ddownOk := udata["dataVolumeDownlink"]; ddownOk { + dataVolumeDownlink += parse.Number(ddown) + } + if dt, dtOk := udata["dataTotalVolume"]; dtOk { + dataTotalVolume += parse.Number(dt) + } + } + } + } + } + } + // 时长 + duration := "-" + if v, ok := cdrJSON["duration"]; ok && v != nil { + duration = fmt.Sprint(parse.Number(v)) + } + // 调用时间 + invocationTimestamp := "" + if v, ok := cdrJSON["invocationTimestamp"]; ok && v != nil { + invocationTimestamp = v.(string) + } + // 记录打开时间 + User_Identifier := "" + SSC_Mode := "" + RAT_Type := "" + DNN_ID := "" + PDU_Type := "" + PDU_IPv4 := "" + PDU_IPv6 := "" + if v, ok := cdrJSON["pDUSessionChargingInformation"]; ok && v != nil { + pduInfo := v.(map[string]any) + + if v, ok := pduInfo["userIdentifier"]; ok && v != nil { + User_Identifier = v.(string) + } + if v, ok := pduInfo["sSCMode"]; ok && v != nil { + SSC_Mode = v.(string) + } + if v, ok := pduInfo["rATType"]; ok && v != nil { + RAT_Type = v.(string) + } + if v, ok := pduInfo["dNNID"]; ok && v != nil { + DNN_ID = v.(string) + } + if v, ok := pduInfo["pDUType"]; ok && v != nil { + PDU_Type = v.(string) + } + if v, ok := pduInfo["pDUAddress"]; ok && v != nil { + pDUAddress := v.(map[string]any) + if addr, ok := pDUAddress["pDUIPv4Address"]; ok && addr != nil { + PDU_IPv4 = addr.(string) + } + if addr, ok := pDUAddress["pDUIPv6AddresswithPrefix"]; ok && addr != nil { + PDU_IPv6 = addr.(string) + } + } + } + + // 记录网络参数ID + recordNFID := "" + if v, ok := cdrJSON["recordingNetworkFunctionID"]; ok && v != nil { + recordNFID = v.(string) + } + + //记录开始时间 + recordOpeningTime := "" + if v, ok := cdrJSON["recordOpeningTime"]; ok && v != nil { + recordOpeningTime = v.(string) + } + + //记录类型 + recordType := "" + if v, ok := cdrJSON["recordType"]; ok && v != nil { + recordType = v.(string) + } + + data = append(data, []string{ + fmt.Sprint(row.ID), + chargingID, + row.NeName, + row.RmUid, + subscriptionIDData, + subscriptionIDType, + fmt.Sprint(dataVolumeUplink), + fmt.Sprint(dataVolumeDownlink), + fmt.Sprint(dataTotalVolume), + duration, + invocationTimestamp, + User_Identifier, + SSC_Mode, + DNN_ID, + PDU_Type, + RAT_Type, + PDU_IPv4, + networkFunctionIPv4Address, + PDU_IPv6, + recordNFID, + recordType, + recordOpeningTime, + }) + } + // 输出到文件 + if err := file.WriterFileCSV(data, filePath); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + if fileType == "xlsx" { + // 第一行表头标题 + headerCells := map[string]string{ + "A1": "ID", + "B1": "Charging ID", + "C1": "NE Name", + "D1": "Resource Unique ID", + "E1": "Subscriber ID Data", + "F1": "Subscriber ID Type", + "G1": "Data Volume Uplink", + "H1": "Data Volume Downlink", + "I1": "Data Total Volume", + "J1": "Duration", + "K1": "Invocation Time", + "L1": "User Identifier", + "M1": "SSC Mode", + "N1": "DNN ID", + "O1": "PDU Type", + "P1": "RAT Type", + "Q1": "PDU IPv4 Address", + "R1": "Network Function IPv4", + "S1": "PDU IPv6 Address Swith Prefix", + "T1": "Record Network Function ID", + "U1": "Record Type", + "V1": "Record Opening Time", + } + // 从第二行开始的数据 + dataCells := make([]map[string]any, 0) + for i, row := range rows { + idx := strconv.Itoa(i + 2) + // 解析 JSON 字符串为 map + var cdrJSON map[string]interface{} + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("CDRExport Error parsing JSON: %s", err.Error()) + continue + } + // 计费ID + chargingID := "" + if v, ok := cdrJSON["chargingID"]; ok && v != nil { + chargingID = fmt.Sprint(parse.Number(v)) + } + // 订阅 ID 类型 + subscriptionIDType := "-" + // 订阅 ID 数据 + subscriptionIDData := "-" + if v, ok := cdrJSON["subscriberIdentifier"]; ok && v != nil { + if sub, subOk := v.(map[string]any); subOk && sub != nil { + subscriptionIDType = sub["subscriptionIDType"].(string) + subscriptionIDData = sub["subscriptionIDData"].(string) + } + } + + // 网络功能 IPv4 地址 + networkFunctionIPv4Address := "" + if v, ok := cdrJSON["nFunctionConsumerInformation"]; ok && v != nil { + if conInfo, conInfoOk := v.(map[string]any); conInfoOk && conInfo != nil { + networkFunctionIPv4Address = conInfo["networkFunctionIPv4Address"].(string) + } + } + + // 数据量上行链路 + var dataVolumeUplink int64 = 0 + // 数据量下行链路 + var dataVolumeDownlink int64 = 0 + // 数据总量 + var dataTotalVolume int64 = 0 + if v, ok := cdrJSON["listOfMultipleUnitUsage"]; ok && v != nil { + usageList := v.([]any) + if len(usageList) > 0 { + for _, used := range usageList { + usedUnit := used.(map[string]any) + usedUnitList := usedUnit["usedUnitContainer"].([]any) + if len(usedUnitList) > 0 { + for _, data := range usedUnitList { + udata := data.(map[string]any) + if dup, dupOk := udata["dataVolumeUplink"]; dupOk { + dataVolumeUplink += parse.Number(dup) + } + if ddown, ddownOk := udata["dataVolumeDownlink"]; ddownOk { + dataVolumeDownlink += parse.Number(ddown) + } + if dt, dtOk := udata["dataTotalVolume"]; dtOk { + dataTotalVolume += parse.Number(dt) + } + } + } + } + } + } + // 时长 + duration := "-" + if v, ok := cdrJSON["duration"]; ok && v != nil { + duration = fmt.Sprint(parse.Number(v)) + } + // 调用时间 + invocationTimestamp := "" + if v, ok := cdrJSON["invocationTimestamp"]; ok && v != nil { + invocationTimestamp = v.(string) + } + // 记录打开时间 + User_Identifier := "" + SSC_Mode := "" + RAT_Type := "" + DNN_ID := "" + PDU_Type := "" + PDU_IPv4 := "" + PDU_IPv6 := "" + if v, ok := cdrJSON["pDUSessionChargingInformation"]; ok && v != nil { + pduInfo := v.(map[string]any) + + if v, ok := pduInfo["userIdentifier"]; ok && v != nil { + User_Identifier = v.(string) + } + if v, ok := pduInfo["sSCMode"]; ok && v != nil { + SSC_Mode = v.(string) + } + if v, ok := pduInfo["rATType"]; ok && v != nil { + RAT_Type = v.(string) + } + if v, ok := pduInfo["dNNID"]; ok && v != nil { + DNN_ID = v.(string) + } + if v, ok := pduInfo["pDUType"]; ok && v != nil { + PDU_Type = v.(string) + } + if v, ok := pduInfo["pDUAddress"]; ok && v != nil { + pDUAddress := v.(map[string]any) + if addr, ok := pDUAddress["pDUIPv4Address"]; ok && addr != nil { + PDU_IPv4 = addr.(string) + } + if addr, ok := pDUAddress["pDUIPv6AddresswithPrefix"]; ok && addr != nil { + PDU_IPv6 = addr.(string) + } + } + } + + // 记录网络参数ID + recordNFID := "" + if v, ok := cdrJSON["recordingNetworkFunctionID"]; ok && v != nil { + recordNFID = v.(string) + } + + //记录开始时间 + recordOpeningTime := "" + if v, ok := cdrJSON["recordOpeningTime"]; ok && v != nil { + recordOpeningTime = v.(string) + } + + //记录类型 + recordType := "" + if v, ok := cdrJSON["recordType"]; ok && v != nil { + recordType = v.(string) + } + + dataCells = append(dataCells, map[string]any{ + "A" + idx: row.ID, + "B" + idx: chargingID, + "C" + idx: row.NeName, + "D" + idx: row.RmUid, + "E" + idx: subscriptionIDData, + "F" + idx: subscriptionIDType, + "G" + idx: dataVolumeUplink, + "H" + idx: dataVolumeDownlink, + "I" + idx: dataTotalVolume, + "J" + idx: duration, + "K" + idx: invocationTimestamp, + "L" + idx: User_Identifier, + "M" + idx: SSC_Mode, + "N" + idx: DNN_ID, + "O" + idx: PDU_Type, + "P" + idx: RAT_Type, + "Q" + idx: PDU_IPv4, + "R" + idx: networkFunctionIPv4Address, + "S" + idx: PDU_IPv6, + "T" + idx: recordNFID, + "U" + idx: recordType, + "V" + idx: recordOpeningTime, + }) + } + // 导出数据表格 + if err := file.WriterFileExecl(headerCells, dataCells, filePath, ""); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + // 上传到FTP服务器 + if err := s.backupService.FTPPushFile(filePath, "cdr"); err != nil { + return "ok, ftp err:" + err.Error() + } + return "ok" +} + +// exportSGWC 导出SGWC-CDR会话事件数据 +func (s BackupExportCDRProcessor) exportSGWC(hour int, rmUID, fileType string) string { + // 前 hour 小时 + now := time.Now() + end := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) + start := end.Add(-time.Duration(hour) * time.Hour) + + query := neDataModel.CDREventSGWCQuery{ + SortField: "timestamp", + SortOrder: "asc", + RmUID: rmUID, + BeginTime: start.UnixMilli(), + EndTime: end.UnixMilli(), + PageNum: 1, + PageSize: 30000, + } + rows, total := s.sgwcCDREventService.FindByPage(query) + if total == 0 { + return "no data" + } + + // 导出文件名称 + fileName := fmt.Sprintf("sgwc_cdr_event_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType) + filePath := filepath.Join(s.backupService.BACKUP_DIR, "/cdr/sgwc_cdr_event", fileName) + if runtime.GOOS == "windows" { + filePath = fmt.Sprintf("C:%s", filePath) + } + + if fileType == "csv" { + // 转换数据 + data := [][]string{ + { + "ID", + "NE Name", + "Resource Unique ID", + "Charging ID", + "IMSI", + "MSISDN", + "GPRS Uplink", + "GPRS Downlink", + "Duration", + "Invocation Time", + "PGW Address", + "SGW Address", + "RAT Type", + "PDPPDN Type", + "PDPPDN Address", + "Node Address", + "Node Type", + "Record Access Point Name NI", + "Record Cause For Rec Closing", + "Record Sequence Number", + "Local Record Sequence Number", + "Record Type", + "Record Opening Time", + }, + } + for _, row := range rows { + // 解析 JSON 字符串为 map + var cdrJSON map[string]interface{} + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("CDRExport Error parsing JSON: %s", err.Error()) + continue + } + // 计费ID + chargingID := "" + if v, ok := cdrJSON["chargingID"]; ok && v != nil { + chargingID = fmt.Sprint(parse.Number(v)) + } + // IMSI + servedIMSI := "" + if v, ok := cdrJSON["servedIMSI"]; ok && v != nil { + servedIMSI = fmt.Sprint(v) + } + // MSISDN + servedMSISDN := "" + if v, ok := cdrJSON["servedMSISDN"]; ok && v != nil { + servedMSISDN = fmt.Sprint(v) + } + // pGWAddressUsed + pGWAddressUsed := "" + if v, ok := cdrJSON["pGWAddressUsed"]; ok && v != nil { + pGWAddressUsed = fmt.Sprint(v) + data[0][10] = "PGW Address" + } + if v, ok := cdrJSON["GGSNAddress"]; ok && v != nil { + pGWAddressUsed = fmt.Sprint(v) + data[0][10] = "GGSN Address" + } + // sGWAddress + sGWAddress := "" + if v, ok := cdrJSON["sGWAddress"]; ok && v != nil { + sGWAddress = fmt.Sprint(v) + data[0][11] = "SGW Address" + } + if v, ok := cdrJSON["SGSNAddress"]; ok && v != nil { + sGWAddress = fmt.Sprint(v) + data[0][11] = "SGSN Address" + } + // recordType + recordType := "" + if v, ok := cdrJSON["recordType"]; ok && v != nil { + recordType = fmt.Sprint(v) + } + // rATType + rATType := "" + if v, ok := cdrJSON["rATType"]; ok && v != nil { + rATType = fmt.Sprint(v) + } + // pdpPDNType + pdpPDNType := "" + if v, ok := cdrJSON["pdpPDNType"]; ok && v != nil { + pdpPDNType = fmt.Sprint(v) + } + // servedPDPPDNAddress + servedPDPPDNAddress := "" + if v, ok := cdrJSON["servedPDPPDNAddress"]; ok && v != nil { + servedPDPPDNAddress = fmt.Sprint(v) + } + // servedPDPPDNAddress + servingNodeAddress := []string{} + if v, ok := cdrJSON["servingNodeAddress"]; ok && v != nil { + for _, v := range v.([]any) { + servingNodeAddress = append(servingNodeAddress, fmt.Sprint(v)) + } + } + // servingNodeType + servingNodeType := []string{} + if v, ok := cdrJSON["servingNodeType"]; ok && v != nil { + for _, v := range v.([]any) { + if v, ok := v.(map[string]any)["servingNodeType"]; ok && v != nil { + servingNodeType = append(servingNodeType, fmt.Sprint(v)) + } + } + } + // accessPointNameNI + accessPointNameNI := "" + if v, ok := cdrJSON["accessPointNameNI"]; ok && v != nil { + accessPointNameNI = fmt.Sprint(v) + } + // causeForRecClosing + causeForRecClosing := "" + if v, ok := cdrJSON["causeForRecClosing"]; ok && v != nil { + causeForRecClosing = fmt.Sprint(v) + } + // recordSequenceNumber + recordSequenceNumber := "" + if v, ok := cdrJSON["recordSequenceNumber"]; ok && v != nil { + recordSequenceNumber = fmt.Sprint(v) + } + // localRecordSequenceNumber + localRecordSequenceNumber := "" + if v, ok := cdrJSON["localRecordSequenceNumber"]; ok && v != nil { + localRecordSequenceNumber = fmt.Sprint(v) + } + // 数据量上行链路 + var dataVolumeGPRSUplink int64 = 0 + // 数据量下行链路 + var dataVolumeGPRSDownlink int64 = 0 + if v, ok := cdrJSON["listOfTrafficVolumes"]; ok && v != nil { + usageList := v.([]any) + if len(usageList) > 0 { + for _, used := range usageList { + usedUnit := used.(map[string]any) + if dup, dupOk := usedUnit["dataVolumeGPRSUplink"]; dupOk { + dataVolumeGPRSUplink = parse.Number(dup) + } + if ddown, ddownOk := usedUnit["dataVolumeGPRSDownlink"]; ddownOk { + dataVolumeGPRSDownlink = parse.Number(ddown) + } + } + } + } + // 时长 + duration := "-" + if v, ok := cdrJSON["duration"]; ok && v != nil { + duration = fmt.Sprint(parse.Number(v)) + } + // 调用时间 + invocationTimestamp := "" + if v, ok := cdrJSON["recordOpeningTime"]; ok && v != nil { + invocationTimestamp = v.(string) + } + + data = append(data, []string{ + fmt.Sprint(row.ID), + row.NeName, + row.RmUid, + chargingID, + servedIMSI, + servedMSISDN, + fmt.Sprint(dataVolumeGPRSUplink), + fmt.Sprint(dataVolumeGPRSDownlink), + duration, + invocationTimestamp, + pGWAddressUsed, + sGWAddress, + rATType, + pdpPDNType, + servedPDPPDNAddress, + strings.Join(servingNodeAddress, ","), + strings.Join(servingNodeType, ","), + accessPointNameNI, + causeForRecClosing, + recordSequenceNumber, + localRecordSequenceNumber, + recordType, + invocationTimestamp, + }) + } + // 输出到文件 + if err := file.WriterFileCSV(data, filePath); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + if fileType == "xlsx" { + // 第一行表头标题 + headerCells := map[string]string{ + "A1": "ID", + "B1": "NE Name", + "C1": "Resource Unique ID", + "D1": "Charging ID", + "E1": "IMSI", + "F1": "MSISDN", + "G1": "GPRS Uplink", + "H1": "GPRS Downlink", + "I1": "Duration", + "J1": "Invocation Time", + "K1": "PGW Address", + "L1": "SGW Address", + "M1": "RAT Type", + "N1": "PDPPDN Type", + "O1": "PDPPDN Address", + "P1": "Node Address", + "Q1": "Node Type", + "R1": "Record Access Point Name NI", + "S1": "Record Cause For Rec Closing", + "T1": "Record Sequence Number", + "U1": "Local Record Sequence Number", + "V1": "Record Type", + "W1": "Record Opening Time", + } + // 从第二行开始的数据 + dataCells := make([]map[string]any, 0) + for i, row := range rows { + idx := strconv.Itoa(i + 2) + // 解析 JSON 字符串为 map + var cdrJSON map[string]interface{} + err := json.Unmarshal([]byte(row.CdrJson), &cdrJSON) + if err != nil { + logger.Warnf("CDRExport Error parsing JSON: %s", err.Error()) + continue + } + // 计费ID + chargingID := "" + if v, ok := cdrJSON["chargingID"]; ok && v != nil { + chargingID = fmt.Sprint(parse.Number(v)) + } + // IMSI + servedIMSI := "" + if v, ok := cdrJSON["servedIMSI"]; ok && v != nil { + servedIMSI = fmt.Sprint(v) + } + // MSISDN + servedMSISDN := "" + if v, ok := cdrJSON["servedMSISDN"]; ok && v != nil { + servedMSISDN = fmt.Sprint(v) + } + // pGWAddressUsed + pGWAddressUsed := "" + if v, ok := cdrJSON["pGWAddressUsed"]; ok && v != nil { + pGWAddressUsed = fmt.Sprint(v) + headerCells["K1"] = "PGW Address" + } + if v, ok := cdrJSON["GGSNAddress"]; ok && v != nil { + pGWAddressUsed = fmt.Sprint(v) + headerCells["K1"] = "GGSN Address" + } + // sGWAddress + sGWAddress := "" + if v, ok := cdrJSON["sGWAddress"]; ok && v != nil { + sGWAddress = fmt.Sprint(v) + headerCells["L1"] = "SGW Address" + } + if v, ok := cdrJSON["SGSNAddress"]; ok && v != nil { + sGWAddress = fmt.Sprint(v) + headerCells["L1"] = "SGSN Address" + } + // recordType + recordType := "" + if v, ok := cdrJSON["recordType"]; ok && v != nil { + recordType = fmt.Sprint(v) + } + // rATType + rATType := "" + if v, ok := cdrJSON["rATType"]; ok && v != nil { + rATType = fmt.Sprint(v) + } + // pdpPDNType + pdpPDNType := "" + if v, ok := cdrJSON["pdpPDNType"]; ok && v != nil { + pdpPDNType = fmt.Sprint(v) + } + // servedPDPPDNAddress + servedPDPPDNAddress := "" + if v, ok := cdrJSON["servedPDPPDNAddress"]; ok && v != nil { + servedPDPPDNAddress = fmt.Sprint(v) + } + // servedPDPPDNAddress + servingNodeAddress := []string{} + if v, ok := cdrJSON["servingNodeAddress"]; ok && v != nil { + for _, v := range v.([]any) { + servingNodeAddress = append(servingNodeAddress, fmt.Sprint(v)) + } + } + // servingNodeType + servingNodeType := []string{} + if v, ok := cdrJSON["servingNodeType"]; ok && v != nil { + for _, v := range v.([]any) { + if v, ok := v.(map[string]any)["servingNodeType"]; ok && v != nil { + servingNodeType = append(servingNodeType, fmt.Sprint(v)) + } + } + } + // accessPointNameNI + accessPointNameNI := "" + if v, ok := cdrJSON["accessPointNameNI"]; ok && v != nil { + accessPointNameNI = fmt.Sprint(v) + } + // causeForRecClosing + causeForRecClosing := "" + if v, ok := cdrJSON["causeForRecClosing"]; ok && v != nil { + causeForRecClosing = fmt.Sprint(v) + } + // recordSequenceNumber + recordSequenceNumber := "" + if v, ok := cdrJSON["recordSequenceNumber"]; ok && v != nil { + recordSequenceNumber = fmt.Sprint(v) + } + // localRecordSequenceNumber + localRecordSequenceNumber := "" + if v, ok := cdrJSON["localRecordSequenceNumber"]; ok && v != nil { + localRecordSequenceNumber = fmt.Sprint(v) + } + // 数据量上行链路 + var dataVolumeGPRSUplink int64 = 0 + // 数据量下行链路 + var dataVolumeGPRSDownlink int64 = 0 + if v, ok := cdrJSON["listOfTrafficVolumes"]; ok && v != nil { + usageList := v.([]any) + if len(usageList) > 0 { + for _, used := range usageList { + usedUnit := used.(map[string]any) + if dup, dupOk := usedUnit["dataVolumeGPRSUplink"]; dupOk { + dataVolumeGPRSUplink = parse.Number(dup) + } + if ddown, ddownOk := usedUnit["dataVolumeGPRSDownlink"]; ddownOk { + dataVolumeGPRSDownlink = parse.Number(ddown) + } + } + } + } + // 时长 + duration := "-" + if v, ok := cdrJSON["duration"]; ok && v != nil { + duration = fmt.Sprint(parse.Number(v)) + } + // 调用时间 + invocationTimestamp := "" + if v, ok := cdrJSON["recordOpeningTime"]; ok && v != nil { + invocationTimestamp = v.(string) + } + + dataCells = append(dataCells, map[string]any{ + "A" + idx: row.ID, + "B" + idx: row.NeName, + "C" + idx: row.RmUid, + "D" + idx: chargingID, + "E" + idx: servedIMSI, + "F" + idx: servedMSISDN, + "G" + idx: dataVolumeGPRSUplink, + "H" + idx: dataVolumeGPRSDownlink, + "I" + idx: duration, + "J" + idx: invocationTimestamp, + "K" + idx: pGWAddressUsed, + "L" + idx: sGWAddress, + "M" + idx: rATType, + "N" + idx: pdpPDNType, + "O" + idx: servedPDPPDNAddress, + "P" + idx: strings.Join(servingNodeAddress, ","), + "Q" + idx: strings.Join(servingNodeType, ","), + "R" + idx: accessPointNameNI, + "S" + idx: causeForRecClosing, + "T" + idx: recordSequenceNumber, + "U" + idx: localRecordSequenceNumber, + "V" + idx: recordType, + "W" + idx: invocationTimestamp, + }) + } + // 导出数据表格 + if err := file.WriterFileExecl(headerCells, dataCells, filePath, ""); err != nil { + logger.Errorf("export sgec cdr err => %v", err.Error()) + return "export err" + } + } + + // 上传到FTP服务器 + if err := s.backupService.FTPPushFile(filePath, "cdr"); err != nil { + return "ok, ftp err:" + err.Error() + } + return "ok" +} diff --git a/src/modules/crontask/processor/backup_export_log/backup_export_log.go b/src/modules/crontask/processor/backup_export_log/backup_export_log.go new file mode 100644 index 00000000..6d7d6509 --- /dev/null +++ b/src/modules/crontask/processor/backup_export_log/backup_export_log.go @@ -0,0 +1,358 @@ +package backup_export_log + +import ( + "encoding/json" + "fmt" + "path/filepath" + "runtime" + "strconv" + "time" + + "be.ems/src/framework/cron" + "be.ems/src/framework/i18n" + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/date" + "be.ems/src/framework/utils/file" + neDataService "be.ems/src/modules/network_data/service" + systemModel "be.ems/src/modules/system/model" + systemService "be.ems/src/modules/system/service" +) + +var NewProcessor = &BackupExportLogProcessor{ + count: 0, + backupService: neDataService.NewBackup, + sysLogLoginService: systemService.NewSysLogLogin, + sysOperateService: systemService.NewSysLogOperate, +} + +// BackupExportLog 队列任务处理 +type BackupExportLogProcessor struct { + count int // 执行次数 + backupService *neDataService.Backup // 备份相关服务 + sysLogLoginService *systemService.SysLogLogin // 系统登录日志服务 + sysOperateService *systemService.SysLogOperate // 系统操作日志服务 +} + +func (s *BackupExportLogProcessor) Execute(data any) (any, error) { + s.count++ // 执行次数加一 + options := data.(cron.JobData) + sysJob := options.SysJob + logger.Infof("重复:%v 任务ID:%d 执行次数:%d", options.Repeat, sysJob.JobId, s.count) + // 返回结果,用于记录执行结果 + result := map[string]any{ + "count": s.count, + } + + var params struct { + DataType []string `json:"dataType"` // 类型支持 operate/login + FileType string `json:"fileType"` // 文件类型 csv/xlsx + Hour int `json:"hour"` // 数据时间从任务执行时间前的小时数 + } + if err := json.Unmarshal([]byte(sysJob.TargetParams), ¶ms); err != nil { + return nil, err + } + if !(params.FileType == "csv" || params.FileType == "xlsx") { + return nil, fmt.Errorf("file type error, only support csv,xlsx") + } + + for _, v := range params.DataType { + switch v { + case "operate": + result[v] = s.exportOperate(params.Hour, params.FileType) + case "login": + result[v] = s.exportLogin(params.Hour, params.FileType) + } + } + + // 返回结果,用于记录执行结果 + return result, nil +} + +// exportOperate 导出系统操作日志数据 +func (s BackupExportLogProcessor) exportOperate(hour int, fileType string) string { + // 前 hour 小时 + now := time.Now() + end := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) + start := end.Add(-time.Duration(hour) * time.Hour) + + language := "en" + query := map[string]string{ + "beginTime": fmt.Sprint(start.UnixMilli()), + "endTime": fmt.Sprint(end.UnixMilli()), + "pageNum": "1", + "pageSize": "30000", + } + rows, total := s.sysOperateService.FindByPage(query, "") + if total == 0 { + return "no data" + } + + // 闭包函数处理多语言 + converI18n := func(language string, arr *[]systemModel.SysLogOperate) { + for i := range *arr { + (*arr)[i].Title = i18n.TKey(language, (*arr)[i].Title) + (*arr)[i].OperaLocation = i18n.TKey(language, (*arr)[i].OperaLocation) + } + } + converI18n(language, &rows) + + // 导出文件名称 + fileName := fmt.Sprintf("sys_log_operate_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType) + filePath := filepath.Join(s.backupService.BACKUP_DIR, "/log/sys_log_operate", fileName) + if runtime.GOOS == "windows" { + filePath = fmt.Sprintf("C:%s", filePath) + } + + // 业务类型 + businessTypeFunc := func(v string) string { + businessType := "" + switch v { + case "0": + // 业务操作类型-其它 + businessType = i18n.TKey(language, "dictData.operType.other") + case "1": + // 业务操作类型-新增 + businessType = i18n.TKey(language, "dictData.operType.add") + case "2": + // 业务操作类型-修改 + businessType = i18n.TKey(language, "dictData.operType.edit") + case "3": + // 业务操作类型-删除 + businessType = i18n.TKey(language, "dictData.operType.delete") + case "4": + // 业务操作类型-授权 + businessType = i18n.TKey(language, "dictData.operType.auth") + case "5": + // 业务操作类型-导出 + businessType = i18n.TKey(language, "dictData.operType.export") + case "6": + // 业务操作类型-导入 + businessType = i18n.TKey(language, "dictData.operType.import") + case "7": + // 业务操作类型-强退 + businessType = i18n.TKey(language, "dictData.operType.forced quit") + case "8": + // 业务操作类型-清空数据 + businessType = i18n.TKey(language, "dictData.operType.clear") + } + return businessType + } + + if fileType == "csv" { + // 转换数据 + data := [][]string{ + { + i18n.TKey(language, "log.operate.export.id"), + i18n.TKey(language, "log.operate.export.title"), + i18n.TKey(language, "log.operate.export.businessType"), + i18n.TKey(language, "log.operate.export.operName"), + i18n.TKey(language, "log.operate.export.method"), + i18n.TKey(language, "log.operate.export.ip"), + i18n.TKey(language, "log.operate.export.status"), + i18n.TKey(language, "log.operate.export.operTime"), + i18n.TKey(language, "log.operate.export.costTime"), + }, + } + for _, row := range rows { + // 业务类型 + businessType := businessTypeFunc(row.BusinessType) + + // 状态 + statusValue := i18n.TKey(language, "dictData.fail") + if row.StatusFlag == "1" { + statusValue = i18n.TKey(language, "dictData.success") + } + data = append(data, []string{ + fmt.Sprint(row.ID), + row.Title, + businessType, + row.OperaBy, + row.OperaUrlMethod, + row.OperaIp, + statusValue, + date.ParseDateToStr(row.OperaTime, date.YYYY_MM_DDTHH_MM_SSZ), + fmt.Sprint(row.CostTime), + }) + } + // 输出到文件 + if err := file.WriterFileCSV(data, filePath); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + if fileType == "xlsx" { + // 第一行表头标题 + headerCells := map[string]string{ + "A1": i18n.TKey(language, "log.operate.export.id"), + "B1": i18n.TKey(language, "log.operate.export.title"), + "C1": i18n.TKey(language, "log.operate.export.businessType"), + "D1": i18n.TKey(language, "log.operate.export.operName"), + "E1": i18n.TKey(language, "log.operate.export.method"), + "F1": i18n.TKey(language, "log.operate.export.ip"), + "G1": i18n.TKey(language, "log.operate.export.status"), + "H1": i18n.TKey(language, "log.operate.export.operTime"), + "I1": i18n.TKey(language, "log.operate.export.costTime"), + } + // 从第二行开始的数据 + dataCells := make([]map[string]any, 0) + for i, row := range rows { + idx := strconv.Itoa(i + 2) + // 业务类型 + businessType := businessTypeFunc(row.BusinessType) + + // 状态 + statusValue := i18n.TKey(language, "dictData.fail") + if row.StatusFlag == "1" { + statusValue = i18n.TKey(language, "dictData.success") + } + dataCells = append(dataCells, map[string]any{ + "A" + idx: row.ID, + "B" + idx: row.Title, + "C" + idx: businessType, + "D" + idx: row.OperaBy, + "E" + idx: row.OperaUrlMethod, + "F" + idx: row.OperaIp, + "G" + idx: statusValue, + "H" + idx: date.ParseDateToStr(row.OperaTime, date.YYYY_MM_DDTHH_MM_SSZ), + "I" + idx: row.CostTime, + }) + } + // 导出数据表格 + if err := file.WriterFileExecl(headerCells, dataCells, filePath, ""); err != nil { + logger.Errorf("export operate log err => %v", err.Error()) + return "export err" + } + } + + // 上传到FTP服务器 + if err := s.backupService.FTPPushFile(filePath, "log"); err != nil { + return "ok, ftp err:" + err.Error() + } + return "ok" +} + +// exportLogin 导出系统登录日志数据 +func (s BackupExportLogProcessor) exportLogin(hour int, fileType string) string { + // 前 hour 小时 + now := time.Now() + end := time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, now.Location()) + start := end.Add(-time.Duration(hour) * time.Hour) + + language := "en" + query := map[string]string{ + "beginTime": fmt.Sprint(start.UnixMilli()), + "endTime": fmt.Sprint(end.UnixMilli()), + "pageNum": "1", + "pageSize": "30000", + } + rows, total := s.sysLogLoginService.FindByPage(query, "") + if total == 0 { + return "no data" + } + + // 闭包函数处理多语言 + converI18n := func(language string, arr *[]systemModel.SysLogLogin) { + for i := range *arr { + (*arr)[i].LoginLocation = i18n.TKey(language, (*arr)[i].LoginLocation) + (*arr)[i].OS = i18n.TKey(language, (*arr)[i].OS) + (*arr)[i].Browser = i18n.TKey(language, (*arr)[i].Browser) + (*arr)[i].Msg = i18n.TKey(language, (*arr)[i].Msg) + } + } + converI18n(language, &rows) + + // 导出文件名称 + fileName := fmt.Sprintf("sys_log_login_export_%d_%s.%s", len(rows), date.ParseDateToStr(end, date.YYYYMMDDHHMMSS), fileType) + filePath := filepath.Join(s.backupService.BACKUP_DIR, "/log/sys_log_login", fileName) + if runtime.GOOS == "windows" { + filePath = fmt.Sprintf("C:%s", filePath) + } + + if fileType == "csv" { + // 转换数据 + data := [][]string{ + { + i18n.TKey(language, "log.login.export.id"), + i18n.TKey(language, "log.login.export.userName"), + i18n.TKey(language, "log.login.export.ip"), + i18n.TKey(language, "log.login.export.location"), + i18n.TKey(language, "log.login.export.os"), + i18n.TKey(language, "log.login.export.browser"), + i18n.TKey(language, "log.login.export.status"), + i18n.TKey(language, "log.login.export.time"), + i18n.TKey(language, "log.login.export.msg"), + }, + } + for _, row := range rows { + // 状态 + statusValue := i18n.TKey(language, "dictData.fail") + if row.StatusFlag == "1" { + statusValue = i18n.TKey(language, "dictData.success") + } + data = append(data, []string{ + fmt.Sprint(row.ID), + row.UserName, + row.LoginIp, + row.LoginLocation, + row.OS, + row.Browser, + statusValue, + date.ParseDateToStr(row.LoginTime, date.YYYY_MM_DDTHH_MM_SSZ), + row.Msg, + }) + } + // 输出到文件 + if err := file.WriterFileCSV(data, filePath); err != nil { + logger.Errorf("export login log err => %v", err.Error()) + return "export err" + } + } + + if fileType == "xlsx" { + // 第一行表头标题 + headerCells := map[string]string{ + "A1": i18n.TKey(language, "log.login.export.id"), + "B1": i18n.TKey(language, "log.login.export.userName"), + "C1": i18n.TKey(language, "log.login.export.ip"), + "D1": i18n.TKey(language, "log.login.export.location"), + "E1": i18n.TKey(language, "log.login.export.os"), + "F1": i18n.TKey(language, "log.login.export.browser"), + "G1": i18n.TKey(language, "log.login.export.status"), + "H1": i18n.TKey(language, "log.login.export.time"), + "I1": i18n.TKey(language, "log.login.export.msg"), + } + // 从第二行开始的数据 + dataCells := make([]map[string]any, 0) + for i, row := range rows { + idx := strconv.Itoa(i + 2) + // 状态 + statusValue := i18n.TKey(language, "dictData.fail") + if row.StatusFlag == "1" { + statusValue = i18n.TKey(language, "dictData.success") + } + dataCells = append(dataCells, map[string]any{ + "A" + idx: row.ID, + "B" + idx: row.UserName, + "C" + idx: row.LoginIp, + "D" + idx: row.LoginLocation, + "E" + idx: row.OS, + "F" + idx: row.Browser, + "G" + idx: statusValue, + "H" + idx: date.ParseDateToStr(row.LoginTime, date.YYYY_MM_DDTHH_MM_SSZ), + "I" + idx: row.Msg, + }) + } + // 导出数据表格 + if err := file.WriterFileExecl(headerCells, dataCells, filePath, ""); err != nil { + logger.Errorf("export login log err => %v", err.Error()) + return "export err" + } + } + + // 上传到FTP服务器 + if err := s.backupService.FTPPushFile(filePath, "log"); err != nil { + return "ok, ftp err:" + err.Error() + } + return "ok" +} diff --git a/src/modules/crontask/processor/ne_alarm_state_check/ne_alarm_state_check.go b/src/modules/crontask/processor/ne_alarm_state_check/ne_alarm_state_check.go index f74a3d1b..f2d6e9a7 100644 --- a/src/modules/crontask/processor/ne_alarm_state_check/ne_alarm_state_check.go +++ b/src/modules/crontask/processor/ne_alarm_state_check/ne_alarm_state_check.go @@ -88,13 +88,13 @@ func (s *NeAlarmStateCheckProcessor) Execute(data any) (any, error) { // 在线且状态为活动告警 if isOnline && alarmStatus == "1" { // 进行清除 - newAlarm, err := s.alarmClear(neInfo, alarmIdArr[0]) + clearAlarm, err := s.alarmClear(neInfo, alarmIdArr[0]) if err != nil { result[neTypeAndId] = err.Error() continue } groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId) - s.wsSendService.ByGroupID(groupID, newAlarm) + s.wsSendService.ByGroupID(groupID, clearAlarm) result[neTypeAndId] = "alarm clear" } @@ -151,19 +151,19 @@ func (s NeAlarmStateCheckProcessor) alarmNew(neInfo neModel.NeInfo, v alarmParam AlarmTitle: v.AlarmTitle, AlarmCode: constants.ALARM_STATE_CHECK, EventTime: lastTime, - AlarmType: "2", - OrigSeverity: "2", - PerceivedSeverity: "2", + AlarmType: v.AlarmType, + OrigSeverity: v.OrigSeverity, + PerceivedSeverity: v.OrigSeverity, ObjectUid: neInfo.RmUID, - ObjectName: "SystemManagement;Heartbeat", - ObjectType: "SystemState", - LocationInfo: "SystemManagement.State: NE Heartbeat", - AlarmStatus: "1", + ObjectName: "NE State", + ObjectType: "state", + LocationInfo: "NE State: Heartbeat", + AlarmStatus: "1", // 活动告警 SpecificProblem: v.SpecificProblem, SpecificProblemId: v.SpecificProblemID, AddInfo: v.AddInfo, } - insertId := s.alarmService.Insert(alarm) + insertId := s.alarmService.InsertAndForword(alarm) if insertId > 0 { alarm.ID = insertId return alarm, nil diff --git a/src/modules/crontask/processor/ne_alarm_state_check_cmd/ne_alarm_state_check_cmd.go b/src/modules/crontask/processor/ne_alarm_state_check_cmd/ne_alarm_state_check_cmd.go new file mode 100644 index 00000000..20017f66 --- /dev/null +++ b/src/modules/crontask/processor/ne_alarm_state_check_cmd/ne_alarm_state_check_cmd.go @@ -0,0 +1,288 @@ +package ne_alarm_state_check_cmd + +import ( + "encoding/json" + "fmt" + "sort" + "strconv" + "strings" + "time" + + "be.ems/src/framework/constants" + "be.ems/src/framework/cron" + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/parse" + neDataModel "be.ems/src/modules/network_data/model" + neDataService "be.ems/src/modules/network_data/service" + neModel "be.ems/src/modules/network_element/model" + neService "be.ems/src/modules/network_element/service" + wsService "be.ems/src/modules/ws/service" +) + +var NewProcessor = &NeAlarmStateCheckCMDProcessor{ + neConfigBackupService: neService.NewNeConfigBackup, + neInfoService: neService.NewNeInfo, + neStateService: neDataService.NewNEState, + alarmService: neDataService.NewAlarm, + wsSendService: wsService.NewWSSend, + count: 0, +} + +// NeAlarmStateCheckCMDProcessor 网元告警内存/CPU/磁盘检查 +type NeAlarmStateCheckCMDProcessor struct { + neConfigBackupService *neService.NeConfigBackup // 网元配置文件备份记录服务 + neInfoService *neService.NeInfo // 网元信息服务 + neStateService *neDataService.NEState // 网元状态信息服务 + alarmService *neDataService.Alarm // 告警信息服务 + wsSendService *wsService.WSSend // ws发送服务 + count int // 执行次数 + +} + +// alarmParams 告警参数 +type alarmParams struct { + AlarmTitle string `json:"alarmTitle"` // NE State Check Alarm CPU/Menory/Disk + AlarmType string `json:"alarmType"` // EquipmentAlarm=2 + OrigSeverity string `json:"origSeverity"` // Major=2 + SpecificProblem string `json:"specificProblem"` // Alarm Cause: CPU/Menory/Disk status received from target NE reaches the threshold + SpecificProblemID string `json:"specificProblemId"` // AC10100 + AddInfo string `json:"addInfo"` // 告警补充信息 + CPUUseGt int64 `json:"cpuUseGt"` // CPU使用率大于, 范围0~100% + MemUseGt int64 `json:"memUseGt"` // 内存使用率大于, 范围0~100% + DiskUseGt int64 `json:"diskUseGt"` // 磁盘使用率大于, 范围0~100% + + // === 非参数字段 === + AlarmId string // 告警ID +} + +func (s *NeAlarmStateCheckCMDProcessor) Execute(data any) (any, error) { + s.count++ // 执行次数加一 + options := data.(cron.JobData) + sysJob := options.SysJob + logger.Infof("重复:%v 任务ID:%d 执行次数:%d", options.Repeat, sysJob.JobId, s.count) + // 返回结果,用于记录执行结果 + result := map[string]any{ + "count": s.count, + } + + // 读取参数值 + var params alarmParams + err := json.Unmarshal([]byte(sysJob.TargetParams), ¶ms) + if err != nil { + return nil, fmt.Errorf("json params err: %v", err) + } + // 检查使用率 + if params.CPUUseGt > 100 || params.CPUUseGt < 0 { + return nil, fmt.Errorf("cpuUseGt must be between 0 and 100") + } + if params.MemUseGt > 100 || params.MemUseGt < 0 { + return nil, fmt.Errorf("memUseGt must be between 0 and 100") + } + if params.DiskUseGt > 100 || params.DiskUseGt < 0 { + return nil, fmt.Errorf("diskUseGt must be between 0 and 100") + } + + neList := s.neInfoService.Find(neModel.NeInfo{}, true, false) + for _, neInfo := range neList { + if neInfo.CreateTime == 0 { + continue + } + + // 网元在线状态 + isOnline := parse.Boolean(neInfo.ServerState["online"]) + if !isOnline { + continue + } + + // 检查状态 + err := s.serverState(neInfo.ServerState, params.CPUUseGt, params.MemUseGt, params.DiskUseGt) + if err == nil { + continue + } + + neTypeAndId := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId) + // 告警ID + params.AlarmId = fmt.Sprintf("%d%d", constants.ALARM_CMD_CHECK, neInfo.CreateTime) + // 检查网元告警ID是否唯一 + alarmIdArr := s.alarmService.Find(neDataModel.Alarm{ + NeType: neInfo.NeType, + NeId: neInfo.NeId, + AlarmId: params.AlarmId, + }) + // 告警状态, 存在的需要手动清除 + alarmStatus := "" + if len(alarmIdArr) > 0 { + alarmStatus = fmt.Sprint(alarmIdArr[0].AlarmStatus) + } + // 活动告警进行清除 + if alarmStatus == "1" { + clearAlarm, err := s.alarmClear(neInfo, alarmIdArr[0]) + if err != nil { + result[neTypeAndId] = err.Error() + continue + } + groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId) + s.wsSendService.ByGroupID(groupID, clearAlarm) + result[neTypeAndId] = "alarm clear" + alarmStatus = "" // 标记为未记录再次发起新告警 + } + // 未记录 + if alarmStatus == "" { + addInfo := params.AddInfo + if params.AddInfo != "" { + params.AddInfo = params.AddInfo + ", " + err.Error() + } else { + params.AddInfo = err.Error() + } + // 进行新增 + newAlarm, err := s.alarmNew(neInfo, params) + params.AddInfo = addInfo // 恢复附加信息 + if err != nil { + result[neTypeAndId] = err.Error() + continue + } + groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId) + s.wsSendService.ByGroupID(groupID, newAlarm) + result[neTypeAndId] = "alarm new" + } + } + + // 返回结果,用于记录执行结果 + return result, nil +} + +// serverState 网元状态 +func (s NeAlarmStateCheckCMDProcessor) serverState(state map[string]any, cpuUseGt, memUseGt, diskUseGt int64) error { + // 网元CPU使用率 + var nfCpuUsage float64 = 0 + // CPU使用率 + var sysCpuUsage float64 = 0 + if state["cpu"] != nil { + cpu := state["cpu"].(map[string]any) + v := parse.Number(cpu["sysCpuUsage"]) + sysCpuUsage = float64(v) / 100 + nfv := parse.Number(cpu["nfCpuUsage"]) + nfCpuUsage = float64(nfv) / 100 + } + + // 网元内存使用KB + var nfMemUsed int64 = 0 + // 内存使用率 + var sysMemUsage float64 = 0 + if state["mem"] != nil { + mem := state["mem"].(map[string]any) + v := parse.Number(mem["sysMemUsage"]) + sysMemUsage = float64(v) / 100 + nfMemUsed = parse.Number(mem["nfUsedMem"]) + } + + // 磁盘使用率 + var sysDiskUsage float64 = 0 + if state["disk"] != nil { + mem := state["disk"].(map[string]any) + disks := mem["partitionInfo"].([]any) + sort.Slice(disks, func(i, j int) bool { + iUsed := parse.Number(disks[i].(map[string]any)["used"]) + jUsed := parse.Number(disks[j].(map[string]any)["used"]) + return iUsed > jUsed + }) + disk := disks[0].(map[string]any) + total := parse.Number(disk["total"]) + used := parse.Number(disk["used"]) + sysDiskUsage = (float64(used) / float64(total)) * 100 + sysDiskUsage, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", sysDiskUsage), 64) + } + + // 插入网元状态记录 + neState := neDataModel.NEState{ + NeType: fmt.Sprint(state["neType"]), + NeId: fmt.Sprint(state["neId"]), + Version: fmt.Sprint(state["version"]), + Capability: parse.Number(state["capability"]), + SerialNum: fmt.Sprint(state["sn"]), + ExpiryDate: fmt.Sprint(state["expire"]), + SysCpuUsage: sysCpuUsage, + SysMemUsage: sysMemUsage, + SysDiskUsage: sysDiskUsage, + NfCpuUsage: nfCpuUsage, + NfMemUsed: nfMemUsed, + CreateTime: parse.Number(state["refreshTime"]), + } + s.neStateService.Insert(neState) + // 删除网元状态记录7天前 + s.neStateService.DeleteByTime(time.Now().UnixMilli() - 7*24*60*60*1000) + // 推送ws消息 + groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_NE_STATE, neState.NeType, neState.NeId) + s.wsSendService.ByGroupID(groupID, neState) + + // 检查CPU/Menory/Disk使用率 + warnMsg := []string{} + if int64(sysCpuUsage) >= cpuUseGt { + warnMsg = append(warnMsg, fmt.Sprintf("cpu usage %.2f%%", sysCpuUsage)) + } + if int64(sysMemUsage) >= memUseGt { + warnMsg = append(warnMsg, fmt.Sprintf("memory usage %.2f%%", sysMemUsage)) + } + if int64(sysDiskUsage) >= diskUseGt { + warnMsg = append(warnMsg, fmt.Sprintf("disk usage %.2f%%", sysDiskUsage)) + } + if len(warnMsg) > 0 { + return fmt.Errorf("greater than %s", strings.Join(warnMsg, ", ")) + } + return nil +} + +// alarmClear 清除告警 +func (s NeAlarmStateCheckCMDProcessor) alarmClear(neInfo neModel.NeInfo, v neDataModel.Alarm) (neDataModel.Alarm, error) { + // 变更告警ID为告警清除ID + v.AlarmId = fmt.Sprintf("%d%d", v.AlarmCode, v.EventTime) + v.AlarmStatus = "0" + // 告警清除 + v.ClearType = 1 + v.ClearTime = neInfo.UpdateTime + v.ClearUser = "system" + rows := s.alarmService.Update(v) + if rows > 0 { + return v, nil + } + return neDataModel.Alarm{}, fmt.Errorf("clear alarm fail") +} + +// alarmNew 新增告警 +func (s NeAlarmStateCheckCMDProcessor) alarmNew(neInfo neModel.NeInfo, v alarmParams) (neDataModel.Alarm, error) { + // seq 告警序号 + lastSeq := s.alarmService.FindAlarmSeqLast(neInfo.NeType, neInfo.NeId) + lastTime := neInfo.UpdateTime // 网元最后更新时间 + if lastTime < neInfo.CreateTime { + lastTime = time.Now().UnixMilli() + } + alarm := neDataModel.Alarm{ + NeType: neInfo.NeType, + NeId: neInfo.NeId, + NeName: neInfo.NeName, + Province: neInfo.Province, + PvFlag: neInfo.PvFlag, + AlarmSeq: lastSeq + 1, + AlarmId: v.AlarmId, + AlarmTitle: v.AlarmTitle, + AlarmCode: constants.ALARM_CMD_CHECK, + EventTime: lastTime, + AlarmType: v.AlarmType, + OrigSeverity: v.OrigSeverity, + PerceivedSeverity: v.OrigSeverity, + ObjectUid: neInfo.RmUID, + ObjectName: "NE CPU/Menory/Disk", + ObjectType: "cmd", + LocationInfo: "NE CPU/Menory/Disk: Heartbeat", + AlarmStatus: "1", // 活动告警 + SpecificProblem: v.SpecificProblem, + SpecificProblemId: v.SpecificProblemID, + AddInfo: v.AddInfo, + } + insertId := s.alarmService.InsertAndForword(alarm) + if insertId > 0 { + alarm.ID = insertId + return alarm, nil + } + return neDataModel.Alarm{}, fmt.Errorf("new alarm fail") +} diff --git a/src/modules/crontask/processor/ne_alarm_state_check_license/ne_alarm_state_check_license.go b/src/modules/crontask/processor/ne_alarm_state_check_license/ne_alarm_state_check_license.go new file mode 100644 index 00000000..57d6d5ae --- /dev/null +++ b/src/modules/crontask/processor/ne_alarm_state_check_license/ne_alarm_state_check_license.go @@ -0,0 +1,213 @@ +package ne_alarm_state_check_license + +import ( + "encoding/json" + "fmt" + "time" + + "be.ems/src/framework/constants" + "be.ems/src/framework/cron" + "be.ems/src/framework/logger" + "be.ems/src/framework/utils/parse" + + neDataModel "be.ems/src/modules/network_data/model" + neDataService "be.ems/src/modules/network_data/service" + neModel "be.ems/src/modules/network_element/model" + neService "be.ems/src/modules/network_element/service" + wsService "be.ems/src/modules/ws/service" +) + +var NewProcessor = &NeAlarmStateCheckLicenseProcessor{ + neConfigBackupService: neService.NewNeConfigBackup, + neInfoService: neService.NewNeInfo, + alarmService: neDataService.NewAlarm, + wsSendService: wsService.NewWSSend, + count: 0, +} + +// NeAlarmStateCheckLicenseProcessor 网元告警License到期检查 +type NeAlarmStateCheckLicenseProcessor struct { + neConfigBackupService *neService.NeConfigBackup // 网元配置文件备份记录服务 + neInfoService *neService.NeInfo // 网元信息服务 + alarmService *neDataService.Alarm // 告警信息服务 + wsSendService *wsService.WSSend // ws发送服务 + count int // 执行次数 +} + +// alarmParams 告警参数 +type alarmParams struct { + AlarmTitle string `json:"alarmTitle"` // NE State Check Alarm License + AlarmType string `json:"alarmType"` // EquipmentAlarm=2 + OrigSeverity string `json:"origSeverity"` // Major=2 + SpecificProblem string `json:"specificProblem"` // Alarm Cause: License received from target NE is about to expire + SpecificProblemID string `json:"specificProblemId"` // AC10200 + AddInfo string `json:"addInfo"` // 告警补充信息 + DayLt int64 `json:"dayLt"` // 天数小于,默认30天 + + // === 非参数字段 === + AlarmId string // 告警ID +} + +func (s *NeAlarmStateCheckLicenseProcessor) Execute(data any) (any, error) { + s.count++ // 执行次数加一 + options := data.(cron.JobData) + sysJob := options.SysJob + logger.Infof("重复:%v 任务ID:%d 执行次数:%d", options.Repeat, sysJob.JobId, s.count) + // 返回结果,用于记录执行结果 + result := map[string]any{ + "count": s.count, + } + + // 读取参数值 + var params alarmParams + err := json.Unmarshal([]byte(sysJob.TargetParams), ¶ms) + if err != nil { + return nil, fmt.Errorf("json params err: %v", err) + } + // 检查参数值 + if params.DayLt == 0 { + params.DayLt = 30 + } + + neList := s.neInfoService.Find(neModel.NeInfo{}, true, false) + for _, neInfo := range neList { + if neInfo.CreateTime == 0 { + continue + } + + // 网元在线状态 + isOnline := parse.Boolean(neInfo.ServerState["online"]) + if !isOnline { + continue + } + + // 检查状态 + err := s.serverState(neInfo.ServerState, params.DayLt) + if err == nil { + continue + } + if params.AddInfo != "" { + params.AddInfo = params.AddInfo + ", " + err.Error() + } else { + params.AddInfo = err.Error() + } + + neTypeAndId := fmt.Sprintf("%s_%s", neInfo.NeType, neInfo.NeId) + // 告警ID + params.AlarmId = fmt.Sprintf("%d%d", constants.ALARM_LICENSE_CHECK, neInfo.CreateTime) + // 检查网元告警ID是否唯一 + alarmIdArr := s.alarmService.Find(neDataModel.Alarm{ + NeType: neInfo.NeType, + NeId: neInfo.NeId, + AlarmId: params.AlarmId, + }) + // 告警状态, 存在的需要手动清除 + alarmStatus := "" + if len(alarmIdArr) > 0 { + alarmStatus = fmt.Sprint(alarmIdArr[0].AlarmStatus) + } + // 活动告警进行清除 + if alarmStatus == "1" { + clearAlarm, err := s.alarmClear(neInfo, alarmIdArr[0]) + if err != nil { + result[neTypeAndId] = err.Error() + continue + } + groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId) + s.wsSendService.ByGroupID(groupID, clearAlarm) + result[neTypeAndId] = "alarm clear" + alarmStatus = "" // 标记为未记录再次发起新告警 + } + // 未记录 + if alarmStatus == "" { + // 进行新增 + newAlarm, err := s.alarmNew(neInfo, params) + if err != nil { + result[neTypeAndId] = err.Error() + continue + } + groupID := fmt.Sprintf("%s_%s_%s", wsService.GROUP_ALARM, neInfo.NeType, neInfo.NeId) + s.wsSendService.ByGroupID(groupID, newAlarm) + result[neTypeAndId] = "alarm new" + } + } + + // 返回结果,用于记录执行结果 + return result, nil +} + +// serverState 网元状态 +func (s NeAlarmStateCheckLicenseProcessor) serverState(state map[string]any, dayLt int64) error { + expire := fmt.Sprint(state["expire"]) + if expire == "" || expire == "" || expire == "2099-12-31" { + return nil + } + + // 解析过期时间 + expireTime, err := time.Parse("2006-01-02", expire) + if err != nil { + return fmt.Errorf("parse expire time error: %v", err) + } + + // 计算距离天数 + daysLeft := int64(time.Since(expireTime).Hours() / 24) + if daysLeft < dayLt { + return fmt.Errorf("license will expire in %d days", daysLeft) + } + return nil +} + +// alarmClear 清除告警 +func (s NeAlarmStateCheckLicenseProcessor) alarmClear(neInfo neModel.NeInfo, v neDataModel.Alarm) (neDataModel.Alarm, error) { + // 变更告警ID为告警清除ID + v.AlarmId = fmt.Sprintf("%d%d", v.AlarmCode, v.EventTime) + v.AlarmStatus = "0" + // 告警清除 + v.ClearType = 1 + v.ClearTime = neInfo.UpdateTime + v.ClearUser = "system" + rows := s.alarmService.Update(v) + if rows > 0 { + return v, nil + } + return neDataModel.Alarm{}, fmt.Errorf("clear alarm fail") +} + +// alarmNew 新增告警 +func (s NeAlarmStateCheckLicenseProcessor) alarmNew(neInfo neModel.NeInfo, v alarmParams) (neDataModel.Alarm, error) { + // seq 告警序号 + lastSeq := s.alarmService.FindAlarmSeqLast(neInfo.NeType, neInfo.NeId) + lastTime := neInfo.UpdateTime // 网元最后更新时间 + if lastTime < neInfo.CreateTime { + lastTime = time.Now().UnixMilli() + } + alarm := neDataModel.Alarm{ + NeType: neInfo.NeType, + NeId: neInfo.NeId, + NeName: neInfo.NeName, + Province: neInfo.Province, + PvFlag: neInfo.PvFlag, + AlarmSeq: lastSeq + 1, + AlarmId: v.AlarmId, + AlarmTitle: v.AlarmTitle, + AlarmCode: constants.ALARM_LICENSE_CHECK, + EventTime: lastTime, + AlarmType: v.AlarmType, + OrigSeverity: v.OrigSeverity, + PerceivedSeverity: v.OrigSeverity, + ObjectUid: neInfo.RmUID, + ObjectName: "NE License", + ObjectType: "license", + LocationInfo: "NE License: Heartbeat", + AlarmStatus: "1", // 活动告警 + SpecificProblem: v.SpecificProblem, + SpecificProblemId: v.SpecificProblemID, + AddInfo: v.AddInfo, + } + insertId := s.alarmService.InsertAndForword(alarm) + if insertId > 0 { + alarm.ID = insertId + return alarm, nil + } + return neDataModel.Alarm{}, fmt.Errorf("new alarm fail") +} diff --git a/src/modules/crontask/processor/processor.go b/src/modules/crontask/processor/processor.go index bc2e4f78..c4e203cf 100644 --- a/src/modules/crontask/processor/processor.go +++ b/src/modules/crontask/processor/processor.go @@ -2,6 +2,8 @@ package processor import ( "be.ems/src/framework/cron" + processorBackupExportCDR "be.ems/src/modules/crontask/processor/backup_export_cdr" + processorBackupExportLog "be.ems/src/modules/crontask/processor/backup_export_log" processorBackupExportTable "be.ems/src/modules/crontask/processor/backup_export_table" processorBackupExportUDM "be.ems/src/modules/crontask/processor/backup_export_udm" processorBackupRemoveFile "be.ems/src/modules/crontask/processor/backup_remove_file" @@ -11,6 +13,8 @@ import ( processorDeleteNeConfigBackup "be.ems/src/modules/crontask/processor/delete_ne_config_backup" processorMonitorSysResource "be.ems/src/modules/crontask/processor/monitor_sys_resource" processorNeAlarmStateCheck "be.ems/src/modules/crontask/processor/ne_alarm_state_check" + processorNeAlarmStateCheckCMD "be.ems/src/modules/crontask/processor/ne_alarm_state_check_cmd" + processorNeAlarmStateCheckLicense "be.ems/src/modules/crontask/processor/ne_alarm_state_check_license" processorNeConfigBackup "be.ems/src/modules/crontask/processor/ne_config_backup" processorNeDataUDM "be.ems/src/modules/crontask/processor/ne_data_udm" ) @@ -23,8 +27,13 @@ func InitCronQueue() { cron.CreateQueue("ne_config_backup", processorNeConfigBackup.NewProcessor) // 网元数据-UDM用户数据同步 cron.CreateQueue("ne_data_udm", processorNeDataUDM.NewProcessor) + // 网元告警-状态检查 cron.CreateQueue("ne_alarm_state_check", processorNeAlarmStateCheck.NewProcessor) + // 网元告警-内存/CPU/磁盘检查 + cron.CreateQueue("ne_alarm_state_check_cmd", processorNeAlarmStateCheckCMD.NewProcessor) + // 网元告警-License到期检查 + cron.CreateQueue("ne_alarm_state_check_license", processorNeAlarmStateCheckLicense.NewProcessor) // 删除-表内数据记录 cron.CreateQueue("delete_data_record", processorDeleteDataRecord.NewProcessor) @@ -41,4 +50,8 @@ func InitCronQueue() { cron.CreateQueue("backup_remove_file", processorBackupRemoveFile.NewProcessor) // 备份-导出UDM用户数据 cron.CreateQueue("backup_export_udm", processorBackupExportUDM.NewProcessor) + // 备份-导出CDR数据 + cron.CreateQueue("backup_export_cdr", processorBackupExportCDR.NewProcessor) + // 备份-导出Log数据 + cron.CreateQueue("backup_export_log", processorBackupExportLog.NewProcessor) }