mirror of
https://github.com/201206030/novel-cloud.git
synced 2025-06-25 06:26:39 +00:00
refactor: 基于 novel 项目 & Spring Cloud 2022 & Spring Cloud Alibaba 2022 重构
This commit is contained in:
54
novel-core/novel-common/pom.xml
Normal file
54
novel-core/novel-common/pom.xml
Normal 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>
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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";
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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";
|
||||
|
||||
}
|
@ -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";
|
||||
|
||||
}
|
@ -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("<", "<")
|
||||
.replace(">", ">");
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user