首页
留言
友链
关于
Search
1
思源笔记docker私有化部署及使用体验分享
1,193 阅读
2
windows11 远程提示:为安全考虑,已锁定该用户帐户,原因是登录尝试或密码更改尝试过多。
619 阅读
3
Pointer-Focus:一款功能强大的教学、录屏辅助软件
389 阅读
4
在vue-cli3使用sass(scss)定义的全局样式及变量
323 阅读
5
使用cspell对项目做拼写规范检查
264 阅读
Web前端
CSS
JavaScript
交互
Vue
小程序
后端
运维
生活
其他
转载
软件
登录
Search
标签搜索
docker
DevOps
magic-boot
Linux
酷壳
RabbitMQ
Node
git
工具
Vue
MybatisPlus
clickhouse
规范
前端
产品
markdown
axios
H5
经纬度
vue-cli
朱治龙
累计撰写
118
篇文章
累计收到
0
条评论
首页
栏目
Web前端
CSS
JavaScript
交互
Vue
小程序
后端
运维
生活
其他
转载
软件
页面
留言
友链
关于
搜索到
19
篇与
后端
的结果
2024-05-12
RabbitMQ学习:③基本使用
初始配置添加用户使用默认的 guest 账号登录后,可以在 Admin → Users中添加用户:添加 Virtual Host在Admin → Virtual Hosts 中添加虚拟机:给用户授权点击 Virtual Host 的名称,进入详情界面,可在Permissions中给新建的用户设置权限:建立连接1、新建Maven 项目2、导入依赖<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>net.x2m</groupId> <artifactId>rabbitmq</artifactId> <version>1.0-SNAPSHOT</version> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.21.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> </project>3、创建工具类连接 RabbitMQpublic static Connection getConnection() { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); factory.setPort(5672); factory.setUsername("zhuzl"); factory.setPassword("123456"); factory.setVirtualHost("/test"); Connection conn = null; try { conn = factory.newConnection(); } catch (IOException e) { throw new RuntimeException(e); } catch (TimeoutException e) { throw new RuntimeException(e); } return conn; }4、代码层面获取连接后,在管理界面可以看到如下连接信息:5、点击Name 可查看连接详情如下:6、调试界面的连接信息:
2024年05月12日
1 阅读
0 评论
0 点赞
2024-05-12
RabbitMQ学习:②基本概念
本文核心内容参加官网链接:https://www.rabbitmq.com/tutorials/amqp-conceptsPublisher:生产者(发布消息到R啊波比跳MQ中的Exchange)Consumer:消费者(监听RabbitMQ中的Queue中的消息)Exchange:交换机(和生产者建立连接并接收生产者的消息)Queue:队列(Exchange会将消息分发到指定的Queue,Queue和消费者进行交互)Routes:路由(交换机以什么样的策略将消息发布到 Queue)RabbitMQ 的完整架构图:
2024年05月12日
1 阅读
0 评论
0 点赞
2024-05-12
RabbitMQ学习:①安装
背景近期参与公司的在线充值业务的功能开发,该业务涉及多个系统交互,采用MQ的方式实现跨系统通讯:而在我既往的项目经验中还未使用过 MQ,便利用工作之余对相关的知识点进行补充学习。本系列内容即是我的一个0基础入门学习记录,仅做参考。RabbitMQ 基本介绍RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源实现。Erlang是为电话交换机编写的语言,天然对分布式和高并发支持良好。常用MQ对比比较项RabbitMQActiveMQRocketMQKafka公司/社区Broadcom Inc.Apache阿里Apache开发语言ErlangJavaJavaScala&Java协议支持AMQP,XMPP,SMTP,STOMPoPENwIRE,stomp,REST,XMPP,AMQP自定义自定义协议,社区封装了http协议支持客户端支持语言官方支持Erlang,Java,Ruby,PHP,.NET,GO,JS等,社区产出多种API,几乎支持所有语言Java,C,C++,Python,PHP,Perl,.NET等Java,C++官方支持Java,社区产出多种API,如PHP,Python等单机吞吐量万级(其次)万级(最差)十万级(最好)十万级(次之)消息延迟微秒级毫秒级毫秒级毫秒以内功能特性并发能力强,性能极其好,延时低,社区活跃,管理界面丰富老牌产品,成熟度高,文档较多MQ功能必要完备,扩展性佳只支持主要的MQ功能,主要为大数据领域场景安装 RabbitMQ为快速安装部署,使用 docker compose 方式运行,docker-compose.yaml文件内容如下:services: rabbitmq: image: rabbitmq:management restart: always container_name: rabbitmq ports: - 5672:5672 - 15672:15672 volumes: - ./data:/var/lib/rabbitmq使用 docker compose up -d 启动后,使用浏览器访问如下地址:http://localhost:15672/,显示如下界面则表示 RabbitMQ 运行成功:在上面的登录界面使用 guest 作为用户名和密码登录,打开如下图所示的主界面:
2024年05月12日
3 阅读
0 评论
0 点赞
2024-04-07
基于 Magic-api + Clickhouse 实现业务数据更新的项目记录
背景介绍项目有用到 Clickhouse 作为数仓,存储一些用户日常业务产生的大数据,下面先简单介绍一下我们这个任务的需求背景:我们的每个用户都会归属于某个用户组,并基于用户所在的计费组织实现产品使用过程中的消费等情况。而按照系统的设定用户初始注册时是没有归属用户组的,计费组织的主账号可以在控制台将用户绑定到该计费组下,也可以解绑,解绑后也可以绑定到其他用户组。为了更好的这个变更情况,我们在 Clickhouse 添加了一张名为 user_type 的表,每次该数据变更都会新增一条记录,该表的结构如下:CREATE TABLE user_type ( `user_id` Nullable(String), `present_type` Nullable(String), `pay_type` Nullable(String), `group_type` Nullable(String), `start_date` Nullable(Date), `end_date` Nullable(Date), `uni_key` Nullable(String) ) ENGINE = Log;实现方案本项目初期由使用 dbt + Clickhouse 的方式来实现,但是经实践运行一段时间后发现 dbt 做数据同步很方便,但是要添加一些业务逻辑就显得很棘手。为了解决 dbt 的问题,我们使用已搭建的 magic-api 来实现这个数据的更新,由于相关数据仅需一天一更新即可,所以我们可以直接利用 magic-api 自带的定时任务机制来实现更新。技术细节为便于相关业务逻辑在接口和定时任务中复用,我们将核心代码写在函数模块中:相关步骤核心代码如下步骤:1、从计费系统获取最新的userType信息var statSQL = `select e.*, CONCAT(e.user_id,'-',e.present_type,'-',e.pay_type,'-',e.group_type,'-',date_format(e.start_date,'%Y-%m-%d')) as uni_key FROM( SELECT a.user_id, CASE WHEN EXISTS ( SELECT 1 FROM ( SELECT t1.user_id user_id from b_contract t1 LEFT JOIN b_contract_item t2 ON t1.id = t2.contract_id WHERE t2.is_present = 0 and t2.received_payments > 0 GROUP BY t1.user_id UNION SELECT u2.user_id user_id from b_user as u1, b_user as u2 where u1.group_id=u2.group_id AND u1.user_id != u2.user_id AND EXISTS( SELECT 1 FROM (SELECT t1.user_id from b_contract t1 LEFT JOIN b_contract_item t2 ON t1.id = t2.contract_id WHERE t2.is_present = 0 and t2.received_payments > 0 GROUP BY t1.user_id) c WHERE c.user_id = u1.user_id ) ) d WHERE a.user_id = d.user_id ) then 'pay' else 'no pay' END as present_type, CASE WHEN EXISTS( SELECT 1 FROM( SELECT t1.user_id FROM b_user t1 , b_group t2 WHERE t1.user_id=t2.pay_user_id AND t2.pay_user_id IS NOT NULL )b WHERE a.user_id = b.user_id ) THEN 'master' ELSE 'slave' END as pay_type, CASE WHEN EXISTS(SELECT 1 FROM(SELECT t1.user_id FROM b_user t1 WHERE t1.group_id IS NOT NULL)b WHERE a.user_id = b.user_id) THEN 'group' ELSE 'no group' END as group_type, CURRENT_DATE as start_date, DATE(null) as end_date FROM b_user a )e` return db['NB'].select(statSQL)2、将上一步获取到的信息存储到Clickhouse 的一张临时表import log; import cn.hutool.core.date.DateUtil; import '@/statForProduction/userTypeStat/getLatestUserTypeData' as getLatestUserType; // ------------------- 一、创建临时表 ------------------- const TEMP_TABLE_NAME = 'user_type_temp' var checkExistRes = db['CH'].select(`SELECT 1 FROM system.tables WHERE database = 'dw' AND name = '${TEMP_TABLE_NAME}'`) log.info(checkExistRes.size() + '') // 不存在表的话就基于 user_type 表创建一张临时表 if (checkExistRes.size() === 0) { var initTemporaryTableSQL = `CREATE TABLE ${TEMP_TABLE_NAME} as user_type` db['CH'].update(initTemporaryTableSQL) } else { // 临时表存在则先清空临时表的数据,便于下一步将输入存入临时表 var truncateTemporaryTableSQL = `truncate table ${TEMP_TABLE_NAME}` db['CH'].update(truncateTemporaryTableSQL) } // ------------------- 二、获取最新的用户类型数据 ------------------- log.info(`============ 开始从计费系统获取最新的用户类型数据,该操作耗时较长,请耐心等待 ============`) var timer = DateUtil.timer() const userTypeList = getLatestUserType() log.info(`getLatestUserType cost time: ${timer.intervalPretty()}.`) // ------------------- 三、将数据存入临时表 ------------------- const BATCH_INSERT_COUNT = 1000 // 分批次入临时表,一次插入记录条数 var timer = DateUtil.timer() const allDataCount = userTypeList.size() if (allDataCount > 0) { log.info(`开始导入数据到临时表,待导入的总记录数为:${allDataCount},预计分${Math.ceil(allDataCount/BATCH_INSERT_COUNT)::int}批导入。`) const willInsertArr = [] var insertSQL = `insert into ${TEMP_TABLE_NAME}(user_id,present_type,pay_type,group_type,start_date,end_date,uni_key)` // 分批次插入临时表 for (index,userTypeItem in userTypeList) { willInsertArr.push(`('${userTypeItem.userId}','${userTypeItem.presentType}','${userTypeItem.payType}','${userTypeItem.groupType}','${userTypeItem.startDate}', null,'${userTypeItem.uniKey}')`) if (willInsertArr.size() === BATCH_INSERT_COUNT) { db['CH'].update(`${insertSQL} values${willInsertArr.join(',')}`) // 清空数据 willInsertArr.clear() log.info('Batch insert:' + index) } } // 不满整批次数据单独处理 if (willInsertArr.size() > 0) { db['CH'].update(`${insertSQL} values${willInsertArr.join(',')}`) // 清空数据 willInsertArr.clear() } } log.info(`insert latest user type to Temporary Table cost time: ${timer.intervalPretty()}.`) return true3、将临时表数据跟前一次最新的用户数据对比后,将有变更和新增的数据写入user_type表import log; import cn.hutool.core.date.DateUtil; const LATEST_TABLE_NAME = 'user_type_latest' // 用户最新类型数据表 const TEMP_TABLE_NAME = 'user_type_temp' // 该表存储从计费表获取到用户当前的用户类型数据,已在上一步获取数据完毕 // 一、从user_type表获取所有用户最新的用户类型数据并插入到用于计算的临时表 // 1.1 新建临时表,用于存储每个用户user_type 表中最新的用户类型数据 var checkExistRes = db['CH'].select(`SELECT 1 FROM system.tables WHERE database = 'dw' AND name = '${LATEST_TABLE_NAME}'`) log.info(checkExistRes.size() + '') // 不存在表的话就基于 user_type 表创建一张临时表 if (checkExistRes.size() === 0) { var initTemporaryTableSQL = `CREATE TABLE ${LATEST_TABLE_NAME} as user_type` db['CH'].update(initTemporaryTableSQL) } else { // 临时表存在则先清空临时表的数据,便于下一步将输入存入临时表 var truncateTemporaryTableSQL = `truncate table ${LATEST_TABLE_NAME}` db['CH'].update(truncateTemporaryTableSQL) } // 1.2 将最新数据写入临时表 // 该方式在数据量较大的情况下极有可能导致内存溢出,拟采取其他方案:在user_type 数据初始化的时候,将最新的用户类型数据存储到user_type_latest表,对比更新完成后将临时表的数据更新到user_type_latest便于下次对比 // const insertLatestDataSQL = `insert into ${LATEST_TABLE_NAME} SELECT user_type.user_id uid,user_type.present_type ,user_type.pay_type ,user_type.group_type,user_type.start_date,user_type.end_date,user_type.uni_key // FROM user_type, (SELECT user_type.user_id uid2,max(user_type.start_date) AS latestDate FROM user_type GROUP BY user_type.user_id) AS temp // WHERE user_type.start_date = temp.latestDate and uid = temp.uid2` // db['CH'].update(insertLatestDataSQL) // 二、两个临时表的数据做对比,并将最新数据更新到 user_type var timer = DateUtil.timer() // 2.1 更新有变更的数据 const changedInsertSQL = `insert into user_type select tuts.* from ${LATEST_TABLE_NAME} tutl left join ${TEMP_TABLE_NAME} tuts on tutl.user_id =tuts.user_id where tutl.present_type != tuts.present_type or tutl.pay_type != tuts.pay_type or tutl.group_type != tuts.group_type` timer.start("insertChangeData") db['CH'].update(changedInsertSQL) // 2.2 新增用户数据直接插入 timer.start("insertNewData") const insertNewUserSQL = `insert into user_type select * from ${TEMP_TABLE_NAME} tuts where tuts.user_id not in (select tutl.user_id from ${LATEST_TABLE_NAME} tutl) ` db['CH'].update(insertNewUserSQL) // 三、如果有数据更新,则将临时表的数据替换latest表 // 3.1 清理已有的数据 const truncateLatestTableSQL = `truncate table ${LATEST_TABLE_NAME}` db['CH'].update(truncateLatestTableSQL) // 3.2 从临时表导入最新的数据 const initialLatestTableDataSQL = `insert into ${LATEST_TABLE_NAME} select * from ${TEMP_TABLE_NAME}` db['CH'].update(initialLatestTableDataSQL) log.info(`insertChangeData cost time: ${timer.intervalPretty('insertChangeData')}`) log.info(`insertNewUser cost time: ${timer.intervalPretty('insertNewData')}`) // 四、清理临时表 const dropTempTableSQL = `drop table ${TEMP_TABLE_NAME}` db['CH'].update(dropTempTableSQL) return true 定义好相关函数后,我们可以直接在接口中用起来了,为此我定义了两个接口,一个接口用于数据初始化,一个接口用于手动更新数据:接口定义01数据初始化import log; import '@/statForProduction/userTypeStat/maintenance/clearUserTypeData' as clearUserTypeData import '@/statForProduction/userTypeStat/saveToTemporaryTable' as saveToTemporaryTable const LATEST_TABLE_NAME = 'user_type_latest' // 用户最新类型数据表 const TEMP_TABLE_NAME = 'user_type_temp' // 该表存储从计费表获取到用户当前的用户类型数据 // 一、清空所有user_type表的数据 clearUserTypeData() // 二、一次性写入所有 saveToTemporaryTable() // 三、将临时表的所有数据一次性写入user_type 表作为初始数据 const initialUserTypeDataSQL = `insert into user_type select * from ${TEMP_TABLE_NAME}` db['CH'].update(initialUserTypeDataSQL) // 四、将数据写入最新用户类型表,便于下一次做数据比对 // 4.1 基于 user_type 表 创建 user_type_latest 表 var checkExistRes = db['CH'].select(`SELECT 1 FROM system.tables WHERE database = 'dw' AND name = '${LATEST_TABLE_NAME}'`) log.info(checkExistRes.size() + '') // 不存在表的话就基于 user_type 表创建一张 if (checkExistRes.size() === 0) { var createLatestTableSQL = `CREATE TABLE ${LATEST_TABLE_NAME} as user_type` db['CH'].update(createLatestTableSQL) } else { // 表存在则先清空表的数据,便于下一步将最新的用户类型数据存入该表 var truncateLatestTableSQL = `truncate table ${LATEST_TABLE_NAME}` db['CH'].update(truncateLatestTableSQL) } // 4.2 插入该表的初始数据 const initialLatestTableDataSQL = `insert into ${LATEST_TABLE_NAME} select * from ${TEMP_TABLE_NAME}` db['CH'].update(initialLatestTableDataSQL) // 五、清理临时表 const dropTempTableSQL = `drop table ${TEMP_TABLE_NAME}` db['CH'].update(dropTempTableSQL) 02手工同步用户类型数据/** * 本接口用于手工临时同步数据用,日常使用定时任务自动同步操作即可 */ import '@/statForProduction/userTypeStat/saveToTemporaryTable' as saveToTemporaryTable import '@/statForProduction/userTypeStat/updateUserTypeData' as updateUserTypeData saveToTemporaryTable() updateUserTypeData()添加定时任务本任务用到的部分 Clickhouse SQL-- 判断数据表是否存在 SELECT 1 FROM system.tables WHERE database = 'dw' AND name = 'temp_user_type_session' -- 根据user_type 表创建一张名为 temp_user_type_session 的临时表 CREATE TABLE temp_user_type_session as user_type; -- 清空某数据表中的所有内容 truncate table temp_user_type_session; -- 查询所有用户最新的用户类型数据 SELECT user_type.user_id uid,user_type.present_type ,user_type.pay_type ,user_type.group_type,user_type.start_date,user_type.end_date,user_type.uni_key FROM user_type, (SELECT user_type.user_id uid2,max(user_type.start_date) AS latestDate FROM user_type GROUP BY user_type.user_id) AS temp WHERE user_type.start_date = temp.latestDate and uid = temp.uid2; -- 获取有差异的数据 select tutl.*,tuts.user_id user_id2, tuts.present_type present_type2, tuts.pay_type pay_type2, tuts.group_type group_type2, tuts.start_date start_date2,tuts.uni_key uni_key2 from temp_user_type_latest tutl left join temp_user_type_session tuts on tutl.user_id =tuts.user_id where tutl.present_type != tuts.present_type or tutl.pay_type != tuts.pay_type or tutl.group_type != tuts.group_type;
2024年04月07日
5 阅读
0 评论
0 点赞
2024-02-28
酷壳(coolshell.cn)镜像站建设经验分享之五——镜像站制作
为了能保障镜像站能永久提供服务,不依赖于后端的数据库及中间件,我们需要将爬取到的正文内容做静态化发布,静态化发布后,我们可以将内容托管到 Gitee 或 Github。
2024年02月28日
24 阅读
0 评论
0 点赞
2024-02-28
酷壳(coolshell.cn)镜像站建设经验分享之四——解析文章正文html中的图片并离线下载图片到本地
上一步我们已经得到了文章详情所需的数据,要想保障酷壳站停掉后,我们仍然能完整的查看内容,我们需要将正文中的图片也都下载到本地,然后后续发布成镜像站的时候就不用外链图片资源了,这样也在最大程度保障原站无法访问的情况下,能通过镜像站完整的浏览博客。
2024年02月28日
6 阅读
0 评论
0 点赞
2024-02-28
酷壳(coolshell.cn)镜像站建设经验分享之三——分析爬取的文章html得到正文及相关的元数据
这个过程主要是分析文章 html,得到文章如下元数据:正文内容、作者、发布时间、分类列表、Tag词列表、浏览次数、评论次数,并将相关元数据存储到 crawler_article 表的对应字段里。
2024年02月28日
5 阅读
0 评论
0 点赞
2024-02-28
酷壳(coolshell.cn)镜像站建设经验分享之二——整站内容爬取
在前面的数据准备章节咱们已经在 magic-boot 基础工程上整合了 jsoup,而 jsoup 正是 java 领域数据爬取及 html 解析的神器,对于 html 的处理如同 JS 中的 jQuery 一般神一样的存在,而且一些 API 跟 jQuery 也比较接近,使用起来不得不说那叫一个丝滑。废话少说,咱们开始 show me the code。
2024年02月28日
3 阅读
0 评论
0 点赞
2024-02-25
酷壳(coolshell.cn)镜像站建设经验分享之一——准备工作
看酷壳网的一些评论,也有不少人像我一样,在担心以后域名或服务器到期后网站不能访问,为了解决这个后顾之忧,我便在工作之余开启了酷壳网站镜像之路,这一系列的文章也就是将这一过程做一个记录,也便于帮助有需要的朋友能够做到举一反三。
2024年02月25日
21 阅读
0 评论
0 点赞
2024-02-21
magic-boot 整合 Clickhouse 及在 magic-api 中的基本使用
项目有用到 Clickhouse 作为数仓, magic-boot 作为万金油般的存在,肯定是需要整合 Clickhouse 获取数据的,下面我们就开始吧。一、整合 clickhouse-jdbc 驱动根据clickhouse 官方文档的指引,在项目的 Maven 依赖管理文件(pom.xml)中的 dependencies 节点添加如下依赖项: <dependency> <groupId>com.clickhouse</groupId> <artifactId>clickhouse-jdbc</artifactId> <classifier>all</classifier> <version>0.6.0</version> </dependency>注:dependency 中一定要添加 <classifier>all</classifier>,否则会出现找不到依赖的className的异常二、magic-api 中添加数据源在 magic-api 主界面右侧的 DataSource 面板中,单击「+」按钮,打开「创建数据源」弹出层,如下图所示:相关表单项填写如下:名称:任意,只要自己能区别数据源即可Key:为便于在代码中引用,尽量采用简写URL:jdbc:(ch|clickhouse)[:<protocol>]://endpoint1,endpoint2,...?param1=value1¶m2=value2用户名:用户名密码:密码驱动类:com.clickhouse.jdbc.ClickHouseDriver类型:com.zaxxer.hikari.HikariDataSource。用Hikari 和 Druid 连接池测试都没碰到问题。本次测试填写后的连接池示例如下图所示:在 magic-api 中写测试代码进行功能验证创建数据表db['CH'].update(""" CREATE TABLE test_for_magic_boot ( `id` UUID, `user_name` String, `real_name` String, `birthday` Date, `gender` String ) ENGINE = MergeTree ORDER BY birthday SETTINGS index_granularity = 8192; """);添加测试数据// 添加数据要使用 update方法,使用insert 方法会报错。 // https://gitee.com/ssssssss-team/magic-api/issues/I4SQYW db['CH'].update(`insert into test_for_magic_boot(id,user_name,real_name,birthday,gender) values(#{uuid()},'shiyu', '时羽','1991-12-15', 'F'),(#{uuid()},'lint', '李宁涛','1985-11-19', 'M'),(#{uuid()},'gaowz', '高文中','1968-01-23', 'M')`)修改测试数据db['CH'].update(`update test_no_index set real_name='时大款' where user_name='shiyu'`) Clickhouse 更新操作有一些限制索引列不能进行更新分布式表不能进行更新不适合频繁更新或point更新查询数据return db['CH'].select('select * from test_for_magic_boot')删除数据db['CH'].update(`delete from test_for_magic_boot where user_name='lint'`)删除测试数据表db['CH'].update('drop table test_for_magic_boot');
2024年02月21日
32 阅读
0 评论
0 点赞
2023-12-13
Jeepay开源版使用过程中踩过的坑
1、商户系统登录问题添加商户的时候有设置登录名,但是没有设置账号密码的位置,好不容易找到对商户重置密码的地方,但是那个勾选重置密码的复选框又超级容易被理解为用户下次登录需重置密码的配置项。勾选后有提示重置为默认密码,但是又没有说明默认密码是什么,然后非得要查看源码才知道是通过常量设置的默认密码为:jeepay6662、证书文件不存在问题好不容易登录商户系统了,进行支付测试的功能验证,提示证书文件不存在:整个应用部署过程完全是基于官方提供的 docker-compose.yml 文件,最后发现默认配置的 /home/jeepay/upload 目录根本就没有挂载到宿主机,修改 docker-compose.yml ,payment、manager、merchant 应用的 volumes 应用均挂载 /home/jeepay 目录,如: volumes: - ./jeepayData:/home/jeepay3、支付测试不显示二维码的问题支付测试时不显示支付二维码,发现HTTP请求中有个 404 请求:检查代码,确定应用存在对应的接口路径:查看docker 日志复现如下 error 信息:基于该信息可得知,nginx在接收到二维码图片请求时根本就没有请求到 jeepay-payment 这个后端服务,而是直接请求了root 目录中的文件,由此我们调整一下 代理的api接口的优先级,修改 jeepay-ui 根目录下的 default.conf.template 文件,在/api/ 前添加 ^~ ,nginx的路径匹配规则如下:/:通用匹配,任何请求都可以匹配=:用于不含正则表达式的uri前,要求请求字符串与uri严格匹配,如果匹配成功,就停止继续向下搜索并立即处理该请求。~:用于表示uri包含正则表达式,并且区分大小写。~*:用于表示uri包含正则表达式,并且不区分大小写。^~:用于不包含正则表达式的uri前,要求nginx服务器找到标识uri和请求字符串匹配度最高的location后,立即使用此location处理请求,而不再使用location块中的正则uri与请求字符串做匹配。!~和!~*:分别表示区分大小写不匹配和不区分大小写不匹配的正则优先级:= --> ^~ --> /* #当有多个包含/进行正则匹配时,选择正则表达式最长的location配置执行。多个location配置的情况下匹配顺序为: 首先匹配 =,其次匹配^~, 其次是按文件中顺序的正则匹配,最后是交给 /通用匹配。当有匹配成功时候,停止匹配,按当前匹配规则处理请求。注意:如果uri包含正则表达式,则必须要有 ~ 或者 ~ 标识。修改后的 default.conf.template 文件如下所示:server { listen 80; listen [::]:80; server_name localhost; root /workspace/; try_files $uri $uri/ /index.html; location ^~ /api/ { proxy_next_upstream http_502 http_504 error timeout invalid_header; proxy_pass http://$BACKEND_HOST; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } # favicon.ico location = /favicon.ico { log_not_found off; access_log off; } # robots.txt location = /robots.txt { log_not_found off; access_log off; } # assets, media location ~* \.(?:css(\.map)?|js(\.map)?|jpe?g|png|gif|ico|cur|heic|webp|tiff?|mp3|m4a|aac|ogg|midi?|wav|mp4|mov|webm|mpe?g|avi|ogv|flv|wmv)$ { expires 7d; access_log off; } # svg, fonts location ~* \.(?:svgz?|ttf|ttc|otf|eot|woff2?)$ { add_header Access-Control-Allow-Origin "*"; expires 7d; access_log off; } # gzip gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml; } 4、公众号/小程序支付的URL多了一级/cashier应用部署完毕,进行支付测试时,采用「微信支付二维码」的方式已支付成功,但是采用「公众号/小程序」的支付方式,在扫码后,发现扫码后的页面显示空白,进一步排查,发现是由于页面的css和js资源文件404导致的,进一步排查,是由于请求的资源多了一级path,一下是问题排查过程:系统配置中的支付网关地址填写的是 https://jeepay-cashier.work.zhuzhilong.com:但是在使用支付测试功能,支付方式采用「公众号/小程序」进行支付测试时,生成的二维码如下:二维码识别后的地址为:https://jeepay-cashier.work.zhuzhilong.com/cashier/index.html#/hub/78d439f3140fe4047c7f8f6cda1048313636890021b83c0c167270dbce4fc2ff根据应用部署情况,比预期的访问路径多了 /cashier,查看源代码后,发现这个路径是写死在 com.jeequan.jeepay.core.model.DBApplicationConfig.java中的:去掉相关方法中的 /cashier 后,根据 docker-compose.yml 重新构建镜像及重启服务后,可正常支付。
2023年12月13日
15 阅读
0 评论
0 点赞
2023-10-31
magic-boot/magic-api 使用随记
magic-api 是一款非常优秀的快速开发框架,在做大屏的过程中找到的宝贝应用,可以用类JS 语法快速开发接口,能非常方便的操作数据库及处理一些复杂的业务逻辑,而 magic-boot 是基于 magic-api 开发的一款快速开发平台,提供了基本的用户鉴权、后台管理等功能。在实际项目过程中我基于 magic-boot 做了如下事项:1、通过Matomo的API定时同步数据至数仓做大数据分析2、采集coolshell.cn整站数据3、每周五定时推送企业微信消息,提醒同事写周报更多功能待进一步挖掘……下面是我在项目中有用到的技术点的一个记录,会在项目过程中不断更新,便于后续有其他项目用到的话,能快速查找运用。获取 系统设置/配置中心 模块设置的配置项import '@/configure/getBykey' as configure; var baseURL = configure('matomo.base-url'); var authToken = configure('matomo.auth-token');http请求数据示例import cn.hutool.json.JSONUtil import org.springframework.util.StringUtils import http; import log; import '@/configure/getBykey' as configure; // 从配置中心获取接口所需数据 var baseURL = configure('matomo.base-url'); var authToken = configure('matomo.auth-token'); // 组转请求URL var reqURL = `${baseURL}?module=API&method=${method}&format=JSON&token_auth=${authToken}`; if (!StringUtils.isEmpty(params)) { reqURL += `&${params}`; } log.info(`reqURL:${reqURL}`); // 请求数据 var resData = http.connect(reqURL).contentType('application/json').get().getBody(); return resData;在原基础上增加http请求出错重试机制import cn.hutool.json.JSONUtil import org.springframework.util.StringUtils import cn.hutool.core.date.DateUtil import cn.hutool.core.thread.ThreadUtil import http import log import '@/configure/getBykey' as configure; // 从配置中心获取接口所需数据 var baseURL = configure('matomo.base-url'); var authToken = configure('matomo.auth-token'); // 组转请求URL var reqURL = `${baseURL}?module=API&method=${method}&format=JSON&token_auth=${authToken}`; if (!StringUtils.isEmpty(params)) { reqURL += `&${params}`; } // log.info(`reqURL:${reqURL}`); var requestStartTime = DateUtil.now(); var successFlag = '' var exceptionContent = '' // 请求数据 var resData = '' // 最大重试次数 var MAX_RETRY_COUNT = 5 // 当前重试次数 var retryCount = 0; while(retryCount < MAX_RETRY_COUNT && successFlag !== 'Y') { try { resData = http.connect(reqURL).contentType('application/json').get().getBody(); successFlag = 'Y' } catch(e) { successFlag = 'N' ThreadUtil.sleep(1000); retryCount++ exceptionContent = e.getMessage(); } } var requestEndTime = DateUtil.now(); db.table('matomo_sync_log').insert({ 'apiMethod' : method, 'requestParams' : params, 'responseContent': resData.asString(), 'exceptionContent': exceptionContent, 'requestTime':requestStartTime, 'responseTime': requestEndTime, 'retryCount':retryCount, 'successFlag': successFlag}); return resData;分页获取数据在获取一些详情数据的时候,存在数据量超大的情况,一次性获取所有数据极有可能会导致数据库及应用挂掉,即便不挂掉的情况下,也会超长事件才会响应结果,所以采用分页获取还是很有必要的。下面的代码是在实际项目中分页调用Matomo的接口获取输入然后将接口返回的数据,结构化处理后,保存到本地数据库。 import cn.hutool.json.JSONUtil import log; import '@/dmcfns/sendMatomoRequest' as getMatomoData; var PAGE_SIZE=10 // 每页获取记录数,获取后批量入库 var currentPage = 0// 当前页 var needLoad = true // 继续加载数据标识,当当前页加载的内容小于PAGE_SIZE时则不再加载 while(needLoad) { var resData = getMatomoData('Live.getLastVisitsDetails', `period=day&date=${date}&idSite=${siteId}&doNotFetchActions=1&filter_offset=${PAGE_SIZE * currentPage}&filter_limit=${PAGE_SIZE}`) if (resData.asString().startsWith("[")) { var siteDatas = JSONUtil.parseArray(resData); var siteData = []; for (index, site in siteDatas) { var siteObj = siteDatas.getJSONObject(index); var visitId = siteObj.getStr("idVisit"); var visitorId = siteObj.getStr("visitorId"); var visitIp = siteObj.getStr("visitIp"); var longitude = siteObj.getStr("longitude"); var latitude = siteObj.getStr("latitude"); var userId = siteObj.getStr("userId"); var country = siteObj.getStr("country"); var referrerName = siteObj.getStr("referrerName"); var visitProps = JSONUtil.toJsonStr(siteObj); db.table('matomo_daily_visit').insert({ date, siteId, visitId, visitorId, visitIp, longitude, latitude, userId, country, referrerName, visitProps }) } if (siteDatas.size() === PAGE_SIZE) { currentPage++ } else { needLoad = false } } }使用多数据源操作数据库db['ZR'].table('crawler_list').insert({ pageURL: linkURL, articleTitle:linkTitle })根据主键更新部分字段内容var updateMap = { id: visitItem.id, ipCountry: country, ipProvince: province, ipCity: city } db.table('matomo_daily_visit').primary('id').update(updateMap)修改某个字段的值db.table("sys_user").column("isLogin", isLogin).where().eq("id",id).update()推送消息至企业微信机器人import http import log // 测试机器人 // var ROBOT_URL = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx' // 超算云研发部2023 微信群的eHour 机器人 var ROBOT_URL = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxx' var msg = { "msgtype": "text", "text": { "content": """为便于公司开展项目成本核算相关工作,请各位同事及时登录eHour系统录入本周工作工时,如有系统使用相关问题可联系 XXX,感谢配合[抱拳][抱拳]\neHour系统链接如下:http://172.18.3.xxx/""", "mentioned_list":["@all"] } } http.connect(ROBOT_URL).body(msg).post(); log.info('eHour消息推送成功') // 以下cron表达式为每周五16:30分执行 00 30 16 * * 05读取Excel文件并转换为jsonimport cn.hutool.poi.excel.ExcelUtil import request import log var datas = ExcelUtil.getReader(new ByteArrayInputStream(request.getFile('file').getBytes())).readAll() var sourceDatas = datas::stringify::json
2023年10月31日
34 阅读
0 评论
0 点赞
2023-10-19
Mybatis-Plus 分页功能实现流程
Mybatis 自带分页功能,但是该分页功能是基于内存的分页,也就是会讲所有符合条件的数据查询出来,然后在从内存中获取当前页的信息,这种方式在数据量大的情况下会存在严重的性能问题。我们通过 Mybatis-Plus 自带的分页插件可以很好的解决这个问题,实现步骤记录如下:1、 添加配置类,示例内容如下:package com.paratera.protect.config; import com.baomidou.mybatisplus.annotation.DbType; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Mybatis Plus 配置类,主要用于继承分页插件 * @author 朱治龙 * @date 2023-10-19 23:14:00 */ @Configuration public class MyBatisPlusConfig { @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } } 2、使用分页方法查询2.1、selectPage2.1.1 示例代码 @Test void testSelectPage() { QueryWrapper<Staff> qw = Wrappers.query(); qw.ge("age", 30); Page<Staff> page = new Page<Staff>(1, 2); // 不查记录数 // page.setSearchCount(false); Page<Staff> pageData = staffMapper.selectPage(page, qw); System.out.println("总页数:" + pageData.getPages()); System.out.println("总记录数:" + pageData.getTotal()); pageData.getRecords().forEach(System.out::println); }2.1.2 查询结果DEBUG==> Preparing: SELECT COUNT(*) AS total FROM staff WHERE (age >= ?) DEBUG==> Parameters: 30(Integer) TRACE<== Columns: total TRACE<== Row: 4 DEBUG<== Total: 1 DEBUG==> Preparing: SELECT id,name,age,email,mobile,manager_id,create_time FROM staff WHERE (age >= ?) LIMIT ? DEBUG==> Parameters: 30(Integer), 2(Long) TRACE<== Columns: id, name, age, email, mobile, manager_id, create_time TRACE<== Row: 1087982257332887553, 大boss, 40, 001@paratera.com, 13888886666, null, 2019-01-11 14:20:20 TRACE<== Row: 1094590409767661570, 张雨琪, 31, zjq@blsc.com, 18684700070, 1088248166370832385, 2019-01-14 09:15:15 DEBUG<== Total: 2 总页数:2 总记录数:4 Staff(id=1087982257332887553, name=大boss, age=40, email=001@paratera.com, mobile=13888886666, managerId=null, createTime=2019-01-11T14:20:20) Staff(id=1094590409767661570, name=张雨琪, age=31, email=zjq@blsc.com, mobile=18684700070, managerId=1088248166370832385, createTime=2019-01-14T09:15:15) 2.2、selectMapsPage2.2.1 示例代码 @Test void testSelectMapsPage() { QueryWrapper<Staff> qw = Wrappers.query(); qw.ge("age", 30); Page<Map<String, Object>> page2 = new Page<>(1, 2); Page<Map<String, Object>> pageData2 = staffMapper.selectMapsPage(page2, qw); System.out.println("总页数:" + pageData2.getPages()); System.out.println("总记录数:" + pageData2.getTotal()); pageData2.getRecords().forEach(System.out::println); }2.2.2 查询结果DEBUG==> Preparing: SELECT COUNT(*) AS total FROM staff WHERE (age >= ?) DEBUG==> Parameters: 30(Integer) TRACE<== Columns: total TRACE<== Row: 4 DEBUG<== Total: 1 DEBUG==> Preparing: SELECT id,name,age,email,mobile,manager_id,create_time FROM staff WHERE (age >= ?) LIMIT ? DEBUG==> Parameters: 30(Integer), 2(Long) TRACE<== Columns: id, name, age, email, mobile, manager_id, create_time TRACE<== Row: 1087982257332887553, 大boss, 40, 001@paratera.com, 13888886666, null, 2019-01-11 14:20:20 TRACE<== Row: 1094590409767661570, 张雨琪, 31, zjq@blsc.com, 18684700070, 1088248166370832385, 2019-01-14 09:15:15 DEBUG<== Total: 2 总页数:2 总记录数:4 {create_time=2019-01-11T14:20:20, name=大boss, mobile=13888886666, id=1087982257332887553, age=40, email=001@paratera.com} {create_time=2019-01-14T09:15:15, manager_id=1088248166370832385, name=张雨琪, mobile=18684700070, id=1094590409767661570, age=31, email=zjq@blsc.com} 3、附加说明使用分页插件查询时默认是会执行两条 SQL,一条获取当前页的数据,一条获取总记录数。在某些场景下(如瀑布流模式),只需要获取当前页的内容即可,不需要总记录数相关分页数值,此时可在创建 Page 时,第三个参数给值为 false:Page(long current, long size, boolean searchCount)。也可使用 page.setSearchCount(false);
2023年10月19日
39 阅读
0 评论
0 点赞
2023-10-18
Mybatis-Plus 自定义SQL
有的时候使用条件构造器自定义SQL满足不了我们的需求,我们既想使用 Wrapper,又想使用SQL,MP 对这种方式也提供了支持,MP 版本号应≥3.0.7。下面是该方案的实现记录:实现方案一:Mapper接口中使用@Select注解1、Mapper 示例代码如下:import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.paratera.protect.entity.Staff; import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Select; import java.util.List; /** * @author 朱治龙 * @date 2023-10-17 11:46:00 */ public interface StaffMapper extends BaseMapper<Staff> { @Select("select * from staff ${ew.customSqlSegment}") List<Staff> selectAll(@Param(Constants.WRAPPER)Wrapper<Staff> wrapper); }2、调用示例代码: @Test void testSelfSQL() { LambdaQueryWrapper<Staff> lqw = Wrappers.lambdaQuery(Staff.class); lqw.eq(Staff::getName, "朱治龙").and(wq2 -> wq2.lt(Staff::getAge, 40).or().isNotNull(Staff::getEmail)); List<Staff> staffList = staffMapper.selectAll(lqw); staffList.forEach(System.out::println); }3、输出结果DEBUG==> Preparing: select * from staff WHERE (name = ? AND (age < ? OR email IS NOT NULL)) DEBUG==> Parameters: 朱治龙(String), 40(Integer) TRACE<== Columns: id, name, age, email, mobile, manager_id, create_time TRACE<== Row: 1714166763984199681, 朱治龙, 36, zhuzl@blsc.cn, 15084978453, 1088248166370832385, 2023-10-17 14:29:38 DEBUG<== Total: 1 Staff(id=1714166763984199681, name=朱治龙, age=36, email=zhuzl@blsc.cn, mobile=15084978453, managerId=1088248166370832385, createTime=2023-10-17T14:29:38)实现方案二:使用xml1、配置xml文件的存放路径。再application.yml中添加 xml 文件的引用路径配置mybatis-plus: mapper-locations: - classpath:/mapper/*Mapper.xml2、在resources目录下添加 mapper 目录,并新建 mapper 文件,如文件名为 StaffMapper.xml,示例内容为:<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.paratera.protect.dao.StaffMapper"> <select id="selectByXml" resultType="com.paratera.protect.entity.Staff"> select * from staff ${ew.customSqlSegment} </select> </mapper>3、Mapper 接口中添加方法,示例代码如下:package com.paratera.protect.dao; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.paratera.protect.entity.Staff; import org.apache.ibatis.annotations.Param; import java.util.List; /** * @author 朱治龙 * @date 2023-10-17 11:46:00 */ public interface StaffMapper extends BaseMapper<Staff> { List<Staff> selectByXml(@Param(Constants.WRAPPER)Wrapper<Staff> wrapper); }4、调用示例代码: @Test void testSelfSQL2() { LambdaQueryWrapper<Staff> lqw = Wrappers.lambdaQuery(Staff.class); lqw.eq(Staff::getName, "朱治龙").and(wq2 -> wq2.lt(Staff::getAge, 50).or().isNotNull(Staff::getEmail)); List<Staff> staffList = staffMapper.selectByXml(lqw); staffList.forEach(System.out::println); }5、输出结果DEBUG==> Preparing: select * from staff WHERE (name = ? AND (age < ? OR email IS NOT NULL)) DEBUG==> Parameters: 朱治龙(String), 50(Integer) TRACE<== Columns: id, name, age, email, mobile, manager_id, create_time TRACE<== Row: 1714166763984199681, 朱治龙, 36, zhuzl@blsc.cn, 15084978453, 1088248166370832385, 2023-10-17 14:29:38 DEBUG<== Total: 1 Staff(id=1714166763984199681, name=朱治龙, age=36, email=zhuzl@blsc.cn, mobile=15084978453, managerId=1088248166370832385, createTime=2023-10-17T14:29:38)
2023年10月18日
9 阅读
0 评论
0 点赞
2022-09-20
记一次KBS项目实施经验分享
背景介绍KBS 是我入职并行后接触的第一个项目,该项目之前有同事基于 OpenKB(采用 Node + Express + Mongodb技术栈的开源知识库应用,github地址:https://github.com/mrvautin/openKB) 搭建过一个简单的知识库,但是初步实施后的效果不是很符合预期,便安排我着手知识库相关的后续工作。以前在 TRS 的时候有接触过三一项目的知识库项目,该项目基于 TRS 自主研发的企业知识门户产品 —— TRS EKP V6.5 进行实施,是整合了公司的EKP、全文检索系统、数据网关系统等产品综合实施的一套企业内部知识分享的平台,是一个将公司内部各领域专家们的经验进行梳理沉淀的知识管理工具。经过前期的了解,我们将要做的知识库跟之前实施的三一知识库项目有非常大的出入,主要是用于一些产品文档的发布,类似阿里云、腾讯云的产品文档生成工具。最开始有考虑采用纯前端的方式实现,如当前较主流的VuePress实现,这样可以很方便的实现markdown文档编写 + algolia全文检索,并能基于git很方便达到文档版本控制的目的,但在深入沟通后,发现该方式不能满足其他的需求,如支持审核发布、支持用户认证后才可浏览。所以只能考虑其他的解决方案了。经过沟通后,决定先找个开源的 CMS,做个基础效果,再论证可行性技术调研及产品选型结合以往工作经验,经过沟通,最终考虑采用CMS的方式来实现我们的知识库项目。可考虑开源 CMS 或低成本的采购商业 CMS。以下是一些当时(2021-07-05)调研的一些开源 CMS 情况:经过沟通,最终选择了基于 jspxCMS 来进行项目可行性验证实施工作,主要有如下原因:1、jspxCMS 采用 SpringBoot + FreeMarker 跟我们团队技术栈较接近2、编辑器支持Ueditor 和 Markdown 两种编辑模式,适合给运营人员进行富文本排版及程序员轻量内容排版3、可商用,但需在页面底部保留jspxCMS版权声明,购买商业版后可去除,商业版价格也就几千块钱,且商业版支持站群模式,功能试用满足运营要求的话,可购买商业版License。项目实施过程1、搭建示例效果项目初期在本地部署了 jspxCMS 应用,准备基于该CMS实现基本的内容管理及网站发布工作。有阿里云文档做参考,主要是仿站,经过两天的实施工作,在没有UI参与的情况下实现了站点的最初的界面效果:知识库首页产品首页内容详情页搜索结果页按照目前的规划,KBS 系统可以通过栏目的方式很方便的做成公司整个产品体系的知识库门户。2、系统功能改造经过需求梳理,整理了如下待办事项:1、知识库站点模板{x} 知识库首页模板 zzl{x} 产品页模板 zzl{x} 内容页模板 zzl{x} 实现检索功能 zzl{x} 用户登录、认证对接 zzl{x} 北龙超云版知识库模板(优先级低) zzl2、后台改造{x} 改造成并行蓝风格 zzl{x} 改造左侧菜单导航样式 zzl{x} 改造顶部显示逻辑 zzl{x} 将列表中的操作列调整到表格最后一列 zzl{x} 去掉左侧菜单中商业版相关菜单入口 zzl{x} 去掉系统业务界面中商业版相关的功能入口 zzl{x} 将启动方式改成 spring-boot 模式,即 java -jar [xxx].war ,不用放到 tomcat 里 hx{x} 调研是否能支持多例 hx{x} 用户认证对接 hx{x} 支持多环境配置,即有生产,stage 和本地。 使用配置中心 hx{x} 支持 JDK 17 hx{x} 购买商业版后整合商业版相关功能模块 zzl{x} 应用资源文件分离改造。将上传文件目录、模板目录、索引文件目录从war包中分离 zzl{x} 后台界面支持并行蓝风格和北龙红风格 zzl3、技术实现细节完成该项目后主要有如下可圈可点的部分技术细节用于分享1、知识库前台模板制作网站模板的工作其实主要就是在html静态文件中插入 CMS 的标签,并结合CMS的静态资源引入机制改写页面相关的静态资源文件。一般会提取一些公共底部、公共底部模板之类的。CMS 实施过程中常用的标签其实并不多,主要也就是获取栏目列表、内容列表、栏目信息、内容信息这些最基础的。经过实践,发现jspxCMS的标签还是非常灵活的,支持模板嵌套、标签嵌套等jspxCMS 使用的freemarker实现静态化,自带了非常灵活的判断、循环等表达式。经过整理,部分标签使用记录如下:获取栏目列表及栏目信息以下是本项目中获取知识库左侧栏目结构树的模板示例<ul class="menu-list-container"> [@NodeList parentId=node.id;list] [#list list as pnode] <li class="level1" data-node-id="${pnode.id}"> <a href="javascript:void(0);"> <i class="icon-triangle kbsIconfont kbs-triangle"></i> <span class="menu-item-text">${pnode.name}</span> </a> <ul> [@NodeList parentId=pnode.id;subNodes] [#list subNodes as subNode] <li class="level2"> <a href="javascript:void(0);"> <i class="icon-triangle kbsIconfont kbs-triangle"></i> <span class="menu-item-text">${subNode.name}</span> </a> <ul> [@InfoList nodeId=subNode.id;subNodeDocs] [#list subNodeDocs as info] <li class="level3"> <a href="${info.url}" target="_self"> <span class="menu-item-text">${info.title}</span> </a> </li> [/#list] [/@InfoList] </ul> </li> [/#list] [/@NodeList] [@InfoList nodeId=pnode.id;infos] [#list infos as info] <li class="level2"> <a href="${info.url}" target="_self"> <span class="menu-item-text">${info.title}</span> </a> </li> [/#list] [/@InfoList] </ul> </li> [/#list] [/@NodeList] [@InfoList nodeId=node.id;infos] [#list infos as info] <li class="level1"> <a href="${info.url}" target="_self"> <span class="menu-item-text">${info.title}</span> </a> </li> [/#list] [/@InfoList] </ul>获取内容列表及内容信息 <div class="box-product-docs"> <div class="box"> <h3 class="box-title">最新发布</h3> <div class="box-content row"> [@InfoList nodeId=node.id limit='6' isIncludeChildren='true';list] [#list list as info] <div class="col-md-4 col-sm-6"> <div class="list-body"> <a href="${info.url}"> <p class="list-title text-left">${info.title}</p> <p class="list-content">${info.description}</p> <span class="list-more">详情+</span> <span class="list-date">更新时间:${info.publishDate?string('yyyy-MM-dd HH:mm')}</span> </a> </div> </div> [/#list] [/@InfoList] </div> </div> <div class="box"> <h3 class="box-title">热门知识</h3> <div class="box-content row"> [@InfoList nodeId=node.id sort='views desc' limit='6' isIncludeChildren='true';list] [#list list as info] <div class="col-md-4 col-sm-6"> <div class="list-body"> <a href="${info.url}"> <p class="list-title text-left">${info.title}</p> <p class="list-content">${info.description}</p> <span class="list-more">详情+</span> <span class="list-date">浏览次数:${info.views}</span> </a> </div> </div> [/#list] [/@InfoList] </div> </div> </div> 模板嵌套[#include "include_header.html"/]当前位置<div class="body-position"> <i class=" kbsIconfont kbs-home"></i> [#list node.hierarchy as n]<a href="${n.url}">${n.name}</a>[#if n_has_next] > [/#if][/#list] </div>内容分页[@InfoPage nodeId=node.id pageSize='15';pagedList] <ul class="list-unstyled mt10"> [#list pagedList.content as info] <li style="padding:15px 0;border-bottom:1px dotted #ccc;"> [#if info.withImage] <div class="left" style="padding:3px 10px 3px 0;width:22%;"> <a href="${info.url}" target="_blank"><img src="${info.smallImageUrl}" width="138" height="92"/></a> </div> [/#if] <div class="left" style="[#if info.withImage]width:75%;[/#if]"> <div>[@A bean=info class='ff-yh fs18 a c-000' target="_blank"/]</div> <div class="" style="line-height:1.8;padding:2px 0;color:#818181;">${substring(info.description,100,'...')}</div> <div class="" style="padding:2px;color:#a1a1a1;">${info.publishDate?string('yyyy-MM-dd HH:mm:ss')}</div> </div> <div class="clear"></div> </li> [/#list] </ul> <div class="mt20"> [#include "inc_page.html"/] </div> [/@InfoPage] 分页嵌套模板(inc_page.html)[#escape x as (x)!?html] <div class="pager"> [#if page>1]<a href="${paging(1)}" class="page"><i class="kbsIconfont kbs-first"></i></a>[/#if] [#if page>1] <a href="${paging(page-1)}" class="page"><i class="kbsIconfont kbs-prev"></i></a> [/#if] [#assign start=page-4/][#if start<1][#assign start=1/][/#if] [#assign end=start+8/][#if end>pagedList.totalPages][#assign end=pagedList.totalPages/][/#if][#if end<1][#assign end=1/][/#if] [#if end-start<8][#assign start=end-8/][/#if][#if start<1][#assign start=1/][/#if] [#list start..end as p] <a[#if page!=p] href="${paging(p)}"[/#if] class="[#if page!=p]page[#else]page-curr[/#if]">${p}</a> [/#list] [#if page < pagedList.totalPages] <a href="${paging(page+1)}" class="page"><i class="kbsIconfont kbs-next"></i></a> <a href="${paging(pagedList.totalPages)}" class="page" ><i class="kbsIconfont kbs-last"></i></a> [/#if] </div> [/#escape] 超级方便的app自由模版app自由模板,主要是可以传参给模板获取一些指定的内容片段,这就极大的扩展了模板的功能,目前我们用得最多的场景是不同系统间通过app模板获取KBS的内容列表及内容详情等json数据,然后在异构系统中进行个性化展现。app自由模板的官网介绍如下(https://www.ujcms.com/documentation/269.html):整理了部分自由模板内容以作备忘:1、内容列表页(app_newsPage.html)[#-- 栏目内容分页列表数据,用于列表新品快报、公告更多页面分页显示数据 --]{ [@InfoPage node=Param.nodeCode pageSize=Param.pageSize page=Param.pageNum;pagedList] "total":${pagedList.totalElements}, "size":${pagedList.size}, "pages":${pagedList.totalPages}, "current":${pagedList.number + 1}, "records": [ [#list pagedList.content as info] { "id":"${info.id}", "title":"${info.title?js_string}", "publishDate":"${info.publishDate?string('yyyy-MM-dd HH:mm:ss')}", "url":"${info.url?js_string}" }[#if info_has_next],[/#if] [/#list] ] [/@InfoPage] } 2、内容详情页(app_newsDetail.html)[#-- 内容详情接口,用于点击查看内容详情 --][@Info id=Param.articleId;bean] { "id":"${bean.id}", "url":"${bean.url?js_string}", "title":"${bean.title?js_string}", "publishDate":"${bean.publishDate?string('yyyy-MM-dd HH:mm')}", "content":"${bean.text?replace("/uploads/1/","${site.domain}/uploads/1/")?js_string}", "editorType":"${bean.customs.text_editor_type}" } [/@Info] 2、应用资源文件分离改造jspxCMS 默认的部署方式是将war包部署到tomcat下,相关的应用资源文件(模板文件、后台上传的图片及音视频文件、静态化文件等)也都在应用根目录下,功能强大的jspxCMS提供了发布点功能,将上传的资源文件、静态化的html文件生成到指定的目录下,但是该方式功能有限,不能完全满足我们的需求,如索引文件目录、站点模板文件等均不可配置。我们的实际需求是需要将应用采用docker部署并使用java -jar 的方式进行启动,那样就需要将原有跟应用整合在一起的资源文件分离出来,独立部署的其他存储目录中。充分理解该需求及使用场景后,特制定了本改造方案。改造过程如下:1、application.yml 增加分离文件相关根目录配置信息经过前期梳理,考虑到应用实际情况,将分离文件拆分为两个目录:kbs.appDataPath:应用数据(临时文件/日志/缓存/索引等)的存放路径kbs.resourceRootPath:需本地持久化保存应用资源数据(模板数据/上传文件/静态化文件等)的存放路径目录支持%{Parent}、%{Self}这样的相对路径进行占位,默认为应用当前目录下的 appdata 和 KBSData 目录。亦可在启动应用时增加启动参数的方式修改路径,如--kbs.resourceRootPath=c:/workspace/KBSData/kbs: appDataPath: '%{Parent}/appdata/' resourceRootPath: '%{Parent}/KBSData/'2、启动应用时对目录进行初始化阅读源代码后,发现应用在启动时,有调用Constants.loadEnvironment()方法,我们可以利用该方法作为入口,调用我们的目录初始化方法 KbsConfig.init(),在应用启动时初始化相关目录,保障相关资源目录是绝对存在的。KbsConfig 相关核心代码如下:/** * 知识库初始化配置 * @author 朱治龙 * @Date 2021-08-27 10:18:32 */ @Component public class KbsConfig { private static String appDataPathInConfig; private static String resourceRootPathInConfig; /** * 应用数据目录 */ protected static String appDataPath = null; /** * 资源数据目录 */ protected static String resourceDataPath = null; public static void init(Environment env) { appDataPathInConfig = env.getProperty("kbs.appDataPath"); resourceRootPathInConfig = env.getProperty("kbs.resourceRootPath"); initPaths(); } /** * 初始化相关文件路径 */ private static void initPaths() { // 初始化应用数据目录 String applicationRealPath = KbsUtil.getApplicationRealPath(); String tempAppDataPath = applicationRealPath; if (ObjectUtil.isNotEmpty(appDataPathInConfig)) { tempAppDataPath = appDataPathInConfig; tempAppDataPath = KbsUtil.replacePathHolder(tempAppDataPath); } else { tempAppDataPath += "appdata/"; } appDataPath = tempAppDataPath; AppDataPath.initialAppDataPath(); // 初始化资源目录 String tempResourceDataPath = applicationRealPath; if (ObjectUtil.isNotEmpty(resourceRootPathInConfig)) { tempResourceDataPath = resourceRootPathInConfig; tempResourceDataPath = KbsUtil.replacePathHolder(tempResourceDataPath); } else { tempResourceDataPath += "KBSData"; } resourceDataPath = tempResourceDataPath; ResourceDataPath.initialResourcePath(); } /** * 获取应用数据目录 * @return */ public static String getAppDataPath() { if(ObjectUtil.isEmpty(appDataPath)) { initPaths(); } return appDataPath; } /** * 获取应用数据目录 * @return */ public static String getResourceRootPath() { if(ObjectUtil.isEmpty(resourceDataPath)) { initPaths(); } return resourceDataPath; } }3、改造模板、索引文件、上传资源资源文件的代码逻辑相关逻辑主要涉及 jspxCMS 的核心代码,均在阅读相关源码后进行改造,此处就不做代码罗列了。4、系统资源目录中的静态资源文件提供对外访问服务/** * 系统资源目录中的静态资源文件提供对外访问服务 * @author 朱治龙 * @Date 2021-08-27 14:48:36 */ @Configuration public class KbsWebMvcConfigurer implements WebMvcConfigurer { /** * 资源目录相关文件提供后台访问 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/uploads/**").addResourceLocations("file:" + ResourceDataPath.getUploadsPath() + "/"); registry.addResourceHandler("/template/**").addResourceLocations("file:" + ResourceDataPath.getTemplatePath() + "/"); } /** * 默认首页跳转到index.html */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("redirect:/index.html"); } }3、后台界面支持并行蓝风格和北龙红风格在之前对 jspxCMS 改造为并行蓝风格时,有将并行蓝风格相关的样式文件提取到paratera-kbs.css中,项目在升级改造过程中后端的同事有整合Spring Cloud 及配置中心组件spring cloud config,可在配置中心中设置当前主题信息,然后应用拿到主题信息后调用不同的样式文件,即可达到同一套后台代码展现不同风格的目的。具体实现过程如下:1、配置中心增加 kbs.theme-mode 配置项值为 blsc 或 paratera,若不配置则默认为 paratera2、全局注入主题风格变量,名称为themeMode在阅读源码后,修改后台拦截器 BackInterceptor 相关的代码逻辑,全局注入themeMode变量,便于在后台jsp文件中拿到该值进行主题相关业务逻辑判断3、修改后台jsp视图文件主要修改代码为:实施成果后台效果1、并行知识库2、北龙知识库前台效果并行知识库1、站点首页2、产品页3、内容详情页4、检索结果页北龙知识库1、站点首页2、产品页3、内容详情页4、检索结果页
2022年09月20日
37 阅读
0 评论
0 点赞
2022-09-01
数据库设计挑战问题
缘起聊天内容整理文字如下问题现有如下表:作业记录表T1作业ID作业日期机时记录时间Job0012022/01/0110002022/01/02Job0022022/01/0120002022/01/02作业扣费表T2作业ID作业日期机时扣费扣费时间Job0012022/01/0110001002022/01/02Job0022022/01/0120002002022/01/02在正常情况下,一切可以工作得很好,但是事实并非理想,作业调度器也会出故障,导致作业机时发生变化。例如在 2022/01/03 重新采集作业时 Job001 的机时从 1000 又变回了 500,但系统对 Job001 已完成了 1000 机时的扣费问题:如何设计数据库表在不修改已写入的历史记录的前提下,解决作业机时发生变化的问题?情境说明:机时变化是不可抗力因素,1000是错误的,后来500是正确的,但也可能最终200是正确的最终时间其实不确定,如果非要给个期限,可以限定为一年内变动都是可能发生的设计字段可以随意,但已存在的记录数据不能变化了增加新表,新字段都不受任何限制综合要求如下:前提:可以增加新表,可以在原先这两个表基础上加字段,但是不能修改或者删除这两个表已有的字段目标:以当前机时作为扣费依据,历史多扣或者少扣需要进行补扣或者补偿我的解答综上问题,我的解决思路如下:1、对最关键的作业记录表增加内容Hash字段,内容为 md5(作业ID+作业日期+机时),该字段可作为主键,由于数据在入库前用于计算Hash的内容都是已知的,作为主键字段可以最大程度的利用主键索引提升检索效率。如果这个Hash数据不存在则在下次 作业调度器 同步作业记录时自动计入 T1 表,如果已存在则直接忽略2、T2 表关联 T1 表的 DataHash字段,主要用于判断作业是否已扣费,便于后续扣费任务的执行3、增加 「异常作业退费记录表T3」,用于对之前已扣费的作业进行退费行为记录。4、若出现重复的情况(作业ID跟作业日期相同),将上一次作业(以记录时间排序)已扣费记录进行退费,并记入T3表调整后的表结构记录如下:作业记录表T1DataHash字段内容为:md5(作业ID+作业日期+机时),如:md5(Job001-2022/01/01-1000)作业ID作业日期机时记录时间DataHashJob0012022/01/0110002022/01/0215dc9093ab0febf9aff2810891ef1a64Job0022022/01/0120002022/01/02fa8f51fb2f36087a9b4c6deacf5f1dd2Job0012022/01/015002022/08/304ec0fea71753f4a7585dc4cf4dd460f2作业扣费表T2作业ID作业日期机时扣费扣费时间作业记录DataHashJob0012022/01/0110001002022/01/0215dc9093ab0febf9aff2810891ef1a64Job0022022/01/0120002002022/01/02fa8f51fb2f36087a9b4c6deacf5f1dd2Job0012022/01/01500502022/08/304ec0fea71753f4a7585dc4cf4dd460f2异常作业退费记录表T3作业ID作业日期机时退费退费时间作业记录DataHashJob0012022/01/0110001002022/08/3015dc9093ab0febf9aff2810891ef1a64其他小伙伴的解答胡大神毛老师
2022年09月01日
8 阅读
0 评论
0 点赞
2022-04-21
Java 中加密算法的编程使用-学习笔记
Base64 编码的编程使用密码学综述密码学基本功能1)机密性2)鉴别3)报文完整性4)不可否认性基本模型密码学算法的分类消息编码: Base64消息摘要: MD类、SHA类、MAC对称密码: DES、3DES、 AES非对称密码: RSA、DH密钥交换数字签名: RSASignature、DSASignature密码学五元组明文密文加密算法(公开)解密算法(公开)密钥密钥密钥和密码的巨大区别密钥!=密码Key != Password密钥+规则==密码在密码破解者看来,拿到密钥就等于有了密码!所以,重点在密钥对称密码(传统密码)与非对称密码(公钥密码)对称密码:加解密使用相同密钥的密码体制非对称密码:加解密使用不同的密钥一公钥与私钥Java编程中常用类消息编码 BASE64Encoder、BASE64Decoder消息摘要 MessageDigest对称密码 KeyGenerator、SecretKey、 Cipher非对称密码 KeyPairGenerator、KeyFactory、 KeyPair、PublicKey、 PrivateKey、 Cipher数字签名 SignatureBase64 算法的编程使用Base64编码示例 密文: UGFyYXRlcmE=明文: ParateraBase64算法定义 Base64是一种基于64个字符的编码算法,以任意8位字节序列组合的描述形式,这种形式不易直接识别。经Base64编码后的字符串的字符数是以4为单位的整数倍。Base64 密钥4.Base64 编程使用引入sun.misc.BASE64Decoder.jar1、加密:byte[] data = "Paratera".getBytes(); String result= new BASE64Encoder().encode(data) ;2、解密:byte[] result = new BASE64Decoder().decodeBuffer(data) ;Base64 算法的实际应用使用Telnet 发送邮件:pubLic class Base64Util { public static String enc ryptBase64(byte[] data){ return new BASE64Encoder().encode(data); } public static String dec ryptBase64(String data) throws IOException{ byte[] resultBytes = new BASE64Decoder().decodeBuffer(data); return new String( resultBytes); } } pubLic class SMTPMain { public static void main(String[] args) { //用户名密码 String sender = "cnsmtp01@163.com"; String receiver = "cnsmtp02@163. com"; String password = "computer"; //将用户名和密码进行Base64编码 String userBase64 = Base64Util.encryptBase64(sender.substring(0, sender.index0f("@")).getBytes()); String passBase64 = Base64Util.encryptBase64(password.getBytes()); try { Socket socket = new Socket("smtp.163.com", 25); InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); PrintWriter writter = new PrintWriter(outputStream, true); System.out.println(reader.readLine()); // HELO writter.println("HELO Paratera"); System.out.println(reader.readLine()); // AUTH LOGIN >> >Base64 writter.println( "AUTH LOGIN"); System.out.println(reader.readLine()); writter.println(userBase64); System.out.println(reader.readLine()); writter.println(passBase64); System.out.println(reader.readLine()); // Set "MAIL FROM" and "RCPT TO" writter.println("MAIL FROM:<" + sender + ">"); System.out.println(reader.readLine()); writter.println( "RCPT TO:<" + receiver + ">"); System.out.println(reader.readLine()); // Set "DATA" writter.println("DATA"); System.out.println(reader.readLine()); writter.println("SUBJECT:你好并行集团"); writter.println("FROM:" + sender); writter.println("T0:" + receiver); writter.println("Content-Type: text/plain;charset=\"gb2312\""); writter.println(); writter.println("北京并行科技股份有限公司(简称并行科技,PARATERA,股票代码:839493,挂牌新三板)成立于2007年,是国内领先的超算云服务和运营服务提供商,提供超算公有云服务、超算行业云服务、AI云服务、设计仿真云和计算资源建设及运营服务。"); writter.println("."); writter.println(""); System.out.println(reader.readLine()); //发送完毕了, 和服务器拜拜 writter.println("RSET"); System.out.println(reader.readLine()); writter.println("QUIT"); System.out.println(reader.readLine()); } catch (Exception e) { e.printStackTrace(); } } } 消息摘要的编程使用消息摘要概述唯一对应一个消息或文本的固定长度的值,由一个单向Hash加密函数对消息进行作用而产生消息摘要的分类:(1) MD (Message Digest) :消息摘要算法(2) SHA (Secure Hash Algorithm) : 安全散列算法(3) MAC (Message Authentication Code) :消息认证码算法消息摘要的日常应用验证数据完整性(防止在传输途中被篡改)MD算法的编程使用为计算机安全领域广泛使用的一种散列函数,用以提供消息的 完整性 保护MD系列算法(JDK)算法数据长度摘要长度MD2任意128MD5任意128MD算法编程使用核心代码//初始化MessageDigest MessageDigest md5 = MessageDigest.getInstance("MD5"); //更新, data为原始数据 md5.update(data); /生成摘要 byte[] result= md5.digest(); 完整示例代码:// ---------1.BytesToHex.java--------- package net.wljy.crypt.util; /** * 字节转16进制 * * @author paratera * */ public class BytesToHex { public static String fromBytesToHex(byte[] resultBytes) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < resultBytes.length; i++) { if (Integer.toHexString(0xFF & resultBytes[i]).length() == 1) { builder.append("0").append(Integer.toHexString(0xFF & resultBytes[i])); } else { builder.append(Integer.toHexString(0xFF & resultBytes[i])); } } return builder.toString(); } } // ---------2.MessageDigestUtil.java--------- package net.wljy.crypt; import java.io.File; import java.io.FileInputStream; import java.security.DigestInputStream; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import net.wljy.crypt.util.BytesToHex; public class MessageDigestUtil { /** * MD5字符串加密 * @param data 待加密字符串 * @return */ public static String encryptMD5(byte [] data) { MessageDigest md5; try { md5 = MessageDigest.getInstance("MD5"); md5.update(data); byte[] resultBytes = md5.digest(); return BytesToHex.fromBytesToHex(resultBytes); } catch (NoSuchAlgorithmException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } /** * 获取文件MD5值 * @param filePath 文件路径 * @return */ public static String getMD5ofFile(String filePath) { FileInputStream fis; try { fis = new FileInputStream(new File(filePath)); DigestInputStream dis = new DigestInputStream(fis, MessageDigest.getInstance("MD5")); byte[] buffer = new byte[1024]; int read = dis.read(buffer, 0, 1024); while(read != -1) { read = dis.read(buffer, 0, 1024); } MessageDigest md = dis.getMessageDigest(); return BytesToHex.fromBytesToHex(md.digest()); } catch (Exception e) { e.printStackTrace(); } return null; } } // ---------3.MD5Test.java--------- package net.wljy.crypt; public class MD5Test { public static final String DATA = "并行集团"; public static void main(String[] args) { String md5Str = MessageDigestUtil.encryptMD5(DATA.getBytes()); System.out.println(md5Str); String fileMd5Str = MessageDigestUtil.getMD5ofFile("D:/API接口文档.html"); System.out.println("文件MD5:" + fileMd5Str); } } SHA算法的编程使用安全哈希算法,主要适用于数字签名标准里面定义的数字签名算法SHA算法种类算法数据长度摘要长度SHA-1任意160SHA-256任意256SHA-384任意384SHA-512任意512核心代码//初始化MessageDigest MessageDigest sha = MessageDigest.getInstance("SHA-x"); // SHA-x需替换为具体的算法名称 //更新, data为原始数据 sha.update(data); /生成摘要 byte[] result= sha.digest(); 示例代码: /** * 计算某一数据的hash摘要值 * @param data * @return */ public static String encryptSHA(byte [] data) { try { MessageDigest sha = MessageDigest.getInstance("SHA-256"); sha.update(data); return BytesToHex.fromBytesToHex(sha.digest()); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; }HMAC算法的编程使用引言:单一MD或SHA算法缺点? 摘要值容易被篡改!结合了MD5和SHA算法的优势,同时用 密钥 对摘要加密,是一种更为安全的消息摘要算法。HMAC算法的种类算法数据长度摘要长度HmacMD5任意128HmacSHA1任意160HmacSHA256任意256HmacSHA384任意384HmacSHA512任意512如何生成密钥KeyGenerator!核心代码1.构建密钥//初始化KeyGenerator KeyGenerator keyGen = KeyGenerator.getInstance("HmacMD5"); //产生密钥 SecretKey secretKey = keyGen.generateKey(); //得到密钥字节数组 byte[] key= secretKey.getEncoded(); 2.执行消息摘要//从字节数组还原密钥 SecretKey secretKey = new SecretKeySpec(key, "HmacMD5"); //实例化Mac Mac mac = Mac.getInstance("HmacMD5"); //用密钥初始化Mac mac.init(secretKey); //执行消息摘要 byte[] result = mac.doFinal(data); 散列函数特征:(1)输入任意长度数据,输出固定长度散列值,计算很容易,过程不可逆(2)对于某数据,其散列值固定(3)两个数据不同,则对应的散列值也不同(4)两个散列值不同,则对应的原始输入数据也不同2.散列函数破解(1)真破解:2005年2月,王小云教授破解了SHA-1 算法,已知数据A和其散列值,找到了另一个数据B与A的散列值相同(2)假破解:根据数据库查询散列值,找到其对应的数据明文,根本在于数据库的查询!对称密码的编程使用对称密码概述1、加密密钥和解密密钥相同,对于大多数对称密码算法,加解密过程互逆2、加解密通信模型3、特点:算法公开、计算量小、加密速度快、加密效率高4、弱点:双方都使用同样密钥,安全性得不到保证5、分组密码工作模式(1) ECB: 电子密码本(2) CBC: 密文链接(3) CFB: 密文反馈(4) OFB:输出反馈(5) CTR: 计数器6、分组密码填充方式(1) NoPadding(2) PKCS5Padding(3) ISO10126Padding7、常用对称密码:(1) DES (Data Encryption Standard)(2) 3DES (Triple DES、DESede)3重DES(3) AES (Advanced Encryption Standard)DES算法的编程使用DES:数据加密标准,是对称加密算法领域中的典型算法特点:密钥偏短(56位)、 生 命周期短JDK实现| 算法 | 密钥长度 | 默认密钥长度 | 工作模式 | 填充方式 |DES5656ECB、CBC、PCBC、CTR、CTS、CFB、CFB8-CFB128、OFB、OFB8-OFB128NoPadding、PKCS5Padding、ISO10126Padding4、示例代码// ---------1.DESUtil.java--------- package net.wljy.crypt.des; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; public class DESUtil { /** * 生成密钥 * * @return */ public static byte[] initKey() { try { KeyGenerator keyGen = KeyGenerator.getInstance("DES"); keyGen.init(56); SecretKey secretKey = keyGen.generateKey(); return secretKey.getEncoded(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } /** * DES加密 * * @param data 要加密的数据 * @param key 密钥 * @return */ public static byte[] encrypt(byte[] data, byte[] key) { SecretKey secretKey = new SecretKeySpec(key, "DES"); try { // DES/ECB/NoPadding --> 加密方式/工作模式/填充方式,没有严格要求的场景只写加密方式即可 Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(data); } catch (Exception e) { e.printStackTrace(); } return null; } /** * DES加密 * * @param data 要解密的数据 * @param key 密钥 * @return */ public static byte[] decrypt(byte[] encryptedData, byte[] key) { SecretKey secretKey = new SecretKeySpec(key, "DES"); try { // DES/ECB/NoPadding --> 加密方式/工作模式/填充方式,没有严格要求的场景只写加密方式即可 Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(encryptedData); } catch (Exception e) { e.printStackTrace(); } return null; } } // ---------2.DESTest.java--------- package net.wljy.crypt.des; import net.wljy.crypt.util.BytesToHex; public class DESTest { public static final String DATA = "并行集团"; public static void main(String[] args) { // 获取密钥 byte[] initKey = DESUtil.initKey(); System.out.println("secretKey:" + BytesToHex.fromBytesToHex(initKey)); // 加密数据 byte[] desResultData = DESUtil.encrypt(DATA.getBytes(), initKey); System.out.println("desResultData:" + BytesToHex.fromBytesToHex(desResultData)); // 解密数据 byte[] decryptResultData = DESUtil.decrypt(desResultData, initKey); System.out.println("decryptResultData:" + new String(decryptResultData)); } } 3DES算法的编程使用1、3DES:将密钥长度增至112位或168位,通过增加迭代次数提高安全性2、缺点:处理速度较慢、密钥计算时间较长、加密效率不高3、JDK实现算法密钥长度默认密钥长度工作模式填充方式3DES112、168168ECB、CBC、PCBC、CTR、CTS、CFB、CFB8-CFB128、OFB、OFB8-OFB128NoPadding、PKCS5Padding、ISO10126Padding核心代码1、生成密钥KeyGenerator keyGen = KeyGenerator.getInstance("DESede"); keyGen.init(168); //可指定密钥长度为112或168, 默认为168 SecretKey secretKey = keyGen.generateKey(); 2.加/解密SecretKey secretKey = new SecretKeySpec(key, "DESede"); Cipher cipher = Cipher.getInstance("DESede"); cipher.init(Cipher.ENCRYPT_MODE,secretKey); byte[] cipherByte = cipher.doFinal(data); 4、完整示例代码// ---------1.TripleDESUtil.java--------- package net.wljy.crypt.tripledes; import java.security.NoSuchAlgorithmException; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; /** * 3DES * * @author paratera * */ public class TripleDESUtil { /** * 生成密钥 * * @return */ public static byte[] initKey() { KeyGenerator keyGen; try { keyGen = KeyGenerator.getInstance("DESede"); keyGen.init(168); // 112 168 SecretKey secretKey = keyGen.generateKey(); return secretKey.getEncoded(); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return null; } /** * 3DES加密 * * @param data 待加密数据 * @param key 密钥 * @return */ public static byte[] encrypt(byte[] data, byte[] key) { try { SecretKey secretKey = new SecretKeySpec(key, "DESede"); Cipher cipher = Cipher.getInstance("DESede"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); return cipher.doFinal(data); } catch (Exception e) { e.printStackTrace(); } return null; } /** * 3DES解密 * * @param encrypedData 待解加密数据 * @param key 密钥 * @return */ public static byte[] decrypt(byte[] encrypedData, byte[] key) { try { SecretKey secretKey = new SecretKeySpec(key, "DESede"); Cipher cipher = Cipher.getInstance("DESede"); cipher.init(Cipher.DECRYPT_MODE, secretKey); return cipher.doFinal(encrypedData); } catch (Exception e) { e.printStackTrace(); } return null; } } // ---------2.TripleDESTest.java--------- package net.wljy.crypt.tripledes; import net.wljy.crypt.util.BytesToHex; public class TripleDESTest { public static final String DATA = "并行集团"; public static void main(String[] args) { // 获取密钥 byte[] initKey = TripleDESUtil.initKey(); System.out.println("secretKey:" + BytesToHex.fromBytesToHex(initKey)); // 加密数据 byte[] resultData = TripleDESUtil.encrypt(DATA.getBytes(), initKey); System.out.println("3DESResultData:" + BytesToHex.fromBytesToHex(resultData)); // 解密数据 byte[] decryptResultData = TripleDESUtil.decrypt(resultData, initKey); System.out.println("3DESdecryptResultData:" + new String(decryptResultData)); } } AES算法的编程使用1、AES:高级数据加密标准,能够有效抵御已知的针对DES算法的所有攻击2、特点:密钥建立时间短、灵敏性好、内存需求低、安全性高3、JDK实现算法密钥长度默认密钥长度工作模式填充方式AES128、192、256128ECB、CBC、PCBC、CTR、CTS、CFB、CFB8-CFB128、OFB、OFB8-OFB128NoPadding、PKCS5Padding、ISO10126Padding核心代码1、生成密钥KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(128); //默认128,获得无政策权限后可为192或256 SecretKey secretKey = keyGen.generateKey();2、加/解密SecretKey secretKey = new SecretKeySpec(key, "AES"); Cipher cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE,secretKey); // ENCRYPT_MODE:加密 || DECRYPT_MODE:解密 byte[] cipherByte = cipher.doFinal(data);政策权限文件keyGen.init(),如果给值为19或256会报错,JDK8及之前的文件可到如下地址下载政策文件:https://www.oracle.com/java/technologies/javase-jce-all-downloads.html。下载后覆盖jdk/jre/lib/security下的两个jar包即可。非对称密码的编程使用非对称密码概述1、对称密码中的密钥配送存在不安全问题2、非对称密码通信模型3、非对称密码的特征(1).需要两个密钥来进行加密和解密,分别为公钥和私钥(2).公钥和私钥相互配对,称为KeyPair4、非对称密码的优缺点(1).优点:相比于对称密码,安全性更高(2).缺点:加解密花费时间长、速度慢5、常用非对称密码:(1).DH密钥交换算法(2).RSA算法(3).EIGamal算法6、非对称密码的作用(1).密钥交换(DH)(2).加密/解密(RSA)(3).数字签名(RSA)DH 算法的编程使用RSA 算法的编程使用数字签名的编程使用0数字签名概述RSASignature 算法的编程使用DSASignature 算法的编程使用
2022年04月21日
5 阅读
0 评论
0 点赞
2022-01-21
自动化项目部署系列:③部署宝塔面板
宝塔面板是一款服务器管理软件,支持windows和linux系统,可以通过Web端轻松管理服务器,提升运维效率。例如:创建管理网站、FTP、数据库,拥有可视化文件管理器,可视化软件管理器,可视化CPU、内存、流量监控图表,计划任务等功能。宝塔官网: https://www.bt.cn/选择使用宝塔,主要是为了更方便的在线管理服务器资源。官方有提供 Docker 版本:https://www.bt.cn/bbs/forum.php?mod=viewthread&tid=79499。但为了更好的应用于服务器管理,我将 Docker 直接安装于宿主机上。安装步骤创建宝塔根目录 /wldata/btroot 并为宝塔应用目录 /www 创建软连接[root@VM-16-6-centos wldata]# mkdir /wldata/btroot [root@VM-16-6-centos wldata]# ln -s /wldata/btroot /www [root@VM-16-6-centos wldata]#开始安装根据官网:https://www.bt.cn/bbs/thread-19376-1-1.html 使用如下命令进行安装yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh安装过程记录如下:[root@VM-16-6-centos wldata]# yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile docker-ce-stable | 3.5 kB 00:00:00 epel | 4.7 kB 00:00:00 extras | 2.9 kB 00:00:00 os | 3.6 kB 00:00:00 updates | 2.9 kB 00:00:00 Package wget-1.14-18.el7_6.1.x86_64 already installed and latest version Nothing to do --2022-01-21 16:16:44-- http://download.bt.cn/install/install_6.0.sh Resolving download.bt.cn (download.bt.cn)... 116.10.184.143, 240e:a5:4200:89::143 Connecting to download.bt.cn (download.bt.cn)|116.10.184.143|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 26258 (26K) [application/octet-stream] Saving to: ‘install.sh’ 100%[========================================================================================================>] 26,258 --.-K/s in 0.03s 2022-01-21 16:16:44 (765 KB/s) - ‘install.sh’ saved [26258/26258] install.sh: line 23: [: : integer expression expected +---------------------------------------------------------------------- | Bt-WebPanel FOR CentOS/Ubuntu/Debian +---------------------------------------------------------------------- | Copyright © 2015-2099 BT-SOFT(http://www.bt.cn) All rights reserved. +---------------------------------------------------------------------- | The WebPanel URL will be http://SERVER_IP:8888 when installed. +---------------------------------------------------------------------- Do you want to install Bt-Panel to the /www directory now?(y/n): y --------------------------------------------- Selected download node... Download node: http://dg2.bt.cn --------------------------------------------- Synchronizing system time... Fri Jan 21 16:16:52 CST 2022 Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile docker-ce-stable | 3.5 kB 00:00:00 epel | 4.7 kB 00:00:00 extras | 2.9 kB 00:00:00 os | 3.6 kB 00:00:00 updates | 2.9 kB 00:00:00 Package ntp-4.2.6p5-29.el7.centos.2.x86_64 already installed and latest version Nothing to do 21 Jan 16:16:52 ntpdate[28087]: the NTP socket is in use, exiting setenforce: SELinux is disabled Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile Package wget-1.14-18.el7_6.1.x86_64 already installed and latest version Package 2:tar-1.26-35.el7.x86_64 already installed and latest version Package gcc-4.8.5-44.el7.x86_64 already installed and latest version Package 1:make-3.82-24.el7.x86_64 already installed and latest version Package zip-3.0-11.el7.x86_64 already installed and latest version Package gcc-4.8.5-44.el7.x86_64 already installed and latest version Package libxml2-2.9.1-6.el7_9.6.x86_64 already installed and latest version Package zlib-1.2.7-19.el7_9.x86_64 already installed and latest version Package zlib-devel-1.2.7-19.el7_9.x86_64 already installed and latest version Package libwebp-0.3.0-10.el7_9.x86_64 already installed and latest version Package freetype-2.8-14.el7_9.1.x86_64 already installed and latest version Package lsof-4.87-6.el7.x86_64 already installed and latest version Package pcre-8.32-17.el7.x86_64 already installed and latest version Package pcre-devel-8.32-17.el7.x86_64 already installed and latest version No package vixie-cron available. Package crontabs-1.11-6.20121102git.el7.noarch already installed and latest version Package xz-devel-5.2.2-1.el7.x86_64 already installed and latest version Resolving Dependencies --> Running transaction check ---> Package bzip2-devel.x86_64 0:1.0.6-13.el7 will be installed ---> Package c-ares.x86_64 0:1.10.0-3.el7 will be installed ---> Package freetype-devel.x86_64 0:2.8-14.el7_9.1 will be installed ---> Package gdbm-devel.x86_64 0:1.10-8.el7 will be installed ---> Package icu.x86_64 0:50.2-4.el7_7 will be installed ---> Package libcurl-devel.x86_64 0:7.29.0-59.el7_9.1 will be installed epel/7/x86_64/filelists_db | 12 MB 00:00:01 os/7/x86_64/filelists_db | 7.2 MB 00:00:00 updates/7/x86_64/filelists_db | 7.2 MB 00:00:00 ---> Package libdb4-devel.x86_64 0:4.8.30-13.el7 will be installed --> Processing Dependency: libdb4(x86-64) = 4.8.30-13.el7 for package: libdb4-devel-4.8.30-13.el7.x86_64 ---> Package libffi-devel.x86_64 0:3.0.13-19.el7 will be installed ---> Package libicu-devel.x86_64 0:50.2-4.el7_7 will be installed ---> Package libjpeg-turbo-devel.x86_64 0:1.2.90-8.el7 will be installed ---> Package libpcap-devel.x86_64 14:1.5.3-12.el7 will be installed ---> Package libpng-devel.x86_64 2:1.5.13-8.el7 will be installed ---> Package libwebp-devel.x86_64 0:0.3.0-10.el7_9 will be installed ---> Package libxml2-devel.x86_64 0:2.9.1-6.el7_9.6 will be installed ---> Package libxslt.x86_64 0:1.1.28-6.el7 will be installed ---> Package libxslt-devel.x86_64 0:1.1.28-6.el7 will be installed --> Processing Dependency: libgcrypt-devel for package: libxslt-devel-1.1.28-6.el7.x86_64 ---> Package libxslt-python.x86_64 0:1.1.28-6.el7 will be installed ---> Package ncurses-devel.x86_64 0:5.9-14.20130511.el7_4 will be installed ---> Package openssl.x86_64 1:1.0.2k-22.el7_9 will be updated ---> Package openssl.x86_64 1:1.0.2k-24.el7_9 will be an update --> Processing Dependency: openssl-libs(x86-64) = 1:1.0.2k-24.el7_9 for package: 1:openssl-1.0.2k-24.el7_9.x86_64 ---> Package openssl-devel.x86_64 1:1.0.2k-22.el7_9 will be updated ---> Package openssl-devel.x86_64 1:1.0.2k-24.el7_9 will be an update ---> Package readline-devel.x86_64 0:6.2-11.el7 will be installed ---> Package sqlite-devel.x86_64 0:3.7.17-8.el7_7.1 will be installed ---> Package tk-devel.x86_64 1:8.5.13-6.el7 will be installed --> Processing Dependency: tcl-devel = 1:8.5.13 for package: 1:tk-devel-8.5.13-6.el7.x86_64 --> Processing Dependency: libXft-devel for package: 1:tk-devel-8.5.13-6.el7.x86_64 --> Processing Dependency: libX11-devel for package: 1:tk-devel-8.5.13-6.el7.x86_64 ---> Package unzip.x86_64 0:6.0-22.el7_9 will be updated ---> Package unzip.x86_64 0:6.0-24.el7_9 will be an update --> Running transaction check ---> Package libX11-devel.x86_64 0:1.6.7-4.el7_9 will be installed --> Processing Dependency: pkgconfig(xcb) >= 1.11.1 for package: libX11-devel-1.6.7-4.el7_9.x86_64 --> Processing Dependency: pkgconfig(xproto) for package: libX11-devel-1.6.7-4.el7_9.x86_64 --> Processing Dependency: pkgconfig(xcb) for package: libX11-devel-1.6.7-4.el7_9.x86_64 --> Processing Dependency: pkgconfig(kbproto) for package: libX11-devel-1.6.7-4.el7_9.x86_64 ---> Package libXft-devel.x86_64 0:2.3.2-2.el7 will be installed --> Processing Dependency: pkgconfig(xrender) for package: libXft-devel-2.3.2-2.el7.x86_64 --> Processing Dependency: pkgconfig(fontconfig) for package: libXft-devel-2.3.2-2.el7.x86_64 ---> Package libdb4.x86_64 0:4.8.30-13.el7 will be installed ---> Package libgcrypt-devel.x86_64 0:1.5.3-14.el7 will be installed --> Processing Dependency: libgpg-error-devel for package: libgcrypt-devel-1.5.3-14.el7.x86_64 ---> Package openssl-libs.x86_64 1:1.0.2k-22.el7_9 will be updated ---> Package openssl-libs.x86_64 1:1.0.2k-24.el7_9 will be an update ---> Package tcl-devel.x86_64 1:8.5.13-8.el7 will be installed --> Running transaction check ---> Package fontconfig-devel.x86_64 0:2.13.0-4.3.el7 will be installed --> Processing Dependency: pkgconfig(uuid) for package: fontconfig-devel-2.13.0-4.3.el7.x86_64 --> Processing Dependency: pkgconfig(expat) for package: fontconfig-devel-2.13.0-4.3.el7.x86_64 ---> Package libXrender-devel.x86_64 0:0.9.10-1.el7 will be installed ---> Package libgpg-error-devel.x86_64 0:1.12-3.el7 will be installed ---> Package libxcb-devel.x86_64 0:1.13-1.el7 will be installed --> Processing Dependency: pkgconfig(xau) >= 0.99.2 for package: libxcb-devel-1.13-1.el7.x86_64 ---> Package xorg-x11-proto-devel.noarch 0:2018.4-1.el7 will be installed --> Running transaction check ---> Package expat-devel.x86_64 0:2.1.0-12.el7 will be installed ---> Package libXau-devel.x86_64 0:1.0.8-2.1.el7 will be installed ---> Package libuuid-devel.x86_64 0:2.23.2-65.el7_9.1 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================================================================================== Package Arch Version Repository Size ================================================================================================================================================== Installing: bzip2-devel x86_64 1.0.6-13.el7 os 218 k c-ares x86_64 1.10.0-3.el7 os 78 k freetype-devel x86_64 2.8-14.el7_9.1 updates 447 k gdbm-devel x86_64 1.10-8.el7 os 47 k icu x86_64 50.2-4.el7_7 os 187 k libcurl-devel x86_64 7.29.0-59.el7_9.1 updates 303 k libdb4-devel x86_64 4.8.30-13.el7 epel 32 k libffi-devel x86_64 3.0.13-19.el7 os 23 k libicu-devel x86_64 50.2-4.el7_7 os 703 k libjpeg-turbo-devel x86_64 1.2.90-8.el7 os 99 k libpcap-devel x86_64 14:1.5.3-12.el7 os 118 k libpng-devel x86_64 2:1.5.13-8.el7 os 122 k libwebp-devel x86_64 0.3.0-10.el7_9 updates 23 k libxml2-devel x86_64 2.9.1-6.el7_9.6 updates 1.1 M libxslt x86_64 1.1.28-6.el7 os 242 k libxslt-devel x86_64 1.1.28-6.el7 os 309 k libxslt-python x86_64 1.1.28-6.el7 os 59 k ncurses-devel x86_64 5.9-14.20130511.el7_4 os 712 k readline-devel x86_64 6.2-11.el7 os 139 k sqlite-devel x86_64 3.7.17-8.el7_7.1 os 104 k tk-devel x86_64 1:8.5.13-6.el7 os 488 k Updating: openssl x86_64 1:1.0.2k-24.el7_9 updates 494 k openssl-devel x86_64 1:1.0.2k-24.el7_9 updates 1.5 M unzip x86_64 6.0-24.el7_9 updates 172 k Installing for dependencies: expat-devel x86_64 2.1.0-12.el7 os 57 k fontconfig-devel x86_64 2.13.0-4.3.el7 os 138 k libX11-devel x86_64 1.6.7-4.el7_9 updates 981 k libXau-devel x86_64 1.0.8-2.1.el7 os 14 k libXft-devel x86_64 2.3.2-2.el7 os 19 k libXrender-devel x86_64 0.9.10-1.el7 os 17 k libdb4 x86_64 4.8.30-13.el7 epel 607 k libgcrypt-devel x86_64 1.5.3-14.el7 os 129 k libgpg-error-devel x86_64 1.12-3.el7 os 16 k libuuid-devel x86_64 2.23.2-65.el7_9.1 updates 93 k libxcb-devel x86_64 1.13-1.el7 os 1.1 M tcl-devel x86_64 1:8.5.13-8.el7 os 165 k xorg-x11-proto-devel noarch 2018.4-1.el7 os 280 k Updating for dependencies: openssl-libs x86_64 1:1.0.2k-24.el7_9 updates 1.2 M Transaction Summary ================================================================================================================================================== Install 21 Packages (+13 Dependent packages) Upgrade 3 Packages (+ 1 Dependent package) Total download size: 12 M Downloading packages: No Presto metadata available for updates (1/38): c-ares-1.10.0-3.el7.x86_64.rpm | 78 kB 00:00:00 (2/38): bzip2-devel-1.0.6-13.el7.x86_64.rpm | 218 kB 00:00:00 (3/38): expat-devel-2.1.0-12.el7.x86_64.rpm | 57 kB 00:00:00 (4/38): gdbm-devel-1.10-8.el7.x86_64.rpm | 47 kB 00:00:00 (5/38): fontconfig-devel-2.13.0-4.3.el7.x86_64.rpm | 138 kB 00:00:00 (6/38): libXau-devel-1.0.8-2.1.el7.x86_64.rpm | 14 kB 00:00:00 (7/38): libXft-devel-2.3.2-2.el7.x86_64.rpm | 19 kB 00:00:00 (8/38): icu-50.2-4.el7_7.x86_64.rpm | 187 kB 00:00:00 (9/38): freetype-devel-2.8-14.el7_9.1.x86_64.rpm | 447 kB 00:00:00 (10/38): libXrender-devel-0.9.10-1.el7.x86_64.rpm | 17 kB 00:00:00 (11/38): libffi-devel-3.0.13-19.el7.x86_64.rpm | 23 kB 00:00:00 (12/38): libX11-devel-1.6.7-4.el7_9.x86_64.rpm | 981 kB 00:00:00 (13/38): libdb4-devel-4.8.30-13.el7.x86_64.rpm | 32 kB 00:00:00 (14/38): libgpg-error-devel-1.12-3.el7.x86_64.rpm | 16 kB 00:00:00 (15/38): libgcrypt-devel-1.5.3-14.el7.x86_64.rpm | 129 kB 00:00:00 (16/38): libcurl-devel-7.29.0-59.el7_9.1.x86_64.rpm | 303 kB 00:00:00 (17/38): libdb4-4.8.30-13.el7.x86_64.rpm | 607 kB 00:00:00 (18/38): libjpeg-turbo-devel-1.2.90-8.el7.x86_64.rpm | 99 kB 00:00:00 (19/38): libicu-devel-50.2-4.el7_7.x86_64.rpm | 703 kB 00:00:00 (20/38): libpcap-devel-1.5.3-12.el7.x86_64.rpm | 118 kB 00:00:00 (21/38): libwebp-devel-0.3.0-10.el7_9.x86_64.rpm | 23 kB 00:00:00 (22/38): libpng-devel-1.5.13-8.el7.x86_64.rpm | 122 kB 00:00:00 (23/38): libuuid-devel-2.23.2-65.el7_9.1.x86_64.rpm | 93 kB 00:00:00 (24/38): libxslt-1.1.28-6.el7.x86_64.rpm | 242 kB 00:00:00 (25/38): libxcb-devel-1.13-1.el7.x86_64.rpm | 1.1 MB 00:00:00 (26/38): libxml2-devel-2.9.1-6.el7_9.6.x86_64.rpm | 1.1 MB 00:00:00 (27/38): libxslt-python-1.1.28-6.el7.x86_64.rpm | 59 kB 00:00:00 (28/38): libxslt-devel-1.1.28-6.el7.x86_64.rpm | 309 kB 00:00:00 (29/38): openssl-1.0.2k-24.el7_9.x86_64.rpm | 494 kB 00:00:00 (30/38): ncurses-devel-5.9-14.20130511.el7_4.x86_64.rpm | 712 kB 00:00:00 (31/38): openssl-devel-1.0.2k-24.el7_9.x86_64.rpm | 1.5 MB 00:00:00 (32/38): sqlite-devel-3.7.17-8.el7_7.1.x86_64.rpm | 104 kB 00:00:00 (33/38): readline-devel-6.2-11.el7.x86_64.rpm | 139 kB 00:00:00 (34/38): tcl-devel-8.5.13-8.el7.x86_64.rpm | 165 kB 00:00:00 (35/38): openssl-libs-1.0.2k-24.el7_9.x86_64.rpm | 1.2 MB 00:00:00 (36/38): unzip-6.0-24.el7_9.x86_64.rpm | 172 kB 00:00:00 (37/38): xorg-x11-proto-devel-2018.4-1.el7.noarch.rpm | 280 kB 00:00:00 (38/38): tk-devel-8.5.13-6.el7.x86_64.rpm | 488 kB 00:00:00 -------------------------------------------------------------------------------------------------------------------------------------------------- Total 4.3 MB/s | 12 MB 00:00:02 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : xorg-x11-proto-devel-2018.4-1.el7.noarch 1/42 Installing : libxslt-1.1.28-6.el7.x86_64 2/42 Updating : 1:openssl-libs-1.0.2k-24.el7_9.x86_64 3/42 Installing : libXau-devel-1.0.8-2.1.el7.x86_64 4/42 Installing : libxcb-devel-1.13-1.el7.x86_64 5/42 Installing : libX11-devel-1.6.7-4.el7_9.x86_64 6/42 Installing : libXrender-devel-0.9.10-1.el7.x86_64 7/42 Installing : 1:tcl-devel-8.5.13-8.el7.x86_64 8/42 Installing : libdb4-4.8.30-13.el7.x86_64 9/42 Installing : libgpg-error-devel-1.12-3.el7.x86_64 10/42 Installing : libgcrypt-devel-1.5.3-14.el7.x86_64 11/42 Installing : expat-devel-2.1.0-12.el7.x86_64 12/42 Installing : ncurses-devel-5.9-14.20130511.el7_4.x86_64 13/42 Installing : libuuid-devel-2.23.2-65.el7_9.1.x86_64 14/42 Installing : libxml2-devel-2.9.1-6.el7_9.6.x86_64 15/42 Installing : 2:libpng-devel-1.5.13-8.el7.x86_64 16/42 Installing : freetype-devel-2.8-14.el7_9.1.x86_64 17/42 Installing : fontconfig-devel-2.13.0-4.3.el7.x86_64 18/42 Installing : libXft-devel-2.3.2-2.el7.x86_64 19/42 Installing : 1:tk-devel-8.5.13-6.el7.x86_64 20/42 Installing : libxslt-devel-1.1.28-6.el7.x86_64 21/42 Installing : readline-devel-6.2-11.el7.x86_64 22/42 Installing : libdb4-devel-4.8.30-13.el7.x86_64 23/42 Updating : 1:openssl-devel-1.0.2k-24.el7_9.x86_64 24/42 Updating : 1:openssl-1.0.2k-24.el7_9.x86_64 25/42 Installing : libxslt-python-1.1.28-6.el7.x86_64 26/42 Installing : sqlite-devel-3.7.17-8.el7_7.1.x86_64 27/42 Installing : libcurl-devel-7.29.0-59.el7_9.1.x86_64 28/42 Installing : icu-50.2-4.el7_7.x86_64 29/42 Installing : libwebp-devel-0.3.0-10.el7_9.x86_64 30/42 Installing : c-ares-1.10.0-3.el7.x86_64 31/42 Installing : bzip2-devel-1.0.6-13.el7.x86_64 32/42 Installing : gdbm-devel-1.10-8.el7.x86_64 33/42 Installing : 14:libpcap-devel-1.5.3-12.el7.x86_64 34/42 Updating : unzip-6.0-24.el7_9.x86_64 35/42 Installing : libffi-devel-3.0.13-19.el7.x86_64 36/42 Installing : libicu-devel-50.2-4.el7_7.x86_64 37/42 Installing : libjpeg-turbo-devel-1.2.90-8.el7.x86_64 38/42 Cleanup : 1:openssl-devel-1.0.2k-22.el7_9.x86_64 39/42 Cleanup : 1:openssl-1.0.2k-22.el7_9.x86_64 40/42 Cleanup : 1:openssl-libs-1.0.2k-22.el7_9.x86_64 41/42 Cleanup : unzip-6.0-22.el7_9.x86_64 42/42 Verifying : libXft-devel-2.3.2-2.el7.x86_64 1/42 Verifying : 2:libpng-devel-1.5.13-8.el7.x86_64 2/42 Verifying : libxslt-python-1.1.28-6.el7.x86_64 3/42 Verifying : libxml2-devel-2.9.1-6.el7_9.6.x86_64 4/42 Verifying : libjpeg-turbo-devel-1.2.90-8.el7.x86_64 5/42 Verifying : 1:openssl-libs-1.0.2k-24.el7_9.x86_64 6/42 Verifying : libicu-devel-50.2-4.el7_7.x86_64 7/42 Verifying : libffi-devel-3.0.13-19.el7.x86_64 8/42 Verifying : unzip-6.0-24.el7_9.x86_64 9/42 Verifying : libuuid-devel-2.23.2-65.el7_9.1.x86_64 10/42 Verifying : libdb4-devel-4.8.30-13.el7.x86_64 11/42 Verifying : 14:libpcap-devel-1.5.3-12.el7.x86_64 12/42 Verifying : gdbm-devel-1.10-8.el7.x86_64 13/42 Verifying : bzip2-devel-1.0.6-13.el7.x86_64 14/42 Verifying : fontconfig-devel-2.13.0-4.3.el7.x86_64 15/42 Verifying : 1:openssl-devel-1.0.2k-24.el7_9.x86_64 16/42 Verifying : libxslt-devel-1.1.28-6.el7.x86_64 17/42 Verifying : ncurses-devel-5.9-14.20130511.el7_4.x86_64 18/42 Verifying : readline-devel-6.2-11.el7.x86_64 19/42 Verifying : libX11-devel-1.6.7-4.el7_9.x86_64 20/42 Verifying : 1:tk-devel-8.5.13-6.el7.x86_64 21/42 Verifying : c-ares-1.10.0-3.el7.x86_64 22/42 Verifying : 1:openssl-1.0.2k-24.el7_9.x86_64 23/42 Verifying : xorg-x11-proto-devel-2018.4-1.el7.noarch 24/42 Verifying : libXrender-devel-0.9.10-1.el7.x86_64 25/42 Verifying : libwebp-devel-0.3.0-10.el7_9.x86_64 26/42 Verifying : icu-50.2-4.el7_7.x86_64 27/42 Verifying : freetype-devel-2.8-14.el7_9.1.x86_64 28/42 Verifying : libgcrypt-devel-1.5.3-14.el7.x86_64 29/42 Verifying : libcurl-devel-7.29.0-59.el7_9.1.x86_64 30/42 Verifying : expat-devel-2.1.0-12.el7.x86_64 31/42 Verifying : libxcb-devel-1.13-1.el7.x86_64 32/42 Verifying : sqlite-devel-3.7.17-8.el7_7.1.x86_64 33/42 Verifying : libxslt-1.1.28-6.el7.x86_64 34/42 Verifying : libgpg-error-devel-1.12-3.el7.x86_64 35/42 Verifying : libdb4-4.8.30-13.el7.x86_64 36/42 Verifying : 1:tcl-devel-8.5.13-8.el7.x86_64 37/42 Verifying : libXau-devel-1.0.8-2.1.el7.x86_64 38/42 Verifying : 1:openssl-1.0.2k-22.el7_9.x86_64 39/42 Verifying : 1:openssl-devel-1.0.2k-22.el7_9.x86_64 40/42 Verifying : 1:openssl-libs-1.0.2k-22.el7_9.x86_64 41/42 Verifying : unzip-6.0-22.el7_9.x86_64 42/42 Installed: bzip2-devel.x86_64 0:1.0.6-13.el7 c-ares.x86_64 0:1.10.0-3.el7 freetype-devel.x86_64 0:2.8-14.el7_9.1 gdbm-devel.x86_64 0:1.10-8.el7 icu.x86_64 0:50.2-4.el7_7 libcurl-devel.x86_64 0:7.29.0-59.el7_9.1 libdb4-devel.x86_64 0:4.8.30-13.el7 libffi-devel.x86_64 0:3.0.13-19.el7 libicu-devel.x86_64 0:50.2-4.el7_7 libjpeg-turbo-devel.x86_64 0:1.2.90-8.el7 libpcap-devel.x86_64 14:1.5.3-12.el7 libpng-devel.x86_64 2:1.5.13-8.el7 libwebp-devel.x86_64 0:0.3.0-10.el7_9 libxml2-devel.x86_64 0:2.9.1-6.el7_9.6 libxslt.x86_64 0:1.1.28-6.el7 libxslt-devel.x86_64 0:1.1.28-6.el7 libxslt-python.x86_64 0:1.1.28-6.el7 ncurses-devel.x86_64 0:5.9-14.20130511.el7_4 readline-devel.x86_64 0:6.2-11.el7 sqlite-devel.x86_64 0:3.7.17-8.el7_7.1 tk-devel.x86_64 1:8.5.13-6.el7 Dependency Installed: expat-devel.x86_64 0:2.1.0-12.el7 fontconfig-devel.x86_64 0:2.13.0-4.3.el7 libX11-devel.x86_64 0:1.6.7-4.el7_9 libXau-devel.x86_64 0:1.0.8-2.1.el7 libXft-devel.x86_64 0:2.3.2-2.el7 libXrender-devel.x86_64 0:0.9.10-1.el7 libdb4.x86_64 0:4.8.30-13.el7 libgcrypt-devel.x86_64 0:1.5.3-14.el7 libgpg-error-devel.x86_64 0:1.12-3.el7 libuuid-devel.x86_64 0:2.23.2-65.el7_9.1 libxcb-devel.x86_64 0:1.13-1.el7 tcl-devel.x86_64 1:8.5.13-8.el7 xorg-x11-proto-devel.noarch 0:2018.4-1.el7 Updated: openssl.x86_64 1:1.0.2k-24.el7_9 openssl-devel.x86_64 1:1.0.2k-24.el7_9 unzip.x86_64 0:6.0-24.el7_9 Dependency Updated: openssl-libs.x86_64 1:1.0.2k-24.el7_9 Complete! Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile Package libxslt-1.1.28-6.el7.x86_64 already installed and latest version Package libxslt-devel-1.1.28-6.el7.x86_64 already installed and latest version Package libxslt-python-1.1.28-6.el7.x86_64 already installed and latest version Nothing to do Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile Package libjpeg-turbo-devel-1.2.90-8.el7.x86_64 already installed and latest version Nothing to do Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile No package vixie-cron available. Error: Nothing to do Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile Package libdb4-devel-4.8.30-13.el7.x86_64 already installed and latest version Nothing to do Loaded plugins: fastestmirror, langpacks Loading mirror speeds from cached hostfile Resolving Dependencies --> Running transaction check ---> Package epel-release.noarch 0:7-14 will be installed --> Finished Dependency Resolution Dependencies Resolved ================================================================================================================================================== Package Arch Version Repository Size ================================================================================================================================================== Installing: epel-release noarch 7-14 epel 15 k Transaction Summary ================================================================================================================================================== Install 1 Package Total download size: 15 k Installed size: 25 k Downloading packages: epel-release-7-14.noarch.rpm | 15 kB 00:00:00 Running transaction check Running transaction test Transaction test succeeded Running transaction Installing : epel-release-7-14.noarch 1/1 Verifying : epel-release-7-14.noarch 1/1 Installed: epel-release.noarch 0:7-14 Complete! https://mirrors.tencent.com/pypi/simple cat: /etc/.productinfo: No such file or directory OS: el - 7 --2022-01-21 16:17:30-- http://dg2.bt.cn/install/pyenv/pyenv-el7-x64.tar.gz Resolving dg2.bt.cn (dg2.bt.cn)... 116.10.184.219, 240e:a5:4200:89::143 Connecting to dg2.bt.cn (dg2.bt.cn)|116.10.184.219|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 73258911 (70M) [application/octet-stream] Saving to: ‘/www/pyenv.tar.gz’ 100%[========================================================================================================>] 73,258,911 10.0MB/s in 7.2s 2022-01-21 16:17:37 (9.68 MB/s) - ‘/www/pyenv.tar.gz’ saved [73258911/73258911] Install python env... Python 3.7.9 --2022-01-21 16:17:40-- http://dg2.bt.cn/install/src/bt6.init Resolving dg2.bt.cn (dg2.bt.cn)... 116.10.184.219, 240e:a5:4200:89::143 Connecting to dg2.bt.cn (dg2.bt.cn)|116.10.184.219|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 9873 (9.6K) [application/octet-stream] Saving to: ‘/etc/init.d/bt’ 100%[========================================================================================================>] 9,873 --.-K/s in 0s 2022-01-21 16:17:40 (229 MB/s) - ‘/etc/init.d/bt’ saved [9873/9873] --2022-01-21 16:17:40-- http://dg2.bt.cn/install/public.sh Resolving dg2.bt.cn (dg2.bt.cn)... 42.157.129.47, 240e:a5:4200:89::143 Connecting to dg2.bt.cn (dg2.bt.cn)|42.157.129.47|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 4267 (4.2K) [application/octet-stream] Saving to: ‘/www/server/panel/install/public.sh’ 100%[========================================================================================================>] 4,267 --.-K/s in 0s 2022-01-21 16:17:40 (419 MB/s) - ‘/www/server/panel/install/public.sh’ saved [4267/4267] --2022-01-21 16:17:40-- http://dg2.bt.cn/install/src/panel6.zip Resolving dg2.bt.cn (dg2.bt.cn)... 42.157.129.47, 240e:a5:4200:89::143 Connecting to dg2.bt.cn (dg2.bt.cn)|42.157.129.47|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 9230063 (8.8M) [application/zip] Saving to: ‘panel.zip’ 100%[========================================================================================================>] 9,230,063 24.7MB/s in 0.4s 2022-01-21 16:17:40 (24.7 MB/s) - ‘panel.zip’ saved [9230063/9230063] --2022-01-21 16:17:40-- http://dg2.bt.cn/install/src/bt7.init Resolving dg2.bt.cn (dg2.bt.cn)... 42.157.129.47, 240e:a5:4200:89::143 Connecting to dg2.bt.cn (dg2.bt.cn)|42.157.129.47|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 10438 (10K) [application/octet-stream] Saving to: ‘/etc/init.d/bt’ 100%[========================================================================================================>] 10,438 --.-K/s in 0s 2022-01-21 16:17:41 (238 MB/s) - ‘/etc/init.d/bt’ saved [10438/10438] --2022-01-21 16:17:41-- http://dg2.bt.cn/install/src/bt7.init Resolving dg2.bt.cn (dg2.bt.cn)... 42.157.129.47, 240e:a5:4200:89::143 Connecting to dg2.bt.cn (dg2.bt.cn)|42.157.129.47|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 10438 (10K) [application/octet-stream] Saving to: ‘/www/server/panel/init.sh’ 100%[========================================================================================================>] 10,438 --.-K/s in 0.006s 2022-01-21 16:17:41 (1.68 MB/s) - ‘/www/server/panel/init.sh’ saved [10438/10438] --2022-01-21 16:17:41-- http://dg2.bt.cn/install/conf/softList.conf Resolving dg2.bt.cn (dg2.bt.cn)... 116.10.184.219, 240e:a5:4200:89::143 Connecting to dg2.bt.cn (dg2.bt.cn)|116.10.184.219|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 3287 (3.2K) [application/octet-stream] Saving to: ‘/www/server/panel/data/softList.conf’ 100%[========================================================================================================>] 3,287 --.-K/s in 0s 2022-01-21 16:17:41 (348 MB/s) - ‘/www/server/panel/data/softList.conf’ saved [3287/3287] Starting Bt-Panel.... done Starting Bt-Tasks... done username: t1c2mbup Stopping Bt-Tasks... done Stopping Bt-Panel... done Starting Bt-Panel.... done Starting Bt-Tasks... done Loaded plugins: fastestmirror, langpacks Repository epel is listed more than once in the configuration Loading mirror speeds from cached hostfile Package firewalld-0.6.3-13.el7_9.noarch already installed and latest version Nothing to do Created symlink from /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service to /usr/lib/systemd/system/firewalld.service. Created symlink from /etc/systemd/system/multi-user.target.wants/firewalld.service to /usr/lib/systemd/system/firewalld.service. success ================================================================== Congratulations! Installed successfully! ================================================================== 外网面板地址: http://110.40.150.131:8888/ac9859fb 内网面板地址: http://172.17.16.6:8888/ac9859fb username: t1c2mbup password: b6abed29 If you cannot access the panel, release the following panel port [8888] in the security group 若无法访问面板,请检查防火墙/安全组是否有放行面板[8888]端口 ================================================================== Time consumed: 1 Minute! [root@VM-16-6-centos wldata]# 根据安装成功的登录信息登录宝塔面板并重置相关登录信息
2022年01月21日
41 阅读
0 评论
0 点赞
2021-06-30
记一次根据经纬度计算距离的项目实践
近期公司接的公租房项目,在小程序模块中有用到离我最近的房源列表的功能,经过项目实践相关实现过程如下:功能演示PC后台配置小区位置:单击地图描点后打开如下图所示的描点对话框,描点选择后单击确定按钮返回经纬度信息:小程序中获取位置后调用接口显示距离:{lamp/}代码实现:管理后台描点Vue组件核心代码<template> <el-dialog width="95%" class="hkt-dlg-darkblue resource-modal" top="5vh" :title="title" :visible.sync="isShow" destroy-on-close :close-on-click-modal="false"> <div class="map-container" ref="mapContainer"></div> <div slot="footer" class="dialog-footer dialog-footer-wrap text-center"> <el-button @click="isShow = false">取 消</el-button> <el-button type="primary" :disabled="!latitude" @click="pickedHandler">{{'确 定'}}</el-button> </div> </el-dialog> </template> <script> export default { name: 'PickerQMapPositionModal', props: { show: { type: Boolean, default: false }, title: { type: String, default: '地图描点' }, dialogProps: { type: Object, reuired: true } }, data () { return { latitude: '', longitude: '' } }, computed: { isShow: { get () { return this.show }, set (val) { this.$emit('update:show', val) } } }, mounted () { this.init() }, methods: { init () { this.initMap() }, async initMap () { // if (!window.TMap) { // } await this.$hktUtils.loadJS('https://map.qq.com/api/gljs?v=1.exp&key=VQ6BZ-ZFSW6-H36SN-E7TZL-QZ6U6-O3F24') var center = new TMap.LatLng(28.239377,112.866161) //定义map变量,调用 TMap.Map() 构造函数创建地图 var map = new TMap.Map(this.$refs.mapContainer, { center: center,//设置地图中心点坐标 zoom: 15, //设置地图缩放级别 // pitch: 43.5, //设置俯仰角 // rotation: 45 //设置地图旋转角度 }); //初始化marker图层 var markerLayer = new TMap.MultiMarker({ id: 'marker-layer', map: map }); map.on("click", (evt) => { if(markerLayer.geometries && markerLayer.geometries.length > 0) { markerLayer.remove(markerLayer.geometries[0].id) } markerLayer.add({ position: evt.latLng }); console.log('evt.latLng', evt.latLng, evt) this.latitude = evt.latLng.lat.toFixed(7) this.longitude = evt.latLng.lng.toFixed(7) }) }, pickedHandler () { this.$emit('callback', { latitude: this.latitude, longitude: this.longitude }) this.isShow = false } } } </script> <style lang="scss" scoped> .map-container { width:100%; height:70vh; } </style> Java端计算距离核心代码package com.hkt.jianfa.rental.housing.manager.wx.util; /** * 根据经纬度计算距离的工具类 * * @author 朱治龙(i@zhuzhilong.cn) * @since 2021-06-29 16:22:18 */ public class DistanceUtil { //地球平均半径 private static final double EARTH_RADIUS = 6378137; //把经纬度转为度(°) private static double rad(double d) { return d * Math.PI / 180.0; } /** * 根据两点间经纬度坐标(double值),计算两点间距离,单位为米 * * @param lng1 第1个坐标的经度值 * @param lat1 第1个坐标的纬度值 * @param lng2 第2个坐标的经度值 * @param lat2 第2个坐标的纬度值 * @return */ public static double getDistance(double lng1, double lat1, double lng2, double lat2) { double radLat1 = rad(lat1); double radLat2 = rad(lat2); double a = radLat1 - radLat2; double b = rad(lng1) - rad(lng2); double s = 2 * Math.asin( Math.sqrt( Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2) ) ); s = s * EARTH_RADIUS; s = Math.round(s * 10000) / 10000; return s; } /** * 传入逗号分隔的两个经纬度的值计算巨鹿 * * @param position1 第一个经纬度 * @param position2 第二个经纬度 * @return */ public static double getDistance(String position1, String position2) { if (position1.contains(",") && position2.contains(",")) { String[] point1 = position1.split(","); String[] point2 = position2.split(","); return getDistance(Double.parseDouble(point1[1]), Double.parseDouble(point1[0]), Double.parseDouble(point2[1]), Double.parseDouble(point2[0])); } return 0; } /** * 将距离数值转换为前端显示所需的字符串信息 * * @param distance * @return */ public static String decodeDistance(double distance) { if (distance < 1000) { return "<1km"; } else if (distance / 1000 > 100) { return ">100km"; } else { return new Double(distance / 1000).intValue() + "km"; } } } 业务逻辑类中调用DistanceUtil方法计算距离:小程序中获取位置后,将经纬度信息传给后端计算距离及排序核心代码如下:uni.getLocation({ type: 'gcj02', success: (res) => { this.searchForm.sort = 'distance' this.searchForm.location = res.latitude + ',' + res.longitude this.getProjects() }, fail: (err) => { this.getProjects() } })
2021年06月30日
128 阅读
0 评论
2 点赞