Merge branch 'lichang'

This commit is contained in:
TsMask
2023-10-18 09:53:42 +08:00
208 changed files with 20526 additions and 203 deletions

3
.vscode/launch.json vendored
View File

@@ -10,7 +10,8 @@
"request": "launch",
"mode": "debug",
"program": "./restagent/restagent.go",
"console": "integratedTerminal"
"console": "integratedTerminal",
"args": ["--env", "local"] // 走开发配置
},
{
"name": "DEBUG restagent",

View File

@@ -1,3 +1,7 @@
# ems_backend
后端
后端
## 版本发布空间
\\192.168.1.205\share\release\omc

View File

@@ -0,0 +1,719 @@
SET FOREIGN_KEY_CHECKS=0;
CREATE TABLE `omc_db`.`sys_dept` (
`dept_id` bigint NOT NULL AUTO_INCREMENT COMMENT '部门id',
`parent_id` bigint NULL DEFAULT 0 COMMENT '父部门id 默认0',
`ancestors` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '祖级列表',
`dept_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '部门名称',
`order_num` int NULL DEFAULT 0 COMMENT '显示顺序',
`leader` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '负责人',
`phone` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '联系电话',
`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '邮箱',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '部门状态0停用 1正常',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志0代表存在 1代表删除',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` bigint NULL DEFAULT 0 COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` bigint NULL DEFAULT 0 COMMENT '更新时间',
PRIMARY KEY (`dept_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '部门表' ROW_FORMAT = DYNAMIC;
CREATE TABLE `omc_db`.`sys_job` (
`job_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务ID',
`job_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '任务名称',
`job_group` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT 'DEFAULT' COMMENT '任务组名',
`invoke_target` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调用目标字符串',
`target_params` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '调用目标传入参数',
`cron_expression` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT 'cron执行表达式',
`misfire_policy` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '3' COMMENT '计划执行错误策略1立即执行 2执行一次 3放弃执行',
`concurrent` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '是否并发执行0禁止 1允许',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '任务状态0暂停 1正常',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` bigint NULL DEFAULT 0 COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` bigint NULL DEFAULT 0 COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '备注',
PRIMARY KEY (`job_id`, `job_name`, `job_group`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '调度任务调度表' ROW_FORMAT = DYNAMIC;
CREATE TABLE `omc_db`.`sys_job_log` (
`job_log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务日志ID',
`job_name` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务名称',
`job_group` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '任务组名',
`invoke_target` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '调用目标字符串',
`target_params` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '调用目标传入参数',
`job_msg` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '日志信息',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '执行状态0失败 1正常',
`create_time` bigint NULL DEFAULT 0 COMMENT '创建时间',
`cost_time` bigint NULL DEFAULT 0 COMMENT '消耗时间(毫秒)',
PRIMARY KEY (`job_log_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '调度任务调度日志表' ROW_FORMAT = DYNAMIC;
CREATE TABLE `omc_db`.`sys_log_login` (
`login_id` bigint NOT NULL AUTO_INCREMENT COMMENT '登录ID',
`user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户账号',
`ipaddr` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '登录IP地址',
`login_location` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '登录地点',
`browser` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '浏览器类型',
`os` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作系统',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '登录状态0失败 1成功',
`msg` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '提示消息',
`login_time` bigint NULL DEFAULT 0 COMMENT '登录时间',
PRIMARY KEY (`login_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统登录日志表' ROW_FORMAT = DYNAMIC;
CREATE TABLE `omc_db`.`sys_log_operate` (
`oper_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志主键',
`title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '模块标题',
`business_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '业务类型0其它 1新增 2修改 3删除 4授权 5导出 6导入 7强退 8清空数据',
`method` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '方法名称',
`request_method` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求方式',
`operator_type` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '操作人员类别0其它 1后台用户 2手机端用户',
`oper_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作人员',
`dept_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '部门名称',
`oper_url` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求URL',
`oper_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '主机地址',
`oper_location` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作地点',
`oper_param` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '请求参数',
`oper_msg` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '操作消息',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '操作状态0异常 1正常',
`oper_time` bigint NULL DEFAULT 0 COMMENT '操作时间',
`cost_time` bigint NULL DEFAULT 0 COMMENT '消耗时间(毫秒)',
PRIMARY KEY (`oper_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '系统操作日志表' ROW_FORMAT = DYNAMIC;
CREATE TABLE `omc_db`.`sys_post` (
`post_id` bigint NOT NULL AUTO_INCREMENT COMMENT '岗位ID',
`post_code` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位编码',
`post_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '岗位名称',
`post_sort` int NULL DEFAULT 0 COMMENT '显示顺序',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '状态0停用 1正常',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` bigint NULL DEFAULT 0 COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` bigint NULL DEFAULT 0 COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`post_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '岗位信息表' ROW_FORMAT = DYNAMIC;
CREATE TABLE `omc_db`.`sys_role_dept` (
`role_id` bigint NOT NULL COMMENT '角色ID',
`dept_id` bigint NOT NULL COMMENT '部门ID',
PRIMARY KEY (`role_id`, `dept_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色和部门关联表' ROW_FORMAT = DYNAMIC;
CREATE TABLE `omc_db`.`sys_user` (
`user_id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`dept_id` bigint NULL DEFAULT NULL COMMENT '部门ID',
`user_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户账号',
`nick_name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '用户昵称',
`user_type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT 'sys' COMMENT '用户类型sys系统用户',
`email` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '用户邮箱',
`phonenumber` varchar(11) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '手机号码',
`sex` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '用户性别0未知 1男 2女',
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '头像地址',
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '密码',
`status` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '帐号状态0停用 1正常',
`del_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '0' COMMENT '删除标志0代表存在 1代表删除',
`login_ip` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '最后登录IP',
`login_date` bigint NULL DEFAULT 0 COMMENT '最后登录时间',
`create_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '创建者',
`create_time` bigint NULL DEFAULT 0 COMMENT '创建时间',
`update_by` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '' COMMENT '更新者',
`update_time` bigint NULL DEFAULT 0 COMMENT '更新时间',
`remark` varchar(500) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户信息表' ROW_FORMAT = DYNAMIC;
CREATE TABLE `omc_db`.`sys_user_post` (
`user_id` bigint NOT NULL COMMENT '用户ID',
`post_id` bigint NOT NULL COMMENT '岗位ID',
PRIMARY KEY (`user_id`, `post_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户与岗位关联表' ROW_FORMAT = DYNAMIC;
-- 数据插入
INSERT INTO `omc_db`.`sys_config` (`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (5, '用户管理-密码锁定时间', 'sys.user.lockTime', '10', 'Y', 'AGrand', 1693911541269, '', 0, '密码锁定时间,单位分钟默认10分钟');
INSERT INTO `omc_db`.`sys_config` (`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (6, '用户管理-授权有效时间', 'sys.user.expiresIn', '120', 'Y', 'AGrand', 1693911541269, '', 0, '令牌有效期默认120分钟');
INSERT INTO `omc_db`.`sys_config` (`config_id`, `config_name`, `config_key`, `config_value`, `config_type`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (7, '用户管理-多端用户授权登录', 'sys.user.multi_login', 'true', 'Y', 'AGrand', 1693911541269, '', 0, '多端授权登录');
UPDATE `omc_db`.`sys_config` SET `config_name` = '用户管理-账号初始密码', `config_key` = 'sys.user.initPassword', `config_value` = 'Abcd@1234..', `config_type` = 'Y', `create_by` = 'AGrand', `create_time` = 1693908079656, `update_by` = '', `update_time` = 0, `remark` = '导入用户初始化密码 123456' WHERE `config_id` = 1;
UPDATE `omc_db`.`sys_config` SET `config_name` = '账号自助-验证码开关', `config_key` = 'sys.account.captchaEnabled', `config_value` = 'false', `config_type` = 'Y', `create_by` = 'AGrand', `create_time` = 1693908079667, `update_by` = '', `update_time` = 0, `remark` = '是否开启验证码功能true开启false关闭' WHERE `config_id` = 2;
UPDATE `omc_db`.`sys_config` SET `config_name` = '账号自助-是否开启用户注册功能', `config_key` = 'sys.account.registerUser', `config_value` = 'false', `config_type` = 'Y', `create_by` = 'AGrand', `create_time` = 1693908079669, `update_by` = '', `update_time` = 0, `remark` = '是否开启注册用户功能true开启false关闭' WHERE `config_id` = 3;
UPDATE `omc_db`.`sys_config` SET `config_name` = '用户管理-密码最大错误次数', `config_key` = 'sys.user.maxRetryCount', `config_value` = '5', `config_type` = 'Y', `create_by` = 'AGrand', `create_time` = 1693908079680, `update_by` = '', `update_time` = 0, `remark` = '密码最大错误次数' WHERE `config_id` = 4;
UPDATE `omc_db`.`sys_config` SET `config_name` = '测试', `config_key` = 'test', `config_value` = 'test', `config_type` = 'Y', `create_by` = 'AGrand', `create_time` = 1693911541269, `update_by` = 'AGrand', `update_time` = 1693911586418, `remark` = '测试' WHERE `config_id` = 100;
INSERT INTO `omc_db`.`sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (100, 0, '0', '千通科技', 0, 'AGrand', '', '', '1', '0', 'AGrand', 1697091866188, '', NULL);
INSERT INTO `omc_db`.`sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (101, 100, '0,100', '深圳总公司', 1, 'AGrand', '', '', '1', '0', 'AGrand', 1697091866192, '', NULL);
INSERT INTO `omc_db`.`sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (102, 101, '0,100,101', '研发部门', 1, 'AGrand', '', '', '1', '0', 'AGrand', 1697091866194, 'AGrand', 1697113796216);
INSERT INTO `omc_db`.`sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (103, 101, '0,100,101', '市场部门', 2, 'AGrand', '', '', '1', '0', 'AGrand', 1697091866197, '', NULL);
INSERT INTO `omc_db`.`sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (104, 101, '0,100,101', '测试部门', 3, 'AGrand', '', '', '1', '0', 'AGrand', 1697091866200, '', NULL);
INSERT INTO `omc_db`.`sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (105, 101, '0,100,101', '财务部门', 4, 'AGrand', '', '', '1', '0', 'AGrand', 1697091866203, '', NULL);
INSERT INTO `omc_db`.`sys_dept` (`dept_id`, `parent_id`, `ancestors`, `dept_name`, `order_num`, `leader`, `phone`, `email`, `status`, `del_flag`, `create_by`, `create_time`, `update_by`, `update_time`) VALUES (106, 101, '0,100,101', '运维部门', 5, 'AGrand', '', '', '1', '0', 'AGrand', 1697091866205, '', NULL);
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (29, 0, '接口跟踪', 'Interface', 'trace_type', NULL, 'blue ', '1', 'AGrand', 1697443290808, '', 0, '接口跟踪');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (30, 1, '设备跟踪', 'Device', 'trace_type', NULL, 'gold', '1', 'AGrand', 1697443307336, 'AGrand', 1697443541082, '设备跟踪');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (31, 2, '用户跟踪', 'UE', 'trace_type', NULL, 'green', '1', 'AGrand', 1697443562042, 'AGrand', 1697443566327, '用户跟踪');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (32, 0, '下载', 'DOWNLOAD', 'operation_log_type', NULL, 'pink', '1', 'AGrand', 1697444092166, 'AGrand', 1697444104048, '下载DOWNLOAD');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (33, 1, '激活', 'Activation', 'operation_log_type', NULL, 'blue ', '1', 'AGrand', 1697444134968, '', 0, 'Activation激活');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (34, 2, '新增', 'ADD', 'operation_log_type', NULL, 'cyan', '1', 'AGrand', 1697444151864, 'AGrand', 1697444156095, 'ADD');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (35, 3, '其他', 'AUTO', 'operation_log_type', NULL, 'gold', '1', 'AGrand', 1697444199990, '', 0, 'AUTO');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (36, 4, '回退', 'BACK', 'operation_log_type', NULL, 'blue ', '1', 'AGrand', 1697444219836, '', 0, 'BACK');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (37, 5, '删除', 'DELETE', 'operation_log_type', NULL, 'red', '1', 'AGrand', 1697444242927, '', 0, '删除DELETE');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (38, 6, '分配', 'Distribute', 'operation_log_type', NULL, 'yellow', '1', 'AGrand', 1697444267174, '', 0, 'Distribute');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (39, 7, '导出', 'EXPORT', 'operation_log_type', NULL, 'green', '1', 'AGrand', 1697444289982, '', 0, 'EXPORT');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (40, 8, '查询', 'SELECT', 'operation_log_type', NULL, 'gold', '1', 'AGrand', 1697444311262, '', 0, 'SELECT');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (41, 9, '设置', 'SET', 'operation_log_type', NULL, NULL, '1', 'AGrand', 1697444336102, '', 0, 'SET');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (42, 10, '更新', 'UPDATE', 'operation_log_type', NULL, 'magenta', '1', 'AGrand', 1697444358599, '', 0, 'UPDATE');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (43, 11, '上传', 'UPLOAD', 'operation_log_type', NULL, 'yellow', '1', 'AGrand', 1697444378660, '', 0, 'UPLOAD');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (44, 12, '查看', 'View', 'operation_log_type', NULL, 'purple', '1', 'AGrand', 1697444400940, 'AGrand', 1697444414022, 'View');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (45, 0, '历史告警', '0', 'alarm_status', NULL, 'orange', '1', 'AGrand', 1697444554302, '', 0, '历史告警');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (46, 1, '活动告警', '1', 'alarm_status', NULL, 'pink', '1', 'AGrand', 1697444569120, '', 0, '活动告警');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (47, 0, '登录', '0', 'security_log_type', NULL, NULL, '1', 'AGrand', 1697444719117, '', 0, 'Login');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (48, 1, '登出', '1', 'security_log_type', NULL, 'cyan', '1', 'AGrand', 1697444738749, 'AGrand', 1697444742784, 'Logout');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (49, 2, '新增', '2', 'security_log_type', NULL, 'green', '1', 'AGrand', 1697444763116, 'AGrand', 1697444766525, 'Add');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (50, 3, '更新', '3', 'security_log_type', NULL, 'lime', '1', 'AGrand', 1697444786405, 'AGrand', 1697444795272, 'Update');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (51, 4, '删除', '4', 'security_log_type', NULL, NULL, '1', 'AGrand', 1697444809133, '', 0, 'Delete');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (52, 5, '锁定', '5', 'security_log_type', NULL, NULL, '1', 'AGrand', 1697444827514, 'AGrand', 1697444838485, 'Lock');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (53, 6, '解锁', '6', 'security_log_type', NULL, 'gold', '1', 'AGrand', 1697444882860, '', 0, 'Unlock');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (54, 7, '重置', '7', 'security_log_type', NULL, 'cyan', '1', 'AGrand', 1697444906929, '', 0, 'Restart');
INSERT INTO `omc_db`.`sys_dict_data` (`dict_code`, `dict_sort`, `dict_label`, `dict_value`, `dict_type`, `tag_class`, `tag_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (55, 8, '停用', '8', 'security_log_type', NULL, 'blue ', '1', 'AGrand', 1697444945199, 'AGrand', 1697445915268, 'Stop');
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '未知', `dict_value` = '0', `dict_type` = 'sys_user_sex', `tag_class` = '', `tag_type` = '', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079577, `update_by` = '', `update_time` = 0, `remark` = '性别未知' WHERE `dict_code` = 1;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '', `dict_value` = '1', `dict_type` = 'sys_user_sex', `tag_class` = '', `tag_type` = '', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079583, `update_by` = '', `update_time` = 0, `remark` = '性别男' WHERE `dict_code` = 2;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 3, `dict_label` = '', `dict_value` = '2', `dict_type` = 'sys_user_sex', `tag_class` = '', `tag_type` = '', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079585, `update_by` = '', `update_time` = 0, `remark` = '性别女' WHERE `dict_code` = 3;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '显示', `dict_value` = '1', `dict_type` = 'sys_show_hide', `tag_class` = '', `tag_type` = 'success', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079587, `update_by` = '', `update_time` = 0, `remark` = '显示菜单' WHERE `dict_code` = 4;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '隐藏', `dict_value` = '0', `dict_type` = 'sys_show_hide', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079589, `update_by` = '', `update_time` = 0, `remark` = '隐藏菜单' WHERE `dict_code` = 5;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '正常', `dict_value` = '1', `dict_type` = 'sys_normal_disable', `tag_class` = '', `tag_type` = 'success', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079591, `update_by` = '', `update_time` = 0, `remark` = '正常状态' WHERE `dict_code` = 6;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '停用', `dict_value` = '0', `dict_type` = 'sys_normal_disable', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079593, `update_by` = '', `update_time` = 0, `remark` = '停用状态' WHERE `dict_code` = 7;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '正常', `dict_value` = '1', `dict_type` = 'sys_job_status', `tag_class` = '', `tag_type` = 'success', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079595, `update_by` = '', `update_time` = 0, `remark` = '正常状态' WHERE `dict_code` = 8;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '暂停', `dict_value` = '0', `dict_type` = 'sys_job_status', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079597, `update_by` = '', `update_time` = 0, `remark` = '停用状态' WHERE `dict_code` = 9;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '默认', `dict_value` = 'DEFAULT', `dict_type` = 'sys_job_group', `tag_class` = '', `tag_type` = '', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079599, `update_by` = '', `update_time` = 0, `remark` = '默认分组' WHERE `dict_code` = 10;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '系统', `dict_value` = 'SYSTEM', `dict_type` = 'sys_job_group', `tag_class` = '', `tag_type` = '', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079601, `update_by` = '', `update_time` = 0, `remark` = '系统分组' WHERE `dict_code` = 11;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '', `dict_value` = 'Y', `dict_type` = 'sys_yes_no', `tag_class` = '', `tag_type` = 'processing', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079602, `update_by` = '', `update_time` = 0, `remark` = '系统默认是' WHERE `dict_code` = 12;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '', `dict_value` = 'N', `dict_type` = 'sys_yes_no', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079604, `update_by` = '', `update_time` = 0, `remark` = '系统默认否' WHERE `dict_code` = 13;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '通知', `dict_value` = '1', `dict_type` = 'sys_notice_type', `tag_class` = '', `tag_type` = 'warning', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079606, `update_by` = '', `update_time` = 0, `remark` = '通知' WHERE `dict_code` = 14;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '公告', `dict_value` = '2', `dict_type` = 'sys_notice_type', `tag_class` = '', `tag_type` = 'processing', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079608, `update_by` = '', `update_time` = 0, `remark` = '公告' WHERE `dict_code` = 15;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '正常', `dict_value` = '1', `dict_type` = 'sys_notice_status', `tag_class` = '', `tag_type` = 'success', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079610, `update_by` = '', `update_time` = 0, `remark` = '正常状态' WHERE `dict_code` = 16;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '关闭', `dict_value` = '0', `dict_type` = 'sys_notice_status', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079612, `update_by` = '', `update_time` = 0, `remark` = '关闭状态' WHERE `dict_code` = 17;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 99, `dict_label` = '其他', `dict_value` = '0', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'processing', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079613, `update_by` = '', `update_time` = 0, `remark` = '其他操作' WHERE `dict_code` = 18;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '新增', `dict_value` = '1', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'processing', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079615, `update_by` = '', `update_time` = 0, `remark` = '新增操作' WHERE `dict_code` = 19;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '修改', `dict_value` = '2', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'processing', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079618, `update_by` = '', `update_time` = 0, `remark` = '修改操作' WHERE `dict_code` = 20;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 3, `dict_label` = '删除', `dict_value` = '3', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079619, `update_by` = '', `update_time` = 0, `remark` = '删除操作' WHERE `dict_code` = 21;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 4, `dict_label` = '授权', `dict_value` = '4', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'success', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079621, `update_by` = '', `update_time` = 0, `remark` = '授权操作' WHERE `dict_code` = 22;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 5, `dict_label` = '导出', `dict_value` = '5', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'warning', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079623, `update_by` = '', `update_time` = 0, `remark` = '导出操作' WHERE `dict_code` = 23;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 6, `dict_label` = '导入', `dict_value` = '6', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'warning', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079625, `update_by` = '', `update_time` = 0, `remark` = '导入操作' WHERE `dict_code` = 24;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 7, `dict_label` = '强退', `dict_value` = '7', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079627, `update_by` = '', `update_time` = 0, `remark` = '强退操作' WHERE `dict_code` = 25;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 8, `dict_label` = '清空', `dict_value` = '8', `dict_type` = 'sys_oper_type', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079630, `update_by` = '', `update_time` = 0, `remark` = '清空操作' WHERE `dict_code` = 26;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 1, `dict_label` = '成功', `dict_value` = '1', `dict_type` = 'sys_common_status', `tag_class` = '', `tag_type` = 'success', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079632, `update_by` = '', `update_time` = 0, `remark` = '正常状态' WHERE `dict_code` = 27;
UPDATE `omc_db`.`sys_dict_data` SET `dict_sort` = 2, `dict_label` = '失败', `dict_value` = '0', `dict_type` = 'sys_common_status', `tag_class` = '', `tag_type` = 'error', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079633, `update_by` = '', `update_time` = 0, `remark` = '停用状态' WHERE `dict_code` = 28;
INSERT INTO `omc_db`.`sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (101, '操作日志类型', 'operation_log_type', '1', 'AGrand', 1697443982999, '', 0, '操作日志类型');
INSERT INTO `omc_db`.`sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (102, '告警日志类型', 'alarm_status', '1', 'AGrand', 1697444486172, '', 0, 'alarm_status');
INSERT INTO `omc_db`.`sys_dict_type` (`dict_id`, `dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (103, '安全日志类型', 'security_log_type', '1', 'AGrand', 1697444692163, '', 0, '安全日志类型');
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '用户性别', `dict_type` = 'sys_user_sex', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079540, `update_by` = '', `update_time` = 0, `remark` = '用户性别列表' WHERE `dict_id` = 1;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '菜单状态', `dict_type` = 'sys_show_hide', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079543, `update_by` = '', `update_time` = 0, `remark` = '菜单状态列表' WHERE `dict_id` = 2;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '系统开关', `dict_type` = 'sys_normal_disable', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079544, `update_by` = '', `update_time` = 0, `remark` = '系统开关列表' WHERE `dict_id` = 3;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '任务状态', `dict_type` = 'sys_job_status', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079546, `update_by` = '', `update_time` = 0, `remark` = '任务状态列表' WHERE `dict_id` = 4;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '任务分组', `dict_type` = 'sys_job_group', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079548, `update_by` = '', `update_time` = 0, `remark` = '任务分组列表' WHERE `dict_id` = 5;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '系统是否', `dict_type` = 'sys_yes_no', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079550, `update_by` = '', `update_time` = 0, `remark` = '系统是否列表' WHERE `dict_id` = 6;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '通知类型', `dict_type` = 'sys_notice_type', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079552, `update_by` = '', `update_time` = 0, `remark` = '通知类型列表' WHERE `dict_id` = 7;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '通知状态', `dict_type` = 'sys_notice_status', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079554, `update_by` = '', `update_time` = 0, `remark` = '通知状态列表' WHERE `dict_id` = 8;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '操作类型', `dict_type` = 'sys_oper_type', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079556, `update_by` = '', `update_time` = 0, `remark` = '操作类型列表' WHERE `dict_id` = 9;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '系统状态', `dict_type` = 'sys_common_status', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693908079557, `update_by` = '', `update_time` = 0, `remark` = '登录状态列表' WHERE `dict_id` = 10;
UPDATE `omc_db`.`sys_dict_type` SET `dict_name` = '跟踪类型', `dict_type` = 'trace_type', `status` = '1', `create_by` = 'AGrand', `create_time` = 1693967758884, `update_by` = 'AGrand', `update_time` = 1697442483216, `remark` = '跟踪类型' WHERE `dict_id` = 100;
INSERT INTO `omc_db`.`sys_job` (`job_id`, `job_name`, `job_group`, `invoke_target`, `target_params`, `cron_expression`, `misfire_policy`, `concurrent`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, '触发执行', 'SYSTEM', 'simple', '{\"t\":10}', '0/10 * * * * ?', '3', '0', '0', 'AGrand', 1697091151523, '', 0, '');
INSERT INTO `omc_db`.`sys_job` (`job_id`, `job_name`, `job_group`, `invoke_target`, `target_params`, `cron_expression`, `misfire_policy`, `concurrent`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '缓慢执行', 'SYSTEM', 'foo', '{\"t\":15}', '0/15 * * * * ?', '3', '0', '0', 'AGrand', 1697091151526, '', 0, '');
INSERT INTO `omc_db`.`sys_job` (`job_id`, `job_name`, `job_group`, `invoke_target`, `target_params`, `cron_expression`, `misfire_policy`, `concurrent`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, '异常执行', 'SYSTEM', 'bar', '{\"t\":20}', '0/20 * * * * ?', '3', '0', '0', 'AGrand', 1697091151529, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, '系统管理', 0, 1, 'system', '', '1', '1', 'D', '1', '1', '', 'icon-wenjian', 'AGrand', 1694599799316, '', 0, '系统管理目录');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, '系统监控', 0, 2, 'monitor', '', '1', '1', 'D', '1', '1', '', 'icon-wenjian', 'AGrand', 1694599799345, '', 0, '系统监控目录');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, '系统工具', 0, 3, 'tool', '', '1', '1', 'D', '0', '0', '', 'icon-wenjian', 'AGrand', 1694599799372, 'AGrand', 1697428470614, '系统工具目录');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (4, '配置管理', 0, 4, 'configManage', '', '1', '0', 'D', '1', '1', '', 'icon-wenjian', 'AGrand', 1694599799396, 'AGrand', 1694600252468, '配置管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (100, '用户管理', 1, 1, 'user', 'system/user/index', '1', '1', 'M', '1', '1', 'system:user:list', '#', 'AGrand', 1697419844499, '', 0, '用户管理菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (101, '角色管理', 1, 2, 'role', 'system/role/index', '1', '1', 'M', '1', '1', 'system:role:list', '#', 'AGrand', 1697419844502, '', 0, '角色管理菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (102, '分配角色', 1, 3, 'role/inline/auth-user/:roleId', 'system/role/auth-user', '1', '1', 'M', '0', '1', 'system:role:auth', '#', 'AGrand', 1697419844503, '', 0, '分配角色内嵌隐藏菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (103, '菜单管理', 1, 4, 'menu', 'system/menu/index', '1', '1', 'M', '1', '1', 'system:menu:list', '#', 'AGrand', 1697419844505, '', 0, '菜单管理菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (104, '部门管理', 1, 5, 'dept', 'system/dept/index', '1', '1', 'M', '1', '1', 'system:dept:list', '#', 'AGrand', 1697419844506, '', 0, '部门管理菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (105, '岗位管理', 1, 6, 'post', 'system/post/index', '1', '1', 'M', '1', '1', 'system:post:list', '#', 'AGrand', 1697419844507, '', 0, '岗位管理菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (106, '字典管理', 1, 7, 'dict', 'system/dict/index', '1', '1', 'M', '1', '1', 'system:dict:list', '#', 'AGrand', 1697419844508, '', 0, '字典管理菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (107, '字典数据', 1, 8, 'dict/inline/data/:dictId', 'system/dict/data', '1', '1', 'M', '0', '1', 'system:dict:data', '#', 'AGrand', 1697419844510, '', 0, '字典数据内嵌隐藏菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (108, '参数设置', 1, 9, 'config', 'system/config/index', '1', '1', 'M', '1', '1', 'system:config:list', '#', 'AGrand', 1697419844516, '', 0, '参数设置菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (109, '通知公告', 1, 10, 'notice', 'system/notice/index', '1', '1', 'M', '0', '0', 'system:notice:list', '#', 'AGrand', 1697419844517, '', 0, '通知公告菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (111, '系统日志', 1, 11, 'log', '', '1', '1', 'D', '1', '1', '', '#', 'AGrand', 1697419844518, '', 0, '日志管理菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (112, '系统信息', 2, 1, 'system-info', 'monitor/system/info', '1', '1', 'M', '1', '1', 'monitor:system:info', '#', 'AGrand', 1697419844520, '', 0, '系统信息菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (113, '缓存信息', 2, 2, 'cache-info', 'monitor/cache/info', '1', '1', 'M', '1', '1', 'monitor:cache:info', '#', 'AGrand', 1697419844521, '', 0, '缓存信息菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (114, '缓存管理', 2, 3, 'cache', 'monitor/cache/index', '1', '1', 'M', '1', '1', 'monitor:cache:list', '#', 'AGrand', 1697419844522, '', 0, '缓存列表菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (115, '在线用户', 2, 4, 'online', 'monitor/online/index', '1', '1', 'M', '1', '1', 'monitor:online:list', '#', 'AGrand', 1697419844524, '', 0, '在线用户菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (116, '调度任务', 2, 5, 'job', 'monitor/job/index', '1', '1', 'M', '1', '1', 'monitor:job:list', '#', 'AGrand', 1697419844525, '', 0, '调度任务菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (117, '调度日志', 2, 6, 'job/inline/log/:jobId', 'monitor/job/log', '1', '1', 'M', '0', '1', 'monitor:job:log', '#', 'AGrand', 1697419844527, '', 0, '调度日志内嵌隐藏菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (118, '帮助文档', 3, 1, 'help', 'tool/help/index', '1', '1', 'M', '0', '0', 'monitor:help:list', '#', 'AGrand', 1697419844528, '', 0, '系统接口菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (500, '操作日志', 111, 1, 'operate', 'system/log/operate/index', '1', '1', 'M', '1', '1', 'system:log:operate:list', '#', 'AGrand', 1697419793720, '', 0, '操作日志菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (501, '登录日志', 111, 2, 'login', 'system/log/login/index', '1', '1', 'M', '1', '1', 'system:log:login:list', '#', 'AGrand', 1697419793722, '', 0, '登录日志菜单');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1000, '用户查询', 100, 1, '', '', '1', '1', 'B', '1', '1', 'system:user:query', '#', 'AGrand', 1694599800500, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1001, '用户新增', 100, 2, '', '', '1', '1', 'B', '1', '1', 'system:user:add', '#', 'AGrand', 1694599800538, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1002, '用户修改', 100, 3, '', '', '1', '1', 'B', '1', '1', 'system:user:edit', '#', 'AGrand', 1694599800587, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1003, '用户删除', 100, 4, '', '', '1', '1', 'B', '1', '1', 'system:user:remove', '#', 'AGrand', 1694599800616, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1004, '用户导出', 100, 5, '', '', '1', '1', 'B', '1', '1', 'system:user:export', '#', 'AGrand', 1694599800642, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1005, '用户导入', 100, 6, '', '', '1', '1', 'B', '1', '1', 'system:user:import', '#', 'AGrand', 1694599800677, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1006, '重置密码', 100, 7, '', '', '1', '1', 'B', '1', '1', 'system:user:resetPwd', '#', 'AGrand', 1694599800766, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1007, '角色查询', 101, 1, '', '', '1', '1', 'B', '1', '1', 'system:role:query', '#', 'AGrand', 1694599800806, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1008, '角色新增', 101, 2, '', '', '1', '1', 'B', '1', '1', 'system:role:add', '#', 'AGrand', 1694599800845, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1009, '角色修改', 101, 3, '', '', '1', '1', 'B', '1', '1', 'system:role:edit', '#', 'AGrand', 1694599800880, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1010, '角色删除', 101, 4, '', '', '1', '1', 'B', '1', '1', 'system:role:remove', '#', 'AGrand', 1694599800976, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1011, '角色导出', 101, 5, '', '', '1', '1', 'B', '1', '1', 'system:role:export', '#', 'AGrand', 1694599801089, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1012, '菜单查询', 103, 1, '', '', '1', '1', 'B', '1', '1', 'system:menu:query', '#', 'AGrand', 1694599801135, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1013, '菜单新增', 103, 2, '', '', '1', '1', 'B', '1', '1', 'system:menu:add', '#', 'AGrand', 1694599801165, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1014, '菜单修改', 103, 3, '', '', '1', '1', 'B', '1', '1', 'system:menu:edit', '#', 'AGrand', 1694599801192, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1015, '菜单删除', 103, 4, '', '', '1', '1', 'B', '1', '1', 'system:menu:remove', '#', 'AGrand', 1694599801246, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1016, '部门查询', 104, 1, '', '', '1', '1', 'B', '1', '1', 'system:dept:query', '#', 'AGrand', 1694599801305, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1017, '部门新增', 104, 2, '', '', '1', '1', 'B', '1', '1', 'system:dept:add', '#', 'AGrand', 1694599801342, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1018, '部门修改', 104, 3, '', '', '1', '1', 'B', '1', '1', 'system:dept:edit', '#', 'AGrand', 1694599801432, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1019, '部门删除', 104, 4, '', '', '1', '1', 'B', '1', '1', 'system:dept:remove', '#', 'AGrand', 1694599801463, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1020, '岗位查询', 105, 1, '', '', '1', '1', 'B', '1', '1', 'system:post:query', '#', 'AGrand', 1694599801496, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1021, '岗位新增', 105, 2, '', '', '1', '1', 'B', '1', '1', 'system:post:add', '#', 'AGrand', 1694599801532, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1022, '岗位修改', 105, 3, '', '', '1', '1', 'B', '1', '1', 'system:post:edit', '#', 'AGrand', 1694599801581, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1023, '岗位删除', 105, 4, '', '', '1', '1', 'B', '1', '1', 'system:post:remove', '#', 'AGrand', 1694599801619, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1024, '岗位导出', 105, 5, '', '', '1', '1', 'B', '1', '1', 'system:post:export', '#', 'AGrand', 1694599801648, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1025, '字典查询', 106, 1, '#', '', '1', '1', 'B', '1', '1', 'system:dict:query', '#', 'AGrand', 1694599801673, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1026, '字典新增', 106, 2, '#', '', '1', '1', 'B', '1', '1', 'system:dict:add', '#', 'AGrand', 1694599801711, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1027, '字典修改', 106, 3, '#', '', '1', '1', 'B', '1', '1', 'system:dict:edit', '#', 'AGrand', 1694599801741, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1028, '字典删除', 106, 4, '#', '', '1', '1', 'B', '1', '1', 'system:dict:remove', '#', 'AGrand', 1694599801772, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1029, '字典导出', 106, 5, '#', '', '1', '1', 'B', '1', '1', 'system:dict:export', '#', 'AGrand', 1694599801801, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1030, '参数查询', 108, 1, '#', '', '1', '1', 'B', '1', '1', 'system:config:query', '#', 'AGrand', 1694599801828, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1031, '参数新增', 108, 2, '#', '', '1', '1', 'B', '1', '1', 'system:config:add', '#', 'AGrand', 1694599801855, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1032, '参数修改', 108, 3, '#', '', '1', '1', 'B', '1', '1', 'system:config:edit', '#', 'AGrand', 1694599801888, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1033, '参数删除', 108, 4, '#', '', '1', '1', 'B', '1', '1', 'system:config:remove', '#', 'AGrand', 1694599801920, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1034, '参数导出', 108, 5, '#', '', '1', '1', 'B', '1', '1', 'system:config:export', '#', 'AGrand', 1694599801963, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1035, '公告查询', 109, 1, '#', '', '1', '1', 'B', '1', '1', 'system:notice:query', '#', 'AGrand', 1694599801991, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1036, '公告新增', 109, 2, '#', '', '1', '1', 'B', '1', '1', 'system:notice:add', '#', 'AGrand', 1694599802024, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1037, '公告修改', 109, 3, '#', '', '1', '1', 'B', '1', '1', 'system:notice:edit', '#', 'AGrand', 1694599802055, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1038, '公告删除', 109, 4, '#', '', '1', '1', 'B', '1', '1', 'system:notice:remove', '#', 'AGrand', 1694599802079, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1039, '操作查询', 500, 1, '#', '', '1', '1', 'B', '1', '1', 'system:log:operate:query', '#', 'AGrand', 1697419763809, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1040, '操作删除', 500, 2, '#', '', '1', '1', 'B', '1', '1', 'system:log:operate:remove', '#', 'AGrand', 1697419763812, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1041, '日志导出', 500, 3, '#', '', '1', '1', 'B', '1', '1', 'system:log:operate:export', '#', 'AGrand', 1697419763813, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1042, '登录查询', 501, 1, '#', '', '1', '1', 'B', '1', '1', 'system:log:login:query', '#', 'AGrand', 1697419763815, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1043, '登录删除', 501, 2, '#', '', '1', '1', 'B', '1', '1', 'system:log:login:remove', '#', 'AGrand', 1697419763816, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1044, '日志导出', 501, 3, '#', '', '1', '1', 'B', '1', '1', 'system:log:login:export', '#', 'AGrand', 1697419763817, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1045, '账户解锁', 501, 4, '#', '', '1', '1', 'B', '1', '1', 'system:log:login:unlock', '#', 'AGrand', 1697419763818, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1046, '缓存查询', 114, 1, '#', '', '1', '1', 'B', '1', '1', 'monitor:cache:query', '#', 'AGrand', 1694599802363, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1047, '缓存删除', 114, 2, '#', '', '1', '1', 'B', '1', '1', 'monitor:cache:remove', '#', 'AGrand', 1694599802393, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1048, '在线查询', 115, 1, '#', '', '1', '1', 'B', '1', '1', 'monitor:online:query', '#', 'AGrand', 1694599802422, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1049, '批量强退', 115, 2, '#', '', '1', '1', 'B', '1', '1', 'monitor:online:batchLogout', '#', 'AGrand', 1694599802453, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1050, '单条强退', 115, 3, '#', '', '1', '1', 'B', '1', '1', 'monitor:online:forceLogout', '#', 'AGrand', 1694599802483, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1051, '任务查询', 116, 1, '#', '', '1', '1', 'B', '1', '1', 'monitor:job:query', '#', 'AGrand', 1694599802551, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1052, '任务新增', 116, 2, '#', '', '1', '1', 'B', '1', '1', 'monitor:job:add', '#', 'AGrand', 1694599802581, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1053, '任务修改', 116, 3, '#', '', '1', '1', 'B', '1', '1', 'monitor:job:edit', '#', 'AGrand', 1694599802605, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1054, '任务删除', 116, 4, '#', '', '1', '1', 'B', '1', '1', 'monitor:job:remove', '#', 'AGrand', 1694599802641, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1055, '状态修改', 116, 5, '#', '', '1', '1', 'B', '1', '1', 'monitor:job:changeStatus', '#', 'AGrand', 1694599802675, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1056, '任务导出', 116, 6, '#', '', '1', '1', 'B', '1', '1', 'monitor:job:export', '#', 'AGrand', 1694599802706, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2076, '参数配置', 4, 2, 'configParam', 'configManage/configParam/index', '1', '0', 'M', '1', '1', 'configManage:configParam:index', 'icon-piliang', 'AGrand', 1694656727697, 'AGrand', 1694664268131, '参数配置');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2077, '信令抓包', 10, 3, '', 'page/trace/pcap.html', '1', '0', 'M', '1', '1', 'trace:pcap', '#', 'AGrand', 1694834069721, '', 0, 'tcpdump抓包pcap文件');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2078, '备份管理', 4, 3, 'backupManage', 'configManage/backupManage/index', '1', '0', 'M', '1', '1', 'configManage:backupManage:index', 'icon-soutubiao', 'AGrand', 1695017673281, '', 0, '备份管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2079, '软件管理', 4, 4, 'softwareManage', 'configManage/softwareManage/index', '1', '0', 'M', '1', '1', 'configManage:softwareManage:index', 'icon-huidingbu', 'AGrand', 1695024225185, '', 0, '软件管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2080, 'IMS在线用户', 5, 4, 'ims', 'neUser/ims/index', '1', '0', 'M', '1', '1', 'neUser:ims:index', 'icon-xiangmuchengyuan', 'AGrand', 1695090024184, '', 0, 'IMS在线用户');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2081, 'UE在线信息', 5, 6, 'ue', 'neUser/ue/index', '1', '0', 'M', '1', '1', 'neUser:ue:index', 'icon-xiangmuchengyuan', 'AGrand', 1695090083747, '', 0, 'UE在线信息');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2082, '5G基站信息', 5, 7, 'base5G', 'neUser/base5G/index', '1', '0', 'M', '1', '1', 'neUser:base5G:index', 'icon-paixu', 'AGrand', 1695090192856, 'AGrand', 1695349622025, '5G基站信息');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2083, '跟踪管理', 0, 6, 'traceManage', '', '1', '0', 'D', '1', '1', '', 'icon-wenjian', 'AGrand', 1695379608530, 'AGrand', 1695696620937, '跟踪管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2084, '跟踪任务', 2083, 1, 'task', 'traceManage/task/index', '1', '0', 'M', '1', '1', 'traceManage:task:index', 'icon-chexiao', 'AGrand', 1695379860503, 'AGrand', 1695696631196, '跟踪任务');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2085, '信令分析', 2083, 1, 'analysis', 'traceManage/analysis/index', '1', '0', 'M', '1', '1', 'traceManage:analysis:index', 'icon-gongnengjieshao', 'AGrand', 1695379915764, 'AGrand', 1695696640147, '信令分析');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2086, '信令抓包', 2083, 3, 'pcap', 'traceManage/pcap/index', '1', '0', 'M', '1', '1', 'traceManage:pcap:index', 'icon-soutubiao', 'AGrand', 1695380002462, 'AGrand', 1695696648928, '信令抓包');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2087, '故障管理', 0, 7, 'faultManage', '', '1', '0', 'D', '1', '1', '', 'icon-wenjian', 'AGrand', 1695638038379, '', 0, '故障管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2088, '活动告警', 2087, 1, 'active-alarm', 'faultManage/active-alarm/index', '1', '0', 'M', '1', '1', 'faultManage:active-alarm:index', 'icon-wenjian', 'AGrand', 1695639086834, 'AGrand', 1695639108038, '活动告警');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2089, '日志管理', 0, 8, 'logManage', '', '1', '0', 'D', '1', '1', '', 'icon-wenjian', 'AGrand', 1695697146107, '', 0, '日志管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2090, '操作日志', 2089, 1, 'operation', 'logManage/operation/index', '1', '0', 'M', '1', '1', 'logManage:operation:index', 'icon-fuzhidaima', 'AGrand', 1695697237355, '', 0, '操作日志');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2091, '操作MML日志', 2089, 2, 'mml', 'logManage/mml/index', '1', '0', 'M', '1', '1', 'logManage:mml:index', 'icon-wocanyu', 'AGrand', 1695697317483, '', 0, '操作MML日志');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2092, '告警日志', 2089, 3, 'alarm', 'logManage/alarm/index', '1', '0', 'M', '1', '1', 'logManage:alarm:index', 'icon-fuzhidaima', 'AGrand', 1695697384204, '', 0, '告警日志');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2093, '安全日志', 2089, 6, 'security', 'logManage/security/index', '1', '0', 'M', '1', '1', 'logManage/security/index', 'icon-gongnengjieshao', 'AGrand', 1695697433544, '', 0, '安全日志');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2094, '告警前转日志', 2089, 7, 'forwarding', 'logManage/forwarding/index', '1', '0', 'M', '1', '1', 'logManage:forwarding:index', 'icon-huizhiguize', 'AGrand', 1695697548801, '', 0, '告警前转日志');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2095, '日志设置', 2089, 15, 'logSet', 'logManage/logSet/index', '1', '0', 'M', '1', '1', 'logManage:logSet:index', 'icon-you', 'AGrand', 1695697636698, '', 0, '日志设置');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2096, '用户会话', 2, 10, 'session', 'monitor/session/index', '1', '0', 'M', '1', '1', 'monitor:session:index', 'icon-gerenzhanghu', 'AGrand', 1695698108049, '', 0, '在线用户-旧用户在线');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2097, '历史告警', 2087, 0, 'history-alarm', 'faultManage/history-alarm/index', '1', '0', 'M', '1', '1', 'faultManage/history-alarm/index', 'icon-huizhiguize', 'AGrand', 1696665696950, 'AGrand', 1696665716447, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2098, '故障通用设置', 2087, 0, 'fault-setting', 'faultManage/fault-setting/index', '1', '0', 'M', '1', '1', 'faultManage/fault-setting/index', 'icon-gonggaodayi', 'AGrand', 1696668601941, '', 0, '');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2099, '性能管理', 0, 9, 'perfManage', '', '1', '0', 'D', '1', '1', '', 'icon-soutubiao', 'AGrand', 1696923032637, 'AGrand', 1696923040267, '性能管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2100, '任务管理', 2099, 1, 'taskManage', 'perfManage/taskManage/index', '1', '0', 'M', '1', '1', 'perfManage:taskManage:index', 'icon-wofaqi', 'AGrand', 1696923215654, '', 0, '任务管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2101, '性能数据', 2099, 2, 'perfData', 'perfManage/perfData/index', '1', '0', 'M', '1', '1', 'perfManage:perfData:index', 'icon-soutubiao', 'AGrand', 1696923311093, '', 0, '性能数据');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2102, '性能报表', 2099, 3, 'perfReport', 'perfManage/perfReport/index', '1', '0', 'M', '1', '1', 'perfManage:perfReport:index', 'icon-gonggaodayi', 'AGrand', 1696923390542, '', 0, '性能报表');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2103, '性能门限', 2099, 4, 'perfThreshold', 'perfManage/perfThreshold/index', '1', '0', 'M', '1', '1', 'perfManage:perfThreshold:index', 'icon-zhuanrang', 'AGrand', 1696923461901, '', 0, '性能门限');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2104, '黄金指标', 2099, 5, 'goldTarget', 'perfManage/goldTarget/index', '1', '0', 'M', '1', '1', 'perfManage:goldTarget:index', 'icon-soutubiao', 'AGrand', 1696923551120, '', 0, '黄金指标');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2105, '自定义指标', 2099, 6, 'customTarget', 'perfManage/customTarget/index', '1', '0', 'M', '0', '0', 'perfManage:customTarget:index', 'icon-fanhui1', 'AGrand', 1696923639489, 'AGrand', 1697077437451, '自定义指标');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2106, '性能通用设置', 2099, 7, 'perfSet', 'perfManage/perfSet/index', '1', '0', 'M', '1', '1', 'perfManage:perfSet:index', 'icon-gonggao', 'AGrand', 1696923787076, '', 0, '性能通用设置');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2107, 'MML管理', 0, 10, 'mmlManage', '', '1', '0', 'D', '1', '1', '', 'icon-wenjian', 'AGrand', 1696924634152, 'AGrand', 1697428173406, 'MML管理');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2108, '网元操作', 2107, 1, 'neOperate', 'mmlManage/neOperate/index', '1', '0', 'M', '1', '1', 'mmlManage:neOperate:index', 'icon-huizhiguize', 'AGrand', 1696924784723, '', 0, '网元操作');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2109, 'UDM操作', 2107, 2, 'udmOperate', 'mmlManage/udmOperate/index', '1', '0', 'M', '1', '1', 'mmlManage:udmOperate:index', 'icon-gonggaodayi', 'AGrand', 1696924889906, 'AGrand', 1697005003783, '用户数据指定网元UDM');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2110, 'MML设置', 2107, 4, 'mmlSet', 'mmlManage/mmlSet/index', '1', '0', 'M', '1', '1', 'mmlManage:mmlSet:index', 'icon-wofaqi', 'AGrand', 1696924994794, '', 0, 'MML设置');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2111, 'OMC操作', 2107, 3, 'omcOperate', 'mmlManage/omcOperate/index', '1', '0', 'M', '1', '1', 'mmlManage:omcOperate:index', 'icon-huizhiguize', 'AGrand', 1696925543196, '', 0, 'OMC操作');
INSERT INTO `omc_db`.`sys_menu` (`menu_id`, `menu_name`, `parent_id`, `menu_sort`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2112, '许可证管理', 4, 5, 'license', 'configManage/license/index', '1', '0', 'M', '1', '1', 'configManage/license/index', 'icon-shang', 'AGrand', 1696989886481, 'AGrand', 1697013380449, 'License管理');
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '用户信息', `parent_id` = 0, `menu_sort` = 5, `path` = 'neUser', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '1', `status` = '1', `perms` = '', `icon` = 'icon-wenjian', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = 'AGrand', `update_time` = 1695089607243, `remark` = '网元用户信息' WHERE `menu_id` = 5;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '故障管理', `parent_id` = 0, `menu_sort` = 50, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = 'AGrand', `update_time` = 1694679965315, `remark` = '故障管理' WHERE `menu_id` = 6;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '配置管理', `parent_id` = 0, `menu_sort` = 550, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = '', `update_time` = 0, `remark` = '配置管理' WHERE `menu_id` = 7;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '性能管理', `parent_id` = 0, `menu_sort` = 551, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = '', `update_time` = 0, `remark` = '性能管理' WHERE `menu_id` = 8;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '操作维护', `parent_id` = 0, `menu_sort` = 552, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = '', `update_time` = 0, `remark` = '操作维护' WHERE `menu_id` = 9;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '跟踪管理', `parent_id` = 0, `menu_sort` = 553, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = 'AGrand', `update_time` = 1694079481395, `remark` = '跟踪管理' WHERE `menu_id` = 10;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '日志管理', `parent_id` = 0, `menu_sort` = 554, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = '', `update_time` = 0, `remark` = '日志管理' WHERE `menu_id` = 11;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '安全管理', `parent_id` = 0, `menu_sort` = 555, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = '', `update_time` = 0, `remark` = '安全管理' WHERE `menu_id` = 12;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '北向管理', `parent_id` = 0, `menu_sort` = 556, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '0', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = 'AGrand', `update_time` = 1693538966603, `remark` = '北向管理' WHERE `menu_id` = 13;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '系统管理', `parent_id` = 0, `menu_sort` = 557, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = '', `update_time` = 0, `remark` = '系统管理' WHERE `menu_id` = 14;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '拓扑管理', `parent_id` = 0, `menu_sort` = 558, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '0', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1693031847898, `update_by` = '', `update_time` = 0, `remark` = '拓扑管理' WHERE `menu_id` = 15;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = 'UDM鉴权用户', `parent_id` = 5, `menu_sort` = 1, `path` = 'auth', `component` = 'neUser/auth/index', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'neUser:auth:index', `icon` = 'icon-xiangmuchengyuan', `create_by` = 'AGrand', `create_time` = 1693452185987, `update_by` = 'AGrand', `update_time` = 1695089843923, `remark` = 'UDM鉴权用户' WHERE `menu_id` = 2009;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = 'UDM签约用户', `parent_id` = 5, `menu_sort` = 2, `path` = 'sub', `component` = 'neUser/sub/index', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'neUser:sub:index', `icon` = 'icon-xiangmuchengyuan', `create_by` = 'AGrand', `create_time` = 1693452241733, `update_by` = 'AGrand', `update_time` = 1695365399746, `remark` = 'UDM签约用户' WHERE `menu_id` = 2010;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '活动告警', `parent_id` = 6, `menu_sort` = 1, `path` = '', `component` = 'page/alarm/alarmListDown.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '活动告警', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693452882261, `update_by` = 'AGrand', `update_time` = 1694679879968, `remark` = '活动告警' WHERE `menu_id` = 2011;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '历史告警', `parent_id` = 6, `menu_sort` = 2, `path` = '', `component` = 'page/alarm/AlarmListHistory.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '历史告警', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693452917746, `update_by` = '', `update_time` = 0, `remark` = '历史告警' WHERE `menu_id` = 2012;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '故障通用设置', `parent_id` = 6, `menu_sort` = 3, `path` = '', `component` = 'page/alarm/alarmInfoConfig.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '故障通用设置', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693452963347, `update_by` = '', `update_time` = 0, `remark` = '故障通用设置' WHERE `menu_id` = 2013;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '定时同步设置', `parent_id` = 6, `menu_sort` = 4, `path` = '', `component` = 'page/alarm/synchronous.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '定时同步设置', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693452983027, `update_by` = 'AGrand', `update_time` = 1693544689303, `remark` = '定时同步设置' WHERE `menu_id` = 2014;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '健康状态检查', `parent_id` = 6, `menu_sort` = 5, `path` = '', `component` = 'page/alarm/healthCheck.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '健康状态检查', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453161355, `update_by` = 'AGrand', `update_time` = 1693544697864, `remark` = '健康状态检查' WHERE `menu_id` = 2015;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '告警前转', `parent_id` = 6, `menu_sort` = 6, `path` = '', `component` = 'page/alarm/alarmForwarding.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '告警前转', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453182346, `update_by` = '', `update_time` = 0, `remark` = '告警前转' WHERE `menu_id` = 2016;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '网元管理', `parent_id` = 7, `menu_sort` = 1, `path` = '', `component` = 'page/nfManage/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '网元管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453229420, `update_by` = '', `update_time` = 0, `remark` = '网元管理' WHERE `menu_id` = 2017;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '参数配置', `parent_id` = 7, `menu_sort` = 2, `path` = '', `component` = 'page/configParam/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '参数配置', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453250426, `update_by` = '', `update_time` = 0, `remark` = '参数配置' WHERE `menu_id` = 2018;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '软件管理', `parent_id` = 7, `menu_sort` = 3, `path` = '', `component` = 'page/softwareManage/softwareManage.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '软件管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453272021, `update_by` = '', `update_time` = 0, `remark` = '软件管理' WHERE `menu_id` = 2019;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '备份管理', `parent_id` = 7, `menu_sort` = 5, `path` = '', `component` = 'page/softwareManage/backupManage.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '备份管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453295606, `update_by` = '', `update_time` = 0, `remark` = '备份管理' WHERE `menu_id` = 2020;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '配置参数设置', `parent_id` = 7, `menu_sort` = 6, `path` = '', `component` = 'page/configParam/configParamSet.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '配置参数设置', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453313510, `update_by` = '', `update_time` = 0, `remark` = '配置参数设置' WHERE `menu_id` = 2021;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '任务管理', `parent_id` = 8, `menu_sort` = 1, `path` = '', `component` = 'page/task/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '任务管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453371550, `update_by` = '', `update_time` = 0, `remark` = '任务管理' WHERE `menu_id` = 2022;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '性能数据', `parent_id` = 8, `menu_sort` = 2, `path` = '', `component` = 'page/repair/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '性能数据', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453391983, `update_by` = '', `update_time` = 0, `remark` = '性能数据' WHERE `menu_id` = 2023;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '性能报表', `parent_id` = 8, `menu_sort` = 3, `path` = '', `component` = 'page/task/perfReport.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '性能报表', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453414547, `update_by` = '', `update_time` = 0, `remark` = '性能报表' WHERE `menu_id` = 2024;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '性能门限', `parent_id` = 8, `menu_sort` = 4, `path` = '', `component` = 'page/task/threshold.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '性能门限', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453436131, `update_by` = '', `update_time` = 0, `remark` = '性能门限' WHERE `menu_id` = 2025;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '黄金指标', `parent_id` = 8, `menu_sort` = 5, `path` = '', `component` = 'page/gold/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '黄金指标', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453458286, `update_by` = '', `update_time` = 0, `remark` = '黄金指标' WHERE `menu_id` = 2026;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '性能通用设置', `parent_id` = 8, `menu_sort` = 7, `path` = '', `component` = 'page/task/perfReportSet.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '性能通用设置', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453477609, `update_by` = '', `update_time` = 0, `remark` = '性能通用设置' WHERE `menu_id` = 2027;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '自定义指标', `parent_id` = 8, `menu_sort` = 8, `path` = '', `component` = 'page/indicators/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '自定义指标', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453497532, `update_by` = '', `update_time` = 0, `remark` = '自定义指标' WHERE `menu_id` = 2028;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '对象模板', `parent_id` = 8, `menu_sort` = 8, `path` = '', `component` = 'page/objectTemplate/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '对象模板', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453519007, `update_by` = 'AGrand', `update_time` = 1693544534649, `remark` = '对象模板' WHERE `menu_id` = 2029;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '自定义测量数据', `parent_id` = 8, `menu_sort` = 9, `path` = '', `component` = 'page/indicators/measuringData.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '自定义测量数据', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453547977, `update_by` = 'AGrand', `update_time` = 1693544544479, `remark` = '自定义测量数据' WHERE `menu_id` = 2030;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '拓扑视图', `parent_id` = 15, `menu_sort` = 1, `path` = '', `component` = 'page/topology/topologyList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '拓扑视图', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453593545, `update_by` = '', `update_time` = 0, `remark` = '拓扑视图' WHERE `menu_id` = 2031;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '系统维护', `parent_id` = 14, `menu_sort` = 1, `path` = '', `component` = 'page/systemManage/systemOperation.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '系统维护', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453620554, `update_by` = '', `update_time` = 0, `remark` = '系统维护' WHERE `menu_id` = 2032;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '稳定性事件列表', `parent_id` = 14, `menu_sort` = 2, `path` = '', `component` = 'page/systemManage/stabilityEvents.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '稳定性事件列表', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453643025, `update_by` = 'AGrand', `update_time` = 1693544384309, `remark` = '稳定性事件列表' WHERE `menu_id` = 2033;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '稳定性统计报告', `parent_id` = 14, `menu_sort` = 3, `path` = '', `component` = 'page/systemManage/stabilityReports.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '稳定性统计报告', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453662988, `update_by` = 'AGrand', `update_time` = 1693544429662, `remark` = '稳定性统计报告' WHERE `menu_id` = 2034;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '系统备份', `parent_id` = 14, `menu_sort` = 4, `path` = '', `component` = 'page/systemManage/systemBackup.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '系统备份', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453682872, `update_by` = 'AGrand', `update_time` = 1693544442726, `remark` = '系统备份' WHERE `menu_id` = 2035;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '系统可扩展', `parent_id` = 14, `menu_sort` = 5, `path` = '', `component` = 'page/systemManage/systemExtended.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '系统可扩展', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453705157, `update_by` = 'AGrand', `update_time` = 1693544457342, `remark` = '\"系统可扩展' WHERE `menu_id` = 2036;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '证书管理', `parent_id` = 14, `menu_sort` = 6, `path` = '', `component` = 'page/systemManage/certificateManage.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '证书管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453726181, `update_by` = 'AGrand', `update_time` = 1693544468109, `remark` = '证书管理' WHERE `menu_id` = 2037;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '北向操作日志', `parent_id` = 13, `menu_sort` = 0, `path` = '', `component` = 'page/log/nbiOperLogList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '0', `status` = '0', `perms` = '北向操作日志', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453756619, `update_by` = '', `update_time` = 0, `remark` = '北向操作日志' WHERE `menu_id` = 2038;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '北向告警日志', `parent_id` = 13, `menu_sort` = 2, `path` = '', `component` = 'page/log/nbiAlarmLog.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '0', `status` = '0', `perms` = '北向告警日志', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453776700, `update_by` = 'AGrand', `update_time` = 1693539031280, `remark` = '北向告警日志' WHERE `menu_id` = 2039;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '北向通用设置', `parent_id` = 13, `menu_sort` = 3, `path` = '', `component` = 'page/log/nbiSet.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '0', `status` = '0', `perms` = '北向通用设置', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453799234, `update_by` = '', `update_time` = 0, `remark` = '北向通用设置' WHERE `menu_id` = 2040;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '用户管理', `parent_id` = 12, `menu_sort` = 1, `path` = '', `component` = 'page/user/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '用户管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453827502, `update_by` = '', `update_time` = 0, `remark` = '用户管理' WHERE `menu_id` = 2041;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '在线状态', `parent_id` = 12, `menu_sort` = 2, `path` = '', `component` = 'page/user/online.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '在线状态', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453847338, `update_by` = '', `update_time` = 0, `remark` = '在线状态' WHERE `menu_id` = 2042;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '用户组管理', `parent_id` = 12, `menu_sort` = 2, `path` = '', `component` = 'page/group/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '用户组管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453875705, `update_by` = 'AGrand', `update_time` = 1693544575599, `remark` = '用户组管理' WHERE `menu_id` = 2043;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '安全策略', `parent_id` = 12, `menu_sort` = 3, `path` = '', `component` = 'page/user/securityPolicy.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '安全策略', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453892806, `update_by` = 'AGrand', `update_time` = 1693544590261, `remark` = '安全策略' WHERE `menu_id` = 2044;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '操作日志', `parent_id` = 11, `menu_sort` = 1, `path` = '', `component` = 'page/log/operLogList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '操作日志', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453925729, `update_by` = '', `update_time` = 0, `remark` = '操作日志' WHERE `menu_id` = 2045;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = 'mml操作日志', `parent_id` = 11, `menu_sort` = 2, `path` = '', `component` = 'page/log/mmlOperLogList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'mml操作日志', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453944710, `update_by` = '', `update_time` = 0, `remark` = 'mml操作日志' WHERE `menu_id` = 2046;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '告警日志', `parent_id` = 11, `menu_sort` = 3, `path` = '', `component` = 'page/log/alarmLogList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '告警日志', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453970200, `update_by` = '', `update_time` = 0, `remark` = '告警日志' WHERE `menu_id` = 2047;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '安全日志', `parent_id` = 11, `menu_sort` = 4, `path` = '', `component` = 'page/log/securityLogList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '安全日志', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693453993525, `update_by` = '', `update_time` = 0, `remark` = '安全日志' WHERE `menu_id` = 2048;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '告警前转日志', `parent_id` = 11, `menu_sort` = 7, `path` = '', `component` = 'page/log/forwardingLog.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '告警前转日志', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454015722, `update_by` = '', `update_time` = 0, `remark` = '告警前转日志' WHERE `menu_id` = 2049;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '日志通用管理', `parent_id` = 11, `menu_sort` = 8, `path` = '', `component` = 'page/log/logSet.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '日志通用管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454038045, `update_by` = '', `update_time` = 0, `remark` = '日志通用管理' WHERE `menu_id` = 2050;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '系统日志', `parent_id` = 11, `menu_sort` = 9, `path` = '', `component` = 'page/log/systemLog.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '系统日志', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454059224, `update_by` = '', `update_time` = 0, `remark` = '系统日志' WHERE `menu_id` = 2051;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '跟踪任务', `parent_id` = 10, `menu_sort` = 1, `path` = '', `component` = 'page/trace/taskList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '0', `status` = '1', `perms` = '跟踪任务', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454085034, `update_by` = 'AGrand', `update_time` = 1694079489394, `remark` = '跟踪任务' WHERE `menu_id` = 2052;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '信令分析', `parent_id` = 10, `menu_sort` = 2, `path` = '', `component` = 'page/trace/traceShow.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '0', `status` = '1', `perms` = '信令分析', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454104730, `update_by` = 'AGrand', `update_time` = 1694080368852, `remark` = '信令分析' WHERE `menu_id` = 2053;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '核心网池', `parent_id` = 9, `menu_sort` = 0, `path` = '', `component` = 'page/mml/poolList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '0', `perms` = '核心网池', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454139150, `update_by` = 'AGrand', `update_time` = 1693544615419, `remark` = '核心网池' WHERE `menu_id` = 2054;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '操作维护MML', `parent_id` = 9, `menu_sort` = 1, `path` = '', `component` = 'page/mml/omcList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '操作维护MML', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454188732, `update_by` = '', `update_time` = 0, `remark` = '操作维护MML' WHERE `menu_id` = 2055;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '网元操作MML', `parent_id` = 9, `menu_sort` = 3, `path` = '', `component` = 'page/mml/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '网元操作MML', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454410796, `update_by` = '', `update_time` = 0, `remark` = '网元操作MML' WHERE `menu_id` = 2056;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '用户数据MML', `parent_id` = 9, `menu_sort` = 4, `path` = '', `component` = 'page/mml/udmList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '用户数据MML', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454436837, `update_by` = '', `update_time` = 0, `remark` = '用户数据MML' WHERE `menu_id` = 2057;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '操作维护设置', `parent_id` = 9, `menu_sort` = 5, `path` = '', `component` = 'page/mml/mmlSet.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '操作维护设置', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454454951, `update_by` = '', `update_time` = 0, `remark` = '操作维护设置' WHERE `menu_id` = 2058;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '角色管理', `parent_id` = 12, `menu_sort` = 6, `path` = '', `component` = 'page/role/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '角色管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454568739, `update_by` = '', `update_time` = 0, `remark` = '角色管理' WHERE `menu_id` = 2059;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '菜单管理', `parent_id` = 12, `menu_sort` = 7, `path` = '', `component` = 'page/menu/list.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = '菜单管理', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693454603776, `update_by` = '', `update_time` = 0, `remark` = '菜单管理' WHERE `menu_id` = 2060;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '数据库URL查询', `parent_id` = 0, `menu_sort` = 10011, `path` = '', `component` = '', `is_frame` = '1', `is_cache` = '1', `menu_type` = 'B', `visible` = '1', `status` = '1', `perms` = 'db:select', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693809550257, `update_by` = '', `update_time` = 0, `remark` = '数据库查询操作' WHERE `menu_id` = 2065;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '数据库URL新增', `parent_id` = 0, `menu_sort` = 10012, `path` = '', `component` = '', `is_frame` = '1', `is_cache` = '1', `menu_type` = 'B', `visible` = '1', `status` = '1', `perms` = 'db:insert', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693809619984, `update_by` = '', `update_time` = 0, `remark` = '数据库URL新增' WHERE `menu_id` = 2066;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '数据库URL更新', `parent_id` = 0, `menu_sort` = 10013, `path` = '', `component` = '', `is_frame` = '1', `is_cache` = '1', `menu_type` = 'B', `visible` = '1', `status` = '1', `perms` = 'db:update', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693809717502, `update_by` = '', `update_time` = 0, `remark` = '数据库URL更新' WHERE `menu_id` = 2067;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '数据库URL删除', `parent_id` = 0, `menu_sort` = 10014, `path` = '', `component` = '', `is_frame` = '1', `is_cache` = '1', `menu_type` = 'B', `visible` = '1', `status` = '1', `perms` = 'db:delete', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1693809752149, `update_by` = '', `update_time` = 0, `remark` = '数据库URL删除' WHERE `menu_id` = 2068;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '用户信息', `parent_id` = 0, `menu_sort` = 559, `path` = 'page', `component` = '', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'D', `visible` = '0', `status` = '1', `perms` = '', `icon` = 'fa fa-home', `create_by` = 'AGrand', `create_time` = 1694086316660, `update_by` = 'AGrand', `update_time` = 1694172973622, `remark` = '' WHERE `menu_id` = 2069;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = 'UDM鉴权用户', `parent_id` = 2069, `menu_sort` = 1, `path` = '', `component` = 'page/nfUserInfo/authList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'root', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1694086376417, `update_by` = 'AGrand', `update_time` = 1694501279203, `remark` = '' WHERE `menu_id` = 2070;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '5G基站信息', `parent_id` = 2069, `menu_sort` = 4, `path` = '', `component` = 'page/nfUserInfo/5gBase.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'root', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1694141926419, `update_by` = 'AGrand', `update_time` = 1694501340788, `remark` = '' WHERE `menu_id` = 2071;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = 'IMS在线用户', `parent_id` = 2069, `menu_sort` = 3, `path` = '', `component` = 'page/baseInfo/imsOnline.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'root', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1694425197596, `update_by` = 'AGrand', `update_time` = 1694575034285, `remark` = '' WHERE `menu_id` = 2072;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = 'UDM签约用户', `parent_id` = 2069, `menu_sort` = 2, `path` = '', `component` = 'page/nfUserInfo/subsList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'root', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1694501260782, `update_by` = 'AGrand', `update_time` = 1694501323769, `remark` = '' WHERE `menu_id` = 2073;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = 'UE在线信息', `parent_id` = 2069, `menu_sort` = 4, `path` = '', `component` = 'page/baseInfo/ueInfoList.html', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'root', `icon` = '#', `create_by` = 'AGrand', `create_time` = 1694595147722, `update_by` = 'AGrand', `update_time` = 1694595166749, `remark` = '' WHERE `menu_id` = 2074;
UPDATE `omc_db`.`sys_menu` SET `menu_name` = '网元管理', `parent_id` = 4, `menu_sort` = 1, `path` = 'neManage', `component` = 'configManage/neManage/index', `is_frame` = '1', `is_cache` = '0', `menu_type` = 'M', `visible` = '1', `status` = '1', `perms` = 'configManage:neManage:index', `icon` = 'icon-biaoqing', `create_by` = 'AGrand', `create_time` = 1694600408970, `update_by` = '', `update_time` = 0, `remark` = '网元管理' WHERE `menu_id` = 2075;
INSERT INTO `omc_db`.`sys_post` (`post_id`, `post_code`, `post_name`, `post_sort`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, 'ceo', '董事长', 1, '1', 'AGrand', 1697110106499, '', 0, '');
INSERT INTO `omc_db`.`sys_post` (`post_id`, `post_code`, `post_name`, `post_sort`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, 'se', '项目经理', 2, '1', 'AGrand', 1697110106502, '', 0, '');
INSERT INTO `omc_db`.`sys_post` (`post_id`, `post_code`, `post_name`, `post_sort`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (3, 'hr', '人力资源', 3, '1', 'AGrand', 1697110106504, '', 0, '');
INSERT INTO `omc_db`.`sys_post` (`post_id`, `post_code`, `post_name`, `post_sort`, `status`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (4, 'user', '普通员工', 4, '1', 'AGrand', 1697110106507, '', 0, '');
DELETE FROM `omc_db`.`sys_role` WHERE `role_id` = 3;
UPDATE `omc_db`.`sys_role` SET `role_name` = '管理员', `role_key` = 'admin', `role_sort` = 1, `data_scope` = '1', `menu_check_strictly` = '1', `dept_check_strictly` = '1', `status` = '1', `del_flag` = '0', `create_by` = 'AGrand', `create_time` = 1697091437683, `update_by` = '', `update_time` = 0, `remark` = '管理员' WHERE `role_id` = 1;
UPDATE `omc_db`.`sys_role` SET `role_name` = '普通角色', `role_key` = 'common', `role_sort` = 2, `data_scope` = '2', `menu_check_strictly` = '1', `dept_check_strictly` = '1', `status` = '1', `del_flag` = '0', `create_by` = 'AGrand', `create_time` = 1697091437686, `update_by` = '', `update_time` = 0, `remark` = '普通角色' WHERE `role_id` = 2;
INSERT INTO `omc_db`.`sys_user` (`user_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (1, 103, 'AGrand', '管理员', 'sys', 'AGrand@agt.com', '', '0', '', '$2a$10$njuHCwb/YqIr/zt.aXb3F.UIAT0aymrbaxd.SwP1Ibo320w2CDzYO', '1', '0', '127.0.0.1', 1697091656500, 'AGrand', 1697091656500, 'AGrand', 1697161493601, '管理员');
INSERT INTO `omc_db`.`sys_user` (`user_id`, `dept_id`, `user_name`, `nick_name`, `user_type`, `email`, `phonenumber`, `sex`, `avatar`, `password`, `status`, `del_flag`, `login_ip`, `login_date`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES (2, 103, 'admin', '管理员', 'sys', 'admin@agt.com', '', '0', '', '$2a$10$QgIcp6yuOEGrEU0TNU12K.uQRLbcufesEU7hiRYlRSSdUO7OAkoTq', '1', '0', '127.0.0.1', 1697091656503, 'AGrand', 1697091656503, 'AGrand', 1697161493601, '普通人员');
INSERT INTO `omc_db`.`sys_user_post` (`user_id`, `post_id`) VALUES (1, 1);
INSERT INTO `omc_db`.`sys_user_post` (`user_id`, `post_id`) VALUES (2, 2);
INSERT INTO `omc_db`.`sys_user_role` (`user_id`, `role_id`) VALUES (2, 1);
SET FOREIGN_KEY_CHECKS = 1;

View File

@@ -12,6 +12,7 @@ import (
"io"
"net/http"
tokenConst "ems.agt/src/framework/constants/token"
"github.com/go-resty/resty/v2"
"github.com/gorilla/mux"
)
@@ -43,7 +44,7 @@ func GetParamConfigFromNF(w http.ResponseWriter, r *http.Request) {
}
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
getNeInfoPattern := fmt.Sprintf(config.UriPrefix+"/databaseManagement/v1/%s/ne_info", config.GetYamlConfig().Database.Name)
getNeInfoPattern := fmt.Sprintf(config.DefaultUriPrefix+"/databaseManagement/v1/%s/ne_info", config.GetYamlConfig().Database.Name)
getNeInfoURI := restHostPort + getNeInfoPattern
neId := services.GetUriParamString(r, "ne_id", ",", true, false)
if neId == "" {
@@ -56,6 +57,7 @@ func GetParamConfigFromNF(w http.ResponseWriter, r *http.Request) {
client := resty.New()
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).

View File

@@ -16,9 +16,8 @@ import (
"ems.agt/lib/services"
"ems.agt/restagent/config"
tokenConst "ems.agt/src/framework/constants/token"
"github.com/gorilla/mux"
_ "github.com/go-sql-driver/mysql"
)
const (
@@ -153,7 +152,7 @@ func PostMMLToNF(w http.ResponseWriter, r *http.Request) {
for _, mml := range mmlRequest.MML {
mmlCommand := fmt.Sprintf("%s\n", mml)
log.Debug("mml command:", mmlCommand)
n, err = conn.Write([]byte(mmlCommand))
_, err = conn.Write([]byte(mmlCommand))
if err != nil {
log.Errorf("Error: %s", err.Error())
return
@@ -211,14 +210,15 @@ func PostMMLToOMC(w http.ResponseWriter, r *http.Request) {
hostUri := fmt.Sprintf("http://%s:%s", neInfo.Ip, neInfo.Port)
omcMmlVar := &mmlp.MmlVar{
Version: "16.1.1",
Output: mmlp.DefaultFormatType,
MmlHome: config.GetYamlConfig().MML.MmlHome,
Limit: 50,
User: "",
SessionToken: token,
HttpUri: hostUri,
UserAgent: config.GetDefaultUserAgent(),
Version: "16.1.1",
Output: mmlp.DefaultFormatType,
MmlHome: config.GetYamlConfig().MML.MmlHome,
Limit: 50,
User: "",
SessionToken: token, // 旧token
Authorization: r.Header.Get(tokenConst.HEADER_KEY), // 请求Token
HttpUri: hostUri,
UserAgent: config.GetDefaultUserAgent(),
}
mmlRequest := new(MMLRequest)
_ = json.Unmarshal(body, mmlRequest)

View File

@@ -14,7 +14,6 @@ import (
sysConfigService "ems.agt/features/sys_config/service"
"ems.agt/lib/core/account"
"ems.agt/lib/core/cache"
"ems.agt/lib/core/conf"
"ems.agt/lib/core/constants/cachekey"
"ems.agt/lib/core/utils/ctx"
"ems.agt/lib/core/vo/result"
@@ -24,7 +23,8 @@ import (
"ems.agt/lib/oauth"
"ems.agt/lib/services"
"ems.agt/restagent/config"
"github.com/go-admin-team/go-admin-core/logger"
srcConfig "ems.agt/src/framework/config"
"ems.agt/src/framework/redis"
"github.com/mojocn/base64Captcha"
)
@@ -136,9 +136,10 @@ func LoginFromOMC(w http.ResponseWriter, r *http.Request) {
if user != nil {
// 缓存用户信息
account.CacheLoginUser(user)
redis.SetByExpire("", "session_token", token, time.Second*1800)
// 角色权限集合,管理员拥有所有权限
userId := fmt.Sprint(user.Id)
isAdmin := conf.IsAdmin(userId)
isAdmin := srcConfig.IsAdmin(userId)
roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin)
services.ResponseStatusOK200LoginWhitRP(w, token, user, roles, perms)
return
@@ -301,6 +302,7 @@ func LoginOMC(w http.ResponseWriter, r *http.Request) {
if user != nil {
// 缓存用户信息
account.CacheLoginUser(user)
redis.SetByExpire("", "session_token", token, time.Second*1800)
ctx.JSON(w, 200, result.OkData(map[string]any{
"accessToken": token,
}))
@@ -363,7 +365,7 @@ func CaptchaImage(w http.ResponseWriter, r *http.Request) {
// 验证码表达式解析输出
item, err := driverCaptcha.DrawCaptcha(question)
if err != nil {
logger.Infof("Generate Id Question Answer %s : %v", question, err)
log.Infof("Generate Id Question Answer %s : %v", question, err)
} else {
data["uuid"] = id
data["img"] = item.EncodeB64string()
@@ -388,7 +390,7 @@ func UserInfo(w http.ResponseWriter, r *http.Request) {
}
// 角色权限集合,管理员拥有所有权限
userId := fmt.Sprint(loginUser.UserID)
isAdmin := conf.IsAdmin(userId)
isAdmin := srcConfig.IsAdmin(userId)
roles, perms := service.NewServiceAccount.RoleAndMenuPerms(userId, isAdmin)
ctx.JSON(w, 200, result.OkData(map[string]any{
@@ -403,7 +405,7 @@ func Routers(w http.ResponseWriter, r *http.Request) {
userID := ctx.LoginUserToUserID(r)
// 前端路由,管理员拥有所有
isAdmin := conf.IsAdmin(userID)
isAdmin := srcConfig.IsAdmin(userID)
buildMenus := service.NewServiceAccount.RouteMenus(userID, isAdmin)
ctx.JSON(w, 200, result.OkData(buildMenus))
}

View File

@@ -17,6 +17,7 @@ import (
"ems.agt/lib/log"
"ems.agt/lib/services"
"ems.agt/restagent/config"
tokenConst "ems.agt/src/framework/constants/token"
)
type CpuUsage struct {
@@ -238,6 +239,7 @@ func GetOneLicenseInfoFromNF(w http.ResponseWriter, r *http.Request) {
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -342,6 +344,7 @@ func GetAllLicenseInfoFromNF(w http.ResponseWriter, r *http.Request) {
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -461,6 +464,7 @@ func GetOneSysinfoFromNF(w http.ResponseWriter, r *http.Request) {
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -594,6 +598,7 @@ func GetAllSysinfoFromNF(w http.ResponseWriter, r *http.Request) {
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -704,13 +709,14 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
// query all NFs
// create rest client
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
getNeInfoPattern := fmt.Sprintf(config.UriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
getNeInfoPattern := fmt.Sprintf(config.DefaultUriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
config.GetYamlConfig().Database.Name)
getNeInfoURI := restHostPort + getNeInfoPattern + "?WHERE=status='0'"
log.Debug("getNeInfoPattern:", getNeInfoPattern)
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"AccessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -724,7 +730,7 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
neList, _ = dborm.XormParseResult(resp.Body())
default:
restHostPort := fmt.Sprintf("http://127.0.0.1:%d", config.GetYamlConfig().Rest[0].Port)
getNeInfoPattern := fmt.Sprintf(config.UriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
getNeInfoPattern := fmt.Sprintf(config.DefaultUriPrefix+"/databaseManagement/v1/elementType/%s/objectType/ne_info",
config.GetYamlConfig().Database.Name)
getNeInfoURI := restHostPort + getNeInfoPattern
neId := services.GetUriParamString(r, "ne_id", ",", true, false)
@@ -737,6 +743,7 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -767,6 +774,7 @@ func GetStateFromNF(w http.ResponseWriter, r *http.Request) {
result["ipAddress"] = ne.Ip
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).

View File

@@ -7,13 +7,13 @@ import (
"ems.agt/features/sys_menu/consts"
"ems.agt/features/sys_menu/model"
"ems.agt/features/sys_menu/service"
"ems.agt/lib/core/conf"
"ems.agt/lib/core/utils/ctx"
"ems.agt/lib/core/utils/regular"
"ems.agt/lib/core/vo/result"
"ems.agt/lib/midware"
"ems.agt/lib/services"
"ems.agt/restagent/config"
srcConfig "ems.agt/src/framework/config"
)
// 菜单接口添加到路由
@@ -109,7 +109,7 @@ func (s *SysMenuApi) List(w http.ResponseWriter, r *http.Request) {
}
userId := ctx.LoginUserToUserID(r)
if conf.IsAdmin(userId) {
if srcConfig.IsAdmin(userId) {
userId = "*"
}
data := s.sysMenuService.SelectMenuList(query, userId)
@@ -315,7 +315,7 @@ func (s *SysMenuApi) TreeSelect(w http.ResponseWriter, r *http.Request) {
}
userId := ctx.LoginUserToUserID(r)
if conf.IsAdmin(userId) {
if srcConfig.IsAdmin(userId) {
userId = "*"
}
data := s.sysMenuService.SelectMenuTreeSelectByUserId(query, userId)
@@ -342,7 +342,7 @@ func (s *SysMenuApi) RoleMenuTreeSelect(w http.ResponseWriter, r *http.Request)
}
userId := ctx.LoginUserToUserID(r)
if conf.IsAdmin(userId) {
if srcConfig.IsAdmin(userId) {
userId = "*"
}
menuTreeSelect := s.sysMenuService.SelectMenuTreeSelectByUserId(query, userId)

View File

@@ -9,13 +9,13 @@ import (
sysRoleService "ems.agt/features/sys_role/service"
sysUserModel "ems.agt/features/sys_user/model"
"ems.agt/features/sys_user/service"
"ems.agt/lib/core/conf"
"ems.agt/lib/core/utils/ctx"
"ems.agt/lib/core/utils/parse"
"ems.agt/lib/core/vo/result"
"ems.agt/lib/midware"
"ems.agt/lib/services"
"ems.agt/restagent/config"
srcConfig "ems.agt/src/framework/config"
)
// 用户接口添加到路由
@@ -114,7 +114,7 @@ func (s *SysUserApi) Info(w http.ResponseWriter, r *http.Request) {
roles := s.sysRoleService.SelectRoleList(sysRoleModel.SysRole{})
// 不是系统指定管理员需要排除其角色
if !conf.IsAdmin(userId) {
if !srcConfig.IsAdmin(userId) {
rolesFilter := make([]sysRoleModel.SysRole, 0)
for _, r := range roles {
if r.RoleID != "1" {
@@ -193,7 +193,7 @@ func (s *SysUserApi) Edit(w http.ResponseWriter, r *http.Request) {
}
// 检查是否管理员用户
// if conf.IsAdmin(body.Id) {
// if srcConfig.IsAdmin(body.Id) {
// ctx.JSON(w, 200, result.ErrMsg("不允许操作管理员用户"))
// return
// }
@@ -261,7 +261,7 @@ func (s *SysUserApi) ResetPwd(w http.ResponseWriter, r *http.Request) {
}
// 检查是否管理员用户
if conf.IsAdmin(body.UserID) {
if srcConfig.IsAdmin(body.UserID) {
ctx.JSON(w, 200, result.ErrMsg("No permission to access user data!"))
return
}

View File

@@ -8,10 +8,10 @@ import (
sysRoleModel "ems.agt/features/sys_role/model"
sysUserModel "ems.agt/features/sys_user/model"
"ems.agt/lib/core/datasource"
"ems.agt/lib/core/utils/crypto"
"ems.agt/lib/core/utils/date"
"ems.agt/lib/core/utils/parse"
"ems.agt/lib/log"
"ems.agt/src/framework/utils/crypto"
)
// 实例化数据层 RepoSysUser 结构体

View File

@@ -130,15 +130,15 @@ func TcpdumpNeUPFTask(w http.ResponseWriter, r *http.Request) {
return
}
// 开始
if body.RunType == "start" {
// 开始telnet
if body.RunType == "start_telnet" {
// 创建TCP连接
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", neInfo.Ip, 5002))
if err != nil {
conn.Close()
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
return
}
defer conn.Close()
filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId)
cmdStr := fmt.Sprintf("pcap dispatch trace on max 100000 file %s", filePcapName)
@@ -169,15 +169,15 @@ func TcpdumpNeUPFTask(w http.ResponseWriter, r *http.Request) {
conn.Close()
return
}
// 停止
if body.RunType == "stop" {
// 停止telnet
if body.RunType == "stop_telnet" {
// 创建TCP连接
conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", neInfo.Ip, 5002))
if err != nil {
conn.Close()
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
return
}
defer conn.Close()
filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId)
cmdStr := "pcap dispatch trace off"
@@ -268,5 +268,72 @@ func TcpdumpNeUPFTask(w http.ResponseWriter, r *http.Request) {
return
}
// 开始-脚本字符串
if body.RunType == "start_str" {
fileLogName := fmt.Sprintf("tmp_%s_%s.log", body.NeType, body.NeId)
filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId)
scriptStr := "#!/bin/expect\nset capcmd [lindex $argv 0]\nspawn telnet localhost 5002\nexpect \"upfd1# \"\nsend \"$capcmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\""
writeLog := fmt.Sprintf(" > %s 2>&1 \ncat %s", fileLogName, fileLogName) // 执行信息写入日志文件输出避免弹出code 127
capCmdStr := fmt.Sprintf("%s file %s", body.Cmd, filePcapName)
cmdStr := fmt.Sprintf("cd /tmp\n\necho '%s' > cap.sh\n\nchmod +x cap.sh\n\n./cap.sh '%s'%s", scriptStr, capCmdStr, writeLog)
usernameNe := conf.Get("ne.user").(string) // 网元统一用户
sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.Ip)
msg, err := cmd.ExecWithCheck("ssh", sshHost, cmdStr)
if err != nil {
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
} else {
s := strings.Index(msg, "pcap dispatch trace:")
if s != -1 {
e := strings.Index(msg, "\r\nupfd1#")
msg = msg[s:e]
} else {
msg = "Executed, please stop before proceeding"
}
ctx.JSON(w, 200, result.OkData(map[string]any{
"cmd": capCmdStr,
"msg": msg,
"fileName": filePcapName,
}))
}
return
}
// 停止-脚本字符串
if body.RunType == "stop_str" {
fileLogName := fmt.Sprintf("tmp_%s_%s.log", body.NeType, body.NeId)
filePcapName := fmt.Sprintf("tmp_%s_%s.pcap", body.NeType, body.NeId)
scriptStr := "#!/bin/expect\nset capcmd [lindex $argv 0]\nspawn telnet localhost 5002\nexpect \"upfd1# \"\nsend \"$capcmd\\n\"\nexpect \"upfd1# \"\nsend \"quit\\n\"\nexpect \"eof\""
writeLog := fmt.Sprintf(" > %s 2>&1 \ncat %s", fileLogName, fileLogName) // 执行信息写入日志文件输出避免弹出code 127
capCmdStr := body.Cmd
cmdStr := fmt.Sprintf("cd /tmp\n\necho '%s' > cap.sh\n\nchmod +x cap.sh\n\n./cap.sh '%s'%s", scriptStr, capCmdStr, writeLog)
usernameNe := conf.Get("ne.user").(string) // 网元统一用户
sshHost := fmt.Sprintf("%s@%s", usernameNe, neInfo.Ip)
msg, err := cmd.ExecWithCheck("ssh", sshHost, cmdStr)
if err != nil {
ctx.JSON(w, 200, result.ErrMsg(err.Error()))
} else {
s := strings.Index(msg, "pcap dispatch trace:")
if s == -1 {
s = strings.Index(msg, "Write ")
}
if s != -1 {
e := strings.Index(msg, "\r\nupfd1#")
msg = msg[s:e]
} else {
msg = "No stoppable found"
}
ctx.JSON(w, 200, result.OkData(map[string]any{
"cmd": capCmdStr,
"msg": msg,
"fileName": filePcapName,
}))
}
return
}
ctx.JSON(w, 200, result.ErrMsg("runType is start or stop"))
}

View File

@@ -4,7 +4,7 @@ import (
"strings"
"ems.agt/features/udm_user/model"
"ems.agt/lib/core/redis"
"ems.agt/src/framework/redis"
)
// phoneImsiList 获取所有imsi

View File

@@ -11,6 +11,7 @@ import (
"ems.agt/lib/log"
"ems.agt/lib/services"
"ems.agt/restagent/config"
tokenConst "ems.agt/src/framework/constants/token"
"github.com/go-resty/resty/v2"
"github.com/gorilla/mux"
)
@@ -129,6 +130,7 @@ func GetUEInfoFromNF(w http.ResponseWriter, r *http.Request) {
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -192,6 +194,7 @@ func GetUENumFromNF(w http.ResponseWriter, r *http.Request) {
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -255,6 +258,7 @@ func GetNBInfoFromNF(w http.ResponseWriter, r *http.Request) {
resp, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: r.Header.Get(tokenConst.HEADER_KEY)}).
SetHeaders(map[string]string{"accessToken": token}).
SetHeaders(map[string]string{"User-Agent": config.GetDefaultUserAgent()}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).

16
go.mod
View File

@@ -4,18 +4,23 @@ go 1.20
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/dlclark/regexp2 v1.10.0
github.com/gin-gonic/gin v1.9.1
github.com/go-admin-team/go-admin-core v1.3.12-0.20221121065133-27b7dbe27a8f
github.com/go-admin-team/go-admin-core/sdk v1.5.1
github.com/go-resty/resty/v2 v2.7.0
github.com/go-sql-driver/mysql v1.7.1
github.com/golang-jwt/jwt/v5 v5.0.0
github.com/gorilla/mux v1.8.0
github.com/gorilla/websocket v1.5.0
github.com/gosnmp/gosnmp v1.35.0
github.com/jasonlvhit/gocron v0.0.1
github.com/lestrrat/go-file-rotatelogs v0.0.0-20180223000712-d3151e2a480f
github.com/matoous/go-nanoid/v2 v2.0.0
github.com/metaleap/go-xsd v0.0.0-20180330193350-61f7638f502f
github.com/mojocn/base64Captcha v1.3.5
github.com/mssola/user_agent v0.6.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/redis/go-redis/v9 v9.1.0
github.com/reiver/go-telnet v0.0.0-20180421082511-9ff0b2ab096e
github.com/robfig/cron/v3 v3.0.1
github.com/shirou/gopsutil v3.21.11+incompatible
@@ -29,10 +34,13 @@ require (
golang.org/x/term v0.11.0
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.1
gorm.io/gorm v1.25.2
xorm.io/xorm v1.3.2
github.com/redis/go-redis/v9 v9.1.0
)
require github.com/go-admin-team/go-admin-core v1.3.12-0.20221121065133-27b7dbe27a8f // indirect
require (
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/bsm/redislock v0.8.2 // indirect
@@ -88,7 +96,6 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/mojocn/base64Captcha v1.3.5
github.com/nsqio/go-nsq v1.0.8 // indirect
github.com/nyaruka/phonenumbers v1.0.55 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
@@ -102,7 +109,7 @@ require (
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/pflag v1.0.5
github.com/subosito/gotenv v1.4.2 // indirect
github.com/syndtr/goleveldb v1.0.0 // indirect
github.com/tebeka/strftime v0.1.5 // indirect
@@ -128,6 +135,5 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/kyokomi/emoji.v1 v1.5.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gorm.io/gorm v1.24.2 // indirect
xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
)

19
go.sum
View File

@@ -121,6 +121,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
@@ -190,6 +192,7 @@ github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPr
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -204,6 +207,8 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
@@ -387,7 +392,6 @@ github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uc
github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -442,6 +446,9 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/matoous/go-nanoid v1.5.0/go.mod h1:zyD2a71IubI24efhpvkJz+ZwfwagzgSO6UNiFsZKN7U=
github.com/matoous/go-nanoid/v2 v2.0.0 h1:d19kur2QuLeHmJBkvYkFdhFBzLoo1XVm2GgTpL+9Tj0=
github.com/matoous/go-nanoid/v2 v2.0.0/go.mod h1:FtS4aGPVfEkxKxhdWPAspZpZSh1cOjtM7Ej/So3hR0g=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -492,6 +499,8 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
@@ -647,6 +656,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@@ -1153,8 +1163,11 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.24.2 h1:9wR6CFD+G8nOusLdvkZelOEhpJVwwHzpQOUM+REd6U0=
gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/driver/mysql v1.5.1 h1:WUEH5VF9obL/lTtzjmML/5e6VfFR/788coz2uaVCAZw=
gorm.io/driver/mysql v1.5.1/go.mod h1:Jo3Xu7mMhCyj8dlrb3WoCaRd1FhsVh+yMXb1jUInf5o=
gorm.io/gorm v1.25.1/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.2 h1:gs1o6Vsa+oVKG/a9ElL3XgyGfghFfkKA2SInQaCyMho=
gorm.io/gorm v1.25.2/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -8,9 +8,9 @@ import (
sysMenuService "ems.agt/features/sys_menu/service"
sysRoleService "ems.agt/features/sys_role/service"
"ems.agt/lib/core/cache"
"ems.agt/lib/core/conf"
"ems.agt/lib/core/vo"
"ems.agt/lib/dborm"
srcConfig "ems.agt/src/framework/config"
)
// 登录缓存用户信息
@@ -35,7 +35,7 @@ func CacheLoginUser(user *dborm.User) {
}
// 是否管理员
if conf.IsAdmin(loginUser.UserID) {
if srcConfig.IsAdmin(loginUser.UserID) {
loginUser.Permissions = []string{"*:*:*"}
} else {
// 获取权限标识

View File

@@ -2,51 +2,35 @@ package conf
import (
"fmt"
"time"
"github.com/spf13/viper"
)
var v *viper.Viper
// 配置文件读取
func InitConfig(configFile string) {
v = viper.New()
// 设置配置文件路径
viper.SetConfigFile(configFile)
v.SetConfigFile(configFile)
// 读取配置文件
err := viper.ReadInConfig()
err := v.ReadInConfig()
if err != nil {
fmt.Printf("读取配置文件失败: %v \n", err)
return
}
// 记录程序开始运行的时间点
viper.Set("runTime", time.Now())
}
// RunTime 程序开始运行的时间
func RunTime() time.Time {
return viper.GetTime("runTime")
}
// Get 获取配置信息
//
// Get("framework.name")
func Get(key string) any {
return viper.Get(key)
return v.Get(key)
}
// IsAdmin 用户是否为管理员
func IsAdmin(userID string) bool {
if userID == "" {
return false
}
// 从本地配置获取user信息
// admins := Get("user.adminList").([]any)
admins := []string{"1", "2", "3"}
for _, s := range admins {
if s == userID {
return true
}
}
return false
// AllSettings 全部配置信息
func AllSettings() map[string]interface{} {
return v.AllSettings()
}

View File

@@ -8,8 +8,11 @@ import (
"net/url"
"os"
"path/filepath"
"strings"
"ems.agt/lib/core/vo"
commonConstants "ems.agt/src/framework/constants/common"
tokenConst "ems.agt/src/framework/constants/token"
"github.com/gorilla/mux"
)
@@ -101,13 +104,27 @@ func SaveUploadedFile(r *http.Request, dst string) error {
/// ==== 登录用户信息, 通过中间件后预置入
// Authorization 解析请求头
func Authorization(r *http.Request) string {
authHeader := r.Header.Get(tokenConst.HEADER_KEY)
if authHeader == "" {
return ""
}
// 拆分 Authorization 请求头,提取 JWT 令牌部分
arr := strings.Split(authHeader, tokenConst.HEADER_PREFIX)
if len(arr) == 2 && arr[1] == "" {
return ""
}
return arr[1]
}
// 定义自定义类型作为键
type ContextKey string
// LoginUser 登录用户信息需要Authorize中间件
func LoginUser(r *http.Request) (vo.LoginUser, error) {
// 上下文
v := r.Context().Value(ContextKey("LoginUser"))
v := r.Context().Value(ContextKey(commonConstants.CTX_LOGIN_USER))
if v != nil {
return v.(vo.LoginUser), nil
}

View File

@@ -26,9 +26,9 @@ const (
)
var (
Version string
BuildTime string
GoVer string
Version string = "-"
BuildTime string = "-"
GoVer string = "-"
)
var (

View File

@@ -10,6 +10,8 @@ import (
"ems.agt/lib/core/vo"
"ems.agt/lib/core/vo/result"
"ems.agt/lib/dborm"
commonConstants "ems.agt/src/framework/constants/common"
tokenUtils "ems.agt/src/framework/utils/token"
)
// Authorize 用户身份授权认证校验
@@ -25,30 +27,74 @@ func Authorize(options map[string][]string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 获取请求头标识信息
tokenStr := ctx.Authorization(r)
// 获取请求头标识信息-旧头
accessToken := r.Header.Get("AccessToken")
if accessToken == "" {
if tokenStr == "" && accessToken != "" {
// 验证令牌 == 这里直接查数据库session
if !dborm.XormExistValidToken(accessToken, 0) {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
return
}
se, err := dborm.XormUpdateSessionShakeTime(accessToken)
if err != nil {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
return
}
// 获取缓存的用户信息
data, ok := cache.GetLocalTTL(se.AccountId)
if data == nil || !ok {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization info error"))
return
}
loginUser := data.(vo.LoginUser)
// 登录用户角色权限校验
if options != nil {
var roles []string
for _, item := range loginUser.User.Roles {
roles = append(roles, item.RoleKey)
}
perms := loginUser.Permissions
verifyOk := verifyRolePermission(roles, perms, options)
if !verifyOk {
msg := fmt.Sprintf("Unauthorized access %s %s", r.Method, r.RequestURI)
ctx.JSON(w, 403, result.CodeMsg(403, msg))
return
}
}
// 在请求的 Context 中存储数据
rContext := r.Context()
rContext = context.WithValue(rContext, ctx.ContextKey(commonConstants.CTX_LOGIN_USER), loginUser)
// 继续处理请求
next.ServeHTTP(w, r.WithContext(rContext))
return
}
// 获取请求头标识信息
if tokenStr == "" {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization token error"))
return
}
// 验证令牌 == 这里直接查数据库session
if !dborm.XormExistValidToken(accessToken, 0) {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
return
}
se, err := dborm.XormUpdateSessionShakeTime(accessToken)
// 验证令牌
claims, err := tokenUtils.Verify(tokenStr)
if err != nil {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization valid error"))
return
}
// 获取缓存的用户信息
data, ok := cache.GetLocalTTL(se.AccountId)
if data == nil || !ok {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization info error"))
loginUser := tokenUtils.LoginUser(claims)
if loginUser.UserID == "" {
ctx.JSON(w, 401, result.CodeMsg(401, "Invalid identity authorization shake error"))
return
}
loginUser := data.(vo.LoginUser)
// 检查刷新有效期后存入上下文
tokenUtils.RefreshIn(&loginUser)
// 登录用户角色权限校验
if options != nil {
@@ -67,7 +113,7 @@ func Authorize(options map[string][]string) func(http.Handler) http.Handler {
// 在请求的 Context 中存储数据
rContext := r.Context()
rContext = context.WithValue(rContext, ctx.ContextKey("LoginUser"), loginUser)
rContext = context.WithValue(rContext, ctx.ContextKey(commonConstants.CTX_LOGIN_USER), loginUser)
// 继续处理请求
next.ServeHTTP(w, r.WithContext(rContext))
})

View File

@@ -6,6 +6,7 @@ import (
"ems.agt/lib/log"
"ems.agt/lib/services"
tokenConst "ems.agt/src/framework/constants/token"
"github.com/gorilla/mux"
)
@@ -22,6 +23,7 @@ func LoggerTrace(next http.Handler) http.Handler {
log.Trace(" User-Agent:", r.Header.Get("User-Agent"))
log.Trace(" Content-Type:", r.Header.Get("Content-Type"))
log.Trace(" AccessToken:", r.Header.Get("AccessToken"))
log.Trace(" Authorization:", r.Header.Get(tokenConst.HEADER_KEY))
log.Trace("Trace End=====")
//body, _ := io.ReadAll(io.LimitReader(r.Body, global.RequestBodyMaxLen))
// nop-close to ready r.Body !!!

View File

@@ -15,6 +15,7 @@ import (
"ems.agt/lib/global"
"ems.agt/lib/log"
"ems.agt/lib/run"
tokenConst "ems.agt/src/framework/constants/token"
"github.com/go-resty/resty/v2"
)
@@ -36,14 +37,15 @@ type MmlCommand struct {
}
type MmlVar struct {
Version string `json:"version"`
Output string `json:"output"`
MmlHome string `json:"mmlHome"`
Limit int `json:"limit"`
User string `json:"user"`
SessionToken string `josn:"sessionToken"`
HttpUri string `json:"httpUri"`
UserAgent string `json:"userAgent"`
Version string `json:"version"`
Output string `json:"output"`
MmlHome string `json:"mmlHome"`
Limit int `json:"limit"`
User string `json:"user"`
SessionToken string `josn:"sessionToken"`
Authorization string `josn:"authorization"`
HttpUri string `json:"httpUri"`
UserAgent string `json:"userAgent"`
}
// func init() {
@@ -504,6 +506,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
log.Debugf("method: Get requestURI: %s", requestURI)
response, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -520,6 +523,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
log.Debugf("method: Post requestURI: %s", requestURI)
response, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -553,6 +557,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
body := ParseInputBody(inputJson, mml)
response, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -569,6 +574,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
log.Debugf("method: Delete requestURI: %s", requestURI)
response, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -584,6 +590,7 @@ func TransMml2HttpReq(omcMmlVar *MmlVar, mml *MmlCommand) (*[]byte, error) {
log.Debugf("method: patch requestURI: %s", requestURI)
response, err := client.R().
EnableTrace().
SetHeaders(map[string]string{tokenConst.HEADER_KEY: omcMmlVar.Authorization}).
SetHeaders(map[string]string{"accessToken": omcMmlVar.SessionToken}).
SetHeaders(map[string]string{"User-Agent": omcMmlVar.UserAgent}).
SetHeaders(map[string]string{"Content-Type": "application/json;charset=UTF-8"}).
@@ -764,12 +771,19 @@ func ParseOutputResponse(omcMmlVar *MmlVar, outputJson *dborm.MmlOutput, respons
output = *ParseErrorOutput(string(response.Body()))
} else {
log.Trace("mapResults:", mapResults)
errResult := mapResults["error"]
log.Trace("errResult:", errResult)
if len(errResult.(map[string]interface{})) > 0 {
errCode, _ := strconv.Atoi(fmt.Sprintf("%v", errResult.(map[string]interface{})["errorCode"]))
errorInfo := errResult.(map[string]interface{})["errorInfo"]
if v, ok := mapResults["error"]; ok {
vMap := v.(map[string]interface{})
if len(vMap) > 0 {
errCode, _ := strconv.Atoi(fmt.Sprintf("%v", vMap["errorCode"]))
errorInfo := vMap["errorInfo"]
output = []byte(fmt.Sprintf(outputJson.ErrMsg, errCode, errorInfo))
}
} else if v, ok := mapResults["code"]; ok {
errCode, _ := strconv.Atoi(fmt.Sprintf("%v", v))
errorInfo := mapResults["msg"]
output = []byte(fmt.Sprintf(outputJson.ErrMsg, errCode, errorInfo))
} else {
output = []byte(fmt.Sprintf("%v", mapResults))
}
}
}

View File

@@ -71,22 +71,10 @@ func init() {
Register("GET", sm.CustomUriOMCLocalTime, sm.GetOMCLocalTime, nil)
// 数据库直连操作权限
selectPermission := midware.Authorize(map[string][]string{
"hasRoles": {"dba"},
"hasPerms": {"db:select"},
})
updatePermission := midware.Authorize(map[string][]string{
"hasRoles": {"dba"},
"hasPerms": {"db:update"},
})
insertPermission := midware.Authorize(map[string][]string{
"hasRoles": {"dba"},
"hasPerms": {"db:insert"},
})
deletePermission := midware.Authorize(map[string][]string{
"hasRoles": {"dba"},
"hasPerms": {"db:delete"},
})
selectPermission := midware.Authorize(map[string][]string{})
updatePermission := midware.Authorize(map[string][]string{})
insertPermission := midware.Authorize(map[string][]string{})
deletePermission := midware.Authorize(map[string][]string{})
// database management
Register("GET", dbrest.XormGetDataUri, dbrest.DatabaseGetData, selectPermission)
@@ -368,12 +356,12 @@ func NewRouter() *mux.Router {
r := mux.NewRouter()
// set custom handle for status 404/405
r.NotFoundHandler = services.CustomResponseNotFound404Handler()
r.MethodNotAllowedHandler = services.CustomResponseMethodNotAllowed405Handler()
// r.NotFoundHandler = services.CustomResponseNotFound404Handler()
// r.MethodNotAllowedHandler = services.CustomResponseMethodNotAllowed405Handler()
r.Use(midware.LoggerTrace)
r.Use(midware.Cors)
//r.Use(midware.OptionProcess)
// r.Use(midware.Cors)
// r.Use(midware.OptionProcess)
// r.Use(midware.ArrowIPAddr)
for _, router := range routers {

View File

@@ -1,12 +1,10 @@
package config
import (
"flag"
"fmt"
"os"
"strings"
"ems.agt/lib/core/conf"
"ems.agt/lib/global"
"ems.agt/lib/log"
@@ -288,7 +286,7 @@ func GetLogLevel() log.LogLevel {
var (
DefaultUriPrefix string = "/api/rest"
UriPrefix string = "/api/rest"
UriPrefix string = "/omc/rest"
//TestDataUDM []map[string]interface{}
TDatas map[string]NeTestData
)
@@ -312,33 +310,33 @@ func GetDefaultUserAgent() string {
return "OMC-restagent/" + global.Version
}
const defaultConfigFile = "./etc/restconf.yaml"
// const defaultConfigFile = "./etc/restconf.yaml"
func init() {
cfile := flag.String("c", defaultConfigFile, "config file")
pv := flag.Bool("version", false, "print version")
ph := flag.Bool("help", false, "print help")
// func init() {
// cfile := flag.String("c", defaultConfigFile, "config file")
// pv := flag.Bool("version", false, "print version")
// ph := flag.Bool("help", false, "print help")
//global.BuildTime = "Wed May 31 18:24:04 CST 2023"
//global.GoVer = "go version go1.15.7 linux/arm64"
flag.Parse()
if *pv {
fmt.Printf("OMC restagent version: %s\n%s\n%s\n\n", global.Version, global.BuildTime, global.GoVer)
os.Exit(0)
}
if *ph {
flag.Usage()
os.Exit(0)
}
// //global.BuildTime = "Wed May 31 18:24:04 CST 2023"
// //global.GoVer = "go version go1.15.7 linux/arm64"
// flag.Parse()
// if *pv {
// fmt.Printf("OMC restagent version: %s\n%s\n%s\n\n", global.Version, global.BuildTime, global.GoVer)
// os.Exit(0)
// }
// if *ph {
// flag.Usage()
// os.Exit(0)
// }
// 使用viper读取配置
conf.InitConfig(*cfile)
// // 使用viper读取配置
// conf.InitConfig(*cfile)
ReadConfig(*cfile)
if GetYamlConfig().OMC.UriPrefix != "" {
UriPrefix = GetYamlConfig().OMC.UriPrefix
}
if GetYamlConfig().TestConfig.Enabled {
ReadTestConfigYaml(GetYamlConfig().TestConfig.File)
}
}
// ReadConfig(*cfile)
// if GetYamlConfig().OMC.UriPrefix != "" {
// UriPrefix = GetYamlConfig().OMC.UriPrefix
// }
// if GetYamlConfig().TestConfig.Enabled {
// ReadTestConfigYaml(GetYamlConfig().TestConfig.File)
// }
// }

View File

@@ -62,7 +62,7 @@ ne:
# chk2ne: true/false, if put OmcNeConfig parameters to NE
omc:
uriPrefix: /api/rest/oam
uriPrefix: "/omc/rest"
neType: OMC
neId: 001
rmUID: 4400HX101

View File

@@ -79,7 +79,7 @@ ne:
# chk2ne: true/false, if put OmcNeConfig parameters to NE
omc:
uriPrefix: /api/rest/oam
uriPrefix: "/omc/rest"
neType: OMC
neId: 001
rmUID: 4400HX101

View File

@@ -4,24 +4,23 @@ import (
"crypto/tls"
"crypto/x509"
"fmt"
"net"
"net/http"
"os"
"strconv"
"strings"
"ems.agt/lib/core/redis"
"ems.agt/features/dbrest"
"ems.agt/features/fm"
"ems.agt/features/lm"
"ems.agt/features/pm"
"ems.agt/lib/dborm"
"ems.agt/lib/global"
"ems.agt/lib/log"
"ems.agt/lib/routes"
"ems.agt/features/dbrest"
"ems.agt/features/fm"
"ems.agt/features/lm"
"ems.agt/features/monitor/monitor"
"ems.agt/features/pm"
"ems.agt/restagent/config"
"ems.agt/src"
libSession "ems.agt/src/lib_features/session"
"github.com/gin-gonic/gin"
)
// const defaultConfigFile = "./etc/restconf.yaml"
@@ -46,25 +45,25 @@ import (
// //fmt.Println(config.UriPrefix)
// }
func listenIPv6(ipv6 string, port int) {
//
addr := &net.TCPAddr{
IP: net.ParseIP(ipv6),
Port: port,
}
// func listenIPv6(ipv6 string, port int) {
// //
// addr := &net.TCPAddr{
// IP: net.ParseIP(ipv6),
// Port: port,
// }
listener, err := net.ListenTCP("tcp6", addr)
if err != nil {
fmt.Println("Failed to listen:", err)
return
}
// listener, err := net.ListenTCP("tcp6", addr)
// if err != nil {
// fmt.Println("Failed to listen:", err)
// return
// }
server := &http.Server{}
err = server.Serve(listener)
if err != nil {
fmt.Println("Failed to serve:", err)
}
}
// server := &http.Server{}
// err = server.Serve(listener)
// if err != nil {
// fmt.Println("Failed to serve:", err)
// }
// }
func HttpListen(addr string, router http.Handler) {
err := http.ListenAndServe(addr, router)
@@ -128,6 +127,10 @@ func HttpListenWebServer(addr string) {
}
func main() {
// src 配置中心初始加载
src.ConfigurationInit()
app := src.AppEngine()
conf := config.GetYamlConfig()
log.InitLogger(conf.Logger.File, conf.Logger.Duration, conf.Logger.Count, "omc:restagent", config.GetLogLevel())
@@ -166,22 +169,28 @@ func main() {
os.Exit(4)
}
// 连接redis
redis.Connect()
// 将 mux.Router 注册到 gin.Engine
router := routes.NewRouter()
// 默认路由组
defaultUriGroup := app.Group(config.DefaultUriPrefix)
defaultUriGroup.Use(libSession.SessionHeader())
defaultUriGroup.Any("/*any", gin.WrapH(routes.NewRouter()))
// 可配置前缀路由组
uriGroup := app.Group(config.UriPrefix)
uriGroup.Use(libSession.SessionHeader())
uriGroup.Any("/*any", gin.WrapH(routes.NewRouter()))
// 开启监控采集
monitor.StartMonitor(false, "")
// monitor.StartMonitor(false, "")
for _, rest := range conf.Rest {
// ipv4 goroutines
if rest.IPv4 != "" {
listen := rest.IPv4 + ":" + strconv.Itoa(int(rest.Port))
if strings.ToLower(rest.Scheme) == "https" {
go HttpListenTLS(listen, rest.CertFile, rest.KeyFile, router)
go HttpListenTLS(listen, rest.CertFile, rest.KeyFile, app)
} else {
go HttpListen(listen, router)
go HttpListen(listen, app)
}
}
@@ -189,9 +198,9 @@ func main() {
if rest.IPv6 != "" {
listenv6 := "[" + rest.IPv6 + "]" + ":" + strconv.Itoa(int(rest.Port))
if strings.ToLower(rest.Scheme) == "https" {
go HttpListenTLS(listenv6, rest.CertFile, rest.KeyFile, router)
go HttpListenTLS(listenv6, rest.CertFile, rest.KeyFile, app)
} else {
go HttpListen(listenv6, router)
go HttpListen(listenv6, app)
}
}
}

119
src/app.go Normal file
View File

@@ -0,0 +1,119 @@
package src
import (
"fmt"
"ems.agt/src/framework/config"
"ems.agt/src/framework/errorcatch"
"ems.agt/src/framework/middleware"
"ems.agt/src/framework/middleware/security"
"ems.agt/src/modules/common"
"ems.agt/src/modules/monitor"
"ems.agt/src/modules/system"
"github.com/gin-gonic/gin"
)
// 路由函数句柄,交给由 http.ListenAndServe(addr, router)
func AppEngine() *gin.Engine {
app := initAppEngine()
// 初始全局默认
initDefeat(app)
// 初始模块路由
initModulesRoute(app)
// 读取服务配置
app.ForwardedByClientIP = config.Get("server.proxy").(bool)
addr := fmt.Sprintf(":%d", config.Get("server.port").(int))
// 启动服务
fmt.Printf("\nopen http://localhost%s \n\n", addr)
return app
}
// 运行服务程序 main.go
//
// func main() {
// src.ConfigurationInit()
// if err := src.RunServer(); err != nil {
// src.ConfigurationClose()
// }
// }
func RunServer() error {
app := initAppEngine()
// 初始全局默认
initDefeat(app)
// 初始模块路由
initModulesRoute(app)
// 读取服务配置
app.ForwardedByClientIP = config.Get("server.proxy").(bool)
addr := fmt.Sprintf(":%d", config.Get("server.port").(int))
// 启动服务
fmt.Printf("\nopen http://localhost%s \n\n", addr)
return app.Run(addr)
}
// 初始应用引擎
func initAppEngine() *gin.Engine {
var app *gin.Engine
// 禁止控制台日志输出的颜色
gin.DisableConsoleColor()
// 根据运行环境注册引擎
if config.Env() == "prod" {
gin.SetMode(gin.ReleaseMode)
app = gin.New()
app.Use(gin.Recovery())
} else {
app = gin.Default()
}
return app
}
// 初始全局默认
func initDefeat(app *gin.Engine) {
// 全局中间件
app.Use(errorcatch.ErrorCatch(), middleware.Report(), middleware.Cors(), security.Security())
// 静态目录-静态资源
if v := config.Get("staticFile.default"); v != nil {
fsMap := v.(map[string]any)
prefix, dir := fsMap["prefix"], fsMap["dir"]
if prefix != nil && dir != nil {
app.StaticFS(prefix.(string), gin.Dir(dir.(string), true))
}
}
// 静态目录-上传资源
if v := config.Get("staticFile.upload"); v != nil {
fsMap := v.(map[string]any)
prefix, dir := fsMap["prefix"], fsMap["dir"]
if prefix != nil && dir != nil {
app.StaticFS(prefix.(string), gin.Dir(dir.(string), true))
}
}
// 路由未找到时
app.NoRoute(func(c *gin.Context) {
c.JSON(404, gin.H{
"code": 404,
"msg": fmt.Sprintf("%s Not Found", c.Request.RequestURI),
})
})
}
// 初始模块路由
func initModulesRoute(app *gin.Engine) {
common.Setup(app)
monitor.Setup(app)
system.Setup(app)
}

35
src/configuration.go Normal file
View File

@@ -0,0 +1,35 @@
package src
import (
"ems.agt/src/framework/config"
"ems.agt/src/framework/cron"
"ems.agt/src/framework/datasource"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/redis"
)
// 配置中心初始加载
func ConfigurationInit() {
// 初始配置参数
config.InitConfig()
// 初始程序日志
logger.InitLogger()
// 连接数据库实例
datasource.Connect()
// 连接Redis实例
redis.Connect()
// 启动调度任务实例
cron.StartCron()
}
// 配置中心相关配置关闭连接
func ConfigurationClose() {
// 停止调度任务实例
cron.StopCron()
// 关闭Redis实例
redis.Close()
// 关闭数据库实例
datasource.Close()
// 关闭程序日志
logger.Close()
}

View File

@@ -0,0 +1,163 @@
package config
import (
"bytes"
"embed"
"fmt"
"log"
"os"
"time"
libConfig "ems.agt/src/lib_features/config"
"github.com/spf13/pflag"
"github.com/spf13/viper"
)
//go:embed config/*.yaml
var configFiles embed.FS
// 初始化程序配置
func InitConfig() {
initFlag()
initViper()
}
// 指定参数绑定
func initFlag() {
// --env prod
pflag.String("env", "prod", "Specify Run Environment Configuration local or prod")
// --c /etc/restconf.yaml
// -c /etc/restconf.yaml
pConfig := pflag.StringP("config", "c", "./etc/restconf.yaml", "Specify Configuration File")
// --version
// -V
pVersion := pflag.BoolP("version", "V", false, "Output program version")
// --help
pHelp := pflag.Bool("help", false, "Viewing Help Commands")
pflag.Parse()
// 参数固定输出
if *pVersion {
buildInfo := libConfig.BuildInfo()
fmt.Println(buildInfo)
os.Exit(1)
}
if *pHelp {
pflag.Usage()
os.Exit(1)
}
// 外层lib和features使用的配置
libConfig.ConfigRead(*pConfig)
viper.BindPFlags(pflag.CommandLine)
}
// 配置文件读取
func initViper() {
// 在当前工作目录中寻找配置
// viper.AddConfigPath("config")
// viper.AddConfigPath("src/config")
// 如果配置文件名中没有扩展名则需要设置Type
viper.SetConfigType("yaml")
// 从 embed.FS 中读取默认配置文件内容
configDefault, err := configFiles.ReadFile("config/config.default.yaml")
if err != nil {
log.Fatalf("ReadFile config default file: %s", err)
return
}
// 设置默认配置文件内容到 viper
err = viper.ReadConfig(bytes.NewReader(configDefault))
if err != nil {
log.Fatalf("NewReader config default file: %s", err)
return
}
// // 配置文件的名称(无扩展名)
// viper.SetConfigName("config.default")
// // 读取默认配置文件
// if err := viper.ReadInConfig(); err != nil {
// log.Fatalf("fatal error config default file: %s", err)
// }
env := viper.GetString("env")
if env != "local" && env != "prod" {
log.Fatalf("fatal error config env for local or prod : %s", env)
}
log.Printf("Current service environment operation configuration => %s \n", env)
// 加载运行配置文件合并相同配置
if env == "prod" {
// viper.SetConfigName("config.prod")
// 从 embed.FS 中读取默认配置文件内容
configProd, err := configFiles.ReadFile("config/config.prod.yaml")
if err != nil {
log.Fatalf("ReadFile config prod file: %s", err)
return
}
// 设置默认配置文件内容到 viper
err = viper.MergeConfig(bytes.NewReader(configProd))
if err != nil {
log.Fatalf("NewReader config prod file: %s", err)
return
}
} else {
// viper.SetConfigName("config.local")
// 从 embed.FS 中读取默认配置文件内容
configLocal, err := configFiles.ReadFile("config/config.local.yaml")
if err != nil {
log.Fatalf("ReadFile config local file: %s", err)
return
}
// 设置默认配置文件内容到 viper
err = viper.MergeConfig(bytes.NewReader(configLocal))
if err != nil {
log.Fatalf("NewReader config local file: %s", err)
return
}
}
// if err := viper.MergeInConfig(); err != nil {
// log.Fatalf("fatal error config MergeInConfig: %s", err)
// }
// 合并外层lib和features使用配置
libConfig.ConfigInMerge()
// 记录程序开始运行的时间点
viper.Set("runTime", time.Now())
}
// Env 获取运行服务环境
// local prod
func Env() string {
return viper.GetString("env")
}
// RunTime 程序开始运行的时间
func RunTime() time.Time {
return viper.GetTime("runTime")
}
// Get 获取配置信息
//
// Get("framework.name")
func Get(key string) any {
return viper.Get(key)
}
// IsAdmin 用户是否为管理员
func IsAdmin(userID string) bool {
if userID == "" {
return false
}
// 从本地配置获取user信息
admins := Get("user.adminList").([]any)
for _, s := range admins {
if s.(string) == userID {
return true
}
}
return false
}

View File

@@ -0,0 +1,208 @@
# 项目信息
framework:
name: "ems_agt"
version: "0.0.1"
# 应用服务配置
server:
# 服务端口
port: 3040
# 是否开启代理
proxy: false
# 日志
logger:
fileDir: "/usr/local/omc/log"
fileName: "ems_agt.log"
level: 2 # 日志记录的等级 0:silent<1:info<2:warn<3:error
maxDay: 30 # 日志会保留 30 天
maxSize: 10 # 调整按 10MB 大小的切割
# 静态文件配置, 相对项目根路径或填绝对路径
staticFile:
# 默认资源dir目录需要预先创建
default:
prefix: "/static"
dir: "/usr/local/omc/static"
# 文件上传资源目录映射,与项目目录同级
upload:
prefix: "/upload"
dir: "/usr/local/omc/upload"
# 文件上传
upload:
# 最大上传文件大小,默认为 10mb
fileSize: 10
# 文件扩展名白名单
whitelist:
# 图片
- ".bmp"
- ".webp"
- ".gif"
- ".jpg"
- ".jpeg"
- ".png"
# word excel powerpoint
- ".doc"
- ".docx"
- ".xls"
- ".xlsx"
- ".ppt"
- ".pptx"
# 文本文件
- ".html"
- ".htm"
- ".txt"
# pdf
- ".pdf"
# 压缩文件
- ".zip"
- ".gz"
- ".tgz"
- ".gzip"
# 音视频格式
- ".mp3"
- ".mp4"
- ".avi"
- ".rmvb"
# cors 跨域
cors:
# 设置 Access-Control-Allow-Origin 的值,【默认值】会获取请求头上的 origin
# 例如http://mask-api.org
# 如果请求设置了 credentials则 origin 不能设置为 *
origin: "*"
# 设置 Access-Control-Allow-Credentials【默认值】false
credentials: true
# 设置 Access-Control-Max-Age
maxAge: 31536000
# 允许跨域的方法,【默认值】为 GET,HEAD,PUT,POST,DELETE,PATCH
allowMethods:
- "OPTIONS"
- "HEAD"
- "GET"
- "POST"
- "PUT"
- "DELETE"
- "PATCH"
# 设置 Access-Control-Allow-Headers 的值,【默认值】会获取请求头上的 Access-Control-Request-Headers
allowHeaders:
- "X-App-Code"
- "X-App-Version"
- "Authorization"
- "Origin"
- "X-Requested-With"
- "Content-Type"
- "Content-Language"
- "Accept"
- "Range"
- "Accesstoken"
- "Operationtype"
# 设置 Access-Control-Expose-Headers 的值
exposeHeaders:
- "X-RepeatSubmit-Rest"
# security 安全
security:
csrf:
enable: false
type: "referer"
# 允许调用的域名地址的例如http://<Referer地址>/mask-api
refererWhiteList:
- "127.0.0.1:3030"
xframe:
enable: true
value: "SAMEORIGIN"
csp:
enable: true
hsts:
enable: false
maxAge: 31536000
includeSubdomains: false
noopen:
enable: false
nosniff:
enable: false
xssProtection:
enable: true
value: "1; mode=block"
# JWT 令牌配置
jwt:
# 令牌算法 HS256 HS384 HS512
algorithm: "HS512"
# 令牌密钥
secret: "217a0481c7f9cfe1cb547d32ee012b0f"
# 令牌有效期默认120分钟
expiresIn: 120
# 验证令牌有效期相差不足xx分钟自动刷新缓存
refreshIn: 20
# GORM 数据源
gorm:
dataSource:
# 默认数据库实例
default:
type: "mysql"
host: "127.0.0.1"
port: 3306
username: "<用户名>"
password: "<密码>"
database: "<数据库>"
logging: false
# 多个数据源时可以用这个指定默认的数据源
defaultDataSourceName: "default"
# Redis 缓存数据
redis:
dataSource:
default:
port: 6379 # Redis port
host: "127.0.0.1" # Redis host
password: "<密码>"
db: 0 # Redis db_num
# 多个数据源时可以用这个指定默认的数据源
defaultDataSourceName: "default"
# 用户配置
user:
# 密码
password:
# 密码最大错误次数
maxRetryCount: 5
# 密码锁定时间,单位分钟默认10分钟
lockTime: 10
# 管理员列表
adminList:
- "1"
- "2"
# char 字符验证码配置
charCaptcha:
# 宽度
width: 120
# 高度
height: 40
# 干扰线条的数量
noise: 4
# 验证码的字符是否有颜色,默认没有,如果设定了背景,则默认有
color: true
# 验证码图片背景颜色
background: "#fafafa"
# 验证码长度
size: 4
# 验证码字符
chars: "023456789abcdefghjkmnprstuvwxyz"
# math 数值计算码配置
mathCaptcha:
# 宽度
width: 120
# 高度
height: 40
# 干扰线条的数量
noise: 4
# 验证码的字符是否有颜色,默认没有,如果设定了背景,则默认有
color: true
# 验证码图片背景颜色
background: "#fafafa"

View File

@@ -0,0 +1,53 @@
# 应用服务配置
server:
port: 3040
# 日志
logger:
fileDir: "C:/usr/local/omc/log"
level: 0 # 输出最低等级
# 静态文件配置, 相对项目根路径或填绝对路径
staticFile:
default:
dir: "C:/usr/local/omc/static"
# 文件上传资源目录映射,与项目目录同级
upload:
dir: "C:/usr/local/omc/upload"
# security 安全
security:
csrf:
refererWhiteList:
- "localhost:3131"
- "127.0.0.1:3131"
# GORM 数据源
gorm:
dataSource:
default:
type: "mysql"
host: "192.168.0.229"
port: 33066
username: "root"
password: "1000omc@kp!"
database: "omc_db_dev"
logging: true
# Redis 缓存数据,数据源声明全小写
redis:
dataSource:
# OMC系统使用库
default:
port: 6379 # Redis port
host: "192.168.0.229" # Redis host
password: ""
db: 10 # Redis db_num
# UDM网元用户库
udmuser:
port: 6379 # Redis port
host: "192.168.0.229"
password: ""
db: 0 # Redis db_num
# 多个数据源时可以用这个指定默认的数据源
defaultDataSourceName: "default"

View File

@@ -0,0 +1,32 @@
# 应用服务配置
server:
port: 3030
proxy: true
# security 安全
security:
csrf:
# 允许调用的域名地址的例如http://<Referer地址>/
refererWhiteList:
- "127.0.0.1"
- "<Referer地址>"
# GORM 数据源
gorm:
dataSource:
default:
type: "mysql"
host: "<mysql地址>"
port: 3306
username: "<用户名>"
password: "<密码>"
database: "<数据库>"
# Redis 缓存数据
redis:
dataSource:
default:
port: 6379 # Redis port
host: "<redis地址>"
password: "<密码>"
db: 0 # Redis db_num

View File

@@ -0,0 +1,12 @@
package admin
// 管理员常量信息
// 管理员-系统指定角色ID
const ROLE_ID = "1"
// 管理员-系统指定角色KEY
const ROLE_KEY = "admin"
// 管理员-系统指定权限
const PERMISSION = "*:*:*"

View File

@@ -0,0 +1,24 @@
package cachekey
// 缓存的key常量
// 登录用户
const LOGIN_TOKEN_KEY = "login_tokens:"
// 验证码
const CAPTCHA_CODE_KEY = "captcha_codes:"
// 参数管理
const SYS_CONFIG_KEY = "sys_config:"
// 字典管理
const SYS_DICT_KEY = "sys_dict:"
// 防重提交
const REPEAT_SUBMIT_KEY = "repeat_submit:"
// 限流
const RATE_LIMIT_KEY = "rate_limit:"
// 登录账户密码错误次数
const PWD_ERR_CNT_KEY = "pwd_err_cnt:"

View File

@@ -0,0 +1,12 @@
package captcha
// 验证码常量信息
// 验证码有效期,单位秒
const EXPIRATION = 2 * 60
// 验证码类型-数值计算
const TYPE_CHAR = "char"
// 验证码类型-字符验证
const TYPE_MATH = "math"

View File

@@ -0,0 +1,21 @@
package common
// 通用常量信息
// www主域
const WWW = "www."
// http请求
const HTTP = "http://"
// https请求
const HTTPS = "https://"
// 通用状态标识-正常/成功/是
const STATUS_YES = "1"
// 通用状态标识-停用/失败/否
const STATUS_NO = "0"
// 上下文信息-登录用户
const CTX_LOGIN_USER = "loginuser"

View File

@@ -0,0 +1,24 @@
package menu
// 系统菜单常量信息
const (
// 组件布局类型-基础布局组件标识
COMPONENT_LAYOUT_BASIC = "BasicLayout"
// 组件布局类型-空白布局组件标识
COMPONENT_LAYOUT_BLANK = "BlankLayout"
// 组件布局类型-内链接布局组件标识
COMPONENT_LAYOUT_LINK = "LinkLayout"
)
const (
// 菜单类型-目录
TYPE_DIR = "D"
// 菜单类型-菜单
TYPE_MENU = "M"
// 菜单类型-按钮
TYPE_BUTTON = "B"
)
// 菜单内嵌地址标识-带/前缀
const PATH_INLINE = "/inline"

View File

@@ -0,0 +1,15 @@
package result
// 响应结果常量信息
const (
// 响应-code错误失败
CODE_ERROR = 0
// 响应-msg错误失败
MSG_ERROR = "error"
// 响应-msg正常成功
CODE_SUCCESS = 1
// 响应-code正常成功
MSG_SUCCESS = "success"
)

View File

@@ -0,0 +1,29 @@
package roledatascope
// 系统角色数据范围常量
const (
// 全部数据权限
ALL = "1"
// 自定数据权限
CUSTOM = "2"
// 部门数据权限
DEPT = "3"
// 部门及以下数据权限
DEPT_AND_CHILD = "4"
// 仅本人数据权限
SELF = "5"
)
// 系统角色数据范围映射
var RoleDataScope = map[string]string{
ALL: "全部数据权限",
CUSTOM: "自定数据权限",
DEPT: "部门数据权限",
DEPT_AND_CHILD: "部门及以下数据权限",
SELF: "仅本人数据权限",
}

View File

@@ -0,0 +1,21 @@
package token
// 令牌常量信息
// 令牌-数据响应字段
const RESPONSE_FIELD = "access_token"
// 令牌-请求头标识前缀
const HEADER_PREFIX = "Bearer "
// 令牌-请求头标识
const HEADER_KEY = "Authorization"
// 令牌-JWT唯一标识字段
const JWT_UUID = "login_key"
// 令牌-JWT标识用户主键字段
const JWT_KEY = "user_id"
// 令牌-JWT标识用户登录账号字段
const JWT_NAME = "user_name"

View File

@@ -0,0 +1,37 @@
package uploadsubpath
// 文件上传-子路径类型常量
const (
// 默认
DEFAULT = "default"
// 头像
AVATART = "avatar"
// 导入
IMPORT = "import"
// 导出
EXPORT = "export"
// 通用上传
COMMON = "common"
// 下载
DOWNLOAD = "download"
// 切片
CHUNK = "chunk"
)
// 子路径类型映射
var UploadSubpath = map[string]string{
DEFAULT: "默认",
AVATART: "头像",
IMPORT: "导入",
EXPORT: "导出",
COMMON: "通用上传",
DOWNLOAD: "下载",
CHUNK: "切片",
}

202
src/framework/cron/cron.go Normal file
View File

@@ -0,0 +1,202 @@
package cron
import (
"fmt"
"time"
"github.com/robfig/cron/v3"
)
// 定义内部调度任务实例
var c *cron.Cron
// 任务队列
var queueMap map[string]Queue
// StartCron 启动调度任务实例
func StartCron() {
queueMap = make(map[string]Queue)
c = cron.New(cron.WithSeconds())
c.Start()
}
// StopCron 停止调度任务实例
func StopCron() {
c.Stop()
}
// CreateQueue 创建队列注册处理器
func CreateQueue(name string, processor QueueProcessor) Queue {
queue := Queue{
Name: name,
Processor: processor,
Job: &[]*QueueJob{},
}
queueMap[name] = queue
return queue
}
// GetQueue 通过名称获取队列实例
func GetQueue(name string) Queue {
if v, ok := queueMap[name]; ok {
return v
}
return Queue{}
}
// QueueNames 获取注册的队列名称
func QueueNames() []string {
keys := make([]string, 0, len(queueMap))
for k := range queueMap {
keys = append(keys, k)
}
return keys
}
// Queue 任务队列
type Queue struct {
Name string // 队列名
Processor QueueProcessor
Job *[]*QueueJob
}
// QueueProcessor 队列处理函数接口
type QueueProcessor interface {
// Execute 实际执行函数
Execute(data any) any
}
// RunJob 运行任务data是传入的数据
func (q *Queue) RunJob(data any, opts JobOptions) int {
job := &QueueJob{
Status: Waiting,
Data: data,
Opts: opts,
queueName: q.Name,
queueProcessor: &q.Processor,
}
// 非重复任务立即执行
if opts.Cron == "" {
// 获取执行的任务
currentJob := job.GetJob(false)
if currentJob.Status == Active {
return Active
}
// 从切片 jobs 中删除指定索引位置的元素
for i, v := range *q.Job {
if v.cid == 0 {
jobs := *q.Job
jobs = append(jobs[:i], jobs[i+1:]...)
*q.Job = jobs
break
}
}
go job.Run()
} else {
// 移除已存的任务ID
q.RemoveJob(opts.JobId)
// 添加新任务
cid, err := c.AddJob(opts.Cron, job)
if err != nil {
newLog.Error(err, "err")
job.Status = Failed
}
job.cid = cid
}
*q.Job = append(*q.Job, job)
newLog.Info("RunJob", job.cid, opts.JobId, job.Status)
return job.Status
}
// RemoveJob 移除任务
func (q *Queue) RemoveJob(jobId string) bool {
for i, v := range *q.Job {
if jobId == v.Opts.JobId {
newLog.Info("RemoveJob", v.cid, jobId, v.Status)
c.Remove(v.cid)
// 从切片 jobs 中删除指定索引位置的元素
jobs := *q.Job
jobs = append(jobs[:i], jobs[i+1:]...)
*q.Job = jobs
return true
}
}
return false
}
// Status 任务执行状态
const (
Waiting = iota
Active
Completed
Failed
)
// JobOptions 任务参数信息
type JobOptions struct {
JobId string // 执行任务编号
Cron string // 重复任务cron表达式
}
// QueueJob 队列内部执行任务
type QueueJob struct {
Status int // 任务执行状态
Timestamp int64 // 执行时间
Data any // 执行任务时传入的参数
Opts JobOptions
cid cron.EntryID // 执行ID
queueName string //队列名
queueProcessor *QueueProcessor
}
// GetJob 获取当前执行任务
func (job *QueueJob) GetJob(repeat bool) *QueueJob {
q := GetQueue(job.queueName)
for _, v := range *q.Job {
if repeat && v.Opts.JobId == job.Opts.JobId {
return v
}
if !repeat && v.cid == 0 {
return v
}
}
return job
}
// Run 实现的接口函数
func (s QueueJob) Run() {
// 检查当前任务
job := s.GetJob(s.cid != 0)
// Active 状态不执行
if job.Status == Active {
return
}
// panics 异常收集
defer func() {
if r := recover(); r != nil {
err, ok := r.(error)
if !ok {
err = fmt.Errorf("%v", r)
}
job.Status = Failed
newLog.Error(err, "failed", job)
}
}()
// 开始执行
job.Status = Active
job.Timestamp = time.Now().UnixMilli()
newLog.Info("run", job.cid, job.Opts.JobId)
// 获取队列处理器接口实现
processor := *job.queueProcessor
result := processor.Execute(job.Data)
job.Status = Completed
newLog.Completed(result, "completed", job)
}

View File

@@ -0,0 +1,179 @@
package cron
import (
"testing"
"time"
"ems.agt/src/framework/logger"
)
// 参考文章:
// https://blog.csdn.net/zjbyough/article/details/113853582
// https://mp.weixin.qq.com/s/Ak7RBv1NuS-VBeDNo8_fww
func init() {
StartCron()
}
// 简单示例 队列任务处理
var NewSimple = &Simple{}
type Simple struct{}
func (s *Simple) Execute(data any) any {
logger.Infof("执行=> %+v ", data)
// 实现任务处理逻辑
return data
}
func TestSimple(t *testing.T) {
simple := CreateQueue("simple", NewSimple)
simple.RunJob(map[string]string{
"ok": "ok",
"data": "data",
}, JobOptions{
JobId: "101",
})
simpleC := CreateQueue("simple", NewSimple)
simpleC.RunJob(map[string]string{
"corn": "*/5 * * * * *",
"id": "102",
}, JobOptions{
JobId: "102",
Cron: "*/5 * * * * *",
})
// simpleC.RunJob(map[string]string{
// "corn": "*/15 * * * * *",
// "id": "103",
// }, JobOptions{
// JobId: "103",
// Cron: "*/15 * * * * *",
// })
// simpleC.RemoveJob("102")
select {}
}
// Foo 队列任务处理
var NewFooProcessor = &FooProcessor{
progress: 0,
count: 0,
}
type FooProcessor struct {
progress int
count int
}
func (s *FooProcessor) Execute(data any) any {
logger.Infof("执行 %d %d => %+v ", s.count, s.progress, data)
s.count++
// 实现任务处理逻辑
i := 0
s.progress = i
for i < 10 {
// 获取任务进度
progress := s.progress
logger.Infof("data: %v => 任务进度:%d", data, progress)
// 延迟响应
time.Sleep(time.Second * 2)
i++
// 改变任务进度
s.progress = i
}
return data
}
func TestFoo(t *testing.T) {
foo := CreateQueue("foo", NewFooProcessor)
foo.RunJob(map[string]string{
"data": "2",
}, JobOptions{
JobId: "2",
})
fooC := CreateQueue("foo", NewFooProcessor)
fooC.RunJob(map[string]string{
"corn": "*/5 * * * * *",
}, JobOptions{
JobId: "3",
Cron: "*/5 * * * * *",
})
select {}
}
// Bar 队列任务处理
var NewBarProcessor = &BarProcessor{
progress: 0,
count: 0,
}
type BarProcessor struct {
progress int
count int
}
func (s *BarProcessor) Execute(data any) any {
logger.Infof("执行 %d %d => %+v ", s.count, s.progress, data)
s.count++
// 实现任务处理逻辑
i := 0
s.progress = i
for i < 5 {
// 获取任务进度
progress := s.progress
logger.Infof("data: %v => 任务进度:%d", data, progress)
// 延迟响应
time.Sleep(time.Second * 2)
// 程序中途执行错误
if i == 3 {
// arr := [1]int{1}
// arr[i] = 3
// fmt.Println(arr)
// return "i = 3"
panic("程序中途执行错误")
}
i++
// 改变任务进度
s.progress = i
}
return data
}
func TestBar(t *testing.T) {
bar := CreateQueue("bar", NewBarProcessor)
bar.RunJob(map[string]string{
"data": "wdf",
}, JobOptions{
JobId: "81923",
})
barC := CreateQueue("bar", NewBarProcessor)
barC.RunJob(map[string]string{
"corn": "*/5 * * * * *",
}, JobOptions{
JobId: "789",
Cron: "*/5 * * * * *",
})
// barDB := CreateQueue("barDB", NewBarProcessor)
// barDB.RunJob(JobData{
// SysJob: model.SysJob{
// JobID: "9123",
// JobName: "测试任务",
// },
// }, JobOptions{
// JobId: "9123",
// })
select {}
}

112
src/framework/cron/log.go Normal file
View File

@@ -0,0 +1,112 @@
package cron
import (
"encoding/json"
"time"
"ems.agt/src/framework/constants/common"
"ems.agt/src/modules/monitor/model"
"ems.agt/src/modules/monitor/repository"
)
// 实例任务执行日志收集
var newLog = cronlog{}
// cronlog 任务执行日志收集
type cronlog struct{}
// Info 任务普通信息收集
func (s cronlog) Info(msg string, keysAndValues ...any) {
// logger.Infof("Info msg: %v ====> kv: %v", msg, keysAndValues)
}
// Error 任务异常错误收集
func (s cronlog) Error(err error, msg string, keysAndValues ...any) {
// logger.Errorf("Error: %v -> msg: %v ====> kv: %v", err, msg, keysAndValues)
// logger.Errorf("k0: %v", keysAndValues[0].(*QueueJob))
// 指定的错误收集
if msg == "failed" {
// 任务对象
job := keysAndValues[0].(*QueueJob)
// 结果信息序列化字符串
jsonByte, _ := json.Marshal(map[string]any{
"name": "failed",
"message": err.Error(),
})
jobMsg := string(jsonByte)
if len(jobMsg) > 500 {
jobMsg = jobMsg[:500]
}
// 读取任务信息创建日志对象
if data, ok := job.Data.(JobData); ok {
duration := time.Since(time.UnixMilli(job.Timestamp))
sysJob := data.SysJob
if sysJob.JobID == job.Opts.JobId {
sysJobLog := model.SysJobLog{
JobName: sysJob.JobName,
JobGroup: sysJob.JobGroup,
InvokeTarget: sysJob.InvokeTarget,
TargetParams: sysJob.TargetParams,
Status: common.STATUS_NO,
JobMsg: jobMsg,
CostTime: duration.Milliseconds(),
}
// 插入数据
repository.NewSysJobLogImpl.InsertJobLog(sysJobLog)
}
}
}
}
// Completed 任务完成return的结果收集
func (s cronlog) Completed(result any, msg string, keysAndValues ...any) {
// logger.Infof("Completed: %v -> msg: %v ====> kv: %v", result, msg, keysAndValues)
// logger.Infof("k0: %v", keysAndValues[0].(*QueueJob))
// 指定的完成收集
if msg == "completed" {
// 任务对象
job := keysAndValues[0].(*QueueJob)
// 结果信息序列化字符串
jsonByte, _ := json.Marshal(map[string]any{
"name": "completed",
"message": result,
})
jobMsg := string(jsonByte)
if len(jobMsg) > 500 {
jobMsg = jobMsg[:500]
}
// 读取任务信息创建日志对象
if data, ok := job.Data.(JobData); ok {
duration := time.Since(time.UnixMilli(job.Timestamp))
sysJob := data.SysJob
if sysJob.JobID == job.Opts.JobId {
sysJobLog := model.SysJobLog{
JobName: sysJob.JobName,
JobGroup: sysJob.JobGroup,
InvokeTarget: sysJob.InvokeTarget,
TargetParams: sysJob.TargetParams,
Status: common.STATUS_YES,
JobMsg: jobMsg,
CostTime: duration.Milliseconds(),
}
// 插入数据
repository.NewSysJobLogImpl.InsertJobLog(sysJobLog)
}
}
}
}
// JobData 调度任务日志收集结构体,执行任务时传入的接收参数
type JobData struct {
// 触发执行cron重复多次
Repeat bool
// 定时任务调度表记录信息
SysJob model.SysJob
}

View File

@@ -0,0 +1,161 @@
package datasource
import (
"fmt"
"log"
"os"
"regexp"
"time"
"ems.agt/src/framework/config"
"ems.agt/src/framework/logger"
"gorm.io/driver/mysql"
"gorm.io/gorm"
gormLog "gorm.io/gorm/logger"
)
// 数据库连接实例
var dbMap = make(map[string]*gorm.DB)
type dialectInfo struct {
dialector gorm.Dialector
logging bool
}
// 载入数据库连接
func loadDialect() map[string]dialectInfo {
dialects := make(map[string]dialectInfo, 0)
// 读取数据源配置
datasource := config.Get("gorm.datasource").(map[string]any)
for key, value := range datasource {
item := value.(map[string]any)
// 数据库类型对应的数据库连接
switch item["type"] {
case "mysql":
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
item["username"],
item["password"],
item["host"],
item["port"],
item["database"],
)
dialects[key] = dialectInfo{
dialector: mysql.Open(dsn),
logging: item["logging"].(bool),
}
default:
logger.Fatalf("%s: %v\n Not Load DB Config Type", key, item)
}
}
return dialects
}
// 载入连接日志配置
func loadLogger() gormLog.Interface {
newLogger := gormLog.New(
log.New(os.Stdout, "[GORM] ", log.LstdFlags), // 将日志输出到控制台
gormLog.Config{
SlowThreshold: time.Second, // Slow SQL 阈值
LogLevel: gormLog.Info, // 日志级别 Silent不输出任何日志
ParameterizedQueries: false, // 参数化查询SQL 用实际值带入?的执行语句
Colorful: false, // 彩色日志输出
},
)
return newLogger
}
// 连接数据库实例
func Connect() {
// 遍历进行连接数据库实例
for key, info := range loadDialect() {
opts := &gorm.Config{}
// 是否需要日志输出
if info.logging {
opts.Logger = loadLogger()
}
// 创建连接
db, err := gorm.Open(info.dialector, opts)
if err != nil {
logger.Fatalf("failed error db connect: %s", err)
}
// 获取底层 SQL 数据库连接
sqlDB, err := db.DB()
if err != nil {
logger.Fatalf("failed error underlying SQL database: %v", err)
}
// 测试数据库连接
err = sqlDB.Ping()
if err != nil {
logger.Fatalf("failed error ping database: %v", err)
}
logger.Infof("database %s connection is successful.", key)
dbMap[key] = db
}
}
// 关闭数据库实例
func Close() {
for _, db := range dbMap {
sqlDB, err := db.DB()
if err != nil {
continue
}
if err := sqlDB.Close(); err != nil {
logger.Errorf("fatal error db close: %s", err)
}
}
}
// 获取默认数据源
func DefaultDB() *gorm.DB {
source := config.Get("gorm.defaultDataSourceName").(string)
return dbMap[source]
}
// 获取数据源
func DB(source string) *gorm.DB {
return dbMap[source]
}
// RawDB 原生查询语句
func RawDB(source string, sql string, parameters []any) ([]map[string]any, error) {
// 数据源
db := DefaultDB()
if source != "" {
db = DB(source)
}
// 使用正则表达式替换连续的空白字符为单个空格
fmtSql := regexp.MustCompile(`\s+`).ReplaceAllString(sql, " ")
// logger.Infof("sql=> %v", fmtSql)
// logger.Infof("parameters=> %v", parameters)
// 查询结果
var rows []map[string]any
res := db.Raw(fmtSql, parameters...).Scan(&rows)
if res.Error != nil {
return nil, res.Error
}
return rows, nil
}
// ExecDB 原生执行语句
func ExecDB(source string, sql string, parameters []any) (int64, error) {
// 数据源
db := DefaultDB()
if source != "" {
db = DB(source)
}
// 使用正则表达式替换连续的空白字符为单个空格
fmtSql := regexp.MustCompile(`\s+`).ReplaceAllString(sql, " ")
// 执行结果
res := db.Exec(fmtSql, parameters...)
if res.Error != nil {
return 0, res.Error
}
return res.RowsAffected, nil
}

View File

@@ -0,0 +1,40 @@
package errorcatch
import (
"fmt"
"ems.agt/src/framework/config"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/vo/result"
"github.com/gin-gonic/gin"
)
// ErrorCatch 全局异常捕获
func ErrorCatch() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
// 在这里处理 Panic 异常,例如记录日志或返回错误信息给客户端
if err := recover(); err != nil {
logger.Errorf("发生了 Panic 异常: %v", err)
// 返回错误响应给客户端
if config.Env() == "prod" {
c.JSON(500, result.ErrMsg("服务器内部错误"))
} else {
// 通过实现 error 接口的 Error() 方法自定义错误类型进行捕获
switch v := err.(type) {
case error:
c.JSON(500, result.ErrMsg(v.Error()))
default:
c.JSON(500, result.ErrMsg(fmt.Sprint(err)))
}
}
c.Abort() // 停止执行后续的处理函数
}
}()
c.Next()
}
}

View File

@@ -0,0 +1,49 @@
package logger
import (
"log"
"github.com/spf13/viper"
)
var logWriter *Logger
// 初始程序日志
func InitLogger() {
env := viper.GetString("env")
conf := viper.GetStringMap("logger")
fileDir := conf["filedir"].(string)
fileName := conf["filename"].(string)
level := conf["level"].(int)
maxDay := conf["maxday"].(int)
maxSize := conf["maxsize"].(int)
newLog, err := NewLogger(env, fileDir, fileName, level, maxDay, maxSize)
if err != nil {
log.Fatalf("failed to initialize logger: %v", err)
}
logWriter = newLog
}
// 关闭程序日志写入
func Close() {
logWriter.Close()
}
func Infof(format string, v ...any) {
logWriter.Infof(format, v...)
}
func Warnf(format string, v ...any) {
logWriter.Warnf(format, v...)
}
func Errorf(format string, v ...any) {
logWriter.Errorf(format, v...)
}
// Fatalf 抛出错误并退出程序
func Fatalf(format string, v ...any) {
log.Fatalf(format, v...)
}

View File

@@ -0,0 +1,190 @@
package logger
import (
"fmt"
"io"
"log"
"os"
"path/filepath"
"strings"
"time"
)
// 日志器对象
type Logger struct {
env string // 运行环境
filePath string // 文件路径
fileName string // 文件名
level int // 日志等级标识
maxDay int // 保留最长天数
maxSize int64 // 文件最大空间
fileHandle *os.File // 文件实例
logger *log.Logger // 日志实例
logLevelMap map[int]string // 日志等级标识名
logDay int // 日志当前日
}
const (
LOG_LEVEL_SILENT = iota
LOG_LEVEL_INFO
LOG_LEVEL_WARN
LOG_LEVEL_ERROR
)
// NewLogger 实例日志器对象
func NewLogger(env, fileDir, fileName string, level, maxDay, maxSize int) (*Logger, error) {
logFilePath := filepath.Join(fileDir, fileName)
if err := os.MkdirAll(filepath.Dir(logFilePath), 0750); err != nil {
return nil, fmt.Errorf("failed to mkdir logger dir: %v", err)
}
fileHandle, err := os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
return nil, fmt.Errorf("failed to open log file: %v", err)
}
writer := io.Writer(fileHandle)
if env == "local" {
writer = io.MultiWriter(fileHandle, os.Stderr)
}
logger := log.New(writer, "", log.LstdFlags|log.Lshortfile)
logLevelMap := map[int]string{
LOG_LEVEL_INFO: "INFO",
LOG_LEVEL_WARN: "WARN",
LOG_LEVEL_ERROR: "ERROR",
}
stdLogger := &Logger{
env: env,
filePath: fileDir,
fileName: fileName,
level: level,
maxDay: maxDay,
maxSize: int64(maxSize * 1024 * 1024),
fileHandle: fileHandle,
logger: logger,
logLevelMap: logLevelMap,
logDay: time.Now().Day(),
}
go stdLogger.checkFile()
return stdLogger, nil
}
// checkFile 检查文件分割,自定时调用
func (l *Logger) checkFile() {
fileInfo, err := l.fileHandle.Stat()
if err != nil {
l.logger.Printf("Failed to get log file info: %v\n", err)
return
}
currTime := time.Now()
if l.logDay != currTime.Day() {
l.logDay = currTime.Day()
l.rotateFile(currTime.AddDate(0, 0, -1).Format("2006_01_02"))
// 移除超过保存最长天数的文件
l.removeOldFile(currTime.AddDate(0, 0, -l.maxDay))
} else if fileInfo.Size() >= l.maxSize {
l.rotateFile(currTime.Format("2006_01_02_150405"))
} else if time.Since(fileInfo.ModTime()).Hours() > 24 {
l.rotateFile(fileInfo.ModTime().Format("2006_01_02"))
}
time.AfterFunc(1*time.Minute, l.checkFile)
}
// rotateFile 检查文件大小进行分割
func (l *Logger) rotateFile(timeFormat string) {
l.fileHandle.Close()
newFileName := fmt.Sprintf("%s.%s", l.fileName, timeFormat)
newFilePath := filepath.Join(l.filePath, newFileName)
oldfilePath := filepath.Join(l.filePath, l.fileName)
// 重命名
os.Rename(oldfilePath, newFilePath)
// 新文件句柄
fileHandle, err := os.OpenFile(oldfilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
l.logger.Printf("Failed to open log file: %v\n", err)
return
}
l.fileHandle = fileHandle
// 重新设置 logger 的 writer
writer := io.Writer(l.fileHandle)
if l.env == "local" {
writer = io.MultiWriter(l.fileHandle, os.Stderr)
}
l.logger.SetOutput(writer)
}
// RemoveOldFile 删除旧文件
func (l *Logger) removeOldFile(oldFileDate time.Time) {
// 遍历目标文件夹中的文件
files, err := os.ReadDir(l.filePath)
if err != nil {
l.Errorf("logger RemoveOldFile ReadDir err: %v", err.Error())
return
}
for _, file := range files {
idx := strings.LastIndex(file.Name(), ".log.")
if idx == -1 {
continue
}
dateStr := file.Name()[idx+5 : idx+15]
// 解析日期字符串
fileDate, err := time.Parse("2006_01_02", dateStr)
if err != nil {
l.Errorf("logger RemoveOldFile Parse err: %v", err.Error())
continue
}
// 判断文件日期是否在给定日期之前
if fileDate.Before(oldFileDate) {
// 删除旧文件
err := os.Remove(filepath.Join(l.filePath, file.Name()))
if err != nil {
l.Errorf("logger RemoveOldFile Remove err: %v", err.Error())
continue
}
}
}
}
// writeLog 写入chan
func (l *Logger) writeLog(level int, format string, args ...interface{}) {
if level < l.level {
return
}
logMsg := fmt.Sprintf("[%s] %s\n", l.logLevelMap[level], fmt.Sprintf(format, args...))
l.logger.Output(4, logMsg)
}
func (l *Logger) Infof(format string, args ...interface{}) {
l.writeLog(LOG_LEVEL_INFO, format, args...)
}
func (l *Logger) Warnf(format string, args ...interface{}) {
l.writeLog(LOG_LEVEL_WARN, format, args...)
}
func (l *Logger) Errorf(format string, args ...interface{}) {
l.writeLog(LOG_LEVEL_ERROR, format, args...)
}
// Close 日志关闭
func (l *Logger) Close() {
err := l.fileHandle.Close()
if err != nil {
l.logger.Printf("Failed to close log file: %v\n", err)
}
}

View File

@@ -0,0 +1,182 @@
package collectlogs
import (
"encoding/json"
"fmt"
"strings"
"time"
"ems.agt/src/framework/constants/common"
"ems.agt/src/framework/utils/ctx"
"ems.agt/src/framework/utils/parse"
"ems.agt/src/framework/vo/result"
"ems.agt/src/modules/system/model"
"ems.agt/src/modules/system/service"
"github.com/gin-gonic/gin"
)
const (
// 业务操作类型-其它
BUSINESS_TYPE_OTHER = "0"
// 业务操作类型-新增
BUSINESS_TYPE_INSERT = "1"
// 业务操作类型-修改
BUSINESS_TYPE_UPDATE = "2"
// 业务操作类型-删除
BUSINESS_TYPE_DELETE = "3"
// 业务操作类型-授权
BUSINESS_TYPE_GRANT = "4"
// 业务操作类型-导出
BUSINESS_TYPE_EXPORT = "5"
// 业务操作类型-导入
BUSINESS_TYPE_IMPORT = "6"
// 业务操作类型-强退
BUSINESS_TYPE_FORCE = "7"
// 业务操作类型-清空数据
BUSINESS_TYPE_CLEAN = "8"
)
const (
// 操作人类别-其它
OPERATOR_TYPE_OTHER = "0"
// 操作人类别-后台用户
OPERATOR_TYPE_MANAGE = "1"
// 操作人类别-手机端用户
OPERATOR_TYPE_MOBILE = "2"
)
// Option 操作日志参数
type Options struct {
Title string `json:"title"` // 标题
BusinessType string `json:"businessType"` // 类型,默认常量 BUSINESS_TYPE_OTHER
OperatorType string `json:"operatorType"` // 操作人类别,默认常量 OPERATOR_TYPE_OTHER
IsSaveRequestData bool `json:"isSaveRequestData"` // 是否保存请求的参数
IsSaveResponseData bool `json:"isSaveResponseData"` // 是否保存响应的参数
}
// OptionNew 操作日志参数默认值
//
// 标题 "title":"--"
//
// 类型 "businessType": BUSINESS_TYPE_OTHER
//
// 注意之后JSON反序列使用c.ShouldBindBodyWith(&params, binding.JSON)
func OptionNew(title, businessType string) Options {
return Options{
Title: title,
BusinessType: businessType,
OperatorType: OPERATOR_TYPE_OTHER,
IsSaveRequestData: true,
IsSaveResponseData: true,
}
}
// 敏感属性字段进行掩码
var maskProperties []string = []string{
"password",
"oldPassword",
"newPassword",
"confirmPassword",
}
// OperateLog 访问操作日志记录
//
// 请在用户身份授权认证校验后使用以便获取登录用户信息
func OperateLog(options Options) gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("startTime", time.Now())
// 函数名
funcName := c.HandlerName()
lastDotIndex := strings.LastIndex(funcName, "/")
funcName = funcName[lastDotIndex+1:]
// 解析ip地址
ipaddr, location := ctx.IPAddrLocation(c)
// 获取登录用户信息
loginUser, err := ctx.LoginUser(c)
if err != nil {
c.JSON(401, result.CodeMsg(401, "无效身份授权"))
c.Abort() // 停止执行后续的处理函数
return
}
// 操作日志记录
operLog := model.SysLogOperate{
Title: options.Title,
BusinessType: options.BusinessType,
OperatorType: options.OperatorType,
Method: funcName,
OperURL: c.Request.RequestURI,
RequestMethod: c.Request.Method,
OperIP: ipaddr,
OperLocation: location,
OperName: loginUser.User.UserName,
DeptName: loginUser.User.Dept.DeptName,
}
if loginUser.User.UserType == "sys" {
operLog.OperatorType = OPERATOR_TYPE_MANAGE
}
// 是否需要保存request参数和值
if options.IsSaveRequestData {
params := ctx.RequestParamsMap(c)
for k, v := range params {
// 敏感属性字段进行掩码
for _, s := range maskProperties {
if s == k {
params[k] = parse.SafeContent(v.(string))
break
}
}
}
jsonStr, _ := json.Marshal(params)
paramsStr := string(jsonStr)
if len(paramsStr) > 2000 {
paramsStr = paramsStr[:2000]
}
operLog.OperParam = paramsStr
}
// 调用下一个处理程序
c.Next()
// 响应状态
status := c.Writer.Status()
if status == 200 {
operLog.Status = common.STATUS_YES
} else {
operLog.Status = common.STATUS_NO
}
// 是否需要保存response参数和值
if options.IsSaveResponseData {
contentDisposition := c.Writer.Header().Get("Content-Disposition")
contentType := c.Writer.Header().Get("Content-Type")
content := contentType + contentDisposition
msg := fmt.Sprintf(`{"status":"%d","size":"%d","content-type":"%s"}`, status, c.Writer.Size(), content)
operLog.OperMsg = msg
}
// 日志记录时间
duration := time.Since(c.GetTime("startTime"))
operLog.CostTime = duration.Milliseconds()
operLog.OperTime = time.Now().UnixMilli()
// 保存操作记录到数据库
service.NewSysLogOperateImpl.InsertSysLogOperate(operLog)
}
}

View File

@@ -0,0 +1,83 @@
package middleware
import (
"fmt"
"strings"
"ems.agt/src/framework/config"
"github.com/gin-gonic/gin"
)
// Cors 跨域
func Cors() gin.HandlerFunc {
return func(c *gin.Context) {
// 设置Vary头部
c.Header("Vary", "Origin")
c.Header("Keep-Alive", "timeout=5")
requestOrigin := c.GetHeader("Origin")
if requestOrigin == "" {
c.Next()
return
}
origin := requestOrigin
if v := config.Get("cors.origin"); v != nil {
origin = v.(string)
}
c.Header("Access-Control-Allow-Origin", origin)
if v := config.Get("cors.credentials"); v != nil && v.(bool) {
c.Header("Access-Control-Allow-Credentials", "true")
}
// OPTIONS
if method := c.Request.Method; method == "OPTIONS" {
requestMethod := c.GetHeader("Access-Control-Request-Method")
if requestMethod == "" {
c.Next()
return
}
// 响应最大时间值
if v := config.Get("cors.maxAge"); v != nil && v.(int) > 10000 {
c.Header("Access-Control-Max-Age", fmt.Sprint(v))
}
// 允许方法
if v := config.Get("cors.allowMethods"); v != nil {
var allowMethods = make([]string, 0)
for _, s := range v.([]any) {
allowMethods = append(allowMethods, s.(string))
}
c.Header("Access-Control-Allow-Methods", strings.Join(allowMethods, ","))
} else {
c.Header("Access-Control-Allow-Methods", "GET,HEAD,PUT,POST,DELETE,PATCH")
}
// 允许请求头
if v := config.Get("cors.allowHeaders"); v != nil {
var allowHeaders = make([]string, 0)
for _, s := range v.([]any) {
allowHeaders = append(allowHeaders, s.(string))
}
c.Header("Access-Control-Allow-Headers", strings.Join(allowHeaders, ","))
}
c.AbortWithStatus(204)
return
}
// 暴露请求头
if v := config.Get("cors.exposeHeaders"); v != nil {
var exposeHeaders = make([]string, 0)
for _, s := range v.([]any) {
exposeHeaders = append(exposeHeaders, s.(string))
}
c.Header("Access-Control-Expose-Headers", strings.Join(exposeHeaders, ","))
}
c.Next()
}
}

View File

@@ -0,0 +1,180 @@
package middleware
import (
"fmt"
AdminConstants "ems.agt/src/framework/constants/admin"
commonConstants "ems.agt/src/framework/constants/common"
ctxUtils "ems.agt/src/framework/utils/ctx"
tokenUtils "ems.agt/src/framework/utils/token"
"ems.agt/src/framework/vo/result"
"github.com/gin-gonic/gin"
)
// PreAuthorize 用户身份授权认证校验
//
// 只需含有其中角色 "hasRoles": {"xxx"},
//
// 只需含有其中权限 "hasPerms": {"xxx"},
//
// 同时匹配其中角色 "matchRoles": {"xxx"},
//
// 同时匹配其中权限 "matchPerms": {"xxx"},
func PreAuthorize(options map[string][]string) gin.HandlerFunc {
return func(c *gin.Context) {
// 获取请求头标识信息
tokenStr := ctxUtils.Authorization(c)
if tokenStr == "" {
c.JSON(401, result.CodeMsg(401, "无效身份授权"))
c.Abort() // 停止执行后续的处理函数
return
}
// 验证令牌
claims, err := tokenUtils.Verify(tokenStr)
if err != nil {
c.JSON(401, result.CodeMsg(401, err.Error()))
c.Abort() // 停止执行后续的处理函数
return
}
// 获取缓存的用户信息
loginUser := tokenUtils.LoginUser(claims)
if loginUser.UserID == "" {
c.JSON(401, result.CodeMsg(401, "无效身份授权"))
c.Abort() // 停止执行后续的处理函数
return
}
// 检查刷新有效期后存入上下文
tokenUtils.RefreshIn(&loginUser)
c.Set(commonConstants.CTX_LOGIN_USER, loginUser)
// 登录用户角色权限校验
if options != nil {
var roles []string
for _, item := range loginUser.User.Roles {
roles = append(roles, item.RoleKey)
}
perms := loginUser.Permissions
verifyOk := verifyRolePermission(roles, perms, options)
if !verifyOk {
msg := fmt.Sprintf("无权访问 %s %s", c.Request.Method, c.Request.RequestURI)
c.JSON(403, result.CodeMsg(403, msg))
c.Abort() // 停止执行后续的处理函数
return
}
}
// 调用下一个处理程序
c.Next()
}
}
// verifyRolePermission 校验角色权限是否满足
//
// roles 角色字符数组
//
// perms 权限字符数组
//
// options 参数
func verifyRolePermission(roles, perms []string, options map[string][]string) bool {
// 直接放行 管理员角色或任意权限
if contains(roles, AdminConstants.ROLE_KEY) || contains(perms, AdminConstants.PERMISSION) {
return true
}
opts := make([]bool, 4)
// 只需含有其中角色
hasRole := false
if arr, ok := options["hasRoles"]; ok && len(arr) > 0 {
hasRole = some(roles, arr)
opts[0] = true
}
// 只需含有其中权限
hasPerms := false
if arr, ok := options["hasPerms"]; ok && len(arr) > 0 {
hasPerms = some(perms, arr)
opts[1] = true
}
// 同时匹配其中角色
matchRoles := false
if arr, ok := options["matchRoles"]; ok && len(arr) > 0 {
matchRoles = every(roles, arr)
opts[2] = true
}
// 同时匹配其中权限
matchPerms := false
if arr, ok := options["matchPerms"]; ok && len(arr) > 0 {
matchPerms = every(perms, arr)
opts[3] = true
}
// 同时判断 含有其中
if opts[0] && opts[1] {
return hasRole || hasPerms
}
// 同时判断 匹配其中
if opts[2] && opts[3] {
return matchRoles && matchPerms
}
// 同时判断 含有其中且匹配其中
if opts[0] && opts[3] {
return hasRole && matchPerms
}
if opts[1] && opts[2] {
return hasPerms && matchRoles
}
return hasRole || hasPerms || matchRoles || matchPerms
}
// contains 检查字符串数组中是否包含指定的字符串
func contains(arr []string, target string) bool {
for _, str := range arr {
if str == target {
return true
}
}
return false
}
// some 检查字符串数组中含有其中一项
func some(origin []string, target []string) bool {
has := false
for _, t := range target {
for _, o := range origin {
if t == o {
has = true
break
}
}
if has {
break
}
}
return has
}
// every 检查字符串数组中同时包含所有项
func every(origin []string, target []string) bool {
match := true
for _, t := range target {
found := false
for _, o := range origin {
if t == o {
found = true
break
}
}
if !found {
match = false
break
}
}
return match
}

View File

@@ -0,0 +1,101 @@
package middleware
import (
"fmt"
"strings"
"time"
"ems.agt/src/framework/constants/cachekey"
"ems.agt/src/framework/redis"
"ems.agt/src/framework/utils/ctx"
"ems.agt/src/framework/utils/ip2region"
"ems.agt/src/framework/vo/result"
"github.com/gin-gonic/gin"
)
const (
// 默认策略全局限流
LIMIT_GLOBAL = 1
// 根据请求者IP进行限流
LIMIT_IP = 2
// 根据用户ID进行限流
LIMIT_USER = 3
)
// LimitOption 请求限流参数
type LimitOption struct {
Time int64 `json:"time"` // 限流时间,单位秒
Count int64 `json:"count"` // 限流次数
Type int64 `json:"type"` // 限流条件类型,默认LIMIT_GLOBAL
}
// RateLimit 请求限流
//
// 示例参数middleware.LimitOption{ Time: 5, Count: 10, Type: middleware.LIMIT_IP }
//
// 参数表示5秒内最多请求10次限制类型为 IP
//
// 使用 USER 时,请在用户身份授权认证校验后使用
// 以便获取登录用户信息,无用户信息时默认为 GLOBAL
func RateLimit(option LimitOption) gin.HandlerFunc {
return func(c *gin.Context) {
// 初始可选参数数据
if option.Time < 5 {
option.Time = 5
}
if option.Count < 10 {
option.Count = 10
}
if option.Type == 0 {
option.Type = LIMIT_GLOBAL
}
// 获取执行函数名称
funcName := c.HandlerName()
lastDotIndex := strings.LastIndex(funcName, "/")
funcName = funcName[lastDotIndex+1:]
// 生成限流key
var limitKey string = cachekey.RATE_LIMIT_KEY + funcName
// 用户
if option.Type == LIMIT_USER {
loginUser, err := ctx.LoginUser(c)
if err != nil {
c.JSON(401, result.Err(map[string]any{
"code": 401,
"msg": err.Error(),
}))
c.Abort() // 停止执行后续的处理函数
return
}
limitKey = cachekey.RATE_LIMIT_KEY + loginUser.UserID + ":" + funcName
}
// IP
if option.Type == LIMIT_IP {
clientIP := ip2region.ClientIP(c.ClientIP())
limitKey = cachekey.RATE_LIMIT_KEY + clientIP + ":" + funcName
}
// 在Redis查询并记录请求次数
rateCount, _ := redis.RateLimit("", limitKey, option.Time, option.Count)
rateTime, _ := redis.GetExpire("", limitKey)
// 设置响应头中的限流声明字段
c.Header("X-RateLimit-Limit", fmt.Sprintf("%d", option.Count)) // 总请求数限制
c.Header("X-RateLimit-Remaining", fmt.Sprintf("%d", option.Count-rateCount)) // 剩余可用请求数
c.Header("X-RateLimit-Reset", fmt.Sprintf("%d", time.Now().Unix()+int64(rateTime))) // 重置时间戳
if rateCount >= option.Count {
c.JSON(200, result.ErrMsg("访问过于频繁,请稍候再试"))
c.Abort() // 停止执行后续的处理函数
return
}
// 调用下一个处理程序
c.Next()
}
}

View File

@@ -0,0 +1,84 @@
package repeat
import (
"encoding/json"
"strconv"
"time"
"ems.agt/src/framework/constants/cachekey"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/redis"
"ems.agt/src/framework/utils/ctx"
"ems.agt/src/framework/utils/ip2region"
"ems.agt/src/framework/vo/result"
"github.com/gin-gonic/gin"
)
// repeatParam 重复提交参数的类型定义
type repeatParam struct {
Time int64 `json:"time"`
Params string `json:"params"`
}
// RepeatSubmit 防止表单重复提交,小于间隔时间视为重复提交
//
// 间隔时间(单位秒) 默认:5
//
// 注意之后JSON反序列使用c.ShouldBindBodyWith(&params, binding.JSON)
func RepeatSubmit(interval int64) gin.HandlerFunc {
return func(c *gin.Context) {
if interval < 5 {
interval = 5
}
// 提交参数
params := ctx.RequestParamsMap(c)
paramsJSONByte, err := json.Marshal(params)
if err != nil {
logger.Errorf("RepeatSubmit params json marshal err: %v", err)
}
paramsJSONStr := string(paramsJSONByte)
// 唯一标识指定key + 客户端IP + 请求地址)
clientIP := ip2region.ClientIP(c.ClientIP())
repeatKey := cachekey.REPEAT_SUBMIT_KEY + clientIP + ":" + c.Request.RequestURI
// 在Redis查询并记录请求次数
repeatStr, _ := redis.Get("", repeatKey)
if repeatStr != "" {
var rp repeatParam
err := json.Unmarshal([]byte(repeatStr), &rp)
if err != nil {
logger.Errorf("RepeatSubmit repeatStr json unmarshal err: %v", err)
}
compareTime := time.Now().Unix() - rp.Time
compareParams := rp.Params == paramsJSONStr
// 设置重复提交声明响应头(毫秒)
c.Header("X-RepeatSubmit-Rest", strconv.FormatInt(time.Now().Add(time.Duration(compareTime)*time.Second).UnixNano()/int64(time.Millisecond), 10))
// 小于间隔时间且参数内容一致
if compareTime < interval && compareParams {
c.JSON(200, result.ErrMsg("不允许重复提交,请稍候再试"))
c.Abort()
return
}
}
// 当前请求参数
rp := repeatParam{
Time: time.Now().Unix(),
Params: paramsJSONStr,
}
rpJSON, err := json.Marshal(rp)
if err != nil {
logger.Errorf("RepeatSubmit rp json marshal err: %v", err)
}
// 保存请求时间和参数
redis.SetByExpire("", repeatKey, string(rpJSON), time.Duration(interval)*time.Second)
// 调用下一个处理程序
c.Next()
}
}

View File

@@ -0,0 +1,23 @@
package middleware
import (
"time"
"ems.agt/src/framework/logger"
"github.com/gin-gonic/gin"
)
// Report 请求响应日志
func Report() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
// 调用下一个处理程序
c.Next()
// 计算请求处理时间,并打印日志
duration := time.Since(start)
logger.Infof("%s %s report end=> %v", c.Request.Method, c.Request.RequestURI, duration)
}
}

View File

@@ -0,0 +1,22 @@
package security
import (
"ems.agt/src/framework/config"
"ems.agt/src/framework/utils/generate"
"github.com/gin-gonic/gin"
)
// TODO
// csp 这将帮助防止跨站脚本攻击XSS
// HTTP 响应头 Content-Security-Policy 允许站点管理者控制指定的页面加载哪些资源。
func csp(c *gin.Context) {
enable := false
if v := config.Get("security.csp.enable"); v != nil {
enable = v.(bool)
}
if enable {
c.Header("x-csp-nonce", generate.Code(8))
}
}

View File

@@ -0,0 +1,37 @@
package security
import (
"fmt"
"ems.agt/src/framework/config"
"github.com/gin-gonic/gin"
)
// hsts 是一个安全功能 HTTP Strict Transport Security通常简称为 HSTS
// 它告诉浏览器只能通过 HTTPS 访问当前资源,而不是 HTTP。
func hsts(c *gin.Context) {
enable := false
if v := config.Get("security.hsts.enable"); v != nil {
enable = v.(bool)
}
maxAge := 365 * 24 * 3600
if v := config.Get("security.hsts.maxAge"); v != nil {
maxAge = v.(int)
}
includeSubdomains := false
if v := config.Get("security.hsts.includeSubdomains"); v != nil {
includeSubdomains = v.(bool)
}
str := fmt.Sprintf("max-age=%d", maxAge)
if includeSubdomains {
str += "; includeSubdomains"
}
if enable {
c.Header("strict-transport-security", str)
}
}

View File

@@ -0,0 +1,20 @@
package security
import (
"ems.agt/src/framework/config"
"github.com/gin-gonic/gin"
)
// noopen 用于指定 IE 8 以上版本的用户不打开文件而直接保存文件。
// 在下载对话框中不显式“打开”选项。
func noopen(c *gin.Context) {
enable := false
if v := config.Get("security.noopen.enable"); v != nil {
enable = v.(bool)
}
if enable {
c.Header("x-download-options", "noopen")
}
}

View File

@@ -0,0 +1,26 @@
package security
import (
"ems.agt/src/framework/config"
"github.com/gin-gonic/gin"
)
// nosniff 用于防止 XSS 等跨站脚本攻击
// 如果从 script 或 stylesheet 读入的文件的 MIME 类型与指定 MIME 类型不匹配,不允许读取该文件。
func nosniff(c *gin.Context) {
// 排除状态码范围
status := c.Writer.Status()
if status >= 300 && status <= 308 {
return
}
enable := false
if v := config.Get("security.nosniff.enable"); v != nil {
enable = v.(bool)
}
if enable {
c.Header("x-content-type-options", "nosniff")
}
}

View File

@@ -0,0 +1,76 @@
package security
import (
"net/url"
"ems.agt/src/framework/config"
"ems.agt/src/framework/vo/result"
"github.com/gin-gonic/gin"
)
// referer 配置 referer 的 host 部分
func referer(c *gin.Context) {
enable := false
if v := config.Get("security.csrf.enable"); v != nil {
enable = v.(bool)
}
if !enable {
return
}
// csrf 校验类型
okType := false
if v := config.Get("security.csrf.type"); v != nil {
vType := v.(string)
if vType == "all" || vType == "any" || vType == "referer" {
okType = true
}
}
if !okType {
return
}
// 忽略请求方法
method := c.Request.Method
ignoreMethods := []string{"GET", "HEAD", "OPTIONS", "TRACE"}
for _, ignore := range ignoreMethods {
if ignore == method {
return
}
}
referer := c.GetHeader("Referer")
if referer == "" {
c.AbortWithStatusJSON(200, result.ErrMsg("无效 Referer 未知"))
return
}
// 获取host
u, err := url.Parse(referer)
if err != nil {
c.AbortWithStatusJSON(200, result.ErrMsg("无效 Referer 未知"))
return
}
host := u.Host
// 允许的来源白名单
refererWhiteList := make([]string, 0)
if v := config.Get("security.csrf.refererWhiteList"); v != nil {
for _, s := range v.([]any) {
refererWhiteList = append(refererWhiteList, s.(string))
}
}
// 遍历检查
ok := false
for _, domain := range refererWhiteList {
if domain == host {
ok = true
}
}
if !ok {
c.AbortWithStatusJSON(200, result.ErrMsg("无效 Referer "+host))
return
}
}

View File

@@ -0,0 +1,23 @@
package security
import (
"github.com/gin-gonic/gin"
)
// Security 安全
func Security() gin.HandlerFunc {
return func(c *gin.Context) {
// 拦截判断是否有效Referer
referer(c)
// 无拦截,仅仅设置响应头
xframe(c)
csp(c)
hsts(c)
noopen(c)
nosniff(c)
xssProtection(c)
c.Next()
}
}

View File

@@ -0,0 +1,26 @@
package security
import (
"ems.agt/src/framework/config"
"github.com/gin-gonic/gin"
)
// xframe 用来配置 X-Frame-Options 响应头
// 用来给浏览器指示允许一个页面可否在 frame, iframe, embed 或者 object 中展现的标记。
// 站点可以通过确保网站没有被嵌入到别人的站点里面,从而避免 clickjacking 攻击。
func xframe(c *gin.Context) {
enable := false
if v := config.Get("security.xframe.enable"); v != nil {
enable = v.(bool)
}
value := "sameorigin"
if v := config.Get("security.xframe.value"); v != nil {
value = v.(string)
}
if enable {
c.Header("x-frame-options", value)
}
}

View File

@@ -0,0 +1,24 @@
package security
import (
"ems.agt/src/framework/config"
"github.com/gin-gonic/gin"
)
// xssProtection 用于启用浏览器的XSS过滤功能以防止 XSS 跨站脚本攻击。
func xssProtection(c *gin.Context) {
enable := false
if v := config.Get("security.xssProtection.enable"); v != nil {
enable = v.(bool)
}
value := "1; mode=block"
if v := config.Get("security.xssProtection.value"); v != nil {
value = v.(string)
}
if enable {
c.Header("x-xss-protection", value)
}
}

View File

@@ -6,8 +6,9 @@ import (
"strings"
"time"
"ems.agt/lib/core/conf"
"ems.agt/lib/log"
"ems.agt/src/framework/config"
"ems.agt/src/framework/logger"
"github.com/redis/go-redis/v9"
)
@@ -33,7 +34,7 @@ return tonumber(current);`)
func Connect() {
ctx := context.Background()
// 读取数据源配置
datasource := conf.Get("redis.dataSource").(map[string]any)
datasource := config.Get("redis.dataSource").(map[string]any)
for k, v := range datasource {
client := v.(map[string]any)
// 创建连接
@@ -46,10 +47,9 @@ func Connect() {
// 测试数据库连接
pong, err := rdb.Ping(ctx).Result()
if err != nil {
log.Fatalf("failed error ping redis %s %d is %v", client["host"], client["db"], err)
continue
logger.Fatalf("Ping redis %s is %v", k, err)
}
log.Infof("redis %s %d %s connection is successful.", client["host"], client["db"], pong)
logger.Infof("redis %s %s %d connection is successful.", k, pong, client["db"].(int))
rdbMap[k] = rdb
}
}
@@ -58,14 +58,14 @@ func Connect() {
func Close() {
for _, rdb := range rdbMap {
if err := rdb.Close(); err != nil {
log.Errorf("fatal error db close: %s", err)
logger.Errorf("fatal error db close: %s", err)
}
}
}
// 获取默认实例
func DefaultRDB() *redis.Client {
source := conf.Get("redis.defaultDataSourceName").(string)
source := config.Get("redis.defaultDataSourceName").(string)
return rdbMap[source]
}
@@ -186,7 +186,7 @@ func GetKeys(source string, pattern string) ([]string, error) {
// 使用 SCAN 命令获取匹配的键
batchKeys, nextCursor, err := rdb.Scan(ctx, cursor, pattern, 100).Result()
if err != nil {
log.Errorf("Failed to scan keys: %v", err)
logger.Errorf("Failed to scan keys: %v", err)
return keys, err
}
cursor = nextCursor
@@ -214,7 +214,7 @@ func GetBatch(source string, keys []string) ([]any, error) {
// 获取缓存数据
result, err := rdb.MGet(context.Background(), keys...).Result()
if err != nil {
log.Errorf("Failed to get batch data: %v", err)
logger.Errorf("Failed to get batch data: %v", err)
return []any{}, err
}
return result, nil
@@ -279,7 +279,7 @@ func Set(source, key string, value any) (bool, error) {
ctx := context.Background()
err := rdb.Set(ctx, key, value, 0).Err()
if err != nil {
log.Errorf("redis lua script err %v", err)
logger.Errorf("redis Set err %v", err)
return false, err
}
return true, nil
@@ -296,7 +296,7 @@ func SetByExpire(source, key string, value any, expiration time.Duration) (bool,
ctx := context.Background()
err := rdb.Set(ctx, key, value, expiration).Err()
if err != nil {
log.Errorf("redis lua script err %v", err)
logger.Errorf("redis SetByExpire err %v", err)
return false, err
}
return true, nil
@@ -313,7 +313,7 @@ func Del(source string, key string) (bool, error) {
ctx := context.Background()
err := rdb.Del(ctx, key).Err()
if err != nil {
log.Errorf("redis lua script err %v", err)
logger.Errorf("redis Del err %v", err)
return false, err
}
return true, nil
@@ -334,7 +334,7 @@ func DelKeys(source string, keys []string) (bool, error) {
ctx := context.Background()
err := rdb.Del(ctx, keys...).Err()
if err != nil {
log.Errorf("redis lua script err %v", err)
logger.Errorf("redis DelKeys err %v", err)
return false, err
}
return true, nil
@@ -351,7 +351,7 @@ func RateLimit(source, limitKey string, time, count int64) (int64, error) {
ctx := context.Background()
result, err := rateLimitCommand.Run(ctx, rdb, []string{limitKey}, time, count).Result()
if err != nil {
log.Errorf("redis lua script err %v", err)
logger.Errorf("redis RateLimit err %v", err)
return 0, err
}
return result.(int64), err

View File

@@ -0,0 +1,199 @@
package ctx
import (
"fmt"
"strings"
"ems.agt/src/framework/config"
"ems.agt/src/framework/constants/common"
"ems.agt/src/framework/constants/roledatascope"
"ems.agt/src/framework/constants/token"
"ems.agt/src/framework/utils/ip2region"
"ems.agt/src/framework/utils/ua"
"ems.agt/src/framework/vo"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
)
// QueryMap 查询参数转换Map
func QueryMap(c *gin.Context) map[string]any {
queryValues := c.Request.URL.Query()
queryParams := make(map[string]any)
for key, values := range queryValues {
queryParams[key] = values[0]
}
return queryParams
}
// BodyJSONMap JSON参数转换Map
func BodyJSONMap(c *gin.Context) map[string]any {
params := make(map[string]any)
c.ShouldBindBodyWith(&params, binding.JSON)
return params
}
// RequestParamsMap 请求参数转换Map
func RequestParamsMap(c *gin.Context) map[string]any {
params := make(map[string]any)
// json
if strings.HasPrefix(c.ContentType(), "application/json") {
c.ShouldBindBodyWith(&params, binding.JSON)
}
// 表单
bodyParams := c.Request.PostForm
for key, value := range bodyParams {
params[key] = value[0]
}
// 查询
queryParams := c.Request.URL.Query()
for key, value := range queryParams {
params[key] = value[0]
}
return params
}
// IPAddrLocation 解析ip地址
func IPAddrLocation(c *gin.Context) (string, string) {
ip := ip2region.ClientIP(c.ClientIP())
location := ip2region.RealAddressByIp(ip)
return ip, location
}
// Authorization 解析请求头
func Authorization(c *gin.Context) string {
authHeader := c.GetHeader(token.HEADER_KEY)
if authHeader == "" {
return ""
}
// 拆分 Authorization 请求头,提取 JWT 令牌部分
arr := strings.Split(authHeader, token.HEADER_PREFIX)
if len(arr) == 2 && arr[1] == "" {
return ""
}
return arr[1]
}
// UaOsBrowser 解析请求用户代理信息
func UaOsBrowser(c *gin.Context) (string, string) {
userAgent := c.GetHeader("user-agent")
uaInfo := ua.Info(userAgent)
browser := "未知 未知"
bName, bVersion := uaInfo.Browser()
if bName != "" && bVersion != "" {
browser = bName + " " + bVersion
}
os := "未知 未知"
bos := uaInfo.OS()
if bos != "" {
os = bos
}
return os, browser
}
// LoginUser 登录用户信息
func LoginUser(c *gin.Context) (vo.LoginUser, error) {
value, exists := c.Get(common.CTX_LOGIN_USER)
if exists {
return value.(vo.LoginUser), nil
}
return vo.LoginUser{}, fmt.Errorf("无效登录用户信息")
}
// LoginUserToUserID 登录用户信息-用户ID
func LoginUserToUserID(c *gin.Context) string {
value, exists := c.Get(common.CTX_LOGIN_USER)
if exists {
loginUser := value.(vo.LoginUser)
return loginUser.UserID
}
return ""
}
// LoginUserToUserName 登录用户信息-用户名称
func LoginUserToUserName(c *gin.Context) string {
value, exists := c.Get(common.CTX_LOGIN_USER)
if exists {
loginUser := value.(vo.LoginUser)
return loginUser.User.UserName
}
return ""
}
// LoginUserToDataScopeSQL 登录用户信息-角色数据范围过滤SQL字符串
func LoginUserToDataScopeSQL(c *gin.Context, deptAlias string, userAlias string) string {
dataScopeSQL := ""
// 登录用户信息
loginUser, err := LoginUser(c)
if err != nil {
return dataScopeSQL
}
userInfo := loginUser.User
// 如果是管理员,则不过滤数据
if config.IsAdmin(userInfo.UserID) {
return dataScopeSQL
}
// 无用户角色
if len(userInfo.Roles) <= 0 {
return dataScopeSQL
}
// 记录角色权限范围定义添加过, 非自定数据权限不需要重复拼接SQL
var scopeKeys []string
var conditions []string
for _, role := range userInfo.Roles {
dataScope := role.DataScope
if roledatascope.ALL == dataScope {
break
}
if roledatascope.CUSTOM != dataScope {
hasKey := false
for _, key := range scopeKeys {
if key == dataScope {
hasKey = true
break
}
}
if hasKey {
continue
}
}
if roledatascope.CUSTOM == dataScope {
sql := fmt.Sprintf(`%s.dept_id IN ( SELECT dept_id FROM sys_role_dept WHERE role_id = '%s' )`, deptAlias, role.RoleID)
conditions = append(conditions, sql)
}
if roledatascope.DEPT_AND_CHILD == dataScope {
sql := fmt.Sprintf(`%s.dept_id IN ( SELECT dept_id FROM sys_dept WHERE dept_id = '%s' or find_in_set('%s' , ancestors ) )`, deptAlias, userInfo.DeptID, userInfo.DeptID)
conditions = append(conditions, sql)
}
if roledatascope.SELF == dataScope {
// 数据权限为仅本人且没有userAlias别名不查询任何数据
if userAlias == "" {
sql := fmt.Sprintf(`%s.dept_id = '0'`, deptAlias)
conditions = append(conditions, sql)
} else {
sql := fmt.Sprintf(`%s.user_id = '%s'`, userAlias, userInfo.UserID)
conditions = append(conditions, sql)
}
}
// 记录角色范围
scopeKeys = append(scopeKeys, dataScope)
}
// 构建查询条件语句
if len(conditions) > 0 {
dataScopeSQL = fmt.Sprintf(" AND ( %s ) ", strings.Join(conditions, " OR "))
}
return dataScopeSQL
}

View File

@@ -0,0 +1,69 @@
package date
import (
"time"
"ems.agt/src/framework/logger"
)
const (
// 年 列如2022
YYYY = "2006"
// 年-月 列如2022-12
YYYY_MM = "2006-01"
// 年-月-日 列如2022-12-30
YYYY_MM_DD = "2006-01-02"
// 年月日时分秒 列如20221230010159
YYYYMMDDHHMMSS = "20060102150405"
// 年-月-日 时:分:秒 列如2022-12-30 01:01:59
YYYY_MM_DD_HH_MM_SS = "2006-01-02 15:04:05"
)
// 格式时间字符串
//
// dateStr 时间字符串
//
// formatStr 时间格式 默认YYYY-MM-DD HH:mm:ss
func ParseStrToDate(dateStr, formatStr string) time.Time {
t, err := time.Parse(formatStr, dateStr)
if err != nil {
logger.Infof("utils ParseStrToDate err %v", err)
return time.Time{}
}
return t
}
// 格式时间
//
// date 可转的Date对象
//
// formatStr 时间格式 默认YYYY-MM-DD HH:mm:ss
func ParseDateToStr(date any, formatStr string) string {
t, ok := date.(time.Time)
if !ok {
switch v := date.(type) {
case int64:
if v == 0 {
return ""
}
t = time.UnixMilli(v)
case string:
parsedTime, err := time.Parse(formatStr, v)
if err != nil {
logger.Infof("utils ParseDateToStr err %v", err)
return ""
}
t = parsedTime
default:
return ""
}
}
return t.Format(formatStr)
}
// 格式时间成日期路径
//
// 年/月 列如2022/12
func ParseDatePath(date time.Time) string {
return date.Format("2006/01")
}

View File

@@ -0,0 +1,154 @@
package file
import (
"fmt"
"mime/multipart"
"os"
"path/filepath"
"time"
"ems.agt/src/framework/constants/uploadsubpath"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/utils/date"
"github.com/xuri/excelize/v2"
)
// TransferExeclUploadFile 表格文件上传保存
//
// file 上传文件对象
func TransferExeclUploadFile(file *multipart.FileHeader) (string, error) {
// 上传文件检查
err := isAllowWrite(file.Filename, []string{".xls", ".xlsx"}, file.Size)
if err != nil {
return "", err
}
// 上传资源路径
_, dir := resourceUpload()
// 新文件名称并组装文件地址
filePath := filepath.Join(uploadsubpath.IMPORT, date.ParseDatePath(time.Now()))
fileName := generateFileName(file.Filename)
writePathFile := filepath.Join(dir, filePath, fileName)
// 存入新文件路径
err = transferToNewFile(file, writePathFile)
if err != nil {
return "", err
}
return filepath.ToSlash(writePathFile), nil
}
// 表格读取数据
//
// filePath 文件路径地址
//
// sheetName 工作簿名称, 空字符默认Sheet1
func ReadSheet(filePath, sheetName string) ([]map[string]string, error) {
data := make([]map[string]string, 0)
// 打开 Excel 文件
f, err := excelize.OpenFile(filePath)
if err != nil {
return data, err
}
defer func() {
if err := f.Close(); err != nil {
logger.Errorf("工作表文件关闭失败 : %v", err)
}
}()
// 检查工作簿是否存在
if sheetName == "" {
sheetName = "Sheet1"
}
if visible, _ := f.GetSheetVisible(sheetName); !visible {
return data, fmt.Errorf("读取工作簿 %s 失败", sheetName)
}
// 获取工作簿记录
rows, err := f.GetRows(sheetName)
if err != nil {
return data, err
}
for i, row := range rows {
// 跳过第一行
if i == 0 {
continue
}
// 遍历每个单元格
rowData := map[string]string{}
for columnIndex, cellValue := range row {
columnName, _ := excelize.ColumnNumberToName(columnIndex + 1)
rowData[columnName] = cellValue
}
data = append(data, rowData)
}
return data, nil
}
// 表格写入数据
//
// headerCells 第一行表头标题 "A1":"?"
//
// dataCells 从第二行开始的数据 "A2":"?"
//
// fileName 文件名称
//
// sheetName 工作簿名称, 空字符默认Sheet1
func WriteSheet(headerCells map[string]string, dataCells []map[string]any, fileName, sheetName string) (string, error) {
f := excelize.NewFile()
defer func() {
if err := f.Close(); err != nil {
logger.Errorf("工作表文件关闭失败 : %v", err)
}
}()
// 创建一个工作表
if sheetName == "" {
sheetName = "Sheet1"
}
index, err := f.NewSheet(sheetName)
if err != nil {
return "", fmt.Errorf("创建工作表失败 %v", err)
}
// 设置工作簿的默认工作表
f.SetActiveSheet(index)
// 首个和最后key名称
firstKey := "A"
lastKey := "B"
// 第一行表头标题
for key, title := range headerCells {
f.SetCellValue(sheetName, key, title)
if key[:1] > lastKey {
lastKey = key[:1]
}
}
// 设置工作表上宽度为 20
f.SetColWidth(sheetName, firstKey, lastKey, 20)
// 从第二行开始的数据
for _, cell := range dataCells {
for key, value := range cell {
f.SetCellValue(sheetName, key, value)
}
}
// 上传资源路径
_, dir := resourceUpload()
filePath := filepath.Join(uploadsubpath.EXPORT, date.ParseDatePath(time.Now()))
saveFilePath := filepath.Join(dir, filePath, fileName)
// 创建文件目录
if err := os.MkdirAll(filepath.Dir(saveFilePath), 0750); err != nil {
return "", fmt.Errorf("创建保存文件失败 %v", err)
}
// 根据指定路径保存文件
if err := f.SaveAs(saveFilePath); err != nil {
return "", fmt.Errorf("保存工作表失败 %v", err)
}
return saveFilePath, nil
}

View File

@@ -0,0 +1,297 @@
package file
import (
"errors"
"fmt"
"mime/multipart"
"path"
"path/filepath"
"strconv"
"strings"
"time"
"ems.agt/src/framework/config"
"ems.agt/src/framework/constants/uploadsubpath"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/utils/date"
"ems.agt/src/framework/utils/generate"
"ems.agt/src/framework/utils/parse"
"ems.agt/src/framework/utils/regular"
)
/**最大文件名长度 */
const DEFAULT_FILE_NAME_LENGTH = 100
// 文件上传路径 prefix, dir
func resourceUpload() (string, string) {
upload := config.Get("staticFile.upload").(map[string]any)
dir, err := filepath.Abs(upload["dir"].(string))
if err != nil {
logger.Errorf("file resourceUpload err %v", err)
}
return upload["prefix"].(string), dir
}
// 最大上传文件大小
func uploadFileSize() int64 {
fileSize := 1 * 1024 * 1024
size := config.Get("upload.fileSize").(int)
if size > 1 {
fileSize = size * 1024 * 1024
}
return int64(fileSize)
}
// 文件上传扩展名白名单
func uploadWhiteList() []string {
arr := config.Get("upload.whitelist").([]any)
strings := make([]string, len(arr))
for i, val := range arr {
if str, ok := val.(string); ok {
strings[i] = str
}
}
return strings
}
// 生成文件名称 fileName_随机值.extName
//
// fileName 原始文件名称含后缀logo.png
func generateFileName(fileName string) string {
fileExt := filepath.Ext(fileName)
// 替换掉后缀和特殊字符保留文件名
newFileName := regular.Replace(fileName, fileExt, "")
newFileName = regular.Replace(newFileName, `[<>:"\\|?*]+`, "")
newFileName = strings.TrimSpace(newFileName)
return fmt.Sprintf("%s_%s%s", newFileName, generate.Code(6), fileExt)
}
// 检查文件允许写入本地
//
// fileName 原始文件名称含后缀midway1_logo_iipc68.png
//
// allowExts 允许上传拓展类型,['.png']
func isAllowWrite(fileName string, allowExts []string, fileSize int64) error {
// 判断上传文件名称长度
if len(fileName) > DEFAULT_FILE_NAME_LENGTH {
return fmt.Errorf("上传文件名称长度限制最长为 %d", DEFAULT_FILE_NAME_LENGTH)
}
// 最大上传文件大小
maxFileSize := uploadFileSize()
if fileSize > maxFileSize {
return fmt.Errorf("最大上传文件大小 %s", parse.Bit(float64(maxFileSize)))
}
// 判断文件拓展是否为允许的拓展类型
fileExt := filepath.Ext(fileName)
hasExt := false
if len(allowExts) == 0 {
allowExts = uploadWhiteList()
}
for _, ext := range allowExts {
if ext == fileExt {
hasExt = true
break
}
}
if !hasExt {
return fmt.Errorf("上传文件类型不支持,仅支持以下类型:%s", strings.Join(allowExts, "、"))
}
return nil
}
// 检查文件允许本地读取
//
// filePath 文件存放资源路径URL相对地址
func isAllowRead(filePath string) error {
// 禁止目录上跳级别
if strings.Contains(filePath, "..") {
return fmt.Errorf("禁止目录上跳级别")
}
// 检查允许下载的文件规则
fileExt := filepath.Ext(filePath)
hasExt := false
for _, ext := range uploadWhiteList() {
if ext == fileExt {
hasExt = true
break
}
}
if !hasExt {
return fmt.Errorf("非法下载的文件规则:%s", fileExt)
}
return nil
}
// TransferUploadFile 上传资源文件转存
//
// subPath 子路径,默认 UploadSubPath.DEFAULT
//
// allowExts 允许上传拓展类型(含“.”),如 ['.png','.jpg']
func TransferUploadFile(file *multipart.FileHeader, subPath string, allowExts []string) (string, error) {
// 上传文件检查
err := isAllowWrite(file.Filename, allowExts, file.Size)
if err != nil {
return "", err
}
// 上传资源路径
prefix, dir := resourceUpload()
// 新文件名称并组装文件地址
fileName := generateFileName(file.Filename)
filePath := filepath.Join(subPath, date.ParseDatePath(time.Now()))
writePathFile := filepath.Join(dir, filePath, fileName)
// 存入新文件路径
err = transferToNewFile(file, writePathFile)
if err != nil {
return "", err
}
urlPath := filepath.Join(prefix, filePath, fileName)
return filepath.ToSlash(urlPath), nil
}
// ReadUploadFileStream 上传资源文件读取
//
// filePath 文件存放资源路径URL相对地址 如:/upload/common/2023/06/xxx.png
//
// headerRange 断点续传范围区间bytes=0-12131
func ReadUploadFileStream(filePath, headerRange string) (map[string]any, error) {
// 读取文件检查
err := isAllowRead(filePath)
if err != nil {
return map[string]any{}, err
}
// 上传资源路径
prefix, dir := resourceUpload()
fileAsbPath := strings.Replace(filePath, prefix, dir, 1)
// 响应结果
result := map[string]any{
"range": "",
"chunkSize": 0,
"fileSize": 0,
"data": nil,
}
// 文件大小
fileSize := getFileSize(fileAsbPath)
if fileSize <= 0 {
return result, nil
}
result["fileSize"] = fileSize
if headerRange != "" {
partsStr := strings.Replace(headerRange, "bytes=", "", 1)
parts := strings.Split(partsStr, "-")
start, err := strconv.ParseInt(parts[0], 10, 64)
if err != nil || start > fileSize {
start = 0
}
end, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil || end > fileSize {
end = fileSize - 1
}
if start > end {
start = end
}
// 分片结果
result["range"] = fmt.Sprintf("bytes %d-%d/%d", start, end, fileSize)
result["chunkSize"] = end - start + 1
byteArr, err := getFileStream(fileAsbPath, start, end)
if err != nil {
return map[string]any{}, errors.New("读取文件失败")
}
result["data"] = byteArr
return result, nil
}
byteArr, err := getFileStream(fileAsbPath, 0, fileSize)
if err != nil {
return map[string]any{}, errors.New("读取文件失败")
}
result["data"] = byteArr
return result, nil
}
// TransferChunkUploadFile 上传资源切片文件转存
//
// file 上传文件对象
//
// index 切片文件序号
//
// identifier 切片文件目录标识符
func TransferChunkUploadFile(file *multipart.FileHeader, index, identifier string) (string, error) {
// 上传文件检查
err := isAllowWrite(file.Filename, []string{}, file.Size)
if err != nil {
return "", err
}
// 上传资源路径
prefix, dir := resourceUpload()
// 新文件名称并组装文件地址
filePath := filepath.Join(uploadsubpath.CHUNK, date.ParseDatePath(time.Now()), identifier)
writePathFile := filepath.Join(dir, filePath, index)
// 存入新文件路径
err = transferToNewFile(file, writePathFile)
if err != nil {
return "", err
}
urlPath := filepath.Join(prefix, filePath, index)
return filepath.ToSlash(urlPath), nil
}
// 上传资源切片文件检查
//
// identifier 切片文件目录标识符
//
// originalFileName 原始文件名称如logo.png
func ChunkCheckFile(identifier, originalFileName string) ([]string, error) {
// 读取文件检查
err := isAllowWrite(originalFileName, []string{}, 0)
if err != nil {
return []string{}, err
}
// 上传资源路径
_, dir := resourceUpload()
dirPath := path.Join(uploadsubpath.CHUNK, date.ParseDatePath(time.Now()), identifier)
readPath := path.Join(dir, dirPath)
fileList, err := getDirFileNameList(readPath)
if err != nil {
return []string{}, errors.New("读取文件失败")
}
return fileList, nil
}
// 上传资源切片文件检查
//
// identifier 切片文件目录标识符
//
// originalFileName 原始文件名称如logo.png
//
// subPath 子路径,默认 DEFAULT
func ChunkMergeFile(identifier, originalFileName, subPath string) (string, error) {
// 读取文件检查
err := isAllowWrite(originalFileName, []string{}, 0)
if err != nil {
return "", err
}
// 上传资源路径
prefix, dir := resourceUpload()
// 切片存放目录
dirPath := path.Join(uploadsubpath.CHUNK, date.ParseDatePath(time.Now()), identifier)
readPath := path.Join(dir, dirPath)
// 组合存放文件路径
fileName := generateFileName(originalFileName)
filePath := path.Join(subPath, date.ParseDatePath(time.Now()))
writePath := path.Join(dir, filePath)
err = mergeToNewFile(readPath, writePath, fileName)
if err != nil {
return "", err
}
urlPath := filepath.Join(prefix, filePath, fileName)
return filepath.ToSlash(urlPath), nil
}

View File

@@ -0,0 +1,185 @@
package file
import (
"fmt"
"io"
"mime/multipart"
"os"
"path/filepath"
"sort"
"strconv"
"ems.agt/src/framework/logger"
)
// transferToNewFile 读取目标文件转移到新路径下
//
// readFilePath 读取目标文件
//
// writePath 写入路径
//
// fileName 文件名称
func transferToNewFile(file *multipart.FileHeader, dst string) error {
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
if err = os.MkdirAll(filepath.Dir(dst), 0750); err != nil {
return err
}
out, err := os.Create(dst)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, src)
return err
}
// mergeToNewFile 将多个文件合并成一个文件并删除合并前的切片目录文件
//
// dirPath 读取要合并文件的目录
//
// writePath 写入路径
//
// fileName 文件名称
func mergeToNewFile(dirPath string, writePath string, fileName string) error {
// 读取目录下所有文件并排序,注意文件名称是否数值
fileNameList, err := getDirFileNameList(dirPath)
if err != nil {
return fmt.Errorf("读取合并目标文件失败: %v", err)
}
if len(fileNameList) <= 0 {
return fmt.Errorf("读取合并目标文件失败")
}
// 排序
sort.Slice(fileNameList, func(i, j int) bool {
numI, _ := strconv.Atoi(fileNameList[i])
numJ, _ := strconv.Atoi(fileNameList[j])
return numI < numJ
})
// 写入到新路径文件
newFilePath := filepath.Join(writePath, fileName)
if err = os.MkdirAll(filepath.Dir(newFilePath), 0750); err != nil {
return err
}
// 转移完成后删除切片目录
defer os.Remove(dirPath)
// 打开新路径文件
outputFile, err := os.Create(newFilePath)
if err != nil {
return fmt.Errorf("failed to create file: %v", err)
}
defer outputFile.Close()
// 逐个读取文件后进行流拷贝数据块
for _, fileName := range fileNameList {
chunkPath := filepath.Join(dirPath, fileName)
// 拷贝结束后删除切片
defer os.Remove(chunkPath)
// 打开切片文件
inputFile, err := os.Open(chunkPath)
if err != nil {
return fmt.Errorf("failed to open file: %v", err)
}
defer inputFile.Close()
// 拷贝文件流
_, err = io.Copy(outputFile, inputFile)
if err != nil {
return fmt.Errorf("failed to copy file data: %w", err)
}
}
return nil
}
// getFileSize 读取文件大小
func getFileSize(filePath string) int64 {
// 获取文件信息
fileInfo, err := os.Stat(filePath)
if err != nil {
logger.Errorf("Failed stat %s: %v", filePath, err)
return 0
}
// 获取文件大小(字节数)
return fileInfo.Size()
}
// 读取文件流用于返回下载
//
// filePath 文件路径
// startOffset, endOffset 分片块读取区间,根据文件切片的块范围
func getFileStream(filePath string, startOffset, endOffset int64) ([]byte, error) {
// 打开文件
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
// 获取文件的大小
fileInfo, err := file.Stat()
if err != nil {
return nil, err
}
fileSize := fileInfo.Size()
// 确保起始和结束偏移量在文件范围内
if startOffset > fileSize {
startOffset = 0
}
if endOffset >= fileSize {
endOffset = fileSize - 1
}
// 计算切片的大小
chunkSize := endOffset - startOffset + 1
// 创建 SectionReader
reader := io.NewSectionReader(file, startOffset, chunkSize)
// 创建一个缓冲区来存储读取的数据
buffer := make([]byte, chunkSize)
// 读取数据到缓冲区
_, err = reader.Read(buffer)
if err != nil && err != io.EOF {
return nil, err
}
return buffer, nil
}
// 获取文件目录下所有文件名称,不含目录名称
//
// filePath 文件路径
func getDirFileNameList(dirPath string) ([]string, error) {
fileNames := []string{}
dir, err := os.Open(dirPath)
if err != nil {
return fileNames, nil
}
defer dir.Close()
fileInfos, err := dir.Readdir(-1)
if err != nil {
return fileNames, err
}
for _, fileInfo := range fileInfos {
if fileInfo.Mode().IsRegular() {
fileNames = append(fileNames, fileInfo.Name())
}
}
return fileNames, nil
}

View File

@@ -0,0 +1,43 @@
package generate
import (
"math/rand"
"time"
"ems.agt/src/framework/logger"
gonanoid "github.com/matoous/go-nanoid/v2"
)
// 生成随机Code
// 包含数字、小写字母
// 不保证长度满足
func Code(size int) string {
str, err := gonanoid.Generate("0123456789abcdefghijklmnopqrstuvwxyz", size)
if err != nil {
logger.Infof("%d : %v", size, err)
return ""
}
return str
}
// 生成随机字符串
// 包含数字、大小写字母、下划线、横杠
// 不保证长度满足
func String(size int) string {
str, err := gonanoid.New(size)
if err != nil {
logger.Infof("%d : %v", size, err)
return ""
}
return str
}
// 随机数 纯数字0-9
func Number(size int) int {
source := rand.NewSource(time.Now().UnixNano())
random := rand.New(source)
min := int64(0)
max := int64(9 * int(size))
return int(random.Int63n(max-min+1) + min)
}

View File

@@ -0,0 +1,238 @@
package ip2region
import (
"encoding/binary"
"fmt"
"os"
)
const (
HeaderInfoLength = 256
VectorIndexRows = 256
VectorIndexCols = 256
VectorIndexSize = 8
SegmentIndexBlockSize = 14
)
// --- Index policy define
type IndexPolicy int
const (
VectorIndexPolicy IndexPolicy = 1
BTreeIndexPolicy IndexPolicy = 2
)
func (i IndexPolicy) String() string {
switch i {
case VectorIndexPolicy:
return "VectorIndex"
case BTreeIndexPolicy:
return "BtreeIndex"
default:
return "unknown"
}
}
// --- Header define
type Header struct {
// data []byte
Version uint16
IndexPolicy IndexPolicy
CreatedAt uint32
StartIndexPtr uint32
EndIndexPtr uint32
}
func NewHeader(input []byte) (*Header, error) {
if len(input) < 16 {
return nil, fmt.Errorf("invalid input buffer")
}
return &Header{
Version: binary.LittleEndian.Uint16(input),
IndexPolicy: IndexPolicy(binary.LittleEndian.Uint16(input[2:])),
CreatedAt: binary.LittleEndian.Uint32(input[4:]),
StartIndexPtr: binary.LittleEndian.Uint32(input[8:]),
EndIndexPtr: binary.LittleEndian.Uint32(input[12:]),
}, nil
}
// --- searcher implementation
type Searcher struct {
handle *os.File
ioCount int
// use it only when this feature enabled.
// Preload the vector index will reduce the number of IO operations
// thus speedup the search process
vectorIndex []byte
// content buffer.
// running with the whole xdb file cached
contentBuff []byte
}
func baseNew(dbFile string, vIndex []byte, cBuff []byte) (*Searcher, error) {
var err error
// content buff first
if cBuff != nil {
return &Searcher{
vectorIndex: nil,
contentBuff: cBuff,
}, nil
}
// open the xdb binary file
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, err
}
return &Searcher{
handle: handle,
vectorIndex: vIndex,
}, nil
}
func NewWithFileOnly(dbFile string) (*Searcher, error) {
return baseNew(dbFile, nil, nil)
}
func NewWithVectorIndex(dbFile string, vIndex []byte) (*Searcher, error) {
return baseNew(dbFile, vIndex, nil)
}
func NewWithBuffer(cBuff []byte) (*Searcher, error) {
return baseNew("", nil, cBuff)
}
func (s *Searcher) Close() {
if s.handle != nil {
err := s.handle.Close()
if err != nil {
return
}
}
}
// GetIOCount return the global io count for the last search
func (s *Searcher) GetIOCount() int {
return s.ioCount
}
// SearchByStr find the region for the specified ip string
func (s *Searcher) SearchByStr(str string) (string, error) {
ip, err := CheckIP(str)
if err != nil {
return "", err
}
return s.Search(ip)
}
// Search find the region for the specified long ip
func (s *Searcher) Search(ip uint32) (string, error) {
// reset the global ioCount
s.ioCount = 0
// locate the segment index block based on the vector index
var il0 = (ip >> 24) & 0xFF
var il1 = (ip >> 16) & 0xFF
var idx = il0*VectorIndexCols*VectorIndexSize + il1*VectorIndexSize
var sPtr, ePtr = uint32(0), uint32(0)
if s.vectorIndex != nil {
sPtr = binary.LittleEndian.Uint32(s.vectorIndex[idx:])
ePtr = binary.LittleEndian.Uint32(s.vectorIndex[idx+4:])
} else if s.contentBuff != nil {
sPtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx:])
ePtr = binary.LittleEndian.Uint32(s.contentBuff[HeaderInfoLength+idx+4:])
} else {
// read the vector index block
var buff = make([]byte, VectorIndexSize)
err := s.read(int64(HeaderInfoLength+idx), buff)
if err != nil {
return "", fmt.Errorf("read vector index block at %d: %w", HeaderInfoLength+idx, err)
}
sPtr = binary.LittleEndian.Uint32(buff)
ePtr = binary.LittleEndian.Uint32(buff[4:])
}
// fmt.Printf("sPtr=%d, ePtr=%d", sPtr, ePtr)
// binary search the segment index to get the region
var dataLen, dataPtr = 0, uint32(0)
var buff = make([]byte, SegmentIndexBlockSize)
var l, h = 0, int((ePtr - sPtr) / SegmentIndexBlockSize)
for l <= h {
m := (l + h) >> 1
p := sPtr + uint32(m*SegmentIndexBlockSize)
err := s.read(int64(p), buff)
if err != nil {
return "", fmt.Errorf("read segment index at %d: %w", p, err)
}
// decode the data step by step to reduce the unnecessary operations
sip := binary.LittleEndian.Uint32(buff)
if ip < sip {
h = m - 1
} else {
eip := binary.LittleEndian.Uint32(buff[4:])
if ip > eip {
l = m + 1
} else {
dataLen = int(binary.LittleEndian.Uint16(buff[8:]))
dataPtr = binary.LittleEndian.Uint32(buff[10:])
break
}
}
}
//fmt.Printf("dataLen: %d, dataPtr: %d", dataLen, dataPtr)
if dataLen == 0 {
return "", nil
}
// load and return the region data
var regionBuff = make([]byte, dataLen)
err := s.read(int64(dataPtr), regionBuff)
if err != nil {
return "", fmt.Errorf("read region at %d: %w", dataPtr, err)
}
return string(regionBuff), nil
}
// do the data read operation based on the setting.
// content buffer first or will read from the file.
// this operation will invoke the Seek for file based read.
func (s *Searcher) read(offset int64, buff []byte) error {
if s.contentBuff != nil {
cLen := copy(buff, s.contentBuff[offset:])
if cLen != len(buff) {
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
} else {
_, err := s.handle.Seek(offset, 0)
if err != nil {
return fmt.Errorf("seek to %d: %w", offset, err)
}
s.ioCount++
rLen, err := s.handle.Read(buff)
if err != nil {
return fmt.Errorf("handle read: %w", err)
}
if rLen != len(buff) {
return fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
}
return nil
}

View File

@@ -0,0 +1,89 @@
package ip2region
import (
"embed"
"strings"
"time"
"ems.agt/src/framework/logger"
)
// 网络地址(内网)
const LOCAT_HOST = "127.0.0.1"
// 全局查询对象
var searcher *Searcher
//go:embed ip2region.xdb
var ip2regionDB embed.FS
func init() {
// 从 dbPath 加载整个 xdb 到内存
buf, err := ip2regionDB.ReadFile("ip2region.xdb")
if err != nil {
logger.Fatalf("failed error load xdb from : %s\n", err)
return
}
// 用全局的 cBuff 创建完全基于内存的查询对象。
base, err := NewWithBuffer(buf)
if err != nil {
logger.Errorf("failed error create searcher with content: %s\n", err)
return
}
// 赋值到全局查询对象
searcher = base
}
// RegionSearchByIp 查询IP所在地
//
// 国家|区域|省份|城市|ISP
func RegionSearchByIp(ip string) (string, int, int64) {
ip = ClientIP(ip)
if ip == LOCAT_HOST {
return "0|0|0|内网IP|内网IP", 0, 0
}
tStart := time.Now()
region, err := searcher.SearchByStr(ip)
if err != nil {
logger.Errorf("failed to SearchIP(%s): %s\n", ip, err)
return "0|0|0|0|0", 0, 0
}
return region, 0, time.Since(tStart).Milliseconds()
}
// RealAddressByIp 地址IP所在地
//
// 218.4.167.70 江苏省 苏州市
func RealAddressByIp(ip string) string {
ip = ClientIP(ip)
if ip == LOCAT_HOST {
return "内网IP"
}
region, err := searcher.SearchByStr(ip)
if err != nil {
logger.Errorf("failed to SearchIP(%s): %s\n", ip, err)
return "未知"
}
parts := strings.Split(region, "|")
province := parts[2]
city := parts[3]
if province == "0" && city != "0" {
return city
}
return province + " " + city
}
// ClientIP 处理客户端IP地址显示iPv4
//
// 转换 ip2region.ClientIP(c.ClientIP())
func ClientIP(ip string) string {
if strings.HasPrefix(ip, "::ffff:") {
ip = strings.Replace(ip, "::ffff:", "", 1)
}
if ip == LOCAT_HOST || ip == "::1" {
return LOCAT_HOST
}
return ip
}

Binary file not shown.

View File

@@ -0,0 +1,175 @@
package ip2region
import (
"fmt"
"os"
"strconv"
"strings"
)
var shiftIndex = []int{24, 16, 8, 0}
func CheckIP(ip string) (uint32, error) {
var ps = strings.Split(strings.TrimSpace(ip), ".")
if len(ps) != 4 {
return 0, fmt.Errorf("invalid ip address `%s`", ip)
}
var val = uint32(0)
for i, s := range ps {
d, err := strconv.Atoi(s)
if err != nil {
return 0, fmt.Errorf("the %dth part `%s` is not an integer", i, s)
}
if d < 0 || d > 255 {
return 0, fmt.Errorf("the %dth part `%s` should be an integer bettween 0 and 255", i, s)
}
val |= uint32(d) << shiftIndex[i]
}
// convert the ip to integer
return val, nil
}
func Long2IP(ip uint32) string {
return fmt.Sprintf("%d.%d.%d.%d", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, ip&0xFF)
}
func MidIP(sip uint32, eip uint32) uint32 {
return uint32((uint64(sip) + uint64(eip)) >> 1)
}
// LoadHeader load the header info from the specified handle
func LoadHeader(handle *os.File) (*Header, error) {
_, err := handle.Seek(0, 0)
if err != nil {
return nil, fmt.Errorf("seek to the header: %w", err)
}
var buff = make([]byte, HeaderInfoLength)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return NewHeader(buff)
}
// LoadHeaderFromFile load header info from the specified db file path
func LoadHeaderFromFile(dbFile string) (*Header, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
defer func(handle *os.File) {
_ = handle.Close()
}(handle)
header, err := LoadHeader(handle)
if err != nil {
return nil, err
}
return header, nil
}
// LoadHeaderFromBuff wrap the header info from the content buffer
func LoadHeaderFromBuff(cBuff []byte) (*Header, error) {
return NewHeader(cBuff[0:256])
}
// LoadVectorIndex util function to load the vector index from the specified file handle
func LoadVectorIndex(handle *os.File) ([]byte, error) {
// load all the vector index block
_, err := handle.Seek(HeaderInfoLength, 0)
if err != nil {
return nil, fmt.Errorf("seek to vector index: %w", err)
}
var buff = make([]byte, VectorIndexRows*VectorIndexCols*VectorIndexSize)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return buff, nil
}
// LoadVectorIndexFromFile load vector index from a specified file path
func LoadVectorIndexFromFile(dbFile string) ([]byte, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
defer func() {
_ = handle.Close()
}()
vIndex, err := LoadVectorIndex(handle)
if err != nil {
return nil, err
}
return vIndex, nil
}
// LoadContent load the whole xdb content from the specified file handle
func LoadContent(handle *os.File) ([]byte, error) {
// get file size
fi, err := handle.Stat()
if err != nil {
return nil, fmt.Errorf("stat: %w", err)
}
size := fi.Size()
// seek to the head of the file
_, err = handle.Seek(0, 0)
if err != nil {
return nil, fmt.Errorf("seek to get xdb file length: %w", err)
}
var buff = make([]byte, size)
rLen, err := handle.Read(buff)
if err != nil {
return nil, err
}
if rLen != len(buff) {
return nil, fmt.Errorf("incomplete read: readed bytes should be %d", len(buff))
}
return buff, nil
}
// LoadContentFromFile load the whole xdb content from the specified db file path
func LoadContentFromFile(dbFile string) ([]byte, error) {
handle, err := os.OpenFile(dbFile, os.O_RDONLY, 0600)
if err != nil {
return nil, fmt.Errorf("open xdb file `%s`: %w", dbFile, err)
}
defer func() {
_ = handle.Close()
}()
cBuff, err := LoadContent(handle)
if err != nil {
return nil, err
}
return cBuff, nil
}

View File

@@ -0,0 +1,167 @@
package parse
import (
"fmt"
"image/color"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"github.com/robfig/cron/v3"
)
// Number 解析数值型
func Number(str any) int64 {
switch str := str.(type) {
case string:
if str == "" {
return 0
}
num, err := strconv.ParseInt(str, 10, 64)
if err != nil {
return 0
}
return num
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
return reflect.ValueOf(str).Int()
case float32, float64:
return int64(reflect.ValueOf(str).Float())
default:
return 0
}
}
// Boolean 解析布尔型
func Boolean(str any) bool {
switch str := str.(type) {
case string:
if str == "" || str == "false" || str == "0" {
return false
}
// 尝试将字符串解析为数字
if num, err := strconv.ParseFloat(str, 64); err == nil {
return num != 0
}
return true
case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:
num := reflect.ValueOf(str).Int()
return num != 0
case float32, float64:
num := reflect.ValueOf(str).Float()
return num != 0
default:
return false
}
}
// ConvertToCamelCase 字符串转换驼峰形式
//
// 字符串 dict/inline/data/:dictId 结果 DictInlineDataDictId
func ConvertToCamelCase(str string) string {
if len(str) == 0 {
return str
}
reg := regexp.MustCompile(`[-_:/]\w`)
result := reg.ReplaceAllStringFunc(str, func(match string) string {
return strings.ToUpper(string(match[1]))
})
words := strings.Fields(result)
for i, word := range words {
str := word[1:]
str = strings.ReplaceAll(str, "/", "")
words[i] = strings.ToUpper(word[:1]) + str
}
return strings.Join(words, "")
}
// Bit 比特位为单位
func Bit(bit float64) string {
var GB, MB, KB string
if bit > float64(1<<30) {
GB = fmt.Sprintf("%0.2f", bit/(1<<30))
}
if bit > float64(1<<20) && bit < (1<<30) {
MB = fmt.Sprintf("%.2f", bit/(1<<20))
}
if bit > float64(1<<10) && bit < (1<<20) {
KB = fmt.Sprintf("%.2f", bit/(1<<10))
}
if GB != "" {
return GB + "GB"
} else if MB != "" {
return MB + "MB"
} else if KB != "" {
return KB + "KB"
} else {
return fmt.Sprintf("%vB", bit)
}
}
// CronExpression 解析 Cron 表达式,返回下一次执行的时间戳(毫秒)
//
// 【*/5 * * * * ?】 6个参数
func CronExpression(expression string) int64 {
specParser := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
schedule, err := specParser.Parse(expression)
if err != nil {
return 0
}
return schedule.Next(time.Now()).UnixMilli()
}
// SafeContent 内容值进行安全掩码
func SafeContent(value string) string {
if len(value) < 3 {
return strings.Repeat("*", len(value))
} else if len(value) < 6 {
return string(value[0]) + strings.Repeat("*", len(value)-1)
} else if len(value) < 10 {
return string(value[0]) + strings.Repeat("*", len(value)-2) + string(value[len(value)-1])
} else if len(value) < 15 {
return value[:2] + strings.Repeat("*", len(value)-4) + value[len(value)-2:]
} else {
return value[:3] + strings.Repeat("*", len(value)-6) + value[len(value)-3:]
}
}
// RemoveDuplicates 数组内字符串去重
func RemoveDuplicates(ids []string) []string {
uniqueIDs := make(map[string]bool)
uniqueIDSlice := make([]string, 0)
for _, id := range ids {
_, ok := uniqueIDs[id]
if !ok && id != "" {
uniqueIDs[id] = true
uniqueIDSlice = append(uniqueIDSlice, id)
}
}
return uniqueIDSlice
}
// Color 解析颜色 #fafafa
func Color(colorStr string) *color.RGBA {
// 去除 # 号
colorStr = colorStr[1:]
// 将颜色字符串拆分为 R、G、B 分量
r, _ := strconv.ParseInt(colorStr[0:2], 16, 0)
g, _ := strconv.ParseInt(colorStr[2:4], 16, 0)
b, _ := strconv.ParseInt(colorStr[4:6], 16, 0)
return &color.RGBA{
R: uint8(r),
G: uint8(g),
B: uint8(b),
A: 255, // 不透明
}
}

View File

@@ -0,0 +1,86 @@
package regular
import (
"regexp"
"github.com/dlclark/regexp2"
)
// Replace 正则替换
func Replace(originStr, pattern, repStr string) string {
regex := regexp.MustCompile(pattern)
return regex.ReplaceAllString(originStr, repStr)
}
// 判断是否为有效用户名格式
//
// 用户名不能以数字开头可包含大写小写字母数字且不少于5位
func ValidUsername(username string) bool {
if username == "" {
return false
}
pattern := `^[a-zA-Z][a-z0-9A-Z]{5,}`
match, err := regexp.MatchString(pattern, username)
if err != nil {
return false
}
return match
}
// 判断是否为有效密码格式
//
// 密码至少包含大小写字母、数字、特殊符号且不少于6位
func ValidPassword(password string) bool {
if password == "" {
return false
}
pattern := `^(?![A-Za-z0-9]+$)(?![a-z0-9\W]+$)(?![A-Za-z\W]+$)(?![A-Z0-9\W]+$)[a-zA-Z0-9\W]{6,}$`
re := regexp2.MustCompile(pattern, 0)
match, err := re.MatchString(password)
if err != nil {
return false
}
return match
}
// 判断是否为有效手机号格式1开头的11位手机号
func ValidMobile(mobile string) bool {
if mobile == "" {
return false
}
pattern := `^1[3|4|5|6|7|8|9][0-9]\d{8}$`
match, err := regexp.MatchString(pattern, mobile)
if err != nil {
return false
}
return match
}
// 判断是否为有效邮箱格式
func ValidEmail(email string) bool {
if email == "" {
return false
}
pattern := `^(([^<>()\\.,;:\s@"]+(\.[^<>()\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+\.)+[a-zA-Z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]{2,}))$`
re := regexp2.MustCompile(pattern, 0)
match, err := re.MatchString(email)
if err != nil {
return false
}
return match
}
// 判断是否为http(s)://开头
//
// link 网络链接
func ValidHttp(link string) bool {
if link == "" {
return false
}
pattern := `^http(s)?:\/\/+`
match, err := regexp.MatchString(pattern, link)
if err != nil {
return false
}
return match
}

View File

@@ -0,0 +1,132 @@
package repo
import (
"fmt"
"reflect"
"strconv"
"strings"
"ems.agt/src/framework/utils/parse"
)
// PageNumSize 分页页码记录数
func PageNumSize(pageNum, pageSize any) (int64, int64) {
// 记录起始索引
num := parse.Number(pageNum)
if num > 5000 {
num = 5000
}
if num < 1 {
num = 1
}
// 显示记录数
size := parse.Number(pageSize)
if size > 50000 {
size = 50000
}
if size < 0 {
size = 10
}
return num - 1, size
}
// SetFieldValue 判断结构体内是否存在指定字段并设置值
func SetFieldValue(obj any, fieldName string, value any) {
// 获取结构体的反射值
userValue := reflect.ValueOf(obj)
// 获取字段的反射值
fieldValue := userValue.Elem().FieldByName(fieldName)
// 检查字段是否存在
if fieldValue.IsValid() && fieldValue.CanSet() {
// 获取字段的类型
fieldType := fieldValue.Type()
// 转换传入的值类型为字段类型
switch fieldType.Kind() {
case reflect.String:
if value == nil {
fieldValue.SetString("")
} else {
fieldValue.SetString(fmt.Sprintf("%v", value))
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
intValue, err := strconv.ParseInt(fmt.Sprintf("%v", value), 10, 64)
if err != nil {
intValue = 0
}
fieldValue.SetInt(intValue)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
uintValue, err := strconv.ParseUint(fmt.Sprintf("%v", value), 10, 64)
if err != nil {
uintValue = 0
}
fieldValue.SetUint(uintValue)
case reflect.Float32, reflect.Float64:
floatValue, err := strconv.ParseFloat(fmt.Sprintf("%v", value), 64)
if err != nil {
floatValue = 0
}
fieldValue.SetFloat(floatValue)
default:
// 设置字段的值
fieldValue.Set(reflect.ValueOf(value).Convert(fieldValue.Type()))
}
}
}
// ConvertIdsSlice 将 []string 转换为 []any
func ConvertIdsSlice(ids []string) []any {
// 将 []string 转换为 []any
arr := make([]any, len(ids))
for i, v := range ids {
arr[i] = v
}
return arr
}
// 查询-参数值的占位符
func KeyPlaceholderByQuery(sum int) string {
placeholders := make([]string, sum)
for i := 0; i < sum; i++ {
placeholders[i] = "?"
}
return strings.Join(placeholders, ",")
}
// 插入-参数映射键值占位符 keys, placeholder, values
func KeyPlaceholderValueByInsert(params map[string]any) ([]string, string, []any) {
// 参数映射的键
keys := make([]string, len(params))
// 参数映射的值
values := make([]any, len(params))
sum := 0
for k, v := range params {
keys[sum] = k
values[sum] = v
sum++
}
// 参数值的占位符
placeholders := make([]string, sum)
for i := 0; i < sum; i++ {
placeholders[i] = "?"
}
return keys, strings.Join(placeholders, ","), values
}
// 更新-参数映射键值占位符 keys, values
func KeyValueByUpdate(params map[string]any) ([]string, []any) {
// 参数映射的键
keys := make([]string, len(params))
// 参数映射的值
values := make([]any, len(params))
sum := 0
for k, v := range params {
keys[sum] = k + "=?"
values[sum] = v
sum++
}
return keys, values
}

View File

@@ -0,0 +1,151 @@
package token
import (
"encoding/json"
"errors"
"time"
"ems.agt/src/framework/config"
cachekeyConstants "ems.agt/src/framework/constants/cachekey"
tokenConstants "ems.agt/src/framework/constants/token"
"ems.agt/src/framework/logger"
redisCahe "ems.agt/src/framework/redis"
"ems.agt/src/framework/utils/generate"
"ems.agt/src/framework/vo"
jwt "github.com/golang-jwt/jwt/v5"
)
// Remove 清除登录用户信息UUID
func Remove(tokenStr string) string {
claims, err := Verify(tokenStr)
if err != nil {
logger.Errorf("token verify err %v", err)
return ""
}
// 清除缓存KEY
uuid := claims[tokenConstants.JWT_UUID].(string)
tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + uuid
hasKey, _ := redisCahe.Has("", tokenKey)
if hasKey {
redisCahe.Del("", tokenKey)
}
return claims[tokenConstants.JWT_NAME].(string)
}
// Create 令牌生成
func Create(loginUser *vo.LoginUser, ilobArgs ...string) string {
// 生成用户唯一tokne32位
loginUser.UUID = generate.Code(32)
// 设置请求用户登录客户端
loginUser.IPAddr = ilobArgs[0]
loginUser.LoginLocation = ilobArgs[1]
loginUser.OS = ilobArgs[2]
loginUser.Browser = ilobArgs[3]
// 设置用户令牌有效期并存入缓存
Cache(loginUser)
// 令牌算法 HS256 HS384 HS512
algorithm := config.Get("jwt.algorithm").(string)
var method *jwt.SigningMethodHMAC
switch algorithm {
case "HS512":
method = jwt.SigningMethodHS512
case "HS384":
method = jwt.SigningMethodHS384
case "HS256":
default:
method = jwt.SigningMethodHS256
}
// 生成令牌负荷绑定uuid标识
jwtToken := jwt.NewWithClaims(method, jwt.MapClaims{
tokenConstants.JWT_UUID: loginUser.UUID,
tokenConstants.JWT_KEY: loginUser.UserID,
tokenConstants.JWT_NAME: loginUser.User.UserName,
"exp": loginUser.ExpireTime,
"ait": loginUser.LoginTime,
})
// 生成令牌设置密钥
secret := config.Get("jwt.secret").(string)
tokenStr, err := jwtToken.SignedString([]byte(secret))
if err != nil {
logger.Infof("jwt sign err : %v", err)
return ""
}
return tokenStr
}
// Cache 缓存登录用户信息
func Cache(loginUser *vo.LoginUser) {
// 计算配置的有效期
expTime := config.Get("jwt.expiresIn").(int)
expTimestamp := time.Duration(expTime) * time.Minute
iatTimestamp := time.Now().UnixMilli()
loginUser.LoginTime = iatTimestamp
loginUser.ExpireTime = iatTimestamp + expTimestamp.Milliseconds()
// 根据登录标识将loginUser缓存
tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + loginUser.UUID
jsonBytes, err := json.Marshal(loginUser)
if err != nil {
return
}
redisCahe.SetByExpire("", tokenKey, string(jsonBytes), expTimestamp)
}
// RefreshIn 验证令牌有效期相差不足xx分钟自动刷新缓存
func RefreshIn(loginUser *vo.LoginUser) {
// 相差不足xx分钟自动刷新缓存
refreshTime := config.Get("jwt.refreshIn").(int)
refreshTimestamp := time.Duration(refreshTime) * time.Minute
// 过期时间
expireTimestamp := loginUser.ExpireTime
currentTimestamp := time.Now().UnixMilli()
if expireTimestamp-currentTimestamp <= refreshTimestamp.Milliseconds() {
Cache(loginUser)
}
}
// Verify 校验令牌是否有效
func Verify(tokenString string) (jwt.MapClaims, error) {
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
// 判断加密算法是预期的加密算法
if _, ok := token.Method.(*jwt.SigningMethodHMAC); ok {
secret := config.Get("jwt.secret").(string)
return []byte(secret), nil
}
return nil, jwt.ErrSignatureInvalid
})
if err != nil {
logger.Errorf("token String Verify : %v", err)
return nil, errors.New("无效身份授权")
}
// 如果解析负荷成功并通过签名校验
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("token valid error")
}
// LoginUser 缓存的登录用户信息
func LoginUser(claims jwt.MapClaims) vo.LoginUser {
uuid := claims[tokenConstants.JWT_UUID].(string)
tokenKey := cachekeyConstants.LOGIN_TOKEN_KEY + uuid
hasKey, _ := redisCahe.Has("", tokenKey)
var loginUser vo.LoginUser
if hasKey {
loginUserStr, _ := redisCahe.Get("", tokenKey)
if loginUserStr == "" {
return loginUser
}
err := json.Unmarshal([]byte(loginUserStr), &loginUser)
if err != nil {
logger.Errorf("loginuser info json err : %v", err)
return loginUser
}
return loginUser
}
return loginUser
}

View File

@@ -0,0 +1,8 @@
package ua
import "github.com/mssola/user_agent"
// 获取user-agent信息
func Info(userAgent string) *user_agent.UserAgent {
return user_agent.New(userAgent)
}

View File

@@ -0,0 +1,39 @@
package vo
import systemModel "ems.agt/src/modules/system/model"
// LoginUser 登录用户身份权限信息对象
type LoginUser struct {
// UserID 用户ID
UserID string `json:"userId"`
// DeptID 部门ID
DeptID string `json:"deptId"`
// UUID 用户唯一标识
UUID string `json:"uuid"`
// LoginTime 登录时间时间戳
LoginTime int64 `json:"loginTime"`
// ExpireTime 过期时间时间戳
ExpireTime int64 `json:"expireTime"`
// IPAddr 登录IP地址 x.x.x.x
IPAddr string `json:"ipaddr"`
// LoginLocation 登录地点 xx xx
LoginLocation string `json:"loginLocation"`
// Browser 浏览器类型
Browser string `json:"browser"`
// OS 操作系统
OS string `json:"os"`
// Permissions 权限列表
Permissions []string `json:"permissions"`
// User 用户信息
User systemModel.SysUser `json:"user"`
}

View File

@@ -0,0 +1,71 @@
package result
import (
"ems.agt/src/framework/constants/result"
)
// CodeMsg 响应结果
func CodeMsg(code int, msg string) map[string]any {
args := make(map[string]any)
args["code"] = code
args["msg"] = msg
return args
}
// 响应成功结果 map[string]any{}
func Ok(v map[string]any) map[string]any {
args := make(map[string]any)
args["code"] = result.CODE_SUCCESS
args["msg"] = result.MSG_SUCCESS
// v合并到args
for key, value := range v {
args[key] = value
}
return args
}
// 响应成功结果信息
func OkMsg(msg string) map[string]any {
args := make(map[string]any)
args["code"] = result.CODE_SUCCESS
args["msg"] = msg
return args
}
// 响应成功结果数据
func OkData(data any) map[string]any {
args := make(map[string]any)
args["code"] = result.CODE_SUCCESS
args["msg"] = result.MSG_SUCCESS
args["data"] = data
return args
}
// 响应失败结果 map[string]any{}
func Err(v map[string]any) map[string]any {
args := make(map[string]any)
args["code"] = result.CODE_ERROR
args["msg"] = result.MSG_ERROR
// v合并到args
for key, value := range v {
args[key] = value
}
return args
}
// 响应失败结果信息
func ErrMsg(msg string) map[string]any {
args := make(map[string]any)
args["code"] = result.CODE_ERROR
args["msg"] = msg
return args
}
// 响应失败结果数据
func ErrData(data any) map[string]any {
args := make(map[string]any)
args["code"] = result.CODE_ERROR
args["msg"] = result.MSG_ERROR
args["data"] = data
return args
}

View File

@@ -0,0 +1,17 @@
package vo
// Router 路由信息对象
type Router struct {
// 路由名字 英文首字母大写
Name string `json:"name"`
// 路由地址
Path string `json:"path"`
// 其他元素
Meta RouterMeta `json:"meta"`
// 组件地址
Component string `json:"component"`
// 重定向地址
Redirect string `json:"redirect"`
// 子路由
Children []Router `json:"children,omitempty"`
}

View File

@@ -0,0 +1,17 @@
package vo
// RouterMeta 路由元信息对象
type RouterMeta struct {
// 设置该菜单在侧边栏和面包屑中展示的名字
Title string `json:"title"`
// 设置该菜单的图标
Icon string `json:"icon"`
// 设置为true则不会被 <keep-alive>缓存
Cache bool `json:"cache"`
// 内链地址http(s)://开头), 打开目标位置 '_blank' | '_self' | ''
Target string `json:"target"`
// 在菜单中隐藏子节点
HideChildInMenu bool `json:"hideChildInMenu"`
// 在菜单中隐藏自己和子节点
HideInMenu bool `json:"hideInMenu"`
}

View File

@@ -0,0 +1,51 @@
package vo
import systemModel "ems.agt/src/modules/system/model"
// TreeSelect 树结构实体类
type TreeSelect struct {
// ID 节点ID
ID string `json:"id"`
// Label 节点名称
Label string `json:"label"`
// Children 子节点
Children []TreeSelect `json:"children"`
}
// SysMenuTreeSelect 使用给定的 SysMenu 对象解析为 TreeSelect 对象
func SysMenuTreeSelect(sysMenu systemModel.SysMenu) TreeSelect {
t := TreeSelect{}
t.ID = sysMenu.MenuID
t.Label = sysMenu.MenuName
if len(sysMenu.Children) > 0 {
for _, menu := range sysMenu.Children {
child := SysMenuTreeSelect(menu)
t.Children = append(t.Children, child)
}
} else {
t.Children = []TreeSelect{}
}
return t
}
// SysDeptTreeSelect 使用给定的 SysDept 对象解析为 TreeSelect 对象
func SysDeptTreeSelect(sysDept systemModel.SysDept) TreeSelect {
t := TreeSelect{}
t.ID = sysDept.DeptID
t.Label = sysDept.DeptName
if len(sysDept.Children) > 0 {
for _, dept := range sysDept.Children {
child := SysDeptTreeSelect(dept)
t.Children = append(t.Children, child)
}
} else {
t.Children = []TreeSelect{}
}
return t
}

View File

@@ -0,0 +1,32 @@
package libfeatures
import (
"time"
"ems.agt/lib/dborm"
"ems.agt/lib/oauth"
libConfig "ems.agt/restagent/config"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/redis"
)
// SessionToken 设置登录会话-兼容旧登录方式
func SessionToken(username, sourceAddr string) bool {
token, _ := redis.Get("", "session_token")
if token != "" {
return true
}
token = oauth.GenRandToken("omc") // Generate new token to session ID
affected, err := dborm.XormInsertSession(username, sourceAddr, token, libConfig.GetExpiresFromConfig(), libConfig.GetYamlConfig().Auth.Session)
if err != nil {
logger.Errorf("SessionToken XormInsertSession err %v", err)
}
if affected == 1 {
// 过期时间单位秒 配置1800是半小时
expireTime := time.Duration(int64(libConfig.GetExpiresFromConfig())) * time.Second
redis.SetByExpire("", "session_token", token, expireTime)
return true
}
return false
}

View File

@@ -0,0 +1,53 @@
package libfeatures
import (
"fmt"
libConf "ems.agt/lib/core/conf"
libGlobal "ems.agt/lib/global"
libConfig "ems.agt/restagent/config"
"github.com/spf13/viper"
)
// BuildInfo 程序-V查看编译版本号信息
func BuildInfo() string {
return fmt.Sprintf("OMC restagent version: %s\n%s\n%s\n\n", libGlobal.Version, libGlobal.BuildTime, libGlobal.GoVer)
}
// ConfigRead 指定配置文件读取
func ConfigRead(configFile string) {
// 外层lib和features使用的配置
libConfig.ReadConfig(configFile)
uriPrefix := libConfig.GetYamlConfig().OMC.UriPrefix
if uriPrefix != "" {
libConfig.UriPrefix = uriPrefix
}
if libConfig.GetYamlConfig().TestConfig.Enabled {
libConfig.ReadTestConfigYaml(libConfig.GetYamlConfig().TestConfig.File)
}
// 外层lib和features使用配置
libConf.InitConfig(configFile)
}
// 配置文件读取进行内部参数合并
func ConfigInMerge() {
// 合并外层lib和features使用配置
for key, value := range libConf.AllSettings() {
// 跳过配置
if key == "testconfig" || key == "rest" || key == "logger" {
continue
}
// 数据库配置
if key == "database" {
item := value.(map[string]any)
defaultItem := viper.GetStringMap("gorm.datasource.default")
defaultItem["host"] = item["host"]
defaultItem["port"] = item["port"]
defaultItem["username"] = item["user"]
defaultItem["password"] = item["password"]
defaultItem["database"] = item["name"]
continue
}
viper.Set(key, value)
}
}

View File

@@ -0,0 +1,5 @@
# 外层 lib 和 features 粘合层
- config.go 配置合并: restagent.yaml 文件内容,主要是数据库配置
- account.go 登录会话生成 token
- session.go 中间件方式设置请求头 token 值

View File

@@ -0,0 +1,23 @@
package session
import (
"ems.agt/src/framework/redis"
"github.com/gin-gonic/gin"
)
// SessionHeader 旧登录方式token头
func SessionHeader() gin.HandlerFunc {
return func(c *gin.Context) {
// 读取登录生成的会话token
token, err := redis.Get("", "session_token")
if err == nil {
c.Request.Header.Set("Accesstoken", token)
}
// Accesstoken: omc-ce4d0a86-8515-ad51-3249-4913c95f8e34
// 调用下一个处理程序
c.Next()
}
}

View File

@@ -0,0 +1,85 @@
package common
import (
"ems.agt/src/framework/logger"
"ems.agt/src/framework/middleware"
"ems.agt/src/modules/common/controller"
"github.com/gin-gonic/gin"
)
// 模块路由注册
func Setup(router *gin.Engine) {
logger.Infof("开始加载 ====> common 模块路由")
// 路由主页
indexGroup := router.Group("/")
indexGroup.GET("",
middleware.RateLimit(middleware.LimitOption{
Time: 300,
Count: 10,
Type: middleware.LIMIT_IP,
}),
controller.NewIndex.Handler,
)
// 验证码操作处理
indexGroup.GET("/captchaImage",
middleware.RateLimit(middleware.LimitOption{
Time: 300,
Count: 60,
Type: middleware.LIMIT_IP,
}),
controller.NewCaptcha.Image,
)
// 账号身份操作处理
{
indexGroup.POST("/login",
middleware.RateLimit(middleware.LimitOption{
Time: 300,
Count: 10,
Type: middleware.LIMIT_IP,
}),
controller.NewAccount.Login,
)
indexGroup.GET("/getInfo", middleware.PreAuthorize(nil), controller.NewAccount.Info)
indexGroup.GET("/getRouters", middleware.PreAuthorize(nil), controller.NewAccount.Router)
indexGroup.POST("/logout",
middleware.RateLimit(middleware.LimitOption{
Time: 300,
Count: 5,
Type: middleware.LIMIT_IP,
}),
controller.NewAccount.Logout,
)
}
// 账号注册操作处理
{
indexGroup.POST("/register",
middleware.RateLimit(middleware.LimitOption{
Time: 300,
Count: 10,
Type: middleware.LIMIT_IP,
}),
controller.NewRegister.UserName,
)
}
// 通用请求
commonGroup := router.Group("/common")
{
commonGroup.GET("/hash", middleware.PreAuthorize(nil), controller.NewCommont.Hash)
}
// 文件操作处理
fileGroup := router.Group("/file")
{
fileGroup.GET("/download/:filePath", middleware.PreAuthorize(nil), controller.NewFile.Download)
fileGroup.POST("/upload", middleware.PreAuthorize(nil), controller.NewFile.Upload)
fileGroup.POST("/chunkCheck", middleware.PreAuthorize(nil), controller.NewFile.ChunkCheck)
fileGroup.POST("/chunkUpload", middleware.PreAuthorize(nil), controller.NewFile.ChunkUpload)
fileGroup.POST("/chunkMerge", middleware.PreAuthorize(nil), controller.NewFile.ChunkMerge)
}
}

View File

@@ -0,0 +1,144 @@
package controller
import (
"ems.agt/src/framework/config"
commonConstants "ems.agt/src/framework/constants/common"
tokenConstants "ems.agt/src/framework/constants/token"
ctxUtils "ems.agt/src/framework/utils/ctx"
tokenUtils "ems.agt/src/framework/utils/token"
"ems.agt/src/framework/vo/result"
libAccount "ems.agt/src/lib_features/account"
commonModel "ems.agt/src/modules/common/model"
commonService "ems.agt/src/modules/common/service"
systemService "ems.agt/src/modules/system/service"
"github.com/gin-gonic/gin"
)
// 实例化控制层 AccountController 结构体
var NewAccount = &AccountController{
accountService: commonService.NewAccountImpl,
sysLogLoginService: systemService.NewSysLogLoginImpl,
}
// 账号身份操作处理
//
// PATH /
type AccountController struct {
// 账号身份操作服务
accountService commonService.IAccount
// 系统登录访问
sysLogLoginService systemService.ISysLogLogin
}
// 系统登录
//
// POST /login
func (s *AccountController) Login(c *gin.Context) {
var loginBody commonModel.LoginBody
if err := c.ShouldBindJSON(&loginBody); err != nil {
c.JSON(400, result.CodeMsg(400, "参数错误"))
return
}
// 当前请求信息
ipaddr, location := ctxUtils.IPAddrLocation(c)
os, browser := ctxUtils.UaOsBrowser(c)
// 校验验证码
err := s.accountService.ValidateCaptcha(
loginBody.Code,
loginBody.UUID,
)
// 根据错误信息,创建系统访问记录
if err != nil {
msg := err.Error() + " " + loginBody.Code
s.sysLogLoginService.NewSysLogLogin(
loginBody.Username, commonConstants.STATUS_NO, msg,
ipaddr, location, os, browser,
)
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 登录用户信息
loginUser, err := s.accountService.LoginByUsername(loginBody.Username, loginBody.Password)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
// 生成令牌,创建系统访问记录
tokenStr := tokenUtils.Create(&loginUser, ipaddr, location, os, browser)
if tokenStr == "" {
c.JSON(200, result.Err(nil))
return
} else {
s.sysLogLoginService.NewSysLogLogin(
loginBody.Username, commonConstants.STATUS_YES, "登录成功",
ipaddr, location, os, browser,
)
}
// 设置登录会话-兼容旧登录方式
libAccount.SessionToken(loginBody.Username, ipaddr)
c.JSON(200, result.OkData(map[string]any{
tokenConstants.RESPONSE_FIELD: tokenStr,
}))
}
// 登录用户信息
//
// GET /getInfo
func (s *AccountController) Info(c *gin.Context) {
loginUser, err := ctxUtils.LoginUser(c)
if err != nil {
c.JSON(401, result.CodeMsg(401, err.Error()))
return
}
// 角色权限集合,管理员拥有所有权限
isAdmin := config.IsAdmin(loginUser.UserID)
roles, perms := s.accountService.RoleAndMenuPerms(loginUser.UserID, isAdmin)
c.JSON(200, result.OkData(map[string]any{
"user": loginUser.User,
"roles": roles,
"permissions": perms,
}))
}
// 登录用户路由信息
//
// GET /getRouters
func (s *AccountController) Router(c *gin.Context) {
userID := ctxUtils.LoginUserToUserID(c)
// 前端路由,管理员拥有所有
isAdmin := config.IsAdmin(userID)
buildMenus := s.accountService.RouteMenus(userID, isAdmin)
c.JSON(200, result.OkData(buildMenus))
}
// 系统登出
//
// POST /logout
func (s *AccountController) Logout(c *gin.Context) {
tokenStr := ctxUtils.Authorization(c)
if tokenStr != "" {
// 存在token时记录退出信息
userName := tokenUtils.Remove(tokenStr)
if userName != "" {
// 当前请求信息
ipaddr, location := ctxUtils.IPAddrLocation(c)
os, browser := ctxUtils.UaOsBrowser(c)
// 创建系统访问记录
s.sysLogLoginService.NewSysLogLogin(
userName, commonConstants.STATUS_NO, "退出成功",
ipaddr, location, os, browser,
)
}
}
c.JSON(200, result.OkMsg("退出成功"))
}

View File

@@ -0,0 +1,129 @@
package controller
import (
"time"
"ems.agt/src/framework/config"
"ems.agt/src/framework/constants/cachekey"
"ems.agt/src/framework/constants/captcha"
"ems.agt/src/framework/logger"
"ems.agt/src/framework/redis"
"ems.agt/src/framework/utils/parse"
"ems.agt/src/framework/vo/result"
systemService "ems.agt/src/modules/system/service"
"github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
)
// 实例化控制层 CaptchaController 结构体
var NewCaptcha = &CaptchaController{
sysConfigService: systemService.NewSysConfigImpl,
}
// 验证码操作处理
//
// PATH /
type CaptchaController struct {
// 参数配置服务
sysConfigService systemService.ISysConfig
}
// 获取验证码
//
// GET /captchaImage
func (s *CaptchaController) Image(c *gin.Context) {
// 从数据库配置获取验证码开关 true开启false关闭
captchaEnabledStr := s.sysConfigService.SelectConfigValueByKey("sys.account.captchaEnabled")
captchaEnabled := parse.Boolean(captchaEnabledStr)
if !captchaEnabled {
c.JSON(200, result.Ok(map[string]any{
"captchaEnabled": captchaEnabled,
}))
return
}
// 生成唯一标识
verifyKey := ""
data := map[string]any{
"captchaEnabled": captchaEnabled,
"uuid": "",
"img": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
}
// 从数据库配置获取验证码类型 math 数值计算 char 字符验证
captchaType := s.sysConfigService.SelectConfigValueByKey("sys.account.captchaType")
if captchaType == captcha.TYPE_MATH {
math := config.Get("mathCaptcha").(map[string]any)
driverCaptcha := &base64Captcha.DriverMath{
//Height png height in pixel.
Height: math["height"].(int),
// Width Captcha png width in pixel.
Width: math["width"].(int),
//NoiseCount text noise count.
NoiseCount: math["noise"].(int),
//ShowLineOptions := OptionShowHollowLine | OptionShowSlimeLine | OptionShowSineLine .
ShowLineOptions: base64Captcha.OptionShowHollowLine,
}
if math["color"].(bool) {
//BgColor captcha image background color (optional)
driverCaptcha.BgColor = parse.Color(math["background"].(string))
}
// 验证码生成
id, question, answer := driverCaptcha.GenerateIdQuestionAnswer()
// 验证码表达式解析输出
item, err := driverCaptcha.DrawCaptcha(question)
if err != nil {
logger.Infof("Generate Id Question Answer %s %s : %v", captchaType, question, err)
} else {
data["uuid"] = id
data["img"] = item.EncodeB64string()
expiration := captcha.EXPIRATION * time.Second
verifyKey = cachekey.CAPTCHA_CODE_KEY + id
redis.SetByExpire("", verifyKey, answer, expiration)
}
}
if captchaType == captcha.TYPE_CHAR {
char := config.Get("charCaptcha").(map[string]any)
driverCaptcha := &base64Captcha.DriverString{
//Height png height in pixel.
Height: char["height"].(int),
// Width Captcha png width in pixel.
Width: char["width"].(int),
//NoiseCount text noise count.
NoiseCount: char["noise"].(int),
//Length random string length.
Length: char["size"].(int),
//Source is a unicode which is the rand string from.
Source: char["chars"].(string),
//ShowLineOptions := OptionShowHollowLine | OptionShowSlimeLine | OptionShowSineLine .
ShowLineOptions: base64Captcha.OptionShowHollowLine,
}
if char["color"].(bool) {
//BgColor captcha image background color (optional)
driverCaptcha.BgColor = parse.Color(char["background"].(string))
}
// 验证码生成
id, question, answer := driverCaptcha.GenerateIdQuestionAnswer()
// 验证码表达式解析输出
item, err := driverCaptcha.DrawCaptcha(question)
if err != nil {
logger.Infof("Generate Id Question Answer %s %s : %v", captchaType, question, err)
} else {
data["uuid"] = id
data["img"] = item.EncodeB64string()
expiration := captcha.EXPIRATION * time.Second
verifyKey = cachekey.CAPTCHA_CODE_KEY + id
redis.SetByExpire("", verifyKey, answer, expiration)
}
}
// 本地开发下返回验证码结果,方便接口调试
if config.Env() == "local" {
text, _ := redis.Get("", verifyKey)
data["text"] = text
c.JSON(200, result.Ok(data))
return
}
c.JSON(200, result.Ok(data))
}

View File

@@ -0,0 +1,20 @@
package controller
import (
"github.com/gin-gonic/gin"
)
// 实例化控制层 CommontController 结构体
var NewCommont = &CommontController{}
// 通用请求
//
// PATH /
type CommontController struct{}
// 哈希加密
//
// GET /hash
func (s *CommontController) Hash(c *gin.Context) {
c.String(200, "commont Hash")
}

View File

@@ -0,0 +1,185 @@
package controller
import (
"encoding/base64"
"fmt"
"net/url"
"strings"
"ems.agt/src/framework/constants/uploadsubpath"
"ems.agt/src/framework/utils/file"
"ems.agt/src/framework/vo/result"
"github.com/gin-gonic/gin"
)
// 实例化控制层 FileController 结构体
var NewFile = &FileController{}
// 文件操作处理
//
// PATH /
type FileController struct{}
// 下载文件
//
// GET /download/:filePath
func (s *FileController) Download(c *gin.Context) {
filePath := c.Param("filePath")
if len(filePath) < 8 {
c.JSON(400, result.CodeMsg(400, "参数错误"))
return
}
// base64解析出地址
decodedBytes, err := base64.StdEncoding.DecodeString(filePath)
if err != nil {
c.JSON(400, result.CodeMsg(400, err.Error()))
return
}
routerPath := string(decodedBytes)
// 地址文件名截取
fileName := routerPath[strings.LastIndex(routerPath, "/")+1:]
// 响应头
c.Writer.Header().Set("Content-Disposition", `attachment; filename="`+url.QueryEscape(fileName)+`"`)
c.Writer.Header().Set("Accept-Ranges", "bytes")
c.Writer.Header().Set("Content-Type", "application/octet-stream")
// 断点续传
headerRange := c.GetHeader("Range")
resultMap, err := file.ReadUploadFileStream(routerPath, headerRange)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
if headerRange != "" {
c.Writer.Header().Set("Content-Range", fmt.Sprint(resultMap["range"]))
c.Writer.Header().Set("Content-Length", fmt.Sprint(resultMap["chunkSize"]))
c.Status(206)
} else {
c.Writer.Header().Set("Content-Length", fmt.Sprint(resultMap["fileSize"]))
c.Status(200)
}
c.Writer.Write(resultMap["data"].([]byte))
}
// 上传文件
//
// POST /upload
func (s *FileController) Upload(c *gin.Context) {
// 上传的文件
formFile, err := c.FormFile("file")
if err != nil {
c.JSON(400, result.CodeMsg(400, "参数错误"))
return
}
// 子路径
subPath := c.PostForm("subPath")
if _, ok := uploadsubpath.UploadSubpath[subPath]; !ok {
c.JSON(400, result.CodeMsg(400, "参数错误"))
return
}
// 上传文件转存
upFilePath, err := file.TransferUploadFile(formFile, subPath, nil)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
newFileName := upFilePath[strings.LastIndex(upFilePath, "/")+1:]
c.JSON(200, result.OkData(map[string]string{
"url": "http://" + c.Request.Host + upFilePath,
"fileName": upFilePath,
"newFileName": newFileName,
"originalFileName": formFile.Filename,
}))
}
// 切片文件检查
//
// POST /chunkCheck
func (s *FileController) ChunkCheck(c *gin.Context) {
var body struct {
// 唯一标识
Identifier string `json:"identifier" binding:"required"`
// 文件名
FileName string `json:"fileName" binding:"required"`
}
err := c.ShouldBindJSON(&body)
if err != nil {
c.JSON(400, result.CodeMsg(400, "参数错误"))
return
}
// 读取标识目录
chunks, err := file.ChunkCheckFile(body.Identifier, body.FileName)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(200, result.OkData(chunks))
}
// 切片文件合并
//
// POST /chunkMerge
func (s *FileController) ChunkMerge(c *gin.Context) {
var body struct {
// 唯一标识
Identifier string `json:"identifier" binding:"required"`
// 文件名
FileName string `json:"fileName" binding:"required"`
// 子路径类型
SubPath string `json:"subPath" binding:"required"`
}
err := c.ShouldBindJSON(&body)
if err != nil {
c.JSON(400, result.CodeMsg(400, "参数错误"))
return
}
if _, ok := uploadsubpath.UploadSubpath[body.SubPath]; !ok {
c.JSON(400, result.CodeMsg(400, "参数错误"))
return
}
// 切片文件合并
mergeFilePath, err := file.ChunkMergeFile(body.Identifier, body.FileName, body.SubPath)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
newFileName := mergeFilePath[strings.LastIndex(mergeFilePath, "/")+1:]
c.JSON(200, result.OkData(map[string]string{
"url": "http://" + c.Request.Host + mergeFilePath,
"fileName": mergeFilePath,
"newFileName": newFileName,
"originalFileName": body.FileName,
}))
}
// 切片文件上传
//
// POST /chunkUpload
func (s *FileController) ChunkUpload(c *gin.Context) {
// 切片编号
index := c.PostForm("index")
// 切片唯一标识
identifier := c.PostForm("identifier")
// 上传的文件
formFile, err := c.FormFile("file")
if index == "" || identifier == "" || err != nil {
c.JSON(400, result.CodeMsg(400, "参数错误"))
return
}
// 上传文件转存
chunkFilePath, err := file.TransferChunkUploadFile(formFile, index, identifier)
if err != nil {
c.JSON(200, result.ErrMsg(err.Error()))
return
}
c.JSON(206, result.OkData(chunkFilePath))
}

View File

@@ -0,0 +1,28 @@
package controller
import (
"fmt"
"ems.agt/src/framework/config"
"ems.agt/src/framework/vo/result"
"github.com/gin-gonic/gin"
)
// 实例化控制层 IndexController 结构体
var NewIndex = &IndexController{}
// 路由主页
//
// PATH /
type IndexController struct{}
// 根路由
//
// GET /
func (s *IndexController) Handler(c *gin.Context) {
name := config.Get("framework.name").(string)
version := config.Get("framework.version").(string)
str := "欢迎使用%s后台管理框架当前版本%s请通过前端管理地址访问。"
c.JSON(200, result.OkMsg(fmt.Sprintf(str, name, version)))
}

View File

@@ -0,0 +1,88 @@
package controller
import (
"strings"
commonConstants "ems.agt/src/framework/constants/common"
ctxUtils "ems.agt/src/framework/utils/ctx"
"ems.agt/src/framework/utils/regular"
"ems.agt/src/framework/vo/result"
commonModel "ems.agt/src/modules/common/model"
commonService "ems.agt/src/modules/common/service"
systemService "ems.agt/src/modules/system/service"
"github.com/gin-gonic/gin"
)
// 实例化控制层 RegisterController 结构体
var NewRegister = &RegisterController{
registerService: commonService.NewRegisterImpl,
sysLogLoginService: systemService.NewSysLogLoginImpl,
}
// 账号注册操作处理
//
// PATH /
type RegisterController struct {
// 账号注册操作服务
registerService commonService.IRegister
// 系统登录访问
sysLogLoginService systemService.ISysLogLogin
}
// 账号注册
//
// GET /captchaImage
func (s *RegisterController) UserName(c *gin.Context) {
var registerBody commonModel.RegisterBody
if err := c.ShouldBindJSON(&registerBody); err != nil {
c.JSON(400, result.ErrMsg("参数错误"))
return
}
// 判断必传参数
if !regular.ValidUsername(registerBody.Username) {
c.JSON(200, result.ErrMsg("账号不能以数字开头可包含大写小写字母数字且不少于5位"))
return
}
if !regular.ValidPassword(registerBody.Password) {
c.JSON(200, result.ErrMsg("登录密码至少包含大小写字母、数字、特殊符号且不少于6位"))
return
}
if registerBody.Password != registerBody.ConfirmPassword {
c.JSON(200, result.ErrMsg("用户确认输入密码不一致"))
return
}
// 当前请求信息
ipaddr, location := ctxUtils.IPAddrLocation(c)
os, browser := ctxUtils.UaOsBrowser(c)
// 校验验证码
err := s.registerService.ValidateCaptcha(
registerBody.Code,
registerBody.UUID,
)
// 根据错误信息,创建系统访问记录
if err != nil {
msg := err.Error() + " " + registerBody.Code
s.sysLogLoginService.NewSysLogLogin(
registerBody.Username, commonConstants.STATUS_NO, msg,
ipaddr, location, os, browser,
)
c.JSON(200, result.ErrMsg(err.Error()))
return
}
infoStr := s.registerService.ByUserName(registerBody.Username, registerBody.Password, registerBody.UserType)
if !strings.HasPrefix(infoStr, "注册") {
msg := registerBody.Username + " 注册成功 " + infoStr
s.sysLogLoginService.NewSysLogLogin(
registerBody.Username, commonConstants.STATUS_NO, msg,
ipaddr, location, os, browser,
)
c.JSON(200, result.OkMsg("注册成功"))
return
}
c.JSON(200, result.ErrMsg(infoStr))
}

View File

@@ -0,0 +1,16 @@
package model
// LoginBody 用户登录对象
type LoginBody struct {
// Username 用户名
Username string `json:"username" binding:"required"`
// Password 用户密码
Password string `json:"password" binding:"required"`
// Code 验证码
Code string `json:"code"`
// UUID 验证码唯一标识
UUID string `json:"uuid"`
}

View File

@@ -0,0 +1,22 @@
package model
// RegisterBody 用户注册对象
type RegisterBody struct {
// Username 用户名
Username string `json:"username" binding:"required"`
// Password 用户密码
Password string `json:"password" binding:"required"`
// ConfirmPassword 用户确认密码
ConfirmPassword string `json:"confirmPassword" binding:"required"`
// Code 验证码
Code string `json:"code"`
// UUID 验证码唯一标识
UUID string `json:"uuid"`
// UserType 标记用户类型
UserType string `json:"userType"`
}

View File

@@ -0,0 +1,21 @@
package service
import "ems.agt/src/framework/vo"
// 账号身份操作服务 服务层接口
type IAccount interface {
// ValidateCaptcha 校验验证码
ValidateCaptcha(code, uuid string) error
// LoginByUsername 登录生成token
LoginByUsername(username, password string) (vo.LoginUser, error)
// ClearLoginRecordCache 清除错误记录次数
ClearLoginRecordCache(username string) bool
// RoleAndMenuPerms 角色和菜单数据权限
RoleAndMenuPerms(userId string, isAdmin bool) ([]string, []string)
// RouteMenus 前端路由所需要的菜单
RouteMenus(userId string, isAdmin bool) []vo.Router
}

View File

@@ -0,0 +1,167 @@
package service
import (
"errors"
"fmt"
"time"
"ems.agt/src/framework/config"
adminConstants "ems.agt/src/framework/constants/admin"
"ems.agt/src/framework/constants/cachekey"
"ems.agt/src/framework/constants/common"
"ems.agt/src/framework/redis"
"ems.agt/src/framework/utils/crypto"
"ems.agt/src/framework/utils/parse"
"ems.agt/src/framework/vo"
systemService "ems.agt/src/modules/system/service"
)
// 实例化服务层 AccountImpl 结构体
var NewAccountImpl = &AccountImpl{
sysUserService: systemService.NewSysUserImpl,
sysConfigService: systemService.NewSysConfigImpl,
sysRoleService: systemService.NewSysRoleImpl,
sysMenuService: systemService.NewSysMenuImpl,
}
// 账号身份操作服务 服务层处理
type AccountImpl struct {
// 用户信息服务
sysUserService systemService.ISysUser
// 参数配置服务
sysConfigService systemService.ISysConfig
// 角色服务
sysRoleService systemService.ISysRole
// 菜单服务
sysMenuService systemService.ISysMenu
}
// ValidateCaptcha 校验验证码
func (s *AccountImpl) ValidateCaptcha(code, uuid string) error {
// 验证码检查,从数据库配置获取验证码开关 true开启false关闭
captchaEnabledStr := s.sysConfigService.SelectConfigValueByKey("sys.account.captchaEnabled")
if !parse.Boolean(captchaEnabledStr) {
return nil
}
if code == "" || uuid == "" {
return errors.New("验证码信息错误")
}
verifyKey := cachekey.CAPTCHA_CODE_KEY + uuid
captcha, _ := redis.Get("", verifyKey)
if captcha == "" {
return errors.New("验证码已失效")
}
redis.Del("", verifyKey)
if captcha != code {
return errors.New("验证码错误")
}
return nil
}
// LoginByUsername 登录创建用户信息
func (s *AccountImpl) LoginByUsername(username, password string) (vo.LoginUser, error) {
loginUser := vo.LoginUser{}
// 检查密码重试次数
retrykey, retryCount, lockTime, err := s.passwordRetryCount(username)
if err != nil {
return loginUser, err
}
// 查询用户登录账号
sysUser := s.sysUserService.SelectUserByUserName(username)
if sysUser.UserName != username {
return loginUser, errors.New("用户不存在或密码错误")
}
if sysUser.DelFlag == common.STATUS_YES {
return loginUser, errors.New("对不起,您的账号已被删除")
}
if sysUser.Status == common.STATUS_NO {
return loginUser, errors.New("对不起,您的账号已禁用")
}
// 检验用户密码
compareBool := crypto.BcryptCompare(password, sysUser.Password)
if !compareBool {
redis.SetByExpire("", retrykey, retryCount+1, lockTime)
return loginUser, errors.New("用户不存在或密码错误")
} else {
// 清除错误记录次数
s.ClearLoginRecordCache(username)
}
// 登录用户信息
loginUser.UserID = sysUser.UserID
loginUser.DeptID = sysUser.DeptID
loginUser.User = sysUser
// 用户权限组标识
isAdmin := config.IsAdmin(sysUser.UserID)
if isAdmin {
loginUser.Permissions = []string{adminConstants.PERMISSION}
} else {
perms := s.sysMenuService.SelectMenuPermsByUserId(sysUser.UserID)
loginUser.Permissions = parse.RemoveDuplicates(perms)
}
return loginUser, nil
}
// ClearLoginRecordCache 清除错误记录次数
func (s *AccountImpl) ClearLoginRecordCache(username string) bool {
cacheKey := cachekey.PWD_ERR_CNT_KEY + username
hasKey, _ := redis.Has("", cacheKey)
if hasKey {
delOk, _ := redis.Del("", cacheKey)
return delOk
}
return false
}
// passwordRetryCount 密码重试次数
func (s *AccountImpl) passwordRetryCount(username string) (string, int64, time.Duration, error) {
// 验证登录次数和错误锁定时间
maxRetryCount := config.Get("user.password.maxRetryCount").(int)
lockTime := config.Get("user.password.lockTime").(int)
// 验证缓存记录次数
retrykey := cachekey.PWD_ERR_CNT_KEY + username
retryCount, err := redis.Get("", retrykey)
if retryCount == "" || err != nil {
retryCount = "0"
}
// 是否超过错误值
retryCountInt64 := parse.Number(retryCount)
if retryCountInt64 >= int64(maxRetryCount) {
msg := fmt.Sprintf("密码输入错误 %d 次,帐户锁定 %d 分钟", maxRetryCount, lockTime)
return retrykey, retryCountInt64, time.Duration(lockTime) * time.Minute, errors.New(msg)
}
return retrykey, retryCountInt64, time.Duration(lockTime) * time.Minute, nil
}
// RoleAndMenuPerms 角色和菜单数据权限
func (s *AccountImpl) RoleAndMenuPerms(userId string, isAdmin bool) ([]string, []string) {
if isAdmin {
return []string{adminConstants.ROLE_KEY}, []string{adminConstants.PERMISSION}
} else {
// 角色key
roleGroup := []string{}
roles := s.sysRoleService.SelectRoleListByUserId(userId)
for _, role := range roles {
roleGroup = append(roleGroup, role.RoleKey)
}
// 菜单权限key
perms := s.sysMenuService.SelectMenuPermsByUserId(userId)
return parse.RemoveDuplicates(roleGroup), parse.RemoveDuplicates(perms)
}
}
// RouteMenus 前端路由所需要的菜单
func (s *AccountImpl) RouteMenus(userId string, isAdmin bool) []vo.Router {
var buildMenus []vo.Router
if isAdmin {
menus := s.sysMenuService.SelectMenuTreeByUserId("*")
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
} else {
menus := s.sysMenuService.SelectMenuTreeByUserId(userId)
buildMenus = s.sysMenuService.BuildRouteMenus(menus, "")
}
return buildMenus
}

Some files were not shown because too many files have changed in this diff Show More