commit 9f56a86116f0dfb6c8c4ec8f297c643ebb34f3c6
Author: abu <3109389044@qq.com>
Date: Mon Mar 10 15:40:51 2025 +0800
init
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a0595ce
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+# 项目排除路径
+/book-api/target/
+/book-common/target/
+/book-mapper/target/
+/book-model/target/
+/book-service/target/
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..35410ca
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..188c465
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644
index 0000000..b734512
--- /dev/null
+++ b/.idea/encodings.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..7628153
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/mybatis_generator_for_imooc_1_0_SNAPSHOT.xml b/.idea/libraries/mybatis_generator_for_imooc_1_0_SNAPSHOT.xml
new file mode 100644
index 0000000..62eb41d
--- /dev/null
+++ b/.idea/libraries/mybatis_generator_for_imooc_1_0_SNAPSHOT.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..d0d019d
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml
new file mode 100644
index 0000000..2b63946
--- /dev/null
+++ b/.idea/uiDesigner.xml
@@ -0,0 +1,124 @@
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+
+ -
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vlog-1.0.0.iml b/.idea/vlog-1.0.0.iml
new file mode 100644
index 0000000..d6ebd48
--- /dev/null
+++ b/.idea/vlog-1.0.0.iml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f6e6af3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,16 @@
+# douyin_dome
+SpringBoot+Uniapp仿抖音短视频App
+
+只用修改api模块下面的配置即可运行
+
+Knife4j 接口文档通过访问 http://ip+prot/doc.html#/home 查看api
+# 架构
+
+
+
+# 效果
+
+
+
+
+
diff --git a/book-api/pom.xml b/book-api/pom.xml
new file mode 100644
index 0000000..b699a3a
--- /dev/null
+++ b/book-api/pom.xml
@@ -0,0 +1,71 @@
+
+
+ 4.0.0
+
+ com.imooc
+ imooc-red-book-dev
+ 1.0-SNAPSHOT
+
+
+ jar
+
+
+ book-api
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-bootstrap
+
+
+
+
+ com.imooc
+ book-service
+ 1.0-SNAPSHOT
+
+
+ com.github.xiaoymin
+ knife4j-spring-boot-starter
+
+
+ com.github.xiaoymin
+ knife4j-springdoc-ui
+
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/book-api/src/main/java/com/imooc/Application.java b/book-api/src/main/java/com/imooc/Application.java
new file mode 100644
index 0000000..d1ba13f
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/Application.java
@@ -0,0 +1,23 @@
+package com.imooc;
+
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
+import tk.mybatis.spring.annotation.MapperScan;
+
+/**
+ * @author vercen
+ * @version 1.0
+ * @date 2023/5/20 14:25
+ */
+@SpringBootApplication
+@MapperScan(basePackages = {"com.imooc.mapper"})
+@ComponentScan(basePackages = {"com.imooc","org.n3r.idworker"})
+@EnableMongoRepositories
+public class Application {
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/config/InterceptorConfig.java b/book-api/src/main/java/com/imooc/config/InterceptorConfig.java
new file mode 100644
index 0000000..51394b0
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/config/InterceptorConfig.java
@@ -0,0 +1,32 @@
+package com.imooc.config;
+
+import com.imooc.intercepter.PassportInterceptor;
+import com.imooc.intercepter.UserTokenInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class InterceptorConfig implements WebMvcConfigurer {
+
+ @Bean
+ public PassportInterceptor passportInterceptor() {
+ return new PassportInterceptor();
+ }
+
+ @Bean
+ public UserTokenInterceptor userTokenInterceptor() {
+ return new UserTokenInterceptor();
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(passportInterceptor())
+ .addPathPatterns("/passport/getSMSCode");
+
+ registry.addInterceptor(userTokenInterceptor())
+ .addPathPatterns("/userInfo/modifyUserInfo")
+ .addPathPatterns("/userInfo/modifyImage");
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/config/MinIOConfig.java b/book-api/src/main/java/com/imooc/config/MinIOConfig.java
new file mode 100644
index 0000000..0ec55ed
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/config/MinIOConfig.java
@@ -0,0 +1,33 @@
+package com.imooc.config;
+
+import com.imooc.utils.MinIOUtils;
+import lombok.Data;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@Data
+public class MinIOConfig {
+
+ @Value("${minio.endpoint}")
+ private String endpoint;
+ @Value("${minio.fileHost}")
+ private String fileHost;
+ @Value("${minio.bucketName}")
+ private String bucketName;
+ @Value("${minio.accessKey}")
+ private String accessKey;
+ @Value("${minio.secretKey}")
+ private String secretKey;
+
+ @Value("${minio.imgSize}")
+ private Integer imgSize;
+ @Value("${minio.fileSize}")
+ private Integer fileSize;
+
+ @Bean
+ public MinIOUtils creatMinioClient() {
+ return new MinIOUtils(endpoint, bucketName, accessKey, secretKey, imgSize, fileSize);
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/config/knife4jConfig.java b/book-api/src/main/java/com/imooc/config/knife4jConfig.java
new file mode 100644
index 0000000..3b2905e
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/config/knife4jConfig.java
@@ -0,0 +1,40 @@
+package com.imooc.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
+
+/**
+ * @author vercen
+ * @version 1.0
+ * @date 2023/5/20 19:55
+ */
+@Configuration
+@EnableSwagger2WebMvc
+public class knife4jConfig {
+ @Bean
+ public Docket defaultApi2() {
+ Docket docket=new Docket(DocumentationType.SWAGGER_2)
+ .apiInfo(new ApiInfoBuilder()
+ //.title("swagger-bootstrap-ui-demo RESTful APIs")
+ .description("短视频实战接口文档")
+ .termsOfServiceUrl("http://www.xx.com/")
+ .contact(new Contact("lee", "http://www.imooc.com/", "abc@imooc.com"))
+ .version("1.0")
+ .build())
+ //分组名称
+ .groupName("2.X版本")
+ .select()
+ //这里指定Controller扫描包路径
+ .apis(RequestHandlerSelectors.basePackage("com.imooc.controller"))
+ .paths(PathSelectors.any())
+ .build();
+ return docket;
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/controller/BaseController.java b/book-api/src/main/java/com/imooc/controller/BaseController.java
new file mode 100644
index 0000000..e331bd3
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/BaseController.java
@@ -0,0 +1,15 @@
+package com.imooc.controller;
+
+import com.imooc.utils.RedisOperator;
+import org.springframework.beans.factory.annotation.Autowired;
+
+//public class BaseController {
+//
+// @Autowired
+// public RedisOperator redis;
+//
+// public static final String MOBILE_SMSCODE = "mobile:smscode";
+// public static final String REDIS_USER_TOKEN = "redis_user_token";
+// public static final String REDIS_USER_INFO = "redis_user_info";
+//
+//}
diff --git a/book-api/src/main/java/com/imooc/controller/BaseInfoProperties.java b/book-api/src/main/java/com/imooc/controller/BaseInfoProperties.java
new file mode 100644
index 0000000..bd0bbdc
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/BaseInfoProperties.java
@@ -0,0 +1,46 @@
+//package com.imooc.controller;
+//
+//import com.github.pagehelper.PageInfo;
+//import com.imooc.utils.RedisOperator;
+//import org.springframework.beans.factory.annotation.Autowired;
+//import org.springframework.validation.BindingResult;
+//import org.springframework.validation.FieldError;
+//
+//import java.util.HashMap;
+//import java.util.List;
+//import java.util.Map;
+//
+//public class BaseInfoProperties {
+//
+// @Autowired
+// public RedisOperator redis;
+//
+// public static final Integer COMMON_START_PAGE = 1;
+// public static final Integer COMMON_PAGE_SIZE = 10;
+//
+// public static final String MOBILE_SMSCODE = "mobile:smscode";
+// public static final String REDIS_USER_TOKEN = "redis_user_token";
+// public static final String REDIS_USER_INFO = "redis_user_info";
+//
+//
+// //我的关注总数
+// public static final String REDIS_MY_FOLLOWS_COUNTS = "redis_my_follows_counts";
+// // 我的粉丝总数
+// public static final String REDIS_MY_FANS_COUNTS = "redis_my_fans_counts";
+//
+// // 视频和发布者获赞数
+// public static final String REDIS_VLOG_BE_LIKED_COUNTS = "redis_vlog_be_liked_counts";
+// public static final String REDIS_VLOGER_BE_LIKED_COUNTS = "redis_vloger_be_liked_counts";
+//
+// public PagedGridResult setterPagedGrid(List> list,
+// Integer page) {
+// PageInfo> pageList = new PageInfo<>(list);
+// PagedGridResult gridResult = new PagedGridResult();
+// gridResult.setRows(list);
+// gridResult.setPage(page);
+// gridResult.setRecords(pageList.getTotal());
+// gridResult.setTotal(pageList.getPages());
+// return gridResult;
+// }
+//
+//}
diff --git a/book-api/src/main/java/com/imooc/controller/CommentController.java b/book-api/src/main/java/com/imooc/controller/CommentController.java
new file mode 100644
index 0000000..4210401
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/CommentController.java
@@ -0,0 +1,114 @@
+package com.imooc.controller;
+
+import com.imooc.base.BaseInfoProperties;
+import com.imooc.bo.CommentBO;
+import com.imooc.enums.MessageEnum;
+import com.imooc.grace.result.GraceJSONResult;
+import com.imooc.pojo.Comment;
+import com.imooc.pojo.Vlog;
+import com.imooc.service.CommentService;
+import com.imooc.service.MsgService;
+import com.imooc.service.VlogService;
+import com.imooc.vo.CommentVO;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.HashMap;
+import java.util.Map;
+
+@Slf4j
+@Api(tags = "CommentController 评论模块的接口")
+@RequestMapping("comment")
+@RestController
+public class CommentController extends BaseInfoProperties {
+
+ @Autowired
+ private CommentService commentService;
+ @Autowired
+ private MsgService msgService;
+ @Autowired
+ private VlogService vlogService;
+//
+ @PostMapping("create")
+ public GraceJSONResult create(@RequestBody @Valid CommentBO commentBO)
+ throws Exception {
+
+ CommentVO commentVO = commentService.createComment(commentBO);
+ return GraceJSONResult.ok(commentVO);
+ }
+//
+ @GetMapping("counts")
+ public GraceJSONResult counts(@RequestParam String vlogId) {
+
+ String countsStr = redis.get(REDIS_VLOG_COMMENT_COUNTS + ":" + vlogId);
+ if (StringUtils.isBlank(countsStr)) {
+ countsStr = "0";
+ }
+
+ return GraceJSONResult.ok(Integer.valueOf(countsStr));
+ }
+
+ @GetMapping("list")
+ public GraceJSONResult list(@RequestParam String vlogId,
+ @RequestParam(defaultValue = "") String userId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+
+ return GraceJSONResult.ok(
+ commentService.queryVlogComments(
+ vlogId,
+ userId,
+ page,
+ pageSize));
+ }
+
+ @DeleteMapping("delete")
+ public GraceJSONResult delete(@RequestParam String commentUserId,
+ @RequestParam String commentId,
+ @RequestParam String vlogId) {
+ commentService.deleteComment(commentUserId,
+ commentId,
+ vlogId);
+ return GraceJSONResult.ok();
+ }
+
+ @PostMapping("like")
+ public GraceJSONResult like(@RequestParam String commentId,
+ @RequestParam String userId) {
+
+ // 故意犯错,bigkey
+ redis.incrementHash(REDIS_VLOG_COMMENT_LIKED_COUNTS, commentId, 1);
+ redis.setHashValue(REDIS_USER_LIKE_COMMENT, userId + ":" + commentId, "1");
+// redis.hset(REDIS_USER_LIKE_COMMENT, userId, "1");
+
+
+ // 系统消息:点赞评论
+ Comment comment = commentService.getComment(commentId);
+ Vlog vlog = vlogService.getVlog(comment.getVlogId());
+ Map msgContent = new HashMap();
+ msgContent.put("vlogId", vlog.getId());
+ msgContent.put("vlogCover", vlog.getCover());
+ msgContent.put("commentId", commentId);
+ msgService.createMsg(userId,
+ comment.getCommentUserId(),
+ MessageEnum.LIKE_COMMENT.type,
+ msgContent);
+
+
+ return GraceJSONResult.ok();
+ }
+
+ @PostMapping("unlike")
+ public GraceJSONResult unlike(@RequestParam String commentId,
+ @RequestParam String userId) {
+
+ redis.decrementHash(REDIS_VLOG_COMMENT_LIKED_COUNTS, commentId, 1);
+ redis.hdel(REDIS_USER_LIKE_COMMENT, userId + ":" + commentId);
+
+ return GraceJSONResult.ok();
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/controller/FansController.java b/book-api/src/main/java/com/imooc/controller/FansController.java
new file mode 100644
index 0000000..9604118
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/FansController.java
@@ -0,0 +1,106 @@
+package com.imooc.controller;
+
+import com.imooc.base.BaseInfoProperties;
+import com.imooc.grace.result.GraceJSONResult;
+import com.imooc.grace.result.ResponseStatusEnum;
+import com.imooc.pojo.Users;
+import com.imooc.service.FansService;
+import com.imooc.service.UserService;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@Api(tags = "FansController 粉丝相关业务功能的接口")
+@RequestMapping("fans")
+@RestController
+public class FansController extends BaseInfoProperties {
+
+ @Autowired
+ private UserService userService;
+ @Autowired
+ private FansService fansService;
+
+ @PostMapping("follow")
+ public GraceJSONResult follow(@RequestParam String myId,
+ @RequestParam String vlogerId) {
+
+ // 判断两个id不能为空
+ if (StringUtils.isBlank(myId) || StringUtils.isBlank(vlogerId)) {
+ return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_ERROR);
+ }
+
+ // 判断当前用户,自己不能关注自己
+ if (myId.equalsIgnoreCase(vlogerId)) {
+ return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_RESPONSE_NO_INFO);
+ }
+
+ // 判断两个id对应的用户是否存在
+ Users vloger = userService.getUser(vlogerId);
+ Users myInfo = userService.getUser(myId);
+
+ // fixme: 两个用户id的数据库查询后的判断,是分开好?还是合并判断好?
+ if (myInfo == null || vloger == null) {
+ return GraceJSONResult.errorCustom(ResponseStatusEnum.SYSTEM_RESPONSE_NO_INFO);
+ }
+//
+ // 保存粉丝关系到数据库
+ fansService.doFollow(myId, vlogerId);
+
+ // 博主的粉丝+1,我的关注+1
+ redis.increment(REDIS_MY_FOLLOWS_COUNTS + ":" + myId, 1);
+ redis.increment(REDIS_MY_FANS_COUNTS + ":" + vlogerId, 1);
+
+ // 我和博主的关联关系,依赖redis,不要存储数据库,避免db的性能瓶颈
+ redis.set(REDIS_FANS_AND_VLOGGER_RELATIONSHIP + ":" + myId + ":" + vlogerId, "1");
+//
+ return GraceJSONResult.ok();
+ }
+
+ @PostMapping("cancel")
+ public GraceJSONResult cancel(@RequestParam String myId,
+ @RequestParam String vlogerId) {
+
+ // 删除业务的执行
+ fansService.doCancel(myId, vlogerId);
+
+ // 博主的粉丝-1,我的关注-1
+ redis.decrement(REDIS_MY_FOLLOWS_COUNTS + ":" + myId, 1);
+ redis.decrement(REDIS_MY_FANS_COUNTS + ":" + vlogerId, 1);
+
+ // 我和博主的关联关系,依赖redis,不要存储数据库,避免db的性能瓶颈
+ redis.del(REDIS_FANS_AND_VLOGGER_RELATIONSHIP + ":" + myId + ":" + vlogerId);
+
+ return GraceJSONResult.ok();
+ }
+
+ @GetMapping("queryDoIFollowVloger")
+ public GraceJSONResult queryDoIFollowVloger(@RequestParam String myId,
+ @RequestParam String vlogerId) {
+ return GraceJSONResult.ok(fansService.queryDoIFollowVloger(myId, vlogerId));
+ }
+
+ @GetMapping("queryMyFollows")
+ public GraceJSONResult queryMyFollows(@RequestParam String myId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+ return GraceJSONResult.ok(
+ fansService.queryMyFollows(
+ myId,
+ page,
+ pageSize));
+ }
+
+ @GetMapping("queryMyFans")
+ public GraceJSONResult queryMyFans(@RequestParam String myId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+ return GraceJSONResult.ok(
+ fansService.queryMyFans(
+ myId,
+ page,
+ pageSize));
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/controller/FileController.java b/book-api/src/main/java/com/imooc/controller/FileController.java
new file mode 100644
index 0000000..e808156
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/FileController.java
@@ -0,0 +1,42 @@
+package com.imooc.controller;
+
+
+import com.imooc.config.MinIOConfig;
+import com.imooc.grace.result.GraceJSONResult;
+import com.imooc.utils.MinIOUtils;
+import com.imooc.utils.SMSUtils;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.multipart.MultipartFile;
+
+@Slf4j
+@Api(tags = "FileController 文件上传测试的接口")
+@RestController
+public class FileController {
+
+ @Autowired
+ private MinIOConfig minIOConfig;
+
+ @PostMapping("upload")
+ public GraceJSONResult upload(MultipartFile file) throws Exception {
+
+ String fileName = file.getOriginalFilename();
+
+ MinIOUtils.uploadFile(minIOConfig.getBucketName(),
+ fileName,
+ file.getInputStream());
+
+ String imgUrl = minIOConfig.getFileHost()
+ + "/"
+ + minIOConfig.getBucketName()
+ + "/"
+ + fileName;
+
+ return GraceJSONResult.ok(imgUrl);
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/controller/MsgController.java b/book-api/src/main/java/com/imooc/controller/MsgController.java
new file mode 100644
index 0000000..c684c77
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/MsgController.java
@@ -0,0 +1,43 @@
+package com.imooc.controller;
+
+import com.imooc.base.BaseInfoProperties;
+import com.imooc.grace.result.GraceJSONResult;
+import com.imooc.mo.MessageMO;
+import com.imooc.service.MsgService;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@Slf4j
+@Api(tags = "MsgController 消息功能模块的接口")
+@RequestMapping("msg")
+@RestController
+public class MsgController extends BaseInfoProperties {
+
+ @Autowired
+ private MsgService msgService;
+
+ @GetMapping("list")
+ public GraceJSONResult list(@RequestParam String userId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+
+ // mongodb 从0分页,区别于数据库
+ if (page == null) {
+ page = COMMON_START_PAGE_ZERO;
+ }
+ if (pageSize == null) {
+ pageSize = COMMON_PAGE_SIZE;
+ }
+
+ List list = msgService.queryList(userId, page, pageSize);
+
+ return GraceJSONResult.ok(list);
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/controller/PassportController.java b/book-api/src/main/java/com/imooc/controller/PassportController.java
new file mode 100644
index 0000000..9267618
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/PassportController.java
@@ -0,0 +1,95 @@
+package com.imooc.controller;
+
+import com.imooc.base.BaseInfoProperties;
+import com.imooc.bo.RegistLoginBO;
+import com.imooc.grace.result.GraceJSONResult;
+import com.imooc.grace.result.ResponseStatusEnum;
+import com.imooc.pojo.Users;
+import com.imooc.service.UserService;
+import com.imooc.utils.IPUtil;
+import com.imooc.utils.SMSUtils;
+import com.imooc.vo.UsersVO;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.validation.Valid;
+import java.util.UUID;
+
+/**
+ * @author vercen
+ * @version 1.0
+ * @date 2023/5/25 10:47
+ */
+@Slf4j
+@RestController
+@RequestMapping("passport")
+@Api(tags = "通行证,验证码登录注册")
+public class PassportController extends BaseInfoProperties {
+ @Autowired
+ SMSUtils smsUtils;
+ @Autowired
+ UserService userService;
+ @PostMapping("getSMSCode")
+ public Object getSMSCode(@RequestParam String mobile, HttpServletRequest request) throws Exception {
+ if (StringUtils.isBlank(mobile)){
+ return GraceJSONResult.ok();
+ }
+
+ //TODO 获得用户ip 限制时间60s只能1次
+ String userIp=IPUtil.getRequestIp(request);
+
+ redis.setnx60s(MOBILE_SMSCODE+":"+userIp, userIp);
+ String code=(int)((Math.random()*9+1)*100000)+"";
+
+ smsUtils.sendSMS(mobile,code);
+
+ log.info(code);
+ redis.set(MOBILE_SMSCODE+":"+mobile, code,30*60);
+ //TODO 验证码放入redis
+ return GraceJSONResult.ok();
+ }
+
+ @PostMapping("login")
+ public Object login(@Valid @RequestBody RegistLoginBO registLoginBO){
+
+ String rediscode = redis.get(MOBILE_SMSCODE + ":" + registLoginBO.getMobile());
+
+ if (StringUtils.isBlank(rediscode)|| !rediscode.equalsIgnoreCase(registLoginBO.getSmsCode())){
+ System.out.println("rediscode"+rediscode);
+ System.out.println("registLoginBO.getMobile()"+registLoginBO.getSmsCode());
+ return GraceJSONResult.errorCustom(ResponseStatusEnum.SMS_CODE_ERROR);
+ }
+
+ Users user = userService.queryMobileIsExist(registLoginBO.getMobile());
+ if (user==null){
+ user = userService.createUser(registLoginBO.getMobile());
+ }
+ String uToken = UUID.randomUUID().toString();
+ redis.set(REDIS_USER_TOKEN+":"+user.getId(),uToken);
+
+ //清除验证码
+ redis.del(MOBILE_SMSCODE+":"+user.getMobile());
+
+ //返回给前端
+ UsersVO usersVO = new UsersVO();
+ BeanUtils.copyProperties(user, usersVO);
+ usersVO.setUserToken(uToken);
+
+ return GraceJSONResult.ok(usersVO);
+
+ }
+
+ @PostMapping("logout")
+ public Object logout(@RequestParam String userId){
+
+ redis.del(REDIS_USER_TOKEN+":"+userId);
+
+ return GraceJSONResult.ok();
+ }
+
+}
diff --git a/book-api/src/main/java/com/imooc/controller/RabbitMQConsumer.java b/book-api/src/main/java/com/imooc/controller/RabbitMQConsumer.java
new file mode 100644
index 0000000..b28bd38
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/RabbitMQConsumer.java
@@ -0,0 +1,66 @@
+package com.imooc.controller;
+
+import com.imooc.base.RabbitMQConfig;
+import com.imooc.enums.MessageEnum;
+import com.imooc.exceptions.GraceException;
+import com.imooc.grace.result.ResponseStatusEnum;
+import com.imooc.mo.MessageMO;
+import com.imooc.service.MsgService;
+import com.imooc.utils.JsonUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class RabbitMQConsumer {
+
+ @Autowired
+ private MsgService msgService;
+
+ @RabbitListener(queues = {RabbitMQConfig.QUEUE_SYS_MSG})
+ public void watchQueue(String payload, Message message) {
+ log.info(payload);
+
+ MessageMO messageMO = JsonUtils.jsonToPojo(payload, MessageMO.class);
+
+ String routingKey = message.getMessageProperties().getReceivedRoutingKey();
+ log.info(routingKey);
+
+ // TODO: 下面这段代码可以优化,一个地方是参数优化,另外是枚举的判断优化
+
+ if (routingKey.equalsIgnoreCase("sys.msg." + MessageEnum.FOLLOW_YOU.enValue)) {
+ msgService.createMsg(messageMO.getFromUserId(),
+ messageMO.getToUserId(),
+ MessageEnum.FOLLOW_YOU.type,
+ null);
+ } else if (routingKey.equalsIgnoreCase("sys.msg." + MessageEnum.LIKE_VLOG.enValue)) {
+ msgService.createMsg(messageMO.getFromUserId(),
+ messageMO.getToUserId(),
+ MessageEnum.FOLLOW_YOU.type,
+ messageMO.getMsgContent());
+ } else if (routingKey.equalsIgnoreCase("sys.msg." + MessageEnum.COMMENT_VLOG.enValue)) {
+ msgService.createMsg(messageMO.getFromUserId(),
+ messageMO.getToUserId(),
+ MessageEnum.COMMENT_VLOG.type,
+ messageMO.getMsgContent());
+ } else if (routingKey.equalsIgnoreCase("sys.msg." + MessageEnum.REPLY_YOU.enValue)) {
+ msgService.createMsg(messageMO.getFromUserId(),
+ messageMO.getToUserId(),
+ MessageEnum.REPLY_YOU.type,
+ messageMO.getMsgContent());
+ } else if (routingKey.equalsIgnoreCase("sys.msg." + MessageEnum.LIKE_COMMENT.enValue)) {
+ msgService.createMsg(messageMO.getFromUserId(),
+ messageMO.getToUserId(),
+ MessageEnum.LIKE_COMMENT.type,
+ messageMO.getMsgContent());
+ } else {
+ GraceException.display(ResponseStatusEnum.SYSTEM_OPERATION_ERROR);
+ }
+
+ }
+
+
+}
diff --git a/book-api/src/main/java/com/imooc/controller/UserInfoController.java b/book-api/src/main/java/com/imooc/controller/UserInfoController.java
new file mode 100644
index 0000000..8b68295
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/UserInfoController.java
@@ -0,0 +1,136 @@
+package com.imooc.controller;
+
+import com.imooc.base.BaseInfoProperties;
+import com.imooc.bo.UpdatedUserBO;
+import com.imooc.config.MinIOConfig;
+import com.imooc.enums.FileTypeEnum;
+import com.imooc.enums.UserInfoModifyType;
+import com.imooc.grace.result.GraceJSONResult;
+import com.imooc.grace.result.ResponseStatusEnum;
+import com.imooc.pojo.Users;
+import com.imooc.service.UserService;
+import com.imooc.utils.MinIOUtils;
+import com.imooc.utils.SMSUtils;
+import com.imooc.vo.UsersVO;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+/**
+ * @author vercen
+ * @version 1.0
+ * @date 2023/5/20 14:32
+ */
+@RestController
+@Slf4j
+@Api(tags = "UserInfoController用户信息接口模块")
+@RequestMapping("userInfo")
+public class UserInfoController extends BaseInfoProperties {
+
+ @Autowired
+ UserService userService;
+
+// @ResponseBody
+ @ApiOperation(value = "根据userId返回个人信息")
+ @GetMapping("query")
+ public Object query(@RequestParam String userId){
+ Users user = userService.getUser(userId);
+
+ UsersVO usersVO = new UsersVO();
+ BeanUtils.copyProperties(user, usersVO);
+
+
+ // 我的关注博主总数量
+ String myFollowsCountsStr = redis.get(REDIS_MY_FOLLOWS_COUNTS + ":" + userId);
+ // 我的粉丝总数
+ String myFansCountsStr = redis.get(REDIS_MY_FANS_COUNTS + ":" + userId);
+ // 用户获赞总数,视频博主(点赞/喜欢)总和
+// String likedVlogCountsStr = redis.get(REDIS_VLOG_BE_LIKED_COUNTS + ":" + userId);
+ String likedVlogerCountsStr = redis.get(REDIS_VLOGER_BE_LIKED_COUNTS + ":" + userId);
+
+ Integer myFollowsCounts = 0;
+ Integer myFansCounts = 0;
+ Integer likedVlogCounts = 0;
+ Integer likedVlogerCounts = 0;
+ Integer totalLikeMeCounts = 0;
+
+
+ if (StringUtils.isNotBlank(myFollowsCountsStr)) {
+ myFollowsCounts = Integer.valueOf(myFollowsCountsStr);
+ }
+ if (StringUtils.isNotBlank(myFansCountsStr)) {
+ myFansCounts = Integer.valueOf(myFansCountsStr);
+ }
+// if (StringUtils.isNotBlank(likedVlogCountsStr)) {
+// likedVlogCounts = Integer.valueOf(likedVlogCountsStr);
+// }
+ if (StringUtils.isNotBlank(likedVlogerCountsStr)) {
+ likedVlogerCounts = Integer.valueOf(likedVlogerCountsStr);
+ }
+ totalLikeMeCounts = likedVlogCounts + likedVlogerCounts;
+
+ usersVO.setMyFollowsCounts(myFollowsCounts);
+ usersVO.setMyFansCounts(myFansCounts);
+ usersVO.setTotalLikeMeCounts(totalLikeMeCounts);
+
+ //usersVO.setMyFansCounts((Integer) myFansCounts);
+
+ return GraceJSONResult.ok(usersVO);
+ }
+
+ @PostMapping("modifyUserInfo")
+ public GraceJSONResult modifyUserInfo(@RequestBody UpdatedUserBO updatedUserBO, @RequestParam Integer type) throws Exception {
+
+ UserInfoModifyType.checkUserInfoTypeIsRight(type);
+ Users newUserInfo = userService.updateUserInfo(updatedUserBO, type);
+ return GraceJSONResult.ok(newUserInfo);
+ }
+
+ @Autowired
+ private MinIOConfig minIOConfig;
+
+ @PostMapping("modifyImage")
+ public GraceJSONResult modifyImage(@RequestParam String userId,
+ @RequestParam Integer type,
+ MultipartFile file) throws Exception {
+
+ if (type != FileTypeEnum.BGIMG.type && type != FileTypeEnum.FACE.type) {
+ return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_UPLOAD_FAILD);
+ }
+
+ String fileName = file.getOriginalFilename();
+
+ MinIOUtils.uploadFile(minIOConfig.getBucketName(),
+ fileName,
+ file.getInputStream());
+
+ String imgUrl = minIOConfig.getFileHost()
+ + "/"
+ + minIOConfig.getBucketName()
+ + "/"
+ + fileName;
+
+
+ // 修改图片地址到数据库
+ UpdatedUserBO updatedUserBO = new UpdatedUserBO();
+ updatedUserBO.setId(userId);
+
+ if (type == FileTypeEnum.BGIMG.type) {
+ updatedUserBO.setBgImg(imgUrl);
+ } else {
+ updatedUserBO.setFace(imgUrl);
+ }
+ Users users = userService.updateUserInfo(updatedUserBO);
+
+ return GraceJSONResult.ok(users);
+ }
+
+
+
+}
diff --git a/book-api/src/main/java/com/imooc/controller/VlogController.java b/book-api/src/main/java/com/imooc/controller/VlogController.java
new file mode 100644
index 0000000..b20673b
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/controller/VlogController.java
@@ -0,0 +1,225 @@
+package com.imooc.controller;
+
+import com.imooc.base.BaseInfoProperties;
+import com.imooc.bo.VlogBO;
+import com.imooc.enums.YesOrNo;
+import com.imooc.grace.result.GraceJSONResult;
+import com.imooc.service.VlogService;
+import com.imooc.utils.PagedGridResult;
+import io.swagger.annotations.Api;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cloud.context.config.annotation.RefreshScope;
+import org.springframework.web.bind.annotation.*;
+
+@Slf4j
+@Api(tags = "VlogController 短视频相关业务功能的接口")
+@RequestMapping("vlog")
+@RestController
+@RefreshScope
+public class VlogController extends BaseInfoProperties {
+
+ @Autowired
+ private VlogService vlogService;
+
+ @PostMapping("publish")
+ public GraceJSONResult publish(@RequestBody VlogBO vlogBO) {
+ // FIXME 作业,校验VlogBO
+ vlogService.createVlog(vlogBO);
+ return GraceJSONResult.ok();
+ }
+
+ @GetMapping("indexList")
+ public GraceJSONResult indexList(@RequestParam(defaultValue = "") String userId,
+ @RequestParam(defaultValue = "") String search,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+
+ if (page == null) {
+ page = COMMON_START_PAGE;
+ }
+ if (pageSize == null) {
+ pageSize = COMMON_PAGE_SIZE;
+ }
+
+ PagedGridResult gridResult = vlogService.getIndexVlogList(userId, search, page, pageSize);
+ return GraceJSONResult.ok(gridResult);
+ }
+
+ @GetMapping("detail")
+ public GraceJSONResult detail(@RequestParam(defaultValue = "") String userId,
+ @RequestParam String vlogId) {
+ return GraceJSONResult.ok(vlogService.getVlogDetailById(userId, vlogId));
+ }
+
+
+ @PostMapping("changeToPrivate")
+ public GraceJSONResult changeToPrivate(@RequestParam String userId,
+ @RequestParam String vlogId) {
+ vlogService.changeToPrivateOrPublic(userId,
+ vlogId,
+ YesOrNo.YES.type);
+ return GraceJSONResult.ok();
+ }
+
+ @PostMapping("changeToPublic")
+ public GraceJSONResult changeToPublic(@RequestParam String userId,
+ @RequestParam String vlogId) {
+ vlogService.changeToPrivateOrPublic(userId,
+ vlogId,
+ YesOrNo.NO.type);
+ return GraceJSONResult.ok();
+ }
+
+
+
+ @GetMapping("myPublicList")
+ public GraceJSONResult myPublicList(@RequestParam String userId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+
+ if (page == null) {
+ page = COMMON_START_PAGE;
+ }
+ if (pageSize == null) {
+ pageSize = COMMON_PAGE_SIZE;
+ }
+
+ PagedGridResult gridResult = vlogService.queryMyVlogList(userId,
+ page,
+ pageSize,
+ YesOrNo.NO.type);
+ return GraceJSONResult.ok(gridResult);
+ }
+
+ @GetMapping("myPrivateList")
+ public GraceJSONResult myPrivateList(@RequestParam String userId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+
+ if (page == null) {
+ page = COMMON_START_PAGE;
+ }
+ if (pageSize == null) {
+ pageSize = COMMON_PAGE_SIZE;
+ }
+
+ PagedGridResult gridResult = vlogService.queryMyVlogList(userId,
+ page,
+ pageSize,
+ YesOrNo.YES.type);
+ return GraceJSONResult.ok(gridResult);
+ }
+
+ @GetMapping("myLikedList")
+ public GraceJSONResult myLikedList(@RequestParam String userId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+
+ if (page == null) {
+ page = COMMON_START_PAGE;
+ }
+ if (pageSize == null) {
+ pageSize = COMMON_PAGE_SIZE;
+ }
+
+ PagedGridResult gridResult = vlogService.getMyLikedVlogList(userId,
+ page,
+ pageSize);
+ return GraceJSONResult.ok(gridResult);
+ }
+
+ @Value(("${nacos.counts}"))
+ private Integer nacosConuts;
+
+
+ @PostMapping("like")
+ public GraceJSONResult like(@RequestParam String userId,
+ @RequestParam String vlogerId,
+ @RequestParam String vlogId) {
+
+ // 我点赞的视频,关联关系保存到数据库
+ vlogService.userLikeVlog(userId, vlogId);
+
+ // 点赞后,视频和视频发布者的获赞都会 +1
+ redis.increment(REDIS_VLOGER_BE_LIKED_COUNTS + ":" + vlogerId, 1);
+ redis.increment(REDIS_VLOG_BE_LIKED_COUNTS + ":" + vlogId, 1);
+
+ // 我点赞的视频,需要在redis中保存关联关系
+ redis.set(REDIS_USER_LIKE_VLOG + ":" + userId + ":" + vlogId, "1");
+
+ log.info("nacosConuts="+nacosConuts);
+
+ String countsStr = redis.get(REDIS_VLOG_BE_LIKED_COUNTS + ":" + vlogId);
+ Integer counts=0;
+ if (StringUtils.isNotBlank(countsStr)){
+
+ counts=Integer.valueOf(countsStr);
+ if (counts>=nacosConuts){
+
+ vlogService.flushCounts(vlogId, counts);
+ }
+ }
+
+ return GraceJSONResult.ok();
+ }
+
+
+ @PostMapping("unlike")
+ public GraceJSONResult unlike(@RequestParam String userId,
+ @RequestParam String vlogerId,
+ @RequestParam String vlogId) {
+
+ // 我取消点赞的视频,关联关系删除
+ vlogService.userUnLikeVlog(userId, vlogId);
+
+ redis.decrement(REDIS_VLOGER_BE_LIKED_COUNTS + ":" + vlogerId, 1);
+ redis.decrement(REDIS_VLOG_BE_LIKED_COUNTS + ":" + vlogId, 1);
+ redis.del(REDIS_USER_LIKE_VLOG + ":" + userId + ":" + vlogId);
+
+ return GraceJSONResult.ok();
+ }
+
+ @PostMapping("totalLikedCounts")
+ public GraceJSONResult totalLikedCounts(@RequestParam String vlogId) {
+ return GraceJSONResult.ok(vlogService.getVlogBeLikedCounts(vlogId));
+ }
+
+ @GetMapping("followList")
+ public GraceJSONResult followList(@RequestParam String myId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+
+ if (page == null) {
+ page = COMMON_START_PAGE;
+ }
+ if (pageSize == null) {
+ pageSize = COMMON_PAGE_SIZE;
+ }
+
+ PagedGridResult gridResult = vlogService.getMyFollowVlogList(myId,
+ page,
+ pageSize);
+ return GraceJSONResult.ok(gridResult);
+ }
+
+ @GetMapping("friendList")
+ public GraceJSONResult friendList(@RequestParam String myId,
+ @RequestParam Integer page,
+ @RequestParam Integer pageSize) {
+
+ if (page == null) {
+ page = COMMON_START_PAGE;
+ }
+ if (pageSize == null) {
+ pageSize = COMMON_PAGE_SIZE;
+ }
+
+ PagedGridResult gridResult = vlogService.getMyFriendVlogList(myId,
+ page,
+ pageSize);
+ return GraceJSONResult.ok(gridResult);
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/intercepter/PassportInterceptor.java b/book-api/src/main/java/com/imooc/intercepter/PassportInterceptor.java
new file mode 100644
index 0000000..cdc0d12
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/intercepter/PassportInterceptor.java
@@ -0,0 +1,47 @@
+package com.imooc.intercepter;
+
+import com.imooc.base.BaseInfoProperties;
+import com.imooc.exceptions.GraceException;
+import com.imooc.grace.result.ResponseStatusEnum;
+import com.imooc.utils.IPUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Slf4j
+public class PassportInterceptor extends BaseInfoProperties implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest request,
+ HttpServletResponse response, Object handler) throws Exception {
+
+ // 获得用户的ip
+ String userIp = IPUtil.getRequestIp(request);
+
+ // 得到是否存在的判断
+ boolean keyIsExist = redis.keyIsExist(MOBILE_SMSCODE + ":" + userIp);
+
+ if (keyIsExist) {
+ GraceException.display(ResponseStatusEnum.SMS_NEED_WAIT_ERROR);
+ log.info("短信发送频率太大!");
+ return false;
+ }
+
+ /**
+ * true: 请求放行
+ * false: 请求拦截
+ */
+ return true;
+ }
+
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ }
+}
diff --git a/book-api/src/main/java/com/imooc/intercepter/UserTokenInterceptor.java b/book-api/src/main/java/com/imooc/intercepter/UserTokenInterceptor.java
new file mode 100644
index 0000000..e88aff1
--- /dev/null
+++ b/book-api/src/main/java/com/imooc/intercepter/UserTokenInterceptor.java
@@ -0,0 +1,58 @@
+package com.imooc.intercepter;
+
+import com.imooc.base.BaseInfoProperties;
+import com.imooc.exceptions.GraceException;
+import com.imooc.grace.result.ResponseStatusEnum;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@Slf4j
+public class UserTokenInterceptor extends BaseInfoProperties implements HandlerInterceptor {
+
+ @Override
+ public boolean preHandle(HttpServletRequest request,
+ HttpServletResponse response, Object handler) throws Exception {
+
+
+ // 从header中获得用户id和token
+ String userId = request.getHeader("headerUserId");
+ String userToken = request.getHeader("headerUserToken");
+
+ // 判断header中用户id和token不能为空
+ if (StringUtils.isNotBlank(userId) && StringUtils.isNotBlank(userToken)) {
+ String redisToken = redis.get(REDIS_USER_TOKEN + ":" + userId);
+ if (StringUtils.isBlank(redisToken)) {
+ GraceException.display(ResponseStatusEnum.UN_LOGIN);
+ return false;
+ } else {
+ // 比较token是否一致,如果不一致,表示用户在别的手机端登录
+ if (!redisToken.equalsIgnoreCase(userToken)) {
+ GraceException.display(ResponseStatusEnum.TICKET_INVALID);
+ return false;
+ }
+ }
+ } else {
+ GraceException.display(ResponseStatusEnum.UN_LOGIN);
+ return false;
+ }
+
+ /**
+ * true: 请求放行
+ * false: 请求拦截
+ */
+ return true;
+ }
+
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ }
+}
diff --git a/book-api/src/main/resources/application-dev.yml b/book-api/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..08a51ec
--- /dev/null
+++ b/book-api/src/main/resources/application-dev.yml
@@ -0,0 +1,65 @@
+server:
+ port: ${port:8099}
+
+spring:
+ datasource: # 数据源的相关配置
+ type: com.zaxxer.hikari.HikariDataSource # 数据源的类型,可以更改为其他的数据源配置,比如druid
+ driver-class-name: com.mysql.cj.jdbc.Driver # mysql/MariaDB 的数据库驱动类名称
+ url: jdbc:mysql://182.92.182.217:3306/tiktok?characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
+ username: tiktok
+ password: Bc3T5AA2pBdt2FBk
+ hikari:
+ connection-timeout: 30000 # 等待连接池分配连接的最大时间(毫秒),超过这个时长还没有可用的连接,则会抛出SQLException
+ minimum-idle: 5 # 最小连接数
+ maximum-pool-size: 20 # 最大连接数
+ auto-commit: true # 自动提交
+ idle-timeout: 600000 # 连接超时的最大时长(毫秒),超时则会被释放(retired)
+ pool-name: DataSourceHikariCP # 连接池的名字
+ max-lifetime: 18000000 # 连接池的最大生命时长(毫秒),超时则会被释放(retired)
+ connection-test-query: SELECT 1
+ redis:
+ host: 182.92.182.217
+ port: 6379
+ password: '!aw5)lJf'
+ database: 1 # 使用的数据库编号
+ jedis:
+ pool:
+ max-idle: 6 # 最大空闲连接
+ max-active: 32 # 连接池最大连接数
+ max-wait: 100 # 连接池最大阻塞等待时间, -1表示没有限制
+ min-idle: 4 # 最小空闲连接
+ timeout: 50000
+ data:
+ mongodb:
+# uri: mongodb://root:root@192.168.1.202:27017
+# database: imooc-red-book
+ uri: mongodb://admin:A1969bf8@82.156.121.2:37017/admin
+ rabbitmq:
+ host: 182.92.182.217
+ port: 5672
+ username: admin
+ password: 123123
+ virtual-host: imooc-red-book
+ application:
+ name: imooc-red-book-nacos
+ cloud:
+ nacos:
+ discovery:
+ server-addr: 182.92.182.217:8848 # nacos 所在的地址
+
+# 打开监控
+management:
+ endpoint:
+ web:
+ exposure:
+ include: '*'
+
+# MinIO 配置
+minio:
+ endpoint: http://82.156.121.2:29000 # MinIO服务地址
+ fileHost: http://82.156.121.2:29000 # 文件地址host
+ bucketName: bucket # 存储桶bucket名称
+ accessKey: NJ2GN2wLVkMnyrznbE9t # 用户名
+ secretKey: nrT3e3pRWF5HXQRiIxxDhq20eJ0YrN8lvx5p8bK6 # 密码
+ imgSize: 1024 # 图片大小限制,单位:m
+ fileSize: 1024 # 文件大小限制,单位:m
diff --git a/book-api/src/main/resources/application-prod.yml b/book-api/src/main/resources/application-prod.yml
new file mode 100644
index 0000000..20378e0
--- /dev/null
+++ b/book-api/src/main/resources/application-prod.yml
@@ -0,0 +1,65 @@
+server:
+ port: 8080
+
+spring:
+ datasource: # 数据源的相关配置
+ type: com.zaxxer.hikari.HikariDataSource # 数据源的类型,可以更改为其他的数据源配置,比如druid
+ driver-class-name: com.mysql.cj.jdbc.Driver # mysql/MariaDB 的数据库驱动类名称
+ url: jdbc:mysql://182.92.182.217/tiktok?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true
+ username: tiktok
+ password: Bc3T5AA2pBdt2FBk
+ hikari:
+ connection-timeout: 30000 # 等待连接池分配连接的最大时间(毫秒),超过这个时长还没有可用的连接,则会抛出SQLException
+ minimum-idle: 5 # 最小连接数
+ maximum-pool-size: 20 # 最大连接数
+ auto-commit: true # 自动提交
+ idle-timeout: 600000 # 连接超时的最大时长(毫秒),超时则会被释放(retired)
+ pool-name: DataSourceHikariCP # 连接池的名字
+ max-lifetime: 18000000 # 连接池的最大生命时长(毫秒),超时则会被释放(retired)
+ connection-test-query: SELECT 1
+ redis:
+ host: 182.92.182.217
+ port: 6379
+ password: '!aw5)lJf'
+ database: 1 # 使用的数据库编号
+ jedis:
+ pool:
+ max-idle: 6 # 最大空闲连接
+ max-active: 32 # 连接池最大连接数
+ max-wait: 100 # 连接池最大阻塞等待时间, -1表示没有限制
+ min-idle: 4 # 最小空闲连接
+ timeout: 50000
+ data:
+ mongodb:
+ uri: mongodb://admin:A1969bf8@82.156.121.2:37017/admin
+ rabbitmq:
+ host: 182.92.182.217
+ port: 5672
+ username: admin
+ password: 123123
+ virtual-host: imooc-red-book
+ application:
+ name: imooc-red-book-nacos
+ cloud:
+ nacos:
+ discovery:
+ server-addr: 182.92.182.217:8848 # nacos 所在的地址
+
+
+# 打开监控
+management:
+ endpoint:
+ web:
+ exposure:
+ include: '*'
+
+
+# MinIO 配置
+minio:
+ endpoint: http://82.156.121.2:29000 # MinIO服务地址
+ fileHost: http://82.156.121.2:29000 # 文件地址host
+ bucketName: bucket # 存储桶bucket名称
+ accessKey: NJ2GN2wLVkMnyrznbE9t # 用户名
+ secretKey: nrT3e3pRWF5HXQRiIxxDhq20eJ0YrN8lvx5p8bK6 # 密码
+ imgSize: 1024 # 图片大小限制,单位:m
+ fileSize: 1024 # 文件大小限制,单位:m
\ No newline at end of file
diff --git a/book-api/src/main/resources/application.yml b/book-api/src/main/resources/application.yml
new file mode 100644
index 0000000..bd6daaf
--- /dev/null
+++ b/book-api/src/main/resources/application.yml
@@ -0,0 +1,43 @@
+server:
+# port: 8099
+ tomcat:
+ uri-encoding: UTF-8
+ max-swallow-size: -1 # tomcat默认大小2M,超过2M的文件不会被捕获,需要调整此处大小为100MB或者-1即可
+
+spring:
+ application:
+ name: imooc-red-book-nacos
+ profiles:
+ active: dev
+ nacos:
+ counts: 10
+ banner:
+ location: classpath:banner/banner.txt
+ servlet:
+ multipart:
+ max-file-size: 1024MB # 文件上传大小限制,设置最大值,不能超过该值,否则报错
+# max-file-size: 500KB # 文件上传大小限制,设置最大值,不能超过该值,否则报错
+ max-request-size: 1024MB # 文件最大请求限制,用于批量上传
+# max-request-size: 500KB
+nacos:
+ counts: 10
+# 整合mybatis
+mybatis:
+ type-aliases-package: com.imooc.pojo # 所有pojo类所在的包路径
+ mapper-locations: classpath:mapper/*.xml # mapper映射文件
+
+# 通用mapper工具的配置
+mapper:
+ mappers: com.imooc.my.mapper.MyMapper # 配置MyMapper,包含了一些封装好的CRUD方法
+ not-empty: false # 在进行数据库操作的时候,username != null 是否会追加 username != ''
+ identity: MYSQL
+
+# 分页插件助手的配置
+pagehelper:
+ helper-dialect: MYSQL
+ support-methods-arguments: true
+
+# 日志级别
+logging:
+ level:
+ root: info
diff --git a/book-api/src/main/resources/banner/banner.txt b/book-api/src/main/resources/banner/banner.txt
new file mode 100644
index 0000000..3d94420
--- /dev/null
+++ b/book-api/src/main/resources/banner/banner.txt
@@ -0,0 +1,22 @@
+////////////////////////////////////////////////////////////////////
+// _ooOoo_ //
+// o8888888o //
+// 88" . "88 //
+// (| ^_^ |) //
+// O\ = /O //
+// ____/`---'\____ //
+// .' \\| |// `. //
+// / \\||| : |||// \ //
+// / _||||| -:- |||||- \ //
+// | | \\\ - /// | | //
+// | \_| ''\---/'' | | //
+// \ .-\__ `-` ___/-. / //
+// ___`. .' /--.--\ `. . ___ //
+// ."" '< `.___\_<|>_/___.' >'"". //
+// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
+// \ \ `-. \_ __\ /__ _/ .-` / / //
+// ========`-.____`-.___\_____/___.-`____.-'======== //
+// `=---=' //
+// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
+// 佛祖保佑 永无BUG 永不修改 //
+////////////////////////////////////////////////////////////////////
\ No newline at end of file
diff --git a/book-api/src/main/resources/banner/cat.png b/book-api/src/main/resources/banner/cat.png
new file mode 100644
index 0000000..f9cece4
Binary files /dev/null and b/book-api/src/main/resources/banner/cat.png differ
diff --git a/book-api/src/main/resources/banner/qiaoba.txt b/book-api/src/main/resources/banner/qiaoba.txt
new file mode 100644
index 0000000..16727a1
--- /dev/null
+++ b/book-api/src/main/resources/banner/qiaoba.txt
@@ -0,0 +1,57 @@
+tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttjtttjttjjtjW#Li;;ijE#Ettttjttjjjtttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttjtttLGtWtttt#i;itttttttjti;tWtttjWtDEjtttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttjjjttfjLjWtEjjtjtjttttttjjtjttLWt;jEjjttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttDf;tjt;jWjjfLtttttttttttttttttttLDjjjjEtjj#ELttttttttttttttttttttttttttttt
+tttttttttttttttttttttttt#DjGjfjtjjtjjfLttttttttttttttttttjL#jjjjtttjtfLGfjtttttttttttttttttttttttttt
+tttttttttttttttttttttttttjfLjGttWjjjjLLtttttttttttttttttttLEjjjjjtttjjjjDjtttttttttttttttttttttttttt
+tttttttttttttttttttttttttjjjjDttLjjjjLLttttttttttttttttttjLLjLjjjttijjjjLjtttttttttttttttttttttttttt
+tttttttttttttttttttttttttjjjjjtttjjjfLLtttttttWEttf jtttttLDjLjjjjttEjfjLttttttttttttttttttttttttttt
+ttttttttttttttttttttttttijjjjjtjDjjjjLLjttttjG GL :jttttLWjjjjttKjWjKjKttttttttttttttttttttttttttt
+ttttttttttttttttttttttttijjjWjGjfjLjjLLjttttt,. ftttjLGjjtttj;jjj#jKjtttttttttttttttttttttttttt
+ttttttttttttttttttttttttEj#ffjjjfjjjWfLfjtttttf KtttttfLL;jjffjjLjjjjjttttttttttttttttttttttttttt
+tttttttttttttttttttttttttjjtjjjjjfLj#fLftttttjf tttttLLL#fjfjjjfjKjfttjttttttttttttttttttttttttt
+tttttttttttttttttttttttttDjjjjjjLjGj#LLLttttjD . . .jtttLLLWGGffjjjGjtjtttttttttttttttttttttttttttt
+tttttttttttttttttttttttttj;jjjjGLLDt#LLLtjjtjK Ej jtttLLLWjKLLLjjjjWttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttjGLLLLLLWKGLLttjtEWL;;;fKKjtttfWEWDLfLLLGWttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttfLLLfLGLD,,;ttjtttttti;;#LGLLLL#jttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttD::.#G;;jtjjjtttttjjtjtti;W:,:Wtttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttt;EjW,jtjjjjttjjttjtjtjjjjtj,Lj;jttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttE;;tjjtjjjKLLLLLLLLLGKjtjtjLLiWjttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttf,tjjttWLLLfLLLLLLLLfLfLKjtLLLfjttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttj,jjttEfLLLLLLLLLLLLLLLLLLLELLLLLttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttjittjtfLLLLLLLLfDEKDLfLfLfLfLLfLLLWtttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttjjffLLLLEGfjjjfjjjjjjGjKLLLfLfLLftjtttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttjjjLLLLKfjj;j;;;;;;;;,;jfjWLLLfLfLtttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttKLLLKf;ifj;i;;;;;;;j.E;;fLLLLGLLtttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttjtjLLLEG,;DWKWi;;;;;;;WWKE;;LfLLLLGtttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttWELLLLL;; WKKD;;;;;;jKKK.;;LLLLLWftttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttffLLLL:;EWKW,,;Di,;;KWKf;;fLLLLLjtttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttWLLLLLE;,E.;;;;;,;;;;tDi;#LLLLfDttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttKLLLLL;;;;;;;;tK,L;;;;;;LLLLfLtttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttjLLLLW,;;;;;EEED;;;;;;WLLLDtttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttjtKEL,;;;;;EjjW;;;;;KtEGjjjttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttjtttttW,,;;jttj,i;tWKffKfjtttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttDLjttfGWtjti,;Kjttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttDjittfj;,;;D;;;;ttttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttttttttttE;;EEjWi;DGKj,iEjtjttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttt;,;;WWt;;;Dtttfttttttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttttttttt;t;;;;;;;;;;Gtttttttttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttttttjj,K;;;;;;;;;;;;jttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttjf;;j;;;;;;;;;;;;tttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttftGKE;;,;,,;;;;;ijttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttEfijDGGGGGGGGGGGKjttttttttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttttttfjtGGGGGGGGGGEDWtttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttfEEDEEEEEEEKttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttLDEWWEEEttttttttttttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttttttttjtttKWjtKWjttttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttttEjtjjijtttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttK,Gttf,fDtttttttttttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttttttttttWjD;tt;,fWtttttttttttttttttttttttttttttttttttttttttttttt
+ttttttttttttttttttttttttttttttttttttttttttttttjttttttttttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
+tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
\ No newline at end of file
diff --git a/book-api/src/main/resources/bootstrap.yml b/book-api/src/main/resources/bootstrap.yml
new file mode 100644
index 0000000..ccb2172
--- /dev/null
+++ b/book-api/src/main/resources/bootstrap.yml
@@ -0,0 +1,8 @@
+spring:
+ application:
+ name: imooc-red-book-nacos
+ cloud:
+ nacos:
+ config:
+ server-addr: 182.92.182.217:8848
+ file-extension: yaml
\ No newline at end of file
diff --git a/book-common/pom.xml b/book-common/pom.xml
new file mode 100644
index 0000000..c00384f
--- /dev/null
+++ b/book-common/pom.xml
@@ -0,0 +1,116 @@
+
+
+ 4.0.0
+
+ com.imooc
+ imooc-red-book-dev
+ 1.0-SNAPSHOT
+
+
+ book-common
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-amqp
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+
+
+ com.fasterxml.jackson.core
+ jackson-annotations
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+ commons-codec
+ commons-codec
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ commons-fileupload
+ commons-fileupload
+
+
+
+ com.google.guava
+ guava
+
+
+
+ joda-time
+ joda-time
+
+
+
+
+ com.tencentcloudapi
+ tencentcloud-sdk-java
+ 3.1.270
+
+
+
+ io.minio
+ minio
+ 8.2.1
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/book-common/src/main/java/com/imooc/enums/FileTypeEnum.java b/book-common/src/main/java/com/imooc/enums/FileTypeEnum.java
new file mode 100644
index 0000000..3678001
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/enums/FileTypeEnum.java
@@ -0,0 +1,17 @@
+package com.imooc.enums;
+
+/**
+ * @Desc: 文件类型 枚举
+ */
+public enum FileTypeEnum {
+ BGIMG(1, "用户背景图"),
+ FACE(2, "用户头像");
+
+ public final Integer type;
+ public final String value;
+
+ FileTypeEnum(Integer type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/enums/MessageEnum.java b/book-common/src/main/java/com/imooc/enums/MessageEnum.java
new file mode 100644
index 0000000..6827435
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/enums/MessageEnum.java
@@ -0,0 +1,22 @@
+package com.imooc.enums;
+
+/**
+ * @Desc: 消息类型
+ */
+public enum MessageEnum {
+ FOLLOW_YOU(1, "关注", "follow"),
+ LIKE_VLOG(2, "点赞视频", "likeVideo"),
+ COMMENT_VLOG(3, "评论视频", "comment"),
+ REPLY_YOU(4, "回复评论", "replay"),
+ LIKE_COMMENT(5, "点赞评论", "likeComment");
+
+ public final Integer type;
+ public final String value;
+ public final String enValue;
+
+ MessageEnum(Integer type, String value, String enValue) {
+ this.type = type;
+ this.value = value;
+ this.enValue = enValue;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/enums/Sex.java b/book-common/src/main/java/com/imooc/enums/Sex.java
new file mode 100644
index 0000000..d29fd92
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/enums/Sex.java
@@ -0,0 +1,18 @@
+package com.imooc.enums;
+
+/**
+ * @Desc: 性别 枚举
+ */
+public enum Sex {
+ woman(0, "女"),
+ man(1, "男"),
+ secret(2, "保密");
+
+ public final Integer type;
+ public final String value;
+
+ Sex(Integer type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/enums/UserInfoModifyType.java b/book-common/src/main/java/com/imooc/enums/UserInfoModifyType.java
new file mode 100644
index 0000000..054b780
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/enums/UserInfoModifyType.java
@@ -0,0 +1,35 @@
+package com.imooc.enums;
+
+import com.imooc.exceptions.GraceException;
+import com.imooc.grace.result.ResponseStatusEnum;
+
+/**
+ * @Desc: 修改用户信息类型 枚举
+ */
+public enum UserInfoModifyType {
+ NICKNAME(1, "昵称"),
+ IMOOCNUM(2, "慕课号"),
+ SEX(3, "性别"),
+ BIRTHDAY(4, "生日"),
+ LOCATION(5, "所在地"),
+ DESC(6, "简介");
+
+ public final Integer type;
+ public final String value;
+
+ UserInfoModifyType(Integer type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ public static void checkUserInfoTypeIsRight(Integer type) {
+ if (type != UserInfoModifyType.NICKNAME.type &&
+ type != UserInfoModifyType.IMOOCNUM.type &&
+ type != UserInfoModifyType.SEX.type &&
+ type != UserInfoModifyType.BIRTHDAY.type &&
+ type != UserInfoModifyType.LOCATION.type &&
+ type != UserInfoModifyType.DESC.type) {
+ GraceException.display(ResponseStatusEnum.USER_INFO_UPDATED_ERROR);
+ }
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/enums/YesOrNo.java b/book-common/src/main/java/com/imooc/enums/YesOrNo.java
new file mode 100644
index 0000000..84a8a7f
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/enums/YesOrNo.java
@@ -0,0 +1,17 @@
+package com.imooc.enums;
+
+/**
+ * @Desc: 是否 枚举
+ */
+public enum YesOrNo {
+ NO(0, "否"),
+ YES(1, "是");
+
+ public final Integer type;
+ public final String value;
+
+ YesOrNo(Integer type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/exceptions/GraceException.java b/book-common/src/main/java/com/imooc/exceptions/GraceException.java
new file mode 100644
index 0000000..4b89650
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/exceptions/GraceException.java
@@ -0,0 +1,14 @@
+package com.imooc.exceptions;
+
+import com.imooc.grace.result.ResponseStatusEnum;
+
+/**
+ * 优雅的处理异常,统一封装
+ */
+public class GraceException {
+
+ public static void display(ResponseStatusEnum responseStatusEnum) {
+ throw new MyCustomException(responseStatusEnum);
+ }
+
+}
diff --git a/book-common/src/main/java/com/imooc/exceptions/GraceExceptionHandler.java b/book-common/src/main/java/com/imooc/exceptions/GraceExceptionHandler.java
new file mode 100644
index 0000000..dd1a96e
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/exceptions/GraceExceptionHandler.java
@@ -0,0 +1,58 @@
+package com.imooc.exceptions;
+
+import com.imooc.grace.result.GraceJSONResult;
+import com.imooc.grace.result.ResponseStatusEnum;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.FieldError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MaxUploadSizeExceededException;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 统一异常拦截处理
+ * 可以针对异常的类型进行捕获,然后返回json信息到前端
+ */
+@ControllerAdvice
+public class GraceExceptionHandler {
+
+ @ExceptionHandler(MyCustomException.class)
+ @ResponseBody
+ public GraceJSONResult returnMyException(MyCustomException e) {
+ //e.printStackTrace();
+ return GraceJSONResult.exception(e.getResponseStatusEnum());
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ @ResponseBody
+ public GraceJSONResult returnMethodArgumentNotValid(MethodArgumentNotValidException e) {
+ BindingResult result = e.getBindingResult();
+ Map map = getErrors(result);
+ return GraceJSONResult.errorMap(map);
+ }
+
+ @ExceptionHandler(MaxUploadSizeExceededException.class)
+ @ResponseBody
+ public GraceJSONResult returnMaxUploadSize(MaxUploadSizeExceededException e) {
+// e.printStackTrace();
+ return GraceJSONResult.errorCustom(ResponseStatusEnum.FILE_MAX_SIZE_2MB_ERROR);
+ }
+
+ public Map getErrors(BindingResult result) {
+ Map map = new HashMap<>();
+ List errorList = result.getFieldErrors();
+ for (FieldError ff : errorList) {
+ // 错误所对应的属性字段名
+ String field = ff.getField();
+ // 错误的信息
+ String msg = ff.getDefaultMessage();
+ map.put(field, msg);
+ }
+ return map;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/exceptions/MyCustomException.java b/book-common/src/main/java/com/imooc/exceptions/MyCustomException.java
new file mode 100644
index 0000000..681574d
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/exceptions/MyCustomException.java
@@ -0,0 +1,28 @@
+package com.imooc.exceptions;
+
+import com.imooc.grace.result.ResponseStatusEnum;
+
+/**
+ * 自定义异常
+ * 目的:统一处理异常信息
+ * 便于解耦,拦截器、service与controller 异常错误的解耦,
+ * 不会被service返回的类型而限制
+ */
+public class MyCustomException extends RuntimeException {
+
+ private ResponseStatusEnum responseStatusEnum;
+
+ public MyCustomException(ResponseStatusEnum responseStatusEnum) {
+ super("异常状态码为:" + responseStatusEnum.status()
+ + ";具体异常信息为:" + responseStatusEnum.msg());
+ this.responseStatusEnum = responseStatusEnum;
+ }
+
+ public ResponseStatusEnum getResponseStatusEnum() {
+ return responseStatusEnum;
+ }
+
+ public void setResponseStatusEnum(ResponseStatusEnum responseStatusEnum) {
+ this.responseStatusEnum = responseStatusEnum;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/grace/result/GraceJSONResult.java b/book-common/src/main/java/com/imooc/grace/result/GraceJSONResult.java
new file mode 100644
index 0000000..60938ad
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/grace/result/GraceJSONResult.java
@@ -0,0 +1,153 @@
+package com.imooc.grace.result;
+
+import java.util.Map;
+
+/**
+ * 自定义响应数据类型枚举升级版本
+ *
+ * @Title: IMOOCJSONResult.java
+ * @Package com.imooc.utils
+ * @Description: 自定义响应数据结构
+ * 本类可提供给 H5/ios/安卓/公众号/小程序 使用
+ * 前端接受此类数据(json object)后,可自行根据业务去实现相关功能
+ *
+ * @Copyright: Copyright (c) 2020
+ * @Company: www.imooc.com
+ * @author 慕课网 - 风间影月
+ * @version V2.0
+ */
+public class GraceJSONResult {
+
+ // 响应业务状态码
+ private Integer status;
+
+ // 响应消息
+ private String msg;
+
+ // 是否成功
+ private Boolean success;
+
+ // 响应数据,可以是Object,也可以是List或Map等
+ private Object data;
+
+ /**
+ * 成功返回,带有数据的,直接往OK方法丢data数据即可
+ * @param data
+ * @return
+ */
+ public static GraceJSONResult ok(Object data) {
+ return new GraceJSONResult(data);
+ }
+ /**
+ * 成功返回,不带有数据的,直接调用ok方法,data无须传入(其实就是null)
+ * @return
+ */
+ public static GraceJSONResult ok() {
+ return new GraceJSONResult(ResponseStatusEnum.SUCCESS);
+ }
+ public GraceJSONResult(Object data) {
+ this.status = ResponseStatusEnum.SUCCESS.status();
+ this.msg = ResponseStatusEnum.SUCCESS.msg();
+ this.success = ResponseStatusEnum.SUCCESS.success();
+ this.data = data;
+ }
+
+
+ /**
+ * 错误返回,直接调用error方法即可,当然也可以在ResponseStatusEnum中自定义错误后再返回也都可以
+ * @return
+ */
+ public static GraceJSONResult error() {
+ return new GraceJSONResult(ResponseStatusEnum.FAILED);
+ }
+
+ /**
+ * 错误返回,map中包含了多条错误信息,可以用于表单验证,把错误统一的全部返回出去
+ * @param map
+ * @return
+ */
+ public static GraceJSONResult errorMap(Map map) {
+ return new GraceJSONResult(ResponseStatusEnum.FAILED, map);
+ }
+
+ /**
+ * 错误返回,直接返回错误的消息
+ * @param msg
+ * @return
+ */
+ public static GraceJSONResult errorMsg(String msg) {
+ return new GraceJSONResult(ResponseStatusEnum.FAILED, msg);
+ }
+
+ /**
+ * 错误返回,token异常,一些通用的可以在这里统一定义
+ * @return
+ */
+ public static GraceJSONResult errorTicket() {
+ return new GraceJSONResult(ResponseStatusEnum.TICKET_INVALID);
+ }
+
+ /**
+ * 自定义错误范围,需要传入一个自定义的枚举,可以到[ResponseStatusEnum.java[中自定义后再传入
+ * @param responseStatus
+ * @return
+ */
+ public static GraceJSONResult errorCustom(ResponseStatusEnum responseStatus) {
+ return new GraceJSONResult(responseStatus);
+ }
+ public static GraceJSONResult exception(ResponseStatusEnum responseStatus) {
+ return new GraceJSONResult(responseStatus);
+ }
+
+ public GraceJSONResult(ResponseStatusEnum responseStatus) {
+ this.status = responseStatus.status();
+ this.msg = responseStatus.msg();
+ this.success = responseStatus.success();
+ }
+ public GraceJSONResult(ResponseStatusEnum responseStatus, Object data) {
+ this.status = responseStatus.status();
+ this.msg = responseStatus.msg();
+ this.success = responseStatus.success();
+ this.data = data;
+ }
+ public GraceJSONResult(ResponseStatusEnum responseStatus, String msg) {
+ this.status = responseStatus.status();
+ this.msg = msg;
+ this.success = responseStatus.success();
+ }
+
+ public GraceJSONResult() {
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ public Boolean getSuccess() {
+ return success;
+ }
+
+ public void setSuccess(Boolean success) {
+ this.success = success;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/grace/result/IMOOCJSONResult.java b/book-common/src/main/java/com/imooc/grace/result/IMOOCJSONResult.java
new file mode 100644
index 0000000..0b87a4a
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/grace/result/IMOOCJSONResult.java
@@ -0,0 +1,135 @@
+package com.imooc.grace.result;
+
+/**
+ *
+ * @Title: IMOOCJSONResult.java
+ * @Package com.imooc.utils
+ * @Description: 自定义响应数据结构
+ * 本类可提供给 H5/ios/安卓/公众号/小程序 使用
+ * 前端接受此类数据(json object)后,可自行根据业务去实现相关功能
+ *
+ * 200:表示成功
+ * 500:表示错误,错误信息在msg字段中
+ * 501:bean验证错误,不管多少个错误都以map形式返回
+ * 502:拦截器拦截到用户token出错
+ * 555:异常抛出信息
+ * 556: 用户qq校验异常
+ * 557: 校验用户是否在CAS登录,用户门票的校验
+ * @Copyright: Copyright (c) 2020
+ * @Company: www.imooc.com
+ * @author 慕课网 - 风间影月
+ * @version V1.0
+ */
+public class IMOOCJSONResult {
+
+ // 响应业务状态
+ private Integer status;
+
+ // 响应消息
+ private String msg;
+
+ // 响应中的数据
+ private Object data;
+
+ private String ok; // 不使用
+
+ public static IMOOCJSONResult build(Integer status, String msg, Object data) {
+ return new IMOOCJSONResult(status, msg, data);
+ }
+
+ public static IMOOCJSONResult build(Integer status, String msg, Object data, String ok) {
+ return new IMOOCJSONResult(status, msg, data, ok);
+ }
+
+ public static IMOOCJSONResult ok(Object data) {
+ return new IMOOCJSONResult(data);
+ }
+
+ public static IMOOCJSONResult ok() {
+ return new IMOOCJSONResult(null);
+ }
+
+ public static IMOOCJSONResult errorMsg(String msg) {
+ return new IMOOCJSONResult(500, msg, null);
+ }
+
+ public static IMOOCJSONResult errorUserTicket(String msg) {
+ return new IMOOCJSONResult(557, msg, null);
+ }
+
+ public static IMOOCJSONResult errorMap(Object data) {
+ return new IMOOCJSONResult(501, "error", data);
+ }
+
+ public static IMOOCJSONResult errorTokenMsg(String msg) {
+ return new IMOOCJSONResult(502, msg, null);
+ }
+
+ public static IMOOCJSONResult errorException(String msg) {
+ return new IMOOCJSONResult(555, msg, null);
+ }
+
+ public static IMOOCJSONResult errorUserQQ(String msg) {
+ return new IMOOCJSONResult(556, msg, null);
+ }
+
+ public IMOOCJSONResult() {
+
+ }
+
+ public IMOOCJSONResult(Integer status, String msg, Object data) {
+ this.status = status;
+ this.msg = msg;
+ this.data = data;
+ }
+
+ public IMOOCJSONResult(Integer status, String msg, Object data, String ok) {
+ this.status = status;
+ this.msg = msg;
+ this.data = data;
+ this.ok = ok;
+ }
+
+ public IMOOCJSONResult(Object data) {
+ this.status = 200;
+ this.msg = "OK";
+ this.data = data;
+ }
+
+ public Boolean isOK() {
+ return this.status == 200;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public String getMsg() {
+ return msg;
+ }
+
+ public void setMsg(String msg) {
+ this.msg = msg;
+ }
+
+ public Object getData() {
+ return data;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ public String getOk() {
+ return ok;
+ }
+
+ public void setOk(String ok) {
+ this.ok = ok;
+ }
+
+}
diff --git a/book-common/src/main/java/com/imooc/grace/result/ResponseStatusEnum.java b/book-common/src/main/java/com/imooc/grace/result/ResponseStatusEnum.java
new file mode 100644
index 0000000..32abfd1
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/grace/result/ResponseStatusEnum.java
@@ -0,0 +1,105 @@
+package com.imooc.grace.result;
+
+/**
+ * 响应结果枚举,用于提供给GraceJSONResult返回给前端的
+ * 本枚举类中包含了很多的不同的状态码供使用,可以自定义
+ * 便于更优雅的对状态码进行管理,一目了然
+ */
+public enum ResponseStatusEnum {
+
+ SUCCESS(200, true, "操作成功!"),
+ FAILED(500, false, "操作失败!"),
+
+ // 50x
+ UN_LOGIN(501,false,"请登录后再继续操作!"),
+ TICKET_INVALID(502,false,"会话失效,请重新登录!"),
+ NO_AUTH(503,false,"您的权限不足,无法继续操作!"),
+ MOBILE_ERROR(504,false,"短信发送失败,请稍后重试!"),
+ SMS_NEED_WAIT_ERROR(505,false,"短信发送太快啦~请稍后再试!"),
+ SMS_CODE_ERROR(506,false,"验证码过期或不匹配,请稍后再试!"),
+ USER_FROZEN(507,false,"用户已被冻结,请联系管理员!"),
+ USER_UPDATE_ERROR(508,false,"用户信息更新失败,请联系管理员!"),
+ USER_INACTIVE_ERROR(509,false,"请前往[账号设置]修改信息激活后再进行后续操作!"),
+ USER_INFO_UPDATED_ERROR(5091,false,"用户信息修改失败!"),
+ USER_INFO_UPDATED_NICKNAME_EXIST_ERROR(5092,false,"昵称已经存在!"),
+ USER_INFO_UPDATED_IMOOCNUM_EXIST_ERROR(5092,false,"慕课号已经存在!"),
+ USER_INFO_CANT_UPDATED_IMOOCNUM_ERROR(5092,false,"慕课号无法修改!"),
+ FILE_UPLOAD_NULL_ERROR(510,false,"文件不能为空,请选择一个文件再上传!"),
+ FILE_UPLOAD_FAILD(511,false,"文件上传失败!"),
+ FILE_FORMATTER_FAILD(512,false,"文件图片格式不支持!"),
+ FILE_MAX_SIZE_500KB_ERROR(5131,false,"仅支持500kb大小以下的图片上传!"),
+ FILE_MAX_SIZE_2MB_ERROR(5132,false,"仅支持2MB大小以下的图片上传!"),
+ FILE_NOT_EXIST_ERROR(514,false,"你所查看的文件不存在!"),
+ USER_STATUS_ERROR(515,false,"用户状态参数出错!"),
+ USER_NOT_EXIST_ERROR(516,false,"用户不存在!"),
+
+ // 自定义系统级别异常 54x
+ SYSTEM_INDEX_OUT_OF_BOUNDS(541, false, "系统错误,数组越界!"),
+ SYSTEM_ARITHMETIC_BY_ZERO(542, false, "系统错误,无法除零!"),
+ SYSTEM_NULL_POINTER(543, false, "系统错误,空指针!"),
+ SYSTEM_NUMBER_FORMAT(544, false, "系统错误,数字转换异常!"),
+ SYSTEM_PARSE(545, false, "系统错误,解析异常!"),
+ SYSTEM_IO(546, false, "系统错误,IO输入输出异常!"),
+ SYSTEM_FILE_NOT_FOUND(547, false, "系统错误,文件未找到!"),
+ SYSTEM_CLASS_CAST(548, false, "系统错误,类型强制转换错误!"),
+ SYSTEM_PARSER_ERROR(549, false, "系统错误,解析出错!"),
+ SYSTEM_DATE_PARSER_ERROR(550, false, "系统错误,日期解析出错!"),
+
+ // admin 管理系统 56x
+ ADMIN_USERNAME_NULL_ERROR(561, false, "管理员登录名不能为空!"),
+ ADMIN_USERNAME_EXIST_ERROR(562, false, "管理员登录名已存在!"),
+ ADMIN_NAME_NULL_ERROR(563, false, "管理员负责人不能为空!"),
+ ADMIN_PASSWORD_ERROR(564, false, "密码不能为空后者两次输入不一致!"),
+ ADMIN_CREATE_ERROR(565, false, "添加管理员失败!"),
+ ADMIN_PASSWORD_NULL_ERROR(566, false, "密码不能为空!"),
+ ADMIN_NOT_EXIT_ERROR(567, false, "管理员不存在或密码错误!"),
+ ADMIN_FACE_NULL_ERROR(568, false, "人脸信息不能为空!"),
+ ADMIN_FACE_LOGIN_ERROR(569, false, "人脸识别失败,请重试!"),
+ CATEGORY_EXIST_ERROR(570, false, "文章分类已存在,请换一个分类名!"),
+
+ // 媒体中心 相关错误 58x
+ ARTICLE_COVER_NOT_EXIST_ERROR(580, false, "文章封面不存在,请选择一个!"),
+ ARTICLE_CATEGORY_NOT_EXIST_ERROR(581, false, "请选择正确的文章领域!"),
+ ARTICLE_CREATE_ERROR(582, false, "创建文章失败,请重试或联系管理员!"),
+ ARTICLE_QUERY_PARAMS_ERROR(583, false, "文章列表查询参数错误!"),
+ ARTICLE_DELETE_ERROR(584, false, "文章删除失败!"),
+ ARTICLE_WITHDRAW_ERROR(585, false, "文章撤回失败!"),
+ ARTICLE_REVIEW_ERROR(585, false, "文章审核出错!"),
+ ARTICLE_ALREADY_READ_ERROR(586, false, "文章重复阅读!"),
+
+ // 人脸识别错误代码
+ FACE_VERIFY_TYPE_ERROR(600, false, "人脸比对验证类型不正确!"),
+ FACE_VERIFY_LOGIN_ERROR(601, false, "人脸登录失败!"),
+
+ // 系统错误,未预期的错误 555
+ SYSTEM_ERROR(555, false, "系统繁忙,请稍后再试!"),
+ SYSTEM_OPERATION_ERROR(556, false, "操作失败,请重试或联系管理员"),
+ SYSTEM_RESPONSE_NO_INFO(557, false, ""),
+ SYSTEM_ERROR_GLOBAL(558, false, "全局降级:系统繁忙,请稍后再试!"),
+ SYSTEM_ERROR_FEIGN(559, false, "客户端Feign降级:系统繁忙,请稍后再试!"),
+ SYSTEM_ERROR_ZUUL(560, false, "请求系统过于繁忙,请稍后再试!");
+
+
+ // 响应业务状态
+ private Integer status;
+ // 调用是否成功
+ private Boolean success;
+ // 响应消息,可以为成功或者失败的消息
+ private String msg;
+
+ ResponseStatusEnum(Integer status, Boolean success, String msg) {
+ this.status = status;
+ this.success = success;
+ this.msg = msg;
+ }
+
+ public Integer status() {
+ return status;
+ }
+ public Boolean success() {
+ return success;
+ }
+ public String msg() {
+ return msg;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/utils/DateUtil.java b/book-common/src/main/java/com/imooc/utils/DateUtil.java
new file mode 100644
index 0000000..f324e03
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/utils/DateUtil.java
@@ -0,0 +1,680 @@
+package com.imooc.utils;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+public class DateUtil {
+
+ /**
+ * Base ISO 8601 Date format yyyyMMdd i.e., 20021225 for the 25th day of December in the year 2002
+ */
+ public static final String ISO_DATE_FORMAT = "yyyyMMdd";
+
+ /**
+ * Expanded ISO 8601 Date format yyyy-MM-dd i.e., 2002-12-25 for the 25th day of December in the year 2002
+ */
+ public static final String ISO_EXPANDED_DATE_FORMAT = "yyyy-MM-dd";
+
+ /**
+ * yyyy-MM-dd hh:mm:ss
+ */
+ public static String DATETIME_PATTERN = "yyyy-MM-dd HH:mm:ss";
+ public static String DATE_PATTERN = "yyyyMMddHHmmss";
+
+ /**
+ * 则个
+ */
+ private static boolean LENIENT_DATE = false;
+
+
+ private static Random random = new Random();
+ private static final int ID_BYTES = 10;
+
+ public synchronized static String generateId() {
+ StringBuffer result = new StringBuffer();
+ result = result.append(System.currentTimeMillis());
+ for (int i = 0; i < ID_BYTES; i++) {
+ result = result.append(random.nextInt(10));
+ }
+ return result.toString();
+ }
+
+ protected static final float normalizedJulian(float JD) {
+
+ float f = Math.round(JD + 0.5f) - 0.5f;
+
+ return f;
+ }
+
+ /**
+ * Returns the Date from a julian. The Julian date will be converted to noon GMT,
+ * such that it matches the nearest half-integer (i.e., a julian date of 1.4 gets
+ * changed to 1.5, and 0.9 gets changed to 0.5.)
+ *
+ * @param JD the Julian date
+ * @return the Gregorian date
+ */
+ public static final Date toDate(float JD) {
+
+ /* To convert a Julian Day Number to a Gregorian date, assume that it is for 0 hours, Greenwich time (so
+ * that it ends in 0.5). Do the following calculations, again dropping the fractional part of all
+ * multiplicatons and divisions. Note: This method will not give dates accurately on the
+ * Gregorian Proleptic Calendar, i.e., the calendar you get by extending the Gregorian
+ * calendar backwards to years earlier than 1582. using the Gregorian leap year
+ * rules. In particular, the method fails if Y<400. */
+ float Z = (normalizedJulian(JD)) + 0.5f;
+ float W = (int) ((Z - 1867216.25f) / 36524.25f);
+ float X = (int) (W / 4f);
+ float A = Z + 1 + W - X;
+ float B = A + 1524;
+ float C = (int) ((B - 122.1) / 365.25);
+ float D = (int) (365.25f * C);
+ float E = (int) ((B - D) / 30.6001);
+ float F = (int) (30.6001f * E);
+ int day = (int) (B - D - F);
+ int month = (int) (E - 1);
+
+ if (month > 12) {
+ month = month - 12;
+ }
+
+ int year = (int) (C - 4715); //(if Month is January or February) or C-4716 (otherwise)
+
+ if (month > 2) {
+ year--;
+ }
+
+ Calendar c = Calendar.getInstance();
+ c.set(Calendar.YEAR, year);
+ c.set(Calendar.MONTH, month - 1); // damn 0 offsets
+ c.set(Calendar.DATE, day);
+
+ return c.getTime();
+ }
+
+ /**
+ * Returns the days between two dates. Positive values indicate that
+ * the second date is after the first, and negative values indicate, well,
+ * the opposite. Relying on specific times is problematic.
+ *
+ * @param early the "first date"
+ * @param late the "second date"
+ * @return the days between the two dates
+ */
+ public static final int daysBetween(Date early, Date late) {
+
+ Calendar c1 = Calendar.getInstance();
+ Calendar c2 = Calendar.getInstance();
+ c1.setTime(early);
+ c2.setTime(late);
+
+ return daysBetween(c1, c2);
+ }
+
+ /**
+ * Returns the days between two dates. Positive values indicate that
+ * the second date is after the first, and negative values indicate, well,
+ * the opposite.
+ *
+ * @param early
+ * @param late
+ * @return the days between two dates.
+ */
+ public static final int daysBetween(Calendar early, Calendar late) {
+
+ return (int) (toJulian(late) - toJulian(early));
+ }
+
+ /**
+ * 计算时间差
+ * @param startDate
+ * @param endDate
+ * @return
+ */
+ public static final String timeBetween(Date startDate, Date endDate) {
+ long nd = 1000 * 24 * 60 * 60;
+ long nh = 1000 * 60 * 60;
+ long nm = 1000 * 60;
+ // long ns = 1000;
+ // 获得两个时间的毫秒时间差异
+ long diff = endDate.getTime() - startDate.getTime();
+ // 计算差多少天
+ long day = diff / nd;
+ // 计算差多少小时
+ long hour = diff % nd / nh;
+ // 计算差多少分钟
+ long min = diff % nd % nh / nm;
+ // 计算差多少秒//输出结果
+ // long sec = diff % nd % nh % nm / ns;
+ return day + "天" + hour + "小时" + min + "分钟";
+ }
+
+ /**
+ * Return a Julian date based on the input parameter. This is
+ * based from calculations found at
+ * Julian Day Calculations
+ * (Gregorian Calendar), provided by Bill Jeffrys.
+ * @param c a calendar instance
+ * @return the julian day number
+ */
+ public static final float toJulian(Calendar c) {
+
+ int Y = c.get(Calendar.YEAR);
+ int M = c.get(Calendar.MONTH);
+ int D = c.get(Calendar.DATE);
+ int A = Y / 100;
+ int B = A / 4;
+ int C = 2 - A + B;
+ float E = (int) (365.25f * (Y + 4716));
+ float F = (int) (30.6001f * (M + 1));
+ float JD = C + D + E + F - 1524.5f;
+
+ return JD;
+ }
+
+ /**
+ * Return a Julian date based on the input parameter. This is
+ * based from calculations found at
+ * Julian Day Calculations
+ * (Gregorian Calendar), provided by Bill Jeffrys.
+ * @param date
+ * @return the julian day number
+ */
+ public static final float toJulian(Date date) {
+
+ Calendar c = Calendar.getInstance();
+ c.setTime(date);
+
+ return toJulian(c);
+ }
+
+ /**
+ * @param isoString
+ * @param fmt
+ * @param field Calendar.YEAR/Calendar.MONTH/Calendar.DATE
+ * @param amount
+ * @return
+ * @throws ParseException
+ */
+ public static final String dateIncrease(String isoString, String fmt,
+ int field, int amount) {
+
+ try {
+ Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone(
+ "GMT"));
+ cal.setTime(stringToDate(isoString, fmt, true));
+ cal.add(field, amount);
+
+ return dateToString(cal.getTime(), fmt);
+
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Time Field Rolling function.
+ * Rolls (up/down) a single unit of time on the given time field.
+ *
+ * @param isoString
+ * @param field the time field.
+ * @param up Indicates if rolling up or rolling down the field value.
+ * @param expanded use formating char's
+ * @exception ParseException if an unknown field value is given.
+ */
+ public static final String roll(String isoString, String fmt, int field,
+ boolean up) throws ParseException {
+
+ Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone(
+ "GMT"));
+ cal.setTime(stringToDate(isoString, fmt));
+ cal.roll(field, up);
+
+ return dateToString(cal.getTime(), fmt);
+ }
+
+ /**
+ * Time Field Rolling function.
+ * Rolls (up/down) a single unit of time on the given time field.
+ *
+ * @param isoString
+ * @param field the time field.
+ * @param up Indicates if rolling up or rolling down the field value.
+ * @exception ParseException if an unknown field value is given.
+ */
+ public static final String roll(String isoString, int field, boolean up) throws
+ ParseException {
+
+ return roll(isoString, DATETIME_PATTERN, field, up);
+ }
+
+ /**
+ * java.util.Date
+ * @param dateText
+ * @param format
+ * @param lenient
+ * @return
+ */
+ public static Date stringToDate(String dateText, String format,
+ boolean lenient) {
+
+ if (dateText == null) {
+
+ return null;
+ }
+
+ DateFormat df = null;
+
+ try {
+
+ if (format == null) {
+ df = new SimpleDateFormat();
+ } else {
+ df = new SimpleDateFormat(format);
+ }
+
+ // setLenient avoids allowing dates like 9/32/2001
+ // which would otherwise parse to 10/2/2001
+ df.setLenient(false);
+
+ return df.parse(dateText);
+ } catch (ParseException e) {
+
+ return null;
+ }
+ }
+
+ /**
+ * @return Timestamp
+ */
+ public static java.sql.Timestamp getCurrentTimestamp() {
+ return new java.sql.Timestamp(new Date().getTime());
+ }
+
+ /** java.util.Date
+ * @param dateText
+ * @param format
+ * @return
+ */
+ public static Date stringToDate(String dateString, String format) {
+
+ return stringToDate(dateString, format, LENIENT_DATE);
+ }
+
+ /**
+ * java.util.Date
+ * @param dateText
+ */
+ public static Date stringToDate(String dateString) {
+ return stringToDate(dateString, ISO_EXPANDED_DATE_FORMAT, LENIENT_DATE);
+ }
+
+ /**
+ * @return
+ * @param pattern
+ * @param date
+ */
+ public static String dateToString(Date date, String pattern) {
+
+ if (date == null) {
+
+ return null;
+ }
+
+ try {
+
+ SimpleDateFormat sfDate = new SimpleDateFormat(pattern);
+ sfDate.setLenient(false);
+
+ return sfDate.format(date);
+ } catch (Exception e) {
+
+ return null;
+ }
+ }
+
+ /**
+ * yyyy-MM-dd
+ * @param date
+ * @return
+ */
+ public static String dateToString(Date date) {
+ return dateToString(date, ISO_EXPANDED_DATE_FORMAT);
+ }
+
+ /**
+ * @return
+ */
+ public static Date getCurrentDateTime() {
+ Calendar calNow = Calendar.getInstance();
+ Date dtNow = calNow.getTime();
+
+ return dtNow;
+ }
+
+ /**
+ *
+ * @param pattern
+ * @return
+ */
+ public static String getCurrentDateString(String pattern) {
+ return dateToString(getCurrentDateTime(), pattern);
+ }
+
+ /**
+ * yyyy-MM-dd
+ * @return
+ */
+ public static String getCurrentDateString() {
+ return dateToString(getCurrentDateTime(), ISO_EXPANDED_DATE_FORMAT);
+ }
+
+ /**
+ * 返回固定格式的当前时间
+ * yyyy-MM-dd hh:mm:ss
+ * @param date
+ * @return
+ */
+ public static String dateToStringWithTime( ) {
+
+ return dateToString(new Date(), DATETIME_PATTERN);
+ }
+
+
+ /**
+ * yyyy-MM-dd hh:mm:ss
+ * @param date
+ * @return
+ */
+ public static String dateToStringWithTime(Date date) {
+
+ return dateToString(date, DATETIME_PATTERN);
+ }
+
+ /**
+ *
+ * @param date
+ * @param days
+ * @return java.util.Date
+ */
+ public static Date dateIncreaseByDay(Date date, int days) {
+
+ Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone(
+ "GMT"));
+ cal.setTime(date);
+ cal.add(Calendar.DATE, days);
+
+ return cal.getTime();
+ }
+
+ /**
+ *
+ * @param date
+ * @param days
+ * @return java.util.Date
+ */
+ public static Date dateIncreaseByMonth(Date date, int mnt) {
+
+ Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone(
+ "GMT"));
+ cal.setTime(date);
+ cal.add(Calendar.MONTH, mnt);
+
+ return cal.getTime();
+ }
+
+ /**
+ *
+ * @param date
+ * @param mnt
+ * @return java.util.Date
+ */
+ public static Date dateIncreaseByYear(Date date, int mnt) {
+
+ Calendar cal = GregorianCalendar.getInstance(TimeZone.getTimeZone(
+ "GMT"));
+ cal.setTime(date);
+ cal.add(Calendar.YEAR, mnt);
+
+ return cal.getTime();
+ }
+
+ /**
+ *
+ * @param date yyyy-MM-dd
+ * @param days
+ * @return yyyy-MM-dd
+ */
+ public static String dateIncreaseByDay(String date, int days) {
+ return dateIncreaseByDay(date, ISO_DATE_FORMAT, days);
+ }
+
+ /**
+ * @param date
+ * @param fmt
+ * @param days
+ * @return
+ */
+ public static String dateIncreaseByDay(String date, String fmt, int days) {
+ return dateIncrease(date, fmt, Calendar.DATE, days);
+ }
+
+ /**
+ *
+ * @param src
+ * @param srcfmt
+ * @param desfmt
+ * @return
+ */
+ public static String stringToString(String src, String srcfmt,
+ String desfmt) {
+ return dateToString(stringToDate(src, srcfmt), desfmt);
+ }
+
+ /**
+ *
+ * @param date
+ * @return string
+ */
+ public static String getYear(Date date) {
+ SimpleDateFormat formater = new SimpleDateFormat(
+ "yyyy");
+ String cur_year = formater.format(date);
+ return cur_year;
+ }
+
+ /**
+ *
+ * @param date
+ * @return string
+ */
+ public static String getMonth(Date date) {
+ SimpleDateFormat formater = new SimpleDateFormat(
+ "MM");
+ String cur_month = formater.format(date);
+ return cur_month;
+ }
+
+ /**
+ * @param date
+ * @return string
+ */
+ public static String getDay(Date date) {
+ SimpleDateFormat formater = new SimpleDateFormat(
+ "dd");
+ String cur_day = formater.format(date);
+ return cur_day;
+ }
+
+ public static int getDayInt(Date date) {
+ SimpleDateFormat formater = new SimpleDateFormat(
+ "dd");
+ String cur_day = formater.format(date);
+ return Integer.valueOf(cur_day);
+ }
+
+ /**
+ * @param date
+ * @return string
+ */
+ public static String getHour(Date date) {
+ SimpleDateFormat formater = new SimpleDateFormat(
+ "HH");
+ String cur_day = formater.format(date);
+ return cur_day;
+ }
+
+ public static int getMinsFromDate(Date dt) {
+ GregorianCalendar cal = new GregorianCalendar();
+ cal.setTime(dt);
+ int hour = cal.get(Calendar.HOUR_OF_DAY);
+ int min = cal.get(Calendar.MINUTE);
+ return ((hour * 60) + min);
+ }
+
+ /**
+ * Function to convert String to Date Object. If invalid input then current or next day date
+ * is returned (Added by Ali Naqvi on 2006-5-16).
+ * @param str String input in YYYY-MM-DD HH:MM[:SS] format.
+ * @param isExpiry boolean if set and input string is invalid then next day date is returned
+ * @return Date
+ */
+ public static Date convertToDate(String str, boolean isExpiry) {
+ SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+ Date dt = null;
+ try {
+ dt = fmt.parse(str);
+ } catch (ParseException ex) {
+ Calendar cal = Calendar.getInstance();
+ if (isExpiry) {
+ cal.add(Calendar.DAY_OF_MONTH, 1);
+ cal.set(Calendar.HOUR_OF_DAY, 23);
+ cal.set(Calendar.MINUTE, 59);
+ } else {
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ }
+ dt = cal.getTime();
+ }
+ return dt;
+ }
+
+ public static Date convertToDate(String str) {
+ SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd hh:mm");
+ Date dt = null;
+ try {
+ dt = fmt.parse(str);
+ } catch (ParseException ex) {
+ dt = new Date();
+ }
+ return dt;
+ }
+
+ public static String dateFromat(Date date, int minute) {
+ String dateFormat = null;
+ int year = Integer.parseInt(getYear(date));
+ int month = Integer.parseInt(getMonth(date));
+ int day = Integer.parseInt(getDay(date));
+ int hour = minute / 60;
+ int min = minute % 60;
+ dateFormat = String.valueOf(year)
+ +
+ (month > 9 ? String.valueOf(month) :
+ "0" + String.valueOf(month))
+ +
+ (day > 9 ? String.valueOf(day) : "0" + String.valueOf(day))
+ + " "
+ +
+ (hour > 9 ? String.valueOf(hour) : "0" + String.valueOf(hour))
+ +
+ (min > 9 ? String.valueOf(min) : "0" + String.valueOf(min))
+ + "00";
+ return dateFormat;
+ }
+
+ public static String sDateFormat() {
+ return new SimpleDateFormat(DATE_PATTERN).format(Calendar.getInstance().getTime());
+ }
+
+ /**
+ *
+ * @Description: 获得本月的第一天日期
+ * @return
+ *
+ * @author leechenxiang
+ * @date 2017年5月31日 下午1:37:34
+ */
+ public static String getFirstDateOfThisMonth() {
+
+ SimpleDateFormat format = new SimpleDateFormat(ISO_EXPANDED_DATE_FORMAT);
+
+ Calendar calendarFirst = Calendar.getInstance();
+ calendarFirst = Calendar.getInstance();
+ calendarFirst.add(Calendar.MONTH, 0);
+ calendarFirst.set(Calendar.DAY_OF_MONTH, 1);
+ String firstDate = format.format(calendarFirst.getTime());
+
+ return firstDate;
+ }
+
+ /**
+ *
+ * @Description: 获得本月的最后一天日期
+ * @return
+ *
+ * @author leechenxiang
+ * @date 2017年5月31日 下午1:37:50
+ */
+ public static String getLastDateOfThisMonth() {
+ SimpleDateFormat format = new SimpleDateFormat(ISO_EXPANDED_DATE_FORMAT);
+
+ Calendar calendarLast = Calendar.getInstance();
+ calendarLast.setTime(new Date());
+ calendarLast.getActualMaximum(Calendar.DAY_OF_MONTH);
+
+ String lastDate = format.format(calendarLast.getTime());
+ return lastDate;
+ }
+
+ /**
+ * @Description: 判断字符串日期是否匹配指定的格式化日期
+ */
+ public static boolean isValidDate(String strDate, String formatter) {
+ SimpleDateFormat sdf = null;
+ ParsePosition pos = new ParsePosition(0);
+
+ if (StringUtils.isBlank(strDate) || StringUtils.isBlank(formatter)) {
+ return false;
+ }
+ try {
+ sdf = new SimpleDateFormat(formatter);
+ sdf.setLenient(false);
+ Date date = sdf.parse(strDate, pos);
+ if (date == null) {
+ return false;
+ } else {
+ if (pos.getIndex() > sdf.format(date).length()) {
+ return false;
+ }
+ return true;
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ return false;
+ }
+ }
+
+ public static void main(String[] args)
+ {
+// String timeDir=DateUtil.dateToString(new Date(),DateUtil.ISO_EXPANDED_DATE_FORMAT);
+// System.out.println(timeDir);
+ boolean flag = DateUtil.isValidDate("1990-10-32", DateUtil.ISO_EXPANDED_DATE_FORMAT);
+ System.out.println(flag);
+ }
+
+}
diff --git a/book-common/src/main/java/com/imooc/utils/DesensitizationUtil.java b/book-common/src/main/java/com/imooc/utils/DesensitizationUtil.java
new file mode 100644
index 0000000..c717c7a
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/utils/DesensitizationUtil.java
@@ -0,0 +1,77 @@
+package com.imooc.utils;
+
+/**
+ * 通用脱敏工具类
+ * 可用于:
+ * 用户名
+ * 手机号
+ * 邮箱
+ * 地址等
+ */
+public class DesensitizationUtil {
+
+ private static final int SIZE = 6;
+ private static final String SYMBOL = "*";
+
+ public static void main(String[] args) {
+ String name = commonDisplay("慕课网");
+ String mobile = commonDisplay("13900000000");
+ String mail = commonDisplay("admin@imooc.com");
+ String address = commonDisplay("北京大运河东路888号");
+
+ System.out.println(name);
+ System.out.println(mobile);
+ System.out.println(mail);
+ System.out.println(address);
+ }
+
+ /**
+ * 通用脱敏方法
+ * @param value
+ * @return
+ */
+ public static String commonDisplay(String value) {
+ if (null == value || "".equals(value)) {
+ return value;
+ }
+ int len = value.length();
+ int pamaone = len / 2;
+ int pamatwo = pamaone - 1;
+ int pamathree = len % 2;
+ StringBuilder stringBuilder = new StringBuilder();
+ if (len <= 2) {
+ if (pamathree == 1) {
+ return SYMBOL;
+ }
+ stringBuilder.append(SYMBOL);
+ stringBuilder.append(value.charAt(len - 1));
+ } else {
+ if (pamatwo <= 0) {
+ stringBuilder.append(value.substring(0, 1));
+ stringBuilder.append(SYMBOL);
+ stringBuilder.append(value.substring(len - 1, len));
+
+ } else if (pamatwo >= SIZE / 2 && SIZE + 1 != len) {
+ int pamafive = (len - SIZE) / 2;
+ stringBuilder.append(value.substring(0, pamafive));
+ for (int i = 0; i < SIZE; i++) {
+ stringBuilder.append(SYMBOL);
+ }
+ if ((pamathree == 0 && SIZE / 2 == 0) || (pamathree != 0 && SIZE % 2 != 0)) {
+ stringBuilder.append(value.substring(len - pamafive, len));
+ } else {
+ stringBuilder.append(value.substring(len - (pamafive + 1), len));
+ }
+ } else {
+ int pamafour = len - 2;
+ stringBuilder.append(value.substring(0, 1));
+ for (int i = 0; i < pamafour; i++) {
+ stringBuilder.append(SYMBOL);
+ }
+ stringBuilder.append(value.substring(len - 1, len));
+ }
+ }
+ return stringBuilder.toString();
+ }
+
+}
diff --git a/book-common/src/main/java/com/imooc/utils/IPUtil.java b/book-common/src/main/java/com/imooc/utils/IPUtil.java
new file mode 100644
index 0000000..501436e
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/utils/IPUtil.java
@@ -0,0 +1,37 @@
+package com.imooc.utils;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * 用户获得用户ip的工具类
+ */
+public class IPUtil {
+
+ /**
+ * 获取请求IP:
+ * 用户的真实IP不能使用request.getRemoteAddr()
+ * 这是因为可能会使用一些代理软件,这样ip获取就不准确了
+ * 此外我们如果使用了多级(LVS/Nginx)反向代理的话,ip需要从X-Forwarded-For中获得第一个非unknown的IP才是用户的有效ip。
+ * @param request
+ * @return
+ */
+ public static String getRequestIp(HttpServletRequest request) {
+ String ip = request.getHeader("x-forwarded-for");
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("HTTP_CLIENT_IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("HTTP_X_FORWARDED_FOR");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+ return ip;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/utils/JsonUtils.java b/book-common/src/main/java/com/imooc/utils/JsonUtils.java
new file mode 100644
index 0000000..ebabfd0
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/utils/JsonUtils.java
@@ -0,0 +1,74 @@
+package com.imooc.utils;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.util.List;
+
+/**
+ *
+ * @Title: JsonUtils.java
+ * @Package com.imooc.utils
+ * @Description: json转换类
+ * Copyright: Copyright (c)
+ * Company: www.imooc.com
+ *
+ * @author imooc
+ */
+public class JsonUtils {
+
+ // 定义jackson对象
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ /**
+ * 将对象转换成json字符串。
+ * @param data
+ * @return
+ */
+ public static String objectToJson(Object data) {
+ try {
+ String string = MAPPER.writeValueAsString(data);
+ return string;
+ } catch (JsonProcessingException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 将json结果集转化为对象
+ *
+ * @param jsonData json数据
+ * @param beanType 对象中的object类型
+ * @return
+ */
+ public static T jsonToPojo(String jsonData, Class beanType) {
+ try {
+ T t = MAPPER.readValue(jsonData, beanType);
+ return t;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 将json数据转换成pojo对象list
+ * @param jsonData
+ * @param beanType
+ * @return
+ */
+ public static List jsonToList(String jsonData, Class beanType) {
+ JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
+ try {
+ List list = MAPPER.readValue(jsonData, javaType);
+ return list;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+}
diff --git a/book-common/src/main/java/com/imooc/utils/MinIOUtils.java b/book-common/src/main/java/com/imooc/utils/MinIOUtils.java
new file mode 100644
index 0000000..5d0d6a9
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/utils/MinIOUtils.java
@@ -0,0 +1,432 @@
+package com.imooc.utils;
+
+import io.minio.*;
+import io.minio.http.Method;
+import io.minio.messages.Bucket;
+import io.minio.messages.DeleteObject;
+import io.minio.messages.Item;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * MinIO工具类
+ */
+@Slf4j
+public class MinIOUtils {
+
+ private static MinioClient minioClient;
+
+ private static String endpoint;
+ private static String bucketName;
+ private static String accessKey;
+ private static String secretKey;
+ private static Integer imgSize;
+ private static Integer fileSize;
+
+
+ private static final String SEPARATOR = "/";
+
+ public MinIOUtils() {
+ }
+
+ public MinIOUtils(String endpoint, String bucketName, String accessKey, String secretKey, Integer imgSize, Integer fileSize) {
+ MinIOUtils.endpoint = endpoint;
+ MinIOUtils.bucketName = bucketName;
+ MinIOUtils.accessKey = accessKey;
+ MinIOUtils.secretKey = secretKey;
+ MinIOUtils.imgSize = imgSize;
+ MinIOUtils.fileSize = fileSize;
+ createMinioClient();
+ }
+
+ /**
+ * 创建基于Java端的MinioClient
+ */
+ public void createMinioClient() {
+ try {
+ if (null == minioClient) {
+ log.info("开始创建 MinioClient...");
+ minioClient = MinioClient
+ .builder()
+ .endpoint(endpoint)
+ .credentials(accessKey, secretKey)
+ .build();
+ createBucket(bucketName);
+ log.info("创建完毕 MinioClient...");
+ }
+ } catch (Exception e) {
+ log.error("MinIO服务器异常:{}", e);
+ }
+ }
+
+ /**
+ * 获取上传文件前缀路径
+ * @return
+ */
+ public static String getBasisUrl() {
+ return endpoint + SEPARATOR + bucketName + SEPARATOR;
+ }
+
+ /****************************** Operate Bucket Start ******************************/
+
+ /**
+ * 启动SpringBoot容器的时候初始化Bucket
+ * 如果没有Bucket则创建
+ * @throws Exception
+ */
+ private static void createBucket(String bucketName) throws Exception {
+ if (!bucketExists(bucketName)) {
+ minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
+ }
+ }
+
+ /**
+ * 判断Bucket是否存在,true:存在,false:不存在
+ * @return
+ * @throws Exception
+ */
+ public static boolean bucketExists(String bucketName) throws Exception {
+ return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
+ }
+
+
+ /**
+ * 获得Bucket的策略
+ * @param bucketName
+ * @return
+ * @throws Exception
+ */
+ public static String getBucketPolicy(String bucketName) throws Exception {
+ String bucketPolicy = minioClient
+ .getBucketPolicy(
+ GetBucketPolicyArgs
+ .builder()
+ .bucket(bucketName)
+ .build()
+ );
+ return bucketPolicy;
+ }
+
+
+ /**
+ * 获得所有Bucket列表
+ * @return
+ * @throws Exception
+ */
+ public static List getAllBuckets() throws Exception {
+ return minioClient.listBuckets();
+ }
+
+ /**
+ * 根据bucketName获取其相关信息
+ * @param bucketName
+ * @return
+ * @throws Exception
+ */
+ public static Optional getBucket(String bucketName) throws Exception {
+ return getAllBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
+ }
+
+ /**
+ * 根据bucketName删除Bucket,true:删除成功; false:删除失败,文件或已不存在
+ * @param bucketName
+ * @throws Exception
+ */
+ public static void removeBucket(String bucketName) throws Exception {
+ minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
+ }
+
+ /****************************** Operate Bucket End ******************************/
+
+
+ /****************************** Operate Files Start ******************************/
+
+ /**
+ * 判断文件是否存在
+ * @param bucketName 存储桶
+ * @param objectName 文件名
+ * @return
+ */
+ public static boolean isObjectExist(String bucketName, String objectName) {
+ boolean exist = true;
+ try {
+ minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
+ } catch (Exception e) {
+ exist = false;
+ }
+ return exist;
+ }
+
+ /**
+ * 判断文件夹是否存在
+ * @param bucketName 存储桶
+ * @param objectName 文件夹名称
+ * @return
+ */
+ public static boolean isFolderExist(String bucketName, String objectName) {
+ boolean exist = false;
+ try {
+ Iterable> results = minioClient.listObjects(
+ ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());
+ for (Result- result : results) {
+ Item item = result.get();
+ if (item.isDir() && objectName.equals(item.objectName())) {
+ exist = true;
+ }
+ }
+ } catch (Exception e) {
+ exist = false;
+ }
+ return exist;
+ }
+
+ /**
+ * 根据文件前缀查询文件
+ * @param bucketName 存储桶
+ * @param prefix 前缀
+ * @param recursive 是否使用递归查询
+ * @return MinioItem 列表
+ * @throws Exception
+ */
+ public static List
- getAllObjectsByPrefix(String bucketName,
+ String prefix,
+ boolean recursive) throws Exception {
+ List
- list = new ArrayList<>();
+ Iterable> objectsIterator = minioClient.listObjects(
+ ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
+ if (objectsIterator != null) {
+ for (Result
- o : objectsIterator) {
+ Item item = o.get();
+ list.add(item);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * 获取文件流
+ * @param bucketName 存储桶
+ * @param objectName 文件名
+ * @return 二进制流
+ */
+ public static InputStream getObject(String bucketName, String objectName) throws Exception {
+ return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
+ }
+
+ /**
+ * 断点下载
+ * @param bucketName 存储桶
+ * @param objectName 文件名称
+ * @param offset 起始字节的位置
+ * @param length 要读取的长度
+ * @return 二进制流
+ */
+ public InputStream getObject(String bucketName, String objectName, long offset, long length)throws Exception {
+ return minioClient.getObject(
+ GetObjectArgs.builder()
+ .bucket(bucketName)
+ .object(objectName)
+ .offset(offset)
+ .length(length)
+ .build());
+ }
+
+ /**
+ * 获取路径下文件列表
+ * @param bucketName 存储桶
+ * @param prefix 文件名称
+ * @param recursive 是否递归查找,false:模拟文件夹结构查找
+ * @return 二进制流
+ */
+ public static Iterable> listObjects(String bucketName, String prefix,
+ boolean recursive) {
+ return minioClient.listObjects(
+ ListObjectsArgs.builder()
+ .bucket(bucketName)
+ .prefix(prefix)
+ .recursive(recursive)
+ .build());
+ }
+
+ /**
+ * 使用MultipartFile进行文件上传
+ * @param bucketName 存储桶
+ * @param file 文件名
+ * @param objectName 对象名
+ * @param contentType 类型
+ * @return
+ * @throws Exception
+ */
+ public static ObjectWriteResponse uploadFile(String bucketName, MultipartFile file,
+ String objectName, String contentType) throws Exception {
+ InputStream inputStream = file.getInputStream();
+ return minioClient.putObject(
+ PutObjectArgs.builder()
+ .bucket(bucketName)
+ .object(objectName)
+ .contentType(contentType)
+ .stream(inputStream, inputStream.available(), -1)
+ .build());
+ }
+
+ /**
+ * 上传本地文件
+ * @param bucketName 存储桶
+ * @param objectName 对象名称
+ * @param fileName 本地文件路径
+ */
+ public static ObjectWriteResponse uploadFile(String bucketName, String objectName,
+ String fileName) throws Exception {
+ return minioClient.uploadObject(
+ UploadObjectArgs.builder()
+ .bucket(bucketName)
+ .object(objectName)
+ .filename(fileName)
+ .build());
+ }
+
+ /**
+ * 通过流上传文件
+ *
+ * @param bucketName 存储桶
+ * @param objectName 文件对象
+ * @param inputStream 文件流
+ */
+ public static ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) throws Exception {
+ return minioClient.putObject(
+ PutObjectArgs.builder()
+ .bucket(bucketName)
+ .object(objectName)
+ .stream(inputStream, inputStream.available(), -1)
+ .build());
+ }
+
+ /**
+ * 创建文件夹或目录
+ * @param bucketName 存储桶
+ * @param objectName 目录路径
+ */
+ public static ObjectWriteResponse createDir(String bucketName, String objectName) throws Exception {
+ return minioClient.putObject(
+ PutObjectArgs.builder()
+ .bucket(bucketName)
+ .object(objectName)
+ .stream(new ByteArrayInputStream(new byte[]{}), 0, -1)
+ .build());
+ }
+
+ /**
+ * 获取文件信息, 如果抛出异常则说明文件不存在
+ *
+ * @param bucketName 存储桶
+ * @param objectName 文件名称
+ */
+ public static String getFileStatusInfo(String bucketName, String objectName) throws Exception {
+ return minioClient.statObject(
+ StatObjectArgs.builder()
+ .bucket(bucketName)
+ .object(objectName)
+ .build()).toString();
+ }
+
+ /**
+ * 拷贝文件
+ *
+ * @param bucketName 存储桶
+ * @param objectName 文件名
+ * @param srcBucketName 目标存储桶
+ * @param srcObjectName 目标文件名
+ */
+ public static ObjectWriteResponse copyFile(String bucketName, String objectName,
+ String srcBucketName, String srcObjectName) throws Exception {
+ return minioClient.copyObject(
+ CopyObjectArgs.builder()
+ .source(CopySource.builder().bucket(bucketName).object(objectName).build())
+ .bucket(srcBucketName)
+ .object(srcObjectName)
+ .build());
+ }
+
+ /**
+ * 删除文件
+ * @param bucketName 存储桶
+ * @param objectName 文件名称
+ */
+ public static void removeFile(String bucketName, String objectName) throws Exception {
+ minioClient.removeObject(
+ RemoveObjectArgs.builder()
+ .bucket(bucketName)
+ .object(objectName)
+ .build());
+ }
+
+ /**
+ * 批量删除文件
+ * @param bucketName 存储桶
+ * @param keys 需要删除的文件列表
+ * @return
+ */
+ public static void removeFiles(String bucketName, List keys) {
+ List objects = new LinkedList<>();
+ keys.forEach(s -> {
+ objects.add(new DeleteObject(s));
+ try {
+ removeFile(bucketName, s);
+ } catch (Exception e) {
+ log.error("批量删除失败!error:{}",e);
+ }
+ });
+ }
+
+ /**
+ * 获取文件外链
+ * @param bucketName 存储桶
+ * @param objectName 文件名
+ * @param expires 过期时间 <=7 秒 (外链有效时间(单位:秒))
+ * @return url
+ * @throws Exception
+ */
+ public static String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws Exception {
+ GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build();
+ return minioClient.getPresignedObjectUrl(args);
+ }
+
+ /**
+ * 获得文件外链
+ * @param bucketName
+ * @param objectName
+ * @return url
+ * @throws Exception
+ */
+ public static String getPresignedObjectUrl(String bucketName, String objectName) throws Exception {
+ GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
+ .bucket(bucketName)
+ .object(objectName)
+ .method(Method.GET).build();
+ return minioClient.getPresignedObjectUrl(args);
+ }
+
+ /**
+ * 将URLDecoder编码转成UTF8
+ * @param str
+ * @return
+ * @throws UnsupportedEncodingException
+ */
+ public static String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {
+ String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
+ return URLDecoder.decode(url, "UTF-8");
+ }
+
+ /****************************** Operate Files End ******************************/
+
+
+}
diff --git a/book-common/src/main/java/com/imooc/utils/MyInfo.java b/book-common/src/main/java/com/imooc/utils/MyInfo.java
new file mode 100644
index 0000000..d3f237e
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/utils/MyInfo.java
@@ -0,0 +1,9 @@
+package com.imooc.utils;
+
+public class MyInfo {
+
+ public static String getMobile() {
+ return "";
+ }
+
+}
diff --git a/book-common/src/main/java/com/imooc/utils/PagedGridResult.java b/book-common/src/main/java/com/imooc/utils/PagedGridResult.java
new file mode 100644
index 0000000..034e3c4
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/utils/PagedGridResult.java
@@ -0,0 +1,49 @@
+package com.imooc.utils;
+
+import java.util.List;
+
+/**
+ *
+ * @Title: PagedGridResult.java
+ * @Package com.imooc.utils
+ * @Description: 用来返回分页Grid的数据格式
+ * Copyright: Copyright (c) 2021
+ */
+public class PagedGridResult {
+
+ private int page; // 当前页数
+ private long total; // 总页数
+ private long records; // 总记录数
+ private List> rows; // 每行显示的内容
+
+ public int getPage() {
+ return page;
+ }
+ public void setPage(int page) {
+ this.page = page;
+ }
+
+ public long getTotal() {
+ return total;
+ }
+
+ public void setTotal(long total) {
+ this.total = total;
+ }
+
+ public void setTotal(int total) {
+ this.total = total;
+ }
+ public long getRecords() {
+ return records;
+ }
+ public void setRecords(long records) {
+ this.records = records;
+ }
+ public List> getRows() {
+ return rows;
+ }
+ public void setRows(List> rows) {
+ this.rows = rows;
+ }
+}
diff --git a/book-common/src/main/java/com/imooc/utils/RedisOperator.java b/book-common/src/main/java/com/imooc/utils/RedisOperator.java
new file mode 100644
index 0000000..8c9ab01
--- /dev/null
+++ b/book-common/src/main/java/com/imooc/utils/RedisOperator.java
@@ -0,0 +1,287 @@
+package com.imooc.utils;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.dao.DataAccessException;
+import org.springframework.data.redis.connection.RedisConnection;
+import org.springframework.data.redis.connection.StringRedisConnection;
+import org.springframework.data.redis.core.RedisCallback;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @Title: Redis 工具类
+ * @author 风间影月
+ */
+@Component
+public class RedisOperator {
+
+ @Autowired
+ private StringRedisTemplate redisTemplate;
+
+ // Key(键),简单的key-value操作
+
+ /**
+ * 判断key是否存在
+ * @param key
+ * @return
+ */
+ public boolean keyIsExist(String key) {
+ return redisTemplate.hasKey(key);
+ }
+
+ /**
+ * 实现命令:TTL key,以秒为单位,返回给定 key的剩余生存时间(TTL, time to live)。
+ *
+ * @param key
+ * @return
+ */
+ public long ttl(String key) {
+ return redisTemplate.getExpire(key);
+ }
+
+ /**
+ * 实现命令:expire 设置过期时间,单位秒
+ *
+ * @param key
+ * @return
+ */
+ public void expire(String key, long timeout) {
+ redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 实现命令:increment key,增加key一次
+ *
+ * @param key
+ * @return
+ */
+ public long increment(String key, long delta) {
+ return redisTemplate.opsForValue().increment(key, delta);
+ }
+
+ /**
+ * 累加,使用hash
+ */
+ public long incrementHash(String name, String key, long delta) {
+ return redisTemplate.opsForHash().increment(name, key, delta);
+ }
+
+ /**
+ * 累减,使用hash
+ */
+ public long decrementHash(String name, String key, long delta) {
+ delta = delta * (-1);
+ return redisTemplate.opsForHash().increment(name, key, delta);
+ }
+
+ /**
+ * hash 设置value
+ */
+ public void setHashValue(String name, String key, String value) {
+ redisTemplate.opsForHash().put(name, key, value);
+ }
+
+ /**
+ * hash 获得value
+ */
+ public String getHashValue(String name, String key) {
+ return (String)redisTemplate.opsForHash().get(name, key);
+ }
+
+ /**
+ * 实现命令:decrement key,减少key一次
+ *
+ * @param key
+ * @return
+ */
+ public long decrement(String key, long delta) {
+ return redisTemplate.opsForValue().decrement(key, delta);
+ }
+
+ /**
+ * 实现命令:KEYS pattern,查找所有符合给定模式 pattern的 key
+ */
+ public Set keys(String pattern) {
+ return redisTemplate.keys(pattern);
+ }
+
+ /**
+ * 实现命令:DEL key,删除一个key
+ *
+ * @param key
+ */
+ public void del(String key) {
+ redisTemplate.delete(key);
+ }
+
+ // String(字符串)
+
+ /**
+ * 实现命令:SET key value,设置一个key-value(将字符串值 value关联到 key)
+ *
+ * @param key
+ * @param value
+ */
+ public void set(String key, String value) {
+ redisTemplate.opsForValue().set(key, value);
+ }
+
+ /**
+ * 实现命令:SET key value EX seconds,设置key-value和超时时间(秒)
+ *
+ * @param key
+ * @param value
+ * @param timeout
+ * (以秒为单位)
+ */
+ public void set(String key, String value, long timeout) {
+ redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 如果key不存在,则设置,如果存在,则报错
+ * @param key
+ * @param value
+ */
+ public void setnx60s(String key, String value) {
+ redisTemplate.opsForValue().setIfAbsent(key, value, 60, TimeUnit.SECONDS);
+ }
+
+ /**
+ * 如果key不存在,则设置,如果存在,则报错
+ * @param key
+ * @param value
+ */
+ public void setnx(String key, String value) {
+ redisTemplate.opsForValue().setIfAbsent(key, value);
+ }
+
+ /**
+ * 实现命令:GET key,返回 key所关联的字符串值。
+ *
+ * @param key
+ * @return value
+ */
+ public String get(String key) {
+ return (String)redisTemplate.opsForValue().get(key);
+ }
+
+ /**
+ * 批量查询,对应mget
+ * @param keys
+ * @return
+ */
+ public List mget(List keys) {
+ return redisTemplate.opsForValue().multiGet(keys);
+ }
+
+ /**
+ * 批量查询,管道pipeline
+ * @param keys
+ * @return
+ */
+ public List