From c47b5f51810d2685f7f6cccb4a8bc1493138ab57 Mon Sep 17 00:00:00 2001 From: Colorful Date: Tue, 6 Jul 2021 18:28:53 +0800 Subject: [PATCH 01/20] Refactor/replace deprecated usage (#236) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: 修正README锚点不跳转的问题 * fix:#190 * fix:#184 * chore(deps-dev): bump freemarker from 2.3.30 to 2.3.31 Bumps freemarker from 2.3.30 to 2.3.31. Signed-off-by: dependabot[bot] * chore(deps): bump lin-cms-spring-boot-starter Bumps [lin-cms-spring-boot-starter](https://github.com/TaleLin/lin-cms-java-core) from 0.2.0-RC2 to 0.2.0-RELEASE. - [Release notes](https://github.com/TaleLin/lin-cms-java-core/releases) - [Commits](https://github.com/TaleLin/lin-cms-java-core/commits) Signed-off-by: dependabot[bot] * Chore/upgrade version (#200) * chore: 修正README锚点不跳转的问题 * fix:#190 * fix:#184 * chore: 升级主工程以及核心库版本号 Co-authored-by: ToWeLong <1191430240@qq.com> * chore(deps): bump spring-boot-starter-parent from 2.4.3 to 2.5.0 Bumps [spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) from 2.4.3 to 2.5.0. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.4.3...v2.5.0) Signed-off-by: dependabot[bot] * Dev (#226) * chore(deps): bump spring-boot-starter-parent from 2.5.0 to 2.5.2 (#220) Bumps [spring-boot-starter-parent](https://github.com/spring-projects/spring-boot) from 2.5.0 to 2.5.2. - [Release notes](https://github.com/spring-projects/spring-boot/releases) - [Commits](https://github.com/spring-projects/spring-boot/compare/v2.5.0...v2.5.2) --- updated-dependencies: - dependency-name: org.springframework.boot:spring-boot-starter-parent dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * refactor: optimize ci action config * Update maven.yml * Update maven.yml * fix issue #131 * chore(deps): bump qiniu-java-sdk from 7.4.0 to 7.8.0 Bumps [qiniu-java-sdk](https://github.com/qiniu/java-sdk) from 7.4.0 to 7.8.0. - [Release notes](https://github.com/qiniu/java-sdk/releases) - [Changelog](https://github.com/qiniu/java-sdk/blob/master/CHANGELOG.md) - [Commits](https://github.com/qiniu/java-sdk/compare/v7.4.0...v7.8.0) --- updated-dependencies: - dependency-name: com.qiniu:qiniu-java-sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Chore/replace expired configurations (#222) * chore: 修正README锚点不跳转的问题 * fix:#190 * fix:#184 * chore: 替换过期的配置项 * test: 临时解决 CI 单元测试异常(原因未明) Co-authored-by: ToWeLong <1191430240@qq.com> Co-authored-by: 华钦 * Docs/upgrade readme (#225) * chore: 修正README锚点不跳转的问题 * fix:#190 * fix:#184 * docs: 更新 readme 中 springboot 版本号 Co-authored-by: ToWeLong <1191430240@qq.com> Co-authored-by: 华钦 Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: dengliming Co-authored-by: fuhao Co-authored-by: ToWeLong <1191430240@qq.com> Co-authored-by: 华钦 * refactor: 替换jackson已过期的配置引用 Co-authored-by: ToWeLong <1191430240@qq.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 华钦 Co-authored-by: dengliming Co-authored-by: fuhao --- .../latticy/common/configuration/CommonConfiguration.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java b/src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java index 2db8d255..a821d908 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java @@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.core.injector.ISqlInjector; import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import io.github.talelin.autoconfigure.bean.PermissionMetaCollector; import io.github.talelin.latticy.common.interceptor.RequestLogInterceptor; import io.github.talelin.latticy.module.log.MDCAccessServletFilter; @@ -72,7 +72,7 @@ public Jackson2ObjectMapperBuilderCustomizer customJackson() { return jacksonObjectMapperBuilder -> { // jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL); jacksonObjectMapperBuilder.failOnUnknownProperties(false); - jacksonObjectMapperBuilder.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + jacksonObjectMapperBuilder.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); }; } From 4ee1a903b525fc573595f83ff41d9cc0f651b6cb Mon Sep 17 00:00:00 2001 From: Colorful Date: Sat, 10 Jul 2021 09:28:45 +0800 Subject: [PATCH 02/20] =?UTF-8?q?chore(pom.xml):=20=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E6=A1=86=E6=9E=B6=E7=89=88=E6=9C=AC=E4=B8=BA0.2.1-RELEASE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 6cdf7ce0..6c6cd01b 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ io.github.talelin latticy - 0.2.0-RELEASE + 0.2.1-RELEASE latticy Demo project for lin cms From cd8725890aa4b5d58065ea4144f3a84cb8e9dad2 Mon Sep 17 00:00:00 2001 From: Gadfly Date: Wed, 14 Jul 2021 18:01:48 +0800 Subject: [PATCH 03/20] =?UTF-8?q?fix=20#223=20&=20test=E5=8E=BB=E9=99=A4de?= =?UTF-8?q?precated=20api=E7=AD=89=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github/talelin/latticy/vo/DeletedVO.java | 8 +-- .../github/talelin/latticy/vo/UpdatedVO.java | 8 +-- .../controller/cms/AdminControllerTest.java | 35 +++++++------ .../controller/cms/UserControllerTest.java | 39 +++++++------- .../controller/v1/BookControllerTest.java | 18 +++---- .../latticy/mapper/FileMapperTest.java | 4 +- .../talelin/latticy/mapper/LogMapperTest.java | 21 ++++---- .../service/impl/AdminServiceImplTest.java | 52 +++++++++---------- .../service/impl/BookServiceImplTest.java | 8 +-- .../service/impl/GroupServiceImplTest.java | 13 ++--- 10 files changed, 102 insertions(+), 104 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/vo/DeletedVO.java b/src/main/java/io/github/talelin/latticy/vo/DeletedVO.java index 2d2c3a9a..67e3a492 100644 --- a/src/main/java/io/github/talelin/latticy/vo/DeletedVO.java +++ b/src/main/java/io/github/talelin/latticy/vo/DeletedVO.java @@ -11,22 +11,22 @@ public class DeletedVO extends UnifyResponseVO { public DeletedVO() { super(Code.DELETED.getCode()); - ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); + ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); } public DeletedVO(int code) { super(code); - ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); + ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); } public DeletedVO(String message) { super(message); - ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); + ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); } public DeletedVO(int code, String message) { super(code, message); - ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); + ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); } @Override diff --git a/src/main/java/io/github/talelin/latticy/vo/UpdatedVO.java b/src/main/java/io/github/talelin/latticy/vo/UpdatedVO.java index 2d25c1b4..26aed191 100644 --- a/src/main/java/io/github/talelin/latticy/vo/UpdatedVO.java +++ b/src/main/java/io/github/talelin/latticy/vo/UpdatedVO.java @@ -11,22 +11,22 @@ public class UpdatedVO extends UnifyResponseVO { public UpdatedVO() { super(Code.UPDATED.getCode()); - ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); + ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); } public UpdatedVO(int code) { super(code); - ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); + ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); } public UpdatedVO(String message) { super(message); - ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); + ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); } public UpdatedVO(int code, String message) { super(code, message); - ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.CREATED.value()); + ResponseUtil.setCurrentResponseHttpStatus(HttpStatus.OK.value()); } @Override diff --git a/src/test/java/io/github/talelin/latticy/controller/cms/AdminControllerTest.java b/src/test/java/io/github/talelin/latticy/controller/cms/AdminControllerTest.java index 49917f0c..a15c7c04 100644 --- a/src/test/java/io/github/talelin/latticy/controller/cms/AdminControllerTest.java +++ b/src/test/java/io/github/talelin/latticy/controller/cms/AdminControllerTest.java @@ -1,7 +1,7 @@ package io.github.talelin.latticy.controller.cms; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import io.github.talelin.latticy.dto.admin.*; import io.github.talelin.latticy.mapper.*; import io.github.talelin.latticy.model.*; @@ -19,6 +19,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; +import java.util.Collections; import static org.junit.jupiter.api.Assertions.*; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @@ -130,13 +131,13 @@ public void changeUserPassword() throws Exception { dto.setConfirmPassword(newPassword); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(put(String.format("/cms/admin/user/%s/password", user.getId())) .contentType(MediaType.APPLICATION_JSON).content(content)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("密码修改成功")); boolean b = userIdentityService.verifyUsernamePassword(user.getId(), username, newPassword); @@ -162,7 +163,7 @@ public void deleteUser() throws Exception { mvc.perform(delete("/cms/admin/user/" + user.getId()) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("删除用户成功")); UserDO hit = userMapper.selectById(user.getId()); @@ -184,16 +185,16 @@ public void updateUser() throws Exception { groupMapper.insert(group); UpdateUserInfoDTO dto = new UpdateUserInfoDTO(); - dto.setGroupIds(Arrays.asList(group.getId())); + dto.setGroupIds(Collections.singletonList(group.getId())); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(put("/cms/admin/user/" + user.getId()) .contentType(MediaType.APPLICATION_JSON).content(content)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("更新用户成功")); } @@ -275,10 +276,10 @@ public void createGroup() throws Exception { PermissionDO permission = PermissionDO.builder().name(permissionName).module(module).build(); permissionMapper.insert(permission); - dto.setPermissionIds(Arrays.asList(permission.getId())); + dto.setPermissionIds(Collections.singletonList(permission.getId())); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(post("/cms/admin/group/") @@ -308,13 +309,13 @@ public void updateGroup() throws Exception { dto.setInfo("flink is a finger"); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(put("/cms/admin/group/" + group.getId()) .contentType(MediaType.APPLICATION_JSON).content(content)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("更新分组成功")); GroupDO hit = groupMapper.selectById(group.getId()); @@ -335,7 +336,7 @@ public void deleteGroup() throws Exception { mvc.perform(delete("/cms/admin/group/" + group.getId()) .contentType(MediaType.APPLICATION_JSON)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("删除分组成功")); GroupDO hit = groupMapper.selectById(group.getId()); assertNull(hit); @@ -359,7 +360,7 @@ public void dispatchPermission() throws Exception { dto.setPermissionId(permission.getId()); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(post("/cms/admin/permission/dispatch") @@ -389,7 +390,7 @@ public void dispatchPermissions() throws Exception { dto.setPermissionIds(Arrays.asList(permission.getId(), permission1.getId())); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(post("/cms/admin/permission/dispatch/batch") @@ -419,16 +420,16 @@ public void removePermissions() throws Exception { RemovePermissionsDTO dto = new RemovePermissionsDTO(); dto.setGroupId(group.getId()); - dto.setPermissionIds(Arrays.asList(permission1.getId())); + dto.setPermissionIds(Collections.singletonList(permission1.getId())); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(post("/cms/admin/permission/remove") .contentType(MediaType.APPLICATION_JSON).content(content)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers.jsonPath("$.message").value("删除权限成功")); } } \ No newline at end of file diff --git a/src/test/java/io/github/talelin/latticy/controller/cms/UserControllerTest.java b/src/test/java/io/github/talelin/latticy/controller/cms/UserControllerTest.java index 33e5bdcf..a1cba9a6 100644 --- a/src/test/java/io/github/talelin/latticy/controller/cms/UserControllerTest.java +++ b/src/test/java/io/github/talelin/latticy/controller/cms/UserControllerTest.java @@ -1,7 +1,7 @@ package io.github.talelin.latticy.controller.cms; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import io.github.talelin.latticy.common.LocalUser; import io.github.talelin.latticy.dto.user.ChangePasswordDTO; import io.github.talelin.latticy.dto.user.LoginDTO; @@ -25,9 +25,10 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Arrays; +import java.util.Collections; import java.util.Random; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -49,11 +50,11 @@ public class UserControllerTest { @Autowired private UserService userService; - private String email = "13129982604@qq.com"; + private final String email = "13129982604@qq.com"; - private String password = "123456"; + private final String password = "123456"; - private String username = "pedro大大"; + private final String username = "pedro大大"; @Test public void register() throws Exception { @@ -61,14 +62,14 @@ public void register() throws Exception { groupMapper.insert(group); RegisterDTO dto = new RegisterDTO(); - dto.setGroupIds(Arrays.asList(group.getId())); + dto.setGroupIds(Collections.singletonList(group.getId())); dto.setEmail(email); dto.setConfirmPassword(password); dto.setPassword(password); dto.setUsername(username); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(post("/cms/user/register") @@ -93,7 +94,7 @@ public void register1() throws Exception { dto.setUsername(username); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(post("/cms/user/register") @@ -120,7 +121,7 @@ public void login() throws Exception { dto1.setPassword(password); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto1); mvc.perform(post("/cms/user/login") @@ -138,7 +139,7 @@ public void update() throws Exception { groupMapper.insert(root); groupMapper.insert(group); RegisterDTO dto = new RegisterDTO(); - dto.setGroupIds(Arrays.asList(group.getId())); + dto.setGroupIds(Collections.singletonList(group.getId())); dto.setEmail(email); dto.setConfirmPassword(password); dto.setPassword(password); @@ -153,19 +154,19 @@ public void update() throws Exception { dto1.setUsername("pedro小小"); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto1); mvc.perform(MockMvcRequestBuilders.put("/cms/user/") .contentType(MediaType.APPLICATION_JSON).content(content)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers. jsonPath("$.message").value("更新用户成功")); UserDO user1 = userService.getUserByUsername("pedro小小"); - assertTrue(user1.getEmail().equals("23129982604@qq.com")); + assertEquals("23129982604@qq.com", user1.getEmail()); } @Test @@ -173,7 +174,7 @@ public void updatePassword() throws Exception { GroupDO group = GroupDO.builder().name("少林足球").info("致敬周星星").build(); groupMapper.insert(group); RegisterDTO dto = new RegisterDTO(); - dto.setGroupIds(Arrays.asList(group.getId())); + dto.setGroupIds(Collections.singletonList(group.getId())); dto.setEmail(email); dto.setConfirmPassword(password); dto.setPassword(password); @@ -189,14 +190,14 @@ public void updatePassword() throws Exception { dto1.setConfirmPassword("147258"); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto1); mvc.perform(MockMvcRequestBuilders.put("/cms/user/change_password") .contentType(MediaType.APPLICATION_JSON).content(content)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers. jsonPath("$.message").value("密码修改成功")); } @@ -206,7 +207,7 @@ public void refreshToken() throws Exception { GroupDO group = GroupDO.builder().name("少林足球").info("致敬周星星").build(); groupMapper.insert(group); RegisterDTO dto = new RegisterDTO(); - dto.setGroupIds(Arrays.asList(group.getId())); + dto.setGroupIds(Collections.singletonList(group.getId())); dto.setEmail(email); dto.setConfirmPassword(password); dto.setPassword(password); @@ -229,7 +230,7 @@ public void getPermissions() throws Exception { GroupDO group = GroupDO.builder().name("少林足球").info("致敬周星星").build(); groupMapper.insert(group); RegisterDTO dto = new RegisterDTO(); - dto.setGroupIds(Arrays.asList(group.getId())); + dto.setGroupIds(Collections.singletonList(group.getId())); dto.setEmail(email); dto.setConfirmPassword(password); dto.setPassword(password); @@ -253,7 +254,7 @@ public void getInformation() throws Exception { groupMapper.insert(root); groupMapper.insert(group); RegisterDTO dto = new RegisterDTO(); - dto.setGroupIds(Arrays.asList(group.getId())); + dto.setGroupIds(Collections.singletonList(group.getId())); dto.setEmail(email); dto.setConfirmPassword(password); dto.setPassword(password); diff --git a/src/test/java/io/github/talelin/latticy/controller/v1/BookControllerTest.java b/src/test/java/io/github/talelin/latticy/controller/v1/BookControllerTest.java index 725dddac..82a9a9e0 100644 --- a/src/test/java/io/github/talelin/latticy/controller/v1/BookControllerTest.java +++ b/src/test/java/io/github/talelin/latticy/controller/v1/BookControllerTest.java @@ -1,7 +1,7 @@ package io.github.talelin.latticy.controller.v1; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; import io.github.talelin.latticy.dto.book.CreateOrUpdateBookDTO; import io.github.talelin.latticy.mapper.BookMapper; import io.github.talelin.latticy.model.BookDO; @@ -35,10 +35,10 @@ public class BookControllerTest { private BookMapper bookMapper; private Integer id; - private String title = "千里之外"; - private String author = "pedro"; - private String image = "千里之外.png"; - private String summary = "千里之外,是周杰伦和费玉清一起发售的歌曲"; + private final String title = "千里之外"; + private final String author = "pedro"; + private final String image = "千里之外.png"; + private final String summary = "千里之外,是周杰伦和费玉清一起发售的歌曲"; @Test @@ -100,7 +100,7 @@ public void createBook() throws Exception { dto.setTitle(title); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(post("/v1/book/") @@ -128,13 +128,13 @@ public void updateBook() throws Exception { dto.setTitle(title + "lol"); ObjectMapper mapper = new ObjectMapper(); - mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); String content = mapper.writeValueAsString(dto); mvc.perform(put("/v1/book/" + this.id) .contentType(MediaType.APPLICATION_JSON).content(content)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers. jsonPath("$.message").value("更新图书成功")); } @@ -151,7 +151,7 @@ public void deleteBook() throws Exception { mvc.perform(delete("/v1/book/" + this.id)) .andDo(print()) - .andExpect(status().isCreated()) + .andExpect(status().isOk()) .andExpect(MockMvcResultMatchers. jsonPath("$.message").value("删除图书成功")); } diff --git a/src/test/java/io/github/talelin/latticy/mapper/FileMapperTest.java b/src/test/java/io/github/talelin/latticy/mapper/FileMapperTest.java index 97280763..4fe58760 100644 --- a/src/test/java/io/github/talelin/latticy/mapper/FileMapperTest.java +++ b/src/test/java/io/github/talelin/latticy/mapper/FileMapperTest.java @@ -23,8 +23,8 @@ public class FileMapperTest { @Autowired private FileMapper fileMapper; - private String md5 = "iiiiilllllll"; - private String name = "千里之外"; + private final String md5 = "iiiiilllllll"; + private final String name = "千里之外"; @BeforeAll public void setUp() throws Exception { diff --git a/src/test/java/io/github/talelin/latticy/mapper/LogMapperTest.java b/src/test/java/io/github/talelin/latticy/mapper/LogMapperTest.java index 51fe1730..a51d222b 100644 --- a/src/test/java/io/github/talelin/latticy/mapper/LogMapperTest.java +++ b/src/test/java/io/github/talelin/latticy/mapper/LogMapperTest.java @@ -15,6 +15,7 @@ import java.util.Date; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -29,13 +30,13 @@ public class LogMapperTest { private LogMapper logMapper; private Date start = new Date(); - private String permission = "查看lin的信息"; - private String message = "就是个瓜皮"; - private String method = "GET"; - private String path = "/"; - private Integer statusCode = 200; - private Integer userId = 1; - private String username = "super"; + private final String permission = "查看lin的信息"; + private final String message = "就是个瓜皮"; + private final String method = "GET"; + private final String path = "/"; + private final Integer statusCode = 200; + private final Integer userId = 1; + private final String username = "super"; @BeforeAll public void setUp() throws Exception { @@ -57,7 +58,7 @@ public void setUp() throws Exception { @Test public void testFindLogsByUsernameAndRange() { Date now = new Date(); - Page page = new Page(0, 10); + Page page = new Page<>(0, 10); IPage iPage = logMapper.findLogsByUsernameAndRange(page, username, start, now); List logs = iPage.getRecords(); assertTrue(logs.size() > 0); @@ -68,10 +69,10 @@ public void testFindLogsByUsernameAndRange1() { long changed = start.getTime(); Date ch = new Date(changed - 1000); Date ch1 = new Date(changed - 2000); - Page page = new Page(1, 10); + Page page = new Page<>(1, 10); IPage iPage = logMapper.findLogsByUsernameAndRange(page, username, ch1, ch); List logs = iPage.getRecords(); - assertTrue(logs.size() == 0); + assertEquals(0, logs.size()); } } \ No newline at end of file diff --git a/src/test/java/io/github/talelin/latticy/service/impl/AdminServiceImplTest.java b/src/test/java/io/github/talelin/latticy/service/impl/AdminServiceImplTest.java index 39c4e1b0..7d34f90e 100644 --- a/src/test/java/io/github/talelin/latticy/service/impl/AdminServiceImplTest.java +++ b/src/test/java/io/github/talelin/latticy/service/impl/AdminServiceImplTest.java @@ -19,10 +19,7 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Random; +import java.util.*; import static org.junit.jupiter.api.Assertions.*; @@ -98,8 +95,8 @@ public void getUserPageByGroupId() { GroupDO group = mockData1(); IPage iPage = adminService.getUserPageByGroupId(null, 10, 0); assertTrue(iPage.getTotal() > 0); - assertTrue(iPage.getSize() == 10); - assertTrue(iPage.getCurrent() == 0); + assertEquals(10, iPage.getSize()); + assertEquals(0, iPage.getCurrent()); boolean anyMatch = iPage.getRecords().stream().anyMatch(it -> it.getUsername().equals( "pedro大大")); assertTrue(anyMatch); @@ -160,23 +157,24 @@ public void deleteUser() { UserDO user = userService.createUser(dto); assertEquals("pedro&佩德罗", user.getUsername()); boolean b = true; - try{ + try { b = adminService.deleteUser(user.getId()); - } catch (ForbiddenException ignored) {} + } catch (ForbiddenException ignored) { + } assertTrue(b); UserDO selected = userMapper.selectById(user.getId()); assertNull(selected); } -// @Test(expected = NotFoundException.class) + // @Test(expected = NotFoundException.class) public void deleteUser1() { Random random = new Random(); boolean b = adminService.deleteUser(random.nextInt()); assertFalse(b); } -// @Test(expected = ForbiddenException.class) + // @Test(expected = ForbiddenException.class) public void updateUserInfo() { UserDO user1 = UserDO.builder().nickname("pedro大大").username("pedro大大").build(); userMapper.insert(user1); @@ -215,7 +213,7 @@ public void updateUserInfo2() { userGroupMapper.insertBatch(relations); UpdateUserInfoDTO dto = new UpdateUserInfoDTO(); - dto.setGroupIds(Arrays.asList(group2.getId())); + dto.setGroupIds(Collections.singletonList(group2.getId())); boolean b = adminService.updateUserInfo(user.getId(), dto); assertTrue(b); @@ -240,8 +238,8 @@ public void getGroupPage() { groupMapper.insert(group2); IPage iPage = adminService.getGroupPage(0, 10); assertTrue(iPage.getTotal() > 0); - assertTrue(iPage.getCurrent() == 0); - assertTrue(iPage.getSize() == 10); + assertEquals(0, iPage.getCurrent()); + assertEquals(10, iPage.getSize()); boolean anyMatch = iPage.getRecords().stream().anyMatch(it -> it.getName().equals("测试分组12" )); assertTrue(anyMatch); @@ -266,7 +264,7 @@ public void getGroup() { assertEquals("测试分组1", group1.getName()); assertNotNull(group1.getId()); boolean anyMatch = group1.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限1"); }); assertTrue(anyMatch); @@ -274,9 +272,9 @@ public void getGroup() { @Test public void getGroup1() { - assertThrows(NotFoundException.class, () ->{ + assertThrows(NotFoundException.class, () -> { Random random = new Random(); - GroupPermissionBO group1 = adminService.getGroup( random.nextInt(100)); + GroupPermissionBO group1 = adminService.getGroup(random.nextInt(100)); assertNull(group1); }); } @@ -303,7 +301,7 @@ public void createGroup() { GroupPermissionBO groupPermissions = adminService.getGroup(group.getId()); boolean anyMatch = groupPermissions.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限1"); }); assertTrue(anyMatch); @@ -369,7 +367,7 @@ public void updateGroup2() { dto.setName("测试分组2"); dto.setInfo("测试分组2info"); Random random = new Random(); - boolean ok = adminService.updateGroup(random.nextInt(100), dto); + boolean ok = adminService.updateGroup(random.nextInt(100) + 10, dto); assertFalse(ok); }); } @@ -389,7 +387,7 @@ public void deleteGroup() { public void deleteGroup1() { assertThrows(NotFoundException.class, () -> { Random random = new Random(); - boolean ok = adminService.deleteGroup( random.nextInt(1000)); + boolean ok = adminService.deleteGroup(random.nextInt(1000)); assertFalse(ok); }); } @@ -418,7 +416,7 @@ public void dispatchPermission() { assertTrue(ok); GroupPermissionBO groupPermissions = adminService.getGroup(group.getId()); boolean anyMatch = groupPermissions.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限3") && p.getModule().equals("炉石传说"); }); assertTrue(anyMatch); @@ -451,13 +449,13 @@ public void dispatchPermissions() { assertTrue(ok); GroupPermissionBO groupPermissions = adminService.getGroup(group.getId()); boolean anyMatch = groupPermissions.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限3") && p.getModule().equals("炉石传说"); }); assertTrue(anyMatch); anyMatch = groupPermissions.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限4") && p.getModule().equals("炉石传说"); }); assertTrue(anyMatch); @@ -490,13 +488,13 @@ public void removePermissions() { assertTrue(ok); GroupPermissionBO groupPermissions = adminService.getGroup(group.getId()); boolean anyMatch = groupPermissions.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限3") && p.getModule().equals("炉石传说"); }); assertFalse(anyMatch); anyMatch = groupPermissions.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限1") && p.getModule().equals("炉石传说"); }); assertTrue(anyMatch); @@ -524,18 +522,18 @@ public void removePermissions1() { RemovePermissionsDTO dto = new RemovePermissionsDTO(); dto.setGroupId(group.getId()); // 3 - dto.setPermissionIds(Arrays.asList(permission3.getId())); + dto.setPermissionIds(Collections.singletonList(permission3.getId())); boolean ok = adminService.removePermissions(dto); assertTrue(ok); GroupPermissionBO groupPermissions = adminService.getGroup(group.getId()); boolean anyMatch = groupPermissions.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限3") && p.getModule().equals("炉石传说"); }); assertFalse(anyMatch); anyMatch = groupPermissions.getPermissions().stream().anyMatch(it -> { - PermissionDO p = (PermissionDO) it; + PermissionDO p = it; return p.getName().equals("权限1") && p.getModule().equals("炉石传说"); }); assertTrue(anyMatch); diff --git a/src/test/java/io/github/talelin/latticy/service/impl/BookServiceImplTest.java b/src/test/java/io/github/talelin/latticy/service/impl/BookServiceImplTest.java index b0f24cce..742f4d69 100644 --- a/src/test/java/io/github/talelin/latticy/service/impl/BookServiceImplTest.java +++ b/src/test/java/io/github/talelin/latticy/service/impl/BookServiceImplTest.java @@ -27,10 +27,10 @@ public class BookServiceImplTest { @Autowired private BookMapper bookMapper; - private String title = "千里之外"; - private String author = "pedro"; - private String image = "千里之外.png"; - private String summary = "千里之外,是周杰伦和费玉清一起发售的歌曲"; + private final String title = "千里之外"; + private final String author = "pedro"; + private final String image = "千里之外.png"; + private final String summary = "千里之外,是周杰伦和费玉清一起发售的歌曲"; @Test public void createBook() { diff --git a/src/test/java/io/github/talelin/latticy/service/impl/GroupServiceImplTest.java b/src/test/java/io/github/talelin/latticy/service/impl/GroupServiceImplTest.java index 4dc711db..5c2621ce 100644 --- a/src/test/java/io/github/talelin/latticy/service/impl/GroupServiceImplTest.java +++ b/src/test/java/io/github/talelin/latticy/service/impl/GroupServiceImplTest.java @@ -15,8 +15,7 @@ import java.util.ArrayList; import java.util.List; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; @SpringBootTest @@ -105,7 +104,7 @@ public void findGroupsByPage() { IPage groups = groupService.getGroupPage(0, 10); assertTrue(groups.getTotal() > 0); assertTrue(groups.getRecords().size() > 0); - assertTrue(groups.getCurrent() == 0); + assertEquals(0, groups.getCurrent()); boolean anyMatch = groups.getRecords().stream().anyMatch(it -> it.getName().equals("测试分组12")); assertTrue(anyMatch); } @@ -121,11 +120,9 @@ public void checkGroupExistById() { public void getGroupAndPermissions() { Integer id = mockData2(); GroupPermissionBO groupAndPermissions = groupService.getGroupAndPermissions(id); - assertTrue(groupAndPermissions.getName().equals("测试分组1")); - boolean anyMatch = groupAndPermissions.getPermissions().stream().anyMatch(permission -> { - PermissionDO permission1 = (PermissionDO) permission; - return permission1.getName().equals("权限2"); - }); + assertEquals("测试分组1", groupAndPermissions.getName()); + boolean anyMatch = groupAndPermissions.getPermissions().stream().anyMatch(permission -> + permission.getName().equals("权限2")); assertTrue(anyMatch); } From 166624013af1b04c14222bf49e5ca7f7c8e8a8a3 Mon Sep 17 00:00:00 2001 From: towelong <1191430240@qq.com> Date: Sun, 14 Nov 2021 11:51:56 +0800 Subject: [PATCH 04/20] fix: #247 --- .../talelin/latticy/mapper/UserGroupMapper.java | 2 ++ .../latticy/service/impl/AdminServiceImpl.java | 14 ++++++++------ src/main/resources/mapper/UserGroupMapper.xml | 5 +++++ 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/mapper/UserGroupMapper.java b/src/main/java/io/github/talelin/latticy/mapper/UserGroupMapper.java index a7caed3f..39e3e14e 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/UserGroupMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/UserGroupMapper.java @@ -14,4 +14,6 @@ public interface UserGroupMapper extends BaseMapper { int insertBatch(@Param("relations") List relations); + + int deleteByUserId(@Param("user_id") Integer userId); } diff --git a/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java index 4c2698aa..54ed2f37 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java @@ -15,11 +15,8 @@ import io.github.talelin.latticy.dto.admin.UpdateGroupDTO; import io.github.talelin.latticy.dto.admin.UpdateUserInfoDTO; import io.github.talelin.latticy.mapper.GroupPermissionMapper; -import io.github.talelin.latticy.model.GroupDO; -import io.github.talelin.latticy.model.GroupPermissionDO; -import io.github.talelin.latticy.model.PermissionDO; -import io.github.talelin.latticy.model.UserDO; -import io.github.talelin.latticy.model.UserIdentityDO; +import io.github.talelin.latticy.mapper.UserGroupMapper; +import io.github.talelin.latticy.model.*; import io.github.talelin.latticy.service.AdminService; import io.github.talelin.latticy.service.GroupService; import io.github.talelin.latticy.service.PermissionService; @@ -58,6 +55,9 @@ public class AdminServiceImpl implements AdminService { @Autowired private GroupPermissionMapper groupPermissionMapper; + @Autowired + private UserGroupMapper userGroupMapper; + @Override public IPage getUserPageByGroupId(Integer groupId, Integer count, Integer page) { Page pager = new Page<>(page, count); @@ -90,7 +90,9 @@ public boolean deleteUser(Integer id) { boolean userRemoved = userService.removeById(id); QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda().eq(UserIdentityDO::getUserId, id); - return userRemoved && userIdentityService.remove(wrapper); + // 删除用户,还应当将 user_group表中的数据删除 + boolean deleteResult = userGroupMapper.deleteByUserId(id) > 0; + return userRemoved && userIdentityService.remove(wrapper) && deleteResult; } @Override diff --git a/src/main/resources/mapper/UserGroupMapper.xml b/src/main/resources/mapper/UserGroupMapper.xml index 781f394b..84beaf32 100644 --- a/src/main/resources/mapper/UserGroupMapper.xml +++ b/src/main/resources/mapper/UserGroupMapper.xml @@ -17,4 +17,9 @@ + + DELETE FROM lin_user_group + WHERE user_id = #{user_id} + + From 7af27b33f348bf7911e3cbe556ebff994b40504b Mon Sep 17 00:00:00 2001 From: Gadfly Date: Mon, 15 Nov 2021 17:31:57 +0800 Subject: [PATCH 05/20] =?UTF-8?q?fix:=20=E5=8F=8D=E4=BB=A3=E5=90=8EIP?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../interceptor/RequestLogInterceptor.java | 3 +- .../talelin/latticy/common/util/IPUtil.java | 48 +++++++++++++++++++ .../module/log/MDCAccessServletFilter.java | 5 +- 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 src/main/java/io/github/talelin/latticy/common/util/IPUtil.java diff --git a/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java b/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java index e8d1d110..7c85d1bf 100644 --- a/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java +++ b/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java @@ -1,5 +1,6 @@ package io.github.talelin.latticy.common.interceptor; +import io.github.talelin.latticy.common.util.IPUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.web.servlet.AsyncHandlerInterceptor; @@ -27,7 +28,7 @@ public void afterCompletion(HttpServletRequest request, HttpServletResponse resp log.info("[{}] -> [{}] from: {} costs: {}ms", request.getMethod(), request.getServletPath(), - request.getRemoteAddr(), + IPUtil.getIPFromRequest(request), System.currentTimeMillis() - startTime.get() ); } diff --git a/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java b/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java new file mode 100644 index 00000000..70c5633d --- /dev/null +++ b/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java @@ -0,0 +1,48 @@ +package io.github.talelin.latticy.common.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; + +/** + * IP工具类 + */ +@Slf4j +public class IPUtil { + + private static final String[] IP_HEADER_CANDIDATES = { + "X-Forwarded-For", + "Proxy-Client-IP", + "WL-Proxy-Client-IP", + "HTTP_X_FORWARDED_FOR", + "HTTP_X_FORWARDED", + "HTTP_X_CLUSTER_CLIENT_IP", + "HTTP_CLIENT_IP", + "HTTP_FORWARDED_FOR", + "HTTP_FORWARDED", + "HTTP_VIA", + "REMOTE_ADDR" + }; + + public static String getIPFromRequest(HttpServletRequest request) { + if (request == null) { + if (null == RequestContextHolder.getRequestAttributes()) { + return null; + } + request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); + } + + for (String header : IP_HEADER_CANDIDATES) { + String ipList = request.getHeader(header); + if (ipList != null && ipList.length() != 0 && !"unknown".equalsIgnoreCase(ipList)) { + log.debug("ipList.split(\",\")[0]::{}", ipList.split(",")[0]); + return ipList.split(",")[0]; + } + } + + log.debug("request.getRemoteAddr()::{}", request.getRemoteAddr()); + return request.getRemoteAddr(); + } +} diff --git a/src/main/java/io/github/talelin/latticy/module/log/MDCAccessServletFilter.java b/src/main/java/io/github/talelin/latticy/module/log/MDCAccessServletFilter.java index f6c27c5a..0ca223ba 100644 --- a/src/main/java/io/github/talelin/latticy/module/log/MDCAccessServletFilter.java +++ b/src/main/java/io/github/talelin/latticy/module/log/MDCAccessServletFilter.java @@ -1,5 +1,6 @@ package io.github.talelin.latticy.module.log; +import io.github.talelin.latticy.common.util.IPUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -55,14 +56,14 @@ public void putResponseMDC(ServletResponse servletResponse) { */ public void putRequestMDC(ServletRequest servletRequest) throws IOException { MDC.put(MDCAccessConstant.REQUEST_REMOTE_HOST_MDC_KEY, servletRequest.getRemoteHost()); - MDC.put(MDCAccessConstant.REQUEST_REMOTE_ADDR_MDC_KEY, servletRequest.getRemoteAddr()); - MDC.put(MDCAccessConstant.REQUEST_PROTOCOL_MDC_KEY, servletRequest.getProtocol()); MDC.put(MDCAccessConstant.REQUEST_PROTOCOL_MDC_KEY, servletRequest.getProtocol()); + MDC.put(MDCAccessConstant.REQUEST_REMOTE_ADDR_MDC_KEY, servletRequest.getRemoteAddr()); MDC.put(MDCAccessConstant.REQUEST_REMOTE_PORT_MDC_KEY, String.valueOf(servletRequest.getRemotePort())); MDC.put(MDCAccessConstant.REQUEST_BODY_BYTES_SENT_MDC_KEY, String.valueOf(servletRequest.getContentLength())); if (servletRequest instanceof HttpServletRequest) { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + MDC.put(MDCAccessConstant.REQUEST_REMOTE_ADDR_MDC_KEY, IPUtil.getIPFromRequest(httpServletRequest)); MDC.put(MDCAccessConstant.REQUEST_REQUEST_URI_MDC_KEY, httpServletRequest.getRequestURI()); StringBuffer requestUrl = httpServletRequest.getRequestURL(); if (requestUrl != null) { From e3a570343c0f354296bb92407a6d2d9680a9d4cd Mon Sep 17 00:00:00 2001 From: Gadfly Date: Fri, 19 Nov 2021 17:05:27 +0800 Subject: [PATCH 06/20] =?UTF-8?q?feat:=20=E7=99=BB=E5=BD=95=E9=AA=8C?= =?UTF-8?q?=E8=AF=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../talelin/latticy/bo/LoginCaptchaBO.java | 17 ++ .../configuration/LoginCaptchaProperties.java | 29 +++ .../latticy/common/util/CaptchaUtil.java | 200 ++++++++++++++++++ .../controller/cms/UserController.java | 27 ++- .../talelin/latticy/dto/user/LoginDTO.java | 2 + .../talelin/latticy/service/UserService.java | 15 ++ .../latticy/service/impl/UserServiceImpl.java | 26 +++ .../talelin/latticy/vo/LoginCaptchaVO.java | 22 ++ src/main/resources/DejaVuSerif-Bold.ttf | Bin 0 -> 356088 bytes .../resources/ValidationMessages.properties | 1 + src/main/resources/application-dev.yml | 4 + src/main/resources/application-prod.yml | 5 + src/main/resources/code-message.properties | 3 +- 13 files changed, 349 insertions(+), 2 deletions(-) create mode 100644 src/main/java/io/github/talelin/latticy/bo/LoginCaptchaBO.java create mode 100644 src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java create mode 100644 src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java create mode 100644 src/main/java/io/github/talelin/latticy/vo/LoginCaptchaVO.java create mode 100644 src/main/resources/DejaVuSerif-Bold.ttf diff --git a/src/main/java/io/github/talelin/latticy/bo/LoginCaptchaBO.java b/src/main/java/io/github/talelin/latticy/bo/LoginCaptchaBO.java new file mode 100644 index 00000000..f74b33a8 --- /dev/null +++ b/src/main/java/io/github/talelin/latticy/bo/LoginCaptchaBO.java @@ -0,0 +1,17 @@ +package io.github.talelin.latticy.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Gadfly + * @since 2021-11-19 15:20 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LoginCaptchaBO { + private String captcha; + private Long expired; +} diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java new file mode 100644 index 00000000..5c0b7970 --- /dev/null +++ b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java @@ -0,0 +1,29 @@ +package io.github.talelin.latticy.common.configuration; + +import io.github.talelin.latticy.common.util.CaptchaUtil; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @author Gadfly + */ +@Getter +@Setter +@Component +@ConfigurationProperties(prefix = "login-captcha") +public class LoginCaptchaProperties { + /** + * aes 密钥 + */ + private String secret; + /** + * aes 偏移量 + */ + private String iv = CaptchaUtil.getRandomString(16); + /** + * 启用验证码 + */ + private Boolean enabled = Boolean.FALSE; +} diff --git a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java new file mode 100644 index 00000000..4d21d441 --- /dev/null +++ b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java @@ -0,0 +1,200 @@ +package io.github.talelin.latticy.common.util; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.github.talelin.latticy.bo.LoginCaptchaBO; +import org.springframework.core.io.ClassPathResource; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.GeneralSecurityException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.Base64; +import java.util.Random; + +/** + * @author Gadfly + */ +@SuppressWarnings("SpellCheckingInspection") +public class CaptchaUtil { + + /** + * 验证码字符个数 + */ + public static final int RANDOM_STR_NUM = 4; + private static final Random RANDOM = new Random(); + /** + * 验证码的宽 + */ + private static final int WIDTH = 80; + /** + * 验证码的高 + */ + private static final int HEIGHT = 40; + /** + * 验证码中夹杂的干扰线数量 + */ + private static final int LINE_SIZE = 30; + private static final String RANDOM_STRING = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWSYZ"; + private static final String AES = "AES"; + private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; + private static final ObjectMapper MAPPER = new ObjectMapper(); + + static { + java.security.Security.setProperty("crypto.policy", "unlimited"); + } + + /** + * 颜色的设置 + */ + private static Color getRandomColor(int fc, int bc) { + + fc = Math.min(fc, 255); + bc = Math.min(bc, 255); + + int r = fc + RANDOM.nextInt(bc - fc - 16); + int g = fc + RANDOM.nextInt(bc - fc - 14); + int b = fc + RANDOM.nextInt(bc - fc - 12); + + return new Color(r, g, b); + } + + /** + * 字体的设置 + */ + private static Font getFont() throws IOException, FontFormatException { + ClassPathResource dejavuSerifBold = new ClassPathResource("DejaVuSerif-Bold.ttf"); + return Font.createFont(Font.TRUETYPE_FONT, dejavuSerifBold.getInputStream()).deriveFont(Font.BOLD, 24); + } + + /** + * 随机字符的获取 + */ + public static String getRandomString(int num) { + num = num > 0 ? num : RANDOM_STRING.length(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < num; i++) { + int number = RANDOM.nextInt(RANDOM_STRING.length()); + sb.append(RANDOM_STRING.charAt(number)); + } + return sb.toString(); + } + + /** + * 干扰线的绘制 + */ + private static void drawLine(Graphics2D g) { + int x = RANDOM.nextInt(WIDTH); + int y = RANDOM.nextInt(HEIGHT); + int xl = WIDTH; + int yl = HEIGHT; + g.setStroke(new BasicStroke(2.0f)); + g.setColor(getRandomColor(98, 200)); + g.drawLine(x, y, x + xl, y + yl); + } + + /** + * 字符串的绘制 + */ + private static void drawString(Graphics2D g, String randomStr, int i) throws IOException, FontFormatException { + g.setFont(getFont()); + g.setColor(getRandomColor(28, 130)); + // 设置每个字符的随机旋转 + double radianPercent = (RANDOM.nextBoolean() ? -1 : 1) * Math.PI * (RANDOM.nextInt(60) / 320D); + g.rotate(radianPercent, WIDTH * 0.8 / RANDOM_STR_NUM * i, HEIGHT / 2); + int y = (RANDOM.nextBoolean() ? -1 : 1) * RANDOM.nextInt(4) + 4; + g.translate(RANDOM.nextInt(3), y); + g.drawString(randomStr, WIDTH / RANDOM_STR_NUM * i, HEIGHT / 2); + g.rotate(-radianPercent, WIDTH * 0.8 / RANDOM_STR_NUM * i, HEIGHT / 2); + g.translate(0, -y); + } + + private static BufferedImage getBufferedImage(String code) throws IOException, FontFormatException { + // BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类 + BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_BGR); + Graphics2D g = (Graphics2D) image.getGraphics(); + g.fillRect(0, 0, WIDTH, HEIGHT); + g.setColor(getRandomColor(105, 189)); + g.setFont(getFont()); + int lineSize = RANDOM.nextInt(5); + // 干扰线 + for (int i = 0; i < lineSize; i++) { + drawLine(g); + } + // 随机字符 + for (int i = 0; i < code.length(); i++) { + drawString(g, String.valueOf(code.charAt(i)), i); + } + g.dispose(); + return image; + } + + /** + * 生成随机图片的base64编码字符串 + * + * @param code 验证码 + * @return base64 + */ + public static String getRandomCodeBase64(String code) throws IOException, FontFormatException { + BufferedImage image = getBufferedImage(code); + // 返回 base64 + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ImageIO.write(image, "PNG", bos); + + byte[] bytes = bos.toByteArray(); + Base64.Encoder encoder = Base64.getEncoder(); + + return encoder.encodeToString(bytes); + } + + public static String getTag(String captcha, String secret, String iv) throws JsonProcessingException, GeneralSecurityException { + LocalDateTime time = LocalDateTime.now().plusMinutes(5); + LoginCaptchaBO captchaBO = new LoginCaptchaBO(captcha, time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + String json = MAPPER.writeValueAsString(captchaBO); + return aesEncode(secret, iv, json); + } + + public static LoginCaptchaBO decodeTag(String secret, String iv, String tag) throws JsonProcessingException, GeneralSecurityException { + String decrypted = aesDecode(secret, iv, tag); + return MAPPER.readValue(decrypted, LoginCaptchaBO.class); + } + + /** + * AES加密 + */ + public static String aesEncode(String secret, String iv, String content) throws GeneralSecurityException { + SecretKey secretKey = new SecretKeySpec(secret.getBytes(), AES); + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + System.out.println(iv.length() + "///" + iv.getBytes(StandardCharsets.UTF_8).length + "///" + iv.getBytes(StandardCharsets.US_ASCII).length); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv.getBytes(StandardCharsets.US_ASCII))); + byte[] byteEncode = content.getBytes(StandardCharsets.UTF_8); + // 根据密码器的初始化方式加密 + byte[] byteAES = cipher.doFinal(byteEncode); + + // 将加密后的数据转换为字符串 + return Base64.getEncoder().encodeToString(byteAES); + } + + /** + * AES解密 + */ + public static String aesDecode(String secret, String iv, String content) throws GeneralSecurityException { + SecretKey secretKey = new SecretKeySpec(secret.getBytes(), AES); + Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv.getBytes(StandardCharsets.US_ASCII))); + // 将加密并编码后的内容解码成字节数组 + byte[] byteContent = Base64.getDecoder().decode(content); + // 解密 + byte[] byteDecode = cipher.doFinal(byteContent); + return new String(byteDecode, StandardCharsets.UTF_8); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java b/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java index bd26e227..60065ed9 100644 --- a/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java +++ b/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java @@ -1,5 +1,6 @@ package io.github.talelin.latticy.controller.cms; +import io.github.talelin.autoconfigure.exception.ForbiddenException; import io.github.talelin.autoconfigure.exception.NotFoundException; import io.github.talelin.autoconfigure.exception.ParameterException; import io.github.talelin.core.annotation.AdminRequired; @@ -9,6 +10,7 @@ import io.github.talelin.core.token.DoubleJWT; import io.github.talelin.core.token.Tokens; import io.github.talelin.latticy.common.LocalUser; +import io.github.talelin.latticy.common.configuration.LoginCaptchaProperties; import io.github.talelin.latticy.dto.user.ChangePasswordDTO; import io.github.talelin.latticy.dto.user.LoginDTO; import io.github.talelin.latticy.dto.user.RegisterDTO; @@ -19,15 +21,18 @@ import io.github.talelin.latticy.service.UserIdentityService; import io.github.talelin.latticy.service.UserService; import io.github.talelin.latticy.vo.CreatedVO; +import io.github.talelin.latticy.vo.LoginCaptchaVO; import io.github.talelin.latticy.vo.UpdatedVO; import io.github.talelin.latticy.vo.UserInfoVO; import io.github.talelin.latticy.vo.UserPermissionVO; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.StringUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; 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.RequestHeader; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -56,6 +61,9 @@ public class UserController { @Autowired private DoubleJWT jwt; + @Autowired + private LoginCaptchaProperties captchaConfig; + /** * 用户注册 */ @@ -70,7 +78,16 @@ public CreatedVO register(@RequestBody @Validated RegisterDTO validator) { * 用户登陆 */ @PostMapping("/login") - public Tokens login(@RequestBody @Validated LoginDTO validator) { + public Tokens login(@RequestBody @Validated LoginDTO validator, @RequestHeader("Tag") String tag) { + // TODO: 使用spring validation验证。暂时还没想到怎么根据配置文件分组 + if (captchaConfig.getEnabled()) { + if (!StringUtils.hasText(validator.getCaptcha()) || !StringUtils.hasText(tag)) { + throw new ParameterException("验证码不可为空"); + } + if (!userService.verifyCaptcha(validator.getCaptcha(), tag)) { + throw new ForbiddenException(10260); + } + } UserDO user = userService.getUserByUsername(validator.getUsername()); if (user == null) { throw new NotFoundException(10021); @@ -85,6 +102,14 @@ public Tokens login(@RequestBody @Validated LoginDTO validator) { return jwt.generateTokens(user.getId()); } + @PostMapping("/captcha") + public LoginCaptchaVO userCaptcha() throws Exception { + if (captchaConfig.getEnabled()) { + return userService.generateCaptcha(); + } + return new LoginCaptchaVO(); + } + /** * 更新用户信息 */ diff --git a/src/main/java/io/github/talelin/latticy/dto/user/LoginDTO.java b/src/main/java/io/github/talelin/latticy/dto/user/LoginDTO.java index 89ee70ab..3239f974 100644 --- a/src/main/java/io/github/talelin/latticy/dto/user/LoginDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/user/LoginDTO.java @@ -18,4 +18,6 @@ public class LoginDTO { @NotBlank(message = "{password.new.not-blank}") private String password; + + private String captcha; } diff --git a/src/main/java/io/github/talelin/latticy/service/UserService.java b/src/main/java/io/github/talelin/latticy/service/UserService.java index c480d9d2..8b28e589 100644 --- a/src/main/java/io/github/talelin/latticy/service/UserService.java +++ b/src/main/java/io/github/talelin/latticy/service/UserService.java @@ -9,6 +9,7 @@ import io.github.talelin.latticy.model.GroupDO; import io.github.talelin.latticy.model.PermissionDO; import io.github.talelin.latticy.model.UserDO; +import io.github.talelin.latticy.vo.LoginCaptchaVO; import java.util.List; import java.util.Map; @@ -129,4 +130,18 @@ public interface UserService extends IService { * @return 超级管理员的id */ Integer getRootUserId(); + + /** + * 生成无状态的登录验证码 + * + * @return 验证码 + */ + LoginCaptchaVO generateCaptcha() throws Exception; + + /** + * 校验登录验证码 + * + * @return 结果 + */ + boolean verifyCaptcha(String captcha, String tag); } diff --git a/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java index 3cebbe1d..5dde38bd 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java @@ -7,9 +7,12 @@ import io.github.talelin.autoconfigure.exception.ForbiddenException; import io.github.talelin.autoconfigure.exception.NotFoundException; import io.github.talelin.autoconfigure.exception.ParameterException; +import io.github.talelin.latticy.bo.LoginCaptchaBO; import io.github.talelin.latticy.common.LocalUser; +import io.github.talelin.latticy.common.configuration.LoginCaptchaProperties; import io.github.talelin.latticy.common.enumeration.GroupLevelEnum; import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.util.CaptchaUtil; import io.github.talelin.latticy.dto.user.ChangePasswordDTO; import io.github.talelin.latticy.dto.user.RegisterDTO; import io.github.talelin.latticy.dto.user.UpdateInfoDTO; @@ -23,6 +26,7 @@ import io.github.talelin.latticy.service.PermissionService; import io.github.talelin.latticy.service.UserIdentityService; import io.github.talelin.latticy.service.UserService; +import io.github.talelin.latticy.vo.LoginCaptchaVO; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -54,6 +58,9 @@ public class UserServiceImpl extends ServiceImpl implements @Autowired private UserGroupMapper userGroupMapper; + @Autowired + private LoginCaptchaProperties captchaConfig; + @Transactional @Override public UserDO createUser(RegisterDTO dto) { @@ -215,6 +222,25 @@ public Integer getRootUserId() { return userGroupDO == null ? 0 : userGroupDO.getUserId(); } + @Override + public LoginCaptchaVO generateCaptcha() throws Exception { + String code = CaptchaUtil.getRandomString(CaptchaUtil.RANDOM_STR_NUM); + String base64String = CaptchaUtil.getRandomCodeBase64(code); + String tag = CaptchaUtil.getTag(code, captchaConfig.getSecret(), captchaConfig.getIv()); + return new LoginCaptchaVO(tag, "data:image/png;base64," + base64String); + } + + @Override + public boolean verifyCaptcha(String captcha, String tag) { + try { + LoginCaptchaBO captchaBO = CaptchaUtil.decodeTag(captchaConfig.getSecret(), captchaConfig.getIv(), tag); + return captcha.equalsIgnoreCase(captchaBO.getCaptcha()) || System.currentTimeMillis() > captchaBO.getExpired(); + } catch (Exception e) { + log.error(e.getMessage(), e); + return false; + } + } + private void checkGroupsExist(List ids) { for (Integer id : ids) { if (!groupService.checkGroupExistById(id)) { diff --git a/src/main/java/io/github/talelin/latticy/vo/LoginCaptchaVO.java b/src/main/java/io/github/talelin/latticy/vo/LoginCaptchaVO.java new file mode 100644 index 00000000..165ed2c5 --- /dev/null +++ b/src/main/java/io/github/talelin/latticy/vo/LoginCaptchaVO.java @@ -0,0 +1,22 @@ +package io.github.talelin.latticy.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Gadfly + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LoginCaptchaVO { + /** + * 加密后的验证码 + */ + private String tag; + /** + * 验证码图片地址,可使用base64 + */ + private String image; +} diff --git a/src/main/resources/DejaVuSerif-Bold.ttf b/src/main/resources/DejaVuSerif-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..3bb755fa1eab7ac6d8a6ab65e565c5d281cde857 GIT binary patch literal 356088 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 IJ*3b70dp)e4FCWD literal 0 HcmV?d00001 diff --git a/src/main/resources/ValidationMessages.properties b/src/main/resources/ValidationMessages.properties index 7d8750ff..b42f97c1 100644 --- a/src/main/resources/ValidationMessages.properties +++ b/src/main/resources/ValidationMessages.properties @@ -26,6 +26,7 @@ password.new.pattern=\u5BC6\u7801\u957F\u5EA6\u5FC5\u987B\u57286~22\u4F4D\u4E4B\ password.confirm.not-blank=\u786E\u8BA4\u5BC6\u7801\u4E0D\u53EF\u4E3A\u7A7A password.old.not-blank=\u65E7\u5BC6\u7801\u4E0D\u53EF\u4E3A\u7A7A password.equal-field=\u4E24\u6B21\u8F93\u5165\u5BC6\u7801\u4E0D\u4E00\u81F4 +captcha.not-blank=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A # \u56FE\u4E66\u5F02\u5E38\u4FE1\u606F book.title.not-empty=\u5FC5\u987B\u4F20\u5165\u56FE\u4E66\u540D book.title.length=\u56FE\u4E66\u540D\u4E0D\u80FD\u8D85\u8FC750\u5B57\u7B26 diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 328de942..2bf88cf9 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -18,6 +18,10 @@ spring: auth: enabled: true +# 开启登录要求验证码 +login-captcha: + enabled: false + secret: "ywdh012379y983hd938j091d" # 开启http请求日志记录 request-log: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 32d5b1bb..c9a49c36 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -16,6 +16,11 @@ spring: auth: enabled: true +# 开启登录要求验证码 +login-captcha: + enabled: false + secret: "ywdh012379y983hd938j091d" + # 开启请求日志记录 request-log: enabled: true diff --git a/src/main/resources/code-message.properties b/src/main/resources/code-message.properties index 4d26e867..daf33132 100644 --- a/src/main/resources/code-message.properties +++ b/src/main/resources/code-message.properties @@ -63,4 +63,5 @@ code-message[10160]=\u7C7B\u578B\u9519\u8BEF code-message[10170]=\u8BF7\u6C42\u4F53\u4E0D\u53EF\u4E3A\u7A7A code-message[10180]=\u5168\u90E8\u6587\u4EF6\u5927\u5C0F\u4E0D\u80FD\u8D85\u8FC7 code-message[10190]=\u8BFB\u53D6\u6587\u4EF6\u6570\u636E\u5931\u8D25 -code-message[10200]=\u5931\u8D25 \ No newline at end of file +code-message[10200]=\u5931\u8D25 +code-message[10260]=\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u9A8C\u8BC1\u7801 \ No newline at end of file From 830d05e0cac4a34e2d2507f4d9630d9ca3865bd2 Mon Sep 17 00:00:00 2001 From: Gadfly Date: Fri, 19 Nov 2021 17:08:01 +0800 Subject: [PATCH 07/20] =?UTF-8?q?fix:=20=E6=97=A0=E7=94=A8=E5=AD=97?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/github/talelin/latticy/common/util/CaptchaUtil.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java index 4d21d441..e169774c 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java @@ -40,10 +40,6 @@ public class CaptchaUtil { * 验证码的高 */ private static final int HEIGHT = 40; - /** - * 验证码中夹杂的干扰线数量 - */ - private static final int LINE_SIZE = 30; private static final String RANDOM_STRING = "23456789abcdefghijkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWSYZ"; private static final String AES = "AES"; private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding"; From 9cec35365d8c0e7d8c0fc0d803737e554f29dfec Mon Sep 17 00:00:00 2001 From: Gadfly Date: Fri, 19 Nov 2021 17:15:40 +0800 Subject: [PATCH 08/20] =?UTF-8?q?fix:=20=E4=B8=8D=E5=BC=80=E5=90=AF?= =?UTF-8?q?=E9=AA=8C=E8=AF=81=E7=A0=81=E6=97=B6=E8=A6=81=E6=B1=82header?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../talelin/latticy/controller/cms/UserController.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java b/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java index 60065ed9..06198c16 100644 --- a/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java +++ b/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java @@ -1,6 +1,5 @@ package io.github.talelin.latticy.controller.cms; -import io.github.talelin.autoconfigure.exception.ForbiddenException; import io.github.talelin.autoconfigure.exception.NotFoundException; import io.github.talelin.autoconfigure.exception.ParameterException; import io.github.talelin.core.annotation.AdminRequired; @@ -78,14 +77,14 @@ public CreatedVO register(@RequestBody @Validated RegisterDTO validator) { * 用户登陆 */ @PostMapping("/login") - public Tokens login(@RequestBody @Validated LoginDTO validator, @RequestHeader("Tag") String tag) { - // TODO: 使用spring validation验证。暂时还没想到怎么根据配置文件分组 + public Tokens login(@RequestBody @Validated LoginDTO validator, @RequestHeader(value = "Tag", required = false) String tag) { if (captchaConfig.getEnabled()) { + // TODO: 使用spring validation验证。暂时还没想到怎么根据配置文件分组 if (!StringUtils.hasText(validator.getCaptcha()) || !StringUtils.hasText(tag)) { throw new ParameterException("验证码不可为空"); } if (!userService.verifyCaptcha(validator.getCaptcha(), tag)) { - throw new ForbiddenException(10260); + throw new ParameterException(10260); } } UserDO user = userService.getUserByUsername(validator.getUsername()); From 5d6d9af696e8841594e49d6e4782ea4cfe8df7b6 Mon Sep 17 00:00:00 2001 From: Gadfly Date: Fri, 19 Nov 2021 17:59:17 +0800 Subject: [PATCH 09/20] fix: sout clean --- .../java/io/github/talelin/latticy/common/util/CaptchaUtil.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java index e169774c..57d47fb3 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java @@ -170,7 +170,6 @@ public static LoginCaptchaBO decodeTag(String secret, String iv, String tag) thr public static String aesEncode(String secret, String iv, String content) throws GeneralSecurityException { SecretKey secretKey = new SecretKeySpec(secret.getBytes(), AES); Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM); - System.out.println(iv.length() + "///" + iv.getBytes(StandardCharsets.UTF_8).length + "///" + iv.getBytes(StandardCharsets.US_ASCII).length); cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv.getBytes(StandardCharsets.US_ASCII))); byte[] byteEncode = content.getBytes(StandardCharsets.UTF_8); // 根据密码器的初始化方式加密 From 6f8103380d694da328f8b026fa9bcad007747dc4 Mon Sep 17 00:00:00 2001 From: Gadfly Date: Fri, 19 Nov 2021 18:44:46 +0800 Subject: [PATCH 10/20] fix: aes192 to 256 --- .../common/configuration/LoginCaptchaProperties.java | 2 +- src/main/resources/application-dev.yml | 8 ++++---- src/main/resources/application-prod.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java index 5c0b7970..0b5284af 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java @@ -17,7 +17,7 @@ public class LoginCaptchaProperties { /** * aes 密钥 */ - private String secret; + private String secret = CaptchaUtil.getRandomString(32); /** * aes 偏移量 */ diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 2bf88cf9..e5475404 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -2,14 +2,14 @@ server: # 服务端口 - port: 5000 + port: 3000 spring: # 数据源配置,请修改为你项目的实际配置 datasource: username: "root" - password: "123456" + password: "yishiyile" driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/lin-cms?useSSL=false&serverTimezone=UTC&characterEncoding=UTF8 @@ -20,8 +20,8 @@ auth: # 开启登录要求验证码 login-captcha: - enabled: false - secret: "ywdh012379y983hd938j091d" + enabled: true + secret: "m49CPM5ak@MDXTzbbT_ZEyMM3KBsBn!h" # 开启http请求日志记录 request-log: diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index c9a49c36..a9b034d8 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -19,7 +19,7 @@ auth: # 开启登录要求验证码 login-captcha: enabled: false - secret: "ywdh012379y983hd938j091d" + secret: "m49CPM5ak@MDXTzbbT_ZEyMM3KBsBn!h" # 开启请求日志记录 request-log: From c99f73da958180e354ebffcf1c81742cac805a8c Mon Sep 17 00:00:00 2001 From: Gadfly Date: Fri, 19 Nov 2021 18:45:27 +0800 Subject: [PATCH 11/20] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E4=BF=AE=E6=94=B9=E7=9A=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e5475404..e5ff737c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -2,14 +2,14 @@ server: # 服务端口 - port: 3000 + port: 5000 spring: # 数据源配置,请修改为你项目的实际配置 datasource: username: "root" - password: "yishiyile" + password: "123456" driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/lin-cms?useSSL=false&serverTimezone=UTC&characterEncoding=UTF8 From 44f84602e66c5965abb79188f17e837385154d89 Mon Sep 17 00:00:00 2001 From: Gadfly Date: Sat, 20 Nov 2021 04:15:45 +0800 Subject: [PATCH 12/20] =?UTF-8?q?fix:=20=E5=A2=9E=E5=8A=A0aes=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=A0=A1=E9=AA=8C=EF=BC=8C=E9=81=BF=E5=85=8D=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E5=8F=82=E6=95=B0=E4=B8=8D=E5=90=88=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../configuration/LoginCaptchaProperties.java | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java index 0b5284af..d5a24b51 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java @@ -3,12 +3,15 @@ import io.github.talelin.latticy.common.util.CaptchaUtil; import lombok.Getter; import lombok.Setter; +import lombok.extern.slf4j.Slf4j; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; /** * @author Gadfly */ +@Slf4j @Getter @Setter @Component @@ -17,13 +20,38 @@ public class LoginCaptchaProperties { /** * aes 密钥 */ - private String secret = CaptchaUtil.getRandomString(32); + private String secret; /** * aes 偏移量 */ - private String iv = CaptchaUtil.getRandomString(16); + private String iv; /** * 启用验证码 */ private Boolean enabled = Boolean.FALSE; + + public void setSecret(String secret) { + this.secret = CaptchaUtil.getRandomString(32); + if (StringUtils.hasText(secret)) { + byte[] bytes = secret.getBytes(); + if (bytes.length == 16 || bytes.length == 24 || bytes.length == 32) { + this.secret = secret; + } else { + log.warn("AES密钥必须为128/192/256bit,输入的密钥为{}bit,已启用随机密钥{}", bytes.length * 8, this.secret); + } + } + } + + public void setIv(String iv) { + this.iv = CaptchaUtil.getRandomString(16); + if (StringUtils.hasText(iv)) { + byte[] bytes = iv.getBytes(); + if (bytes.length == 16) { + this.iv = iv; + } else { + log.warn("AES初始向量必须为128bit,输入的密钥为{}bit,已启用随机向量{}", bytes.length * 8, this.iv); + } + } + } + } From a83c65680c622758182c168a540a280c3a8a4e42 Mon Sep 17 00:00:00 2001 From: Gadfly Date: Mon, 22 Nov 2021 11:04:36 +0800 Subject: [PATCH 13/20] =?UTF-8?q?fix:=20=E9=83=A8=E5=88=86=E6=83=85?= =?UTF-8?q?=E5=86=B5=E4=B8=8BfileMap.size()=E4=B8=8D=E5=8F=AF=E9=9D=A0&?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E4=BF=A1=E6=81=AF=E4=B8=8D=E9=9C=80=E8=A6=81?= =?UTF-8?q?${}?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../talelin/latticy/module/file/AbstractUploader.java | 9 ++++++++- src/main/resources/ValidationMessages.properties | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/module/file/AbstractUploader.java b/src/main/java/io/github/talelin/latticy/module/file/AbstractUploader.java index f738b1bc..b0f3f5ab 100644 --- a/src/main/java/io/github/talelin/latticy/module/file/AbstractUploader.java +++ b/src/main/java/io/github/talelin/latticy/module/file/AbstractUploader.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; /** * 文件上传类的基类 @@ -128,7 +129,13 @@ protected void checkFileMap(MultiValueMap fileMap) { throw new NotFoundException(10026); } int nums = getFileProperties().getNums(); - if (fileMap.size() > nums) { + AtomicInteger sizes = new AtomicInteger(); + fileMap.keySet().forEach(key -> fileMap.get(key).forEach(file -> { + if (!file.isEmpty()) { + sizes.getAndIncrement(); + } + })); + if (sizes.get() > nums) { throw new FileTooManyException(10121); } } diff --git a/src/main/resources/ValidationMessages.properties b/src/main/resources/ValidationMessages.properties index b42f97c1..b209f4c8 100644 --- a/src/main/resources/ValidationMessages.properties +++ b/src/main/resources/ValidationMessages.properties @@ -1,7 +1,7 @@ # \u901A\u7528\u5F02\u5E38\u4FE1\u606F id.positive=id\u5FC5\u987B\u4E3A\u6B63\u6574\u6570 page.count.min=\u5206\u9875\u6570\u91CF\u5FC5\u987B\u4E3A\u6B63\u6574\u6570 -page.count.max=\u5206\u9875\u6570\u91CF\u5FC5\u987B\u5C0F\u4E8E${value} +page.count.max=\u5206\u9875\u6570\u91CF\u5FC5\u987B\u5C0F\u4E8E{value} page.number.min=\u5206\u9875\u9875\u7801\u5FC5\u987B\u4E3A\u6B63\u6574\u6570 # \u5206\u7EC4\u5F02\u5E38\u4FE1\u606F group.id.positive=\u5206\u7EC4id\u5FC5\u987B\u4E3A\u6B63\u6574\u6570 From 19c21d8c6e226d7e1a07d02d6a45cf912eae4d9b Mon Sep 17 00:00:00 2001 From: Gadfly Date: Mon, 22 Nov 2021 11:12:52 +0800 Subject: [PATCH 14/20] =?UTF-8?q?fix:=20=E5=B1=9E=E6=80=A7=E5=89=8D?= =?UTF-8?q?=E7=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/code-message.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/code-message.properties b/src/main/resources/code-message.properties index daf33132..07b5c90a 100644 --- a/src/main/resources/code-message.properties +++ b/src/main/resources/code-message.properties @@ -55,7 +55,7 @@ code-message[10080]=\u8BF7\u6C42\u65B9\u6CD5\u4E0D\u5141\u8BB8 code-message[10100]=\u5237\u65B0\u4EE4\u724C\u83B7\u53D6\u5931\u8D25 code-message[10110]=\u6587\u4EF6\u4F53\u79EF\u8FC7\u5927 code-message[10120]=\u6587\u4EF6\u6570\u91CF\u8FC7\u591A -code-message[10121]=\u6587\u4EF6\u592A\u591A\uFF0C\u6587\u4EF6\u603B\u6570\u4E0D\u53EF\u8D85\u8FC7${file.nums} +code-message[10121]=\u6587\u4EF6\u592A\u591A\uFF0C\u6587\u4EF6\u603B\u6570\u4E0D\u53EF\u8D85\u8FC7${lin.file.nums} code-message[10130]=\u6587\u4EF6\u6269\u5C55\u540D\u4E0D\u7B26\u5408\u89C4\u8303 code-message[10140]=\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5 code-message[10150]=\u4E22\u5931\u53C2\u6570 From 838ca9cdc896f2b8bc5602e59ee15556ee29740c Mon Sep 17 00:00:00 2001 From: Gadfly Date: Thu, 9 Dec 2021 20:21:16 +0800 Subject: [PATCH 15/20] fix: fix #264 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 配置文件中未配置字段时,spring不会调用set方法 --- .../common/configuration/LoginCaptchaProperties.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java index d5a24b51..fb7c9a13 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java @@ -20,18 +20,17 @@ public class LoginCaptchaProperties { /** * aes 密钥 */ - private String secret; + private String secret = CaptchaUtil.getRandomString(32); /** * aes 偏移量 */ - private String iv; + private String iv = CaptchaUtil.getRandomString(16); /** * 启用验证码 */ private Boolean enabled = Boolean.FALSE; public void setSecret(String secret) { - this.secret = CaptchaUtil.getRandomString(32); if (StringUtils.hasText(secret)) { byte[] bytes = secret.getBytes(); if (bytes.length == 16 || bytes.length == 24 || bytes.length == 32) { @@ -43,7 +42,6 @@ public void setSecret(String secret) { } public void setIv(String iv) { - this.iv = CaptchaUtil.getRandomString(16); if (StringUtils.hasText(iv)) { byte[] bytes = iv.getBytes(); if (bytes.length == 16) { From 5ce80648502a7929614df5d5c808e14ae1d08ef2 Mon Sep 17 00:00:00 2001 From: Colorful Date: Sat, 13 Aug 2022 20:42:14 +0800 Subject: [PATCH 16/20] =?UTF-8?q?chore(application-dev.yml):=20=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=85=B3=E9=97=AD=E9=AA=8C=E8=AF=81=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-dev.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e5ff737c..7fb07f12 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -20,7 +20,7 @@ auth: # 开启登录要求验证码 login-captcha: - enabled: true + enabled: false secret: "m49CPM5ak@MDXTzbbT_ZEyMM3KBsBn!h" # 开启http请求日志记录 From 92f511d2ed4f2bf8603a42df3272e324230a4acd Mon Sep 17 00:00:00 2001 From: Colorful Date: Sun, 25 Sep 2022 22:44:48 +0800 Subject: [PATCH 17/20] =?UTF-8?q?refactor:=20=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20is=5Fdeleted=20=E5=AD=97=E6=AE=B5=EF=BC=8C?= =?UTF-8?q?=E4=BD=9C=E4=B8=BA=E5=88=A0=E9=99=A4=E6=A0=87=E8=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../talelin/latticy/model/BaseModel.java | 5 ++++- src/main/resources/application.yml | 4 ++-- src/main/resources/mapper/BookMapper.xml | 5 +++-- src/main/resources/mapper/FileMapper.xml | 5 +++-- src/main/resources/mapper/GroupMapper.xml | 14 +++++++------- src/main/resources/mapper/LogMapper.xml | 7 ++++--- .../resources/mapper/PermissionMapper.xml | 7 ++++--- src/main/resources/mapper/UserMapper.xml | 7 ++++--- src/main/resources/schema.sql | 19 +++++++++++++------ src/test/resources/h2-test.sql | 7 +++++++ 10 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/model/BaseModel.java b/src/main/java/io/github/talelin/latticy/model/BaseModel.java index 9b672be5..d92e147f 100644 --- a/src/main/java/io/github/talelin/latticy/model/BaseModel.java +++ b/src/main/java/io/github/talelin/latticy/model/BaseModel.java @@ -22,7 +22,10 @@ public class BaseModel { @JsonIgnore private Date updateTime; - @TableLogic @JsonIgnore private Date deleteTime; + + @TableLogic + @JsonIgnore + private Boolean isDeleted; } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index fe30c011..a8e7b89a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -25,8 +25,8 @@ mybatis-plus: banner: false db-config: # 逻辑删除(软删除) - logic-delete-value: NOW() - logic-not-delete-value: 'NULL' + logic-delete-value: 1 + logic-not-delete-value: 0 # mapper路径位置 mapper-locations: classpath:mapper/*.xml diff --git a/src/main/resources/mapper/BookMapper.xml b/src/main/resources/mapper/BookMapper.xml index 3da0f20f..0105be61 100644 --- a/src/main/resources/mapper/BookMapper.xml +++ b/src/main/resources/mapper/BookMapper.xml @@ -11,6 +11,7 @@ + @@ -20,7 +21,7 @@ WHERE b.title LIKE #{q} AND - b.delete_time IS NULL + b.is_deleted = 0 \ No newline at end of file diff --git a/src/main/resources/mapper/FileMapper.xml b/src/main/resources/mapper/FileMapper.xml index d51e9075..a9f37324 100644 --- a/src/main/resources/mapper/FileMapper.xml +++ b/src/main/resources/mapper/FileMapper.xml @@ -14,6 +14,7 @@ + diff --git a/src/main/resources/mapper/GroupMapper.xml b/src/main/resources/mapper/GroupMapper.xml index 5f979713..904d9401 100644 --- a/src/main/resources/mapper/GroupMapper.xml +++ b/src/main/resources/mapper/GroupMapper.xml @@ -10,7 +10,7 @@ - + @@ -19,7 +19,7 @@ g.create_time,g.update_time,g.delete_time from lin_group AS g WHERE - g.delete_time IS NULL + g.is_deleted = 0 AND g.id IN ( @@ -28,7 +28,7 @@ LEFT JOIN lin_user_group as ug ON ug.user_id = u.id WHERE u.id = #{userId} - AND u.delete_time IS NULL + AND u.is_deleted = 0 ) @@ -36,7 +36,7 @@ SELECT g.id from lin_group AS g WHERE - g.delete_time IS NULL + g.is_deleted = 0 AND g.id IN ( @@ -45,12 +45,12 @@ LEFT JOIN lin_user_group as ug ON ug.user_id = u.id WHERE u.id = #{userId} - AND u.delete_time IS NULL + AND u.is_deleted = 0 ) diff --git a/src/main/resources/mapper/LogMapper.xml b/src/main/resources/mapper/LogMapper.xml index a7090228..69d58f74 100644 --- a/src/main/resources/mapper/LogMapper.xml +++ b/src/main/resources/mapper/LogMapper.xml @@ -15,11 +15,12 @@ + SELECT l.* FROM lin_log l - WHERE l.delete_time IS NULL + WHERE l.is_deleted = 0 AND l.username=#{name} @@ -47,7 +48,7 @@ diff --git a/src/main/resources/mapper/PermissionMapper.xml b/src/main/resources/mapper/PermissionMapper.xml index 4b12f5e8..4df0293d 100644 --- a/src/main/resources/mapper/PermissionMapper.xml +++ b/src/main/resources/mapper/PermissionMapper.xml @@ -11,6 +11,7 @@ + - SELECT COUNT(*) FROM lin_user as u WHERE u.username = #{username} AND u.delete_time IS NULL + SELECT COUNT(*) FROM lin_user as u WHERE u.username = #{username} AND u.is_deleted = 0 diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index ed9436d0..5100cd9e 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -17,6 +17,7 @@ CREATE TABLE lin_file create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY md5_del (md5, delete_time) ) ENGINE = InnoDB @@ -40,6 +41,7 @@ CREATE TABLE lin_log create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 @@ -57,7 +59,8 @@ CREATE TABLE lin_permission mount tinyint(1) NOT NULL DEFAULT 1 COMMENT '0:关闭 1:开启', create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), - delete_time datetime(3) DEFAULT NULL, + delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 @@ -76,6 +79,7 @@ CREATE TABLE lin_group create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY name_del (name, delete_time) ) ENGINE = InnoDB @@ -111,6 +115,7 @@ CREATE TABLE lin_user create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY username_del (username, delete_time), UNIQUE KEY email_del (email, delete_time) @@ -120,11 +125,11 @@ CREATE TABLE lin_user -- ---------------------------- -- 用户授权信息表 -# id -# user_id -# identity_type 登录类型(手机号 邮箱 用户名)或第三方应用名称(微信 微博等) -# identifier 标识(手机号 邮箱 用户名或第三方应用的唯一标识) -# credential 密码凭证(站内的保存密码,站外的不保存或保存token) +-- id +-- user_id +-- identity_type 登录类型(手机号 邮箱 用户名)或第三方应用名称(微信 微博等) +-- identifier 标识(手机号 邮箱 用户名或第三方应用的唯一标识) +-- credential 密码凭证(站内的保存密码,站外的不保存或保存token) -- ---------------------------- DROP TABLE IF EXISTS lin_user_identity; CREATE TABLE lin_user_identity @@ -137,6 +142,7 @@ CREATE TABLE lin_user_identity create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 @@ -172,6 +178,7 @@ CREATE TABLE book create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 diff --git a/src/test/resources/h2-test.sql b/src/test/resources/h2-test.sql index d34ec581..1836c55b 100644 --- a/src/test/resources/h2-test.sql +++ b/src/test/resources/h2-test.sql @@ -9,6 +9,7 @@ CREATE TABLE book create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; @@ -26,6 +27,7 @@ CREATE TABLE lin_file create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY md5_del (md5, delete_time) ) ENGINE = InnoDB @@ -45,6 +47,7 @@ CREATE TABLE lin_log create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; @@ -62,6 +65,7 @@ CREATE TABLE lin_permission create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; @@ -79,6 +83,7 @@ CREATE TABLE lin_group create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY name_del (name, delete_time) ) ENGINE = InnoDB @@ -111,6 +116,7 @@ CREATE TABLE lin_user create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id), UNIQUE KEY username_del (username, delete_time), UNIQUE KEY email_del (email, delete_time) @@ -128,6 +134,7 @@ CREATE TABLE lin_user_identity create_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), update_time datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), delete_time datetime(3) DEFAULT NULL, + is_deleted tinyint(1) DEFAULT 0, PRIMARY KEY (id) ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; From c08253f4351696a1550b5ed4ac06a4f799caec29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8C=AA=E8=B9=84?= Date: Sun, 20 Nov 2022 22:37:23 +0800 Subject: [PATCH 18/20] refactor: non null copy (#300) --- .../latticy/common/util/BeanCopyUtil.java | 138 ++++++++++++++++++ .../latticy/service/impl/UserServiceImpl.java | 16 +- 2 files changed, 140 insertions(+), 14 deletions(-) create mode 100644 src/main/java/io/github/talelin/latticy/common/util/BeanCopyUtil.java diff --git a/src/main/java/io/github/talelin/latticy/common/util/BeanCopyUtil.java b/src/main/java/io/github/talelin/latticy/common/util/BeanCopyUtil.java new file mode 100644 index 00000000..92607f9e --- /dev/null +++ b/src/main/java/io/github/talelin/latticy/common/util/BeanCopyUtil.java @@ -0,0 +1,138 @@ +package io.github.talelin.latticy.common.util; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ReflectionUtils; + +import java.util.*; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +/** + * @author Gadfly + */ +@Slf4j +public class BeanCopyUtil extends BeanUtils { + public static void copyNonNullProperties(Object source, Object target) { + String[] properties = Arrays.stream(ReflectionUtils.getDeclaredMethods(source.getClass())) + .map(method -> { + if (method.getName().startsWith("get")) { + Object fieldValue = null; + try { + fieldValue = method.invoke(source); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + if (fieldValue == null) { + String fieldName = method.getName().substring(3); + return com.baomidou.mybatisplus.core.toolkit.StringUtils.firstToLowerCase(fieldName); + } + } + return null; + }) + .filter(Objects::nonNull) + .toArray(String[]::new); + copyProperties(source, target, properties); + } + + /** + * 集合数据的拷贝 + * + * @param source: 数据源类 + * @param target: 目标类::new(eg: UserVO::new) + * @return 拷贝后的集合 + */ + public static T copyProperties(S source, Supplier target) { + T t = target.get(); + copyProperties(source, t); + return t; + } + + /** + * 集合数据的拷贝 + * + * @param sources: 数据源类 + * @param target: 目标类::new(eg: UserVO::new) + * @return 拷贝后的集合 + */ + public static List copyListProperties(List sources, Supplier target) { + return copyListProperties(sources, target, null); + } + + /** + * 带回调函数的集合数据的拷贝(可自定义字段拷贝规则) + * + * @param sources: 数据源类 + * @param target: 目标类::new(eg: UserVO::new) + * @param callBack: 回调函数 + * @return 拷贝后的集合 + */ + public static List copyListProperties(List sources, Supplier target, + BeanCopyUtilCallBack callBack) { + if (CollectionUtils.isEmpty(sources)) { + return new ArrayList<>(); + } + List list = new ArrayList<>(sources.size()); + for (S source : sources) { + T t = target.get(); + copyProperties(source, t); + list.add(t); + if (null != callBack) { + // 回调 + callBack.callBack(source, t); + } + } + return list; + } + + public static T copySingleProperties(S source, Supplier target, BeanCopyUtilCallBack callBack) { + T t = target.get(); + copyProperties(source, t); + if (null != callBack) { + // 回调 + callBack.callBack(source, t); + } + return t; + } + + /** + * 将集合对象中的类型转换成另一种类型 + * + * @param collection 集合 + * @param clazz 目标对象 + * @return 转换后的集合 + */ + public static Collection covertObject(Collection collection, Class clazz, + BeanCopyUtilCallBack callBack) { + if (CollectionUtils.isEmpty(collection)) { + return new ArrayList<>(); + } + return collection.stream().map(oldObject -> { + T instance = null; + try { + instance = clazz.getDeclaredConstructor().newInstance(); + copyProperties(oldObject, instance); + if (null != callBack) { + // 回调 + callBack.callBack(oldObject, instance); + } + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return instance; + }).collect(Collectors.toList()); + } + + @FunctionalInterface + public interface BeanCopyUtilCallBack { + + /** + * 定义默认回调方法 + * + * @param t target + * @param s source + */ + void callBack(S s, T t); + } +} diff --git a/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java index 5dde38bd..5db15eef 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java @@ -12,6 +12,7 @@ import io.github.talelin.latticy.common.configuration.LoginCaptchaProperties; import io.github.talelin.latticy.common.enumeration.GroupLevelEnum; import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.util.BeanCopyUtil; import io.github.talelin.latticy.common.util.CaptchaUtil; import io.github.talelin.latticy.dto.user.ChangePasswordDTO; import io.github.talelin.latticy.dto.user.RegisterDTO; @@ -114,20 +115,7 @@ public UserDO updateUserInfo(UpdateInfoDTO dto) { user.setUsername(dto.getUsername()); } } - - // todo 增加工具类实现忽略 null 的 BeanCopy,简化这段代码 - if (dto.getUsername() != null) { - user.setUsername(dto.getUsername()); - } - if (dto.getAvatar() != null) { - user.setAvatar(dto.getAvatar()); - } - if (dto.getEmail() != null) { - user.setEmail(dto.getEmail()); - } - if (dto.getNickname() != null) { - user.setNickname(dto.getNickname()); - } + BeanCopyUtil.copyNonNullProperties(dto, user); this.baseMapper.updateById(user); return user; From b78eac3638513ab5f4519891a5297e84d3c4be4d Mon Sep 17 00:00:00 2001 From: Colorful Date: Mon, 28 Nov 2022 11:51:35 +0800 Subject: [PATCH 19/20] Refactor/code specification (#312) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 数据库新增 is_deleted 字段,作为删除标识 * refactor: 整理代码规范 --- .../talelin/latticy/LatticyApplication.java | 1 + .../talelin/latticy/common/LocalUser.java | 2 +- .../latticy/common/aop/ResultAspect.java | 2 +- .../CodeMessageConfiguration.java | 2 ++ .../configuration/CommonConfiguration.java | 8 ++--- ...mServletModelAttributeMethodProcessor.java | 4 ++- .../CustomServletRequestDataBinder.java | 2 ++ .../configuration/LoginCaptchaProperties.java | 7 ++++- .../common/constant/IdentityConstant.java | 4 +++ .../common/enumeration/GroupLevelEnum.java | 2 ++ .../exception/RestExceptionHandler.java | 2 ++ .../factory/YamlPropertySourceFactory.java | 1 + .../AuthorizeVerifyResolverImpl.java | 1 + .../common/interceptor/LoggerImpl.java | 1 + .../interceptor/RequestLogInterceptor.java | 1 + .../listener/PermissionHandleListener.java | 1 + .../talelin/latticy/common/mybatis/Page.java | 1 + .../latticy/common/util/CaptchaUtil.java | 1 + .../talelin/latticy/common/util/IPUtil.java | 1 + .../talelin/latticy/common/util/PageUtil.java | 5 +-- .../latticy/common/util/ResponseUtil.java | 7 +---- .../latticy/controller/v1/BookController.java | 6 ++-- .../dto/admin/DispatchPermissionDTO.java | 1 + .../dto/admin/DispatchPermissionsDTO.java | 1 + .../latticy/dto/admin/NewGroupDTO.java | 1 + .../latticy/dto/admin/QueryUsersDTO.java | 1 + .../dto/admin/RemovePermissionsDTO.java | 1 + .../latticy/dto/admin/ResetPasswordDTO.java | 1 + .../latticy/dto/admin/UpdateGroupDTO.java | 1 + .../latticy/dto/admin/UpdateUserInfoDTO.java | 1 + .../dto/book/CreateOrUpdateBookDTO.java | 1 + .../talelin/latticy/dto/log/QueryLogDTO.java | 1 + .../latticy/dto/query/BasePageDTO.java | 2 +- .../latticy/dto/user/ChangePasswordDTO.java | 1 + .../talelin/latticy/dto/user/LoginDTO.java | 1 + .../talelin/latticy/dto/user/RegisterDTO.java | 1 + .../latticy/dto/user/UpdateInfoDTO.java | 1 + .../latticy/extension/file/QiniuUploader.java | 2 +- .../talelin/latticy/mapper/BookMapper.java | 11 +++++++ .../talelin/latticy/mapper/FileMapper.java | 11 +++++++ .../talelin/latticy/mapper/GroupMapper.java | 2 ++ .../latticy/mapper/GroupPermissionMapper.java | 12 +++++++ .../talelin/latticy/mapper/LogMapper.java | 29 ++++++++++++++++- .../latticy/mapper/PermissionMapper.java | 1 + .../latticy/mapper/UserGroupMapper.java | 11 +++++++ .../latticy/mapper/UserIdentityMapper.java | 1 + .../talelin/latticy/mapper/UserMapper.java | 3 +- .../talelin/latticy/model/BaseModel.java | 1 + .../github/talelin/latticy/model/BookDO.java | 1 + .../github/talelin/latticy/model/FileDO.java | 1 + .../github/talelin/latticy/model/GroupDO.java | 1 + .../latticy/model/GroupPermissionDO.java | 1 + .../github/talelin/latticy/model/LogDO.java | 1 + .../talelin/latticy/model/PermissionDO.java | 1 + .../github/talelin/latticy/model/UserDO.java | 1 + .../talelin/latticy/model/UserGroupDO.java | 1 + .../talelin/latticy/model/UserIdentityDO.java | 12 ++----- .../talelin/latticy/module/file/File.java | 1 + .../latticy/module/file/FileConstant.java | 6 ++-- .../latticy/module/file/FileProperties.java | 1 + .../talelin/latticy/module/file/FileUtil.java | 3 ++ .../latticy/module/file/UploadHandler.java | 3 ++ .../latticy/module/log/MDCAccessConstant.java | 3 ++ .../module/message/MessageConstant.java | 3 ++ .../message/MessageWebSocketHandler.java | 1 + .../module/message/WebSocketInterceptor.java | 3 +- .../message/WebsocketConfiguration.java | 1 + .../latticy/module/message/WsHandler.java | 1 + .../latticy/module/message/WsHandlerImpl.java | 1 + .../talelin/latticy/service/AdminService.java | 1 + .../talelin/latticy/service/BookService.java | 31 +++++++++++++++++++ .../talelin/latticy/service/FileService.java | 1 + .../talelin/latticy/service/GroupService.java | 2 ++ .../talelin/latticy/service/LogService.java | 1 + .../latticy/service/PermissionService.java | 1 + .../latticy/service/UserIdentityService.java | 1 + .../talelin/latticy/service/UserService.java | 2 +- .../service/impl/AdminServiceImpl.java | 27 +++++----------- .../latticy/service/impl/BookServiceImpl.java | 10 +++--- .../latticy/service/impl/FileServiceImpl.java | 3 +- .../service/impl/GroupServiceImpl.java | 4 +-- .../latticy/service/impl/LogServiceImpl.java | 10 +++--- .../service/impl/PermissionServiceImpl.java | 3 +- .../service/impl/UserIdentityServiceImpl.java | 1 + .../latticy/service/impl/UserServiceImpl.java | 1 + .../github/talelin/latticy/vo/CreatedVO.java | 1 + .../github/talelin/latticy/vo/DeletedVO.java | 1 + .../talelin/latticy/vo/LoginCaptchaVO.java | 1 + .../talelin/latticy/vo/PageResponseVO.java | 2 +- .../talelin/latticy/vo/UnifyResponseVO.java | 2 +- .../github/talelin/latticy/vo/UpdatedVO.java | 1 + src/main/resources/application-dev.yml | 2 +- 92 files changed, 240 insertions(+), 78 deletions(-) diff --git a/src/main/java/io/github/talelin/latticy/LatticyApplication.java b/src/main/java/io/github/talelin/latticy/LatticyApplication.java index 4667027b..05f91192 100644 --- a/src/main/java/io/github/talelin/latticy/LatticyApplication.java +++ b/src/main/java/io/github/talelin/latticy/LatticyApplication.java @@ -8,6 +8,7 @@ /** * @author pedro@TaleLin + * 启动入口类 */ @RestController @MapperScan(basePackages = {"io.github.talelin.latticy.mapper"}) diff --git a/src/main/java/io/github/talelin/latticy/common/LocalUser.java b/src/main/java/io/github/talelin/latticy/common/LocalUser.java index eaebb1dd..7388c979 100644 --- a/src/main/java/io/github/talelin/latticy/common/LocalUser.java +++ b/src/main/java/io/github/talelin/latticy/common/LocalUser.java @@ -3,7 +3,7 @@ import io.github.talelin.latticy.model.UserDO; /** - * 线程安全的当前登录用户,如果用户为登录,则得到 null + * 线程安全的当前登录用户,如果用户未登录,则得到 null * * @author pedro@TaleLin */ diff --git a/src/main/java/io/github/talelin/latticy/common/aop/ResultAspect.java b/src/main/java/io/github/talelin/latticy/common/aop/ResultAspect.java index 7407117a..635326d5 100644 --- a/src/main/java/io/github/talelin/latticy/common/aop/ResultAspect.java +++ b/src/main/java/io/github/talelin/latticy/common/aop/ResultAspect.java @@ -8,7 +8,7 @@ import org.springframework.util.StringUtils; /** - * 处理返回结果为 UnifyResponseVO 的 Controller + * 处理返回结果为 UnifyResponseVO 的控制器层方法 * message 默认为 null,在此处通过 code 设置为对应消息 * * @author pedro@TaleLin diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/CodeMessageConfiguration.java b/src/main/java/io/github/talelin/latticy/common/configuration/CodeMessageConfiguration.java index 27696f9e..ac893d21 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/CodeMessageConfiguration.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/CodeMessageConfiguration.java @@ -9,6 +9,8 @@ /** * @author pedro@TaleLin + * + * 消息码配置类 */ @SuppressWarnings("ConfigurationProperties") @Component diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java b/src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java index a821d908..1546e074 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/CommonConfiguration.java @@ -18,6 +18,8 @@ /** * @author pedro@TaleLin * @author colorful@TaleLin + * + * 公共配置 */ @Configuration(proxyBeanMethods = false) public class CommonConfiguration { @@ -29,8 +31,6 @@ public RequestLogInterceptor requestLogInterceptor() { /** * 新的分页插件,一缓和二缓遵循mybatis的规则 - * 需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除) - * 参考链接:https://mp.baomidou.com/guide/interceptor.html */ @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { @@ -39,9 +39,6 @@ public MybatisPlusInterceptor mybatisPlusInterceptor() { return interceptor; } - /** - * 参考链接:https://mp.baomidou.com/guide/interceptor.html - */ @Bean @SuppressWarnings("deprecation") public ConfigurationCustomizer configurationCustomizer() { @@ -70,7 +67,6 @@ public PermissionMetaCollector postProcessBeans() { @Bean public Jackson2ObjectMapperBuilderCustomizer customJackson() { return jacksonObjectMapperBuilder -> { - // jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL); jacksonObjectMapperBuilder.failOnUnknownProperties(false); jacksonObjectMapperBuilder.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE); }; diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/CustomServletModelAttributeMethodProcessor.java b/src/main/java/io/github/talelin/latticy/common/configuration/CustomServletModelAttributeMethodProcessor.java index 934bc33d..f7a9b5c8 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/CustomServletModelAttributeMethodProcessor.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/CustomServletModelAttributeMethodProcessor.java @@ -10,8 +10,10 @@ /** * @author Gadfly + * + * A customizing Servlet-specific ModelAttributeMethodProcessor that applies data binding through + * a WebDataBinder of type CustomServletModelAttributeMethodProcessor. */ - public class CustomServletModelAttributeMethodProcessor extends ServletModelAttributeMethodProcessor { public CustomServletModelAttributeMethodProcessor(final boolean annotationNotRequired) { diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/CustomServletRequestDataBinder.java b/src/main/java/io/github/talelin/latticy/common/configuration/CustomServletRequestDataBinder.java index 79029f71..8ebca2de 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/CustomServletRequestDataBinder.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/CustomServletRequestDataBinder.java @@ -11,6 +11,8 @@ /** * @author Gadfly + * + * 自定义servlet请求参数绑定类 */ public class CustomServletRequestDataBinder extends ServletRequestDataBinder { diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java index fb7c9a13..20e1f418 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java @@ -10,6 +10,10 @@ /** * @author Gadfly + * + * 登录图形验证码配置类 + * + * TODO 整理代码规范 */ @Slf4j @Getter @@ -42,9 +46,10 @@ public void setSecret(String secret) { } public void setIv(String iv) { + final long ivLen = 16; if (StringUtils.hasText(iv)) { byte[] bytes = iv.getBytes(); - if (bytes.length == 16) { + if (bytes.length == ivLen) { this.iv = iv; } else { log.warn("AES初始向量必须为128bit,输入的密钥为{}bit,已启用随机向量{}", bytes.length * 8, this.iv); diff --git a/src/main/java/io/github/talelin/latticy/common/constant/IdentityConstant.java b/src/main/java/io/github/talelin/latticy/common/constant/IdentityConstant.java index 83308f43..59728f9c 100644 --- a/src/main/java/io/github/talelin/latticy/common/constant/IdentityConstant.java +++ b/src/main/java/io/github/talelin/latticy/common/constant/IdentityConstant.java @@ -12,4 +12,8 @@ public class IdentityConstant { */ public static final String USERNAME_PASSWORD_IDENTITY = "USERNAME_PASSWORD"; + private IdentityConstant() { + throw new IllegalStateException("Utility class"); + } + } diff --git a/src/main/java/io/github/talelin/latticy/common/enumeration/GroupLevelEnum.java b/src/main/java/io/github/talelin/latticy/common/enumeration/GroupLevelEnum.java index 421b206f..76592243 100644 --- a/src/main/java/io/github/talelin/latticy/common/enumeration/GroupLevelEnum.java +++ b/src/main/java/io/github/talelin/latticy/common/enumeration/GroupLevelEnum.java @@ -6,6 +6,8 @@ /** * @author colorful@TaleLin * @author Juzi@TaleLin + * + * 分组级别枚举类 */ public enum GroupLevelEnum implements IEnum { /** diff --git a/src/main/java/io/github/talelin/latticy/common/exception/RestExceptionHandler.java b/src/main/java/io/github/talelin/latticy/common/exception/RestExceptionHandler.java index 518e26d1..429e5a40 100644 --- a/src/main/java/io/github/talelin/latticy/common/exception/RestExceptionHandler.java +++ b/src/main/java/io/github/talelin/latticy/common/exception/RestExceptionHandler.java @@ -36,6 +36,8 @@ * @author pedro@TaleLin * @author colorful@TaleLin * @author Juzi@TaleLin + * + * Restful 接口异常配置类 */ @Order @RestControllerAdvice diff --git a/src/main/java/io/github/talelin/latticy/common/factory/YamlPropertySourceFactory.java b/src/main/java/io/github/talelin/latticy/common/factory/YamlPropertySourceFactory.java index 650c0c3c..456acbbd 100644 --- a/src/main/java/io/github/talelin/latticy/common/factory/YamlPropertySourceFactory.java +++ b/src/main/java/io/github/talelin/latticy/common/factory/YamlPropertySourceFactory.java @@ -10,6 +10,7 @@ /** * @author Juzi@TaleLin + * YAML配置预加载 */ public class YamlPropertySourceFactory implements PropertySourceFactory { @Override diff --git a/src/main/java/io/github/talelin/latticy/common/interceptor/AuthorizeVerifyResolverImpl.java b/src/main/java/io/github/talelin/latticy/common/interceptor/AuthorizeVerifyResolverImpl.java index cbc5f7d3..6482170e 100644 --- a/src/main/java/io/github/talelin/latticy/common/interceptor/AuthorizeVerifyResolverImpl.java +++ b/src/main/java/io/github/talelin/latticy/common/interceptor/AuthorizeVerifyResolverImpl.java @@ -32,6 +32,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 鉴权实现类 */ @Component public class AuthorizeVerifyResolverImpl implements AuthorizeVerifyResolver { diff --git a/src/main/java/io/github/talelin/latticy/common/interceptor/LoggerImpl.java b/src/main/java/io/github/talelin/latticy/common/interceptor/LoggerImpl.java index a720d976..56a64f1f 100644 --- a/src/main/java/io/github/talelin/latticy/common/interceptor/LoggerImpl.java +++ b/src/main/java/io/github/talelin/latticy/common/interceptor/LoggerImpl.java @@ -21,6 +21,7 @@ * @author pedro@TaleLin * @author Juzi@TaleLin * @author colorful@TaleLin + * 行为日志实现类 */ @Slf4j @Component diff --git a/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java b/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java index 7c85d1bf..8fbc18dd 100644 --- a/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java +++ b/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java @@ -10,6 +10,7 @@ /** * @author pedro@TaleLin * @author colorful@TaleLin + * 请求日志拦截器 */ @Slf4j public class RequestLogInterceptor implements AsyncHandlerInterceptor { diff --git a/src/main/java/io/github/talelin/latticy/common/listener/PermissionHandleListener.java b/src/main/java/io/github/talelin/latticy/common/listener/PermissionHandleListener.java index cdd47729..b02c4abe 100644 --- a/src/main/java/io/github/talelin/latticy/common/listener/PermissionHandleListener.java +++ b/src/main/java/io/github/talelin/latticy/common/listener/PermissionHandleListener.java @@ -16,6 +16,7 @@ /** * @author pedro@TaleLin * @author colorful@TaleLin + * 权限监听器 */ @Component public class PermissionHandleListener implements ApplicationListener { diff --git a/src/main/java/io/github/talelin/latticy/common/mybatis/Page.java b/src/main/java/io/github/talelin/latticy/common/mybatis/Page.java index f65e1bfc..221ae72f 100644 --- a/src/main/java/io/github/talelin/latticy/common/mybatis/Page.java +++ b/src/main/java/io/github/talelin/latticy/common/mybatis/Page.java @@ -1,6 +1,7 @@ package io.github.talelin.latticy.common.mybatis; /** + * 分页对象 * 为和其他端保持一致 * 重写 MyBatis-Plus 分页对象,将起始页从 1 改为 0 * diff --git a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java index 57d47fb3..d2bd33f1 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java @@ -23,6 +23,7 @@ /** * @author Gadfly + * 验证码工具类 */ @SuppressWarnings("SpellCheckingInspection") public class CaptchaUtil { diff --git a/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java b/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java index 70c5633d..187b2d24 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java @@ -7,6 +7,7 @@ import javax.servlet.http.HttpServletRequest; /** + * @author Gadfy * IP工具类 */ @Slf4j diff --git a/src/main/java/io/github/talelin/latticy/common/util/PageUtil.java b/src/main/java/io/github/talelin/latticy/common/util/PageUtil.java index 96c44e9e..cf53444e 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/PageUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/PageUtil.java @@ -7,16 +7,17 @@ /** * @author colorful@TaleLin + * 分页工具类 */ public class PageUtil { public static PageResponseVO build(IPage iPage) { - return new PageResponseVO(Math.toIntExact(iPage.getTotal()), iPage.getRecords(), + return new PageResponseVO<>(Math.toIntExact(iPage.getTotal()), iPage.getRecords(), Math.toIntExact(iPage.getCurrent()), Math.toIntExact(iPage.getSize())); } public static PageResponseVO build(IPage iPage, List records) { - return new PageResponseVO(Math.toIntExact(iPage.getTotal()), records, + return new PageResponseVO<>(Math.toIntExact(iPage.getTotal()), records, Math.toIntExact(iPage.getCurrent()), Math.toIntExact(iPage.getSize())); } diff --git a/src/main/java/io/github/talelin/latticy/common/util/ResponseUtil.java b/src/main/java/io/github/talelin/latticy/common/util/ResponseUtil.java index caf81ff9..2c4ddeac 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/ResponseUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/ResponseUtil.java @@ -2,18 +2,16 @@ import io.github.talelin.autoconfigure.bean.Code; import io.github.talelin.autoconfigure.util.RequestUtil; -import io.github.talelin.latticy.vo.PageResponseVO; import io.github.talelin.latticy.vo.UnifyResponseVO; import lombok.extern.slf4j.Slf4j; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletResponse; -import java.util.List; /** - * 响应结果生成工具 + * 响应结果生成工具类 * @author pedro@TaleLin */ @SuppressWarnings("unchecked") @@ -88,7 +86,4 @@ public static UnifyResponseVO generateUnifyResponse(int code) { .build(); } - public static PageResponseVO generatePageResult(int total, List items, int page, int count) { - return new PageResponseVO(total, items, page, count); - } } diff --git a/src/main/java/io/github/talelin/latticy/controller/v1/BookController.java b/src/main/java/io/github/talelin/latticy/controller/v1/BookController.java index 7298ede9..4af69a2d 100644 --- a/src/main/java/io/github/talelin/latticy/controller/v1/BookController.java +++ b/src/main/java/io/github/talelin/latticy/controller/v1/BookController.java @@ -47,15 +47,13 @@ public BookDO getBook(@PathVariable(value = "id") @Positive(message = "{id.posit @GetMapping("") public List getBooks() { - List books = bookService.findAll(); - return books; + return bookService.findAll(); } @GetMapping("/search") public List searchBook(@RequestParam(value = "q", required = false, defaultValue = "") String q) { - List books = bookService.getBookByKeyword("%" + q + "%"); - return books; + return bookService.getBookByKeyword("%" + q + "%"); } diff --git a/src/main/java/io/github/talelin/latticy/dto/admin/DispatchPermissionDTO.java b/src/main/java/io/github/talelin/latticy/dto/admin/DispatchPermissionDTO.java index 67dafd9f..ac2bd4c5 100644 --- a/src/main/java/io/github/talelin/latticy/dto/admin/DispatchPermissionDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/admin/DispatchPermissionDTO.java @@ -8,6 +8,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 分配权限(单个)数据传输对象 */ @Data public class DispatchPermissionDTO { diff --git a/src/main/java/io/github/talelin/latticy/dto/admin/DispatchPermissionsDTO.java b/src/main/java/io/github/talelin/latticy/dto/admin/DispatchPermissionsDTO.java index 7fce2396..b5701bd3 100644 --- a/src/main/java/io/github/talelin/latticy/dto/admin/DispatchPermissionsDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/admin/DispatchPermissionsDTO.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 分配权限(多个)数据传输对象 */ @Data public class DispatchPermissionsDTO { diff --git a/src/main/java/io/github/talelin/latticy/dto/admin/NewGroupDTO.java b/src/main/java/io/github/talelin/latticy/dto/admin/NewGroupDTO.java index 2e8159d3..7ef070d9 100644 --- a/src/main/java/io/github/talelin/latticy/dto/admin/NewGroupDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/admin/NewGroupDTO.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 分组新增数据传输对象 */ @Data public class NewGroupDTO { diff --git a/src/main/java/io/github/talelin/latticy/dto/admin/QueryUsersDTO.java b/src/main/java/io/github/talelin/latticy/dto/admin/QueryUsersDTO.java index eed6fc49..a38f932c 100644 --- a/src/main/java/io/github/talelin/latticy/dto/admin/QueryUsersDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/admin/QueryUsersDTO.java @@ -9,6 +9,7 @@ /** * @author Gadfly * @since 2021-06-28 18:48 + * 用户查询数据传输对象 */ @Data @EqualsAndHashCode(callSuper = true) diff --git a/src/main/java/io/github/talelin/latticy/dto/admin/RemovePermissionsDTO.java b/src/main/java/io/github/talelin/latticy/dto/admin/RemovePermissionsDTO.java index 4cfdbcb1..df34c973 100644 --- a/src/main/java/io/github/talelin/latticy/dto/admin/RemovePermissionsDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/admin/RemovePermissionsDTO.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 删除权限数据传输对象 */ @Data public class RemovePermissionsDTO { diff --git a/src/main/java/io/github/talelin/latticy/dto/admin/ResetPasswordDTO.java b/src/main/java/io/github/talelin/latticy/dto/admin/ResetPasswordDTO.java index b87c635f..b987001c 100644 --- a/src/main/java/io/github/talelin/latticy/dto/admin/ResetPasswordDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/admin/ResetPasswordDTO.java @@ -8,6 +8,7 @@ /** * @author pedro@TaleLin + * 重置密码数据传输对象 */ @EqualField(srcField = "newPassword", dstField = "confirmPassword", message = "{password.equal-field}") @Data diff --git a/src/main/java/io/github/talelin/latticy/dto/admin/UpdateGroupDTO.java b/src/main/java/io/github/talelin/latticy/dto/admin/UpdateGroupDTO.java index facf4204..c0a78efe 100644 --- a/src/main/java/io/github/talelin/latticy/dto/admin/UpdateGroupDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/admin/UpdateGroupDTO.java @@ -7,6 +7,7 @@ /** * @author pedro@TaleLin + * 分组更新数据传输对象 */ @Data public class UpdateGroupDTO { diff --git a/src/main/java/io/github/talelin/latticy/dto/admin/UpdateUserInfoDTO.java b/src/main/java/io/github/talelin/latticy/dto/admin/UpdateUserInfoDTO.java index 722f404b..49c8c4f6 100644 --- a/src/main/java/io/github/talelin/latticy/dto/admin/UpdateUserInfoDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/admin/UpdateUserInfoDTO.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 用户信息更新数据传输对象 */ @Data public class UpdateUserInfoDTO { diff --git a/src/main/java/io/github/talelin/latticy/dto/book/CreateOrUpdateBookDTO.java b/src/main/java/io/github/talelin/latticy/dto/book/CreateOrUpdateBookDTO.java index b034660f..bba1c825 100644 --- a/src/main/java/io/github/talelin/latticy/dto/book/CreateOrUpdateBookDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/book/CreateOrUpdateBookDTO.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 图书创建/更新数据传输对象 */ @Data @NoArgsConstructor diff --git a/src/main/java/io/github/talelin/latticy/dto/log/QueryLogDTO.java b/src/main/java/io/github/talelin/latticy/dto/log/QueryLogDTO.java index 123790e9..686aab2d 100644 --- a/src/main/java/io/github/talelin/latticy/dto/log/QueryLogDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/log/QueryLogDTO.java @@ -9,6 +9,7 @@ /** * @author colorful@TaleLin + * 日志查询数据传输对象 */ @Data @EqualsAndHashCode(callSuper = true) diff --git a/src/main/java/io/github/talelin/latticy/dto/query/BasePageDTO.java b/src/main/java/io/github/talelin/latticy/dto/query/BasePageDTO.java index d8b0cc8f..496ddbe1 100644 --- a/src/main/java/io/github/talelin/latticy/dto/query/BasePageDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/query/BasePageDTO.java @@ -7,8 +7,8 @@ /** * @author Gadfly + * 基础分页数据传输对象 */ - @Data public class BasePageDTO { diff --git a/src/main/java/io/github/talelin/latticy/dto/user/ChangePasswordDTO.java b/src/main/java/io/github/talelin/latticy/dto/user/ChangePasswordDTO.java index e71f3dc4..de958d4c 100644 --- a/src/main/java/io/github/talelin/latticy/dto/user/ChangePasswordDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/user/ChangePasswordDTO.java @@ -10,6 +10,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 密码修改数据传输对象 */ @Data @NoArgsConstructor diff --git a/src/main/java/io/github/talelin/latticy/dto/user/LoginDTO.java b/src/main/java/io/github/talelin/latticy/dto/user/LoginDTO.java index 3239f974..eda96440 100644 --- a/src/main/java/io/github/talelin/latticy/dto/user/LoginDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/user/LoginDTO.java @@ -8,6 +8,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 登录数据传输对象 */ @Data @NoArgsConstructor diff --git a/src/main/java/io/github/talelin/latticy/dto/user/RegisterDTO.java b/src/main/java/io/github/talelin/latticy/dto/user/RegisterDTO.java index e6f70339..d7e836f6 100644 --- a/src/main/java/io/github/talelin/latticy/dto/user/RegisterDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/user/RegisterDTO.java @@ -13,6 +13,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 注册数据传输对象 */ @Data @NoArgsConstructor diff --git a/src/main/java/io/github/talelin/latticy/dto/user/UpdateInfoDTO.java b/src/main/java/io/github/talelin/latticy/dto/user/UpdateInfoDTO.java index 6d71a446..1ffa27c7 100644 --- a/src/main/java/io/github/talelin/latticy/dto/user/UpdateInfoDTO.java +++ b/src/main/java/io/github/talelin/latticy/dto/user/UpdateInfoDTO.java @@ -7,6 +7,7 @@ /** * @author pedro@TaleLin + * 更新用户信息数据传输对象 */ @NoArgsConstructor @Data diff --git a/src/main/java/io/github/talelin/latticy/extension/file/QiniuUploader.java b/src/main/java/io/github/talelin/latticy/extension/file/QiniuUploader.java index 7a8ad736..d611eba8 100644 --- a/src/main/java/io/github/talelin/latticy/extension/file/QiniuUploader.java +++ b/src/main/java/io/github/talelin/latticy/extension/file/QiniuUploader.java @@ -16,7 +16,7 @@ import java.io.ByteArrayInputStream; /** - * 文件上传服务实现,上传到七牛 + * 文件上传服务实现,上传到七牛云 * * @author pedro@TaleLin * @author colorful@TaleLin diff --git a/src/main/java/io/github/talelin/latticy/mapper/BookMapper.java b/src/main/java/io/github/talelin/latticy/mapper/BookMapper.java index b6422be4..ce4b906e 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/BookMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/BookMapper.java @@ -9,11 +9,22 @@ /** * @author pedro@TaleLin + * 图书mapper接口 */ @Repository public interface BookMapper extends BaseMapper { + /** + * 根据标题模糊查询图书列表 + * @param q 查询关键字 + * @return 图书数据对象列表 + */ List selectByTitleLikeKeyword(@Param("q") String q); + /** + * 根据标题查询图书列表 + * @param title 图书标题 + * @return 图书数据对象列表 + */ List selectByTitle(@Param("title") String title); } diff --git a/src/main/java/io/github/talelin/latticy/mapper/FileMapper.java b/src/main/java/io/github/talelin/latticy/mapper/FileMapper.java index 609620d4..7ae7d54f 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/FileMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/FileMapper.java @@ -7,11 +7,22 @@ /** * @author pedro@TaleLin + * 文件mapper接口 */ @Repository public interface FileMapper extends BaseMapper { + /** + * 根据文件md5查询文件对象 + * @param md5 文件md5 + * @return 文件数据对象 + */ FileDO selectByMd5(@Param("md5") String md5); + /** + * 根据文件md5查询文件数量 + * @param md5 文件md5 + * @return 文件数据传输对象 + */ int selectCountByMd5(@Param("md5") String md5); } diff --git a/src/main/java/io/github/talelin/latticy/mapper/GroupMapper.java b/src/main/java/io/github/talelin/latticy/mapper/GroupMapper.java index 7bfe04cd..f87ed83f 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/GroupMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/GroupMapper.java @@ -10,6 +10,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 分组mapper接口 */ @Repository public interface GroupMapper extends BaseMapper { @@ -43,6 +44,7 @@ public interface GroupMapper extends BaseMapper { * * @param userId 用户id * @param groupName 分组名 + * @return 数量 */ int selectCountUserByUserIdAndGroupName(@Param("userId") Integer userId, @Param("groupName") String groupName); } diff --git a/src/main/java/io/github/talelin/latticy/mapper/GroupPermissionMapper.java b/src/main/java/io/github/talelin/latticy/mapper/GroupPermissionMapper.java index 3c0e5179..090a5ae9 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/GroupPermissionMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/GroupPermissionMapper.java @@ -10,11 +10,23 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 分组权限mapper接口 */ @Repository public interface GroupPermissionMapper extends BaseMapper { + /** + * 批量新增 + * @param relations 分组权限关系集合 + * @return last insert id + */ int insertBatch(@Param("relations") List relations); + /** + * 根据分组id和权限id集合批量删除分组权限关系 + * @param groupId 分组id + * @param permissionIds 权限id集合 + * @return 受影响行数 + */ int deleteBatchByGroupIdAndPermissionId(@Param("groupId") Integer groupId, @Param("permissionIds") List permissionIds); } diff --git a/src/main/java/io/github/talelin/latticy/mapper/LogMapper.java b/src/main/java/io/github/talelin/latticy/mapper/LogMapper.java index 70e34652..c8a9976e 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/LogMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/LogMapper.java @@ -1,22 +1,49 @@ package io.github.talelin.latticy.mapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import io.github.talelin.latticy.common.mybatis.Page; import io.github.talelin.latticy.model.LogDO; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.springframework.stereotype.Repository; import java.util.Date; /** * @author pedro@TaleLin + * 日志mapper接口 */ @Repository public interface LogMapper extends BaseMapper { + /** + * 查询日志 + * + * @param pager 分页对象 + * @param name 用户名 + * @param start 开始日期 + * @param end 结束日期 + * @return 日志分页对象 + */ IPage findLogsByUsernameAndRange(Page pager, String name, Date start, Date end); + /** + * 查询日志 + * + * @param pager 分页对象 + * @param name 用户名 + * @param keyword 查询关键字 + * @param start 开始日期 + * @param end 结束日期 + * @return 日志分页对象 + */ IPage searchLogsByUsernameAndKeywordAndRange(Page pager, String name, String keyword, Date start, Date end); + /** + * 查询用户名分页列表 + * + * @param pager Page + * @return 用户名分页对象 + */ IPage getUserNames(Page pager); + } diff --git a/src/main/java/io/github/talelin/latticy/mapper/PermissionMapper.java b/src/main/java/io/github/talelin/latticy/mapper/PermissionMapper.java index 0c37c4a4..084da597 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/PermissionMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/PermissionMapper.java @@ -10,6 +10,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 权限mapper接口 */ @Repository public interface PermissionMapper extends BaseMapper { diff --git a/src/main/java/io/github/talelin/latticy/mapper/UserGroupMapper.java b/src/main/java/io/github/talelin/latticy/mapper/UserGroupMapper.java index 39e3e14e..d7b26598 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/UserGroupMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/UserGroupMapper.java @@ -9,11 +9,22 @@ /** * @author pedro@TaleLin + * 用户分组mapper */ @Repository public interface UserGroupMapper extends BaseMapper { + /** + * 批量新增 + * @param relations 用户分组数据对象集合 + * @return last insert id + */ int insertBatch(@Param("relations") List relations); + /** + * 根据用户id删除 + * @param userId 用户id + * @return 受影响行数 + */ int deleteByUserId(@Param("user_id") Integer userId); } diff --git a/src/main/java/io/github/talelin/latticy/mapper/UserIdentityMapper.java b/src/main/java/io/github/talelin/latticy/mapper/UserIdentityMapper.java index efad881d..2beba56d 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/UserIdentityMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/UserIdentityMapper.java @@ -6,6 +6,7 @@ /** * @author pedro@TaleLin + * 用户身份标识mapper接口 */ @Repository public interface UserIdentityMapper extends BaseMapper { diff --git a/src/main/java/io/github/talelin/latticy/mapper/UserMapper.java b/src/main/java/io/github/talelin/latticy/mapper/UserMapper.java index 0455ecdc..fa6f7469 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/UserMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/UserMapper.java @@ -11,6 +11,7 @@ * @author pedro@TaleLin * @author colorful@TaleLin * @author Juzi@TaleLin + * 用户mapper接口 */ @Repository public interface UserMapper extends BaseMapper { @@ -39,5 +40,5 @@ public interface UserMapper extends BaseMapper { * @param rootGroupId 超级用户组id(不返回超级用户组的用户) * @return 分页数据 */ - IPage selectPageByGroupId(Page pager, Integer groupId, Integer rootGroupId); + IPage selectPageByGroupId(Page pager, Integer groupId, Integer rootGroupId); } diff --git a/src/main/java/io/github/talelin/latticy/model/BaseModel.java b/src/main/java/io/github/talelin/latticy/model/BaseModel.java index d92e147f..e2d45dc5 100644 --- a/src/main/java/io/github/talelin/latticy/model/BaseModel.java +++ b/src/main/java/io/github/talelin/latticy/model/BaseModel.java @@ -10,6 +10,7 @@ /** * @author Juzi@TaleLin + * 基础数据模型类 */ @Data public class BaseModel { diff --git a/src/main/java/io/github/talelin/latticy/model/BookDO.java b/src/main/java/io/github/talelin/latticy/model/BookDO.java index e2023d1a..1a4ab3d8 100644 --- a/src/main/java/io/github/talelin/latticy/model/BookDO.java +++ b/src/main/java/io/github/talelin/latticy/model/BookDO.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 图书数据对象 */ @Data @TableName("book") diff --git a/src/main/java/io/github/talelin/latticy/model/FileDO.java b/src/main/java/io/github/talelin/latticy/model/FileDO.java index b93795c7..5bf6508f 100644 --- a/src/main/java/io/github/talelin/latticy/model/FileDO.java +++ b/src/main/java/io/github/talelin/latticy/model/FileDO.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 文件数据对象 */ @Data @TableName("lin_file") diff --git a/src/main/java/io/github/talelin/latticy/model/GroupDO.java b/src/main/java/io/github/talelin/latticy/model/GroupDO.java index db6820c9..071b1aa4 100644 --- a/src/main/java/io/github/talelin/latticy/model/GroupDO.java +++ b/src/main/java/io/github/talelin/latticy/model/GroupDO.java @@ -11,6 +11,7 @@ * @author pedro@TaleLin * @author Juzi@TaleLin * @author Jokky@TaleLin + * 分组数据对象 */ @Data @Builder diff --git a/src/main/java/io/github/talelin/latticy/model/GroupPermissionDO.java b/src/main/java/io/github/talelin/latticy/model/GroupPermissionDO.java index 448446a6..b2f26c84 100644 --- a/src/main/java/io/github/talelin/latticy/model/GroupPermissionDO.java +++ b/src/main/java/io/github/talelin/latticy/model/GroupPermissionDO.java @@ -10,6 +10,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 分组权限数据对象 */ @Data @TableName("lin_group_permission") diff --git a/src/main/java/io/github/talelin/latticy/model/LogDO.java b/src/main/java/io/github/talelin/latticy/model/LogDO.java index b2224b09..a8a1f89c 100644 --- a/src/main/java/io/github/talelin/latticy/model/LogDO.java +++ b/src/main/java/io/github/talelin/latticy/model/LogDO.java @@ -12,6 +12,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 日志数据对象 */ @Data @Builder diff --git a/src/main/java/io/github/talelin/latticy/model/PermissionDO.java b/src/main/java/io/github/talelin/latticy/model/PermissionDO.java index 21646165..2580e8c0 100644 --- a/src/main/java/io/github/talelin/latticy/model/PermissionDO.java +++ b/src/main/java/io/github/talelin/latticy/model/PermissionDO.java @@ -8,6 +8,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 权限数据对象 */ @Data @Builder diff --git a/src/main/java/io/github/talelin/latticy/model/UserDO.java b/src/main/java/io/github/talelin/latticy/model/UserDO.java index 1d7c0f1e..e9d03620 100644 --- a/src/main/java/io/github/talelin/latticy/model/UserDO.java +++ b/src/main/java/io/github/talelin/latticy/model/UserDO.java @@ -12,6 +12,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 用户数据对象 */ @Data @Builder diff --git a/src/main/java/io/github/talelin/latticy/model/UserGroupDO.java b/src/main/java/io/github/talelin/latticy/model/UserGroupDO.java index efad1320..fd9d20a8 100644 --- a/src/main/java/io/github/talelin/latticy/model/UserGroupDO.java +++ b/src/main/java/io/github/talelin/latticy/model/UserGroupDO.java @@ -10,6 +10,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 用户分组数据对象 */ @Data @TableName("lin_user_group") diff --git a/src/main/java/io/github/talelin/latticy/model/UserIdentityDO.java b/src/main/java/io/github/talelin/latticy/model/UserIdentityDO.java index 7199dd27..aeb52681 100644 --- a/src/main/java/io/github/talelin/latticy/model/UserIdentityDO.java +++ b/src/main/java/io/github/talelin/latticy/model/UserIdentityDO.java @@ -1,22 +1,16 @@ package io.github.talelin.latticy.model; import com.baomidou.mybatisplus.annotation.TableName; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; +import lombok.*; import java.io.Serializable; /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 用户鉴权标识数据对象 */ -@Setter -@Getter -@ToString +@Data @Builder @NoArgsConstructor @AllArgsConstructor diff --git a/src/main/java/io/github/talelin/latticy/module/file/File.java b/src/main/java/io/github/talelin/latticy/module/file/File.java index 999e681f..eb299918 100644 --- a/src/main/java/io/github/talelin/latticy/module/file/File.java +++ b/src/main/java/io/github/talelin/latticy/module/file/File.java @@ -4,6 +4,7 @@ /** * @author pedro@TaleLin + * 文件对象 */ @Setter @Getter diff --git a/src/main/java/io/github/talelin/latticy/module/file/FileConstant.java b/src/main/java/io/github/talelin/latticy/module/file/FileConstant.java index 5eaf13ad..a0ba21d4 100644 --- a/src/main/java/io/github/talelin/latticy/module/file/FileConstant.java +++ b/src/main/java/io/github/talelin/latticy/module/file/FileConstant.java @@ -5,15 +5,15 @@ * * @author pedro@TaleLin */ -public class FileConstant { +public interface FileConstant { /** * 本地文件 */ - public static final String LOCAL = "LOCAL"; + String LOCAL = "LOCAL"; /** * 远程文件,例如OSS */ - public static final String REMOTE = "REMOTE"; + String REMOTE = "REMOTE"; } diff --git a/src/main/java/io/github/talelin/latticy/module/file/FileProperties.java b/src/main/java/io/github/talelin/latticy/module/file/FileProperties.java index 98b02ccc..2e62398d 100644 --- a/src/main/java/io/github/talelin/latticy/module/file/FileProperties.java +++ b/src/main/java/io/github/talelin/latticy/module/file/FileProperties.java @@ -7,6 +7,7 @@ /** * @author pedro@TaleLin + * 文件配置类 */ @Component @ConfigurationProperties("lin.file") diff --git a/src/main/java/io/github/talelin/latticy/module/file/FileUtil.java b/src/main/java/io/github/talelin/latticy/module/file/FileUtil.java index be14b144..37bdea87 100644 --- a/src/main/java/io/github/talelin/latticy/module/file/FileUtil.java +++ b/src/main/java/io/github/talelin/latticy/module/file/FileUtil.java @@ -11,9 +11,12 @@ /** * @author pedro@TaleLin * @author colorful@TaleLin + * 文件工具类 */ public class FileUtil { + private FileUtil() {} + public static FileSystem getDefaultFileSystem() { return FileSystems.getDefault(); } diff --git a/src/main/java/io/github/talelin/latticy/module/file/UploadHandler.java b/src/main/java/io/github/talelin/latticy/module/file/UploadHandler.java index e5886a67..8f1791ad 100644 --- a/src/main/java/io/github/talelin/latticy/module/file/UploadHandler.java +++ b/src/main/java/io/github/talelin/latticy/module/file/UploadHandler.java @@ -10,12 +10,15 @@ public interface UploadHandler { /** * 在文件写入本地或者上传到云之前调用此方法 * + * @param file 文件对象 * @return 是否上传,若返回false,则不上传 */ boolean preHandle(File file); /** * 在文件写入本地或者上传到云之后调用此方法 + * + * @param file 文件对象 */ void afterHandle(File file); } diff --git a/src/main/java/io/github/talelin/latticy/module/log/MDCAccessConstant.java b/src/main/java/io/github/talelin/latticy/module/log/MDCAccessConstant.java index e897fdc7..e70d719b 100644 --- a/src/main/java/io/github/talelin/latticy/module/log/MDCAccessConstant.java +++ b/src/main/java/io/github/talelin/latticy/module/log/MDCAccessConstant.java @@ -5,6 +5,9 @@ * @date 2020/6/20 10:22 */ public class MDCAccessConstant { + + private MDCAccessConstant() {} + public static final String REQUEST_METHOD_MDC_KEY = "req.method"; public static final String RESPONSE_STATUS_MDC_KEY = "res.status"; public static final String REQUEST_REFERER_MDC_KEY = "req.referer"; diff --git a/src/main/java/io/github/talelin/latticy/module/message/MessageConstant.java b/src/main/java/io/github/talelin/latticy/module/message/MessageConstant.java index a0264d9d..54805e9b 100644 --- a/src/main/java/io/github/talelin/latticy/module/message/MessageConstant.java +++ b/src/main/java/io/github/talelin/latticy/module/message/MessageConstant.java @@ -6,5 +6,8 @@ * @author pedro@TaleLin */ public class MessageConstant { + + private MessageConstant() {} + public static final String USER_KEY = "user"; } diff --git a/src/main/java/io/github/talelin/latticy/module/message/MessageWebSocketHandler.java b/src/main/java/io/github/talelin/latticy/module/message/MessageWebSocketHandler.java index e3b91715..5e89318e 100644 --- a/src/main/java/io/github/talelin/latticy/module/message/MessageWebSocketHandler.java +++ b/src/main/java/io/github/talelin/latticy/module/message/MessageWebSocketHandler.java @@ -5,6 +5,7 @@ /** * @author pedro@TaleLin + * websocket 消息实现类 */ public class MessageWebSocketHandler implements WebSocketHandler { diff --git a/src/main/java/io/github/talelin/latticy/module/message/WebSocketInterceptor.java b/src/main/java/io/github/talelin/latticy/module/message/WebSocketInterceptor.java index b106e23e..0c2b8341 100644 --- a/src/main/java/io/github/talelin/latticy/module/message/WebSocketInterceptor.java +++ b/src/main/java/io/github/talelin/latticy/module/message/WebSocketInterceptor.java @@ -27,6 +27,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * websocket 拦截器 */ public class WebSocketInterceptor implements HandshakeInterceptor { @Autowired @@ -75,7 +76,7 @@ public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse res @Override public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) { - + // default implementation ignored } private boolean verifyAdmin(UserDO user) { diff --git a/src/main/java/io/github/talelin/latticy/module/message/WebsocketConfiguration.java b/src/main/java/io/github/talelin/latticy/module/message/WebsocketConfiguration.java index 0965ba4c..d4ce12e6 100644 --- a/src/main/java/io/github/talelin/latticy/module/message/WebsocketConfiguration.java +++ b/src/main/java/io/github/talelin/latticy/module/message/WebsocketConfiguration.java @@ -10,6 +10,7 @@ /** * @author pedro@TaleLin + * websocket 配置类 */ @Configuration @ConditionalOnProperty(prefix = "lin.cms.websocket", value = "enable", havingValue = "true") diff --git a/src/main/java/io/github/talelin/latticy/module/message/WsHandler.java b/src/main/java/io/github/talelin/latticy/module/message/WsHandler.java index 24f2c0aa..c81636dc 100644 --- a/src/main/java/io/github/talelin/latticy/module/message/WsHandler.java +++ b/src/main/java/io/github/talelin/latticy/module/message/WsHandler.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * websocket 接口 */ public interface WsHandler { diff --git a/src/main/java/io/github/talelin/latticy/module/message/WsHandlerImpl.java b/src/main/java/io/github/talelin/latticy/module/message/WsHandlerImpl.java index 55cdfab6..686eff66 100644 --- a/src/main/java/io/github/talelin/latticy/module/message/WsHandlerImpl.java +++ b/src/main/java/io/github/talelin/latticy/module/message/WsHandlerImpl.java @@ -17,6 +17,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * websocket实现类 */ @Slf4j public class WsHandlerImpl implements WsHandler { diff --git a/src/main/java/io/github/talelin/latticy/service/AdminService.java b/src/main/java/io/github/talelin/latticy/service/AdminService.java index 7f725d91..f1aeede8 100644 --- a/src/main/java/io/github/talelin/latticy/service/AdminService.java +++ b/src/main/java/io/github/talelin/latticy/service/AdminService.java @@ -19,6 +19,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 管理员服务接口 */ public interface AdminService { diff --git a/src/main/java/io/github/talelin/latticy/service/BookService.java b/src/main/java/io/github/talelin/latticy/service/BookService.java index 89f45302..d9d5e3a2 100644 --- a/src/main/java/io/github/talelin/latticy/service/BookService.java +++ b/src/main/java/io/github/talelin/latticy/service/BookService.java @@ -8,18 +8,49 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 图书服务接口 */ public interface BookService { + /** + * 新增图书 + * @param validator 数据传输对象 + * @return 是否成功 + */ boolean createBook(CreateOrUpdateBookDTO validator); + /** + * 根据关键字获取图书集合 + * @param q 查询关键字 + * @return BookDO List + */ List getBookByKeyword(String q); + /** + * 更新图书 + * @param book 图书对象 + * @param validator 数据传输对象 + * @return 是否更新成功 + */ boolean updateBook(BookDO book, CreateOrUpdateBookDTO validator); + /** + * 获取图书 + * @param id 主键id + * @return 图书数据对象 + */ BookDO getById(Integer id); + /** + * 查询所有图书 + * @return 图书数据对象集合 + */ List findAll(); + /** + * 删除图书 + * @param id 主键id + * @return 是否删除成功 + */ boolean deleteById(Integer id); } diff --git a/src/main/java/io/github/talelin/latticy/service/FileService.java b/src/main/java/io/github/talelin/latticy/service/FileService.java index 37a36d54..18fb75b8 100644 --- a/src/main/java/io/github/talelin/latticy/service/FileService.java +++ b/src/main/java/io/github/talelin/latticy/service/FileService.java @@ -10,6 +10,7 @@ /** * @author pedro@TaleLin + * 文件服务接口 */ public interface FileService extends IService { diff --git a/src/main/java/io/github/talelin/latticy/service/GroupService.java b/src/main/java/io/github/talelin/latticy/service/GroupService.java index 2b68d1f6..58c2f3a9 100644 --- a/src/main/java/io/github/talelin/latticy/service/GroupService.java +++ b/src/main/java/io/github/talelin/latticy/service/GroupService.java @@ -11,6 +11,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 分组服务接口 */ public interface GroupService extends IService { @@ -110,4 +111,5 @@ public interface GroupService extends IService { * @return 用户组id */ Integer getParticularGroupIdByLevel(GroupLevelEnum level); + } diff --git a/src/main/java/io/github/talelin/latticy/service/LogService.java b/src/main/java/io/github/talelin/latticy/service/LogService.java index 83c12a18..977025ed 100644 --- a/src/main/java/io/github/talelin/latticy/service/LogService.java +++ b/src/main/java/io/github/talelin/latticy/service/LogService.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 日志服务接口 */ public interface LogService extends IService { diff --git a/src/main/java/io/github/talelin/latticy/service/PermissionService.java b/src/main/java/io/github/talelin/latticy/service/PermissionService.java index cb9b2cc1..2a03a96b 100644 --- a/src/main/java/io/github/talelin/latticy/service/PermissionService.java +++ b/src/main/java/io/github/talelin/latticy/service/PermissionService.java @@ -9,6 +9,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 权限服务接口 */ public interface PermissionService extends IService { diff --git a/src/main/java/io/github/talelin/latticy/service/UserIdentityService.java b/src/main/java/io/github/talelin/latticy/service/UserIdentityService.java index 7f149438..75a50809 100644 --- a/src/main/java/io/github/talelin/latticy/service/UserIdentityService.java +++ b/src/main/java/io/github/talelin/latticy/service/UserIdentityService.java @@ -6,6 +6,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 用户身份标识服务接口 */ public interface UserIdentityService extends IService { diff --git a/src/main/java/io/github/talelin/latticy/service/UserService.java b/src/main/java/io/github/talelin/latticy/service/UserService.java index 8b28e589..06b69a65 100644 --- a/src/main/java/io/github/talelin/latticy/service/UserService.java +++ b/src/main/java/io/github/talelin/latticy/service/UserService.java @@ -15,7 +15,7 @@ import java.util.Map; /** - * 用户业务 + * 用户服务实现类 * * @author pedro@TaleLin * @author Juzi@TaleLin diff --git a/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java index 54ed2f37..b6c3579d 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java @@ -7,21 +7,11 @@ import io.github.talelin.latticy.bo.GroupPermissionBO; import io.github.talelin.latticy.common.enumeration.GroupLevelEnum; import io.github.talelin.latticy.common.mybatis.Page; -import io.github.talelin.latticy.dto.admin.DispatchPermissionDTO; -import io.github.talelin.latticy.dto.admin.DispatchPermissionsDTO; -import io.github.talelin.latticy.dto.admin.NewGroupDTO; -import io.github.talelin.latticy.dto.admin.RemovePermissionsDTO; -import io.github.talelin.latticy.dto.admin.ResetPasswordDTO; -import io.github.talelin.latticy.dto.admin.UpdateGroupDTO; -import io.github.talelin.latticy.dto.admin.UpdateUserInfoDTO; +import io.github.talelin.latticy.dto.admin.*; import io.github.talelin.latticy.mapper.GroupPermissionMapper; import io.github.talelin.latticy.mapper.UserGroupMapper; import io.github.talelin.latticy.model.*; -import io.github.talelin.latticy.service.AdminService; -import io.github.talelin.latticy.service.GroupService; -import io.github.talelin.latticy.service.PermissionService; -import io.github.talelin.latticy.service.UserIdentityService; -import io.github.talelin.latticy.service.UserService; +import io.github.talelin.latticy.service.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -36,6 +26,7 @@ * @author pedro@TaleLin * @author colorful@TaleLin * @author Juzi@TaleLin + * 管理员服务实现类 */ @Service public class AdminServiceImpl implements AdminService { @@ -80,7 +71,7 @@ public boolean changeUserPassword(Integer id, ResetPasswordDTO dto) { return userIdentityService.changePassword(id, dto.getNewPassword()); } - @Transactional + @Transactional(rollbackFor = Exception.class) @Override public boolean deleteUser(Integer id) { throwUserNotExistById(id); @@ -113,8 +104,7 @@ public boolean updateUserInfo(Integer id, UpdateUserInfoDTO validator) { @Override public IPage getGroupPage(Integer page, Integer count) { - IPage iPage = groupService.getGroupPage(page, count); - return iPage; + return groupService.getGroupPage(page, count); } @Override @@ -123,7 +113,7 @@ public GroupPermissionBO getGroup(Integer id) { return groupService.getGroupAndPermissions(id); } - @Transactional + @Transactional(rollbackFor = Exception.class) @Override public boolean createGroup(NewGroupDTO dto) { throwGroupNameExist(dto.getName()); @@ -165,7 +155,7 @@ public boolean deleteGroup(Integer id) { } throwGroupNotExistById(id); List groupUserIds = groupService.getGroupUserIds(id); - if(groupUserIds.size() > 0) { + if(!groupUserIds.isEmpty()) { throw new ForbiddenException(10027); } return groupService.removeById(id); @@ -195,8 +185,7 @@ public List getAllGroups() { QueryWrapper wrapper = new QueryWrapper<>(); Integer rootGroupId = groupService.getParticularGroupIdByLevel(GroupLevelEnum.ROOT); wrapper.lambda().ne(GroupDO::getId, rootGroupId); - List groups = groupService.list(wrapper); - return groups; + return groupService.list(wrapper); } @Override diff --git a/src/main/java/io/github/talelin/latticy/service/impl/BookServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/BookServiceImpl.java index e89c276c..c09b4dc8 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/BookServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/BookServiceImpl.java @@ -12,6 +12,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 图书服务实现类 */ @Service public class BookServiceImpl implements BookService { @@ -31,8 +32,7 @@ public boolean createBook(CreateOrUpdateBookDTO validator) { @Override public List getBookByKeyword(String q) { - List books = bookMapper.selectByTitleLikeKeyword(q); - return books; + return bookMapper.selectByTitleLikeKeyword(q); } @Override @@ -46,14 +46,12 @@ public boolean updateBook(BookDO book, CreateOrUpdateBookDTO validator) { @Override public BookDO getById(Integer id) { - BookDO book = bookMapper.selectById(id); - return book; + return bookMapper.selectById(id); } @Override public List findAll() { - List books = bookMapper.selectList(null); - return books; + return bookMapper.selectList(null); } @Override diff --git a/src/main/java/io/github/talelin/latticy/service/impl/FileServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/FileServiceImpl.java index 121c89f8..a76e9b2d 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/FileServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/FileServiceImpl.java @@ -19,6 +19,7 @@ * @author pedro@TaleLin * @author Juzi@TaleLin * @author colorful@TaleLin + * 文件服务接口实现类 */ @Service public class FileServiceImpl extends ServiceImpl implements FileService { @@ -82,7 +83,7 @@ private FileBO transformDoToBo(FileDO file, String key) { // replaceAll 是将 windows 平台下的 \ 替换为 / if(System.getProperties().getProperty("os.name").toUpperCase().contains("WINDOWS")){ bo.setUrl(fileProperties.getDomain() + s + "/" + file.getPath().replaceAll("\\\\","/")); - }else { + } else { bo.setUrl(fileProperties.getDomain() + s + "/" + file.getPath()); } } else { diff --git a/src/main/java/io/github/talelin/latticy/service/impl/GroupServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/GroupServiceImpl.java index 1b5c7070..0860bd3d 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/GroupServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/GroupServiceImpl.java @@ -24,6 +24,7 @@ * @author pedro@TaleLin * @author colorful@TaleLin * @author Juzi@TaleLin + * 分组服务实现类 */ @Service public class GroupServiceImpl extends ServiceImpl implements GroupService { @@ -122,8 +123,7 @@ public GroupDO getParticularGroupByLevel(GroupLevelEnum level) { } else { QueryWrapper wrapper = new QueryWrapper<>(); wrapper.lambda().eq(GroupDO::getLevel, level.getValue()); - GroupDO groupDO = this.baseMapper.selectOne(wrapper); - return groupDO; + return this.baseMapper.selectOne(wrapper); } } diff --git a/src/main/java/io/github/talelin/latticy/service/impl/LogServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/LogServiceImpl.java index a3f893f5..1de8aa26 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/LogServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/LogServiceImpl.java @@ -13,6 +13,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 日志服务实现类 */ @Service public class LogServiceImpl extends ServiceImpl implements LogService { @@ -20,22 +21,19 @@ public class LogServiceImpl extends ServiceImpl implements Log @Override public IPage getLogPage(Integer page, Integer count, String name, Date start, Date end) { Page pager = new Page<>(page, count); - IPage iPage = this.baseMapper.findLogsByUsernameAndRange(pager, name, start, end); - return iPage; + return this.baseMapper.findLogsByUsernameAndRange(pager, name, start, end); } @Override public IPage searchLogPage(Integer page, Integer count, String name, String keyword, Date start, Date end) { Page pager = new Page<>(page, count); - IPage iPage = this.baseMapper.searchLogsByUsernameAndKeywordAndRange(pager, name, "%" + keyword + "%", start, end); - return iPage; + return this.baseMapper.searchLogsByUsernameAndKeywordAndRange(pager, name, "%" + keyword + "%", start, end); } @Override public IPage getUserNamePage(Integer page, Integer count) { Page pager = new Page<>(page, count); - IPage iPage = this.baseMapper.getUserNames(pager); - return iPage; + return this.baseMapper.getUserNames(pager); } @Override diff --git a/src/main/java/io/github/talelin/latticy/service/impl/PermissionServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/PermissionServiceImpl.java index cdb7cc12..0655aa62 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/PermissionServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/PermissionServiceImpl.java @@ -14,6 +14,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 权限服务实现类 */ @Service public class PermissionServiceImpl extends ServiceImpl implements PermissionService { @@ -48,7 +49,7 @@ public Map> getPermissionMapByGroupIds(List gr @Override public List>>> structuringPermissions(List permissions) { - Map>> tmp = new HashMap(); + Map>> tmp = new HashMap(50); permissions.forEach(permission -> { if (!tmp.containsKey(permission.getModule())) { Map tiny = new HashMap(); diff --git a/src/main/java/io/github/talelin/latticy/service/impl/UserIdentityServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/UserIdentityServiceImpl.java index 52642df6..08d3ecce 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/UserIdentityServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/UserIdentityServiceImpl.java @@ -12,6 +12,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin + * 用户身份标识服务实现类 */ @Service public class UserIdentityServiceImpl extends ServiceImpl implements UserIdentityService { diff --git a/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java index 5db15eef..0a0cb4f5 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java @@ -43,6 +43,7 @@ * @author pedro@TaleLin * @author colorful@TaleLin * @author Juzi@TaleLin + * 用户服务实现类 */ @Service public class UserServiceImpl extends ServiceImpl implements UserService { diff --git a/src/main/java/io/github/talelin/latticy/vo/CreatedVO.java b/src/main/java/io/github/talelin/latticy/vo/CreatedVO.java index e28fe9a3..ba969cce 100644 --- a/src/main/java/io/github/talelin/latticy/vo/CreatedVO.java +++ b/src/main/java/io/github/talelin/latticy/vo/CreatedVO.java @@ -6,6 +6,7 @@ /** * @author colorful@TaleLin + * 创建成功视图对象 */ public class CreatedVO extends UnifyResponseVO { diff --git a/src/main/java/io/github/talelin/latticy/vo/DeletedVO.java b/src/main/java/io/github/talelin/latticy/vo/DeletedVO.java index 67e3a492..3a6a46fb 100644 --- a/src/main/java/io/github/talelin/latticy/vo/DeletedVO.java +++ b/src/main/java/io/github/talelin/latticy/vo/DeletedVO.java @@ -6,6 +6,7 @@ /** * @author colorful@TaleLin + * 删除成功视图对象 */ public class DeletedVO extends UnifyResponseVO { diff --git a/src/main/java/io/github/talelin/latticy/vo/LoginCaptchaVO.java b/src/main/java/io/github/talelin/latticy/vo/LoginCaptchaVO.java index 165ed2c5..9700d657 100644 --- a/src/main/java/io/github/talelin/latticy/vo/LoginCaptchaVO.java +++ b/src/main/java/io/github/talelin/latticy/vo/LoginCaptchaVO.java @@ -6,6 +6,7 @@ /** * @author Gadfly + * 登录验证码视图对象 */ @Data @NoArgsConstructor diff --git a/src/main/java/io/github/talelin/latticy/vo/PageResponseVO.java b/src/main/java/io/github/talelin/latticy/vo/PageResponseVO.java index 0796390b..09c586f8 100644 --- a/src/main/java/io/github/talelin/latticy/vo/PageResponseVO.java +++ b/src/main/java/io/github/talelin/latticy/vo/PageResponseVO.java @@ -8,7 +8,7 @@ import java.util.List; /** - * 分页数据统一 view object + * 分页数据统一视图对象 * * @author pedro@TaleLin */ diff --git a/src/main/java/io/github/talelin/latticy/vo/UnifyResponseVO.java b/src/main/java/io/github/talelin/latticy/vo/UnifyResponseVO.java index 3ca5f973..eaf5320c 100644 --- a/src/main/java/io/github/talelin/latticy/vo/UnifyResponseVO.java +++ b/src/main/java/io/github/talelin/latticy/vo/UnifyResponseVO.java @@ -10,7 +10,7 @@ /** - * 统一API响应结果封装 + * 统一API响应结果封装视图对象 * * @author pedro@TaleLin * @author colorful@TaleLin diff --git a/src/main/java/io/github/talelin/latticy/vo/UpdatedVO.java b/src/main/java/io/github/talelin/latticy/vo/UpdatedVO.java index 26aed191..633a31b6 100644 --- a/src/main/java/io/github/talelin/latticy/vo/UpdatedVO.java +++ b/src/main/java/io/github/talelin/latticy/vo/UpdatedVO.java @@ -6,6 +6,7 @@ /** * @author pedro@TaleLin + * 更新成功视图对象 */ public class UpdatedVO extends UnifyResponseVO { diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 7fb07f12..e5ff737c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -20,7 +20,7 @@ auth: # 开启登录要求验证码 login-captcha: - enabled: false + enabled: true secret: "m49CPM5ak@MDXTzbbT_ZEyMM3KBsBn!h" # 开启http请求日志记录 From e1c97cf947f36c15744783fa0bfe61cadf1bc32a Mon Sep 17 00:00:00 2001 From: Colorful Date: Tue, 29 Nov 2022 18:34:36 +0800 Subject: [PATCH 20/20] =?UTF-8?q?refactor:=20=E6=95=B4=E7=90=86=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../talelin/latticy/bo/GroupPermissionBO.java | 1 + .../talelin/latticy/bo/LoginCaptchaBO.java | 1 + .../talelin/latticy/common/LocalUser.java | 14 +++++--- .../configuration/LoginCaptchaProperties.java | 6 ++-- .../exception/RestExceptionHandler.java | 4 +-- .../AuthorizeVerifyResolverImpl.java | 16 ++++++---- .../common/interceptor/LoggerImpl.java | 3 +- .../interceptor/RequestLogInterceptor.java | 7 ++-- .../mybatis/{Page.java => LinPage.java} | 14 ++++---- .../latticy/common/util/CaptchaUtil.java | 8 +++-- .../talelin/latticy/common/util/IPUtil.java | 4 +++ .../talelin/latticy/common/util/PageUtil.java | 4 +++ .../latticy/common/util/ResponseUtil.java | 4 +++ .../controller/cms/AdminController.java | 1 + .../controller/cms/FileController.java | 1 + .../latticy/controller/cms/LogController.java | 1 + .../controller/cms/UserController.java | 10 +++--- .../latticy/controller/v1/BookController.java | 11 ++----- .../latticy/extension/file/LocalUploader.java | 13 +++----- .../latticy/extension/file/QiniuUploader.java | 4 +-- .../talelin/latticy/mapper/LogMapper.java | 8 ++--- .../talelin/latticy/mapper/UserMapper.java | 4 +-- .../latticy/module/file/FileConstant.java | 19 ----------- .../latticy/module/file/FileTypeEnum.java | 32 +++++++++++++++++++ .../latticy/module/file/UploadHandler.java | 2 +- .../latticy/module/log/MDCAccessConstant.java | 1 + .../module/message/WebSocketInterceptor.java | 2 +- .../talelin/latticy/service/UserService.java | 14 +++++--- .../service/impl/AdminServiceImpl.java | 4 +-- .../latticy/service/impl/FileServiceImpl.java | 7 ++-- .../service/impl/GroupServiceImpl.java | 4 +-- .../latticy/service/impl/LogServiceImpl.java | 14 ++++---- .../latticy/service/impl/UserServiceImpl.java | 13 +++++--- .../talelin/latticy/mapper/LogMapperTest.java | 12 +++---- .../latticy/mapper/UserMapperTest.java | 12 +++---- .../mpg/templates/premium/controller.java.ftl | 2 +- 36 files changed, 160 insertions(+), 117 deletions(-) rename src/main/java/io/github/talelin/latticy/common/mybatis/{Page.java => LinPage.java} (78%) delete mode 100644 src/main/java/io/github/talelin/latticy/module/file/FileConstant.java create mode 100644 src/main/java/io/github/talelin/latticy/module/file/FileTypeEnum.java diff --git a/src/main/java/io/github/talelin/latticy/bo/GroupPermissionBO.java b/src/main/java/io/github/talelin/latticy/bo/GroupPermissionBO.java index 99254100..fbf49225 100644 --- a/src/main/java/io/github/talelin/latticy/bo/GroupPermissionBO.java +++ b/src/main/java/io/github/talelin/latticy/bo/GroupPermissionBO.java @@ -13,6 +13,7 @@ * @author pedro@TaleLin * @author Juzi@TaleLin * @author colorful@TaleLin + * 分组权限业务对象 */ @Data @NoArgsConstructor diff --git a/src/main/java/io/github/talelin/latticy/bo/LoginCaptchaBO.java b/src/main/java/io/github/talelin/latticy/bo/LoginCaptchaBO.java index f74b33a8..d8ec918e 100644 --- a/src/main/java/io/github/talelin/latticy/bo/LoginCaptchaBO.java +++ b/src/main/java/io/github/talelin/latticy/bo/LoginCaptchaBO.java @@ -7,6 +7,7 @@ /** * @author Gadfly * @since 2021-11-19 15:20 + * 登录验证码业务对象 */ @Data @NoArgsConstructor diff --git a/src/main/java/io/github/talelin/latticy/common/LocalUser.java b/src/main/java/io/github/talelin/latticy/common/LocalUser.java index 7388c979..5a1da2f9 100644 --- a/src/main/java/io/github/talelin/latticy/common/LocalUser.java +++ b/src/main/java/io/github/talelin/latticy/common/LocalUser.java @@ -9,7 +9,11 @@ */ public class LocalUser { - private static ThreadLocal local = new ThreadLocal<>(); + private LocalUser() { + throw new IllegalStateException("Utility class"); + } + + private static final ThreadLocal LOCAL = new ThreadLocal<>(); /** * 得到当前登录用户 @@ -17,7 +21,7 @@ public class LocalUser { * @return user | null */ public static UserDO getLocalUser() { - return LocalUser.local.get(); + return LocalUser.LOCAL.get(); } /** @@ -26,17 +30,17 @@ public static UserDO getLocalUser() { * @param user user */ public static void setLocalUser(UserDO user) { - LocalUser.local.set(user); + LocalUser.LOCAL.set(user); } public static T getLocalUser(Class clazz) { - return (T) local.get(); + return (T) LOCAL.get(); } /** * 清理当前用户 */ public static void clearLocalUser() { - LocalUser.local.remove(); + LOCAL.remove(); } } diff --git a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java index 20e1f418..6ea2d86b 100644 --- a/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java +++ b/src/main/java/io/github/talelin/latticy/common/configuration/LoginCaptchaProperties.java @@ -13,7 +13,6 @@ * * 登录图形验证码配置类 * - * TODO 整理代码规范 */ @Slf4j @Getter @@ -35,9 +34,12 @@ public class LoginCaptchaProperties { private Boolean enabled = Boolean.FALSE; public void setSecret(String secret) { + final long ivLen1 = 16; + final long ivLen2 = 24; + final long ivLen3 = 32; if (StringUtils.hasText(secret)) { byte[] bytes = secret.getBytes(); - if (bytes.length == 16 || bytes.length == 24 || bytes.length == 32) { + if (bytes.length == ivLen1 || bytes.length == ivLen2 || bytes.length == ivLen3) { this.secret = secret; } else { log.warn("AES密钥必须为128/192/256bit,输入的密钥为{}bit,已启用随机密钥{}", bytes.length * 8, this.secret); diff --git a/src/main/java/io/github/talelin/latticy/common/exception/RestExceptionHandler.java b/src/main/java/io/github/talelin/latticy/common/exception/RestExceptionHandler.java index 429e5a40..66be8b25 100644 --- a/src/main/java/io/github/talelin/latticy/common/exception/RestExceptionHandler.java +++ b/src/main/java/io/github/talelin/latticy/common/exception/RestExceptionHandler.java @@ -78,7 +78,7 @@ public UnifyResponseVO> processException(BindException excep HttpServletRequest request, HttpServletResponse response) { log.error(exception.toString()); - Map msg = new HashMap<>(); + Map msg = new HashMap<>(3); exception.getAllErrors().forEach(error -> { if (error instanceof FieldError) { FieldError fieldError = (FieldError) error; @@ -100,7 +100,7 @@ public UnifyResponseVO> processException(ConstraintViolation HttpServletRequest request, HttpServletResponse response) { log.error("", exception); - Map msg = new HashMap<>(); + Map msg = new HashMap<>(3); exception.getConstraintViolations().forEach(constraintViolation -> { String template = constraintViolation.getMessage(); String path = constraintViolation.getPropertyPath().toString(); diff --git a/src/main/java/io/github/talelin/latticy/common/interceptor/AuthorizeVerifyResolverImpl.java b/src/main/java/io/github/talelin/latticy/common/interceptor/AuthorizeVerifyResolverImpl.java index 6482170e..eed25081 100644 --- a/src/main/java/io/github/talelin/latticy/common/interceptor/AuthorizeVerifyResolverImpl.java +++ b/src/main/java/io/github/talelin/latticy/common/interceptor/AuthorizeVerifyResolverImpl.java @@ -37,9 +37,9 @@ @Component public class AuthorizeVerifyResolverImpl implements AuthorizeVerifyResolver { - public final static String AUTHORIZATION_HEADER = "Authorization"; + public static final String AUTHORIZATION_HEADER = "Authorization"; - public final static String BEARER_PATTERN = "^Bearer$"; + public static final String BEARER_PATTERN = "^Bearer$"; @Autowired private DoubleJWT jwt; @@ -59,7 +59,7 @@ public class AuthorizeVerifyResolverImpl implements AuthorizeVerifyResolver { @Override public boolean handleLogin(HttpServletRequest request, HttpServletResponse response, MetaInfo meta) { - String tokenStr = verifyHeader(request, response); + String tokenStr = verifyHeader(request); Map claims; try { claims = jwt.decodeAccessToken(tokenStr); @@ -102,7 +102,7 @@ public boolean handleAdmin(HttpServletRequest request, HttpServletResponse respo @Override public boolean handleRefresh(HttpServletRequest request, HttpServletResponse response, MetaInfo meta) { - String tokenStr = verifyHeader(request, response); + String tokenStr = verifyHeader(request); Map claims; try { claims = jwt.decodeRefreshToken(tokenStr); @@ -135,9 +135,10 @@ private boolean getClaim(Map claims) { throw new NotFoundException(10021); } String avatarUrl; + final String protocolPrefix = "http"; if (user.getAvatar() == null) { avatarUrl = null; - } else if (user.getAvatar().startsWith("http")) { + } else if (user.getAvatar().startsWith(protocolPrefix)) { avatarUrl = user.getAvatar(); } else { avatarUrl = domain + servePath.split("/")[0] + "/" + user.getAvatar(); @@ -156,14 +157,15 @@ private boolean verifyAdmin(UserDO user) { return groupService.checkIsRootByUserId(user.getId()); } - private String verifyHeader(HttpServletRequest request, HttpServletResponse response) { + private String verifyHeader(HttpServletRequest request) { // 处理头部header,带有access_token的可以访问 String authorization = request.getHeader(AUTHORIZATION_HEADER); if (authorization == null || Strings.isBlank(authorization)) { throw new AuthorizationException(10012); } String[] splits = authorization.split(" "); - if (splits.length != 2) { + final int tokenSplitLen = 2; + if (splits.length != tokenSplitLen) { throw new AuthorizationException(10013); } // Bearer 字段 diff --git a/src/main/java/io/github/talelin/latticy/common/interceptor/LoggerImpl.java b/src/main/java/io/github/talelin/latticy/common/interceptor/LoggerImpl.java index 56a64f1f..272df000 100644 --- a/src/main/java/io/github/talelin/latticy/common/interceptor/LoggerImpl.java +++ b/src/main/java/io/github/talelin/latticy/common/interceptor/LoggerImpl.java @@ -10,7 +10,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -42,7 +41,7 @@ public void handle(PermissionMeta meta, Logger logger, HttpServletRequest reques template = this.parseTemplate(template, user, request, response); String permission = ""; if (meta != null) { - permission = !StringUtils.hasLength(meta.value()) ? meta.value() : meta.value(); + permission = meta.value(); } Integer userId = user.getId(); String username = user.getUsername(); diff --git a/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java b/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java index 8fbc18dd..20a4116f 100644 --- a/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java +++ b/src/main/java/io/github/talelin/latticy/common/interceptor/RequestLogInterceptor.java @@ -16,21 +16,22 @@ public class RequestLogInterceptor implements AsyncHandlerInterceptor { - private ThreadLocal startTime = new ThreadLocal<>(); + private final ThreadLocal startTime = new ThreadLocal<>(); @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { startTime.set(System.currentTimeMillis()); return true; } @Override - public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { log.info("[{}] -> [{}] from: {} costs: {}ms", request.getMethod(), request.getServletPath(), IPUtil.getIPFromRequest(request), System.currentTimeMillis() - startTime.get() ); + startTime.remove(); } } diff --git a/src/main/java/io/github/talelin/latticy/common/mybatis/Page.java b/src/main/java/io/github/talelin/latticy/common/mybatis/LinPage.java similarity index 78% rename from src/main/java/io/github/talelin/latticy/common/mybatis/Page.java rename to src/main/java/io/github/talelin/latticy/common/mybatis/LinPage.java index 221ae72f..04dd6367 100644 --- a/src/main/java/io/github/talelin/latticy/common/mybatis/Page.java +++ b/src/main/java/io/github/talelin/latticy/common/mybatis/LinPage.java @@ -1,5 +1,7 @@ package io.github.talelin.latticy.common.mybatis; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + /** * 分页对象 * 为和其他端保持一致 @@ -7,26 +9,26 @@ * * @author Juzi@TaleLin */ -public class Page extends com.baomidou.mybatisplus.extension.plugins.pagination.Page { +public class LinPage extends Page { private static final long serialVersionUID = -2183463672525305273L; /** * 该构造方法使得 current 总为 0 */ - public Page() { + public LinPage() { super.setCurrent(0); } - public Page(int current, int size) { + public LinPage(int current, int size) { this(current, size, 0); } - public Page(int current, int size, int total) { + public LinPage(int current, int size, int total) { this(current, size, total, true); } - public Page(int current, int size, boolean isSearchCount) { + public LinPage(int current, int size, boolean isSearchCount) { this(current, size, 0, isSearchCount); } @@ -38,7 +40,7 @@ public Page(int current, int size, boolean isSearchCount) { * @param total 总数 * @param isSearchCount 是否进行 count 查询 */ - public Page(int current, int size, int total, boolean isSearchCount) { + public LinPage(int current, int size, int total, boolean isSearchCount) { super(current, size, total, isSearchCount); if (current < 0) { diff --git a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java index d2bd33f1..4566cb46 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/CaptchaUtil.java @@ -28,6 +28,10 @@ @SuppressWarnings("SpellCheckingInspection") public class CaptchaUtil { + private CaptchaUtil () { + throw new IllegalStateException("Utility class"); + } + /** * 验证码字符个数 */ @@ -107,11 +111,11 @@ private static void drawString(Graphics2D g, String randomStr, int i) throws IOE g.setColor(getRandomColor(28, 130)); // 设置每个字符的随机旋转 double radianPercent = (RANDOM.nextBoolean() ? -1 : 1) * Math.PI * (RANDOM.nextInt(60) / 320D); - g.rotate(radianPercent, WIDTH * 0.8 / RANDOM_STR_NUM * i, HEIGHT / 2); + g.rotate(radianPercent, WIDTH * 0.8 / RANDOM_STR_NUM * i, HEIGHT / 2.0); int y = (RANDOM.nextBoolean() ? -1 : 1) * RANDOM.nextInt(4) + 4; g.translate(RANDOM.nextInt(3), y); g.drawString(randomStr, WIDTH / RANDOM_STR_NUM * i, HEIGHT / 2); - g.rotate(-radianPercent, WIDTH * 0.8 / RANDOM_STR_NUM * i, HEIGHT / 2); + g.rotate(-radianPercent, WIDTH * 0.8 / RANDOM_STR_NUM * i, HEIGHT / 2.0); g.translate(0, -y); } diff --git a/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java b/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java index 187b2d24..c403c242 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/IPUtil.java @@ -13,6 +13,10 @@ @Slf4j public class IPUtil { + private IPUtil() { + throw new IllegalStateException("Utility class"); + } + private static final String[] IP_HEADER_CANDIDATES = { "X-Forwarded-For", "Proxy-Client-IP", diff --git a/src/main/java/io/github/talelin/latticy/common/util/PageUtil.java b/src/main/java/io/github/talelin/latticy/common/util/PageUtil.java index cf53444e..214afc20 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/PageUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/PageUtil.java @@ -11,6 +11,10 @@ */ public class PageUtil { + private PageUtil() { + throw new IllegalStateException("Utility class"); + } + public static PageResponseVO build(IPage iPage) { return new PageResponseVO<>(Math.toIntExact(iPage.getTotal()), iPage.getRecords(), Math.toIntExact(iPage.getCurrent()), Math.toIntExact(iPage.getSize())); diff --git a/src/main/java/io/github/talelin/latticy/common/util/ResponseUtil.java b/src/main/java/io/github/talelin/latticy/common/util/ResponseUtil.java index 2c4ddeac..51ee649d 100644 --- a/src/main/java/io/github/talelin/latticy/common/util/ResponseUtil.java +++ b/src/main/java/io/github/talelin/latticy/common/util/ResponseUtil.java @@ -18,6 +18,10 @@ @Slf4j public class ResponseUtil { + private ResponseUtil() { + throw new IllegalStateException("Utility class"); + } + /** * 获得当前响应 * diff --git a/src/main/java/io/github/talelin/latticy/controller/cms/AdminController.java b/src/main/java/io/github/talelin/latticy/controller/cms/AdminController.java index 6f5adaa4..4c894696 100644 --- a/src/main/java/io/github/talelin/latticy/controller/cms/AdminController.java +++ b/src/main/java/io/github/talelin/latticy/controller/cms/AdminController.java @@ -24,6 +24,7 @@ import java.util.stream.Collectors; /** + * 管理员控制器 * @author pedro@TaleLin * @author Juzi@TaleLin */ diff --git a/src/main/java/io/github/talelin/latticy/controller/cms/FileController.java b/src/main/java/io/github/talelin/latticy/controller/cms/FileController.java index 3e763a00..bb41ef9e 100644 --- a/src/main/java/io/github/talelin/latticy/controller/cms/FileController.java +++ b/src/main/java/io/github/talelin/latticy/controller/cms/FileController.java @@ -14,6 +14,7 @@ import java.util.List; /** + * 文件控制器 * @author pedro@TaleLin * @author Juzi@TaleLin */ diff --git a/src/main/java/io/github/talelin/latticy/controller/cms/LogController.java b/src/main/java/io/github/talelin/latticy/controller/cms/LogController.java index 9897e9af..7f8185d5 100644 --- a/src/main/java/io/github/talelin/latticy/controller/cms/LogController.java +++ b/src/main/java/io/github/talelin/latticy/controller/cms/LogController.java @@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.RestController; /** + * 日志控制器 * @author pedro@TaleLin * @author Juzi@TaleLin */ diff --git a/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java b/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java index 06198c16..36eac5bd 100644 --- a/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java +++ b/src/main/java/io/github/talelin/latticy/controller/cms/UserController.java @@ -39,6 +39,7 @@ import java.util.Map; /** + * 用户控制器 * @author pedro@TaleLin * @author Juzi@TaleLin */ @@ -77,11 +78,10 @@ public CreatedVO register(@RequestBody @Validated RegisterDTO validator) { * 用户登陆 */ @PostMapping("/login") - public Tokens login(@RequestBody @Validated LoginDTO validator, @RequestHeader(value = "Tag", required = false) String tag) { - if (captchaConfig.getEnabled()) { - // TODO: 使用spring validation验证。暂时还没想到怎么根据配置文件分组 + public Tokens login(@RequestBody @Validated LoginDTO validator, @RequestHeader(required = false) String tag) { + if (Boolean.TRUE.equals(captchaConfig.getEnabled())) { if (!StringUtils.hasText(validator.getCaptcha()) || !StringUtils.hasText(tag)) { - throw new ParameterException("验证码不可为空"); + throw new ParameterException(10260); } if (!userService.verifyCaptcha(validator.getCaptcha(), tag)) { throw new ParameterException(10260); @@ -103,7 +103,7 @@ public Tokens login(@RequestBody @Validated LoginDTO validator, @RequestHeader(v @PostMapping("/captcha") public LoginCaptchaVO userCaptcha() throws Exception { - if (captchaConfig.getEnabled()) { + if (Boolean.TRUE.equals(captchaConfig.getEnabled())) { return userService.generateCaptcha(); } return new LoginCaptchaVO(); diff --git a/src/main/java/io/github/talelin/latticy/controller/v1/BookController.java b/src/main/java/io/github/talelin/latticy/controller/v1/BookController.java index 4af69a2d..222a91bf 100644 --- a/src/main/java/io/github/talelin/latticy/controller/v1/BookController.java +++ b/src/main/java/io/github/talelin/latticy/controller/v1/BookController.java @@ -11,20 +11,13 @@ import io.github.talelin.latticy.vo.UpdatedVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; -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.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import javax.validation.constraints.Positive; import java.util.List; /** + * 图书控制器 * @author pedro@TaleLin * @author Juzi@TaleLin */ diff --git a/src/main/java/io/github/talelin/latticy/extension/file/LocalUploader.java b/src/main/java/io/github/talelin/latticy/extension/file/LocalUploader.java index 3d8d53a2..8972c2b5 100644 --- a/src/main/java/io/github/talelin/latticy/extension/file/LocalUploader.java +++ b/src/main/java/io/github/talelin/latticy/extension/file/LocalUploader.java @@ -1,8 +1,8 @@ package io.github.talelin.latticy.extension.file; import io.github.talelin.latticy.module.file.AbstractUploader; -import io.github.talelin.latticy.module.file.FileConstant; import io.github.talelin.latticy.module.file.FileProperties; +import io.github.talelin.latticy.module.file.FileTypeEnum; import io.github.talelin.latticy.module.file.FileUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; @@ -10,7 +10,7 @@ import javax.annotation.PostConstruct; import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileOutputStream; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.text.SimpleDateFormat; @@ -38,14 +38,11 @@ public void initStoreDir() { protected boolean handleOneFile(byte[] bytes, String newFilename) { String absolutePath = FileUtil.getFileAbsolutePath(fileProperties.getStoreDir(), getStorePath(newFilename)); - try { - BufferedOutputStream stream = - new BufferedOutputStream(new FileOutputStream(new java.io.File(absolutePath))); + try (BufferedOutputStream stream = + new BufferedOutputStream(Files.newOutputStream(new File(absolutePath).toPath()))) { stream.write(bytes); - stream.close(); } catch (Exception e) { log.error("write file to local err:", e); - // throw new FailedException(10190); return false; } return true; @@ -71,6 +68,6 @@ protected String getStorePath(String newFilename) { @Override protected String getFileType() { - return FileConstant.LOCAL; + return FileTypeEnum.LOCAL.getValue(); } } diff --git a/src/main/java/io/github/talelin/latticy/extension/file/QiniuUploader.java b/src/main/java/io/github/talelin/latticy/extension/file/QiniuUploader.java index d611eba8..bee47262 100644 --- a/src/main/java/io/github/talelin/latticy/extension/file/QiniuUploader.java +++ b/src/main/java/io/github/talelin/latticy/extension/file/QiniuUploader.java @@ -7,8 +7,8 @@ import com.qiniu.storage.UploadManager; import com.qiniu.util.Auth; import io.github.talelin.latticy.module.file.AbstractUploader; -import io.github.talelin.latticy.module.file.FileConstant; import io.github.talelin.latticy.module.file.FileProperties; +import io.github.talelin.latticy.module.file.FileTypeEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -59,7 +59,7 @@ protected String getStorePath(String newFilename) { @Override protected String getFileType() { - return FileConstant.REMOTE; + return FileTypeEnum.REMOTE.getValue(); } /** diff --git a/src/main/java/io/github/talelin/latticy/mapper/LogMapper.java b/src/main/java/io/github/talelin/latticy/mapper/LogMapper.java index c8a9976e..d4e7e2f4 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/LogMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/LogMapper.java @@ -2,7 +2,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.model.LogDO; import org.springframework.stereotype.Repository; @@ -24,7 +24,7 @@ public interface LogMapper extends BaseMapper { * @param end 结束日期 * @return 日志分页对象 */ - IPage findLogsByUsernameAndRange(Page pager, String name, Date start, Date end); + IPage findLogsByUsernameAndRange(LinPage pager, String name, Date start, Date end); /** * 查询日志 @@ -36,7 +36,7 @@ public interface LogMapper extends BaseMapper { * @param end 结束日期 * @return 日志分页对象 */ - IPage searchLogsByUsernameAndKeywordAndRange(Page pager, String name, String keyword, Date start, Date end); + IPage searchLogsByUsernameAndKeywordAndRange(LinPage pager, String name, String keyword, Date start, Date end); /** * 查询用户名分页列表 @@ -44,6 +44,6 @@ public interface LogMapper extends BaseMapper { * @param pager Page * @return 用户名分页对象 */ - IPage getUserNames(Page pager); + IPage getUserNames(LinPage pager); } diff --git a/src/main/java/io/github/talelin/latticy/mapper/UserMapper.java b/src/main/java/io/github/talelin/latticy/mapper/UserMapper.java index fa6f7469..29887a76 100644 --- a/src/main/java/io/github/talelin/latticy/mapper/UserMapper.java +++ b/src/main/java/io/github/talelin/latticy/mapper/UserMapper.java @@ -2,7 +2,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.model.UserDO; import org.springframework.stereotype.Repository; @@ -40,5 +40,5 @@ public interface UserMapper extends BaseMapper { * @param rootGroupId 超级用户组id(不返回超级用户组的用户) * @return 分页数据 */ - IPage selectPageByGroupId(Page pager, Integer groupId, Integer rootGroupId); + IPage selectPageByGroupId(LinPage pager, Integer groupId, Integer rootGroupId); } diff --git a/src/main/java/io/github/talelin/latticy/module/file/FileConstant.java b/src/main/java/io/github/talelin/latticy/module/file/FileConstant.java deleted file mode 100644 index a0ba21d4..00000000 --- a/src/main/java/io/github/talelin/latticy/module/file/FileConstant.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.github.talelin.latticy.module.file; - -/** - * 文件相关常量值 - * - * @author pedro@TaleLin - */ -public interface FileConstant { - - /** - * 本地文件 - */ - String LOCAL = "LOCAL"; - - /** - * 远程文件,例如OSS - */ - String REMOTE = "REMOTE"; -} diff --git a/src/main/java/io/github/talelin/latticy/module/file/FileTypeEnum.java b/src/main/java/io/github/talelin/latticy/module/file/FileTypeEnum.java new file mode 100644 index 00000000..fb53f6d9 --- /dev/null +++ b/src/main/java/io/github/talelin/latticy/module/file/FileTypeEnum.java @@ -0,0 +1,32 @@ +package io.github.talelin.latticy.module.file; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** + * @author colorful + * 文件类型枚举 + */ +public enum FileTypeEnum implements IEnum { + + /** + * 本地文件 + */ + LOCAL("LOCAL"), + + /** + * 远程文件,例如OSS + */ + REMOTE("REMOTE") + ; + + final String value; + + FileTypeEnum(String value) { + this.value = value; + } + + @Override + public String getValue() { + return value; + } +} diff --git a/src/main/java/io/github/talelin/latticy/module/file/UploadHandler.java b/src/main/java/io/github/talelin/latticy/module/file/UploadHandler.java index 8f1791ad..e4879626 100644 --- a/src/main/java/io/github/talelin/latticy/module/file/UploadHandler.java +++ b/src/main/java/io/github/talelin/latticy/module/file/UploadHandler.java @@ -1,7 +1,7 @@ package io.github.talelin.latticy.module.file; /** - * 文件前预处理器 + * 上传预处理器 * * @author pedro@TaleLin */ diff --git a/src/main/java/io/github/talelin/latticy/module/log/MDCAccessConstant.java b/src/main/java/io/github/talelin/latticy/module/log/MDCAccessConstant.java index e70d719b..e5d77eb9 100644 --- a/src/main/java/io/github/talelin/latticy/module/log/MDCAccessConstant.java +++ b/src/main/java/io/github/talelin/latticy/module/log/MDCAccessConstant.java @@ -1,6 +1,7 @@ package io.github.talelin.latticy.module.log; /** + * 日志常量类 * @author Juzi@TaleLin * @date 2020/6/20 10:22 */ diff --git a/src/main/java/io/github/talelin/latticy/module/message/WebSocketInterceptor.java b/src/main/java/io/github/talelin/latticy/module/message/WebSocketInterceptor.java index 0c2b8341..7c3e66ba 100644 --- a/src/main/java/io/github/talelin/latticy/module/message/WebSocketInterceptor.java +++ b/src/main/java/io/github/talelin/latticy/module/message/WebSocketInterceptor.java @@ -27,7 +27,7 @@ /** * @author pedro@TaleLin * @author Juzi@TaleLin - * websocket 拦截器 + * websocket 拦截器,主要是jwt鉴权 */ public class WebSocketInterceptor implements HandshakeInterceptor { @Autowired diff --git a/src/main/java/io/github/talelin/latticy/service/UserService.java b/src/main/java/io/github/talelin/latticy/service/UserService.java index 06b69a65..33c0af79 100644 --- a/src/main/java/io/github/talelin/latticy/service/UserService.java +++ b/src/main/java/io/github/talelin/latticy/service/UserService.java @@ -2,7 +2,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.IService; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.dto.user.ChangePasswordDTO; import io.github.talelin.latticy.dto.user.RegisterDTO; import io.github.talelin.latticy.dto.user.UpdateInfoDTO; @@ -11,6 +11,9 @@ import io.github.talelin.latticy.model.UserDO; import io.github.talelin.latticy.vo.LoginCaptchaVO; +import java.awt.*; +import java.io.IOException; +import java.security.GeneralSecurityException; import java.util.List; import java.util.Map; @@ -121,7 +124,7 @@ public interface UserService extends IService { * @param groupId 分组id * @return 数据页 */ - IPage getUserPageByGroupId(Page pager, Integer groupId); + IPage getUserPageByGroupId(LinPage pager, Integer groupId); /** @@ -134,9 +137,12 @@ public interface UserService extends IService { /** * 生成无状态的登录验证码 * - * @return 验证码 + * @return LoginCaptchaVO 验证码视图对象 + * @throws IOException + * @throws FontFormatException + * @throws GeneralSecurityException */ - LoginCaptchaVO generateCaptcha() throws Exception; + LoginCaptchaVO generateCaptcha() throws IOException, FontFormatException, GeneralSecurityException; /** * 校验登录验证码 diff --git a/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java index b6c3579d..963b15a7 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/AdminServiceImpl.java @@ -6,7 +6,7 @@ import io.github.talelin.autoconfigure.exception.NotFoundException; import io.github.talelin.latticy.bo.GroupPermissionBO; import io.github.talelin.latticy.common.enumeration.GroupLevelEnum; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.dto.admin.*; import io.github.talelin.latticy.mapper.GroupPermissionMapper; import io.github.talelin.latticy.mapper.UserGroupMapper; @@ -51,7 +51,7 @@ public class AdminServiceImpl implements AdminService { @Override public IPage getUserPageByGroupId(Integer groupId, Integer count, Integer page) { - Page pager = new Page<>(page, count); + LinPage pager = new LinPage<>(page, count); IPage iPage; // 如果group_id为空,则以分页的形式返回所有用户 if (groupId == null) { diff --git a/src/main/java/io/github/talelin/latticy/service/impl/FileServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/FileServiceImpl.java index a76e9b2d..ac219a67 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/FileServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/FileServiceImpl.java @@ -47,8 +47,7 @@ public List upload(MultiValueMap fileMap) { @Override public boolean preHandle(File file) { FileDO found = baseMapper.selectByMd5(file.getMd5()); - // 数据库中不存在,存储操作放在上传到本地或云上之后, - // 修复issue131:https://github.com/TaleLin/lin-cms-spring-boot/issues/131 + // 数据库中不存在,存储操作放在上传到本地或云上之后 if (found == null) { return true; } @@ -59,7 +58,7 @@ public boolean preHandle(File file) { @Override public void afterHandle(File file) { - // 保存到数据库, 修复issue131:https://github.com/TaleLin/lin-cms-spring-boot/issues/131 + // 保存到数据库 FileDO fileDO = new FileDO(); BeanUtils.copyProperties(file, fileDO); getBaseMapper().insert(fileDO); @@ -77,7 +76,7 @@ public boolean checkFileExistByMd5(String md5) { private FileBO transformDoToBo(FileDO file, String key) { FileBO bo = new FileBO(); BeanUtils.copyProperties(file, bo); - if (file.getType().equals(FileConstant.LOCAL)) { + if (file.getType().equals(FileTypeEnum.LOCAL.getValue())) { String s = fileProperties.getServePath().split("/")[0]; // replaceAll 是将 windows 平台下的 \ 替换为 / diff --git a/src/main/java/io/github/talelin/latticy/service/impl/GroupServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/GroupServiceImpl.java index 0860bd3d..405fabc2 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/GroupServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/GroupServiceImpl.java @@ -6,7 +6,7 @@ import io.github.talelin.autoconfigure.exception.ForbiddenException; import io.github.talelin.latticy.bo.GroupPermissionBO; import io.github.talelin.latticy.common.enumeration.GroupLevelEnum; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.mapper.GroupMapper; import io.github.talelin.latticy.mapper.UserGroupMapper; import io.github.talelin.latticy.model.GroupDO; @@ -47,7 +47,7 @@ public List getUserGroupIdsByUserId(Integer userId) { @Override public IPage getGroupPage(int page, int count) { - Page pager = new Page<>(page, count); + LinPage pager = new LinPage<>(page, count); return this.baseMapper.selectPage(pager, null); } diff --git a/src/main/java/io/github/talelin/latticy/service/impl/LogServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/LogServiceImpl.java index 1de8aa26..970edfb6 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/LogServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/LogServiceImpl.java @@ -2,7 +2,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.mapper.LogMapper; import io.github.talelin.latticy.model.LogDO; import io.github.talelin.latticy.service.LogService; @@ -20,25 +20,25 @@ public class LogServiceImpl extends ServiceImpl implements Log @Override public IPage getLogPage(Integer page, Integer count, String name, Date start, Date end) { - Page pager = new Page<>(page, count); + LinPage pager = new LinPage<>(page, count); return this.baseMapper.findLogsByUsernameAndRange(pager, name, start, end); } @Override public IPage searchLogPage(Integer page, Integer count, String name, String keyword, Date start, Date end) { - Page pager = new Page<>(page, count); + LinPage pager = new LinPage<>(page, count); return this.baseMapper.searchLogsByUsernameAndKeywordAndRange(pager, name, "%" + keyword + "%", start, end); } @Override public IPage getUserNamePage(Integer page, Integer count) { - Page pager = new Page<>(page, count); + LinPage pager = new LinPage<>(page, count); return this.baseMapper.getUserNames(pager); } @Override public boolean createLog(String message, String permission, Integer userId, String username, String method, String path, Integer status) { - LogDO record = LogDO.builder() + LogDO log = LogDO.builder() .message(message) .userId(userId) .username(username) @@ -47,8 +47,8 @@ public boolean createLog(String message, String permission, Integer userId, Stri .path(path) .build(); if (permission != null) { - record.setPermission(permission); + log.setPermission(permission); } - return this.baseMapper.insert(record) > 0; + return this.baseMapper.insert(log) > 0; } } diff --git a/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java b/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java index 0a0cb4f5..299fbe62 100644 --- a/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java +++ b/src/main/java/io/github/talelin/latticy/service/impl/UserServiceImpl.java @@ -11,7 +11,7 @@ import io.github.talelin.latticy.common.LocalUser; import io.github.talelin.latticy.common.configuration.LoginCaptchaProperties; import io.github.talelin.latticy.common.enumeration.GroupLevelEnum; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.common.util.BeanCopyUtil; import io.github.talelin.latticy.common.util.CaptchaUtil; import io.github.talelin.latticy.dto.user.ChangePasswordDTO; @@ -34,6 +34,9 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import java.awt.*; +import java.io.IOException; +import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -151,7 +154,7 @@ public List>>> getStructuralUserPermissions public List getUserPermissions(Integer userId) { // 查找用户搜索分组,查找分组下的所有权限 List groupIds = groupService.getUserGroupIdsByUserId(userId); - if (groupIds == null || groupIds.size() == 0) { + if (groupIds == null || groupIds.isEmpty()) { return new ArrayList<>(); } return permissionService.getPermissionByGroupIds(groupIds); @@ -160,7 +163,7 @@ public List getUserPermissions(Integer userId) { @Override public List getUserPermissionsByModule(Integer userId, String module) { List groupIds = groupService.getUserGroupIdsByUserId(userId); - if (groupIds == null || groupIds.size() == 0) { + if (groupIds == null || groupIds.isEmpty()) { return new ArrayList<>(); } return permissionService.getPermissionByGroupIdsAndModule(groupIds, module); @@ -194,7 +197,7 @@ public boolean checkUserExistById(Integer id) { } @Override - public IPage getUserPageByGroupId(Page pager, Integer groupId) { + public IPage getUserPageByGroupId(LinPage pager, Integer groupId) { Integer rootGroupId = groupService.getParticularGroupIdByLevel(GroupLevelEnum.ROOT); return this.baseMapper.selectPageByGroupId(pager, groupId, rootGroupId); } @@ -212,7 +215,7 @@ public Integer getRootUserId() { } @Override - public LoginCaptchaVO generateCaptcha() throws Exception { + public LoginCaptchaVO generateCaptcha() throws IOException, FontFormatException, GeneralSecurityException { String code = CaptchaUtil.getRandomString(CaptchaUtil.RANDOM_STR_NUM); String base64String = CaptchaUtil.getRandomCodeBase64(code); String tag = CaptchaUtil.getTag(code, captchaConfig.getSecret(), captchaConfig.getIv()); diff --git a/src/test/java/io/github/talelin/latticy/mapper/LogMapperTest.java b/src/test/java/io/github/talelin/latticy/mapper/LogMapperTest.java index a51d222b..407593b0 100644 --- a/src/test/java/io/github/talelin/latticy/mapper/LogMapperTest.java +++ b/src/test/java/io/github/talelin/latticy/mapper/LogMapperTest.java @@ -1,7 +1,7 @@ package io.github.talelin.latticy.mapper; import com.baomidou.mybatisplus.core.metadata.IPage; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.model.LogDO; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -24,7 +24,7 @@ @Rollback @ActiveProfiles("test") @TestInstance(TestInstance.Lifecycle.PER_CLASS) -public class LogMapperTest { +class LogMapperTest { @Autowired private LogMapper logMapper; @@ -56,20 +56,20 @@ public void setUp() throws Exception { } @Test - public void testFindLogsByUsernameAndRange() { + void testFindLogsByUsernameAndRange() { Date now = new Date(); - Page page = new Page<>(0, 10); + LinPage page = new LinPage<>(0, 10); IPage iPage = logMapper.findLogsByUsernameAndRange(page, username, start, now); List logs = iPage.getRecords(); assertTrue(logs.size() > 0); } @Test - public void testFindLogsByUsernameAndRange1() { + void testFindLogsByUsernameAndRange1() { long changed = start.getTime(); Date ch = new Date(changed - 1000); Date ch1 = new Date(changed - 2000); - Page page = new Page<>(1, 10); + LinPage page = new LinPage<>(1, 10); IPage iPage = logMapper.findLogsByUsernameAndRange(page, username, ch1, ch); List logs = iPage.getRecords(); assertEquals(0, logs.size()); diff --git a/src/test/java/io/github/talelin/latticy/mapper/UserMapperTest.java b/src/test/java/io/github/talelin/latticy/mapper/UserMapperTest.java index 2c0cafbc..f1dcd4ed 100644 --- a/src/test/java/io/github/talelin/latticy/mapper/UserMapperTest.java +++ b/src/test/java/io/github/talelin/latticy/mapper/UserMapperTest.java @@ -1,7 +1,7 @@ package io.github.talelin.latticy.mapper; import com.baomidou.mybatisplus.core.metadata.IPage; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.model.GroupDO; import io.github.talelin.latticy.model.UserDO; import io.github.talelin.latticy.model.UserGroupDO; @@ -19,7 +19,7 @@ @Transactional @Rollback @ActiveProfiles("test") -public class UserMapperTest { +class UserMapperTest { @Autowired private UserMapper userMapper; @@ -32,7 +32,7 @@ public class UserMapperTest { @Test - public void selectCountByUsername() { + void selectCountByUsername() { String email = "13129982604@qq.com"; String username = "pedro-test"; UserDO userDO = new UserDO(); @@ -44,7 +44,7 @@ public void selectCountByUsername() { } @Test - public void selectCountById() { + void selectCountById() { String email = "13129982604@qq.com"; String username = "pedro-test"; UserDO userDO = new UserDO(); @@ -56,7 +56,7 @@ public void selectCountById() { } @Test - public void selectPageByGroupId() { + void selectPageByGroupId() { String email = "13129982604@qq.com"; String username = "pedro-test"; UserDO userDO = new UserDO(); @@ -69,7 +69,7 @@ public void selectPageByGroupId() { userGroupMapper.insert(new UserGroupDO(userDO.getId(), group.getId())); - Page page = new Page(0, 10); + LinPage page = new LinPage(0, 10); IPage iPage = userMapper.selectPageByGroupId(page, group.getId(), 99999); assertTrue(iPage.getTotal() > 0); boolean anyMatch = iPage.getRecords().stream().anyMatch(it -> it.getUsername().equals(username)); diff --git a/src/test/resources/mpg/templates/premium/controller.java.ftl b/src/test/resources/mpg/templates/premium/controller.java.ftl index 0e55d15c..1f7d93cd 100644 --- a/src/test/resources/mpg/templates/premium/controller.java.ftl +++ b/src/test/resources/mpg/templates/premium/controller.java.ftl @@ -5,7 +5,7 @@ import io.github.talelin.autoconfigure.exception.NotFoundException; <#if package.Controller?split(".")?last == "cms"> import io.github.talelin.core.annotation.LoginRequired; -import io.github.talelin.latticy.common.mybatis.Page; +import io.github.talelin.latticy.common.mybatis.LinPage; import io.github.talelin.latticy.common.util.PageUtil; import io.github.talelin.latticy.dto.query.BasePageDTO; import ${package.Service}.${table.serviceName};