Commit 9b2207d5 authored by 赵啸非's avatar 赵啸非

Initial commit

parents
Pipeline #2795 failed with stages
*.iml
node_modules/
.idea/
*.class
target/
\ No newline at end of file
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>release</id>
<formats>
<format>tar.gz</format>
</formats>
<!-- 需要打包的文件集 -->
<fileSets>
<fileSet>
<directory>${project.parent.basedir}/bill-manager-ui/admin/dist</directory>
<includes>
<include>**/*</include>
</includes>
<outputDirectory>/dist</outputDirectory>
</fileSet>
</fileSets>
</assembly>
\ No newline at end of file
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>release</id>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>target/bin</directory>
<outputDirectory>bin</outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>${project.parent.basedir}/dist/${project.parent.artifactId}/boot</directory>
<outputDirectory>boot</outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>./db</directory>
<includes>
<include>*.sql</include>
</includes>
<outputDirectory>db</outputDirectory>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
<files>
<file>
<source>target/${project.artifactId}-${project.version}.jar</source>
<outputDirectory>boot</outputDirectory>
</file>
</files>
</assembly>
\ No newline at end of file
*.iml
node_modules/
.idea/
*.class
target/
\ No newline at end of file
-- ----------------------------
2024-04-08
-- ----------------------------
-- ----------------------------
-- 排号汇总表
-- ----------------------------
DROP TABLE IF EXISTS `mortals_xhx_ph_queue`;
CREATE TABLE mortals_xhx_ph_queue(
`id` bigint(20) AUTO_INCREMENT COMMENT '序号,主键,自增长',
`ordernumber` varchar(64) NOT NULL COMMENT '预约编号,为空现场取号',
`style` char(8) NOT NULL DEFAULT '未叫号' COMMENT '叫号状态 (未叫号,叫号中,完成)',
`business` varchar(128) NOT NULL COMMENT '业务名',
`window_name` varchar(128) NOT NULL COMMENT '窗口名',
`window_fromnum` varchar(128) NOT NULL COMMENT '窗口编号',
`flownum` varchar(64) NOT NULL COMMENT '流水编号,当天的第xxx号',
`formernum` varchar(32) NOT NULL COMMENT '呼叫转移号',
`people_idcard` varchar(64) NOT NULL COMMENT '身份证号',
`people_name` varchar(64) NOT NULL COMMENT '姓名',
`people_sex` char(6) NOT NULL COMMENT '性别',
`people_phone` varchar(20) NOT NULL COMMENT '手机号',
`workman_name` varchar(32) NOT NULL COMMENT '工作人员姓名',
`workman_number` varchar(20) NOT NULL COMMENT '工作人员工号',
`taketime` datetime COMMENT '取号时间',
`calltime` datetime COMMENT '叫号时间',
`endtime` datetime COMMENT '结束时间',
`wait_time` int(9) NOT NULL DEFAULT '0' COMMENT '等待时间,单位s',
`handle_time` int(9) NOT NULL DEFAULT '0' COMMENT '办理时间,单位s',
`device_name` varchar(128) NOT NULL COMMENT '取号设备名',
`call_name` varchar(128) NOT NULL COMMENT '呼叫设备',
`matter_name` varchar(256) NOT NULL COMMENT '事项名',
`queueid` varchar(128) NOT NULL COMMENT '排号队列ID,唯一',
`wy_signin` varchar(16) NOT NULL DEFAULT '现场取号' COMMENT '取号方式 (现场取号,微信取号)',
`section_name` varchar(128) NOT NULL COMMENT '部门名称',
`hall_name` varchar(128) NOT NULL COMMENT '大厅名称',
`device_type` varchar(32) NOT NULL DEFAULT '排号机' COMMENT '取号设备类型',
`site_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '站点Id',
`site_code` varchar(128) NOT NULL COMMENT '站点编码',
`site_name` varchar(128) COMMENT '站点名称',
`ext_num` varchar(128) NOT NULL COMMENT '扩展编号',
`create_user_id` bigint(20) COMMENT '创建用户',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime COMMENT '更新时间',
PRIMARY KEY (`id`)
,KEY `style` (`style`) USING BTREE
,KEY `business` (`business`) USING BTREE
,KEY `window_fromnum` (`window_fromnum`) USING BTREE
,KEY `people_idcard` (`people_idcard`) USING BTREE
,KEY `people_phone` (`people_phone`) USING BTREE
,KEY `workman_number` (`workman_number`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='排号汇总';
-- ----------------------------
-- 评价汇总表
-- ----------------------------
DROP TABLE IF EXISTS `mortals_xhx_pj_evaluate`;
CREATE TABLE mortals_xhx_pj_evaluate(
`id` bigint(20) AUTO_INCREMENT COMMENT '序号,主键,自增长',
`people_idcard` varchar(64) NOT NULL COMMENT '评价人身份证号',
`people_name` varchar(64) NOT NULL COMMENT '评价人姓名',
`people_sex` char(6) NOT NULL COMMENT '评价人性别',
`people_phone` varchar(20) NOT NULL COMMENT '评价人手机号',
`pj_option` varchar(32) NOT NULL COMMENT '评价选项(非常满意,满意,基本满意,不满意,非常不满意)',
`content_tag` varchar(255) NOT NULL COMMENT '评价标签',
`pic_url` varchar(128) NOT NULL COMMENT '评价人图片地址',
`section_name` varchar(128) NOT NULL COMMENT '部门',
`hall_name` varchar(128) NOT NULL COMMENT '大厅',
`pj_source` varchar(20) NOT NULL COMMENT '评价来源 (安卓,导视机,微信)',
`opinion` varchar(512) COMMENT '手输意见',
`window_name` varchar(128) NOT NULL COMMENT '窗口名',
`window_fromnum` varchar(128) NOT NULL COMMENT '窗口编号',
`flounum` varchar(20) COMMENT '排号编号',
`pjxt` varchar(32) DEFAULT '窗口评价' COMMENT '窗口评价(自助服务终端,背靠背评价,微官网)',
`workman_name` varchar(32) NOT NULL COMMENT '工作人员姓名',
`workman_number` varchar(20) NOT NULL COMMENT '工作人员工号',
`devicenum` varchar(64) COMMENT '评价器mac',
`evaluatestatus` varchar(32) DEFAULT '等待评价' COMMENT '评价状态(等待评价,完成,截图有误,用户截图签名超时,用户评价超时,收到图片等待签名',
`evaluatetype` char(8) DEFAULT '截图' COMMENT '截图还是评价 (截图,评价)',
`photobefor` varchar(256) NOT NULL COMMENT '截图地址',
`photoautograph` varchar(256) COMMENT '签字图片',
`picture` varchar(256) NOT NULL COMMENT '抓拍评价人照片',
`process` varchar(256) NOT NULL COMMENT '音频视频地址',
`eyevaluate` varchar(16) DEFAULT '未标记' COMMENT '评价标记(未标记,标记非恶意差评,标记恶意差评)',
`pj_type` varchar(16) NOT NULL DEFAULT '窗口评价' COMMENT '评价指向 (窗口评价,部门评价,排号评价)',
`pj_time` datetime COMMENT '评价时间',
`site_id` bigint(20) DEFAULT '0' COMMENT '站点Id',
`site_code` varchar(128) COMMENT '站点编码',
`site_name` varchar(128) COMMENT '站点名称',
`ext_num` varchar(128) COMMENT '扩展编号',
`create_user_id` bigint(20) COMMENT '创建用户',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime COMMENT '更新时间',
PRIMARY KEY (`id`)
,KEY `people_idcard` (`people_idcard`) USING BTREE
,KEY `people_phone` (`people_phone`) USING BTREE
,KEY `pj_option` (`pj_option`) USING BTREE
,KEY `pj_source` (`pj_source`) USING BTREE
,KEY `site_id` (`site_id`) USING BTREE
,KEY `site_code` (`site_code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='评价汇总';
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
use `device-platform`;
-- ----------------------------
-- 设备生产厂商菜单 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_menu` VALUES (null, '设备生产厂商', '/firm/list', 0, 1, 1, 0, 0,'',NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL);
-- ----------------------------
-- 设备生产厂商资源路径 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备生产厂商-菜单管理-查看', '/firm/list,/firm/view,/firm/info,/firm/export,/firm/exportExcel,/firm/downloadTemplate,/firm/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备生产厂商-菜单管理-维护', '/firm/add,/firm/edit,/firm/delete,/firm/logicDelete,/firm/save,/firm/importData', 3, 0, NULL, NULL, NULL, 0);
-- ----------------------------
-- 设备菜单 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_menu` VALUES (null, '设备', '/device/list', 0, 1, 1, 0, 0,'',NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL);
-- ----------------------------
-- 设备资源路径 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备-菜单管理-查看', '/device/list,/device/view,/device/info,/device/export,/device/exportExcel,/device/downloadTemplate,/device/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备-菜单管理-维护', '/device/add,/device/edit,/device/delete,/device/logicDelete,/device/save,/device/importData', 3, 0, NULL, NULL, NULL, 0);
-- ----------------------------
-- 平台系统菜单 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_menu` VALUES (null, '平台系统', '/platform/list', 0, 1, 1, 0, 0,'',NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL);
-- ----------------------------
-- 平台系统资源路径 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_resource` VALUES (null, '平台系统-菜单管理-查看', '/platform/list,/platform/view,/platform/info,/platform/export,/platform/exportExcel,/platform/downloadTemplate,/platform/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '平台系统-菜单管理-维护', '/platform/add,/platform/edit,/platform/delete,/platform/logicDelete,/platform/save,/platform/importData', 3, 0, NULL, NULL, NULL, 0);
-- ----------------------------
-- 产品菜单 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_menu` VALUES (null, '产品', '/product/list', 0, 1, 1, 0, 0,'',NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL);
-- ----------------------------
-- 产品资源路径 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_resource` VALUES (null, '产品-菜单管理-查看', '/product/list,/product/view,/product/info,/product/export,/product/exportExcel,/product/downloadTemplate,/product/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '产品-菜单管理-维护', '/product/add,/product/edit,/product/delete,/product/logicDelete,/product/save,/product/importData', 3, 0, NULL, NULL, NULL, 0);
-- ----------------------------
-- 设备日志菜单 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_menu` VALUES (null, '设备日志', '/device/log/list', 0, 1, 1, 0, 0,'',NULL, NULL, NULL, 0, 0, 1, NULL, NULL, NULL);
-- ----------------------------
-- 设备日志资源路径 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备日志-菜单管理-查看', '/device/log/list,/device/log/view,/device/log/info,/device/log/export,/device/log/exportExcel,/device/log/downloadTemplate,/device/log/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备日志-菜单管理-维护', '/device/log/add,/device/log/edit,/device/log/delete,/device/log/logicDelete,/device/log/save,/device/log/importData', 3, 0, NULL, NULL, NULL, 0);
-- ----------------------------
-- 设备统计资源路径 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备统计-菜单管理-查看', '/device/stat/list,/device/stat/view,/device/stat/info,/device/stat/export,/device/stat/exportExcel,/device/stat/downloadTemplate,/device/stat/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备统计-菜单管理-维护', '/device/stat/add,/device/stat/edit,/device/stat/delete,/device/stat/logicDelete,/device/stat/save,/device/stat/importData', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备告警日志-菜单管理-查看', '/device/alarm/info/list,/device/alarm/info/view,/device/alarm/info/info,/device/alarm/info/export,/device/alarm/info/exportExcel,/device/alarm/info/downloadTemplate,/device/alarm/info/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备告警日志-菜单管理-维护', '/device/alarm/info/add,/device/alarm/info/edit,/device/alarm/info/delete,/device/alarm/info/logicDelete,/device/alarm/info/save,/device/alarm/info/importData', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备告警配置-菜单管理-查看', '/alarm/config/list,/alarm/config/view,/alarm/config/info,/alarm/config/export,/alarm/config/exportExcel,/alarm/config/downloadTemplate,/alarm/config/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备告警配置-菜单管理-维护', '/alarm/config/add,/alarm/config/edit,/alarm/config/delete,/alarm/config/logicDelete,/alarm/config/save,/alarm/config/importData', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备模块信息-菜单管理-查看', '/device/module/list,/device/module/view,/device/module/info,/device/module/export,/device/module/exportExcel,/device/module/downloadTemplate,/device/module/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备模块信息-菜单管理-维护', '/device/module/add,/device/module/edit,/device/module/delete,/device/module/logicDelete,/device/module/save,/device/module/importData', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '短信发送记录-菜单管理-查看', '/alarm/sms/send/list,/alarm/sms/send/view,/alarm/sms/send/info,/alarm/sms/send/export,/alarm/sms/send/exportExcel,/alarm/sms/send/downloadTemplate,/alarm/sms/send/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '短信发送记录-菜单管理-维护', '/alarm/sms/send/add,/alarm/sms/send/edit,/alarm/sms/send/delete,/alarm/sms/send/logicDelete,/alarm/sms/send/save,/alarm/sms/send/importData', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备前端模块部署-菜单管理-查看', '/device/module/distribute/list,/device/module/distribute/view,/device/module/distribute/info,/device/module/distribute/export,/device/module/distribute/exportExcel,/device/module/distribute/downloadTemplate,/device/module/distribute/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '设备前端模块部署-菜单管理-维护', '/device/module/distribute/add,/device/module/distribute/edit,/device/module/distribute/delete,/device/module/distribute/logicDelete,/device/module/distribute/save,/device/module/distribute/importData', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '产品客户端版本-菜单管理-查看', '/product/version/list,/product/version/view,/product/version/info,/product/version/export,/product/version/exportExcel,/product/version/downloadTemplate,/product/version/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '产品客户端版本-菜单管理-维护', '/product/version/add,/product/version/edit,/product/version/delete,/product/version/logicDelete,/product/version/save,/product/version/importData', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '应用发布部署-菜单管理-查看', '/app/publish/list,/app/publish/view,/app/publish/info,/app/publish/export,/app/publish/exportExcel,/app/publish/downloadTemplate,/app/publish/download', 3, 0, NULL, NULL, NULL, 0);
INSERT INTO `mortals_xhx_resource` VALUES (null, '应用发布部署-菜单管理-维护', '/app/publish/add,/app/publish/edit,/app/publish/delete,/app/publish/logicDelete,/app/publish/save,/app/publish/importData', 3, 0, NULL, NULL, NULL, 0);
-- ----------------------------
-- 应用发布部署参数 SQL
-- ----------------------------
INSERT INTO `mortals_xhx_param` VALUES (null, '应用类型', 'AppPublish', 'appType', '0', '前端', 1, 4, 0, 'appType', NULL, NULL, NULL);
INSERT INTO `mortals_xhx_param` VALUES (null, '应用类型', 'AppPublish', 'appType', '1', '后端', 1, 4, 0, 'appType', NULL, NULL, NULL);
INSERT INTO `mortals_xhx_param` VALUES (null, '是否部署', 'AppPublish', 'distribute', '0', '否', 1, 4, 0, 'distribute', NULL, NULL, NULL);
INSERT INTO `mortals_xhx_param` VALUES (null, '是否部署', 'AppPublish', 'distribute', '1', '是', 1, 4, 0, 'distribute', NULL, NULL, NULL);
\ No newline at end of file
-- ----------------------------
-- 排号汇总表
-- ----------------------------
DROP TABLE IF EXISTS `mortals_xhx_ph_queue`;
CREATE TABLE mortals_xhx_ph_queue(
`id` bigint(20) AUTO_INCREMENT COMMENT '序号,主键,自增长',
`ordernumber` varchar(64) NOT NULL COMMENT '预约编号,为空现场取号',
`style` char(8) NOT NULL DEFAULT '未叫号' COMMENT '叫号状态 (未叫号,叫号中,完成)',
`business` varchar(128) NOT NULL COMMENT '业务名',
`window_name` varchar(128) NOT NULL COMMENT '窗口名',
`window_fromnum` varchar(128) NOT NULL COMMENT '窗口编号',
`flownum` varchar(64) NOT NULL COMMENT '流水编号,当天的第xxx号',
`formernum` varchar(32) NOT NULL COMMENT '呼叫转移号',
`people_idcard` varchar(64) NOT NULL COMMENT '身份证号',
`people_name` varchar(64) NOT NULL COMMENT '姓名',
`people_sex` char(6) NOT NULL COMMENT '性别',
`people_phone` varchar(20) NOT NULL COMMENT '手机号',
`workman_name` varchar(32) NOT NULL COMMENT '工作人员姓名',
`workman_number` varchar(20) NOT NULL COMMENT '工作人员工号',
`taketime` datetime COMMENT '取号时间',
`calltime` datetime COMMENT '叫号时间',
`endtime` datetime COMMENT '结束时间',
`wait_time` int(9) NOT NULL DEFAULT '0' COMMENT '等待时间,单位s',
`handle_time` int(9) NOT NULL DEFAULT '0' COMMENT '办理时间,单位s',
`device_name` varchar(128) NOT NULL COMMENT '取号设备名',
`call_name` varchar(128) NOT NULL COMMENT '呼叫设备',
`matter_name` varchar(256) NOT NULL COMMENT '事项名',
`queueid` varchar(128) NOT NULL COMMENT '排号队列ID,唯一',
`wy_signin` varchar(16) NOT NULL DEFAULT '现场取号' COMMENT '取号方式 (现场取号,微信取号)',
`section_name` varchar(128) NOT NULL COMMENT '部门名称',
`hall_name` varchar(128) NOT NULL COMMENT '大厅名称',
`device_type` varchar(32) NOT NULL DEFAULT '排号机' COMMENT '取号设备类型',
`site_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '站点Id',
`site_code` varchar(128) NOT NULL COMMENT '站点编码',
`site_name` varchar(128) COMMENT '站点名称',
`ext_num` varchar(128) NOT NULL COMMENT '扩展编号',
`create_user_id` bigint(20) COMMENT '创建用户',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime COMMENT '更新时间',
PRIMARY KEY (`id`)
,KEY `style` (`style`) USING BTREE
,KEY `business` (`business`) USING BTREE
,KEY `window_fromnum` (`window_fromnum`) USING BTREE
,KEY `people_idcard` (`people_idcard`) USING BTREE
,KEY `people_phone` (`people_phone`) USING BTREE
,KEY `workman_number` (`workman_number`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='排号汇总';
-- ----------------------------
-- 评价汇总表
-- ----------------------------
DROP TABLE IF EXISTS `mortals_xhx_pj_evaluate`;
CREATE TABLE mortals_xhx_pj_evaluate(
`id` bigint(20) AUTO_INCREMENT COMMENT '序号,主键,自增长',
`people_idcard` varchar(64) NOT NULL COMMENT '评价人身份证号',
`people_name` varchar(64) NOT NULL COMMENT '评价人姓名',
`people_sex` char(6) NOT NULL COMMENT '评价人性别',
`people_phone` varchar(20) NOT NULL COMMENT '评价人手机号',
`pj_option` varchar(32) NOT NULL COMMENT '评价选项(非常满意,满意,基本满意,不满意,非常不满意)',
`content_tag` varchar(255) NOT NULL COMMENT '评价标签',
`pic_url` varchar(128) NOT NULL COMMENT '评价人图片地址',
`section_name` varchar(128) NOT NULL COMMENT '部门',
`hall_name` varchar(128) NOT NULL COMMENT '大厅',
`pj_source` varchar(20) NOT NULL COMMENT '评价来源 (安卓,导视机,微信)',
`opinion` varchar(512) COMMENT '手输意见',
`window_name` varchar(128) NOT NULL COMMENT '窗口名',
`window_fromnum` varchar(128) NOT NULL COMMENT '窗口编号',
`flounum` varchar(20) COMMENT '排号编号',
`pjxt` varchar(32) DEFAULT '窗口评价' COMMENT '窗口评价(自助服务终端,背靠背评价,微官网)',
`workman_name` varchar(32) NOT NULL COMMENT '工作人员姓名',
`workman_number` varchar(20) NOT NULL COMMENT '工作人员工号',
`devicenum` varchar(64) COMMENT '评价器mac',
`evaluatestatus` varchar(32) DEFAULT '等待评价' COMMENT '评价状态(等待评价,完成,截图有误,用户截图签名超时,用户评价超时,收到图片等待签名',
`evaluatetype` char(8) DEFAULT '截图' COMMENT '截图还是评价 (截图,评价)',
`photobefor` varchar(256) NOT NULL COMMENT '截图地址',
`photoautograph` varchar(256) COMMENT '签字图片',
`picture` varchar(256) NOT NULL COMMENT '抓拍评价人照片',
`process` varchar(256) NOT NULL COMMENT '音频视频地址',
`eyevaluate` varchar(16) DEFAULT '未标记' COMMENT '评价标记(未标记,标记非恶意差评,标记恶意差评)',
`pj_type` varchar(16) NOT NULL DEFAULT '窗口评价' COMMENT '评价指向 (窗口评价,部门评价,排号评价)',
`pj_time` datetime COMMENT '评价时间',
`site_id` bigint(20) DEFAULT '0' COMMENT '站点Id',
`site_code` varchar(128) COMMENT '站点编码',
`site_name` varchar(128) COMMENT '站点名称',
`ext_num` varchar(128) COMMENT '扩展编号',
`create_user_id` bigint(20) COMMENT '创建用户',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime COMMENT '更新时间',
PRIMARY KEY (`id`)
,KEY `people_idcard` (`people_idcard`) USING BTREE
,KEY `people_phone` (`people_phone`) USING BTREE
,KEY `pj_option` (`pj_option`) USING BTREE
,KEY `pj_source` (`pj_source`) USING BTREE
,KEY `site_id` (`site_id`) USING BTREE
,KEY `site_code` (`site_code`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='评价汇总';
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
#!/bin/bash
PORT="@profiles.server.port@"
PROJECT_NAME="@project.artifactId@";
MAIN_CLASS="$PROJECT_NAME-@project.version@.jar";
SHELL_NAME=$0
SHELL_LOG="${SHELL_NAME}.log"
LOG_DATE='date "+%Y-%m-%d"'
LOG_TIME='date "+%H-%M-%S"'
CDATE=$(date "+%Y-%m-%d")
CTIME=$(date "+%H-%M-%S")
#写日志
writelog() {
LOGINFO=$1
echo "${CDATE} ${CTIME}: ${SHELL_NAME} : ${LOGINFO}" >>${SHELL_LOG}
}
jcpid=`ps -ef | grep -v "grep" | grep "$MAIN_CLASS" | grep "app.port=$PORT" | sed -n '1P' | awk '{print $2}'`
if [ $jcpid ]; then
writelog "The $PROJECT_NAME start finished, PID is $jcpid"
exit $SUCCESS
else
writelog "start service..."
systemctl stop ${PROJECT_NAME} && systemctl start ${PROJECT_NAME}
fi
#!/bin/sh
RETVAL=$?
SHELL_NAME="deploy"
BASEDIR=$(dirname $0)
BASEDIR=$( (
cd "$BASEDIR"
pwd
))
LOCK_FILE="/tmp/deploy.lock"
# 时间变量
CDATE=$(date "+%Y-%m-%d")
CTIME=$(date "+%H:%M:%S")
SHELL_LOG="${BASEDIR}/${SHELL_NAME}.log"
JAVA_HOME="/usr/local/java/jdk1.8"
SERVICE_PATH="/usr/lib/systemd/system"
PUBLISH_PATH="/home/publish"
PROJECT_NAME="@project.artifactId@"
PROJECT_EXECPATH="${PUBLISH_PATH}/${PROJECT_NAME}"
PROJECT_UI_EXECPATH="${PUBLISH_PATH}/${PROJECT_NAME}-ui/dist"
PROJECT_FILENAME="${PROJECT_NAME}.tar.gz"
PROJECT_UI_FILENAME="${PROJECT_NAME}-ui.tar.gz"
PROJECT_SERVICE="${SERVICE_PATH}/${PROJECT_NAME}.service"
#写日志
writelog() {
LOGINFO=$1
echo "${CDATE} ${CTIME}: ${SHELL_NAME} : ${LOGINFO}" >>${SHELL_LOG}
echo ${LOGINFO}
}
#清理目标
clear_deploy() {
SERVICE=$1
EXECPATH=$2
#清理后台自启服务
rm -f ${SERVICE}
#清理执行文件目录
rm -rf ${EXECPATH}
}
#清理ui
clear_ui_deploy() {
EXEC_UI_PATH=$1
rm -rf ${EXEC_UI_PATH}
mkdir -p ${EXEC_UI_PATH}
}
build_service() {
SERVICE=$1
EXECPATH=$2
echo "" >${SERVICE}
echo "[Unit]" >>${SERVICE}
echo "Description=${PROJECT_NAME}" >>${SERVICE}
echo "After=network.target" >>${SERVICE}
echo "" >>${SERVICE}
echo "[Service]" >>${SERVICE}
echo "Environment=\"JAVA_HOME=$JAVA_HOME\"" >>${SERVICE}
echo "Type=forking" >>${SERVICE}
echo "ExecStart=${EXECPATH}/bin/start.sh" >>${SERVICE}
echo "ExecStop=${EXECPATH}/bin/shutdown.sh" >>${SERVICE}
echo "PrivateTmp=true" >>${SERVICE}
echo "" >>${SERVICE}
echo "[Install]" >>${SERVICE}
echo "WantedBy=multi-user.target" >>${SERVICE}
writelog "${PROJECT_NAME}服务创建完成!"
}
#启动服务与nginx
start_service() {
systemctl enable ${PROJECT_NAME}
systemctl daemon-reload
writelog "${PROJECT_NAME}服务启动..."
systemctl stop ${PROJECT_NAME} && systemctl start ${PROJECT_NAME}
project_status=$(systemctl status "${PROJECT_NAME}"|grep Active |awk '{print $2}')
jcpid=$(ps -ef | grep -v "grep" | grep "${PROJECT_NAME} " | awk '{print $2}')
writelog "${PROJECT_NAME}服务启动,PID is ${jcpid} ,status:${project_status}"
}
#部署后台服务
project_deploy() {
writelog "${PROJECT_NAME}_deploy"
systemctl stop ${PROJECT_NAME}
sleep 5
clear_deploy ${PROJECT_SERVICE} ${PROJECT_EXECPATH}
writelog "${PROJECT_NAME}_clear_finish"
tar -zvxf ./${PROJECT_FILENAME} -C ${PUBLISH_PATH}
build_service ${PROJECT_SERVICE} ${PROJECT_EXECPATH}
start_service
writelog "${PROJECT_NAME}_deploy_finish"
}
#部署前台服务
project_ui_deploy() {
writelog "${PROJECT_NAME}_ui_deploy"
clear_ui_deploy ${PROJECT_UI_EXECPATH}
tar -zvxf ./${PROJECT_UI_FILENAME} -C ${PUBLISH_PATH}
writelog "${PROJECT_NAME}_ui_deploy_finish"
}
#主函数
main() {
echo "后台服务部署"
project_deploy
#判断是否需要部署ui
if [ "@skipDeploy@" == "false" ];then
echo "前端服务部署"
project_ui_deploy
fi;
exit ${RETVAL}
}
main $1
if not exist "%JAVA_HOME%\bin\jps.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better! & EXIT /B 1
setlocal
set "PATH=%JAVA_HOME%\bin;%PATH%"
echo killing server
for /f "tokens=1" %%i in ('jps -m ^| find "@project.artifactId@"') do ( taskkill /F /PID %%i )
echo Done!
#! /bin/sh
PORT="@profiles.server.port@"
BASEDIR=$(dirname $0)
BASEDIR=$( (
cd "$BASEDIR"
pwd
))
PROJECT_NAME="@project.artifactId@"
MAIN_CLASS="$PROJECT_NAME"
if [ ! -n "$PORT" ]; then
echo $"Usage: $0 {port}"
exit $FAIL
fi
pid=$(ps ax | grep -i "$MAIN_CLASS" | grep java | grep -v grep | awk '{print $1}')
if [ -z "$pid" ]; then
echo "No Server running."
exit 1
fi
echo "stoping application $PROJECT_NAME......"
kill -15 ${pid}
echo "Send shutdown request to Server $PROJECT_NAME OK"
echo off
if not exist "%JAVA_HOME%\bin\java.exe" echo Please set the JAVA_HOME variable in your environment, We need java(x64)! jdk8 or later is better! & EXIT /B 1
set "JAVA=%JAVA_HOME%\bin\java.exe"
set BASEDIR=%~dp0
set BASEDIR="%BASEDIR:~0,-5%"
set PROJECT_NAME=@project.artifactId@
set APP_NAME=%PROJECT_NAME%-@project.version@.jar
set LOG_PATH="%BASEDIR%@profiles.log.path@/%PROJECT_NAME%"
if not exist "%LOG_PATH%" md "%LOG_PATH%"
set GC_PATH=%LOG_PATH%/gc.log
set HS_ERR_PATH=%LOG_PATH%PORT%-hs_err.log
set HEAP_DUMP_PATH=%LOG_PATH%/heap_dump.hprof
set TEMP_PATH=%LOG_PATH%/temp/
if not exist "%TEMP_PATH%" md "%TEMP_PATH%"
rem jvm启动参数
set JAVA_OPTS=-Xms512M -Xmx512M -Xss256K -XX:+UseAdaptiveSizePolicy -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:GCTimeRatio=39 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError
set JAVA_OPTS=%JAVA_OPTS% -XX:+PrintGCDateStamps -Xloggc:%GC_PATH%
set JAVA_OPTS=%JAVA_OPTS% -XX:ErrorFile=%HS_ERR_PATH%
set JAVA_OPTS=%JAVA_OPTS% -XX:HeapDumpPath=%HEAP_DUMP_PATH%
rem jvm config
set JVM_CONFIG=%JVM_CONFIG% -Dapp.name=%PROJECT_NAME%
set JVM_CONFIG=%JVM_CONFIG% -Dapp.port=%PORT%
set JVM_CONFIG=%JVM_CONFIG% -Djava.io.tmpdir=%TEMP_PATH%
set JVM_CONFIG=%JVM_CONFIG% -Dbasedir=%BASEDIR%
set JVM_CONFIG=%JVM_CONFIG% -Dloader.path=file://%BASEDIR%/conf,file://%BASEDIR%/lib
set JVM_CONFIG=%JVM_CONFIG% -Dfile.encoding=utf-8
set DEBUG_OPTS=
if ""%1"" == ""debug"" (
set DEBUG_OPTS= -Xloggc:../logs/gc.log -verbose:gc -XX:+PrintGCDetails -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=../logs
goto debug
)
set JMX_OPTS=
if ""%1"" == ""jmx"" (
set JMX_OPTS= -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9888 -Dcom.sun.management.jmxremote.ssl=FALSE -Dcom.sun.management.jmxremote.authenticate=FALSE
goto jmx
)
set "LOG_OPTS=--logging.config=%BASEDIR%/conf/logback-spring.xml"
echo "Starting the %APP_NAME%"
"%JAVA%" %JAVA_OPTS% -server %JVM_CONFIG% -jar %BASEDIR%/boot/%APP_NAME%
echo ""%JAVA%" %JAVA_OPTS% -server %JVM_CONFIG% -jar %BASEDIR%/boot/%APP_NAME% "
goto end
:debug
echo "debug"
"%JAVA%" -Xms512m -Xmx512m -server %DEBUG_OPTS% %JVM_CONFIG% -jar ../boot/%APP_NAME%
goto end
:jmx
"%JAVA%" -Xms512m -Xmx512m -server %JMX_OPTS% %JVM_CONFIG% -jar ../boot/%APP_NAME%
goto end
:end
pause
#!/bin/sh
PORT="@profiles.server.port@"
BASEDIR=`dirname $0`/..
BASEDIR=`(cd "$BASEDIR"; pwd)`
PROJECT_NAME="@project.artifactId@";
MAIN_CLASS="$PROJECT_NAME-@project.version@.jar";
LOG_PATH="@profiles.log.path@/$PROJECT_NAME"
GC_PATH=$LOG_PATH/PROJECT_NAME"-gc.log"
HS_ERR_PATH=$LOG_PATH/$PROJECT_NAME"-hs_err.log"
HEAP_DUMP_PATH=$LOG_PATH/$PROJECT_NAME"-heap_dump.hprof"
TEMP_PATH=$LOG_PATH/temp/
SUCCESS=0
FAIL=9
if [ ! -n "$PORT" ]; then
echo $"Usage: $0 {port}"
exit $FAIL
fi
if [ ! -d $LOG_PATH ];
then
mkdir -p $LOG_PATH;
fi
if [ ! -d $TEMP_PATH ];
then
mkdir -p $TEMP_PATH;
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD=`which java`
echo "Error: JAVA_HOME is $JAVACMD"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "We cannot execute $JAVACMD"
exit $ERR_NO_JAVA
fi
if [ -e "$BASEDIR" ]
then
JAVA_OPTS="-Xms512M -Xmx2048M -Xss256K -XX:+UseAdaptiveSizePolicy -XX:+UseParallelGC -XX:+UseParallelOldGC -XX:GCTimeRatio=39 -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:$GC_PATH -XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=$HS_ERR_PATH -XX:HeapDumpPath=$HEAP_DUMP_PATH"
fi
CLASSPATH=$CLASSPATH_PREFIX:
EXTRA_JVM_ARGUMENTS=""
cd "$BASEDIR/boot";
echo "starting application $PROJECT_NAME......"
exec "$JAVACMD" $JAVA_OPTS \
$EXTRA_JVM_ARGUMENTS \
-Dapp.name="$PROJECT_NAME" \
-Dapp.port="$PORT" \
-Dbasedir="$BASEDIR" \
-Djava.io.tmpdir=$TEMP_PATH \
-Dloader.path="file://$BASEDIR/conf,file://$BASEDIR/lib" \
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5516 \
-jar $MAIN_CLASS \
> /dev/null &
for i in {1..60}
do
jcpid=`ps -ef | grep -v "grep" | grep "$MAIN_CLASS" | grep "app.port=$PORT" | sed -n '1P' | awk '{print $2}'`
if [ $jcpid ]; then
echo "The $PROJECT_NAME start finished, PID is $jcpid"
exit $SUCCESS
else
echo "starting the application .. $i"
sleep 1
fi
done
echo "$PROJECT_NAME start failure!"
package com.mortals.xhx;
import com.mortals.framework.springcloud.boot.BaseWebApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ImportResource;
@EnableFeignClients
@EnableConfigurationProperties
@SpringBootApplication(scanBasePackages = {"com.mortals"})
@ServletComponentScan("com.mortals")
@ImportResource(locations = {"classpath:config/spring-config.xml"})
public class ManagerApplication extends BaseWebApplication {
public static void main(String[] args) {
SpringApplication.run(ManagerApplication.class, args);
}
}
package com.mortals.xhx.annotation;
import java.lang.annotation.*;
/**
*
* @author: zxfei
* @date: 2024/5/15 16:09
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
public @interface DataPermission {
/**
* 数据权限类型
* 1 上下级授权 2 数据范围授权
*/
String permissionType() default "2";
/**
* 配置菜单的组件路径,用于数据权限
*/
String componentRoute() default "";
}
package com.mortals.xhx.base.framework;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.springframework.util.ObjectUtils;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author: zxfei
* @date: 2022/6/30 10:49
* @description:
**/
public class CustomJsonDateDeserializer extends JsonDeserializer<Date> {
@Override
public Date deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date = jp.getText();
if (!ObjectUtils.isEmpty(date)) {
try {
return format.parse(date);
} catch (ParseException e) {
return null;
}
} else {
return null;
}
}
}
package com.mortals.xhx.base.framework;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import java.util.ArrayList;
import java.util.List;
/**
* @author: zxfei
* @date: 2022/6/28 15:57
* @description:
**/
// 创建一个新的转换器 解析微信的 [text/plain]
public class WxMessageConverter extends MappingJackson2HttpMessageConverter {
public WxMessageConverter() {
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.TEXT_PLAIN);
setSupportedMediaTypes(mediaTypes);
}
}
\ No newline at end of file
package com.mortals.xhx.base.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ApiUserAuth {
String msg() default "";
String params() default "";
}
package com.mortals.xhx.base.framework.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Operlog {
String msg() default "";
String params() default "";
}
package com.mortals.xhx.base.framework.aspect;
import com.mortals.framework.model.OperateLogPdu;
import com.mortals.framework.service.ILogService;
import com.mortals.framework.service.IMessageProduceService;
import com.mortals.framework.service.impl.FileLogServiceImpl;
import com.mortals.xhx.base.system.oper.service.OperLogService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import java.util.Date;
/**
* 操作日志记录
*
* @author: zxfei
* @date: 2021/11/5 13:28
*/
@Component
@Slf4j
public class OperlogAspect extends FileLogServiceImpl implements ILogService {
@Autowired
private OperLogService operLogService;
@Autowired
private IMessageProduceService messageProducer;
@Override
public void doHandlerLog(String platformMark, Long userId, String userName, String loginName, String requestUrl,
String content, String ip, Date logDate) {
super.doHandlerLog(platformMark, userId, userName, loginName, requestUrl, content, ip, logDate);
if(ObjectUtils.isEmpty(userId)) return;
operLogService.insertOperLog(ip, requestUrl, userId, userName, loginName, content);
OperateLogPdu operateLogPdu = new OperateLogPdu();
operateLogPdu.initAttrValue();
operateLogPdu.setIp(ip);
operateLogPdu.setRequestUrl(requestUrl);
operateLogPdu.setUserId(userId);
operateLogPdu.setUserName(userName);
operateLogPdu.setLoginName(loginName);
operateLogPdu.setPlatformMark(platformMark);
operateLogPdu.setLogDate(logDate);
operateLogPdu.setContent(content);
operateLogPdu.setOperType(1);
messageProducer.syncOperSend(operateLogPdu);
}
@Override
public void doHandlerLog(String platformMark, String loginName, String requestUrl, String content, String ip) {
this.doHandlerLog(platformMark, null, "", loginName, requestUrl, content, ip, new Date());
}
}
package com.mortals.xhx.base.framework.aspect;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.core.annotation.Order;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 打印每个请求的入参、出参等信息
*
* @author: zxfei
* @date: 2022/4/20 9:24
*/
//@Aspect
//@Component
@Slf4j
@Order(1)
//@Profile({"default", "develop", "test"})
public class WebLogAspect {
@Pointcut("execution(public * com.mortals..*Controller.*(..))")
public void webLog() {
}
@Pointcut("execution(public * com.mortals.xhx.base.framework.exception.ExceptionHandle.*(..))")
public void exceptions() {
}
/**
* 只在进入controller时记录请求信息
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// log.debug("请求路径 {} ,进入方法 {}", request.getRequestURI(), joinPoint.getSignature().getDeclaringTypeName() + ":" + joinPoint.getSignature().getName());
MDC.put("req", getRequestInfo(request).toJSONString());
MDC.put("startTime", String.valueOf(System.currentTimeMillis()));
}
/**
* 打印请求日志
*/
@AfterReturning(pointcut = "webLog()|| exceptions()", returning = "result")
public void afterReturning(Object result) {
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
Map<String, String> map = MDC.getCopyOfContextMap();
if (map != null&&result!=null) {
String startTime = map.getOrDefault("startTime", String.valueOf(System.currentTimeMillis()));
long takeTime = (System.currentTimeMillis() - Long.parseLong(startTime));
String req = map.getOrDefault("req", "");
if (result instanceof String) {
log.info(" \n 请求路径:{} 耗时:{}ms 客户端IP:{} \n 请求报文:{} \n 响应报文:{}\n cookies:{} "
, request.getRequestURI(), takeTime,ServletUtil.getClientIP(request), req, result);
} else {
log.info(" \n 请求路径:{} 耗时:{}ms 客户端IP:{}\n 请求报文:{} \n 响应报文:{}\n cookies:{}"
, request.getRequestURI(), takeTime,ServletUtil.getClientIP(request), req, JSON.toJSONString(result));
}
}
}
/**
* 读取请求信息,如果是表单则转换为json
*/
private JSONObject getRequestInfo(HttpServletRequest req) {
JSONObject requestInfo = new JSONObject();
try {
StringBuffer requestURL = req.getRequestURL();
requestInfo.put("requestURL", requestURL);
String method = req.getMethod();
requestInfo.put("method", method);
if (req.getQueryString() != null) {
requestInfo.put("queryString", URLDecoder.decode(req.getQueryString(), "UTF-8"));
}
String remoteAddr = req.getRemoteAddr();
requestInfo.put("remoteAddr", remoteAddr);
if (req instanceof ContentCachingRequestWrapper) {
ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) req;
String bodyStr = new String(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
if (bodyStr.startsWith("{")) {
JSONObject jsonObject = JSON.parseObject(bodyStr);
requestInfo.put("requestBody", jsonObject);
}
String cookieStr = Arrays.asList(wrapper.getCookies()).stream().map(item -> JSON.toJSONString(item)).collect(Collectors.joining("|"));
requestInfo.put("cookieStr", cookieStr);
}
} catch (Exception e) {
log.error("解析请求失败", e);
requestInfo.put("parseError", e.getMessage());
}
return requestInfo;
}
}
package com.mortals.xhx.base.framework.config;
import com.mortals.framework.springcloud.config.web.BaseWebMvcConfigurer;
import com.mortals.xhx.base.framework.WxMessageConverter;
import feign.codec.Decoder;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.SpringDecoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author: zxfei
* @date: 2021/8/13 0:24
* @description:
**/
@Configuration
public class AccountConfig {
@Bean
public BaseWebMvcConfigurer getBaseWebMvc() {
return new BaseWebMvcConfigurer(false, null);
}
@Bean
public Decoder feignDecoder() {
WxMessageConverter wxConverter = new WxMessageConverter();
ObjectFactory<HttpMessageConverters> objectFactory = () -> new HttpMessageConverters(wxConverter);
return new SpringDecoder(objectFactory);
}
}
package com.mortals.xhx.base.framework.config;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author: zxfei
* @date: 2022/6/6 15:05
* @description:添加跨域响应
**/
@Component
public class CrossInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setHeader("Access-Control-Allow-Origin", "*");
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "*");
response.setHeader("Access-Control-Allow-Credentials", "true");
return true;
}
}
package com.mortals.xhx.base.framework.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.sql.SQLException;
//@Component
//@ConfigurationProperties(prefix = "spring.datasource")
public class DruidSource {
private String url;
private String username;
private String password;
private String driverClassName;
private int initialSize;
private int minIdle;
private int maxActive;
private int maxWait;
private int timeBetweenEvictionRunsMillis;
private int minEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public int getInitialSize() {
return initialSize;
}
public void setInitialSize(int initialSize) {
this.initialSize = initialSize;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getMaxWait() {
return maxWait;
}
public void setMaxWait(int maxWait) {
this.maxWait = maxWait;
}
public int getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(int timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public int getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(int minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public boolean isTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
@Bean //声明其为Bean实例
@Primary //在同样的DataSource中,首先使用被标注的DataSource
public DataSource dataSource() throws SQLException {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(url);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
//configuration
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
return datasource;
}
}
package com.mortals.xhx.base.framework.config;
import com.mortals.framework.filter.RepeatableFilter;
import com.mortals.framework.filter.XssFilter;
import com.mortals.framework.util.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import javax.servlet.DispatcherType;
import java.util.HashMap;
import java.util.Map;
/**
* Filter配置
*
* @author zxfei
*/
//@Configuration
public class FilterConfig {
@Value("${xss.enabled}")
private String enabled;
@Value("${xss.excludes}")
private String excludes;
@Value("${xss.urlPatterns}")
private String urlPatterns;
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
public FilterRegistrationBean xssFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns(StringUtils.split(urlPatterns, ","));
registration.setName("xssFilter");
registration.setOrder(FilterRegistrationBean.HIGHEST_PRECEDENCE);
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("excludes", excludes);
initParameters.put("enabled", enabled);
registration.setInitParameters(initParameters);
return registration;
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
public FilterRegistrationBean someFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new RepeatableFilter());
registration.addUrlPatterns("/*");
registration.setName("repeatableFilter");
registration.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE);
return registration;
}
}
package com.mortals.xhx.base.framework.config;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.PostConstruct;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import com.mortals.framework.util.StringUtils;
@Configuration
public class InterceptorConfig {
/**
* cookie的密钥
*/
@Value("${cookie.key}")
private String securityKey;
/**
* 不需要登录的URL地址,多个用逗号分隔
*/
@Value("${application.auth.unloginUrl}")
private String uncheckLoginUrl;
/**
* 不需要鉴权的URL地址,多个用逗号分隔
*/
@Value("${application.auth.uncheckUrl}")
private String uncheckAuthUrl;
private Set<String> uncheckLoginUrls = new HashSet<>();
private Set<String> uncheckAuthUrls = new HashSet<>();
/**
* 有后缀占位符的不检验地址,如/spi/user/*
*/
private Set<String> uncheckAuthUrlsSuffix = new HashSet<>();
public String getSecurityKey() {
return securityKey;
}
public Set<String> getUncheckLoginUrls() {
return uncheckLoginUrls;
}
public Set<String> getUncheckAuthUrls() {
return uncheckAuthUrls;
}
/**
* 校验地址是否需要权限
*
* @param url
* @return true:需要,false:不需要
*/
public boolean needCheckAuth(String url) {
if (uncheckAuthUrls.contains(url)) {
return false;
}
for (String regexUrl : uncheckAuthUrlsSuffix) {// 支持*结尾
if (url.startsWith(regexUrl)) {
return false;
}
}
return true;
}
@PostConstruct
public void convert() {
uncheckLoginUrls.clear();
uncheckLoginUrls.addAll(StringUtils.converStr2Set(uncheckLoginUrl));
uncheckAuthUrls.clear();
uncheckAuthUrls.addAll(StringUtils.converStr2Set(uncheckAuthUrl));
uncheckAuthUrlsSuffix.clear();
for (String url : uncheckAuthUrls) {
int index = url.indexOf("*");// 支持*结尾
if (index != -1) {
uncheckAuthUrlsSuffix.add(url.substring(0, index));
}
}
}
}
package com.mortals.xhx.base.framework.config;
import com.mortals.framework.springcloud.config.mybatis.AbstractMybatisConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
//@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisConfiguration extends AbstractMybatisConfiguration {
private static Log logger = LogFactory.getLog(MybatisConfiguration.class);
// 配置类型别名
@Value("${spring.application.name}")
private String name;
// 配置类型别名
@Value("${mybatis.root-path}")
private String rootPath;
// 配置类型别名
@Value("${mybatis.type-aliases-package}")
private String typeAliasesPackage;
// 配置mapper的扫描,找到所有的mapper.xml映射文件
@Value("${mybatis.mapper-locations}")
private String mapperLocations;
// 加载全局的配置文件
@Value("${mybatis.config-location}")
private String configLocation;
// 提供SqlSeesion
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("dataSource") DataSource dataSource) {
return super.getSqlSessionFactoryBean(dataSource);
}
@Override
public String getTypeAliasesRootPackage() {
return rootPath;
}
@Override
public String getSqlMappers() {
return mapperLocations;
}
@Override
public String getMybatisConfig() {
return configLocation;
}
@Override
public String getTypeAliasesPackage() {
logger.info("typeAliasesPackage:"+typeAliasesPackage);
return typeAliasesPackage;
}
}
\ No newline at end of file
package com.mortals.xhx.base.framework.config;
import com.mortals.framework.springcloud.config.mybatis.AbstractMybatisMasterSlaveDataSourceConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
import java.util.List;
//@Configuration
//@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisConfigurationMasterSlave extends AbstractMybatisMasterSlaveDataSourceConfiguration {
private static Log logger = LogFactory.getLog(MybatisConfigurationMasterSlave.class);
// 配置类型别名
@Value("${spring.application.name}")
private String name;
// 配置类型别名
@Value("${mybatis.root-path}")
private String rootPath;
// 配置类型别名
@Value("${mybatis.type-aliases-package}")
private String typeAliasesPackage;
// 配置mapper的扫描,找到所有的mapper.xml映射文件
@Value("${mybatis.mapper-locations}")
private String mapperLocations;
// 加载全局的配置文件
@Value("${mybatis.config-location}")
private String configLocation;
// 提供SqlSeesion
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(@Qualifier("dataSource") DataSource dataSource) {
return super.getSqlSessionFactoryBean(dataSource);
}
@Override
public String getTypeAliasesRootPackage() {
return rootPath;
}
@Override
public String getSqlMappers() {
return mapperLocations;
}
@Override
public String getMybatisConfig() {
return configLocation;
}
@Override
public String getTypeAliasesPackage() {
logger.info("typeAliasesPackage:"+typeAliasesPackage);
return typeAliasesPackage;
}
@Override
public DataSource getDefaultDataSource() {
return null;
}
@Override
public List<DataSource> findMasterDataSource() {
return null;
}
@Override
public List<DataSource> findSlaveDataSource() {
return null;
}
}
\ No newline at end of file
package com.mortals.xhx.base.framework.config;
import com.mortals.xhx.base.framework.listener.RabbitLoggingErrorHandler;
import com.mortals.xhx.base.framework.listener.SimpleDynamicListener;
import com.mortals.xhx.base.system.message.MessageCallbackService;
import com.mortals.xhx.base.system.message.impl.MessageProducer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.AsyncRabbitTemplate;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.DirectMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.amqp.SimpleRabbitListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@Configuration
@Order(1)
@Slf4j
public class RabbitConfig {
@Autowired
private SimpleDynamicListener simpleDynamicListener;
@Autowired
private RabbitLoggingErrorHandler rabbitLoggingErrorHandler;
@Autowired
private MessageProducer messageProducer;
@Autowired
private MessageCallbackService messageCallbackService;
//@Bean("simpleMessageListenerContainer")
public SimpleMessageListenerContainer simpleMessageListenerContainer(CachingConnectionFactory cachingConnectionFactory) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(cachingConnectionFactory);
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setConcurrentConsumers(10);
container.setMaxConcurrentConsumers(100);
container.setMessageListener(simpleDynamicListener);//配置队列监听器
container.start();
return container;
}
@Bean
public DirectMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory) {
DirectMessageListenerContainer container = new DirectMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
//后置处理器,接收到的消息都添加头信息
container.setAfterReceivePostProcessors(message -> {
message.getMessageProperties().setContentType("application/json");
return message;
});
container.setDefaultRequeueRejected(false);
//设置异常处理
//container.setErrorHandler(rabbitLoggingErrorHandler);
// 并发消费,不使用
// container.setConsumersPerQueue(3);
// container.setMaxConcurrentConsumers(10);
// container.setMessageListener(directDynamicListener);
return container;
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
/* rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
}
});*/
/* rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if(!ObjectUtils.isEmpty(correlationData)){
// 发送消息的时候发送的业务id
log.info("发送消息id:{},ack:{}",correlationData.getId(),ack);
}
}
});*/
rabbitTemplate.setReturnCallback(messageCallbackService);
/* rabbitTemplate.setReturnCallback(messageCallbackService);
rabbitTemplate.setConfirmCallback(messageCallbackService);*/
return rabbitTemplate;
}
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
return rabbitAdmin;
}
//@Bean(name = "consumerBatchContainerFactory")
public SimpleRabbitListenerContainerFactory consumerBatchContainerFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer, ConnectionFactory connectionFactory) {
// 创建 SimpleRabbitListenerContainerFactory 对象
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
// 额外添加批量消费的属性
factory.setBatchListener(true);
factory.setBatchSize(20);
factory.setReceiveTimeout(5 * 1000L);
factory.setConsumerBatchEnabled(true);
return factory;
}
//修改系列和与反序列化转换器
@Bean
public MessageConverter messageConverter() {
return new Jackson2JsonMessageConverter();
}
//@Bean
public AsyncRabbitTemplate asyncRabbitTemplate(RabbitTemplate rabbitTemplate) {
AsyncRabbitTemplate asyncRabbitTemplate = new AsyncRabbitTemplate(rabbitTemplate);
asyncRabbitTemplate.setReceiveTimeout(10000);
return asyncRabbitTemplate;
}
}
//package com.mortals.xhx.base.framework.config;
//
//import lombok.Data;
//import lombok.extern.slf4j.Slf4j;
//import org.redisson.Redisson;
//import org.redisson.api.RedissonClient;
//import org.redisson.client.codec.Codec;
//import org.redisson.codec.JsonJacksonCodec;
//import org.redisson.config.Config;
//import org.springframework.boot.context.properties.ConfigurationProperties;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//
///**
// * RedissonConfig配置
// *
// * @author: zxfei
// * @date: 2022/8/8 16:02
// */
//@Configuration
//@ConfigurationProperties(prefix = "spring.redis")
//@Slf4j
//@Data
//public class RedissonConfig {
//
// private String host;
//
// private int port;
//
// private String password;
//
// private int database;
//
// @Bean
// public RedissonClient redissonClient() {
// RedissonClient redissonClient;
// Config config = new Config();
// String url = "redis://" + host + ":" + port;
// // 单节点配置
// if("".equalsIgnoreCase(password)) password=null;
// config.useSingleServer().setAddress(url).setDatabase(database).setPassword(password);
// //使用json序列化方式
// Codec codec = new JsonJacksonCodec();
// config.setCodec(codec);
//
// // 主从配置
// /*config.useMasterSlaveServers()
// // 设置redis主节点
// .setMasterAddress("redis://192.168.1.120:6379")
// // 设置redis从节点
// .addSlaveAddress("redis://192.168.1.130:6379", "redis://192.168.1.140:6379");*/
// // 哨兵部署方式,sentinel是采用Paxos拜占庭协议,一般sentinel至少3个节点
// /*config.useSentinelServers()
// .setMasterName("my-sentinel-name")
// .addSentinelAddress("redis://192.168.1.120:6379")
// .addSentinelAddress("redis://192.168.1.130:6379")
// .addSentinelAddress("redis://192.168.1.140:6379");*/
// // 集群部署方式,cluster方式至少6个节点,3主3从,3主做sharding,3从用来保证主宕机后可以高可用
// /*config.useClusterServers()
// // 集群状态扫描间隔时间,单位是毫秒
// .setScanInterval(2000)
// .addNodeAddress("redis://192.168.1.120:6379")
// .addNodeAddress("redis://192.168.1.130:6379")
// .addNodeAddress("redis://192.168.1.140:6379")
// .addNodeAddress("redis://192.168.1.150:6379")
// .addNodeAddress("redis://192.168.1.160:6379")
// .addNodeAddress("redis://192.168.1.170:6379");*/
// // 云托管部署方式,这种方式主要解决redis提供商为云服务的提供商的redis连接,比如亚马逊云、微软云
// /*config.useReplicatedServers()
// // 主节点变化扫描间隔时间
// .setScanInterval(2000)
// .addNodeAddress("redis://192.168.1.120:6379")
// .addNodeAddress("redis://192.168.1.130:6379")
// .addNodeAddress("redis://192.168.1.140:6379");*/
// redissonClient = Redisson.create(config);
// return redissonClient;
// }
//}
\ No newline at end of file
package com.mortals.xhx.base.framework.exception;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.exception.AppException;
/**
* 统一异常处理
*/
@ControllerAdvice
public class ExceptionHandle {
private final static Logger log = LoggerFactory.getLogger(ExceptionHandle.class);
public static final String KEY_RESULT_CODE = "code";
public static final String KEY_RESULT_MSG = "msg";
public static final String KEY_RESULT_DATA = "data";
public static final int VALUE_RESULT_FAILURE = -1;
@ExceptionHandler(value = Exception.class)
@ResponseBody
public String handle(Exception e) {
JSONObject ret = new JSONObject();
ret.put(KEY_RESULT_CODE, VALUE_RESULT_FAILURE);
if (e instanceof AppException) {
StackTraceElement stack = e.getStackTrace()[0];
log.error("[business error]=========stack message[{}],[{},method:{},line{}][{}]", e.getMessage(),
stack.getClassName(), stack.getMethodName(), stack.getLineNumber(), e.getClass().getName());
AppException ex = (AppException) e;
ret.put(KEY_RESULT_MSG, ex.getMessage());
} else {
log.error("[system error]", e);
ret.put(KEY_RESULT_MSG, "unknown exception!");
}
return ret.toJSONString();
}
}
package com.mortals.xhx.base.framework.filter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* 请求过滤链
* @author: zxfei
* @date: 2022/4/20 14:52
*/
//@Component
@Slf4j
public class RequestFilter extends OncePerRequestFilter implements Filter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
try {
request = new ContentCachingRequestWrapper(request);
filterChain.doFilter(request, response);
} catch (Exception e) {
throw e;
} finally {
//清理ThreadLocal
MDC.clear();
}
}
/*private void setUsername(HttpServletRequest request) {
//通过token解析出username
String token = authTokenService.getToken(request);
//String token = request.getHeader("token");
if (!ObjectUtils.isEmpty(token)) {
MDC.put("token",token);
MDC.put("token", token);
try {
SessionUserInfo info = tokenService.getUserInfo();
if (info != null) {
String username = info.getUsername();
MDC.put("username", username);
}
} catch (CommonJsonException e) {
log.info("无效的token:{}", token);
}
}
}*/
}
package com.mortals.xhx.base.framework.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.ap.SysConstains;
import com.mortals.framework.service.IAuthTokenService;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.service.IUser;
import com.mortals.framework.util.DateUtils;
import com.mortals.framework.util.StringUtils;
import com.mortals.xhx.base.system.resource.service.ResourceService;
import com.mortals.xhx.base.system.user.model.UserEntity;
import com.mortals.xhx.base.system.user.service.UserService;
import com.mortals.xhx.common.code.PlatformTypeEnum;
import com.mortals.xhx.common.key.RedisKey;
import com.mortals.xhx.common.utils.MenuEncodeUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* token验证处理
*
* @author zxfei
*/
@Service
@Order(1)
@Slf4j
public class AuthTokenServiceImpl implements IAuthTokenService {
// 令牌自定义标识
@Value("${token.header:Authorization}")
private String header;
// 令牌秘钥
@Value("${token.secret:026db82420614469897fcc2dc1b4ce38}")
private String secret;
// 令牌有效期(默认60分钟)
@Value("${token.expireTime:60}")
private int expireTime;
// 令牌前缀
@Value("${token.prefix:}")
private String tokenPrefix;
// redis db
@Value("${spring.redis.database:}")
private Integer db;
@Value("${token.database:0}")
private Integer portalDb;
@Value("${platform.type:cloud}")
private String platFormType;//版本,默认云服务版本
@Autowired
private ResourceService resourceService;
@Autowired
private UserService userService;
protected static final Long SECOND = 1l;
protected static final Long SECOND_MINUTE = 60 * SECOND;
protected static final Long SECOND_HOUR = 60 * SECOND_MINUTE;
protected static final Long SECOND_DAY = 24 * SECOND_HOUR;
protected static final Long SECOND_WEEK = 7 * SECOND_DAY;
private static final Long SECOND_MINUTE_TEN = 20 * SECOND_MINUTE;
@Autowired
private ICacheService cacheService;
/**
* 获取信息
*
* @return 用户信息
*/
@Override
public IUser getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌
String token = getToken(request);
if (StringUtils.isNotEmpty(token)) {
try {
boolean signed = Jwts.parser().isSigned(token);
if (!signed) {
log.error("token非法!=>{}", token);
return null;
}
Claims claims = parseToken(token);
String uuid = (String) claims.get(SysConstains.LOGIN_USER_KEY);
String userKey = getTokenKey(uuid);
String userStr = "";
if (platFormType.equalsIgnoreCase(PlatformTypeEnum.CLOUD.getValue())) {
/* cacheService.select(portalDb);
userStr = cacheService.get(userKey);*/
RedisTemplate<String, String> redisTemplate = cacheService.selectDbRedisTemplate(portalDb);
userStr =redisTemplate.opsForValue().get(userKey);
//刷新token时间
UserEntity userEntity = JSONObject.parseObject(userStr, UserEntity.class);
if (!ObjectUtils.isEmpty(userEntity)) {
verifyToken(userEntity);
}
// cacheService.select(db);
} else {
userStr = cacheService.get(userKey);
//刷新token时间
UserEntity userEntity = JSONObject.parseObject(userStr, UserEntity.class);
if (!ObjectUtils.isEmpty(userEntity)) {
verifyToken(userEntity);
}
}
if (StringUtils.isNotEmpty(userStr)) {
UserEntity userEntity = JSONObject.parseObject(userStr, UserEntity.class);
userEntity.setToken(token);
//更新portal 中的id 为 device中的id
// UserEntity temp = userService.selectOne(new UserQuery().loginName(userEntity.getLoginName()));
UserEntity temp = userService.getExtCache(userEntity.getLoginName());
if (!ObjectUtils.isEmpty(temp)) {
userEntity.setId(temp.getId());
}
//更新resource 路径
String menuUrlCode = cacheService.hget(RedisKey.KEY_USER_MENU_CACHE, userEntity.getId().toString(), String.class);
if (ObjectUtils.isEmpty(menuUrlCode)) {
Set<String> urls = resourceService.findUrlSetByUserId(userEntity.getId());
menuUrlCode = MenuEncodeUtil.generateMenuUrlCode(urls);
cacheService.hset(RedisKey.KEY_USER_MENU_CACHE, userEntity.getId().toString(), menuUrlCode);
}
userEntity.setMenuUrl(menuUrlCode);
return userEntity;
}
} catch (Exception e) {
log.error("解析jwt token异常!", e);
return null;
}
}
return null;
}
/**
* 设置用户信息
*/
@Override
public void setUser(IUser user) {
if (StringUtils.isNotNull(user) && StringUtils.isNotEmpty(user.getToken())) {
refreshToken(user);
}
}
/**
* 删除用户身份信息
*/
@Override
public void delUser(String token) {
if (StringUtils.isNotEmpty(token)) {
String userKey = getTokenKey(token);
cacheService.del(userKey);
}
}
/**
* 创建令牌
*
* @param user 用户信息
* @return 令牌
*/
@Override
public String createToken(IUser user) {
// String token = IdUtil.fastSimpleUUID();
// user.setToken(token);
refreshToken(user);
Map<String, Object> claims = new HashMap<>();
claims.put(SysConstains.LOGIN_USER_KEY, user.getToken());
return createToken(claims);
}
/**
* 验证令牌有效期,相差不足20分钟,自动刷新缓存
*
* @param user
* @return 令牌
*/
@Override
public void verifyToken(IUser user) {
long expireTime = user.getExpireTime();
long currentTime = System.currentTimeMillis();
if (expireTime - currentTime <= SECOND_MINUTE_TEN*1000) {
log.info("不足十分钟,刷新过期时间");
refreshToken(user);
}
}
/**
* 刷新令牌有效期
*
* @param user 信息
*/
public void refreshToken(IUser user) {
//user.setLoginTime(System.currentTimeMillis());
user.setExpireTime(user.getLoginTime() == null ? System.currentTimeMillis() : user.getLoginTime() + expireTime * SECOND_MINUTE);
// 根据uuid将user缓存
String userKey = getTokenKey(user.getToken());
//设置有效时间 单位秒
cacheService.set(userKey, user, expireTime * SECOND_MINUTE);
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String createToken(Map<String, Object> claims) {
String token = Jwts.builder()
.setExpiration(DateUtils.addCurrDate(7))
.setClaims(claims)
.signWith(SignatureAlgorithm.HS256, Base64.getEncoder()
.encodeToString(secret.getBytes())).compact();
return token;
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
@Override
public Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(Base64.getEncoder().encodeToString(secret.getBytes()))
.parseClaimsJws(token)
.getBody();
}
/**
* 从令牌中获取用户
*
* @param token 令牌
* @return 用户名
*/
@Override
public String getUserNumFromToken(String token) {
Claims claims = parseToken(token);
return claims.getSubject();
}
/**
* 获取请求token
*
* @param request
* @return token
*/
@Override
public String getToken(HttpServletRequest request) {
String token = request.getHeader(header);
if (StringUtils.isNotEmpty(token) && token.startsWith(tokenPrefix)) {
token = token.replace(tokenPrefix, "");
}
return token;
}
private String getTokenKey(String uuid) {
return SysConstains.LOGIN_TOKEN_KEY + uuid;
}
}
package com.mortals.xhx.base.framework.interceptor;
import com.alibaba.fastjson.JSONObject;
import com.mortals.framework.annotation.UnAuth;
import com.mortals.framework.common.Rest;
import com.mortals.framework.service.IAuthTokenService;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.service.IUser;
import com.mortals.framework.util.AESUtil;
import com.mortals.framework.utils.ServletUtils;
import com.mortals.framework.web.interceptor.BaseInterceptor;
import com.mortals.xhx.base.framework.config.InterceptorConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import static com.mortals.xhx.common.key.ErrorCode.*;
/**
* 用户权限验证,基于token
*
* @author: zxfei
* @date: 2022/4/24 11:04
*/
@Component
public class AuthUserInterceptor extends BaseInterceptor {
@Autowired
private InterceptorConfig config;
@Autowired
private IAuthTokenService authTokenService;
@Autowired
private ICacheService cacheService;
@Override
public int getOrder() {
return Integer.MAX_VALUE - 9;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
JSONObject ret = new JSONObject();
if(handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
UnAuth annotation = method.getAnnotation(UnAuth.class);
if (annotation != null) {
//取消校验
return true;
}
}else if(handler instanceof ResourceHttpRequestHandler){
return true;
}
try {
String uri = request.getServletPath();
//校验配置的请求路径是否需要检查权限
if (config.needCheckAuth(uri)) {
//需要校验权限
boolean auth = this.checkAuth(request, uri, config.getSecurityKey());
if (!auth) {
//校验token不正常
String token = authTokenService.getToken(request);
if(ObjectUtils.isEmpty(token)){
ServletUtils.renderString(response, JSONObject.toJSONString(Rest.fail(ERROR_TOKEN_UNAUTHORIZED, ERROR_TOKEN_UNAUTHORIZED_CONTENT)));
return false;
}
//不存在时候 如果是管理员也不做拦截
IUser loginUser = authTokenService.getLoginUser(request);
if (ObjectUtils.isEmpty(loginUser)) {
ServletUtils.renderString(response, JSONObject.toJSONString(Rest.fail(ERROR_TOKEN_EXPIRED, ERROR_TOKEN_EXPIRED_CONTENT)));
return false;
// } else if (loginUser.isAdmin() || loginUser.getUserType() == 1) {
} else if (loginUser.isAdmin()) {
return super.preHandle(request, response, handler);
} else {
ServletUtils.renderString(response, JSONObject.toJSONString(Rest.fail(ERROR_USER_OPERATION, ERROR_USER_OPERATION_CONTENT)));
return false;
}
}
}
} catch (Exception e) {
logger.error("权限校验拦截请求处理异常-->" + e.getMessage());
writeJsonResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "用户权限校验异常");
return false;
}
return super.preHandle(request, response, handler);
}
private boolean checkAuth(HttpServletRequest request, String requestUrl, String securityKey) throws Exception {
int code = requestUrl.hashCode() & (Integer.MAX_VALUE - 1);
IUser loginUser = authTokenService.getLoginUser(request);
if (ObjectUtils.isEmpty(loginUser)) return false;
String menuUrl = loginUser.getMenuUrl();
if (ObjectUtils.isEmpty(menuUrl)) return false;
menuUrl = AESUtil.decrypt(menuUrl, securityKey);
String codes = "," + menuUrl + ",";
String codeKey = "," + code + ",";
if (codes.indexOf(codeKey) != -1) {
return true;
}
return false;
}
}
package com.mortals.xhx.base.framework.listener;
import com.alibaba.fastjson.JSON;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.service.IMessageProduceService;
import com.mortals.xhx.common.pdu.DefaultQueueMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 设备上行消息处理
*
* @author: zxfei
* @date: 2022/8/23 0:32
* @description:
**/
@Slf4j
@Service
public class DirectDynamicListener implements MessageListener {
@Autowired
private ICacheService cacheService;
@Autowired
private IMessageProduceService messageProducer;
@Override
public void onMessage(Message message) {
String queue = message.getMessageProperties().getConsumerQueue();
byte[] body = message.getBody();
String data = new String(body);
// log.info("接收到:{} ,\n消息内容为:{}", queue, data);
DefaultQueueMsg queueMsg = JSON.parseObject(data, DefaultQueueMsg.class);
}
}
package com.mortals.xhx.base.framework.listener;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ErrorHandler;
@Component
@Slf4j
public class RabbitLoggingErrorHandler implements ErrorHandler {
public RabbitLoggingErrorHandler(SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory) {
rabbitListenerContainerFactory.setErrorHandler(this);
}
@Override
public void handleError(Throwable t) {
log.error("[handleError][发生异常]]", t);
}
}
package com.mortals.xhx.base.framework.listener;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Service;
/**
* @author: zxfei
* @date: 2022/8/23 0:32
* @description:
**/
@Slf4j
@Service
public class SimpleDynamicListener implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) {
try {
if (message.equals("exception")) {
log.info("rabbitmq ecception!");
}
String queue = message.getMessageProperties().getConsumerQueue();
byte[] body = message.getBody();
log.info("接收到:" + queue + ",消息内容为:" + new String(body));
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info(queue + "队列消息消费成功");
} catch (Exception e) {
log.error("接收消息异常");
}
}
}
package com.mortals.xhx.base.framework.ws;
import com.mortals.xhx.base.framework.ws.websocket.WebSocketHandler;
import com.mortals.xhx.base.framework.ws.websocket.WebSocketShakeInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket // 开启 Spring WebSocket
public class WebSocketConfiguration implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(this.webSocketHandler(), "/ws") // 配置处理器
.addInterceptors(new WebSocketShakeInterceptor()) // 配置拦截器
.setAllowedOrigins("*"); // 解决跨域问题
}
@Bean
public WebSocketHandler webSocketHandler() {
return new WebSocketHandler();
}
@Bean
public WebSocketShakeInterceptor webSocketShakeInterceptor() {
return new WebSocketShakeInterceptor();
}
}
package com.mortals.xhx.base.framework.ws.handler;
import com.alibaba.fastjson.JSON;
import com.mortals.xhx.base.framework.ws.message.AuthRequest;
import com.mortals.xhx.base.framework.ws.message.AuthResponse;
import com.mortals.xhx.base.framework.ws.util.WebSocketUtil;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketSession;
@Component
@CommonsLog
public class AuthMessageHandler implements MessageHandler<AuthRequest> {
@Override
public void execute(WebSocketSession session, AuthRequest message) {
log.info("receive message:"+ JSON.toJSONString(message));
// 如果未传递 accessToken
if (StringUtils.isEmpty(message.getAccessToken())) {
WebSocketUtil.send(session, AuthResponse.TYPE,
new AuthResponse().setCode(1).setMessage("认证 accessToken 未传入"));
return;
}
// 添加到 WebSocketUtil 中
WebSocketUtil.addSession(session, message.getAccessToken()); // 考虑到代码简化,我们先直接使用 accessToken 作为 User
// 判断是否认证成功。这里,假装直接成功
WebSocketUtil.send(session, AuthResponse.TYPE, new AuthResponse().setCode(0));
// 通知所有人,某个人加入了。这个是可选逻辑,仅仅是为了演示
// WebSocketUtil.broadcast(UserJoinNoticeRequest.TYPE,
// new UserJoinNoticeRequest().setNickname(message.getAccessToken())); // 考虑到代码简化,我们先直接使用 accessToken 作为 User
//
}
@Override
public String getType() {
return AuthRequest.TYPE;
}
}
package com.mortals.xhx.base.framework.ws.handler;
import com.mortals.xhx.base.framework.ws.message.HeartBeatRequest;
import com.mortals.xhx.base.framework.ws.message.SendResponse;
import com.mortals.xhx.base.framework.ws.util.WebSocketUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
@Component
public class HeartBeatHandler implements MessageHandler<HeartBeatRequest> {
@Override
public void execute(WebSocketSession session, HeartBeatRequest message) {
// 这里,假装直接成功
SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0);
WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);
}
@Override
public String getType() {
return HeartBeatRequest.TYPE;
}
}
package com.mortals.xhx.base.framework.ws.handler;
import com.mortals.xhx.base.framework.ws.message.Message;
import org.springframework.web.socket.WebSocketSession;
/**
* 消息处理器接口
*/
public interface MessageHandler<T extends Message> {
/**
* 执行处理消息
*
* @param session 会话
* @param message 消息
*/
void execute(WebSocketSession session, T message);
/**
* @return 消息类型,即每个 Message 实现类上的 TYPE 静态字段
*/
String getType();
}
package com.mortals.xhx.base.framework.ws.handler;
import com.mortals.xhx.base.framework.ws.message.SendResponse;
import com.mortals.xhx.base.framework.ws.message.SendToAllRequest;
import com.mortals.xhx.base.framework.ws.message.SendToUserRequest;
import com.mortals.xhx.base.framework.ws.util.WebSocketUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
@Component
public class SendToAllHandler implements MessageHandler<SendToAllRequest> {
@Override
public void execute(WebSocketSession session, SendToAllRequest message) {
// 这里,假装直接成功
SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0);
WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);
// 创建转发的消息
SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId())
.setContent(message.getContent());
// 广播发送
WebSocketUtil.broadcast(SendToUserRequest.TYPE, sendToUserRequest);
}
@Override
public String getType() {
return SendToAllRequest.TYPE;
}
}
package com.mortals.xhx.base.framework.ws.handler;
import com.mortals.xhx.base.framework.ws.message.SendResponse;
import com.mortals.xhx.base.framework.ws.message.SendToOneRequest;
import com.mortals.xhx.base.framework.ws.message.SendToUserRequest;
import com.mortals.xhx.base.framework.ws.util.WebSocketUtil;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;
@Component
public class SendToOneHandler implements MessageHandler<SendToOneRequest> {
@Override
public void execute(WebSocketSession session, SendToOneRequest message) {
// 这里,假装直接成功
SendResponse sendResponse = new SendResponse().setMsgId(message.getMsgId()).setCode(0);
WebSocketUtil.send(session, SendResponse.TYPE, sendResponse);
// 创建转发的消息
SendToUserRequest sendToUserRequest = new SendToUserRequest().setMsgId(message.getMsgId())
.setContent(message.getContent());
// 广播发送
WebSocketUtil.send(message.getToUser(), SendToUserRequest.TYPE, sendToUserRequest);
}
@Override
public String getType() {
return SendToOneRequest.TYPE;
}
}
package com.mortals.xhx.base.framework.ws.message;
/**
* 用户认证请求
*/
public class AuthRequest implements Message {
public static final String TYPE = "AUTH_REQUEST";
/**
* 认证 Token
*/
private String accessToken;
public String getAccessToken() {
return accessToken;
}
public AuthRequest setAccessToken(String accessToken) {
this.accessToken = accessToken;
return this;
}
}
package com.mortals.xhx.base.framework.ws.message;
/**
* 用户认证响应
*/
public class AuthResponse implements Message {
public static final String TYPE = "AUTH_RESPONSE";
/**
* 响应状态码
*/
private Integer code;
/**
* 响应提示
*/
private String message;
public Integer getCode() {
return code;
}
public AuthResponse setCode(Integer code) {
this.code = code;
return this;
}
public String getMessage() {
return message;
}
public AuthResponse setMessage(String message) {
this.message = message;
return this;
}
}
package com.mortals.xhx.base.framework.ws.message;
import com.alibaba.fastjson.JSON;
/**
* 心跳 Message
*/
public class HeartBeatRequest implements Message {
public static final String TYPE = "HEART_BEAT_REQUEST";
/**
* 消息编号
*/
private String msgId;
/**
* 内容
*/
private String content;
public String getMsgId() {
return msgId;
}
public HeartBeatRequest setMsgId(String msgId) {
this.msgId = msgId;
return this;
}
public String getContent() {
return content;
}
public HeartBeatRequest setContent(String content) {
this.content = content;
return this;
}
public static void main(String[] args) {
HeartBeatRequest heartBeatRequest = new HeartBeatRequest();
heartBeatRequest.setMsgId("123");
System.out.println(JSON.toJSONString(heartBeatRequest));
}
}
package com.mortals.xhx.base.framework.ws.message;
/**
* 基础消息体
*/
public interface Message {
}
package com.mortals.xhx.base.framework.ws.message;
/**
* 发送消息响应结果的 Message
*/
public class SendResponse implements Message {
public static final String TYPE = "SEND_RESPONSE";
/**
* 消息编号
*/
private String msgId;
/**
* 响应状态码
*/
private Integer code;
/**
* 响应提示
*/
private String message;
public String getMsgId() {
return msgId;
}
public SendResponse setMsgId(String msgId) {
this.msgId = msgId;
return this;
}
public Integer getCode() {
return code;
}
public SendResponse setCode(Integer code) {
this.code = code;
return this;
}
public String getMessage() {
return message;
}
public SendResponse setMessage(String message) {
this.message = message;
return this;
}
}
package com.mortals.xhx.base.framework.ws.message;
/**
* 发送给所有人的群聊消息的 Message
*/
public class SendToAllRequest implements Message {
public static final String TYPE = "SEND_TO_ALL_REQUEST";
/**
* 消息编号
*/
private String msgId;
/**
* 内容
*/
private String content;
public String getContent() {
return content;
}
public SendToAllRequest setContent(String content) {
this.content = content;
return this;
}
public String getMsgId() {
return msgId;
}
public SendToAllRequest setMsgId(String msgId) {
this.msgId = msgId;
return this;
}
}
package com.mortals.xhx.base.framework.ws.message;
/**
* 发送给指定人的私聊消息的 Message
*/
public class SendToOneRequest implements Message {
public static final String TYPE = "SEND_TO_ONE_REQUEST";
/**
* 发送给的用户
*/
private String toUser;
/**
* 消息编号
*/
private String msgId;
/**
* 内容
*/
private String content;
public String getToUser() {
return toUser;
}
public SendToOneRequest setToUser(String toUser) {
this.toUser = toUser;
return this;
}
public String getMsgId() {
return msgId;
}
public SendToOneRequest setMsgId(String msgId) {
this.msgId = msgId;
return this;
}
public String getContent() {
return content;
}
public SendToOneRequest setContent(String content) {
this.content = content;
return this;
}
}
package com.mortals.xhx.base.framework.ws.message;
/**
* 发送消息给一个用户的 Message
*/
public class SendToUserRequest implements Message {
public static final String TYPE = "SEND_TO_USER_REQUEST";
/**
* 消息编号
*/
private String msgId;
/**
* 内容
*/
private String content;
public String getMsgId() {
return msgId;
}
public SendToUserRequest setMsgId(String msgId) {
this.msgId = msgId;
return this;
}
public String getContent() {
return content;
}
public SendToUserRequest setContent(String content) {
this.content = content;
return this;
}
}
package com.mortals.xhx.base.framework.ws.message;
/**
* 用户加入群聊的通知 Message
*/
public class UserJoinNoticeRequest implements Message {
public static final String TYPE = "USER_JOIN_NOTICE_REQUEST";
/**
* 昵称
*/
private String nickname;
public String getNickname() {
return nickname;
}
public UserJoinNoticeRequest setNickname(String nickname) {
this.nickname = nickname;
return this;
}
}
package com.mortals.xhx.base.framework.ws.util;
import com.alibaba.fastjson.JSONObject;
import com.mortals.xhx.base.framework.ws.message.Message;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* WebSocket 工具类,提供客户端连接的管理等功能
*/
public class WebSocketUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUtil.class);
// ========== 会话相关 ==========
/**
* Session 与用户的映射
*/
private static final Map<WebSocketSession, String> SESSION_USER_MAP = new ConcurrentHashMap<>();
/**
* 用户与 Session 的映射
*/
private static final Map<String, WebSocketSession> USER_SESSION_MAP = new ConcurrentHashMap<>();
/**
* 添加 Session 。在这个方法中,会添加用户和 Session 之间的映射
*
* @param session Session
* @param user 用户
*/
public static void addSession(WebSocketSession session, String user) {
// 更新 USER_SESSION_MAP
USER_SESSION_MAP.put(user, session);
// 更新 SESSION_USER_MAP
SESSION_USER_MAP.put(session, user);
}
/**
* 移除 Session 。
*
* @param session Session
*/
public static void removeSession(WebSocketSession session) {
// 从 SESSION_USER_MAP 中移除
String user = SESSION_USER_MAP.remove(session);
// 从 USER_SESSION_MAP 中移除
if (user != null && user.length() > 0) {
USER_SESSION_MAP.remove(user);
}
}
// ========== 消息相关 ==========
/**
* 广播发送消息给所有在线用户
*
* @param type 消息类型
* @param message 消息体
* @param <T> 消息类型
*/
public static <T extends Message> void broadcast(String type, T message) {
// 创建消息
TextMessage textMessage = buildTextMessage(type, message);
// 遍历 SESSION_USER_MAP ,进行逐个发送
for (WebSocketSession session : SESSION_USER_MAP.keySet()) {
sendTextMessage(session, textMessage);
}
}
/**
* 发送消息给单个用户的 Session
*
* @param session Session
* @param type 消息类型
* @param message 消息体
* @param <T> 消息类型
*/
public static <T extends Message> void send(WebSocketSession session, String type, T message) {
// 创建消息
TextMessage textMessage = buildTextMessage(type, message);
// 遍历给单个 Session ,进行逐个发送
sendTextMessage(session, textMessage);
}
/**
* 发送消息给指定用户
*
* @param user 指定用户
* @param type 消息类型
* @param message 消息体
* @param <T> 消息类型
* @return 发送是否成功你那个
*/
public static <T extends Message> boolean send(String user, String type, T message) {
// 获得用户对应的 Session
WebSocketSession session = USER_SESSION_MAP.get(user);
if (session == null) {
LOGGER.error("[send][user({}) 不存在对应的 session]", user);
return false;
}
// 发送消息
send(session, type, message);
return true;
}
/**
* 构建完整的消息
*
* @param type 消息类型
* @param message 消息体
* @param <T> 消息类型
* @return 消息
*/
private static <T extends Message> TextMessage buildTextMessage(String type, T message) {
JSONObject messageObject = new JSONObject();
messageObject.put("type", type);
messageObject.put("body", message);
return new TextMessage(messageObject.toString());
}
/**
* 真正发送消息
*
* @param session Session
* @param textMessage 消息
*/
private static void sendTextMessage(WebSocketSession session, TextMessage textMessage) {
if (session == null) {
LOGGER.error("[sendTextMessage][session 为 null]");
return;
}
try {
session.sendMessage(textMessage);
} catch (IOException e) {
LOGGER.error("[sendTextMessage][session({}) 发送消息{}) 发生异常",
session, textMessage, e);
}
}
}
package com.mortals.xhx.base.framework.ws.websocket;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mortals.xhx.base.framework.ws.handler.MessageHandler;
import com.mortals.xhx.base.framework.ws.message.AuthRequest;
import com.mortals.xhx.base.framework.ws.message.Message;
import com.mortals.xhx.base.framework.ws.util.WebSocketUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public class WebSocketHandler extends TextWebSocketHandler implements InitializingBean {
private Logger logger = LoggerFactory.getLogger(getClass());
/**
* 消息类型与 MessageHandler 的映射
*
* 无需设置成静态变量
*/
private final Map<String, MessageHandler> HANDLERS = new HashMap<>();
@Autowired
private ApplicationContext applicationContext;
@Override // 对应 open 事件
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
logger.info("[afterConnectionEstablished][session({}) 接入]", session);
// 解析 accessToken
String accessToken = (String) session.getAttributes().get("accessToken");
// 创建 AuthRequest 消息类型
AuthRequest authRequest = new AuthRequest().setAccessToken(accessToken);
// 获得消息处理器
MessageHandler<AuthRequest> messageHandler = HANDLERS.get(AuthRequest.TYPE);
if (messageHandler == null) {
logger.error("[onOpen][认证消息类型,不存在消息处理器]");
return;
}
messageHandler.execute(session, authRequest);
}
@Override // 对应 message 事件
public void handleTextMessage(WebSocketSession session, TextMessage textMessage) throws Exception {
//logger.info("[handleMessage][session({}) 接收到一条消息({})]", session, textMessage); // 生产环境下,请设置成 debug 级别
try {
// 获得消息类型
JSONObject jsonMessage = JSON.parseObject(textMessage.getPayload());
String messageType = jsonMessage.getString("type");
// 获得消息处理器
MessageHandler messageHandler = HANDLERS.get(messageType);
if (messageHandler == null) {
logger.error("[onMessage][消息类型({}) 不存在消息处理器]", messageType);
return;
}
// 解析消息
Class<? extends Message> messageClass = this.getMessageClass(messageHandler);
// 处理消息
Message messageObj = JSON.parseObject(jsonMessage.getString("body"), messageClass);
messageHandler.execute(session, messageObj);
} catch (Throwable throwable) {
logger.info("[onMessage][session({}) message({}) 发生异常]", session, throwable);
}
}
@Override // 对应 close 事件
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
logger.info("[afterConnectionClosed][session({}) 连接关闭。关闭原因是({})}]", session, status);
WebSocketUtil.removeSession(session);
}
@Override // 对应 error 事件
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
logger.info("[handleTransportError][session({}) 发生异常]", session, exception);
}
@Override
public void afterPropertiesSet() throws Exception {
// 通过 ApplicationContext 获得所有 MessageHandler Bean
applicationContext.getBeansOfType(MessageHandler.class).values() // 获得所有 MessageHandler Bean
.forEach(messageHandler -> HANDLERS.put(messageHandler.getType(), messageHandler)); // 添加到 handlers 中
logger.info("[afterPropertiesSet][消息处理器数量:{}]", HANDLERS.size());
}
private Class<? extends Message> getMessageClass(MessageHandler handler) {
// 获得 Bean 对应的 Class 类名。因为有可能被 AOP 代理过。
Class<?> targetClass = AopProxyUtils.ultimateTargetClass(handler);
// 获得接口的 Type 数组
Type[] interfaces = targetClass.getGenericInterfaces();
Class<?> superclass = targetClass.getSuperclass();
while ((Objects.isNull(interfaces) || 0 == interfaces.length) && Objects.nonNull(superclass)) { // 此处,是以父类的接口为准
interfaces = superclass.getGenericInterfaces();
superclass = targetClass.getSuperclass();
}
if (Objects.nonNull(interfaces)) {
// 遍历 interfaces 数组
for (Type type : interfaces) {
// 要求 type 是泛型参数
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// 要求是 MessageHandler 接口
if (Objects.equals(parameterizedType.getRawType(), MessageHandler.class)) {
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
// 取首个元素
if (Objects.nonNull(actualTypeArguments) && actualTypeArguments.length > 0) {
return (Class<Message>) actualTypeArguments[0];
} else {
throw new IllegalStateException(String.format("类型(%s) 获得不到消息类型", handler));
}
}
}
}
}
throw new IllegalStateException(String.format("类型(%s) 获得不到消息类型", handler));
}
public static void main(String[] args) {
WebSocketHandler webSocketHandler = new WebSocketHandler();
}
}
package com.mortals.xhx.base.framework.ws.websocket;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import java.util.Map;
/**
* 自定义 HttpSessionHandshakeInterceptor 拦截器
*
* 因为 WebSocketSession 无法获得 ws 地址上的请求参数,所以只好通过该拦截器,获得 accessToken 请求参数,设置到 attributes 中
*/
@CommonsLog
public class WebSocketShakeInterceptor extends HttpSessionHandshakeInterceptor {
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, org.springframework.web.socket.WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
// 获得 accessToken
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request;
attributes.put("accessToken", serverRequest.getServletRequest().getParameter("accessToken"));
}
return super.beforeHandshake(request, response, wsHandler, attributes);
}
public static void main(String[] args) {
WebSocketShakeInterceptor webSocketShakeInterceptor = new WebSocketShakeInterceptor();
}
}
package com.mortals.xhx.base.login.interceptor;
import com.mortals.framework.annotation.UnAuth;
import com.mortals.xhx.base.framework.config.InterceptorConfig;
import com.mortals.framework.ap.CookieService;
import com.mortals.framework.ap.SysConstains;
import com.mortals.framework.model.CookieInfo;
import com.mortals.framework.service.ICacheService;
import com.mortals.framework.util.AESUtil;
import com.mortals.framework.util.HttpUtil;
import com.mortals.framework.util.StringUtils;
import com.mortals.framework.web.interceptor.BaseInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
@Order(1)
//@Component
public class AuthJsonInterceptor extends BaseInterceptor {
@Autowired
private InterceptorConfig config;
@Autowired
private ICacheService cacheService;
@Override
public int getOrder() {
return Integer.MAX_VALUE - 9;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
UnAuth annotation = method.getAnnotation(UnAuth.class);
if (annotation != null) {
//取消校验
return true;
}
String uri = request.getServletPath();
if (config.needCheckAuth(uri)) {
boolean auth = this.checkAuth(request, uri, config.getSecurityKey());
if (!auth) {
CookieInfo cookie = CookieService.getLoginCookie(request, config.getSecurityKey());
if (cookie == null || cookie.getUser() == null) {
writeJsonResponse(response, HttpServletResponse.SC_FORBIDDEN, "用户未登录或登录失效,请重新登录");
return false;
} else if (cookie.getUser().isAdmin()) {
return super.preHandle(request, response, handler);
} else {
writeJsonResponse(response, HttpServletResponse.SC_UNAUTHORIZED, "用户无该操作权限");
return false;
}
}
}
} catch (Exception e) {
logger.error("权限校验拦截请求处理异常-->" + e.getMessage());
writeJsonResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "用户权限校验异常");
return false;
}
return super.preHandle(request, response, handler);
}
private boolean checkAuth(HttpServletRequest request, String requestUrl, String securityKey) throws Exception {
int code = requestUrl.hashCode() & (Integer.MAX_VALUE - 1);
String cacheKey = HttpUtil.getCookieValue(request, SysConstains.COOKIE_MENU);
if (StringUtils.isEmpty(cacheKey)) {
return false;
}
String menuUrl = cacheService.get(cacheKey);
if (StringUtils.isEmpty(menuUrl)) {
return false;
}
menuUrl = AESUtil.decrypt(menuUrl, securityKey);
String codes = "," + menuUrl + ",";
String codeKey = "," + code + ",";
if (codes.indexOf(codeKey) != -1) {
return true;
}
return false;
}
}
package com.mortals.xhx.base.login.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mortals.xhx.base.framework.config.InterceptorConfig;
import org.springframework.beans.factory.annotation.Autowired;
import com.mortals.framework.ap.CookieService;
import com.mortals.framework.model.CookieInfo;
import com.mortals.framework.service.ITokenService;
import com.mortals.framework.web.interceptor.BaseInterceptor;
//@Component
public class LoginJsonInterceptor extends BaseInterceptor {
@Autowired(required = false)
private ITokenService tokenService;
@Autowired
private InterceptorConfig config;
@Override
public int getOrder() {
return Integer.MAX_VALUE - 10;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
try {
String uri = request.getServletPath();
if (config.needCheckAuth(uri)) {
CookieInfo cookie = CookieService.getLoginCookie(request, config.getSecurityKey());
boolean expire = true;
try {
if (cookie == null || cookie.getUser() == null
|| (cookie != null && tokenService != null && tokenService.isExpireToken(cookie))) {
expire = true;
} else {
expire = false;
}
} catch (Exception e) {
logger.warn("校验cookie是否过期异常-->原因:" + e.getMessage());
}
// 判断用户是否登录
if (expire) {
CookieService.deleteLoginCookie(request, response);
writeJsonResponse(response, HttpServletResponse.SC_FORBIDDEN, "用户未登录或登录失效,请重新登录111333");
return false;
}
}
} catch (Exception e) {
logger.error("登录校验拦截请求处理异常-->" + e.getMessage());
writeJsonResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "用户登录校验异常");
return false;
}
return super.preHandle(request, response, handler);
}
}
package com.mortals.xhx.base.login.service;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Random;
import javax.imageio.ImageIO;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* gif动态图验证码图片生成器
*
*/
public class GifSecurityImage {
private static Log logger = LogFactory.getLog(GifSecurityImage.class);
public static BufferedImage createImage(String securityCode) {
int codeLength = securityCode.length();
int fSize = 14;
int fWidth = fSize + 1;
int width = codeLength * fWidth + 6;
int height = fSize * 2 + 1;
BufferedImage image = new BufferedImage(width, height, 1);
Graphics g = image.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, width, height);
g.setColor(Color.LIGHT_GRAY);
g.setFont(new Font("Arial", 1, height - 2));
Random rand = new Random();
// 画点
g.setColor(Color.LIGHT_GRAY);
for (int i = 0; i < codeLength * 10; i++) {
g.setColor(randomColor());
int x = rand.nextInt(width);
int y = rand.nextInt(height);
g.drawOval(x, y, 1, 1);
}
for (int i = 0; i < 5; i++) {
g.setColor(randomColor());
int x = rand.nextInt(width - 1);
int y = rand.nextInt(height - 1);
int xl = rand.nextInt(6) + 1;
int yl = rand.nextInt(12) + 1;
g.drawLine(x, y, xl, yl);
}
// 写字
g.setFont(new Font("Georgia", 1, fSize));
for (int i = 0; i < codeLength; i++) {
int codeY = height - 8 - rand.nextInt(5);
int codeX = width / 4 * i + (width / 4 - fSize) / 2;
g.drawString(String.valueOf(securityCode.charAt(i)), codeX, codeY);
}
g.dispose();
return image;
}
public static byte[] createGifImage(String securityCode) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
AnimatedGifEncoder gifEncoder = new AnimatedGifEncoder();
gifEncoder.setRepeat(0);
gifEncoder.start(outputStream);
for (int j = 0; j < 4; j++) {
gifEncoder.setDelay(200); // 设置播放的延迟时间
gifEncoder.addFrame(createImage(securityCode));
}
gifEncoder.finish();
return outputStream.toByteArray();
}
private static Color randomColor() {
Random random = new Random();
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
return new Color(r, g, b);
}
public static ByteArrayInputStream getImageAsInputStream(String securityCode) {
BufferedImage image = createImage(securityCode);
return convertImageToStream(image);
}
private static ByteArrayInputStream convertImageToStream(BufferedImage image) {
ByteArrayInputStream inputStream = null;
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
byte[] bts = bos.toByteArray();
inputStream = new ByteArrayInputStream(bts);
ImageIO.write(image, "JPG", bos);
} catch (Exception e) {
logger.debug("转换图片数据流异常-->" + e.getMessage());
}
return inputStream;
}
}
package com.mortals.xhx.base.login.service;
import java.io.IOException;
import java.io.OutputStream;
//==============================================================================
// Adapted from Jef Poskanzer's Java port by way of J. M. G. Elliott.
// K Weiner 12/00
class LZWEncoder {
private static final int EOF = -1;
private int imgW, imgH;
private byte[] pixAry;
private int initCodeSize;
private int remaining;
private int curPixel;
// GIFCOMPR.C - GIF Image compression routines
//
// Lempel-Ziv compression based on 'compress'. GIF modifications by
// David Rowley (mgardi@watdcsu.waterloo.edu)
// General DEFINEs
static final int BITS = 12;
static final int HSIZE = 5003; // 80% occupancy
// GIF Image compression - modified 'compress'
//
// Based on: compress.c - File compression ala IEEE Computer, June 1984.
//
// By Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas)
// Jim McKie (decvax!mcvax!jim)
// Steve Davies (decvax!vax135!petsd!peora!srd)
// Ken Turkowski (decvax!decwrl!turtlevax!ken)
// James A. Woods (decvax!ihnp4!ames!jaw)
// Joe Orost (decvax!vax135!petsd!joe)
int n_bits; // number of bits/code
int maxbits = BITS; // user settable max # bits/code
int maxcode; // maximum code, given n_bits
int maxmaxcode = 1 << BITS; // should NEVER generate this code
int[] htab = new int[HSIZE];
int[] codetab = new int[HSIZE];
int hsize = HSIZE; // for dynamic table sizing
int free_ent = 0; // first unused entry
// block compression parameters -- after all codes are used up,
// and compression rate changes, start over.
boolean clear_flg = false;
// Algorithm: use open addressing double hashing (no chaining) on the
// prefix code / next character combination. We do a variant of Knuth's
// algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime
// secondary probe. Here, the modular division first probe is gives way
// to a faster exclusive-or manipulation. Also do block compression with
// an adaptive reset, whereby the code table is cleared when the compression
// ratio decreases, but after the table fills. The variable-length output
// codes are re-sized at this point, and a special CLEAR code is generated
// for the decompressor. Late addition: construct the table according to
// file size for noticeable speed improvement on small files. Please direct
// questions about this implementation to ames!jaw.
int g_init_bits;
int ClearCode;
int EOFCode;
// output
//
// Output the given code.
// Inputs:
// code: A n_bits-bit integer. If == -1, then EOF. This assumes
// that n_bits =< wordsize - 1.
// Outputs:
// Outputs code to the file.
// Assumptions:
// Chars are 8 bits long.
// Algorithm:
// Maintain a BITS character long buffer (so that 8 codes will
// fit in it exactly). Use the VAX insv instruction to insert each
// code in turn. When the buffer fills up empty it and start over.
int cur_accum = 0;
int cur_bits = 0;
int masks[] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF,
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF };
// Number of characters so far in this 'packet'
int a_count;
// Define the storage for the packet accumulator
byte[] accum = new byte[256];
// ----------------------------------------------------------------------------
LZWEncoder(int width, int height, byte[] pixels, int color_depth) {
imgW = width;
imgH = height;
pixAry = pixels;
initCodeSize = Math.max(2, color_depth);
}
// Add a character to the end of the current packet, and if it is 254
// characters, flush the packet to disk.
void char_out(byte c, OutputStream outs) throws IOException {
accum[a_count++] = c;
if (a_count >= 254)
flush_char(outs);
}
// Clear out the hash table
// table clear for block compress
void cl_block(OutputStream outs) throws IOException {
cl_hash(hsize);
free_ent = ClearCode + 2;
clear_flg = true;
output(ClearCode, outs);
}
// reset code table
void cl_hash(int hsize) {
for (int i = 0; i < hsize; ++i)
htab[i] = -1;
}
void compress(int init_bits, OutputStream outs) throws IOException {
int fcode;
int i /* = 0 */;
int c;
int ent;
int disp;
int hsize_reg;
int hshift;
// Set up the globals: g_init_bits - initial number of bits
g_init_bits = init_bits;
// Set up the necessary values
clear_flg = false;
n_bits = g_init_bits;
maxcode = MAXCODE(n_bits);
ClearCode = 1 << (init_bits - 1);
EOFCode = ClearCode + 1;
free_ent = ClearCode + 2;
a_count = 0; // clear packet
ent = nextPixel();
hshift = 0;
for (fcode = hsize; fcode < 65536; fcode *= 2)
++hshift;
hshift = 8 - hshift; // set hash code range bound
hsize_reg = hsize;
cl_hash(hsize_reg); // clear hash table
output(ClearCode, outs);
outer_loop: while ((c = nextPixel()) != EOF) {
fcode = (c << maxbits) + ent;
i = (c << hshift) ^ ent; // xor hashing
if (htab[i] == fcode) {
ent = codetab[i];
continue;
} else if (htab[i] >= 0) // non-empty slot
{
disp = hsize_reg - i; // secondary hash (after G. Knott)
if (i == 0)
disp = 1;
do {
if ((i -= disp) < 0)
i += hsize_reg;
if (htab[i] == fcode) {
ent = codetab[i];
continue outer_loop;
}
} while (htab[i] >= 0);
}
output(ent, outs);
ent = c;
if (free_ent < maxmaxcode) {
codetab[i] = free_ent++; // code -> hashtable
htab[i] = fcode;
} else
cl_block(outs);
}
// Put out the final code.
output(ent, outs);
output(EOFCode, outs);
}
// ----------------------------------------------------------------------------
void encode(OutputStream os) throws IOException {
os.write(initCodeSize); // write "initial code size" byte
remaining = imgW * imgH; // reset navigation variables
curPixel = 0;
compress(initCodeSize + 1, os); // compress and write the pixel data
os.write(0); // write block terminator
}
// Flush the packet to disk, and reset the accumulator
void flush_char(OutputStream outs) throws IOException {
if (a_count > 0) {
outs.write(a_count);
outs.write(accum, 0, a_count);
a_count = 0;
}
}
final int MAXCODE(int n_bits) {
return (1 << n_bits) - 1;
}
// ----------------------------------------------------------------------------
// Return the next pixel from the image
// ----------------------------------------------------------------------------
private int nextPixel() {
if (remaining == 0)
return EOF;
--remaining;
byte pix = pixAry[curPixel++];
return pix & 0xff;
}
void output(int code, OutputStream outs) throws IOException {
cur_accum &= masks[cur_bits];
if (cur_bits > 0)
cur_accum |= (code << cur_bits);
else
cur_accum = code;
cur_bits += n_bits;
while (cur_bits >= 8) {
char_out((byte) (cur_accum & 0xff), outs);
cur_accum >>= 8;
cur_bits -= 8;
}
// If the next entry is going to be too big for the code size,
// then increase it, if possible.
if (free_ent > maxcode || clear_flg) {
if (clear_flg) {
maxcode = MAXCODE(n_bits = g_init_bits);
clear_flg = false;
} else {
++n_bits;
if (n_bits == maxbits)
maxcode = maxmaxcode;
else
maxcode = MAXCODE(n_bits);
}
}
if (code == EOFCode) {
// At EOF, write the rest of the buffer.
while (cur_bits > 0) {
char_out((byte) (cur_accum & 0xff), outs);
cur_accum >>= 8;
cur_bits -= 8;
}
flush_char(outs);
}
}
}
\ No newline at end of file
package com.mortals.xhx.base.system.message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
public interface MessageCallbackService extends RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment