refactor: 基于 novel 项目 & Spring Cloud 2022 & Spring Cloud Alibaba 2022 重构

This commit is contained in:
xiongxiaoyang
2023-03-30 16:15:56 +08:00
parent d68ce51c82
commit 3d098eea5e
505 changed files with 14127 additions and 24067 deletions

View File

@ -0,0 +1,54 @@
<?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">
<parent>
<groupId>io.github.xxyopen</groupId>
<artifactId>novel-cloud</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>novel-common</artifactId>
<dependencies>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-common</artifactId>
<version>${springdoc-openapi.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>${jjwt.version}</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>${jjwt.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,78 @@
package io.github.xxyopen.novel.common.auth;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
/**
* JWT 工具类
*
* @author xiongxiaoyang
* @date 2022/5/17
*/
@UtilityClass
@Slf4j
public class JwtUtils {
/**
* JWT 加密密钥
*/
private static final String SECRET = "E66559580A1ADF48CDD928516062F12E";
/**
* 定义系统标识头常量
*/
private static final String HEADER_SYSTEM_KEY = "systemKeyHeader";
/**
* 根据用户ID生成JWT
*
* @param uid 用户ID
* @param systemKey 系统标识
* @return JWT
*/
public String generateToken(Long uid, String systemKey) {
return Jwts.builder()
.setHeaderParam(HEADER_SYSTEM_KEY, systemKey)
.setSubject(uid.toString())
.signWith(Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)))
.compact();
}
/**
* 解析JWT返回用户ID
*
* @param token JWT
* @param systemKey 系统标识
* @return 用户ID
*/
public Long parseToken(String token, String systemKey) {
Jws<Claims> claimsJws;
try {
claimsJws = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(SECRET.getBytes(StandardCharsets.UTF_8)))
.build()
.parseClaimsJws(token);
// OK, we can trust this JWT
// 判断该 JWT 是否属于指定系统
if (Objects.equals(claimsJws.getHeader().get(HEADER_SYSTEM_KEY), systemKey)) {
return Long.parseLong(claimsJws.getBody().getSubject());
}
} catch (JwtException e) {
log.warn("JWT解析失败:{}", token);
// don't trust the JWT!
}
return null;
}
}

View File

@ -0,0 +1,45 @@
package io.github.xxyopen.novel.common.auth;
import lombok.experimental.UtilityClass;
/**
* 用户信息 持有类
*
* @author xiongxiaoyang
* @date 2022/5/18
*/
@UtilityClass
public class UserHolder {
/**
* 当前线程用户ID
*/
private static final ThreadLocal<Long> userIdTL = new ThreadLocal<>();
/**
* 当前线程作家ID
*/
private static final ThreadLocal<Long> authorIdTL = new ThreadLocal<>();
public void setUserId(Long userId) {
userIdTL.set(userId);
}
public Long getUserId() {
return userIdTL.get();
}
public void setAuthorId(Long authorId) {
authorIdTL.set(authorId);
}
public Long getAuthorId() {
return authorIdTL.get();
}
public void clear() {
userIdTL.remove();
authorIdTL.remove();
}
}

View File

@ -0,0 +1,33 @@
package io.github.xxyopen.novel.common.constant;
/**
* AMQP 相关常量
*
* @author xiongxiaoyang
* @date 2022/5/25
*/
public class AmqpConsts {
/**
* 小说信息改变 MQ
*/
public static class BookChangeMq {
/**
* 小说信息改变交换机
*/
public static final String EXCHANGE_NAME = "EXCHANGE-BOOK-CHANGE";
/**
* Elasticsearch book 索引更新的队列
*/
public static final String QUEUE_ES_UPDATE = "QUEUE-ES-BOOK-UPDATE";
/**
* Redis book 缓存更新的队列
*/
public static final String QUEUE_REDIS_UPDATE = "QUEUE-REDIS-BOOK-UPDATE";
}
}

View File

@ -0,0 +1,114 @@
package io.github.xxyopen.novel.common.constant;
/**
* API 路由常量
*
* @author xiongxiaoyang
* @date 2022/5/12
*/
public class ApiRouterConsts {
private ApiRouterConsts() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
/**
* API请求路径前缀
*/
public static final String API_URL_PREFIX = "/api";
/**
* 前台门户系统请求路径前缀
*/
public static final String API_FRONT_URL_PREFIX = API_URL_PREFIX + "/front";
/**
* 作家管理系统请求路径前缀
*/
public static final String API_AUTHOR_URL_PREFIX = API_URL_PREFIX + "/author";
/**
* 平台后台管理系统请求路径前缀
*/
public static final String API_ADMIN_URL_PREFIX = API_URL_PREFIX + "/admin";
/**
* 微服务内部调用请求路径前缀
*/
public static final String API_INNER_URL_PREFIX = API_URL_PREFIX + "/inner";
/**
* 首页模块请求路径前缀
*/
public static final String HOME_URL_PREFIX = "/home";
/**
* 首页模块请求路径前缀
*/
public static final String NEWS_URL_PREFIX = "/news";
/**
* 小说模块请求路径前缀
*/
public static final String BOOK_URL_PREFIX = "/book";
/**
* 会员模块请求路径前缀
*/
public static final String USER_URL_PREFIX = "/user";
/**
* 资源(图片/视频/文档)模块请求路径前缀
*/
public static final String RESOURCE_URL_PREFIX = "/resource";
/**
* 搜索模块请求路径前缀
*/
public static final String SEARCH_URL_PREFIX = "/search";
/**
* 前台门户首页API请求路径前缀
*/
public static final String API_FRONT_HOME_URL_PREFIX = API_FRONT_URL_PREFIX + HOME_URL_PREFIX;
/**
* 前台门户新闻相关API请求路径前缀
*/
public static final String API_FRONT_NEWS_URL_PREFIX = API_FRONT_URL_PREFIX + NEWS_URL_PREFIX;
/**
* 前台门户小说相关API请求路径前缀
*/
public static final String API_FRONT_BOOK_URL_PREFIX = API_FRONT_URL_PREFIX + BOOK_URL_PREFIX;
/**
* 前台门户会员相关API请求路径前缀
*/
public static final String API_FRONT_USER_URL_PREFIX = API_FRONT_URL_PREFIX + USER_URL_PREFIX;
/**
* 前台门户资源(图片/视频/文档相关API请求路径前缀
*/
public static final String API_FRONT_RESOURCE_URL_PREFIX =
API_FRONT_URL_PREFIX + RESOURCE_URL_PREFIX;
/**
* 前台门户搜索相关API请求路径前缀
*/
public static final String API_FRONT_SEARCH_URL_PREFIX =
API_FRONT_URL_PREFIX + SEARCH_URL_PREFIX;
/**
* 小说微服务内部调用接口
* */
public static final String API_INNER_BOOK_URL_PREFIX =
API_INNER_URL_PREFIX + BOOK_URL_PREFIX;
/**
* 会员微服务内部调用接口
* */
public static final String API_INNER_USER_URL_PREFIX =
API_INNER_URL_PREFIX + USER_URL_PREFIX;
}

View File

@ -0,0 +1,175 @@
package io.github.xxyopen.novel.common.constant;
/**
* 缓存相关常量
*
* @author xiongxiaoyang
* @date 2022/5/12
*/
public class CacheConsts {
/**
* 本项目 Redis 缓存前缀
*/
public static final String REDIS_CACHE_PREFIX = "Cache::Novel::";
/**
* Caffeine 缓存管理器
*/
public static final String CAFFEINE_CACHE_MANAGER = "caffeineCacheManager";
/**
* Redis 缓存管理器
*/
public static final String REDIS_CACHE_MANAGER = "redisCacheManager";
/**
* 首页小说推荐缓存
*/
public static final String HOME_BOOK_CACHE_NAME = "homeBookCache";
/**
* 最新新闻缓存
*/
public static final String LATEST_NEWS_CACHE_NAME = "latestNewsCache";
/**
* 小说点击榜缓存
*/
public static final String BOOK_VISIT_RANK_CACHE_NAME = "bookVisitRankCache";
/**
* 小说新书榜缓存
*/
public static final String BOOK_NEWEST_RANK_CACHE_NAME = "bookNewestRankCache";
/**
* 小说更新榜缓存
*/
public static final String BOOK_UPDATE_RANK_CACHE_NAME = "bookUpdateRankCache";
/**
* 首页友情链接缓存
*/
public static final String HOME_FRIEND_LINK_CACHE_NAME = "homeFriendLinkCache";
/**
* 小说分类列表缓存
*/
public static final String BOOK_CATEGORY_LIST_CACHE_NAME = "bookCategoryListCache";
/**
* 小说信息缓存
*/
public static final String BOOK_INFO_CACHE_NAME = "bookInfoCache";
/**
* 小说章节缓存
*/
public static final String BOOK_CHAPTER_CACHE_NAME = "bookChapterCache";
/**
* 小说内容缓存
*/
public static final String BOOK_CONTENT_CACHE_NAME = "bookContentCache";
/**
* 最近更新小说ID列表缓存
*/
public static final String LAST_UPDATE_BOOK_ID_LIST_CACHE_NAME = "lastUpdateBookIdListCache";
/**
* 图片验证码缓存 KEY
*/
public static final String IMG_VERIFY_CODE_CACHE_KEY =
REDIS_CACHE_PREFIX + "imgVerifyCodeCache::";
/**
* 用户信息缓存
*/
public static final String USER_INFO_CACHE_NAME = "userInfoCache";
/**
* 作家信息缓存
*/
public static final String AUTHOR_INFO_CACHE_NAME = "authorInfoCache";
/**
* 缓存配置常量
*/
public enum CacheEnum {
HOME_BOOK_CACHE(0, HOME_BOOK_CACHE_NAME, 60 * 60 * 24, 1),
LATEST_NEWS_CACHE(0, LATEST_NEWS_CACHE_NAME, 60 * 10, 1),
BOOK_VISIT_RANK_CACHE(2, BOOK_VISIT_RANK_CACHE_NAME, 60 * 60 * 6, 1),
BOOK_NEWEST_RANK_CACHE(0, BOOK_NEWEST_RANK_CACHE_NAME, 60 * 30, 1),
BOOK_UPDATE_RANK_CACHE(0, BOOK_UPDATE_RANK_CACHE_NAME, 60, 1),
HOME_FRIEND_LINK_CACHE(2, HOME_FRIEND_LINK_CACHE_NAME, 0, 1),
BOOK_CATEGORY_LIST_CACHE(0, BOOK_CATEGORY_LIST_CACHE_NAME, 0, 2),
BOOK_INFO_CACHE(0, BOOK_INFO_CACHE_NAME, 60 * 60 * 18, 500),
BOOK_CHAPTER_CACHE(0, BOOK_CHAPTER_CACHE_NAME, 60 * 60 * 6, 5000),
BOOK_CONTENT_CACHE(2, BOOK_CONTENT_CACHE_NAME, 60 * 60 * 12, 3000),
LAST_UPDATE_BOOK_ID_LIST_CACHE(0, LAST_UPDATE_BOOK_ID_LIST_CACHE_NAME, 60 * 60, 10),
USER_INFO_CACHE(2, USER_INFO_CACHE_NAME, 60 * 60 * 24, 10000),
AUTHOR_INFO_CACHE(2, AUTHOR_INFO_CACHE_NAME, 60 * 60 * 48, 1000);
/**
* 缓存类型 0-本地 1-本地和远程 2-远程
*/
private int type;
/**
* 缓存的名字
*/
private String name;
/**
* 失效时间(秒) 0-永不失效
*/
private int ttl;
/**
* 最大容量
*/
private int maxSize;
CacheEnum(int type, String name, int ttl, int maxSize) {
this.type = type;
this.name = name;
this.ttl = ttl;
this.maxSize = maxSize;
}
public boolean isLocal() {
return type <= 1;
}
public boolean isRemote() {
return type >= 1;
}
public String getName() {
return name;
}
public int getTtl() {
return ttl;
}
public int getMaxSize() {
return maxSize;
}
}
}

View File

@ -0,0 +1,57 @@
package io.github.xxyopen.novel.common.constant;
/**
* 通用常量
*
* @author xiongxiaoyang
* @date 2022/5/12
*/
public class CommonConsts {
/**
* 是
*/
public static final Integer YES = 1;
public static final String TRUE = "true";
/**
* 否
*/
public static final Integer NO = 0;
public static final String FALSE = "false";
/**
* 性别常量
*/
public enum SexEnum {
/**
* 男
*/
MALE(0, ""),
/**
* 女
*/
FEMALE(1, "");
SexEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}
private int code;
private String desc;
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
}

View File

@ -0,0 +1,201 @@
package io.github.xxyopen.novel.common.constant;
import lombok.Getter;
/**
* 数据库 常量
*
* @author xiongxiaoyang
* @date 2022/5/12
*/
public class DatabaseConsts {
/**
* 用户信息表
*/
public static class UserInfoTable {
private UserInfoTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_USERNAME = "username";
}
/**
* 用户反馈表
*/
public static class UserFeedBackTable {
private UserFeedBackTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_USER_ID = "user_id";
}
/**
* 用户书架表
*/
public static class UserBookshelfTable {
private UserBookshelfTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_USER_ID = "user_id";
public static final String COLUMN_BOOK_ID = "book_id";
}
/**
* 作家信息表
*/
public static class AuthorInfoTable {
private AuthorInfoTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_USER_ID = "user_id";
}
/**
* 小说类别表
*/
public static class BookCategoryTable {
private BookCategoryTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_WORK_DIRECTION = "work_direction";
}
/**
* 小说表
*/
public static class BookTable {
private BookTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_CATEGORY_ID = "category_id";
public static final String COLUMN_BOOK_NAME = "book_name";
public static final String AUTHOR_ID = "author_id";
public static final String COLUMN_VISIT_COUNT = "visit_count";
public static final String COLUMN_WORD_COUNT = "word_count";
public static final String COLUMN_LAST_CHAPTER_UPDATE_TIME = "last_chapter_update_time";
}
/**
* 小说章节表
*/
public static class BookChapterTable {
private BookChapterTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_BOOK_ID = "book_id";
public static final String COLUMN_CHAPTER_NUM = "chapter_num";
public static final String COLUMN_LAST_CHAPTER_UPDATE_TIME = "last_chapter_update_time";
}
/**
* 小说内容表
*/
public static class BookContentTable {
private BookContentTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_CHAPTER_ID = "chapter_id";
}
/**
* 小说评论表
*/
public static class BookCommentTable {
private BookCommentTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_BOOK_ID = "book_id";
public static final String COLUMN_USER_ID = "user_id";
}
/**
* 新闻内容表
*/
public static class NewsContentTable {
private NewsContentTable() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
}
public static final String COLUMN_NEWS_ID = "news_id";
}
/**
* 通用列枚举类
*/
@Getter
public enum CommonColumnEnum {
ID("id"),
SORT("sort"),
CREATE_TIME("create_time"),
UPDATE_TIME("update_time");
private String name;
CommonColumnEnum(String name) {
this.name = name;
}
}
/**
* SQL语句枚举类
*/
@Getter
public enum SqlEnum {
LIMIT_1("limit 1"),
LIMIT_2("limit 2"),
LIMIT_5("limit 5"),
LIMIT_30("limit 30"),
LIMIT_500("limit 500");
private String sql;
SqlEnum(String sql) {
this.sql = sql;
}
}
}

View File

@ -0,0 +1,152 @@
package io.github.xxyopen.novel.common.constant;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 错误码枚举类。
* <p>
* 错误码为字符串类型,共 5 位,分成两个部分:错误产生来源+四位数字编号。 错误产生来源分为 A/B/C A 表示错误来源于用户,比如参数错误,用户安装版本过低,用户支付 超时等问题; B
* 表示错误来源于当前系统,往往是业务逻辑出错,或程序健壮性差等问题; C 表示错误来源 于第三方服务,比如 CDN 服务出错,消息投递超时等问题;四位数字编号从 0001 到 9999大类之间的
* 步长间距预留 100。
* <p>
* 错误码分为一级宏观错误码、二级宏观错误码、三级宏观错误码。 在无法更加具体确定的错误场景中,可以直接使用一级宏观错误码。
*
* @author xiongxiaoyang
* @date 2022/5/11
*/
@Getter
@AllArgsConstructor
public enum ErrorCodeEnum {
/**
* 正确执行后的返回
*/
OK("00000", "一切 ok"),
/**
* 一级宏观错误码,用户端错误
*/
USER_ERROR("A0001", "用户端错误"),
/**
* 二级宏观错误码,用户注册错误
*/
USER_REGISTER_ERROR("A0100", "用户注册错误"),
/**
* 用户未同意隐私协议
*/
USER_NO_AGREE_PRIVATE_ERROR("A0101", "用户未同意隐私协议"),
/**
* 注册国家或地区受限
*/
USER_REGISTER_AREA_LIMIT_ERROR("A0102", "注册国家或地区受限"),
/**
* 用户验证码错误
*/
USER_VERIFY_CODE_ERROR("A0240", "用户验证码错误"),
/**
* 用户名已存在
*/
USER_NAME_EXIST("A0111", "用户名已存在"),
/**
* 用户账号不存在
*/
USER_ACCOUNT_NOT_EXIST("A0201", "用户账号不存在"),
/**
* 用户密码错误
*/
USER_PASSWORD_ERROR("A0210", "用户密码错误"),
/**
* 二级宏观错误码,用户请求参数错误
*/
USER_REQUEST_PARAM_ERROR("A0400", "用户请求参数错误"),
/**
* 用户登录已过期
*/
USER_LOGIN_EXPIRED("A0230", "用户登录已过期"),
/**
* 访问未授权
*/
USER_UN_AUTH("A0301", "访问未授权"),
/**
* 用户请求服务异常
*/
USER_REQ_EXCEPTION("A0500", "用户请求服务异常"),
/**
* 请求超出限制
*/
USER_REQ_MANY("A0501", "请求超出限制"),
/**
* 用户评论异常
*/
USER_COMMENT("A2000", "用户评论异常"),
/**
* 用户评论异常
*/
USER_COMMENTED("A2001", "用户已发表评论"),
/**
* 作家发布异常
*/
AUTHOR_PUBLISH("A3000", "作家发布异常"),
/**
* 小说名已存在
*/
AUTHOR_BOOK_NAME_EXIST("A3001", "小说名已存在"),
/**
* 用户上传文件异常
*/
USER_UPLOAD_FILE_ERROR("A0700", "用户上传文件异常"),
/**
* 用户上传文件类型不匹配
*/
USER_UPLOAD_FILE_TYPE_NOT_MATCH("A0701", "用户上传文件类型不匹配"),
/**
* 一级宏观错误码,系统执行出错
*/
SYSTEM_ERROR("B0001", "系统执行出错"),
/**
* 二级宏观错误码,系统执行超时
*/
SYSTEM_TIMEOUT_ERROR("B0100", "系统执行超时"),
/**
* 一级宏观错误码,调用第三方服务出错
*/
THIRD_SERVICE_ERROR("C0001", "调用第三方服务出错"),
/**
* 一级宏观错误码,中间件服务出错
*/
MIDDLEWARE_SERVICE_ERROR("C0100", "中间件服务出错");
/**
* 错误码
*/
private final String code;
/**
* 中文描述
*/
private final String message;
}

View File

@ -0,0 +1,25 @@
package io.github.xxyopen.novel.common.constant;
/**
* 消息发送器的类型
*
* @author xiongxiaoyang
* @date 2023/3/24
*/
public class MessageSenderTypeConsts {
private MessageSenderTypeConsts() {
throw new IllegalStateException("Constant class");
}
/**
* 注册成功的邮件发送器
*/
public static final String REGISTER_MAIL_SENDER = "registerMailSender";
/**
* 秒杀活动的系统通知发送器
*/
public static final String SECKILL_SYS_NOTICE_SENDER = "seckillSysNoticeSender";
}

View File

@ -0,0 +1,45 @@
package io.github.xxyopen.novel.common.constant;
/**
* 系统配置相关常量
*
* @author xiongxiaoyang
* @date 2022/5/12
*/
public class SystemConfigConsts {
private SystemConfigConsts() {
throw new IllegalStateException(CONST_INSTANCE_EXCEPTION_MSG);
}
/**
* Http 请求认证 Header
*/
public static final String HTTP_AUTH_HEADER_NAME = "Authorization";
/**
* 前台门户系统标识
*/
public static final String NOVEL_FRONT_KEY = "front";
/**
* 作家管理系统标识
*/
public static final String NOVEL_AUTHOR_KEY = "author";
/**
* 后台管理系统标识
*/
public static final String NOVEL_ADMIN_KEY = "admin";
/**
* 图片上传目录
*/
public static final String IMAGE_UPLOAD_DIRECTORY = "/image/";
/**
* 常量类实例化异常信息
*/
public static final String CONST_INSTANCE_EXCEPTION_MSG = "Constant class";
}

View File

@ -0,0 +1,32 @@
package io.github.xxyopen.novel.common.json.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import java.io.IOException;
import org.springframework.boot.jackson.JsonComponent;
/**
* JSON 全局反序列化器
*
* @author xiongxiaoyang
* @date 2022/5/21
*/
@JsonComponent
public class GlobalJsonDeserializer {
/**
* 字符串反序列化器:过滤特殊字符,解决 XSS 攻击
*/
public static class StringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
return jsonParser.getValueAsString()
.replace("<", "&lt;")
.replace(">", "&gt;");
}
}
}

View File

@ -0,0 +1,22 @@
package io.github.xxyopen.novel.common.json.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**
* 用户名序列化器(敏感信息,不应该在页面上完全显示)
*
* @author xiongxiaoyang
* @date 2022/5/20
*/
public class UsernameSerializer extends JsonSerializer<String> {
@Override
public void serialize(String s, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(s.substring(0, 4) + "****" + s.substring(8));
}
}

View File

@ -0,0 +1,33 @@
package io.github.xxyopen.novel.common.req;
import io.swagger.v3.oas.annotations.Parameter;
import lombok.Data;
/**
* 分页请求数据格式封装所有分页请求的Dto类都应继承该类
*
* @author xiongxiaoyang
* @date 2022/5/11
*/
@Data
public class PageReqDto {
/**
* 请求页码,默认第 1 页
*/
@Parameter(description = "请求页码,默认第 1 页")
private int pageNum = 1;
/**
* 每页大小,默认每页 10 条
*/
@Parameter(description = "每页大小,默认每页 10 条")
private int pageSize = 10;
/**
* 是否查询所有,默认不查所有 为 true 时pageNum 和 pageSize 无效
*/
@Parameter(hidden = true)
private boolean fetchAll = false;
}

View File

@ -0,0 +1,63 @@
package io.github.xxyopen.novel.common.resp;
import java.util.List;
import lombok.Getter;
/**
* 分页响应数据格式封装
*
* @author xiongxiaoyang
* @date 2022/5/11
*/
@Getter
public class PageRespDto<T> {
/**
* 页码
*/
private final long pageNum;
/**
* 每页大小
*/
private final long pageSize;
/**
* 总记录数
*/
private final long total;
/**
* 分页数据集
*/
private final List<? extends T> list;
/**
* 该构造函数用于通用分页查询的场景 接收普通分页数据和普通集合
*/
public PageRespDto(long pageNum, long pageSize, long total, List<T> list) {
this.pageNum = pageNum;
this.pageSize = pageSize;
this.total = total;
this.list = list;
}
public static <T> PageRespDto<T> of(long pageNum, long pageSize, long total, List<T> list) {
return new PageRespDto<>(pageNum, pageSize, total, list);
}
/**
* 获取分页数
*/
public long getPages() {
if (this.pageSize == 0L) {
return 0L;
} else {
long pages = this.total / this.pageSize;
if (this.total % this.pageSize != 0L) {
++pages;
}
return pages;
}
}
}

View File

@ -0,0 +1,87 @@
package io.github.xxyopen.novel.common.resp;
import io.github.xxyopen.novel.common.constant.ErrorCodeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import java.util.Objects;
/**
* Http Rest 响应工具及数据格式封装
*
* @author xiongxiaoyang
* @date 2022/5/11
*/
@Getter
public class RestResp<T> {
/**
* 响应码
*/
@Schema(description = "错误码00000-没有错误")
private String code;
/**
* 响应消息
*/
@Schema(description = "响应消息")
private String message;
/**
* 响应数据
*/
@Schema(description = "响应数据")
private T data;
private RestResp() {
this.code = ErrorCodeEnum.OK.getCode();
this.message = ErrorCodeEnum.OK.getMessage();
}
private RestResp(ErrorCodeEnum errorCode) {
this.code = errorCode.getCode();
this.message = errorCode.getMessage();
}
private RestResp(T data) {
this();
this.data = data;
}
/**
* 业务处理成功,无数据返回
*/
public static RestResp<Void> ok() {
return new RestResp<>();
}
/**
* 业务处理成功,有数据返回
*/
public static <T> RestResp<T> ok(T data) {
return new RestResp<>(data);
}
/**
* 业务处理失败
*/
public static RestResp<Void> fail(ErrorCodeEnum errorCode) {
return new RestResp<>(errorCode);
}
/**
* 系统错误
*/
public static RestResp<Void> error() {
return new RestResp<>(ErrorCodeEnum.SYSTEM_ERROR);
}
/**
* 判断是否成功
*/
public boolean isOk() {
return Objects.equals(this.code, ErrorCodeEnum.OK.getCode());
}
}