白菜的开发日记1

emm。从16号早上出门前Initial Commit,到23号中午12点白菜正式上线,这一周的时间过的真是快啊……

https://github.com/Arsenolite/osubot/commits/master

43次commit之后,终于有时间坐下来写点开发日记了。严格的说这是第一个我写出来能用的程序啊(x

先介绍一下她吧,她是一个面向osu群的QQ机器人,具体用法可以参见github的readme(普通用户就两条指令你还写个readme真是凑不要脸x)

相对别的机器人的亮点就是她返回的是图片,样式主要由啊哇设计,我们从旁修改,当然最后是我实现的。(记得18号那天我画一行丢一个图在群里x

作为Java程序员,老本行就是折腾数据库,当然白菜也有这方面的功能。每天凌晨4点将所有登记了的玩家的数据爬取,然后存入,提供一个对比功能。

18号晚上写完用户名片绘制,19号上午写完凌晨的定时任务,20号做了一些sudo命令。

22号凌晨1点终于搞定BP绘制,然后白天研究maven打包,开通vps,关闭mysql占用空闲内存缓存等等。

22号晚上怕他突然提出来一个大BUG,之前数据库中我存入的是用户名,但是这样会导致无法识别改名玩家的问题。

于是晚上赶工重构代码+改表结构,由于发现操作系统的时区就是北京时间,而BP返回时间是根据API key对应玩家的国籍来的(也是北京时间),砍掉了所有的时区转换。

在vps上运行的时候查API失败率非常低,于是将api工具类中加上重试机制,砍掉了其他地方代码对网络错误的try-catch。

然后就是些小bug(之前改的时候没改彻底导致的各种问题),昨天中午正式上线,下午晚上研究了jsoup爬取网页指定元素,又把爬score rank改成了二分法。

顺手写了个自动欢迎新人,由于啊哇还没想好scorerank的呈现方式,把绘制scorerank的代码注释掉发布到了服务器上。

……挺后悔没有边开发边写博文的,现在有的遇到的bug都已经忘记了……

总之这个项目增强了我对SQL、前端(jsoup提供的是js的getElementById方法,和css的div.Class风格的选择器)的熟练度,顺便还达成了第一次使用WinServer系统的成就,勉强算是linux+WinServer都能干的运维?

复习了以前练手搞的多线程机制,IO流,各种包装类,日期处理,字符串处理之类的基础问题,初次尝试手写二分法(特别新鲜),可以说它弥补了我Java基础代码写得少的缺陷……

毕竟之前在单位搞的SSM框架更多是做填空题,把XML配好,crud和参数验证写好,甚至那个小项目都用不着组装复杂pojo……到处都是提供好的最佳实践(

……虽然这个项目用的也都是给好的类和方法,充其量起到了熟悉JDK本身的作用,毕竟不是和c一样要手写String的查找替换啥的(x

emm,白菜大概是不会弃坑的,以后尽量写出详细的遇到/解决bug的过程吧233

修改hexo-material主题的代码高亮风格

其实比较简单,做几个填空题而已。

根据主题官网:https://material.viosey.com/expert/ 的说明,
从 1.3.0 版本开始,您可以使用 hexo-prism-plugin 进行代码染色,具体文档请参阅 Hexo-Prism-Plugin 插件文档

转到插件文档:https://github.com/ele828/hexo-prism-plugin

运行npm安装,根据文档,在博客的_config.yml中加上:

1
2
3
4
prism_plugin:
mode: 'preprocess' # realtime/preprocess
theme: 'default'
line_number: false # default false

并且关闭自带的highlight即可。

另外所有主题的预览在这里:
https://github.com/PrismJS/prism-themes#available-themes

记一次代码重构

最近在编写Service层的时候,出现了这样的问题:

由于对数据合法性的要求比较高,光靠外键显然不能胜任,因此我全面抛弃外键,在Service层用大量代码校验数据的合法性。

于是就出现了这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 public Map editXXX(Entity entity){

Map result = new Map<>;
boolean flag = true;
if(entity.getparam1() is invaild){
flag = false;
logger.log;
result.put("msg","param1 is invaild");
}
...(param2)

result.put("result",flag);
if(!flag){
return result;
}

...mapper.Update(entity);
logger.log;
result.put("msg","success");

return result;
}

这里具体检测是否有效的代码比较复杂,例如update方法的id肯定不能是null,积分明细的uid必须在user表里查得到。

这样写着写着,写了五个Service之后开始觉得迷茫,懵逼,姜硬,可读性、可维护性差,耦合度高。

于是萌生出重构的想法,某人一语惊醒我把这些if独立成一个方法

直接独立肯定不行,我Service是面向接口写的,难道在接口中先定义好这样的check方法?

本来想将它抽出来做切面,但是毕竟这是业务逻辑的核心代码,不是日志和性能这些边缘功能,AOP印象里是改变不了方法某个局部变量的值的……

看了一眼之前整合shiro的util包,决定在util包下建一个subpackage叫checker,然后定义baseChecker类,里面定义几个常用方法。

其他具体的实体检查器就继承它,然后将这些checker对象注入Service类进行调用即可。

在定义baseChecker类的时候,由于对Java基础语法不够熟悉,我写出了这样的代码:

1
2
3
4
5
6
public class baseChecker {

public boolean checkNull(Object object,boolean flag){
return flag;
};
}

然后我发现这里需要使用的是泛型,而不是Object,而且泛型声明应该放在类里,正确的写法应该是:

1
2
3
4
5
6
public class baseChecker<T> {

public boolean checkNull(T t,boolean flag){
return flag;
};
}

然后子类这么写:

1
2
3
4
5
6
public class userChecker extends baseChecker {
@Override
public boolean checkNull(Object o , boolean flag) {
return flag;
}
}

路由器上ngrok设置的一点笔记

鸟枪换炮,用k3把残疾的k2给换了。

k3除了千兆LAN口外,最大的好处就是补上了U口,而且还是3.0的。既然能挂载硬盘,那这不就成了一个小型的NAS吗?

目前我是在出租屋里蹭着房东的网,本来想通过阿里云DDNS,完成在外网访问路由器(aria2随时上车)的目的。

作为二级路由,要实现DDNS,必须要一级路由开启端口转发,但是房东的路由器管理权并不在我手中。

一开始想爆破,找不到太好的爆破机器,自己又懒得写,试了几个弱口令失败后放弃了。看样子房东家里应该有略懂一点的人在吧。

于是只好另辟蹊径,采用ngrok客户端完成内网映射。

我使用的是https://ngrok.cc/ 这家免费的ngrok服务提供者,注册账户之后会提供一个Token,直接开通免费的HTTP隧道。

先随便填写一个前置域名,用户名和密码也随便填写,本地端口写了127.1:80,用于访问路由器的Web管理页面。

保存,点击进入修改,将域名改为自定义域名,我直接在我买的域名上开了个二级域名,并且按照网站的提示,把CNAME解析到了server.ngrok.cc。

连上k3,在客户端里把令牌填好,下面的通道和网站上设置保持一致,保存。


这里遇到另一个问题,aria2是分为本体+web控制台的,而web控制台连接的是本体提供的RPC接口,端口是6800。

而目前的隧道只有80端口转发,因此会出现连不上的情况。

这里有一个坑,表面看起来aria2用的是http://xxx:6800/jsonrpc的地址,但是网站和路由器中一定要开TCP隧道,本地端口写6800,服务器端口随便选一个没被占用的。

接下来修改aria2Web控制台配置,把RPC地址改成隧道的地址即可。同时为了安全,将aria2的rpc连接修改为需要Token,保持Web控制台和路由器中的设置一致即可。