mirror of
https://github.com/201206030/novel-cloud.git
synced 2025-07-14 06:06:38 +00:00
上传通用模块/代码生成模块/网关基础服务/监控基础服务/用户微服务
This commit is contained in:
@ -0,0 +1,40 @@
|
||||
package com.java2nb.novel.common.base;
|
||||
|
||||
import com.java2nb.novel.common.bean.UserDetails;
|
||||
import com.java2nb.novel.common.utils.CookieUtil;
|
||||
import com.java2nb.novel.common.utils.JwtTokenUtil;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author 11797
|
||||
*/
|
||||
public class BaseController {
|
||||
|
||||
protected JwtTokenUtil jwtTokenUtil;
|
||||
|
||||
|
||||
protected String getToken(HttpServletRequest request){
|
||||
String token = CookieUtil.getCookie(request,"Authorization");
|
||||
if(token != null){
|
||||
return token;
|
||||
}
|
||||
return request.getHeader("Authorization");
|
||||
}
|
||||
|
||||
protected UserDetails getUserDetails(HttpServletRequest request) {
|
||||
String token = getToken(request);
|
||||
if(StringUtils.isBlank(token)){
|
||||
return null;
|
||||
}else{
|
||||
return jwtTokenUtil.getUserDetailsFromToken(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired
|
||||
public void setJwtTokenUtil(JwtTokenUtil jwtTokenUtil) {
|
||||
this.jwtTokenUtil = jwtTokenUtil;
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.java2nb.novel.common.bean;
|
||||
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 分装通用分页数据,接收PageHelper、SpringData等框架的分页数据,转换成通用的PageBean对象
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
* @param <T> 分页集合类型
|
||||
*/
|
||||
@Data
|
||||
public class PageBean<T> {
|
||||
|
||||
@ApiModelProperty(value = "页码")
|
||||
private Integer pageNum;
|
||||
@ApiModelProperty(value = "每页大小")
|
||||
private Integer pageSize;
|
||||
@ApiModelProperty(value = "总页数")
|
||||
private Integer totalPage;
|
||||
@ApiModelProperty(value = "总记录数")
|
||||
private Long total;
|
||||
@ApiModelProperty(value = "分页数据集合")
|
||||
private List<T> list;
|
||||
|
||||
|
||||
/**
|
||||
* 接收PageHelper分页后的list
|
||||
*/
|
||||
public PageBean(List<T> list){
|
||||
PageInfo<T> pageInfo = new PageInfo<>(list);
|
||||
this.totalPage = pageInfo.getPages();
|
||||
this.pageNum = pageInfo.getPageNum();
|
||||
this.pageSize = pageInfo.getPageSize();
|
||||
this.total = pageInfo.getTotal();
|
||||
this.list = pageInfo.getList();
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package com.java2nb.novel.common.bean;
|
||||
|
||||
|
||||
import com.java2nb.novel.common.enums.ResponseStatus;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 自定义Api响应结构
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
* @param <T> 响应数据类型
|
||||
*/
|
||||
@Data
|
||||
public class ResultBean<T>{
|
||||
|
||||
@ApiModelProperty(value = "响应状态码,200表示成功")
|
||||
private int code = ResponseStatus.OK.getCode();
|
||||
|
||||
/**
|
||||
* 响应消息
|
||||
* */
|
||||
@ApiModelProperty(value = "响应状态信息")
|
||||
private String msg = ResponseStatus.OK.getMsg();
|
||||
/**
|
||||
* 响应中的数据
|
||||
* */
|
||||
@ApiModelProperty(value = "响应数据")
|
||||
private T data;
|
||||
|
||||
private ResultBean() {
|
||||
|
||||
}
|
||||
|
||||
private ResultBean(ResponseStatus ResponseStatus) {
|
||||
this.code = ResponseStatus.getCode();;
|
||||
this.msg = ResponseStatus.getMsg();
|
||||
}
|
||||
|
||||
private ResultBean(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 业务处理成功,无数据返回
|
||||
* */
|
||||
public static ResultBean ok() {
|
||||
return new ResultBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务处理成功,有数据返回
|
||||
* */
|
||||
public static <T> ResultBean ok(T data) {
|
||||
return new ResultBean(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务处理失败
|
||||
* */
|
||||
public static ResultBean fail(ResponseStatus ResponseStatus) {
|
||||
return new ResultBean(ResponseStatus);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 系统错误
|
||||
* */
|
||||
public static ResultBean error() {
|
||||
return new ResultBean(ResponseStatus.ERROR);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package com.java2nb.novel.common.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author 11797
|
||||
*/
|
||||
@Data
|
||||
public class UserDetails {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String username;
|
||||
|
||||
private String nickName;
|
||||
}
|
68
novel-common/src/main/java/com/java2nb/novel/common/cache/CacheKey.java
vendored
Normal file
68
novel-common/src/main/java/com/java2nb/novel/common/cache/CacheKey.java
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
package com.java2nb.novel.common.cache;
|
||||
|
||||
/**
|
||||
* @author 11797
|
||||
*/
|
||||
public interface CacheKey {
|
||||
|
||||
/**
|
||||
* 首页小说设置
|
||||
* */
|
||||
String INDEX_BOOK_SETTINGS_KEY = "indexBookSettingsKey";
|
||||
|
||||
/**
|
||||
* 首页新闻
|
||||
* */
|
||||
String INDEX_NEWS_KEY = "indexNewsKey";
|
||||
|
||||
/**
|
||||
* 首页点击榜单
|
||||
* */
|
||||
String INDEX_CLICK_BANK_BOOK_KEY = "indexClickBankBookKey";
|
||||
|
||||
/**
|
||||
* 首页友情链接
|
||||
* */
|
||||
String INDEX_LINK_KEY = "indexLinkKey";
|
||||
|
||||
/**
|
||||
* 首页新书榜单
|
||||
* */
|
||||
String INDEX_NEW_BOOK_KEY = "indexNewBookKey";
|
||||
|
||||
|
||||
/**
|
||||
* 首页更新榜单
|
||||
* */
|
||||
String INDEX_UPDATE_BOOK_KEY = "indexUpdateBookKey";
|
||||
|
||||
/**
|
||||
* 模板目录保存key
|
||||
* */
|
||||
String TEMPLATE_DIR_KEY = "templateDirKey";;
|
||||
|
||||
/**
|
||||
* 正在运行的爬虫线程存储KEY前缀
|
||||
* */
|
||||
String RUNNING_CRAWL_THREAD_KEY_PREFIX = "runningCrawlTreadDataKeyPrefix";
|
||||
|
||||
/**
|
||||
* 上一次搜索引擎更新的时间
|
||||
* */
|
||||
String ES_LAST_UPDATE_TIME = "esLastUpdateTime";
|
||||
|
||||
/**
|
||||
* 搜索引擎转换锁
|
||||
* */
|
||||
String ES_TRANS_LOCK = "esTransLock";
|
||||
|
||||
/**
|
||||
* 上一次搜索引擎是否更新过小说点击量
|
||||
* */
|
||||
String ES_IS_UPDATE_VISIT = "esIsUpdateVisit";
|
||||
|
||||
/**
|
||||
* 累积的小说点击量
|
||||
* */
|
||||
String BOOK_ADD_VISIT_COUNT = "bookAddVisitCount";
|
||||
}
|
55
novel-common/src/main/java/com/java2nb/novel/common/cache/CacheService.java
vendored
Normal file
55
novel-common/src/main/java/com/java2nb/novel/common/cache/CacheService.java
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
package com.java2nb.novel.common.cache;
|
||||
|
||||
/**
|
||||
* @author 11797
|
||||
*/
|
||||
public interface CacheService {
|
||||
|
||||
/**
|
||||
* 根据key获取缓存的String类型数据
|
||||
*/
|
||||
String get(String key);
|
||||
|
||||
/**
|
||||
* 设置String类型的缓存
|
||||
*/
|
||||
void set(String key, String value);
|
||||
|
||||
/**
|
||||
* 设置一个有过期时间的String类型的缓存,单位秒
|
||||
*/
|
||||
void set(String key, String value, long timeout);
|
||||
|
||||
/**
|
||||
* 根据key获取缓存的Object类型数据
|
||||
*/
|
||||
Object getObject(String key);
|
||||
|
||||
/**
|
||||
* 设置Object类型的缓存
|
||||
*/
|
||||
void setObject(String key, Object value);
|
||||
|
||||
/**
|
||||
* 设置一个有过期时间的Object类型的缓存,单位秒
|
||||
*/
|
||||
void setObject(String key, Object value, long timeout);
|
||||
|
||||
/**
|
||||
* 根据key删除缓存的数据
|
||||
*/
|
||||
void del(String key);
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否存在一个key
|
||||
* */
|
||||
boolean contains(String key);
|
||||
|
||||
/**
|
||||
* 设置key过期时间
|
||||
* */
|
||||
void expire(String key, long timeout);
|
||||
|
||||
|
||||
}
|
73
novel-common/src/main/java/com/java2nb/novel/common/cache/impl/RedisServiceImpl.java
vendored
Normal file
73
novel-common/src/main/java/com/java2nb/novel/common/cache/impl/RedisServiceImpl.java
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
package com.java2nb.novel.common.cache.impl;
|
||||
|
||||
import com.java2nb.novel.common.cache.CacheService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author xxy
|
||||
*/
|
||||
@ConditionalOnProperty(prefix = "cache", name = "type", havingValue = "redis")
|
||||
@RequiredArgsConstructor
|
||||
@Service
|
||||
public class RedisServiceImpl implements CacheService {
|
||||
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
private final RedisTemplate<Object, Object> redisTemplate;
|
||||
|
||||
|
||||
@Override
|
||||
public String get(String key) {
|
||||
return stringRedisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, String value) {
|
||||
stringRedisTemplate.opsForValue().set(key,value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, String value, long timeout) {
|
||||
stringRedisTemplate.opsForValue().set(key,value,timeout, TimeUnit.SECONDS);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getObject(String key) {
|
||||
return redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(String key, Object value) {
|
||||
redisTemplate.opsForValue().set(key,value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setObject(String key, Object value, long timeout) {
|
||||
redisTemplate.opsForValue().set(key,value,timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void del(String key) {
|
||||
redisTemplate.delete(key);
|
||||
stringRedisTemplate.delete(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(String key) {
|
||||
return redisTemplate.hasKey(key) || stringRedisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expire(String key, long timeout) {
|
||||
redisTemplate.expire(key,timeout, TimeUnit.SECONDS);
|
||||
stringRedisTemplate.expire(key,timeout, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package com.java2nb.novel.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import springfox.documentation.builders.ApiInfoBuilder;
|
||||
import springfox.documentation.builders.PathSelectors;
|
||||
import springfox.documentation.builders.RequestHandlerSelectors;
|
||||
import springfox.documentation.service.ApiInfo;
|
||||
import springfox.documentation.spi.DocumentationType;
|
||||
import springfox.documentation.spring.web.plugins.Docket;
|
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2;
|
||||
|
||||
/**
|
||||
* Swagger2API文档的配置
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/27
|
||||
*/
|
||||
@Configuration
|
||||
@EnableSwagger2
|
||||
public class Swagger2Config {
|
||||
@Bean
|
||||
public Docket createRestApi(){
|
||||
return new Docket(DocumentationType.SWAGGER_2)
|
||||
.apiInfo(apiInfo())
|
||||
.select()
|
||||
//为当前包下controller生成API文档
|
||||
.apis(RequestHandlerSelectors.basePackage("com.java2nb.novel"))
|
||||
//为有@Api注解的Controller生成API文档
|
||||
//.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
|
||||
//为有@ApiOperation注解的方法生成API文档
|
||||
//.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
|
||||
.paths(PathSelectors.any())
|
||||
.build();
|
||||
}
|
||||
|
||||
private ApiInfo apiInfo() {
|
||||
return new ApiInfoBuilder()
|
||||
.title("novel-cloud")
|
||||
.description("小说精品屋-cloud接口文档")
|
||||
.contact("xxy")
|
||||
.version("1.0.0")
|
||||
.build();
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.java2nb.novel.common.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author 11797
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public enum ResponseStatus {
|
||||
|
||||
/**
|
||||
* 请求成功
|
||||
* */
|
||||
OK(200,"SUCCESS"),
|
||||
|
||||
/**
|
||||
* 服务器异常
|
||||
* */
|
||||
ERROR(500,"未知异常,请联系管理员!"),
|
||||
|
||||
/**
|
||||
* 参数错误
|
||||
* */
|
||||
PARAM_ERROR(400,"非法参数!"),
|
||||
|
||||
/**
|
||||
* 拒绝访问
|
||||
* */
|
||||
FORBIDDEN(403,"拒绝访问!"),
|
||||
|
||||
|
||||
/**
|
||||
* 用户相关错误
|
||||
* */
|
||||
NO_LOGIN(1001, "未登录或登陆失效!"),
|
||||
VEL_CODE_ERROR(1002, "验证码错误!"),
|
||||
USERNAME_EXIST(1003,"该手机号已注册!"),
|
||||
USERNAME_PASS_ERROR(1004,"手机号或密码错误!"),
|
||||
TWO_PASSWORD_DIFF(1005, "两次输入的新密码不匹配!"),
|
||||
OLD_PASSWORD_ERROR(1006, "旧密码不匹配!"),
|
||||
USER_NO_BALANCE(1007, "用户余额不足"),
|
||||
|
||||
/**
|
||||
* 评论相关错误
|
||||
* */
|
||||
HAS_COMMENTS(3001, "已评价过该书籍!"),
|
||||
|
||||
/**
|
||||
* 作者相关错误
|
||||
* */
|
||||
INVITE_CODE_INVALID(4001, "邀请码无效!"),
|
||||
AUTHOR_STATUS_FORBIDDEN(4002, "作者状态异常,暂不能管理小说!")
|
||||
, BOOKNAME_EXISTS(4003,"已发布过同名小说!")
|
||||
|
||||
,
|
||||
|
||||
|
||||
/**
|
||||
* 其他通用错误
|
||||
* */
|
||||
PASSWORD_ERROR(88001,"密码错误!");
|
||||
|
||||
private int code;
|
||||
private String msg;
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package com.java2nb.novel.common.exception;
|
||||
|
||||
import com.java2nb.novel.common.enums.ResponseStatus;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 自定义业务异常,用于处理用户请求时,业务错误时抛出
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
@Data
|
||||
public class BusinessException extends RuntimeException {
|
||||
|
||||
private ResponseStatus resStatus;
|
||||
|
||||
public BusinessException(ResponseStatus resStatus) {
|
||||
//不调用父类Throwable的fillInStackTrace()方法生成栈追踪信息,提高应用性能
|
||||
//构造器之间的调用必须在第一行
|
||||
super(resStatus.getMsg(), null, false, false);
|
||||
this.resStatus = resStatus;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.java2nb.novel.common.exception;
|
||||
|
||||
import com.java2nb.novel.common.bean.ResultBean;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.ControllerAdvice;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* 通用的异常处理器
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
* */
|
||||
@Slf4j
|
||||
@ControllerAdvice
|
||||
@ResponseBody
|
||||
public class CommonExceptionHandler {
|
||||
|
||||
/**
|
||||
* 处理业务异常
|
||||
* */
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ResultBean handlerBusinessException(BusinessException e){
|
||||
log.error(e.getMessage(),e);
|
||||
return ResultBean.fail(e.getResStatus());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 处理系统异常
|
||||
* */
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResultBean handlerException(Exception e){
|
||||
log.error(e.getMessage(),e);
|
||||
return ResultBean.error();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bean操作工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class BeanUtil {
|
||||
|
||||
/**
|
||||
* 复制集合对象属性值,生成新类型集合
|
||||
* @param source 源集合
|
||||
* @param targetClass 目标集合类型
|
||||
* @return 新集合
|
||||
* */
|
||||
@SneakyThrows
|
||||
public static <T> List<T> copyList(List source,Class<T> targetClass){
|
||||
List<T> target = new ArrayList<>(source.size());
|
||||
for( int i = 0 ; i < source.size() ; i++){
|
||||
Object sourceItem = source.get(i);
|
||||
T targetItem = targetClass.newInstance();
|
||||
BeanUtils.copyProperties(sourceItem,targetItem);
|
||||
target.add(targetItem);
|
||||
}
|
||||
return target;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import javax.servlet.http.Cookie;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* @author Administrator
|
||||
*/
|
||||
public class CookieUtil {
|
||||
|
||||
public static String getCookie(HttpServletRequest request,String key){
|
||||
Cookie[] cookies = request.getCookies();
|
||||
if(cookies != null) {
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equals(key)) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static void setCookie(HttpServletResponse response,String key,String value){
|
||||
Cookie cookie = new Cookie(key, value);
|
||||
cookie.setPath("/");
|
||||
response.addCookie(cookie);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.apache.http.client.utils.DateUtils;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.Date;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 文件操作工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
@Slf4j
|
||||
public class FileUtil {
|
||||
|
||||
/**
|
||||
* 网络图片转本地
|
||||
* */
|
||||
public static String network2Local(String picSrc,String picSavePath,String visitPrefix) {
|
||||
InputStream input = null;
|
||||
OutputStream out = null;
|
||||
try {
|
||||
//本地图片保存
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
HttpEntity<String> requestEntity = new HttpEntity<>(null, headers);
|
||||
ResponseEntity<Resource> resEntity = RestTemplateUtil.getInstance(Charsets.ISO_8859_1.name()).exchange(picSrc, HttpMethod.GET, requestEntity, Resource.class);
|
||||
input = Objects.requireNonNull(resEntity.getBody()).getInputStream();
|
||||
Date currentDate = new Date();
|
||||
picSrc = visitPrefix + DateUtils.formatDate(currentDate, "yyyy") + "/" + DateUtils.formatDate(currentDate, "MM") + "/" + DateUtils.formatDate(currentDate, "dd") + "/"
|
||||
+ UUIDUtil.getUUID32()
|
||||
+ picSrc.substring(picSrc.lastIndexOf("."));
|
||||
File picFile = new File(picSavePath + picSrc);
|
||||
File parentFile = picFile.getParentFile();
|
||||
if (!parentFile.exists()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
out = new FileOutputStream(picFile);
|
||||
byte[] b = new byte[4096];
|
||||
for (int n; (n = input.read(b)) != -1; ) {
|
||||
out.write(b, 0, n);
|
||||
}
|
||||
|
||||
}catch (Exception e){
|
||||
log.error(e.getMessage(),e);
|
||||
|
||||
picSrc = "/images/default.gif";
|
||||
}finally {
|
||||
if(input != null){
|
||||
try {
|
||||
input.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
}finally {
|
||||
if(out != null){
|
||||
try {
|
||||
out.close();
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage(),e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
return picSrc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* Http请求工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class HttpUtil {
|
||||
|
||||
private static RestTemplate restTemplate = RestTemplateUtil.getInstance("utf-8");
|
||||
|
||||
|
||||
public static String getByHttpClient(String url) {
|
||||
try {
|
||||
|
||||
ResponseEntity<String> forEntity = restTemplate.getForEntity(url, String.class);
|
||||
if (forEntity.getStatusCode() == HttpStatus.OK) {
|
||||
return forEntity.getBody();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,163 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
|
||||
/**
|
||||
* <p>名称:IdWorker.java</p>
|
||||
* <p>描述:分布式自增长ID</p>
|
||||
* <pre>
|
||||
* Twitter的 Snowflake JAVA实现方案
|
||||
* </pre>
|
||||
* 核心代码为其IdWorker这个类实现,其原理结构如下,我分别用一个0表示一位,用—分割开部分的作用:
|
||||
* 1||0---0000000000 0000000000 0000000000 0000000000 0 --- 00000 ---00000 ---000000000000
|
||||
* 在上面的字符串中,第一位为未使用(实际上也可作为long的符号位),接下来的41位为毫秒级时间,
|
||||
* 然后5位datacenter标识位,5位机器ID(并不算标识符,实际是为线程标识),
|
||||
* 然后12位该毫秒内的当前毫秒内的计数,加起来刚好64位,为一个Long型。
|
||||
* 这样的好处是,整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和机器ID作区分),
|
||||
* 并且效率较高,经测试,snowflake每秒能够产生26万ID左右,完全满足需要。
|
||||
* <p>
|
||||
* 64位ID (42(毫秒)+5(机器ID)+5(业务编码)+12(重复累加))
|
||||
*
|
||||
*/
|
||||
public class IdWorker {
|
||||
// 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动)
|
||||
private final static long twepoch = 1288834974657L;
|
||||
// 机器标识位数
|
||||
private final static long workerIdBits = 5L;
|
||||
// 数据中心标识位数
|
||||
private final static long datacenterIdBits = 5L;
|
||||
// 机器ID最大值
|
||||
private final static long maxWorkerId = -1L ^ (-1L << workerIdBits);
|
||||
// 数据中心ID最大值
|
||||
private final static long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
|
||||
// 毫秒内自增位
|
||||
private final static long sequenceBits = 12L;
|
||||
// 机器ID偏左移12位
|
||||
private final static long workerIdShift = sequenceBits;
|
||||
// 数据中心ID左移17位
|
||||
private final static long datacenterIdShift = sequenceBits + workerIdBits;
|
||||
// 时间毫秒左移22位
|
||||
private final static long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
|
||||
|
||||
private final static long sequenceMask = -1L ^ (-1L << sequenceBits);
|
||||
/* 上次生产id时间戳 */
|
||||
private static long lastTimestamp = -1L;
|
||||
// 0,并发控制
|
||||
private long sequence = 0L;
|
||||
|
||||
private final long workerId;
|
||||
// 数据标识id部分
|
||||
private final long datacenterId;
|
||||
|
||||
public IdWorker(){
|
||||
this.datacenterId = getDatacenterId(maxDatacenterId);
|
||||
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
|
||||
}
|
||||
/**
|
||||
* @param workerId
|
||||
* 工作机器ID
|
||||
* @param datacenterId
|
||||
* 序列号
|
||||
*/
|
||||
public IdWorker(long workerId, long datacenterId) {
|
||||
if (workerId > maxWorkerId || workerId < 0) {
|
||||
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
|
||||
}
|
||||
if (datacenterId > maxDatacenterId || datacenterId < 0) {
|
||||
throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
|
||||
}
|
||||
this.workerId = workerId;
|
||||
this.datacenterId = datacenterId;
|
||||
}
|
||||
/**
|
||||
* 获取下一个ID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public synchronized long nextId() {
|
||||
long timestamp = timeGen();
|
||||
if (timestamp < lastTimestamp) {
|
||||
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
|
||||
}
|
||||
|
||||
if (lastTimestamp == timestamp) {
|
||||
// 当前毫秒内,则+1
|
||||
sequence = (sequence + 1) & sequenceMask;
|
||||
if (sequence == 0) {
|
||||
// 当前毫秒内计数满了,则等待下一秒
|
||||
timestamp = tilNextMillis(lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
sequence = 0L;
|
||||
}
|
||||
lastTimestamp = timestamp;
|
||||
// ID偏移组合生成最终的ID,并返回ID
|
||||
long nextId = ((timestamp - twepoch) << timestampLeftShift)
|
||||
| (datacenterId << datacenterIdShift)
|
||||
| (workerId << workerIdShift) | sequence;
|
||||
|
||||
return nextId;
|
||||
}
|
||||
|
||||
private long tilNextMillis(final long lastTimestamp) {
|
||||
long timestamp = this.timeGen();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
timestamp = this.timeGen();
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
private long timeGen() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 获取 maxWorkerId
|
||||
* </p>
|
||||
*/
|
||||
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
|
||||
StringBuffer mpid = new StringBuffer();
|
||||
mpid.append(datacenterId);
|
||||
String name = ManagementFactory.getRuntimeMXBean().getName();
|
||||
if (!name.isEmpty()) {
|
||||
/*
|
||||
* GET jvmPid
|
||||
*/
|
||||
mpid.append(name.split("@")[0]);
|
||||
}
|
||||
/*
|
||||
* MAC + PID 的 hashcode 获取16个低位
|
||||
*/
|
||||
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 数据标识id部分
|
||||
* </p>
|
||||
*/
|
||||
protected static long getDatacenterId(long maxDatacenterId) {
|
||||
long id = 0L;
|
||||
try {
|
||||
InetAddress ip = InetAddress.getLocalHost();
|
||||
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
|
||||
if (network == null) {
|
||||
id = 1L;
|
||||
} else {
|
||||
byte[] mac = network.getHardwareAddress();
|
||||
id = ((0x000000FF & (long) mac[mac.length - 1])
|
||||
| (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
|
||||
id = id % (maxDatacenterId + 1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println(" getDatacenterId: " + e.getMessage());
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* Ip相关工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class IpUtil {
|
||||
|
||||
/**
|
||||
* 获取真实IP
|
||||
* @param request 请求体
|
||||
* @return 真实IP
|
||||
*/
|
||||
public static String getRealIp(HttpServletRequest request) {
|
||||
// 这个一般是Nginx反向代理设置的参数
|
||||
String ip = request.getHeader("X-Real-IP");
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("X-Forwarded-For");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getHeader("WL-Proxy-Client-IP");
|
||||
}
|
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
|
||||
ip = request.getRemoteAddr();
|
||||
}
|
||||
// 处理多IP的情况(只取第一个IP)
|
||||
if (ip != null && ip.contains(",")) {
|
||||
String[] ipArray = ip.split(",");
|
||||
ip = ipArray[0];
|
||||
}
|
||||
return ip;
|
||||
}
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.java2nb.novel.common.bean.UserDetails;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author 11797
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class JwtTokenUtil {
|
||||
|
||||
private static final String CLAIM_KEY_USERNAME = "sub";
|
||||
private static final String CLAIM_KEY_CREATED = "created";
|
||||
@Value("${jwt.secret}")
|
||||
private String secret;
|
||||
@Value("${jwt.expiration}")
|
||||
private Long expiration;
|
||||
|
||||
/**
|
||||
* 根据负责生成JWT的token
|
||||
*/
|
||||
private String generateToken(Map<String, Object> claims) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setExpiration(generateExpirationDate())
|
||||
.signWith(SignatureAlgorithm.HS512, secret)
|
||||
.compact();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从token中获取JWT中的负载
|
||||
*/
|
||||
private Claims getClaimsFromToken(String token) {
|
||||
Claims claims = null;
|
||||
try {
|
||||
claims = Jwts.parser()
|
||||
.setSigningKey(secret)
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
} catch (Exception e) {
|
||||
log.info("JWT格式验证失败:{}",token);
|
||||
}
|
||||
return claims;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成token的过期时间
|
||||
*/
|
||||
private Date generateExpirationDate() {
|
||||
return new Date(System.currentTimeMillis() + expiration * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从token中获取用户信息
|
||||
*/
|
||||
public UserDetails getUserDetailsFromToken(String token) {
|
||||
if(isTokenExpired(token)){
|
||||
return null;
|
||||
}
|
||||
UserDetails userDetail;
|
||||
try {
|
||||
Claims claims = getClaimsFromToken(token);
|
||||
userDetail = new ObjectMapper().readValue(claims.getSubject(),UserDetails.class);
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage(),e);
|
||||
userDetail = null;
|
||||
}
|
||||
return userDetail;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断token是否已经失效
|
||||
*/
|
||||
private boolean isTokenExpired(String token) {
|
||||
Date expiredDate = getExpiredDateFromToken(token);
|
||||
return expiredDate.before(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从token中获取过期时间
|
||||
*/
|
||||
private Date getExpiredDateFromToken(String token) {
|
||||
Claims claims = getClaimsFromToken(token);
|
||||
return claims.getExpiration();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户信息生成token
|
||||
*/
|
||||
@SneakyThrows
|
||||
public String generateToken(UserDetails userDetails) {
|
||||
Map<String, Object> claims = new HashMap<>(2);
|
||||
claims.put(CLAIM_KEY_USERNAME, new ObjectMapper().writeValueAsString(userDetails));
|
||||
claims.put(CLAIM_KEY_CREATED, new Date());
|
||||
return generateToken(claims);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断token是否可以被刷新
|
||||
*/
|
||||
public boolean canRefresh(String token) {
|
||||
return !isTokenExpired(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新token
|
||||
*/
|
||||
public String refreshToken(String token) {
|
||||
Claims claims = getClaimsFromToken(token);
|
||||
claims.put(CLAIM_KEY_CREATED, new Date());
|
||||
return generateToken(claims);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import sun.misc.BASE64Encoder;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
/**
|
||||
* MD5工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class MD5Util {
|
||||
|
||||
private static final String DEFAUL_CHARSET = "utf-8" ;
|
||||
|
||||
private static String byteArrayToHexString(byte[] b) {
|
||||
StringBuffer resultSb = new StringBuffer();
|
||||
for (int i = 0; i < b.length; i++) {
|
||||
resultSb.append(byteToHexString(b[i]));
|
||||
}
|
||||
|
||||
return resultSb.toString();
|
||||
}
|
||||
|
||||
private static String byteToHexString(byte b) {
|
||||
int n = b;
|
||||
if (n < 0) {
|
||||
n += 256;
|
||||
}
|
||||
int d1 = n / 16;
|
||||
int d2 = n % 16;
|
||||
return HEX_DIGITS[d1] + HEX_DIGITS[d2];
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String MD5Encode(String origin,String charsetname) {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
if (charsetname == null || "".equals(charsetname)) {
|
||||
return byteArrayToHexString(md.digest(origin
|
||||
.getBytes()));
|
||||
} else {
|
||||
return byteArrayToHexString(md.digest(origin
|
||||
.getBytes(charsetname)));
|
||||
}
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
public static String MD5New(String str) {
|
||||
//首先利用MD5算法将密码加密,变成等长字节
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] b1 = md.digest(str.getBytes());
|
||||
//将等长字节利用Base64算法转换成字符串
|
||||
BASE64Encoder encoder = new BASE64Encoder();
|
||||
return encoder.encode(b1);
|
||||
}
|
||||
|
||||
private static final String[] HEX_DIGITS = {"0", "1", "2", "3", "4", "5",
|
||||
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,97 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 随机生成小说信息工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class RandomBookInfoUtil {
|
||||
|
||||
/**
|
||||
* 根据评分获取访问人数
|
||||
* */
|
||||
public static Long getVisitCountByScore(Float score){
|
||||
Long visitCount ;
|
||||
|
||||
if(score > 9){
|
||||
visitCount = 100000 + new Random(100000).nextLong();
|
||||
}else if(score > 8){
|
||||
visitCount = 10000 + new Random(10000).nextLong();
|
||||
}else if(score > 7){
|
||||
visitCount = 1000 + new Random(1000).nextLong();
|
||||
}else if(score > 6){
|
||||
visitCount = 100 + new Random(100).nextLong();
|
||||
}else{
|
||||
visitCount = new Random(100).nextLong();
|
||||
}
|
||||
|
||||
|
||||
return visitCount;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据访问人数获取评分
|
||||
* */
|
||||
public static Float getScoreByVisitCount(Long visitCount){
|
||||
Float score;
|
||||
if(visitCount>100000) {
|
||||
score = 8.9f;
|
||||
}else if(visitCount>10000){
|
||||
score = 8.0f+(visitCount/10000)*0.1f;
|
||||
}else if(visitCount>1000){
|
||||
score = 7.0f+(visitCount/1000)*0.1f;
|
||||
}else if(visitCount>100){
|
||||
score = 6.0f+(visitCount/100)*0.1f;
|
||||
}else{
|
||||
score = 6.0f;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
/**
|
||||
* 获取分类名
|
||||
* */
|
||||
public static String getCatNameById(Integer catId) {
|
||||
String catName = "其他";
|
||||
|
||||
switch (catId) {
|
||||
case 1: {
|
||||
catName = "玄幻奇幻";
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
catName = "武侠仙侠";
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
catName = "都市言情";
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
catName = "历史军事";
|
||||
break;
|
||||
}
|
||||
case 5: {
|
||||
catName = "科幻灵异";
|
||||
break;
|
||||
}
|
||||
case 6: {
|
||||
catName = "网游竞技";
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
catName = "女生频道";
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return catName;
|
||||
}
|
||||
}
|
@ -0,0 +1,141 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import com.java2nb.novel.common.cache.CacheService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* 图片验证码工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class RandomValidateCodeUtil {
|
||||
|
||||
|
||||
/**
|
||||
* 放到session中的key
|
||||
* */
|
||||
public static final String RANDOM_CODE_KEY = "randomValidateCodeKey";
|
||||
/**
|
||||
* 随机产生只有数字的字符串 private String
|
||||
* */
|
||||
private String randString = "0123456789";
|
||||
/**
|
||||
* 图片宽
|
||||
* */
|
||||
private int width = 100;
|
||||
/**
|
||||
* 图片高
|
||||
* */
|
||||
private int height = 38;
|
||||
/**
|
||||
* 干扰线数量
|
||||
* */
|
||||
private int lineSize = 40;
|
||||
/**
|
||||
* 随机产生字符数量
|
||||
* */
|
||||
private int stringNum = 4;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RandomValidateCodeUtil.class);
|
||||
|
||||
private Random random = new Random();
|
||||
|
||||
/**
|
||||
* 获得字体
|
||||
*/
|
||||
private Font getFont() {
|
||||
return new Font("Fixedsys", Font.ROMAN_BASELINE, 23);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得颜色
|
||||
*/
|
||||
private Color getRandColor(int fc, int bc) {
|
||||
if (fc > 255) {
|
||||
fc = 255;
|
||||
}
|
||||
if (bc > 255) {
|
||||
bc = 255;
|
||||
}
|
||||
int r = fc + random.nextInt(bc - fc - 16);
|
||||
int g = fc + random.nextInt(bc - fc - 14);
|
||||
int b = fc + random.nextInt(bc - fc - 18);
|
||||
return new Color(r, g, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成随机图片
|
||||
*/
|
||||
public void getRandcode(CacheService cacheService, HttpServletResponse response) {
|
||||
// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
|
||||
// 产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作
|
||||
Graphics g = image.getGraphics();
|
||||
//图片大小
|
||||
g.fillRect(0, 0, width, height);
|
||||
//字体大小
|
||||
//字体颜色
|
||||
g.setColor(new Color(204,204,204));
|
||||
// 绘制干扰线
|
||||
for (int i = 0; i <= lineSize; i++) {
|
||||
drowLine(g);
|
||||
}
|
||||
// 绘制随机字符
|
||||
String randomString = "";
|
||||
for (int i = 1; i <= stringNum; i++) {
|
||||
randomString = drowString(g, randomString, i);
|
||||
}
|
||||
logger.info(randomString);
|
||||
//将生成的随机字符串保存到缓存中
|
||||
cacheService.set(RANDOM_CODE_KEY,randomString,60*5);
|
||||
g.dispose();
|
||||
try {
|
||||
// 将内存中的图片通过流动形式输出到客户端
|
||||
ImageIO.write(image, "JPEG", response.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
logger.error("将内存中的图片通过流动形式输出到客户端失败>>>> ", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制字符串
|
||||
*/
|
||||
private String drowString(Graphics g, String randomString, int i) {
|
||||
g.setFont(getFont());
|
||||
g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
|
||||
.nextInt(121)));
|
||||
String rand = String.valueOf(getRandomString(random.nextInt(randString
|
||||
.length())));
|
||||
randomString += rand;
|
||||
g.translate(random.nextInt(3), random.nextInt(3));
|
||||
g.drawString(rand, 13 * i, 23);
|
||||
return randomString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制干扰线
|
||||
*/
|
||||
private void drowLine(Graphics g) {
|
||||
int x = random.nextInt(width);
|
||||
int y = random.nextInt(height);
|
||||
int xl = random.nextInt(13);
|
||||
int yl = random.nextInt(15);
|
||||
g.drawLine(x, y, x + xl, y + yl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取随机的字符
|
||||
*/
|
||||
public String getRandomString(int num) {
|
||||
return String.valueOf(randString.charAt(num));
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.http.config.Registry;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.TrustStrategy;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* RestTemplate工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class RestTemplateUtil {
|
||||
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
public static RestTemplate getInstance(String charset) {
|
||||
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
|
||||
|
||||
//忽略证书
|
||||
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
|
||||
.loadTrustMaterial(null, acceptingTrustStrategy)
|
||||
.build();
|
||||
|
||||
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
|
||||
|
||||
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
|
||||
.register("http", PlainConnectionSocketFactory.getSocketFactory())
|
||||
.register("https", csf)
|
||||
.build();
|
||||
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
|
||||
|
||||
//连接池的最大连接数,0代表不限;如果取0,需要考虑连接泄露导致系统崩溃的后果
|
||||
connectionManager.setMaxTotal(1000);
|
||||
//每个路由的最大连接数,如果只调用一个地址,可以将其设置为最大连接数
|
||||
connectionManager.setDefaultMaxPerRoute(300);
|
||||
|
||||
CloseableHttpClient httpClient = HttpClients.custom()
|
||||
.setConnectionManager(connectionManager)
|
||||
.build();
|
||||
|
||||
|
||||
HttpComponentsClientHttpRequestFactory requestFactory =
|
||||
new HttpComponentsClientHttpRequestFactory();
|
||||
|
||||
requestFactory.setHttpClient(httpClient);
|
||||
requestFactory.setConnectionRequestTimeout(3000);
|
||||
requestFactory.setConnectTimeout(3000);
|
||||
requestFactory.setReadTimeout(30000);
|
||||
RestTemplate restTemplate = new RestTemplate(requestFactory);
|
||||
List<HttpMessageConverter<?>> list = restTemplate.getMessageConverters();
|
||||
for (HttpMessageConverter<?> httpMessageConverter : list) {
|
||||
if(httpMessageConverter instanceof StringHttpMessageConverter) {
|
||||
((StringHttpMessageConverter) httpMessageConverter).setDefaultCharset(Charset.forName(charset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return restTemplate;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Spring工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
@Component
|
||||
public class SpringUtil implements ApplicationContextAware {
|
||||
|
||||
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
if (SpringUtil.applicationContext == null) {
|
||||
SpringUtil.applicationContext = applicationContext;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取applicationContext
|
||||
* */
|
||||
public static ApplicationContext getApplicationContext() {
|
||||
return applicationContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过name获取 Bean.
|
||||
* */
|
||||
public static Object getBean(String name) {
|
||||
return getApplicationContext().getBean(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过class获取Bean
|
||||
* */
|
||||
public static <T> T getBean(Class<T> clazz) {
|
||||
return getApplicationContext().getBean(clazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过name,以及Clazz返回指定的Bean
|
||||
* */
|
||||
public static <T> T getBean(String name, Class<T> clazz) {
|
||||
return getApplicationContext().getBean(name, clazz);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
/**
|
||||
* 字符串操作工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class StringUtil {
|
||||
|
||||
/**
|
||||
* 将驼峰式命名的字符串转换为下划线大写方式。如果转换前的驼峰式命名的字符串为空,则返回空字符串。</br>
|
||||
* 例如:HelloWorld->HELLO_WORLD
|
||||
* @param name 转换前的驼峰式命名的字符串
|
||||
* @return 转换后下划线大写方式命名的字符串
|
||||
*/
|
||||
public static String underscoreName(String name) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
if (name != null && name.length() > 0) {
|
||||
// 将第一个字符处理成大写
|
||||
result.append(name.substring(0, 1).toUpperCase());
|
||||
// 循环处理其余字符
|
||||
for (int i = 1; i < name.length(); i++) {
|
||||
String s = name.substring(i, i + 1);
|
||||
// 在大写字母前添加下划线
|
||||
if (s.equals(s.toUpperCase()) && !Character.isDigit(s.charAt(0))) {
|
||||
result.append("_");
|
||||
}
|
||||
// 其他字符直接转成大写
|
||||
result.append(s.toUpperCase());
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将下划线大写方式命名的字符串转换为驼峰式。如果转换前的下划线大写方式命名的字符串为空,则返回空字符串。</br>
|
||||
* 例如:HELLO_WORLD->HelloWorld
|
||||
* @param name 转换前的下划线大写方式命名的字符串
|
||||
* @return 转换后的驼峰式命名的字符串
|
||||
*/
|
||||
public static String camelName(String name) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
// 快速检查
|
||||
if (name == null || name.isEmpty()) {
|
||||
// 没必要转换
|
||||
return "";
|
||||
} else if (!name.contains("_")) {
|
||||
// 不含下划线,仅将首字母小写
|
||||
return name.substring(0, 1).toLowerCase() + name.substring(1);
|
||||
}
|
||||
// 用下划线将原始字符串分割
|
||||
String camels[] = name.split("_");
|
||||
for (String camel : camels) {
|
||||
// 跳过原始字符串中开头、结尾的下换线或双重下划线
|
||||
if (camel.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
// 处理真正的驼峰片段
|
||||
if (result.length() == 0) {
|
||||
// 第一个驼峰片段,全部字母都小写
|
||||
result.append(camel.toLowerCase());
|
||||
} else {
|
||||
// 其他的驼峰片段,首字母大写
|
||||
result.append(camel.substring(0, 1).toUpperCase());
|
||||
result.append(camel.substring(1).toLowerCase());
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
/**
|
||||
* 线程工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class ThreadUtil {
|
||||
|
||||
/**
|
||||
* 根据线程ID获取线程
|
||||
* */
|
||||
public static Thread findThread(long threadId) {
|
||||
ThreadGroup group = Thread.currentThread().getThreadGroup();
|
||||
while(group != null) {
|
||||
Thread[] threads = new Thread[(int)(group.activeCount() * 1.2)];
|
||||
int count = group.enumerate(threads, true);
|
||||
for(int i = 0; i < count; i++) {
|
||||
if(threadId == threads[i].getId()) {
|
||||
return threads[i];
|
||||
}
|
||||
}
|
||||
group = group.getParent();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
package com.java2nb.novel.common.utils;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* UUID工具类
|
||||
* @author xiongxiaoyang
|
||||
* @version 1.0
|
||||
* @since 2020/5/23
|
||||
*/
|
||||
public class UUIDUtil {
|
||||
|
||||
public static final String[] CHARS = new String[] { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l",
|
||||
"m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6",
|
||||
"7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
|
||||
"S", "T", "U", "V", "W", "X", "Y", "Z" };
|
||||
|
||||
/**
|
||||
* 生成指定长度的uuid
|
||||
*
|
||||
* @param length
|
||||
* @return
|
||||
*/
|
||||
private static String getUUID(int length, UUID uuid) {
|
||||
int groupLength = 32 / length;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String id = uuid.toString().replace("-", "");
|
||||
for (int i = 0; i < length; i++) {
|
||||
String str = id.substring(i * groupLength, i * groupLength + groupLength);
|
||||
int x = Integer.parseInt(str, 16);
|
||||
sb.append(CHARS[x % 0x3E]);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 8位UUID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getUUID8() {
|
||||
return getUUID(8, UUID.randomUUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* 8位UUID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getUUID8(byte[] bytes) {
|
||||
return getUUID(8, UUID.nameUUIDFromBytes(bytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* 8位UUID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getUUID8(String fromString) {
|
||||
return getUUID(8, UUID.fromString(fromString));
|
||||
}
|
||||
|
||||
/**
|
||||
* 16位UUID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getUUID16() {
|
||||
return getUUID(16, UUID.randomUUID());
|
||||
}
|
||||
|
||||
/**
|
||||
* 16位UUID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getUUID16(String fromString) {
|
||||
return getUUID(16, UUID.fromString(fromString));
|
||||
}
|
||||
|
||||
/**
|
||||
* 16位UUID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getUUID16(byte[] bytes) {
|
||||
return getUUID(16, UUID.nameUUIDFromBytes(bytes));
|
||||
}
|
||||
|
||||
/**
|
||||
* 32位UUID
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static String getUUID32() {
|
||||
return UUID.randomUUID().toString().replace("-", "");
|
||||
}
|
||||
|
||||
}
|
17
novel-common/src/main/resources/application-common.yml
Normal file
17
novel-common/src/main/resources/application-common.yml
Normal file
@ -0,0 +1,17 @@
|
||||
# 将所有数字转为 String 类型返回,避免前端数据精度丢失的问题
|
||||
jackson:
|
||||
generator:
|
||||
write-numbers-as-strings: true
|
||||
|
||||
|
||||
mybatis:
|
||||
configuration:
|
||||
#自动将数据库带下划线的表字段值映射到Java类的驼峰字段上
|
||||
map-underscore-to-camel-case: true
|
||||
mapper-locations: classpath*:mybatis/mapping/*.xml
|
||||
type-aliases-package: com.java2nb.novel.*.entity
|
||||
|
||||
logging:
|
||||
config: classpath:logback-boot.xml
|
||||
|
||||
|
Reference in New Issue
Block a user