style: 代码格式化

This commit is contained in:
xiongxiaoyang 2022-06-30 21:34:54 +08:00
parent 6d2fa74237
commit 9b93d90270
67 changed files with 1293 additions and 620 deletions

6
doc/sql/README.md Normal file
View File

@ -0,0 +1,6 @@
1. 初始状态下MySQL 只需要执行 `novel.sql` 文件即可正常运行本系统
2. 代码更新后再执行以日期命名的增量 SQL 文件
3. 只有开启 XXL-JOB 的功能,才需要执行 `xxl-job.sql` 和以 xxl-job 开头日期结尾的增量 SQL 文件
4. 只有开启 ShardingSphere-JDBC 的功能,才需要执行 `shardingsphere-jdbc.sql` 和以 shardingsphere-jdbc 开头日期结尾的增量 SQL
文件

View File

@ -1,5 +0,0 @@
1. 初始状态下MYSQL 只需要执行 novel.sql 文件即可正常运行本系统
2. 代码更新后再执行以日期命名的增量 SQL 文件
3. 只有开启 XXL-JOB 的功能,才需要执行 xxl-job.sql 和以 xxl-job 开头日期结尾的增量 SQL 文件
4. 只有开启 ShardingSphere-JDBC 的功能,才需要执行 shardingsphere-jdbc.sql 和以 shardingsphere-jdbc 开头日期结尾的增量 SQL 文件

3
doc/style/README.md Normal file
View File

@ -0,0 +1,3 @@
IntelliJ IDEA 中导入 `intellij-java-google-style.xml` 文件:
`Preferences` => `Editor` => `Code Style` => `Java` => `Schema` => `Import Schema`

View File

@ -0,0 +1,598 @@
<?xml version="1.0" encoding="UTF-8"?>
<code_scheme name="GoogleStyle">
<option name="OTHER_INDENT_OPTIONS">
<value>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
<option name="USE_TAB_CHARACTER" value="false" />
<option name="SMART_TABS" value="false" />
<option name="LABEL_INDENT_SIZE" value="0" />
<option name="LABEL_INDENT_ABSOLUTE" value="false" />
<option name="USE_RELATIVE_INDENTS" value="false" />
</value>
</option>
<option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
<value />
</option>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
<option name="JD_P_AT_EMPTY_LINES" value="false" />
<option name="JD_KEEP_EMPTY_PARAMETER" value="false" />
<option name="JD_KEEP_EMPTY_EXCEPTION" value="false" />
<option name="JD_KEEP_EMPTY_RETURN" value="false" />
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="0" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="0" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="WRAP_COMMENTS" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE" value="true" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
<option name="LAYOUT_SETTINGS">
<value>
<option name="INSERT_BLANK_LINE_BEFORE_TAG" value="false" />
</value>
</option>
</AndroidXmlCodeStyleSettings>
<JSCodeStyleSettings>
<option name="INDENT_CHAINED_CALLS" value="false" />
</JSCodeStyleSettings>
<Python>
<option name="USE_CONTINUATION_INDENT_FOR_ARGUMENTS" value="true" />
</Python>
<TypeScriptCodeStyleSettings>
<option name="INDENT_CHAINED_CALLS" value="false" />
</TypeScriptCodeStyleSettings>
<XML>
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="CSS">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="ECMA Script Level 4">
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="PARENT_SETTINGS_INSTALLED" value="true" />
</codeStyleSettings>
<codeStyleSettings language="HTML">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="KEEP_CONTROL_STATEMENT_IN_ONE_LINE" value="false" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_RESOURCES" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="EXTENDS_LIST_WRAP" value="1" />
<option name="THROWS_KEYWORD_WRAP" value="1" />
<option name="METHOD_CALL_CHAIN_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="WRAP_COMMENTS" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="PARENT_SETTINGS_INSTALLED" value="true" />
<indentOptions>
<option name="INDENT_SIZE" value="4" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JSON">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="RIGHT_MARGIN" value="80" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_FOR" value="false" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="METHOD_PARAMETERS_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="TERNARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ARRAY_INITIALIZER_WRAP" value="1" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
<option name="PARENT_SETTINGS_INSTALLED" value="true" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="PROTO">
<option name="RIGHT_MARGIN" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="protobuf">
<option name="RIGHT_MARGIN" value="80" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Python">
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="RIGHT_MARGIN" value="80" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="PARENT_SETTINGS_INSTALLED" value="true" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="SASS">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="SCSS">
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="4" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:android</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>xmlns:.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:id</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>^$</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:.*Style</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_width</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_height</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_weight</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_margin</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginTop</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginBottom</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginStart</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginEnd</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginLeft</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_marginRight</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:layout_.*</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:padding</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingTop</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingBottom</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingStart</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingEnd</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingLeft</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*:paddingRight</NAME>
<XML_ATTRIBUTE />
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/apk/res-auto</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>http://schemas.android.com/tools</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>.*</NAME>
<XML_NAMESPACE>.*</XML_NAMESPACE>
</AND>
</match>
<order>BY_NAME</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
<Objective-C>
<option name="INDENT_NAMESPACE_MEMBERS" value="0" />
<option name="INDENT_C_STRUCT_MEMBERS" value="2" />
<option name="INDENT_CLASS_MEMBERS" value="2" />
<option name="INDENT_VISIBILITY_KEYWORDS" value="1" />
<option name="INDENT_INSIDE_CODE_BLOCK" value="2" />
<option name="KEEP_STRUCTURES_IN_ONE_LINE" value="true" />
<option name="FUNCTION_PARAMETERS_WRAP" value="5" />
<option name="FUNCTION_CALL_ARGUMENTS_WRAP" value="5" />
<option name="TEMPLATE_CALL_ARGUMENTS_WRAP" value="5" />
<option name="TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE" value="true" />
<option name="ALIGN_INIT_LIST_IN_COLUMNS" value="false" />
<option name="SPACE_BEFORE_SUPERCLASS_COLON" value="false" />
</Objective-C>
<Objective-C-extensions>
<option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
<option name="RELEASE_STYLE" value="IVAR" />
<option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cc" header="h" />
<pair source="c" header="h" />
</extensions>
</Objective-C-extensions>
<codeStyleSettings language="ObjectiveC">
<option name="RIGHT_MARGIN" value="80" />
<option name="KEEP_BLANK_LINES_BEFORE_RBRACE" value="1" />
<option name="BLANK_LINES_BEFORE_IMPORTS" value="0" />
<option name="BLANK_LINES_AFTER_IMPORTS" value="0" />
<option name="BLANK_LINES_AROUND_CLASS" value="0" />
<option name="BLANK_LINES_AROUND_METHOD" value="0" />
<option name="BLANK_LINES_AROUND_METHOD_IN_INTERFACE" value="0" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="false" />
<option name="BINARY_OPERATION_SIGN_ON_NEXT_LINE" value="true" />
<option name="FOR_STATEMENT_WRAP" value="1" />
<option name="ASSIGNMENT_WRAP" value="1" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="4" />
</indentOptions>
</codeStyleSettings>
</code_scheme>

View File

@ -7,6 +7,7 @@ import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.info.License;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.CommandLineRunner;
@ -21,8 +22,6 @@ import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
import java.util.Map;
@OpenAPIDefinition(info = @Info(title = "novel 项目接口文档", version = "v3.2.0", license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0")))
@SecurityScheme(type = SecuritySchemeType.APIKEY, in = SecuritySchemeIn.HEADER, name = SystemConfigConsts.HTTP_AUTH_HEADER_NAME, description = "登录 token")
@SpringBootApplication
@ -52,8 +51,8 @@ public class NovelApplication {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests(requests -> requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests(requests -> requests.anyRequest().hasRole("ENDPOINT_ADMIN"));
http.httpBasic();
return http.build();
}

View File

@ -20,7 +20,12 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 作家后台-作家模块 API 控制器
@ -81,7 +86,9 @@ public class AuthorController {
*/
@Operation(summary = "小说章节发布接口")
@PostMapping("book/chapter/{bookId}")
public RestResp<Void> publishBookChapter(@Parameter(description = "小说ID") @PathVariable("bookId") Long bookId, @Valid @RequestBody ChapterAddReqDto dto) {
public RestResp<Void> publishBookChapter(
@Parameter(description = "小说ID") @PathVariable("bookId") Long bookId,
@Valid @RequestBody ChapterAddReqDto dto) {
dto.setBookId(bookId);
return bookService.saveBookChapter(dto);
}
@ -91,7 +98,9 @@ public class AuthorController {
*/
@Operation(summary = "小说章节发布列表查询接口")
@GetMapping("book/chapters/{bookId}")
public RestResp<PageRespDto<BookChapterRespDto>> listBookChapters(@Parameter(description = "小说ID") @PathVariable("bookId") Long bookId,@ParameterObject PageReqDto dto) {
public RestResp<PageRespDto<BookChapterRespDto>> listBookChapters(
@Parameter(description = "小说ID") @PathVariable("bookId") Long bookId,
@ParameterObject PageReqDto dto) {
return bookService.listBookChapters(bookId, dto);
}

View File

@ -2,16 +2,25 @@ package io.github.xxyopen.novel.controller.front;
import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.core.constant.ApiRouterConsts;
import io.github.xxyopen.novel.dto.resp.*;
import io.github.xxyopen.novel.dto.resp.BookCategoryRespDto;
import io.github.xxyopen.novel.dto.resp.BookChapterAboutRespDto;
import io.github.xxyopen.novel.dto.resp.BookChapterRespDto;
import io.github.xxyopen.novel.dto.resp.BookCommentRespDto;
import io.github.xxyopen.novel.dto.resp.BookContentAboutRespDto;
import io.github.xxyopen.novel.dto.resp.BookInfoRespDto;
import io.github.xxyopen.novel.dto.resp.BookRankRespDto;
import io.github.xxyopen.novel.service.BookService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 前台门户-小说模块 API 控制器
@ -32,7 +41,8 @@ public class BookController {
*/
@Operation(summary = "小说分类列表查询接口")
@GetMapping("category/list")
public RestResp<List<BookCategoryRespDto>> listCategory(@Parameter(description = "作品方向",required = true) Integer workDirection) {
public RestResp<List<BookCategoryRespDto>> listCategory(
@Parameter(description = "作品方向", required = true) Integer workDirection) {
return bookService.listCategory(workDirection);
}
@ -41,7 +51,8 @@ public class BookController {
*/
@Operation(summary = "小说信息查询接口")
@GetMapping("{id}")
public RestResp<BookInfoRespDto> getBookById(@Parameter(description = "小说 ID") @PathVariable("id") Long bookId) {
public RestResp<BookInfoRespDto> getBookById(
@Parameter(description = "小说 ID") @PathVariable("id") Long bookId) {
return bookService.getBookById(bookId);
}
@ -59,7 +70,8 @@ public class BookController {
*/
@Operation(summary = "小说最新章节相关信息查询接口")
@GetMapping("last_chapter/about")
public RestResp<BookChapterAboutRespDto> getLastChapterAbout(@Parameter(description = "小说ID") Long bookId) {
public RestResp<BookChapterAboutRespDto> getLastChapterAbout(
@Parameter(description = "小说ID") Long bookId) {
return bookService.getLastChapterAbout(bookId);
}
@ -68,7 +80,8 @@ public class BookController {
*/
@Operation(summary = "小说推荐列表查询接口")
@GetMapping("rec_list")
public RestResp<List<BookInfoRespDto>> listRecBooks(@Parameter(description = "小说ID") Long bookId) throws NoSuchAlgorithmException {
public RestResp<List<BookInfoRespDto>> listRecBooks(
@Parameter(description = "小说ID") Long bookId) throws NoSuchAlgorithmException {
return bookService.listRecBooks(bookId);
}
@ -77,7 +90,8 @@ public class BookController {
*/
@Operation(summary = "小说章节列表查询接口")
@GetMapping("chapter/list")
public RestResp<List<BookChapterRespDto>> listChapters(@Parameter(description = "小说ID") Long bookId) {
public RestResp<List<BookChapterRespDto>> listChapters(
@Parameter(description = "小说ID") Long bookId) {
return bookService.listChapters(bookId);
}
@ -86,7 +100,8 @@ public class BookController {
*/
@Operation(summary = "小说内容相关信息查询接口")
@GetMapping("content/{chapterId}")
public RestResp<BookContentAboutRespDto> getBookContentAbout(@Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId) {
public RestResp<BookContentAboutRespDto> getBookContentAbout(
@Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId) {
return bookService.getBookContentAbout(chapterId);
}
@ -95,7 +110,8 @@ public class BookController {
*/
@Operation(summary = "获取上一章节ID接口")
@GetMapping("pre_chapter_id/{chapterId}")
public RestResp<Long> getPreChapterId(@Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId) {
public RestResp<Long> getPreChapterId(
@Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId) {
return bookService.getPreChapterId(chapterId);
}
@ -104,7 +120,8 @@ public class BookController {
*/
@Operation(summary = "获取下一章节ID接口")
@GetMapping("next_chapter_id/{chapterId}")
public RestResp<Long> getNextChapterId(@Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId) {
public RestResp<Long> getNextChapterId(
@Parameter(description = "章节ID") @PathVariable("chapterId") Long chapterId) {
return bookService.getNextChapterId(chapterId);
}
@ -140,7 +157,8 @@ public class BookController {
*/
@Operation(summary = "小说最新评论查询接口")
@GetMapping("comment/newest_list")
public RestResp<BookCommentRespDto> listNewestComments(@Parameter(description = "小说ID") Long bookId) {
public RestResp<BookCommentRespDto> listNewestComments(
@Parameter(description = "小说ID") Long bookId) {
return bookService.listNewestComments(bookId);
}

View File

@ -1,20 +1,19 @@
package io.github.xxyopen.novel.controller.front;
import io.github.xxyopen.novel.core.constant.ApiRouterConsts;
import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.core.constant.ApiRouterConsts;
import io.github.xxyopen.novel.dto.resp.NewsInfoRespDto;
import io.github.xxyopen.novel.service.NewsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 前台门户-新闻模块 API 控制器
*
@ -43,7 +42,8 @@ public class NewsController {
*/
@Operation(summary = "新闻信息查询接口")
@GetMapping("{id}")
public RestResp<NewsInfoRespDto> getNews(@Parameter(description = "新闻ID") @PathVariable Long id) {
public RestResp<NewsInfoRespDto> getNews(
@Parameter(description = "新闻ID") @PathVariable Long id) {
return newsService.getNews(id);
}
}

View File

@ -7,11 +7,14 @@ import io.github.xxyopen.novel.service.ResourceService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
/**
* 前台门户-资源(图片/视频/文档)模块 API 控制器
@ -38,10 +41,11 @@ public class ResourceController {
/**
* 图片上传接口
* */
*/
@Operation(summary = "图片上传接口")
@PostMapping("/image")
RestResp<String> uploadImage(@Parameter(description = "上传文件") @RequestParam("file") MultipartFile file) {
RestResp<String> uploadImage(
@Parameter(description = "上传文件") @RequestParam("file") MultipartFile file) {
return resourceService.uploadImage(file);
}

View File

@ -33,7 +33,8 @@ public class SearchController {
*/
@Operation(summary = "小说搜索接口")
@GetMapping("books")
public RestResp<PageRespDto<BookInfoRespDto>> searchBooks(@ParameterObject BookSearchReqDto condition) {
public RestResp<PageRespDto<BookInfoRespDto>> searchBooks(
@ParameterObject BookSearchReqDto condition) {
return searchService.searchBooks(condition);
}

View File

@ -19,7 +19,14 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 前台门户-会员模块 API 控制器
@ -108,7 +115,8 @@ public class UserController {
*/
@Operation(summary = "修改评论接口")
@PutMapping("comment/{id}")
public RestResp<Void> updateComment(@Parameter(description = "评论ID") @PathVariable Long id, String content) {
public RestResp<Void> updateComment(@Parameter(description = "评论ID") @PathVariable Long id,
String content) {
return bookService.updateComment(UserHolder.getUserId(), id, content);
}
@ -122,9 +130,7 @@ public class UserController {
}
/**
* 查询书架状态接口
* 0-不在书架
* 1-已在书架
* 查询书架状态接口 0-不在书架 1-已在书架
*/
@Operation(summary = "查询书架状态接口")
@GetMapping("bookshelf_status")

View File

@ -1,12 +1,12 @@
package io.github.xxyopen.novel.core.annotation;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* 分布式锁-Key 注解
*
@ -17,5 +17,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Retention(RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
public @interface Key {
String expr() default "";
}

View File

@ -3,6 +3,9 @@ package io.github.xxyopen.novel.core.aspect;
import io.github.xxyopen.novel.core.annotation.Key;
import io.github.xxyopen.novel.core.annotation.Lock;
import io.github.xxyopen.novel.core.common.exception.BusinessException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.concurrent.TimeUnit;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
@ -17,10 +20,6 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.concurrent.TimeUnit;
/**
* 分布式锁 切面
*
@ -42,7 +41,7 @@ public record LockAspect(RedissonClient redissonClient) {
Method targetMethod = methodSignature.getMethod();
Lock lock = targetMethod.getAnnotation(Lock.class);
String lockKey = KEY_PREFIX + buildLockKey(lock.prefix(), targetMethod,
joinPoint.getArgs());
joinPoint.getArgs());
RLock rLock = redissonClient.getLock(lockKey);
if (lock.isWait() ? rLock.tryLock(lock.waitTime(), TimeUnit.SECONDS) : rLock.tryLock()) {
try {

View File

@ -6,9 +6,8 @@ import io.github.xxyopen.novel.core.constant.SystemConfigConsts;
import io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dto.UserInfoDto;
import io.github.xxyopen.novel.manager.cache.UserInfoCacheManager;
import org.springframework.util.StringUtils;
import java.util.Objects;
import org.springframework.util.StringUtils;
/**
* 策略模式实现用户认证授权功能
@ -21,7 +20,7 @@ public interface AuthStrategy {
/**
* 请求用户认证
*
* @param token 登录 token
* @param token 登录 token
* @param requestUri 请求的 URI
* @throws BusinessException 认证失败则抛出业务异常
*/
@ -35,7 +34,8 @@ public interface AuthStrategy {
* @param token token 登录 token
* @return 用户ID
*/
default Long authSSO(JwtUtils jwtUtils, UserInfoCacheManager userInfoCacheManager, String token) {
default Long authSSO(JwtUtils jwtUtils, UserInfoCacheManager userInfoCacheManager,
String token) {
if (!StringUtils.hasText(token)) {
// token 为空
throw new BusinessException(ErrorCodeEnum.USER_LOGIN_EXPIRED);

View File

@ -7,11 +7,10 @@ import io.github.xxyopen.novel.core.util.JwtUtils;
import io.github.xxyopen.novel.dto.AuthorInfoDto;
import io.github.xxyopen.novel.manager.cache.AuthorInfoCacheManager;
import io.github.xxyopen.novel.manager.cache.UserInfoCacheManager;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* 作家后台管理系统 认证策略
@ -31,17 +30,17 @@ public class AuthorAuthStrategy implements AuthStrategy {
/**
* 不需要进行作家权限认证的 URI
* */
*/
private static final List<String> EXCLUDE_URI = List.of(
ApiRouterConsts.API_AUTHOR_URL_PREFIX + "/register",
ApiRouterConsts.API_AUTHOR_URL_PREFIX +"/status"
ApiRouterConsts.API_AUTHOR_URL_PREFIX + "/register",
ApiRouterConsts.API_AUTHOR_URL_PREFIX + "/status"
);
@Override
public void auth(String token, String requestUri) throws BusinessException {
// 统一账号认证
Long userId = authSSO(jwtUtils, userInfoCacheManager, token);
if(EXCLUDE_URI.contains(requestUri)){
if (EXCLUDE_URI.contains(requestUri)) {
// 该请求不需要进行作家权限认证
return;
}

View File

@ -23,6 +23,6 @@ public class FrontAuthStrategy implements AuthStrategy {
@Override
public void auth(String token, String requestUri) throws BusinessException {
// 统一账号认证
authSSO(jwtUtils,userInfoCacheManager,token);
authSSO(jwtUtils, userInfoCacheManager, token);
}
}

View File

@ -13,12 +13,12 @@ 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) {
@ -37,7 +37,7 @@ public class UserHolder {
return authorIdTL.get();
}
public void clear(){
public void clear() {
userIdTL.remove();
authorIdTL.remove();
}

View File

@ -11,33 +11,33 @@ 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{
*/
public enum SexEnum {
/**
*
* */
MALE(0,""),
*/
MALE(0, ""),
/**
*
* */
FEMALE(1,"");
*/
FEMALE(1, "");
SexEnum(int code,String desc){
SexEnum(int code, String desc) {
this.code = code;
this.desc = desc;
}

View File

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

View File

@ -19,28 +19,28 @@ public class CommonExceptionHandler {
/**
* 处理数据校验异常
* */
*/
@ExceptionHandler(BindException.class)
public RestResp<Void> handlerBindException(BindException e){
log.error(e.getMessage(),e);
public RestResp<Void> handlerBindException(BindException e) {
log.error(e.getMessage(), e);
return RestResp.fail(ErrorCodeEnum.USER_REQUEST_PARAM_ERROR);
}
/**
* 处理业务异常
* */
*/
@ExceptionHandler(BusinessException.class)
public RestResp<Void> handlerBusinessException(BusinessException e){
log.error(e.getMessage(),e);
public RestResp<Void> handlerBusinessException(BusinessException e) {
log.error(e.getMessage(), e);
return RestResp.fail(e.getErrorCodeEnum());
}
/**
* 处理系统异常
* */
*/
@ExceptionHandler(Exception.class)
public RestResp<Void> handlerException(Exception e){
log.error(e.getMessage(),e);
public RestResp<Void> handlerException(Exception e) {
log.error(e.getMessage(), e);
return RestResp.error();
}

View File

@ -14,20 +14,19 @@ public class PageReqDto {
/**
* 请求页码默认第 1
* */
*/
@Parameter(description = "请求页码,默认第 1 页")
private int pageNum = 1;
/**
* 每页大小默认每页 10
* */
*/
@Parameter(description = "每页大小,默认每页 10 条")
private int pageSize = 10;
/**
* 是否查询所有默认不查所有
* true pageNum pageSize 无效
* */
* 是否查询所有默认不查所有 true pageNum pageSize 无效
*/
@Parameter(hidden = true)
private boolean fetchAll = false;

View File

@ -1,8 +1,7 @@
package io.github.xxyopen.novel.core.common.resp;
import lombok.Getter;
import java.util.List;
import lombok.Getter;
/**
* 分页响应数据格式封装
@ -34,8 +33,7 @@ public class PageRespDto<T> {
private final List<? extends T> list;
/**
* 该构造函数用于通用分页查询的场景
* 接收普通分页数据和普通集合
* 该构造函数用于通用分页查询的场景 接收普通分页数据和普通集合
*/
public PageRespDto(long pageNum, long pageSize, long total, List<T> list) {
this.pageNum = pageNum;
@ -50,7 +48,7 @@ public class PageRespDto<T> {
/**
* 获取分页数
* */
*/
public long getPages() {
if (this.pageSize == 0L) {
return 0L;
@ -59,7 +57,6 @@ public class PageRespDto<T> {
if (this.total % this.pageSize != 0L) {
++pages;
}
return pages;
}
}

View File

@ -1,14 +1,15 @@
package io.github.xxyopen.novel.core.common.util;
import lombok.experimental.UtilityClass;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.Random;
import javax.imageio.ImageIO;
import lombok.experimental.UtilityClass;
/**
* 图片验证码工具类
@ -79,7 +80,7 @@ public class ImgVerifyCodeUtils {
for (int i = 1; i <= verifyCode.length(); i++) {
g.setFont(getFont());
g.setColor(new Color(random.nextInt(101), random.nextInt(111), random
.nextInt(121)));
.nextInt(121)));
g.translate(random.nextInt(3), random.nextInt(3));
g.drawString(String.valueOf(verifyCode.charAt(i - 1)), 13 * i, 23);
}

View File

@ -16,6 +16,7 @@ public class IpUtils {
/**
* 获取真实IP
*
* @return 真实IP
*/
public String getRealIp(HttpServletRequest request) {

View File

@ -2,6 +2,11 @@ package io.github.xxyopen.novel.core.config;
import com.github.benmanes.caffeine.cache.Caffeine;
import io.github.xxyopen.novel.core.constant.CacheConsts;
import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.cache.support.SimpleCacheManager;
@ -13,12 +18,6 @@ import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import java.time.Duration;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* 缓存配置类
*
@ -40,7 +39,8 @@ public class CacheConfig {
// 类型推断 var 非常适合 for 循环JDK 10 引入JDK 11 改进
for (var c : CacheConsts.CacheEnum.values()) {
if (c.isLocal()) {
Caffeine<Object, Object> caffeine = Caffeine.newBuilder().recordStats().maximumSize(c.getMaxSize());
Caffeine<Object, Object> caffeine = Caffeine.newBuilder().recordStats()
.maximumSize(c.getMaxSize());
if (c.getTtl() > 0) {
caffeine.expireAfterWrite(Duration.ofSeconds(c.getTtl()));
}
@ -57,26 +57,32 @@ public class CacheConfig {
*/
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(
connectionFactory);
RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig()
.disableCachingNullValues().prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX);
.disableCachingNullValues().prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX);
Map<String, RedisCacheConfiguration> cacheMap = new LinkedHashMap<>(CacheConsts.CacheEnum.values().length);
Map<String, RedisCacheConfiguration> cacheMap = new LinkedHashMap<>(
CacheConsts.CacheEnum.values().length);
// 类型推断 var 非常适合 for 循环JDK 10 引入JDK 11 改进
for (var c : CacheConsts.CacheEnum.values()) {
if (c.isRemote()) {
if (c.getTtl() > 0) {
cacheMap.put(c.getName(), RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
.prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX).entryTtl(Duration.ofSeconds(c.getTtl())));
cacheMap.put(c.getName(),
RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
.prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX)
.entryTtl(Duration.ofSeconds(c.getTtl())));
} else {
cacheMap.put(c.getName(), RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
cacheMap.put(c.getName(),
RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
.prefixCacheNameWith(CacheConsts.REDIS_CACHE_PREFIX));
}
}
}
RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig, cacheMap);
RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter,
defaultCacheConfig, cacheMap);
redisCacheManager.setTransactionAware(true);
redisCacheManager.initializeCaches();
return redisCacheManager;

View File

@ -37,7 +37,7 @@ public class CorsConfig {
UrlBasedCorsConfigurationSource configurationSource = new UrlBasedCorsConfigurationSource();
// 添加映射路径拦截一切请求
configurationSource.registerCorsConfiguration("/**",config);
configurationSource.registerCorsConfiguration("/**", config);
return new CorsFilter(configurationSource);
}

View File

@ -26,7 +26,7 @@ public class EsConfig {
// Create the transport with a Jackson mapper
ElasticsearchTransport transport = new RestClientTransport(
restClient, new JacksonJsonpMapper());
restClient, new JacksonJsonpMapper());
// And create the API client
return new ElasticsearchClient(transport);

View File

@ -18,7 +18,7 @@ public class RedissonConfig {
@Bean
@SneakyThrows
public RedissonClient redissonClient(){
public RedissonClient redissonClient() {
Config config = Config.fromYAML(getClass().getResource("/redisson.yml"));
return Redisson.create(config);
}

View File

@ -12,9 +12,8 @@ import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Spring Web Mvc 相关配置
* 不要加 @EnableWebMvc 注解否则会导致 jackson 的全局配置失效
* 类上添加 @EnableWebMvc 会导致 WebMvcAutoConfiguration 中的自动配置全部失效
* Spring Web Mvc 相关配置不要加 @EnableWebMvc 注解否则会导致 jackson 的全局配置失效因为 @EnableWebMvc 注解会导致
* WebMvcAutoConfiguration 自动配置失效
*
* @author xiongxiaoyang
* @date 2022/5/18
@ -36,33 +35,33 @@ public class WebConfig implements WebMvcConfigurer {
// 流量限制拦截器
registry.addInterceptor(flowLimitInterceptor)
.addPathPatterns("/**")
.order(0);
.addPathPatterns("/**")
.order(0);
// 文件访问拦截
registry.addInterceptor(fileInterceptor)
.addPathPatterns(SystemConfigConsts.IMAGE_UPLOAD_DIRECTORY + "**")
.order(1);
.addPathPatterns(SystemConfigConsts.IMAGE_UPLOAD_DIRECTORY + "**")
.order(1);
// 权限认证拦截
registry.addInterceptor(authInterceptor)
// 拦截会员中心相关请求接口
.addPathPatterns(ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/**",
// 拦截作家后台相关请求接口
ApiRouterConsts.API_AUTHOR_URL_PREFIX + "/**",
// 拦截平台后台相关请求接口
ApiRouterConsts.API_ADMIN_URL_PREFIX + "/**")
// 放行登录注册相关请求接口
.excludePathPatterns(ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/register",
ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/login",
ApiRouterConsts.API_ADMIN_URL_PREFIX + "/login")
.order(2);
// 拦截会员中心相关请求接口
.addPathPatterns(ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/**",
// 拦截作家后台相关请求接口
ApiRouterConsts.API_AUTHOR_URL_PREFIX + "/**",
// 拦截平台后台相关请求接口
ApiRouterConsts.API_ADMIN_URL_PREFIX + "/**")
// 放行登录注册相关请求接口
.excludePathPatterns(ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/register",
ApiRouterConsts.API_FRONT_USER_URL_PREFIX + "/login",
ApiRouterConsts.API_ADMIN_URL_PREFIX + "/login")
.order(2);
// Token 解析拦截器
registry.addInterceptor(tokenParseInterceptor)
// 拦截小说内容查询接口需要解析 token 以判断该用户是否有权阅读该章节付费章节是否已购买
.addPathPatterns(ApiRouterConsts.API_FRONT_BOOK_URL_PREFIX + "/content/*")
.order(3);
// 拦截小说内容查询接口需要解析 token 以判断该用户是否有权阅读该章节付费章节是否已购买
.addPathPatterns(ApiRouterConsts.API_FRONT_BOOK_URL_PREFIX + "/content/*")
.order(3);
}
}

View File

@ -1,8 +1,7 @@
package io.github.xxyopen.novel.core.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* Xss 过滤配置属性
@ -11,6 +10,6 @@ import java.util.List;
* @date 2022/5/17
*/
@ConfigurationProperties(prefix = "novel.xss")
public record XssProperties(Boolean enabled,List<String> excludes) {
public record XssProperties(Boolean enabled, List<String> excludes) {
}

View File

@ -10,22 +10,22 @@ public class AmqpConsts {
/**
* 小说信息改变 MQ
* */
public static class BookChangeMq{
*/
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

@ -34,32 +34,32 @@ public class ApiRouterConsts {
/**
* 首页模块请求路径前缀
* */
*/
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";
/**
@ -85,11 +85,13 @@ public class ApiRouterConsts {
/**
* 前台门户资源图片/视频/文档相关API请求路径前缀
*/
public static final String API_FRONT_RESOURCE_URL_PREFIX = API_FRONT_URL_PREFIX + RESOURCE_URL_PREFIX;
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_FRONT_SEARCH_URL_PREFIX =
API_FRONT_URL_PREFIX + SEARCH_URL_PREFIX;
}

View File

@ -56,7 +56,7 @@ public class CacheConsts {
/**
* 小说分类列表缓存
* */
*/
public static final String BOOK_CATEGORY_LIST_CACHE_NAME = "bookCategoryListCache";
/**
@ -76,13 +76,14 @@ public class CacheConsts {
/**
* 最近更新小说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 IMG_VERIFY_CODE_CACHE_KEY =
REDIS_CACHE_PREFIX + "imgVerifyCodeCache::";
/**
* 用户信息缓存
@ -111,19 +112,19 @@ public class CacheConsts {
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_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_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),
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),
USER_INFO_CACHE(2, USER_INFO_CACHE_NAME, 60 * 60 * 24, 10000),
AUTHOR_INFO_CACHE(2,AUTHOR_INFO_CACHE_NAME,60 * 60 * 48, 1000);
AUTHOR_INFO_CACHE(2, AUTHOR_INFO_CACHE_NAME, 60 * 60 * 48, 1000);
/**
* 缓存类型 0-本地 1-本地和远程 2-远程

View File

@ -10,7 +10,6 @@ import lombok.Getter;
*/
public class DatabaseConsts {
/**
* 用户信息表
*/

View File

@ -14,8 +14,8 @@ public class EsConsts {
/**
* 小说索引
* */
public static class BookIndex{
*/
public static class BookIndex {
private BookIndex() {
throw new IllegalStateException(SystemConfigConsts.CONST_INSTANCE_EXCEPTION_MSG);
@ -23,18 +23,18 @@ public class EsConsts {
/**
* 索引名
* */
*/
public static final String INDEX_NAME = "book";
/**
* id
*/
public static final String FIELD_ID = "id";
public static final String FIELD_ID = "id";
/**
* 作品方向;0-男频 1-女频
*/
public static final String FIELD_WORK_DIRECTION = "workDirection";
public static final String FIELD_WORK_DIRECTION = "workDirection";
/**
* 类别ID
@ -110,7 +110,7 @@ public class EsConsts {
* 是否收费;1-收费 0-免费
*/
public static final String FIELD_IS_VIP = "isVip";
}
}

View File

@ -14,32 +14,32 @@ public class SystemConfigConsts {
/**
* 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

@ -3,18 +3,22 @@ package io.github.xxyopen.novel.core.filter;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import io.github.xxyopen.novel.core.config.XssProperties;
import io.github.xxyopen.novel.core.wrapper.XssHttpServletRequestWrapper;
import jakarta.servlet.*;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.annotation.WebFilter;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 防止 XSS 攻击的过滤器
*
@ -22,13 +26,12 @@ import java.util.regex.Pattern;
* @date 2022/5/17
*/
@Component
@ConditionalOnProperty(value = "novel.xss.enabled",havingValue = "true")
@ConditionalOnProperty(value = "novel.xss.enabled", havingValue = "true")
@WebFilter(urlPatterns = "/*", filterName = "xssFilter")
@EnableConfigurationProperties(value = {XssProperties.class})
@RequiredArgsConstructor
public class XssFilter implements Filter {
private final XssProperties xssProperties;
@Override
@ -37,13 +40,15 @@ public class XssFilter implements Filter {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
if (handleExcludeUrl(req)) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper((HttpServletRequest) servletRequest);
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) servletRequest);
filterChain.doFilter(xssRequest, servletResponse);
}

View File

@ -9,18 +9,16 @@ import io.github.xxyopen.novel.core.constant.ApiRouterConsts;
import io.github.xxyopen.novel.core.constant.SystemConfigConsts;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import java.nio.charset.StandardCharsets;
import java.util.Map;
/**
* 认证 拦截器
* 为了注入其它的 Spring beans需要通过 @Component 注解将该拦截器注册到 Spring 上下文
* 认证 拦截器为了注入其它的 Spring beans需要通过 @Component 注解将该拦截器注册到 Spring 上下文
*
* @author xiongxiaoyang
* @date 2022/5/18
@ -29,13 +27,14 @@ import java.util.Map;
@RequiredArgsConstructor
public class AuthInterceptor implements HandlerInterceptor {
private final Map<String,AuthStrategy> authStrategy;
private final Map<String, AuthStrategy> authStrategy;
private final ObjectMapper objectMapper;
@SuppressWarnings("NullableProblems")
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 获取登录 JWT
String token = request.getHeader(SystemConfigConsts.HTTP_AUTH_HEADER_NAME);
@ -44,25 +43,27 @@ public class AuthInterceptor implements HandlerInterceptor {
// 根据请求的 URI 得到认证策略
String subUri = requestUri.substring(ApiRouterConsts.API_URL_PREFIX.length() + 1);
String systemName = subUri.substring(0,subUri.indexOf("/"));
String authStrategyName = String.format("%sAuthStrategy",systemName);
String systemName = subUri.substring(0, subUri.indexOf("/"));
String authStrategyName = String.format("%sAuthStrategy", systemName);
// 开始认证
try {
authStrategy.get(authStrategyName).auth(token,requestUri);
authStrategy.get(authStrategyName).auth(token, requestUri);
return HandlerInterceptor.super.preHandle(request, response, handler);
}catch (BusinessException exception){
} catch (BusinessException exception) {
// 认证失败
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(objectMapper.writeValueAsString(RestResp.fail(exception.getErrorCodeEnum())));
response.getWriter().write(
objectMapper.writeValueAsString(RestResp.fail(exception.getErrorCodeEnum())));
return false;
}
}
@SuppressWarnings("NullableProblems")
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 清理当前线程保存的用户数据
UserHolder.clear();
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);

View File

@ -2,15 +2,14 @@ package io.github.xxyopen.novel.core.interceptor;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
/**
* 文件 拦截器
*
@ -26,12 +25,14 @@ public class FileInterceptor implements HandlerInterceptor {
@SuppressWarnings("NullableProblems")
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 获取请求的 URI
String requestUri = request.getRequestURI();
// 缓存10天
response.setDateHeader("expires", System.currentTimeMillis() + 60 * 60 * 24 * 10 * 1000);
try (OutputStream out = response.getOutputStream(); InputStream input = new FileInputStream(fileUploadPath + requestUri)) {
try (OutputStream out = response.getOutputStream(); InputStream input = new FileInputStream(
fileUploadPath + requestUri)) {
byte[] b = new byte[4096];
for (int n; (n = input.read(b)) != -1; ) {
out.write(b, 0, n);

View File

@ -15,20 +15,18 @@ import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.core.common.util.IpUtils;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 流量限制 拦截器
* 实现接口防刷和限流
* 流量限制 拦截器实现接口防刷和限流
*
* @author xiongxiaoyang
* @date 2022/6/1
@ -59,18 +57,19 @@ public class FlowLimitInterceptor implements HandlerInterceptor {
// 接口防刷规则 1所有的请求限制每个 IP 每秒最多只能通过 50 超出限制直接拒绝
ParamFlowRule rule2 = new ParamFlowRule(NOVEL_RESOURCE)
.setParamIdx(0)
.setCount(50);
.setParamIdx(0)
.setCount(50);
// 接口防刷规则 2所有的请求限制每个 IP 每分钟最多只能通过 1000 超出限制直接拒绝
ParamFlowRule rule3 = new ParamFlowRule(NOVEL_RESOURCE)
.setParamIdx(0)
.setCount(1000)
.setDurationInSec(60);
.setParamIdx(0)
.setCount(1000)
.setDurationInSec(60);
ParamFlowRuleManager.loadRules(Arrays.asList(rule2, rule3));
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
String ip = IpUtils.getRealIp(request);
Entry entry = null;
try {
@ -85,7 +84,8 @@ public class FlowLimitInterceptor implements HandlerInterceptor {
log.info("IP:{}被限流了!", ip);
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.getWriter().write(objectMapper.writeValueAsString(RestResp.fail(ErrorCodeEnum.USER_REQ_MANY)));
response.getWriter()
.write(objectMapper.writeValueAsString(RestResp.fail(ErrorCodeEnum.USER_REQ_MANY)));
} finally {
// 注意exit 的时候也一定要带上对应的参数否则可能会有统计错误
if (entry != null) {

View File

@ -24,7 +24,8 @@ public class TokenParseInterceptor implements HandlerInterceptor {
private final JwtUtils jwtUtils;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 获取登录 JWT
String token = request.getHeader(SystemConfigConsts.HTTP_AUTH_HEADER_NAME);
if (StringUtils.hasText(token)) {
@ -35,7 +36,8 @@ public class TokenParseInterceptor implements HandlerInterceptor {
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// 清理当前线程保存的用户数据
UserHolder.clear();
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);

View File

@ -3,9 +3,8 @@ package io.github.xxyopen.novel.core.json.deserializer;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import org.springframework.boot.jackson.JsonComponent;
import java.io.IOException;
import org.springframework.boot.jackson.JsonComponent;
/**
@ -18,16 +17,16 @@ import java.io.IOException;
public class GlobalJsonDeserializer {
/**
* 字符串反序列化器
* 过滤特殊字符解决 XSS 攻击
* 字符串反序列化器过滤特殊字符解决 XSS 攻击
*/
public static class StringDeserializer extends JsonDeserializer<String> {
@Override
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
public String deserialize(JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
return jsonParser.getValueAsString()
.replace("<", "&lt;")
.replace(">", "&gt;");
.replace("<", "&lt;")
.replace(">", "&gt;");
}
}
}

View File

@ -3,7 +3,6 @@ package io.github.xxyopen.novel.core.json.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
/**
@ -15,8 +14,9 @@ import java.io.IOException;
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));
public void serialize(String s, JsonGenerator jsonGenerator,
SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString(s.substring(0, 4) + "****" + s.substring(8));
}
}

View File

@ -21,7 +21,8 @@ import org.springframework.stereotype.Component;
* @date 2022/5/25
*/
@Component
@ConditionalOnProperty(prefix = "spring", name = {"elasticsearch.enable","amqp.enable"}, havingValue = "true")
@ConditionalOnProperty(prefix = "spring", name = {"elasticsearch.enable",
"amqp.enable"}, havingValue = "true")
@RequiredArgsConstructor
@Slf4j
public class RabbitQueueListener {
@ -32,15 +33,15 @@ public class RabbitQueueListener {
/**
* 监听小说信息改变的 ES 更新队列更新最新小说信息到 ES
* */
*/
@RabbitListener(queues = AmqpConsts.BookChangeMq.QUEUE_ES_UPDATE)
@SneakyThrows
public void updateEsBook(Long bookId) {
BookInfo bookInfo = bookInfoMapper.selectById(bookId);
IndexResponse response = esClient.index(i -> i
.index(EsConsts.BookIndex.INDEX_NAME)
.id(bookInfo.getId().toString())
.document(EsBookDto.build(bookInfo))
.index(EsConsts.BookIndex.INDEX_NAME)
.id(bookInfo.getId().toString())
.document(EsBookDto.build(bookInfo))
);
log.info("Indexed with version " + response.version());
}

View File

@ -13,14 +13,13 @@ import io.github.xxyopen.novel.core.constant.EsConsts;
import io.github.xxyopen.novel.dao.entity.BookInfo;
import io.github.xxyopen.novel.dao.mapper.BookInfoMapper;
import io.github.xxyopen.novel.dto.es.EsBookDto;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 小说数据同步到 elasticsearch 任务
*
@ -51,10 +50,10 @@ public class BookToEsTask {
for (; ; ) {
queryWrapper.clear();
queryWrapper
.orderByAsc(DatabaseConsts.CommonColumnEnum.ID.getName())
.gt(DatabaseConsts.CommonColumnEnum.ID.getName(), maxId)
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT, 0)
.last(DatabaseConsts.SqlEnum.LIMIT_30.getSql());
.orderByAsc(DatabaseConsts.CommonColumnEnum.ID.getName())
.gt(DatabaseConsts.CommonColumnEnum.ID.getName(), maxId)
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT, 0)
.last(DatabaseConsts.SqlEnum.LIMIT_30.getSql());
bookInfos = bookInfoMapper.selectList(queryWrapper);
if (bookInfos.isEmpty()) {
break;
@ -63,11 +62,11 @@ public class BookToEsTask {
for (BookInfo book : bookInfos) {
br.operations(op -> op
.index(idx -> idx
.index(EsConsts.BookIndex.INDEX_NAME)
.id(book.getId().toString())
.document(EsBookDto.build(book))
)
.index(idx -> idx
.index(EsConsts.BookIndex.INDEX_NAME)
.id(book.getId().toString())
.document(EsBookDto.build(book))
)
).timeout(Time.of(t -> t.time("10s")));
maxId = book.getId();
}

View File

@ -5,14 +5,13 @@ import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
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 工具类
*
@ -37,21 +36,23 @@ public class JwtUtils {
/**
* 根据用户ID生成JWT
* @param uid 用户ID
*
* @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();
.setHeaderParam(HEADER_SYSTEM_KEY, systemKey)
.setSubject(uid.toString())
.signWith(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
.compact();
}
/**
* 解析JWT返回用户ID
* @param token JWT
*
* @param token JWT
* @param systemKey 系统标识
* @return 用户ID
*/
@ -59,9 +60,9 @@ public class JwtUtils {
Jws<Claims> claimsJws;
try {
claimsJws = Jwts.parserBuilder()
.setSigningKey(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8)))
.build()
.parseClaimsJws(token);
.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)) {

View File

@ -2,7 +2,6 @@ package io.github.xxyopen.novel.core.wrapper;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletRequestWrapper;
import java.util.HashMap;
import java.util.Map;
@ -14,7 +13,7 @@ import java.util.Map;
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
private static final Map<String,String> REPLACE_RULE = new HashMap<>();
private static final Map<String, String> REPLACE_RULE = new HashMap<>();
static {
REPLACE_RULE.put("<", "&lt;");
@ -34,7 +33,8 @@ public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
for (int i = 0; i < length; i++) {
escapeValues[i] = values[i];
int index = i;
REPLACE_RULE.forEach((k, v)-> escapeValues[index] = escapeValues[index].replaceAll(k, v));
REPLACE_RULE.forEach(
(k, v) -> escapeValues[index] = escapeValues[index].replaceAll(k, v));
}
return escapeValues;
}

View File

@ -6,13 +6,12 @@ import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.AuthorInfo;
import io.github.xxyopen.novel.dao.mapper.AuthorInfoMapper;
import io.github.xxyopen.novel.dto.AuthorInfoDto;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 作家信息 缓存管理类
*
@ -29,24 +28,24 @@ public class AuthorInfoCacheManager {
* 查询作家信息并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.REDIS_CACHE_MANAGER,
value = CacheConsts.AUTHOR_INFO_CACHE_NAME, unless = "#result == null")
value = CacheConsts.AUTHOR_INFO_CACHE_NAME, unless = "#result == null")
public AuthorInfoDto getAuthor(Long userId) {
QueryWrapper<AuthorInfo> queryWrapper = new QueryWrapper<>();
queryWrapper
.eq(DatabaseConsts.AuthorInfoTable.COLUMN_USER_ID, userId)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.eq(DatabaseConsts.AuthorInfoTable.COLUMN_USER_ID, userId)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
AuthorInfo authorInfo = authorInfoMapper.selectOne(queryWrapper);
if (Objects.isNull(authorInfo)) {
return null;
}
return AuthorInfoDto.builder()
.id(authorInfo.getId())
.penName(authorInfo.getPenName())
.status(authorInfo.getStatus()).build();
.id(authorInfo.getId())
.penName(authorInfo.getPenName())
.status(authorInfo.getStatus()).build();
}
@CacheEvict(cacheManager = CacheConsts.REDIS_CACHE_MANAGER,
value = CacheConsts.AUTHOR_INFO_CACHE_NAME)
value = CacheConsts.AUTHOR_INFO_CACHE_NAME)
public void evictAuthorCache() {
// 调用此方法自动清除作家信息的缓存
}

View File

@ -6,12 +6,11 @@ import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.BookCategory;
import io.github.xxyopen.novel.dao.mapper.BookCategoryMapper;
import io.github.xxyopen.novel.dto.resp.BookCategoryRespDto;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 小说分类 缓存管理类
*
@ -28,15 +27,15 @@ public class BookCategoryCacheManager {
* 根据作品方向查询小说分类列表并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_CATEGORY_LIST_CACHE_NAME)
value = CacheConsts.BOOK_CATEGORY_LIST_CACHE_NAME)
public List<BookCategoryRespDto> listCategory(Integer workDirection) {
QueryWrapper<BookCategory> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.BookCategoryTable.COLUMN_WORK_DIRECTION, workDirection);
return bookCategoryMapper.selectList(queryWrapper).stream().map(v ->
BookCategoryRespDto.builder()
.id(v.getId())
.name(v.getName())
.build()).toList();
BookCategoryRespDto.builder()
.id(v.getId())
.name(v.getName())
.build()).toList();
}
}

View File

@ -24,17 +24,17 @@ public class BookChapterCacheManager {
* 查询小说章节信息并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_CHAPTER_CACHE_NAME)
value = CacheConsts.BOOK_CHAPTER_CACHE_NAME)
public BookChapterRespDto getChapter(Long chapterId) {
BookChapter bookChapter = bookChapterMapper.selectById(chapterId);
return BookChapterRespDto.builder()
.id(chapterId)
.bookId(bookChapter.getBookId())
.chapterNum(bookChapter.getChapterNum())
.chapterName(bookChapter.getChapterName())
.chapterWordCount(bookChapter.getWordCount())
.chapterUpdateTime(bookChapter.getUpdateTime())
.build();
.id(chapterId)
.bookId(bookChapter.getBookId())
.chapterNum(bookChapter.getChapterNum())
.chapterName(bookChapter.getChapterName())
.chapterWordCount(bookChapter.getWordCount())
.chapterUpdateTime(bookChapter.getUpdateTime())
.build();
}

View File

@ -25,11 +25,11 @@ public class BookContentCacheManager {
* 查询小说内容并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.REDIS_CACHE_MANAGER,
value = CacheConsts.BOOK_CONTENT_CACHE_NAME)
value = CacheConsts.BOOK_CONTENT_CACHE_NAME)
public String getBookContent(Long chapterId) {
QueryWrapper<BookContent> contentQueryWrapper = new QueryWrapper<>();
contentQueryWrapper.eq(DatabaseConsts.BookContentTable.COLUMN_CHAPTER_ID, chapterId)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
BookContent bookContent = bookContentMapper.selectOne(contentQueryWrapper);
return bookContent.getContent();
}

View File

@ -8,14 +8,13 @@ import io.github.xxyopen.novel.dao.entity.BookInfo;
import io.github.xxyopen.novel.dao.mapper.BookChapterMapper;
import io.github.xxyopen.novel.dao.mapper.BookInfoMapper;
import io.github.xxyopen.novel.dto.resp.BookInfoRespDto;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 小说信息 缓存管理类
*
@ -33,8 +32,9 @@ public class BookInfoCacheManager {
/**
* 从缓存中查询小说信息先判断缓存中是否已存在存在则直接从缓存中取否则执行方法体中的逻辑后缓存结果
*/
@Cacheable(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_INFO_CACHE_NAME)
@Cacheable(
cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_INFO_CACHE_NAME)
public BookInfoRespDto getBookInfo(Long id) {
return cachePutBookInfo(id);
}
@ -42,39 +42,41 @@ public class BookInfoCacheManager {
/**
* 缓存小说信息不管缓存中是否存在都执行方法体中的逻辑然后缓存起来
*/
@CachePut(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_INFO_CACHE_NAME)
@CachePut(
cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_INFO_CACHE_NAME)
public BookInfoRespDto cachePutBookInfo(Long id) {
// 查询基础信息
BookInfo bookInfo = bookInfoMapper.selectById(id);
// 查询首章ID
QueryWrapper<BookChapter> queryWrapper = new QueryWrapper<>();
queryWrapper
.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID, id)
.orderByAsc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID, id)
.orderByAsc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
BookChapter firstBookChapter = bookChapterMapper.selectOne(queryWrapper);
// 组装响应对象
return BookInfoRespDto.builder()
.id(bookInfo.getId())
.bookName(bookInfo.getBookName())
.bookDesc(bookInfo.getBookDesc())
.bookStatus(bookInfo.getBookStatus())
.authorId(bookInfo.getAuthorId())
.authorName(bookInfo.getAuthorName())
.categoryId(bookInfo.getCategoryId())
.categoryName(bookInfo.getCategoryName())
.commentCount(bookInfo.getCommentCount())
.firstChapterId(firstBookChapter.getId())
.lastChapterId(bookInfo.getLastChapterId())
.picUrl(bookInfo.getPicUrl())
.visitCount(bookInfo.getVisitCount())
.wordCount(bookInfo.getWordCount())
.build();
.id(bookInfo.getId())
.bookName(bookInfo.getBookName())
.bookDesc(bookInfo.getBookDesc())
.bookStatus(bookInfo.getBookStatus())
.authorId(bookInfo.getAuthorId())
.authorName(bookInfo.getAuthorName())
.categoryId(bookInfo.getCategoryId())
.categoryName(bookInfo.getCategoryName())
.commentCount(bookInfo.getCommentCount())
.firstChapterId(firstBookChapter.getId())
.lastChapterId(bookInfo.getLastChapterId())
.picUrl(bookInfo.getPicUrl())
.visitCount(bookInfo.getVisitCount())
.wordCount(bookInfo.getWordCount())
.build();
}
@CacheEvict(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_INFO_CACHE_NAME)
@CacheEvict(
cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_INFO_CACHE_NAME)
public void evictBookInfoCache(Long ignoredId) {
// 调用此方法自动清除小说信息的缓存
}
@ -82,15 +84,16 @@ public class BookInfoCacheManager {
/**
* 查询每个类别下最新更新的 500 个小说ID列表并放入缓存中 1 个小时
*/
@Cacheable(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.LAST_UPDATE_BOOK_ID_LIST_CACHE_NAME)
@Cacheable(
cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.LAST_UPDATE_BOOK_ID_LIST_CACHE_NAME)
public List<Long> getLastUpdateIdList(Long categoryId) {
QueryWrapper<BookInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.BookTable.COLUMN_CATEGORY_ID, categoryId)
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT,0)
.orderByDesc(DatabaseConsts.BookTable.COLUMN_LAST_CHAPTER_UPDATE_TIME)
.last(DatabaseConsts.SqlEnum.LIMIT_500.getSql());
queryWrapper
.eq(DatabaseConsts.BookTable.COLUMN_CATEGORY_ID, categoryId)
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT, 0)
.orderByDesc(DatabaseConsts.BookTable.COLUMN_LAST_CHAPTER_UPDATE_TIME)
.last(DatabaseConsts.SqlEnum.LIMIT_500.getSql());
return bookInfoMapper.selectList(queryWrapper).stream().map(BookInfo::getId).toList();
}
}

View File

@ -6,12 +6,11 @@ import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.BookInfo;
import io.github.xxyopen.novel.dao.mapper.BookInfoMapper;
import io.github.xxyopen.novel.dto.resp.BookRankRespDto;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 小说排行榜 缓存管理类
*
@ -28,7 +27,7 @@ public class BookRankCacheManager {
* 查询小说点击榜列表并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.REDIS_CACHE_MANAGER,
value = CacheConsts.BOOK_VISIT_RANK_CACHE_NAME)
value = CacheConsts.BOOK_VISIT_RANK_CACHE_NAME)
public List<BookRankRespDto> listVisitRankBooks() {
QueryWrapper<BookInfo> bookInfoQueryWrapper = new QueryWrapper<>();
bookInfoQueryWrapper.orderByDesc(DatabaseConsts.BookTable.COLUMN_VISIT_COUNT);
@ -39,12 +38,12 @@ public class BookRankCacheManager {
* 查询小说新书榜列表并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_NEWEST_RANK_CACHE_NAME)
value = CacheConsts.BOOK_NEWEST_RANK_CACHE_NAME)
public List<BookRankRespDto> listNewestRankBooks() {
QueryWrapper<BookInfo> bookInfoQueryWrapper = new QueryWrapper<>();
bookInfoQueryWrapper
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT,0)
.orderByDesc(DatabaseConsts.CommonColumnEnum.CREATE_TIME.getName());
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT, 0)
.orderByDesc(DatabaseConsts.CommonColumnEnum.CREATE_TIME.getName());
return listRankBooks(bookInfoQueryWrapper);
}
@ -52,19 +51,19 @@ public class BookRankCacheManager {
* 查询小说更新榜列表并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.BOOK_UPDATE_RANK_CACHE_NAME)
value = CacheConsts.BOOK_UPDATE_RANK_CACHE_NAME)
public List<BookRankRespDto> listUpdateRankBooks() {
QueryWrapper<BookInfo> bookInfoQueryWrapper = new QueryWrapper<>();
bookInfoQueryWrapper
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT,0)
.orderByDesc(DatabaseConsts.CommonColumnEnum.UPDATE_TIME.getName());
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT, 0)
.orderByDesc(DatabaseConsts.CommonColumnEnum.UPDATE_TIME.getName());
return listRankBooks(bookInfoQueryWrapper);
}
private List<BookRankRespDto> listRankBooks(QueryWrapper<BookInfo> bookInfoQueryWrapper) {
bookInfoQueryWrapper
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT,0)
.last(DatabaseConsts.SqlEnum.LIMIT_30.getSql());
.gt(DatabaseConsts.BookTable.COLUMN_WORD_COUNT, 0)
.last(DatabaseConsts.SqlEnum.LIMIT_30.getSql());
return bookInfoMapper.selectList(bookInfoQueryWrapper).stream().map(v -> {
BookRankRespDto respDto = new BookRankRespDto();
respDto.setId(v.getId());

View File

@ -6,12 +6,11 @@ import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.HomeFriendLink;
import io.github.xxyopen.novel.dao.mapper.HomeFriendLinkMapper;
import io.github.xxyopen.novel.dto.resp.HomeFriendLinkRespDto;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 友情链接 缓存管理类
*
@ -28,7 +27,7 @@ public class FriendLinkCacheManager {
* 友情链接列表查询并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.REDIS_CACHE_MANAGER,
value = CacheConsts.HOME_FRIEND_LINK_CACHE_NAME)
value = CacheConsts.HOME_FRIEND_LINK_CACHE_NAME)
public List<HomeFriendLinkRespDto> listFriendLinks() {
// 从友情链接表中查询出友情链接列表
QueryWrapper<HomeFriendLink> queryWrapper = new QueryWrapper<>();

View File

@ -8,16 +8,15 @@ import io.github.xxyopen.novel.dao.entity.HomeBook;
import io.github.xxyopen.novel.dao.mapper.BookInfoMapper;
import io.github.xxyopen.novel.dao.mapper.HomeBookMapper;
import io.github.xxyopen.novel.dto.resp.HomeBookRespDto;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
/**
* 首页推荐小说 缓存管理类
@ -37,7 +36,7 @@ public class HomeBookCacheManager {
* 查询首页小说推荐并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.HOME_BOOK_CACHE_NAME)
value = CacheConsts.HOME_BOOK_CACHE_NAME)
public List<HomeBookRespDto> listHomeBooks() {
// 从首页小说推荐表中查询出需要推荐的小说
QueryWrapper<HomeBook> queryWrapper = new QueryWrapper<>();
@ -47,8 +46,8 @@ public class HomeBookCacheManager {
// 获取推荐小说ID列表
if (!CollectionUtils.isEmpty(homeBooks)) {
List<Long> bookIds = homeBooks.stream()
.map(HomeBook::getBookId)
.toList();
.map(HomeBook::getBookId)
.toList();
// 根据小说ID列表查询相关的小说信息列表
QueryWrapper<BookInfo> bookInfoQueryWrapper = new QueryWrapper<>();
@ -56,9 +55,9 @@ public class HomeBookCacheManager {
List<BookInfo> bookInfos = bookInfoMapper.selectList(bookInfoQueryWrapper);
// 组装 HomeBookRespDto 列表数据并返回
if(!CollectionUtils.isEmpty(bookInfos)){
if (!CollectionUtils.isEmpty(bookInfos)) {
Map<Long, BookInfo> bookInfoMap = bookInfos.stream()
.collect(Collectors.toMap(BookInfo::getId, Function.identity()));
.collect(Collectors.toMap(BookInfo::getId, Function.identity()));
return homeBooks.stream().map(v -> {
BookInfo bookInfo = bookInfoMap.get(v.getBookId());
HomeBookRespDto bookRespDto = new HomeBookRespDto();

View File

@ -6,12 +6,11 @@ import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.NewsInfo;
import io.github.xxyopen.novel.dao.mapper.NewsInfoMapper;
import io.github.xxyopen.novel.dto.resp.NewsInfoRespDto;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 新闻 缓存管理类
*
@ -28,20 +27,20 @@ public class NewsCacheManager {
* 最新新闻列表查询并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.CAFFEINE_CACHE_MANAGER,
value = CacheConsts.LATEST_NEWS_CACHE_NAME)
value = CacheConsts.LATEST_NEWS_CACHE_NAME)
public List<NewsInfoRespDto> listLatestNews() {
// 从新闻信息表中查询出最新发布的两条新闻
QueryWrapper<NewsInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc(DatabaseConsts.CommonColumnEnum.CREATE_TIME.getName())
.last(DatabaseConsts.SqlEnum.LIMIT_2.getSql());
.last(DatabaseConsts.SqlEnum.LIMIT_2.getSql());
return newsInfoMapper.selectList(queryWrapper).stream().map(v -> NewsInfoRespDto.builder()
.id(v.getId())
.categoryId(v.getCategoryId())
.categoryName(v.getCategoryName())
.title(v.getTitle())
.sourceName(v.getSourceName())
.updateTime(v.getUpdateTime())
.build()).toList();
.id(v.getId())
.categoryId(v.getCategoryId())
.categoryName(v.getCategoryName())
.title(v.getTitle())
.sourceName(v.getSourceName())
.updateTime(v.getUpdateTime())
.build()).toList();
}
}

View File

@ -4,12 +4,11 @@ import io.github.xxyopen.novel.core.constant.CacheConsts;
import io.github.xxyopen.novel.dao.entity.UserInfo;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper;
import io.github.xxyopen.novel.dto.UserInfoDto;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* 用户信息 缓存管理类
*
@ -26,15 +25,15 @@ public class UserInfoCacheManager {
* 查询用户信息并放入缓存中
*/
@Cacheable(cacheManager = CacheConsts.REDIS_CACHE_MANAGER,
value = CacheConsts.USER_INFO_CACHE_NAME)
value = CacheConsts.USER_INFO_CACHE_NAME)
public UserInfoDto getUser(Long userId) {
UserInfo userInfo = userInfoMapper.selectById(userId);
if (Objects.isNull(userInfo)) {
return null;
}
return UserInfoDto.builder()
.id(userInfo.getId())
.status(userInfo.getStatus()).build();
.id(userInfo.getId())
.status(userInfo.getStatus()).build();
}

View File

@ -4,13 +4,13 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.UserInfo;
import io.github.xxyopen.novel.dao.mapper.UserInfoMapper;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 用户模块 DAO管理类
*
* @author xiongxiaoyang
* @date 2022/5/20
*/
@ -22,12 +22,13 @@ public class UserDaoManager {
/**
* 根据用户ID集合批量查询用户信息列表
*
* @param userIds 需要查询的用户ID集合
* @return 满足条件的用户信息列表
* */
public List<UserInfo> listUsers(List<Long> userIds){
*/
public List<UserInfo> listUsers(List<Long> userIds) {
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.in(DatabaseConsts.CommonColumnEnum.ID.getName(),userIds);
queryWrapper.in(DatabaseConsts.CommonColumnEnum.ID.getName(), userIds);
return userInfoMapper.selectList(queryWrapper);
}

View File

@ -2,6 +2,7 @@ package io.github.xxyopen.novel.manager.mq;
import io.github.xxyopen.novel.core.common.constant.CommonConsts;
import io.github.xxyopen.novel.core.constant.AmqpConsts;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Value;
@ -9,8 +10,6 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import java.util.Objects;
/**
* AMQP 消息管理类
*
@ -35,15 +34,17 @@ public class AmqpMsgManager {
}
}
private void sendAmqpMessage(AmqpTemplate amqpTemplate, String exchange, String routingKey, Object message) {
private void sendAmqpMessage(AmqpTemplate amqpTemplate, String exchange, String routingKey,
Object message) {
// 如果在事务中则在事务执行完成后再发送否则可以直接发送
if (TransactionSynchronizationManager.isActualTransactionActive()) {
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
@Override
public void afterCommit() {
amqpTemplate.convertAndSend(exchange, routingKey, message);
}
});
TransactionSynchronizationManager.registerSynchronization(
new TransactionSynchronization() {
@Override
public void afterCommit() {
amqpTemplate.convertAndSend(exchange, routingKey, message);
}
});
return;
}
amqpTemplate.convertAndSend(exchange, routingKey, message);

View File

@ -2,15 +2,14 @@ package io.github.xxyopen.novel.manager.redis;
import io.github.xxyopen.novel.core.common.util.ImgVerifyCodeUtils;
import io.github.xxyopen.novel.core.constant.CacheConsts;
import java.io.IOException;
import java.time.Duration;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.time.Duration;
import java.util.Objects;
/**
* 验证码 管理类
*
@ -31,7 +30,7 @@ public class VerifyCodeManager {
String verifyCode = ImgVerifyCodeUtils.getRandomVerifyCode(4);
String img = ImgVerifyCodeUtils.genVerifyCodeImg(verifyCode);
stringRedisTemplate.opsForValue().set(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + sessionId,
verifyCode, Duration.ofMinutes(5));
verifyCode, Duration.ofMinutes(5));
return img;
}
@ -39,7 +38,8 @@ public class VerifyCodeManager {
* 校验图形验证码
*/
public boolean imgVerifyCodeOk(String sessionId, String verifyCode) {
return Objects.equals(stringRedisTemplate.opsForValue().get(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + sessionId), verifyCode);
return Objects.equals(stringRedisTemplate.opsForValue()
.get(CacheConsts.IMG_VERIFY_CODE_CACHE_KEY + sessionId), verifyCode);
}
/**

View File

@ -21,8 +21,9 @@ public interface AuthorService {
/**
* 查询作家状态
*
* @param userId 用户ID
* @return 作家状态
* */
*/
RestResp<Integer> getStatus(Long userId);
}

View File

@ -11,7 +11,11 @@ import io.github.xxyopen.novel.core.common.req.PageReqDto;
import io.github.xxyopen.novel.core.common.resp.PageRespDto;
import io.github.xxyopen.novel.core.common.resp.RestResp;
import io.github.xxyopen.novel.core.constant.DatabaseConsts;
import io.github.xxyopen.novel.dao.entity.*;
import io.github.xxyopen.novel.dao.entity.BookChapter;
import io.github.xxyopen.novel.dao.entity.BookComment;
import io.github.xxyopen.novel.dao.entity.BookContent;
import io.github.xxyopen.novel.dao.entity.BookInfo;
import io.github.xxyopen.novel.dao.entity.UserInfo;
import io.github.xxyopen.novel.dao.mapper.BookChapterMapper;
import io.github.xxyopen.novel.dao.mapper.BookCommentMapper;
import io.github.xxyopen.novel.dao.mapper.BookContentMapper;
@ -20,23 +24,39 @@ import io.github.xxyopen.novel.dto.AuthorInfoDto;
import io.github.xxyopen.novel.dto.req.BookAddReqDto;
import io.github.xxyopen.novel.dto.req.ChapterAddReqDto;
import io.github.xxyopen.novel.dto.req.UserCommentReqDto;
import io.github.xxyopen.novel.dto.resp.*;
import io.github.xxyopen.novel.manager.cache.*;
import io.github.xxyopen.novel.dto.resp.BookCategoryRespDto;
import io.github.xxyopen.novel.dto.resp.BookChapterAboutRespDto;
import io.github.xxyopen.novel.dto.resp.BookChapterRespDto;
import io.github.xxyopen.novel.dto.resp.BookCommentRespDto;
import io.github.xxyopen.novel.dto.resp.BookContentAboutRespDto;
import io.github.xxyopen.novel.dto.resp.BookInfoRespDto;
import io.github.xxyopen.novel.dto.resp.BookRankRespDto;
import io.github.xxyopen.novel.manager.cache.AuthorInfoCacheManager;
import io.github.xxyopen.novel.manager.cache.BookCategoryCacheManager;
import io.github.xxyopen.novel.manager.cache.BookChapterCacheManager;
import io.github.xxyopen.novel.manager.cache.BookContentCacheManager;
import io.github.xxyopen.novel.manager.cache.BookInfoCacheManager;
import io.github.xxyopen.novel.manager.cache.BookRankCacheManager;
import io.github.xxyopen.novel.manager.dao.UserDaoManager;
import io.github.xxyopen.novel.manager.mq.AmqpMsgManager;
import io.github.xxyopen.novel.service.BookService;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Random;
import java.util.function.Function;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* 小说模块 服务实现类
*
@ -100,7 +120,8 @@ public class BookServiceImpl implements BookService {
BookInfoRespDto bookInfo = bookInfoCacheManager.getBookInfo(bookId);
// 查询最新章节信息
BookChapterRespDto bookChapter = bookChapterCacheManager.getChapter(bookInfo.getLastChapterId());
BookChapterRespDto bookChapter = bookChapterCacheManager.getChapter(
bookInfo.getLastChapterId());
// 查询章节内容
String content = bookContentCacheManager.getBookContent(bookInfo.getLastChapterId());
@ -112,14 +133,15 @@ public class BookServiceImpl implements BookService {
// 组装数据并返回
return RestResp.ok(BookChapterAboutRespDto.builder()
.chapterInfo(bookChapter)
.chapterTotal(chapterTotal)
.contentSummary(content.substring(0, 30))
.build());
.chapterInfo(bookChapter)
.chapterTotal(chapterTotal)
.contentSummary(content.substring(0, 30))
.build());
}
@Override
public RestResp<List<BookInfoRespDto>> listRecBooks(Long bookId) throws NoSuchAlgorithmException {
public RestResp<List<BookInfoRespDto>> listRecBooks(Long bookId)
throws NoSuchAlgorithmException {
Long categoryId = bookInfoCacheManager.getBookInfo(bookId).getCategoryId();
List<Long> lastUpdateIdList = bookInfoCacheManager.getLastUpdateIdList(categoryId);
List<BookInfoRespDto> respDtoList = new ArrayList<>();
@ -155,13 +177,13 @@ public class BookServiceImpl implements BookService {
// 查询上一章信息并返回章节ID
QueryWrapper<BookChapter> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID, bookId)
.lt(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM, chapterNum)
.orderByDesc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.lt(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM, chapterNum)
.orderByDesc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
return RestResp.ok(
Optional.ofNullable(bookChapterMapper.selectOne(queryWrapper))
.map(BookChapter::getId)
.orElse(null)
Optional.ofNullable(bookChapterMapper.selectOne(queryWrapper))
.map(BookChapter::getId)
.orElse(null)
);
}
@ -175,13 +197,13 @@ public class BookServiceImpl implements BookService {
// 查询下一章信息并返回章节ID
QueryWrapper<BookChapter> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID, bookId)
.gt(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM, chapterNum)
.orderByAsc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.gt(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM, chapterNum)
.orderByAsc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
return RestResp.ok(
Optional.ofNullable(bookChapterMapper.selectOne(queryWrapper))
.map(BookChapter::getId)
.orElse(null)
Optional.ofNullable(bookChapterMapper.selectOne(queryWrapper))
.map(BookChapter::getId)
.orElse(null)
);
}
@ -189,8 +211,9 @@ public class BookServiceImpl implements BookService {
public RestResp<List<BookChapterRespDto>> listChapters(Long bookId) {
QueryWrapper<BookChapter> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID, bookId)
.orderByAsc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM);
return RestResp.ok(bookChapterMapper.selectList(queryWrapper).stream().map(v -> BookChapterRespDto.builder()
.orderByAsc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM);
return RestResp.ok(bookChapterMapper.selectList(queryWrapper).stream()
.map(v -> BookChapterRespDto.builder()
.id(v.getId())
.chapterName(v.getChapterName())
.isVip(v.getIsVip())
@ -204,11 +227,12 @@ public class BookServiceImpl implements BookService {
@Lock(prefix = "userComment")
@Override
public RestResp<Void> saveComment(@Key(expr = "#{userId + '::' + bookId}") UserCommentReqDto dto) {
public RestResp<Void> saveComment(
@Key(expr = "#{userId + '::' + bookId}") UserCommentReqDto dto) {
// 校验用户是否已发表评论
QueryWrapper<BookComment> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.BookCommentTable.COLUMN_USER_ID, dto.getUserId())
.eq(DatabaseConsts.BookCommentTable.COLUMN_BOOK_ID, dto.getBookId());
.eq(DatabaseConsts.BookCommentTable.COLUMN_BOOK_ID, dto.getBookId());
if (bookCommentMapper.selectCount(queryWrapper) > 0) {
// 用户已发表评论
return RestResp.fail(ErrorCodeEnum.USER_COMMENTED);
@ -229,28 +253,30 @@ public class BookServiceImpl implements BookService {
QueryWrapper<BookComment> commentCountQueryWrapper = new QueryWrapper<>();
commentCountQueryWrapper.eq(DatabaseConsts.BookCommentTable.COLUMN_BOOK_ID, bookId);
Long commentTotal = bookCommentMapper.selectCount(commentCountQueryWrapper);
BookCommentRespDto bookCommentRespDto = BookCommentRespDto.builder().commentTotal(commentTotal).build();
BookCommentRespDto bookCommentRespDto = BookCommentRespDto.builder()
.commentTotal(commentTotal).build();
if (commentTotal > 0) {
// 查询最新的评论列表
QueryWrapper<BookComment> commentQueryWrapper = new QueryWrapper<>();
commentQueryWrapper.eq(DatabaseConsts.BookCommentTable.COLUMN_BOOK_ID, bookId)
.orderByDesc(DatabaseConsts.CommonColumnEnum.CREATE_TIME.getName())
.last(DatabaseConsts.SqlEnum.LIMIT_5.getSql());
.orderByDesc(DatabaseConsts.CommonColumnEnum.CREATE_TIME.getName())
.last(DatabaseConsts.SqlEnum.LIMIT_5.getSql());
List<BookComment> bookComments = bookCommentMapper.selectList(commentQueryWrapper);
// 查询评论用户信息并设置需要返回的评论用户名
List<Long> userIds = bookComments.stream().map(BookComment::getUserId).toList();
List<UserInfo> userInfos = userDaoManager.listUsers(userIds);
Map<Long, UserInfo> userInfoMap = userInfos.stream().collect(Collectors.toMap(UserInfo::getId, Function.identity()));
Map<Long, UserInfo> userInfoMap = userInfos.stream()
.collect(Collectors.toMap(UserInfo::getId, Function.identity()));
List<BookCommentRespDto.CommentInfo> commentInfos = bookComments.stream()
.map(v -> BookCommentRespDto.CommentInfo.builder()
.id(v.getId())
.commentUserId(v.getUserId())
.commentUser(userInfoMap.get(v.getUserId()).getUsername())
.commentUserPhoto(userInfoMap.get(v.getUserId()).getUserPhoto())
.commentContent(v.getCommentContent())
.commentTime(v.getCreateTime()).build()).toList();
.map(v -> BookCommentRespDto.CommentInfo.builder()
.id(v.getId())
.commentUserId(v.getUserId())
.commentUser(userInfoMap.get(v.getUserId()).getUsername())
.commentUserPhoto(userInfoMap.get(v.getUserId()).getUserPhoto())
.commentContent(v.getCommentContent())
.commentTime(v.getCreateTime()).build()).toList();
bookCommentRespDto.setComments(commentInfos);
} else {
bookCommentRespDto.setComments(Collections.emptyList());
@ -262,7 +288,7 @@ public class BookServiceImpl implements BookService {
public RestResp<Void> deleteComment(Long userId, Long commentId) {
QueryWrapper<BookComment> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.CommonColumnEnum.ID.getName(), commentId)
.eq(DatabaseConsts.BookCommentTable.COLUMN_USER_ID, userId);
.eq(DatabaseConsts.BookCommentTable.COLUMN_USER_ID, userId);
bookCommentMapper.delete(queryWrapper);
return RestResp.ok();
}
@ -271,7 +297,7 @@ public class BookServiceImpl implements BookService {
public RestResp<Void> updateComment(Long userId, Long id, String content) {
QueryWrapper<BookComment> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.CommonColumnEnum.ID.getName(), id)
.eq(DatabaseConsts.BookCommentTable.COLUMN_USER_ID, userId);
.eq(DatabaseConsts.BookCommentTable.COLUMN_USER_ID, userId);
BookComment bookComment = new BookComment();
bookComment.setCommentContent(content);
bookCommentMapper.update(bookComment, queryWrapper);
@ -320,8 +346,8 @@ public class BookServiceImpl implements BookService {
int chapterNum = 0;
QueryWrapper<BookChapter> chapterQueryWrapper = new QueryWrapper<>();
chapterQueryWrapper.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID, dto.getBookId())
.orderByDesc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.orderByDesc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
BookChapter bookChapter = bookChapterMapper.selectOne(chapterQueryWrapper);
if (Objects.nonNull(bookChapter)) {
chapterNum = bookChapter.getChapterNum() + 1;
@ -369,18 +395,18 @@ public class BookServiceImpl implements BookService {
page.setSize(dto.getPageSize());
QueryWrapper<BookInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.BookTable.AUTHOR_ID, UserHolder.getAuthorId())
.orderByDesc(DatabaseConsts.CommonColumnEnum.CREATE_TIME.getName());
.orderByDesc(DatabaseConsts.CommonColumnEnum.CREATE_TIME.getName());
IPage<BookInfo> bookInfoPage = bookInfoMapper.selectPage(page, queryWrapper);
return RestResp.ok(PageRespDto.of(dto.getPageNum(), dto.getPageSize(), page.getTotal(),
bookInfoPage.getRecords().stream().map(v -> BookInfoRespDto.builder()
.id(v.getId())
.bookName(v.getBookName())
.picUrl(v.getPicUrl())
.categoryName(v.getCategoryName())
.wordCount(v.getWordCount())
.visitCount(v.getVisitCount())
.updateTime(v.getUpdateTime())
.build()).toList()));
bookInfoPage.getRecords().stream().map(v -> BookInfoRespDto.builder()
.id(v.getId())
.bookName(v.getBookName())
.picUrl(v.getPicUrl())
.categoryName(v.getCategoryName())
.wordCount(v.getWordCount())
.visitCount(v.getVisitCount())
.updateTime(v.getUpdateTime())
.build()).toList()));
}
@Override
@ -390,15 +416,15 @@ public class BookServiceImpl implements BookService {
page.setSize(dto.getPageSize());
QueryWrapper<BookChapter> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.BookChapterTable.COLUMN_BOOK_ID, bookId)
.orderByDesc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM);
.orderByDesc(DatabaseConsts.BookChapterTable.COLUMN_CHAPTER_NUM);
IPage<BookChapter> bookChapterPage = bookChapterMapper.selectPage(page, queryWrapper);
return RestResp.ok(PageRespDto.of(dto.getPageNum(), dto.getPageSize(), page.getTotal(),
bookChapterPage.getRecords().stream().map(v -> BookChapterRespDto.builder()
.id(v.getId())
.chapterName(v.getChapterName())
.chapterUpdateTime(v.getUpdateTime())
.isVip(v.getIsVip())
.build()).toList()));
bookChapterPage.getRecords().stream().map(v -> BookChapterRespDto.builder()
.id(v.getId())
.chapterName(v.getChapterName())
.chapterUpdateTime(v.getUpdateTime())
.isVip(v.getIsVip())
.build()).toList()));
}
@Override
@ -415,9 +441,9 @@ public class BookServiceImpl implements BookService {
// 组装数据并返回
return RestResp.ok(BookContentAboutRespDto.builder()
.bookInfo(bookInfo)
.chapterInfo(bookChapter)
.bookContent(content)
.build());
.bookInfo(bookInfo)
.chapterInfo(bookChapter)
.bookContent(content)
.build());
}
}

View File

@ -8,13 +8,12 @@ import io.github.xxyopen.novel.dao.mapper.BookInfoMapper;
import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
import io.github.xxyopen.novel.dto.resp.BookInfoRespDto;
import io.github.xxyopen.novel.service.SearchService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 数据库搜索 服务实现类
*
@ -35,17 +34,18 @@ public class DbSearchServiceImpl implements SearchService {
page.setCurrent(condition.getPageNum());
page.setSize(condition.getPageSize());
List<BookInfo> bookInfos = bookInfoMapper.searchBooks(page, condition);
return RestResp.ok(PageRespDto.of(condition.getPageNum(), condition.getPageSize(), page.getTotal(),
return RestResp.ok(
PageRespDto.of(condition.getPageNum(), condition.getPageSize(), page.getTotal(),
bookInfos.stream().map(v -> BookInfoRespDto.builder()
.id(v.getId())
.bookName(v.getBookName())
.categoryId(v.getCategoryId())
.categoryName(v.getCategoryName())
.authorId(v.getAuthorId())
.authorName(v.getAuthorName())
.wordCount(v.getWordCount())
.lastChapterName(v.getLastChapterName())
.build()).toList()));
.id(v.getId())
.bookName(v.getBookName())
.categoryId(v.getCategoryId())
.categoryName(v.getCategoryName())
.authorId(v.getAuthorId())
.authorName(v.getAuthorName())
.wordCount(v.getWordCount())
.lastChapterName(v.getLastChapterName())
.build()).toList()));
}
}

View File

@ -19,16 +19,15 @@ import io.github.xxyopen.novel.dto.es.EsBookDto;
import io.github.xxyopen.novel.dto.req.BookSearchReqDto;
import io.github.xxyopen.novel.dto.resp.BookInfoRespDto;
import io.github.xxyopen.novel.service.SearchService;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Elasticsearch 搜索 服务实现类
*
@ -49,28 +48,28 @@ public class EsSearchServiceImpl implements SearchService {
SearchResponse<EsBookDto> response = esClient.search(s -> {
SearchRequest.Builder searchBuilder = s.index(EsConsts.BookIndex.INDEX_NAME);
// 构建检索条件
buildSearchCondition(condition, searchBuilder);
// 排序
if (!StringUtils.isBlank(condition.getSort())) {
searchBuilder.sort(o -> o.field(f -> f
.field(StringUtils.underlineToCamel(condition.getSort().split(" ")[0]))
.order(SortOrder.Desc))
);
}
// 分页
searchBuilder.from((condition.getPageNum() - 1) * condition.getPageSize())
.size(condition.getPageSize());
// 设置高亮显示
searchBuilder.highlight(h -> h.fields(EsConsts.BookIndex.FIELD_BOOK_NAME,
t -> t.preTags("<em style='color:red'>").postTags("</em>"))
.fields(EsConsts.BookIndex.FIELD_AUTHOR_NAME,
t -> t.preTags("<em style='color:red'>").postTags("</em>")));
SearchRequest.Builder searchBuilder = s.index(EsConsts.BookIndex.INDEX_NAME);
// 构建检索条件
buildSearchCondition(condition, searchBuilder);
// 排序
if (!StringUtils.isBlank(condition.getSort())) {
searchBuilder.sort(o -> o.field(f -> f
.field(StringUtils.underlineToCamel(condition.getSort().split(" ")[0]))
.order(SortOrder.Desc))
);
}
// 分页
searchBuilder.from((condition.getPageNum() - 1) * condition.getPageSize())
.size(condition.getPageSize());
// 设置高亮显示
searchBuilder.highlight(h -> h.fields(EsConsts.BookIndex.FIELD_BOOK_NAME,
t -> t.preTags("<em style='color:red'>").postTags("</em>"))
.fields(EsConsts.BookIndex.FIELD_AUTHOR_NAME,
t -> t.preTags("<em style='color:red'>").postTags("</em>")));
return searchBuilder;
},
EsBookDto.class
return searchBuilder;
},
EsBookDto.class
);
TotalHits total = response.hits().total();
@ -84,45 +83,49 @@ public class EsSearchServiceImpl implements SearchService {
if (!CollectionUtils.isEmpty(hit.highlight().get(EsConsts.BookIndex.FIELD_BOOK_NAME))) {
book.setBookName(hit.highlight().get(EsConsts.BookIndex.FIELD_BOOK_NAME).get(0));
}
if (!CollectionUtils.isEmpty(hit.highlight().get(EsConsts.BookIndex.FIELD_AUTHOR_NAME))) {
book.setAuthorName(hit.highlight().get(EsConsts.BookIndex.FIELD_AUTHOR_NAME).get(0));
if (!CollectionUtils.isEmpty(
hit.highlight().get(EsConsts.BookIndex.FIELD_AUTHOR_NAME))) {
book.setAuthorName(
hit.highlight().get(EsConsts.BookIndex.FIELD_AUTHOR_NAME).get(0));
}
list.add(BookInfoRespDto.builder()
.id(book.getId())
.bookName(book.getBookName())
.categoryId(book.getCategoryId())
.categoryName(book.getCategoryName())
.authorId(book.getAuthorId())
.authorName(book.getAuthorName())
.wordCount(book.getWordCount())
.lastChapterName(book.getLastChapterName())
.build());
.id(book.getId())
.bookName(book.getBookName())
.categoryId(book.getCategoryId())
.categoryName(book.getCategoryName())
.authorId(book.getAuthorId())
.authorName(book.getAuthorName())
.wordCount(book.getWordCount())
.lastChapterName(book.getLastChapterName())
.build());
}
assert total != null;
return RestResp.ok(PageRespDto.of(condition.getPageNum(), condition.getPageSize(), total.value(), list));
return RestResp.ok(
PageRespDto.of(condition.getPageNum(), condition.getPageSize(), total.value(), list));
}
/**
* 构建检索条件
*/
private void buildSearchCondition(BookSearchReqDto condition, SearchRequest.Builder searchBuilder) {
private void buildSearchCondition(BookSearchReqDto condition,
SearchRequest.Builder searchBuilder) {
BoolQuery boolQuery = BoolQuery.of(b -> {
// 只查有字数的小说
b.must(RangeQuery.of(m -> m
.field(EsConsts.BookIndex.FIELD_WORD_COUNT)
.gt(JsonData.of(0))
.field(EsConsts.BookIndex.FIELD_WORD_COUNT)
.gt(JsonData.of(0))
)._toQuery());
if (!StringUtils.isBlank(condition.getKeyword())) {
// 关键词匹配
b.must((q -> q.multiMatch(t -> t
.fields(EsConsts.BookIndex.FIELD_BOOK_NAME + "^2",
EsConsts.BookIndex.FIELD_AUTHOR_NAME + "^1.8",
EsConsts.BookIndex.FIELD_BOOK_DESC + "^0.1")
.query(condition.getKeyword())
.fields(EsConsts.BookIndex.FIELD_BOOK_NAME + "^2",
EsConsts.BookIndex.FIELD_AUTHOR_NAME + "^1.8",
EsConsts.BookIndex.FIELD_BOOK_DESC + "^0.1")
.query(condition.getKeyword())
)
));
}
@ -130,37 +133,37 @@ public class EsSearchServiceImpl implements SearchService {
// 精确查询
if (Objects.nonNull(condition.getWorkDirection())) {
b.must(TermQuery.of(m -> m
.field(EsConsts.BookIndex.FIELD_WORK_DIRECTION)
.value(condition.getWorkDirection())
.field(EsConsts.BookIndex.FIELD_WORK_DIRECTION)
.value(condition.getWorkDirection())
)._toQuery());
}
if (Objects.nonNull(condition.getCategoryId())) {
b.must(TermQuery.of(m -> m
.field(EsConsts.BookIndex.FIELD_CATEGORY_ID)
.value(condition.getCategoryId())
.field(EsConsts.BookIndex.FIELD_CATEGORY_ID)
.value(condition.getCategoryId())
)._toQuery());
}
// 范围查询
if (Objects.nonNull(condition.getWordCountMin())) {
b.must(RangeQuery.of(m -> m
.field(EsConsts.BookIndex.FIELD_WORD_COUNT)
.gte(JsonData.of(condition.getWordCountMin()))
.field(EsConsts.BookIndex.FIELD_WORD_COUNT)
.gte(JsonData.of(condition.getWordCountMin()))
)._toQuery());
}
if (Objects.nonNull(condition.getWordCountMax())) {
b.must(RangeQuery.of(m -> m
.field(EsConsts.BookIndex.FIELD_WORD_COUNT)
.lt(JsonData.of(condition.getWordCountMax()))
.field(EsConsts.BookIndex.FIELD_WORD_COUNT)
.lt(JsonData.of(condition.getWordCountMax()))
)._toQuery());
}
if (Objects.nonNull(condition.getUpdateTimeMin())) {
b.must(RangeQuery.of(m -> m
.field(EsConsts.BookIndex.FIELD_LAST_CHAPTER_UPDATE_TIME)
.gte(JsonData.of(condition.getUpdateTimeMin().getTime()))
.field(EsConsts.BookIndex.FIELD_LAST_CHAPTER_UPDATE_TIME)
.gte(JsonData.of(condition.getUpdateTimeMin().getTime()))
)._toQuery());
}

View File

@ -10,11 +10,10 @@ import io.github.xxyopen.novel.dao.mapper.NewsInfoMapper;
import io.github.xxyopen.novel.dto.resp.NewsInfoRespDto;
import io.github.xxyopen.novel.manager.cache.NewsCacheManager;
import io.github.xxyopen.novel.service.NewsService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 新闻模块 服务实现类
*
@ -41,13 +40,13 @@ public class NewsServiceImpl implements NewsService {
NewsInfo newsInfo = newsInfoMapper.selectById(id);
QueryWrapper<NewsContent> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.NewsContentTable.COLUMN_NEWS_ID, id)
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
NewsContent newsContent = newsContentMapper.selectOne(queryWrapper);
return RestResp.ok(NewsInfoRespDto.builder()
.title(newsInfo.getTitle())
.sourceName(newsInfo.getSourceName())
.updateTime(newsInfo.getUpdateTime())
.content(newsContent.getContent())
.build());
.title(newsInfo.getTitle())
.sourceName(newsInfo.getSourceName())
.updateTime(newsInfo.getUpdateTime())
.content(newsContent.getContent())
.build());
}
}

View File

@ -8,20 +8,19 @@ import io.github.xxyopen.novel.core.constant.SystemConfigConsts;
import io.github.xxyopen.novel.dto.resp.ImgVerifyCodeRespDto;
import io.github.xxyopen.novel.manager.redis.VerifyCodeManager;
import io.github.xxyopen.novel.service.ResourceService;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;
import javax.imageio.ImageIO;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
/**
* 资源图片/视频/文档相关服务实现类
@ -43,9 +42,9 @@ public class ResourceServiceImpl implements ResourceService {
public RestResp<ImgVerifyCodeRespDto> getImgVerifyCode() throws IOException {
String sessionId = IdWorker.get32UUID();
return RestResp.ok(ImgVerifyCodeRespDto.builder()
.sessionId(sessionId)
.img(verifyCodeManager.genImgVerifyCode(sessionId))
.build());
.sessionId(sessionId)
.img(verifyCodeManager.genImgVerifyCode(sessionId))
.build());
}
@SneakyThrows
@ -53,10 +52,10 @@ public class ResourceServiceImpl implements ResourceService {
public RestResp<String> uploadImage(MultipartFile file) {
LocalDateTime now = LocalDateTime.now();
String savePath =
SystemConfigConsts.IMAGE_UPLOAD_DIRECTORY
+ now.format(DateTimeFormatter.ofPattern("yyyy")) + File.separator
+ now.format(DateTimeFormatter.ofPattern("MM")) + File.separator
+ now.format(DateTimeFormatter.ofPattern("dd"));
SystemConfigConsts.IMAGE_UPLOAD_DIRECTORY
+ now.format(DateTimeFormatter.ofPattern("yyyy")) + File.separator
+ now.format(DateTimeFormatter.ofPattern("MM")) + File.separator
+ now.format(DateTimeFormatter.ofPattern("dd"));
String oriName = file.getOriginalFilename();
assert oriName != null;
String saveFileName = IdWorker.get32UUID() + oriName.substring(oriName.lastIndexOf("."));

View File

@ -22,13 +22,12 @@ import io.github.xxyopen.novel.dto.resp.UserLoginRespDto;
import io.github.xxyopen.novel.dto.resp.UserRegisterRespDto;
import io.github.xxyopen.novel.manager.redis.VerifyCodeManager;
import io.github.xxyopen.novel.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.Objects;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
/**
* 会员模块 服务实现类
@ -61,7 +60,7 @@ public class UserServiceImpl implements UserService {
// 校验手机号是否已注册
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.UserInfoTable.COLUMN_USERNAME, dto.getUsername())
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
if (userInfoMapper.selectCount(queryWrapper) > 0) {
// 手机号已注册
throw new BusinessException(ErrorCodeEnum.USER_NAME_EXIST);
@ -69,7 +68,8 @@ public class UserServiceImpl implements UserService {
// 注册成功保存用户信息
UserInfo userInfo = new UserInfo();
userInfo.setPassword(DigestUtils.md5DigestAsHex(dto.getPassword().getBytes(StandardCharsets.UTF_8)));
userInfo.setPassword(
DigestUtils.md5DigestAsHex(dto.getPassword().getBytes(StandardCharsets.UTF_8)));
userInfo.setUsername(dto.getUsername());
userInfo.setNickName(dto.getUsername());
userInfo.setCreateTime(LocalDateTime.now());
@ -82,10 +82,10 @@ public class UserServiceImpl implements UserService {
// 生成JWT 并返回
return RestResp.ok(
UserRegisterRespDto.builder()
.token(jwtUtils.generateToken(userInfo.getId(), SystemConfigConsts.NOVEL_FRONT_KEY))
.uid(userInfo.getId())
.build()
UserRegisterRespDto.builder()
.token(jwtUtils.generateToken(userInfo.getId(), SystemConfigConsts.NOVEL_FRONT_KEY))
.uid(userInfo.getId())
.build()
);
}
@ -95,7 +95,7 @@ public class UserServiceImpl implements UserService {
// 查询用户信息
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.UserInfoTable.COLUMN_USERNAME, dto.getUsername())
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
.last(DatabaseConsts.SqlEnum.LIMIT_1.getSql());
UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);
if (Objects.isNull(userInfo)) {
// 用户不存在
@ -104,16 +104,16 @@ public class UserServiceImpl implements UserService {
// 判断密码是否正确
if (!Objects.equals(userInfo.getPassword()
, DigestUtils.md5DigestAsHex(dto.getPassword().getBytes(StandardCharsets.UTF_8)))) {
, DigestUtils.md5DigestAsHex(dto.getPassword().getBytes(StandardCharsets.UTF_8)))) {
// 密码错误
throw new BusinessException(ErrorCodeEnum.USER_PASSWORD_ERROR);
}
// 登录成功生成JWT并返回
return RestResp.ok(UserLoginRespDto.builder()
.token(jwtUtils.generateToken(userInfo.getId(), SystemConfigConsts.NOVEL_FRONT_KEY))
.uid(userInfo.getId())
.nickName(userInfo.getNickName()).build());
.token(jwtUtils.generateToken(userInfo.getId(), SystemConfigConsts.NOVEL_FRONT_KEY))
.uid(userInfo.getId())
.nickName(userInfo.getNickName()).build());
}
@Override
@ -142,7 +142,7 @@ public class UserServiceImpl implements UserService {
public RestResp<Void> deleteFeedback(Long userId, Long id) {
QueryWrapper<UserFeedback> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.CommonColumnEnum.ID.getName(), id)
.eq(DatabaseConsts.UserFeedBackTable.COLUMN_USER_ID, userId);
.eq(DatabaseConsts.UserFeedBackTable.COLUMN_USER_ID, userId);
userFeedbackMapper.delete(queryWrapper);
return RestResp.ok();
}
@ -151,11 +151,11 @@ public class UserServiceImpl implements UserService {
public RestResp<Integer> getBookshelfStatus(Long userId, String bookId) {
QueryWrapper<UserBookshelf> queryWrapper = new QueryWrapper<>();
queryWrapper.eq(DatabaseConsts.UserBookshelfTable.COLUMN_USER_ID, userId)
.eq(DatabaseConsts.UserBookshelfTable.COLUMN_BOOK_ID, bookId);
.eq(DatabaseConsts.UserBookshelfTable.COLUMN_BOOK_ID, bookId);
return RestResp.ok(
userBookshelfMapper.selectCount(queryWrapper) > 0
? CommonConsts.YES
: CommonConsts.NO
userBookshelfMapper.selectCount(queryWrapper) > 0
? CommonConsts.YES
: CommonConsts.NO
);
}
@ -163,9 +163,9 @@ public class UserServiceImpl implements UserService {
public RestResp<UserInfoRespDto> getUserInfo(Long userId) {
UserInfo userInfo = userInfoMapper.selectById(userId);
return RestResp.ok(UserInfoRespDto.builder()
.nickName(userInfo.getNickName())
.userSex(userInfo.getUserSex())
.userPhoto(userInfo.getUserPhoto())
.build());
.nickName(userInfo.getNickName())
.userSex(userInfo.getUserSex())
.userPhoto(userInfo.getUserPhoto())
.build());
}
}