目录
之前写了一个项目,可以后台生成自动插件,用户在前端付完款,提交到后台,会自动生成他的私有化插件,整个过程无人操控,全自动化,也为我赚到了人生第一桶金,但是,这第一桶金也太少了,哈哈哈。
今天再来,回顾一下写过的东西:
怎么有些东西,刚开始写都觉得自己逻辑写的可棒了,怎么再过半年一看,简直没法看了。。
哎,我好垃圾。什么时候才能写出有质量的代码?
一、时间相关
1.1 关于mysql 时间字段的设定。
类型 | 大小 (字节) | 范围 | 格式 | 用途 |
---|---|---|---|---|
DATE | 3 | 1000-01-01~9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | -838:59:59 ~838:59:59 | HH:MM:SS | 时间值或持续时间 |
YEAR | 1 | 1901~2155 | YYYY | 年份值 |
DATETIME | 8 | 1000-01-01 00:00:00~9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP | 4 | 1970-01-01 00:00:00~2037 年某时 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值,时间戳 |
TIMESTAMP
与 DATETIME
除了存储字节和支持的范围不同外,还有一个最大的区别是:
DATETIME
在存储日期数据时,按实际输入的格式存储,即输入什么就存储什么,与时区无关;- 而
TIMESTAMP
值的存储是以 UTC(世界标准时间)格式保存的,存储时对当前时区进行转换,检索时再转换回当前时区。即查询时,根据当前时区的不同,显示的时间值是不同的。
我们可以使用 DATE_FORMAT(date,format) 来格式化时间:
%a | 缩写星期名 |
---|---|
%b | 缩写月名 |
%c | 月,数值 |
%D | 带有英文前缀的月中的天 |
%d | 月的天,数值(00-31) |
%e | 月的天,数值(0-31) |
%f | 微秒 |
%H | 小时 (00-23) |
%h | 小时 (01-12) |
%I | 小时 (01-12) |
%i | 分钟,数值(00-59) |
%j | 年的天 (001-366) |
%k | 小时 (0-23) |
%l | 小时 (1-12) |
%M | 月名 |
%m | 月,数值(00-12) |
%p | AM 或 PM |
%r | 时间,12-小时(hh:mm:ss AM 或 PM) |
%S | 秒(00-59) |
%s | 秒(00-59) |
%T | 时间, 24-小时 (hh:mm:ss) |
%U | 周 (00-53) 星期日是一周的第一天 |
%u | 周 (00-53) 星期一是一周的第一天 |
%V | 周 (01-53) 星期日是一周的第一天,与 %X 使用 |
%v | 周 (01-53) 星期一是一周的第一天,与 %x 使用 |
%W | 星期名 |
%w | 周的天 (0=星期日, 6=星期六) |
%X | 年,其中的星期日是周的第一天,4 位,与 %V 使用 |
%x | 年,其中的星期一是周的第一天,4 位,与 %v 使用 |
%Y | 年,4 位 |
%y | 年,2 位 |
若字段使用了 dateTime(3),即时间精确到1毫秒,也就是0.001秒。
1.2 java中时间操作
在java中,我们对时间的转换如下:
jdk1.8之前,我们习惯这样格式化时间:
SimpleDateFormat sm =new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
String format = sm.format(new Date());
System.out.println(format);
// 获取时间戳
Long time = new Date().getTime();
Date date = new Date();
date.setTime(time);
SimpleDateFormat sm2 =new SimpleDateFormat("yyyy年MM月dd日");
// 时间戳格式化输出
String format2 = sm2.format(date);
System.out.println(format2);
// 格式化时间转为时间戳
Date parse = null;
try {
parse = sm2.parse("2020年05月20日");
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println(parse.getTime());
运行完上述代码,会得到如下结果:
而在jdk1.8版本以后,为我们提供了新的方式。
更为详细的教程:javaTime
1.2.1 LocalTime
System.out.println("当前时间(毫秒):"+Instant.now().toEpochMilli());;
System.out.println("当前时间戳(秒)"+Instant.now().getEpochSecond());;
System.out.println("最大时间:"+LocalTime.MAX);
// 1200*5秒 即5点:05:00
System.out.println("一天起始秒数转化为对应时间:"+LocalTime.ofSecondOfDay(60*60*5));
//20:32:12.930
System.out.println("现在时间:"+LocalTime.now());
// 精确到纳秒 05:20:52.520
System.out.println("设定时间:"+LocalTime.of(5,20,52,520000000));
LocalTime parse = LocalTime.parse("05:20");
// 05:20
System.out.println("字符串时间格式化:"+parse);
// 若该时间大于现在返回 1 若小于当前时间返回-1
System.out.println("比较大小:"+parse.compareTo(LocalTime.now()));
1.2.2 LocalDate
LocalDate localDate = LocalDate.now();
// 2020-10-30
System.out.println("现在日期:"+localDate);
// 日期拼接时间
LocalDateTime localDateTime = localDate.atTime(LocalTime.now());
// 2020-10-30T20:32:12.929
System.out.println("日期拼接时间:"+localDateTime.toString());
// 生成日期:2020-05-20
System.out.println("生成日期:"+ LocalDate.of(2020,5,20));
// 生成日期:2020-05-20
System.out.println("生成日期:"+LocalDate.of(2020, Month.MAY,20));
// 一年的第几天的日期:2020-01-02
System.out.println("一年的第几天的日期:"+LocalDate.ofYearDay(2020,2));;
// (以计算从1970-01-01以后开始的给定天数) 时间戳转化:1970-01-02
System.out.println("时间戳转化:"+LocalDate.ofEpochDay(1));
// 格式化字符串日期
LocalDate localDateFormat = LocalDate.parse("2020 05 20", DateTimeFormatter.ofPattern("yyyy MM dd"));
// 2020-05-20
System.out.println("转换为LocalDate对象:"+localDateFormat);
// 2020年05月20日
System.out.println("日期格式化为指定格式:"+localDateFormat.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")));
// WEDNESDAY
System.out.println("这天是一周的第几天"+localDateFormat.getDayOfWeek());
// 3
System.out.println("这天是一周的第几天"+localDateFormat.getDayOfWeek().getValue());
// 20
System.out.println("这天是一月的第几天"+localDateFormat.getDayOfMonth());
//141
System.out.println("一年的第几天:"+localDateFormat.get(ChronoField.DAY_OF_YEAR));
// 5
System.out.println("一年的第几个月:"+localDateFormat.get(ChronoField.MONTH_OF_YEAR));
// 2020-05-21
System.out.println("这一天的后一天:"+localDateFormat.plusDays(1));
// 2021-05-20
System.out.println("这一天的后一年:"+localDateFormat.plusYears(1));
// 2020-05-15
System.out.println("这一天的五天前的日期:"+localDateFormat.minusDays(5));
// 2020-06-14 ChronoUnit 实现了TemporalUnit
System.out.println("这一天的往后推25天:"+localDateFormat.plus(25, ChronoUnit.DAYS));
// false 当前时间是:2020.10.30 所以是false
System.out.println("这天是否在现在在之后:"+localDateFormat.isAfter(LocalDate.now()));
// 先比较月份 再比较日期 返回相差月份或者相差天数
System.out.println("这天和现在比较大小:"+localDateFormat.compareTo(LocalDate.now()));
下一个周日
System.out.println(""+localDateFormat.with(nextOrSame(DayOfWeek.SUNDAY)));
// 该月最后一天
System.out.println(""+localDateFormat.with(lastDayOfMonth()));
1.2.3 LocalDateTime
// 23:59:59.999999999
LocalDateTime time = LocalDateTime.now();
// 2020-10-30T20:32:12.930
System.out.println("直接获取日期时间:"+time.toString());
Calendar instance = Calendar.getInstance();
//Calendar calendar = new GregorianCalendar();
// 2020
System.out.println("年份:"+instance.get(Calendar.YEAR));
DateTimeFormatter dtf2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss:SSS");
String format = dtf2.format(time);
// 2020年10月30日 22:17:402
System.out.println("格式化输出"+format);
// 和SimpleDateFormat相比,DateTimeFormatter是线程安全的
// 格式化时间转为 LocalDateTime 进一步可调用
//toInstant方法 转为时间戳
LocalDateTime parse1 = LocalDateTime.parse("2020年05月20日 05:20:00:000", DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss:SSS"));
System.out.println(parse1);
// LocalDateTime 转换为 时间戳
System.out.println("时间戳:"+LocalDateTime.now().toInstant(ZoneOffset.ofHours(8)).toEpochMilli());
// 时间戳转化为LocalDateTime 格式化
Instant instant = Instant.ofEpochMilli(System.currentTimeMillis());
ZoneId zone = ZoneId.systemDefault();
LocalDateTime timeStamp = LocalDateTime.ofInstant(instant, zone);
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss:SSS");
System.out.println("时间戳格式化:"+dateTimeFormatter.format(timeStamp));
1.2.4 Date与LocalDateTime转换(常用)
下面这段代码摘抄来自:点此进入,方便引用,我复制过来了。
/**
* LocalDateTime转毫秒时间戳
* @param localDateTime LocalDateTime
* @return 时间戳
*/
public static Long localDateTimeToTimestamp(LocalDateTime localDateTime) {
try {
ZoneId zoneId = ZoneId.systemDefault();
Instant instant = localDateTime.atZone(zoneId).toInstant();
return instant.toEpochMilli();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 时间戳转LocalDateTime
* @param timestamp 时间戳
* @return LocalDateTime
*/
public static LocalDateTime timestampToLocalDateTime(long timestamp) {
try {
Instant instant = Instant.ofEpochMilli(timestamp);
ZoneId zone = ZoneId.systemDefault();
return LocalDateTime.ofInstant(instant, zone);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Date转LocalDateTime
* @param date Date
* @return LocalDateTime
*/
public static LocalDateTime dateToLocalDateTime(Date date) {
try {
Instant instant = date.toInstant();
ZoneId zoneId = ZoneId.systemDefault();
return instant.atZone(zoneId).toLocalDateTime();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* LocalDateTime转Date
* @param localDateTime LocalDateTime
* @return Date
*/
public static Date localDateTimeToDate(LocalDateTime localDateTime) {
try {
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zdt = localDateTime.atZone(zoneId);
return Date.from(zdt.toInstant());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
1.2.5 计算两个日期相差的年份、月份、日期差值
LocalDate localDate = LocalDate.of(2018,5,20);
// 当前时间为:2020/10/30
Period between = Period.between(LocalDateTime.now().toLocalDate(),
localDate);
// 天数相差10天所以 -10
System.out.println(between.getDays());
// 月份相差5天所以 -5
System.out.println(between.getMonths());
// 年份相差2年所以 -2
System.out.println(between.getYears());
1.2.6 计算两个时间所差的天数
LocalDate from = LocalDate.of(2017, 9, 1);
long day = LocalDate.now().toEpochDay() - from.toEpochDay();
System.out.println("距离当前多少日:" + day);
LocalDate date1 = LocalDate.of(2019, 10, 27);
//会改变date1
LocalDate date2 = date1.withMonth(9);//2019-09-27
System.out.println("修改日期原日期受影响:");
System.out.println("修改前:"+date1);
System.out.println("修改后:"+date2);
LocalDate date3 = LocalDate.of(2019, 10, 27);
//不会改变date3
LocalDate date4 = date1.with(TemporalAdjusters.firstDayOfMonth());
System.out.println("修改日期原日期不受影响:");
System.out.println("修改前:"+date3);
System.out.println("修改后:"+date4);
输出:
二、 int(4),int(11)的区别
首先,这两个所占的字节都为4个字节,最大的不同就是,int(4)在数字不足4位的时候,会左端补齐0,直到4个为止,而int(11)是补齐0到11个长度为止。
我刚开始很郁闷,为什么我设置了mysql也没有补齐呢?
最后才发现:定义字段需要添加:UNSIGNED ZEROFILL
,
这样就会补齐了。如:
create TABLE exam_pay (
`id` int(11) UNSIGNED ZEROFILL NOT NULL AUTO_INCREMENT,
`create_time` datetime(6) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM ;
接下来看看查询出来的id果然补0了。
三、 POST提交数据的方式
方式 | 形式 | springboot接收 |
---|---|---|
application/x-www-form-urlencoded | aa=123&bb=456 | 参数名字匹配即可 |
multipart/form-data | 常用于上传文件,会随机生成boundary值分割数据 | 略 |
application/json | {“app”:“曼拉精灵”,“version”:“1.0.0”} | @RequestBody注解即可,参数类型为JSONObject |
text/xml | 略 | 略 |
在爬虫或者使用其他HTTP请求库的时候,一定得指定你post的类型。最常用的就是application/x-www-form-urlencoded
和application/json
,而默认post类型则为:application/x-www-form-urlencoded
四、 springData Jpa 小坑
如下,在创建实体类的时候,一般我们不使用columnDefinition
属性的时候就没必要指定属性映射字段类型,如果使用了columnDefinition
使用comment
指定了字段注释,那么需要添加字段类型,即下面的varchar(20)
,不然在程序启动的时候会报错!建表失败,可怜我还找了好久…还是基础不扎实。。
@Column(nullable = false,columnDefinition = "varchar(20) comment '账户名' ")
private String username;
// 用户密码
@JsonIgnore
@Column(nullable = false,columnDefinition = "varchar(20) comment '账户密码'")
private String password;
五、关于参数的场景校验
场景一:
在用户登录的时候,需要两个基础参数,用户名、密码。
场景二:
在用户注册的时候,需要三个基础参数,用户名、密码、确认密码。
而在这两种场景下,我们不可能建立两个实体类吧!
所以这就出现了Hibernate为我们提供的参数场景校验,真的太强大了。
看下文:
首先,我们建立一个分组器,相当于,场景分离:
千万不要被我说的这个概念吓到…我就是随口一说,其实很简单。
package com.cri.miaosuyun.web.validator;
/**
* @description:
* @author: raven
* @create: 2020-10-28 16:02
**/
public class UserAccountValid {
// 登陆需要
public interface login{}
// 注册需要
public interface register {}
}
对,没错,场景分离器我们已经建立好了,上面的两个属性,很清楚了每个接口(相当于标记)的场景。
接下来 我们需要建立实体类,并在每个属性上面使用```groups ``属性标注场景接口:
package com.cri.miaosuyun.web.model.Vo;
import com.cri.miaosuyun.web.validator.UserAccountValid;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotNull;
/**
* @description:
* @author: raven
* @create: 2020-10-28 20:14
**/
@Data
public class ViewUser {
// 用户名
@NotNull(message = "账户名不能为空!",groups = {UserAccountValid.login.class,
UserAccountValid.register.class,
UserAccountValid.proxyAddLower.class})
@ApiModelProperty(value = "用户账号",required = true)
private String username;
@NotNull(message = "密码不能为空!",groups = {UserAccountValid.login.class,
UserAccountValid.register.class,
} )
@ApiModelProperty(value = "用户密码",required = true)
private String password;
@NotNull(message = "确认密码不能为空!",groups =
UserAccountValid.register.class,
})
@ApiModelProperty(hidden = true)
private String password2;
}
可以看到:
username(用户名) 我使用了两个场景接口。
groups = {UserAccountValid.login.class,
UserAccountValid.register.class,
}
即就是,登陆和注册都需要这个参数。
password(用户名) 也使用了两个场景接口。
groups = {UserAccountValid.login.class,
UserAccountValid.register.class,
}
即就是,登陆和注册都需要这个参数。
而看看确认密码属性:
只使用了一个注册场景接口,即就是注册用户的时候,才检测这个属性是否满足条件
groups = { UserAccountValid.register.class}
接下来,看看API:
@PostMapping("/register")
private Result<Object> registerUserAccount(
@Validated({UserAccountValid.register.class})ViewUser viewUser){}
@PostMapping("/login")
private Result<UserAccount> Login(@Validated({
UserAccountValid.login.class}) ViewUser viewUser){}
接下来,捕获异常,使用@ControllerAdvice
解决全局异常即可!
传递实体类到前端时,不想传入某属性,请使用:@JsonIgnore
实体类属性不想映射到数据表字段时,请使用:@Transient
六、kotlin 中线程如何调用协程语句?
没学过kotlin,因为QQ机器人需要使用这个框架,可太难受了,搞了半天都不知道,主要是Kotlin资料太少了。最后在群里大佬的解答才知道。
使用runBlocking块,即把协程语句放入runBlocking。
runBlocking{
qqSubject.sendMessage("您的线上考试已经完成,成绩100分")
}
kotlin 遍历数组:
// 相当于java的static
companion object{
val examList = JSONArray()
}
for ((index,e) in examList.withIndex()){
println("${index}---${e}")
}
七、 python使用requests下载图片
image_verity = self.session.get(url_config['imageUrl']).content
# 生成随机图片名字
img_name = ''.join(random.sample(string.ascii_letters + string.digits, 5))
img_path = ('../img/%s.png' % img_name)
with open(img_path, 'wb') as f:
f.write(image_verity)
f.flush()
f.close()
不得不说的是,在爬虫 这方面java真的太不行了。太罗嗦了,还是python舒服。
使用BeautifulSoup4解析文本:
post_login = BeautifulSoup(
self.session.post(url=url_config['url'],
data=login_param, headers=self.headers).text,
'html.parser')
self.user_info['name'] = post_login.find(attrs={"id": 'xhxm'}).text
八、selinium阻塞直到某结点出来继续执行
在使用自动化测试的时候,某些节点可能渲染的比较迟,代码继续进行要操作该节点,例如等待浏览器渲染好某个按钮,我们才去点击它。如下代码,阻塞,直到页面出现元素类型为input
,类型为text
,类为input-txt
才往下执行。
WebDriverWait(browser, 8, 0.5, ignored_exceptions=TimeoutException).until(
lambda x: x.find_element_by_css_selector("input[type='text'].input-txt"))
print('hello world!')
# 寻找输入框
usernameInput = browser.find_element_by_css_selector("input[type='text'].input-txt")
# 输入值
usernameInput.send_keys('hello')
# 模拟点击按钮
browser.find_element_by_css_selector("div.checkbox__mark").click()
# 执行js返回给py
browser.execute_script('return sessionStorage.getItem("sessionId");')
有时间决定去了解了解:Puppeteer,谷歌的亲儿子,也挺强的。
九、 实战微信小程序
建议开发微信小程序,先学习以下VUE,效果更佳。官方文档
app.json 里的pages,第一个默认即小程序启动默认页面。在这里直接可以创建新的页面或者组件。
"pages":[
"pages/index/index",
"pages/select/score/score",
"pages/table/table",
"pages/logs/logs",
"pages/center/center",
"pages/login/login"
]
注意各个阶段的生命周期:
created 组件实例化,但节点树还未导入,因此这时不能用setData
attached 节点树完成,可以用setData渲染节点,但无法操作节点
attached: function (options) {
console.log('进来')
//success 使用 箭头函数或者that
//var that = this;
wx.getStorage({
key: 'score',
success: (res)=> {
console.log('ok')
//被坑惨了 that...
this.setData({
score:res.data
})
},
})
}
这里要注意,使用this特别容易犯的错误,刚开始一直赋值不了数据,甚至让我毫无办法,后面才知道疏忽了。要知道,箭头函数是没有this指针的,所有这里面要么用that记录this,或者使用箭头函数。
// 跳转页面
wx.navigateTo({
url: '/pages/table/table',
})
//设置本地缓存
wx.setStorage({
key: "score",
data: res.data.data
})
//模态框
wx.showModal({
title: '温馨提示',
content: '服务器异常,请稍后再试',
showCancel: false
})
十、常用正则提取文本
待完善
十一、 java 发起各种HTTP请求
11.1 http篇
11.1 https篇
待完善