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 +# 架构 +![image](https://github.com/vercen/douyin_dome/assets/70558042/460d308a-0b6b-4918-a443-d2a6bf77e2dd) +![image](https://github.com/vercen/douyin_dome/assets/70558042/50b63912-39b0-4095-8c94-a731f59cb78b) + +# 效果 + +![Screenshot_2023-06-01-11-51-48-628_uni UNI800FF13](https://github.com/vercen/douyin_dome/assets/70558042/60a656c8-6492-4d23-b569-7c8810ba507b) +![Screenshot_2023-06-01-11-52-17-198_uni UNI800FF13](https://github.com/vercen/douyin_dome/assets/70558042/63656269-ab10-4064-9b43-0e703a3d70b5) +![Screenshot_2023-06-01-11-52-20-875_uni UNI800FF13](https://github.com/vercen/douyin_dome/assets/70558042/9bd20a3c-2774-4a21-b0c1-b8a4945e2273) +![Screenshot_2023-06-01-11-52-24-302_uni UNI800FF13](https://github.com/vercen/douyin_dome/assets/70558042/118a8284-a64b-48f9-9caa-47308ec1c532) 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 batchGet(List keys) { + +// nginx -> keepalive +// redis -> pipeline + + List result = redisTemplate.executePipelined(new RedisCallback() { + @Override + public String doInRedis(RedisConnection connection) throws DataAccessException { + StringRedisConnection src = (StringRedisConnection)connection; + + for (String k : keys) { + src.get(k); + } + return null; + } + }); + + return result; + } + + + // Hash(哈希表) + + /** + * 实现命令:HSET key field value,将哈希表 key中的域 field的值设为 value + * + * @param key + * @param field + * @param value + */ + public void hset(String key, String field, Object value) { + redisTemplate.opsForHash().put(key, field, value); + } + + /** + * 实现命令:HGET key field,返回哈希表 key中给定域 field的值 + * + * @param key + * @param field + * @return + */ + public String hget(String key, String field) { + return (String) redisTemplate.opsForHash().get(key, field); + } + + /** + * 实现命令:HDEL key field [field ...],删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。 + * + * @param key + * @param fields + */ + public void hdel(String key, Object... fields) { + redisTemplate.opsForHash().delete(key, fields); + } + + /** + * 实现命令:HGETALL key,返回哈希表 key中,所有的域和值。 + * + * @param key + * @return + */ + public Map hgetall(String key) { + return redisTemplate.opsForHash().entries(key); + } + + // List(列表) + + /** + * 实现命令:LPUSH key value,将一个值 value插入到列表 key的表头 + * + * @param key + * @param value + * @return 执行 LPUSH命令后,列表的长度。 + */ + public long lpush(String key, String value) { + return redisTemplate.opsForList().leftPush(key, value); + } + + /** + * 实现命令:LPOP key,移除并返回列表 key的头元素。 + * + * @param key + * @return 列表key的头元素。 + */ + public String lpop(String key) { + return (String)redisTemplate.opsForList().leftPop(key); + } + + /** + * 实现命令:RPUSH key value,将一个值 value插入到列表 key的表尾(最右边)。 + * + * @param key + * @param value + * @return 执行 LPUSH命令后,列表的长度。 + */ + public long rpush(String key, String value) { + return redisTemplate.opsForList().rightPush(key, value); + } + +} \ No newline at end of file diff --git a/book-common/src/main/java/com/imooc/utils/SMSUtils.java b/book-common/src/main/java/com/imooc/utils/SMSUtils.java new file mode 100644 index 0000000..fbf7028 --- /dev/null +++ b/book-common/src/main/java/com/imooc/utils/SMSUtils.java @@ -0,0 +1,74 @@ +package com.imooc.utils; + +import com.tencentcloudapi.common.Credential; +import com.tencentcloudapi.common.exception.TencentCloudSDKException; +import com.tencentcloudapi.common.profile.ClientProfile; +import com.tencentcloudapi.common.profile.HttpProfile; +import com.tencentcloudapi.sms.v20210111.SmsClient; +import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest; +import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class SMSUtils { + @Autowired + private TencentCloudProperties tencentCloudProperties; + + public void sendSMS(String phone, String code) throws Exception { + try { + /* 必要步骤: + * 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。 + * 这里采用的是从环境变量读取的方式,需要在环境变量中先设置这两个值。 + * 你也可以直接在代码中写死密钥对,但是小心不要将代码复制、上传或者分享给他人, + * 以免泄露密钥对危及你的财产安全。 + * CAM密匙查询获取: https://console.cloud.tencent.com/cam/capi*/ + Credential cred = new Credential(tencentCloudProperties.getSecretId(), + tencentCloudProperties.getSecretKey()); + + // 实例化一个http选项,可选的,没有特殊需求可以跳过 + HttpProfile httpProfile = new HttpProfile(); + +// httpProfile.setReqMethod("POST"); // 默认使用POST + + /* SDK会自动指定域名。通常是不需要特地指定域名的,但是如果你访问的是金融区的服务 + * 则必须手动指定域名,例如sms的上海金融区域名: sms.ap-shanghai-fsi.tencentcloudapi.com */ + httpProfile.setEndpoint("sms.tencentcloudapi.com"); + + // 实例化一个client选项 + ClientProfile clientProfile = new ClientProfile(); + clientProfile.setHttpProfile(httpProfile); + // 实例化要请求产品的client对象,clientProfile是可选的 + SmsClient client = new SmsClient(cred, "ap-nanjing", clientProfile); + + // 实例化一个请求对象,每个接口都会对应一个request对象 + SendSmsRequest req = new SendSmsRequest(); + String[] phoneNumberSet1 = {"+86" + phone};//电话号码 + req.setPhoneNumberSet(phoneNumberSet1); + req.setSmsSdkAppId("1400966042"); // 短信应用ID: 短信SdkAppId在 [短信控制台] 添加应用后生成的实际SdkAppId + req.setSignName("无终街科技"); // 签名 + req.setTemplateId("2375314"); // 模板id:必须填写已审核通过的模板 ID。模板ID可登录 [短信控制台] 查看 + + /* 模板参数(自定义占位变量): 若无模板参数,则设置为空 */ + String[] templateParamSet1 = {code}; + req.setTemplateParamSet(templateParamSet1); + + // 返回的resp是一个SendSmsResponse的实例,与请求对象对应 + SendSmsResponse resp = client.SendSms(req); + // 输出json格式的字符串回包 +// System.out.println(SendSmsResponse.toJsonString(resp)); + } catch (TencentCloudSDKException e) { + System.out.println(e.toString()); + } + } + + public static void main(String[] args) { + try { + new SMSUtils().sendSMS("15237439161", "7896"); + } catch (Exception e) { + e.printStackTrace(); + } + } +} + + diff --git a/book-common/src/main/java/com/imooc/utils/TencentCloudProperties.java b/book-common/src/main/java/com/imooc/utils/TencentCloudProperties.java new file mode 100644 index 0000000..6dbf8e4 --- /dev/null +++ b/book-common/src/main/java/com/imooc/utils/TencentCloudProperties.java @@ -0,0 +1,23 @@ +package com.imooc.utils; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.PropertySource; +import org.springframework.stereotype.Component; + +/** + * @author vercen + * @version 1.0 + * @date 2023/5/25 10:21 + * 获取短信配置 + */ +@Component +@Data +@PropertySource("classpath:tencentcloud.properties") +@ConfigurationProperties(prefix = "tencent.cloud") +public class TencentCloudProperties { + + private String secretId; + private String secretKey; + +} diff --git a/book-common/src/main/java/org/n3r/idworker/Code.java b/book-common/src/main/java/org/n3r/idworker/Code.java new file mode 100644 index 0000000..2c59dd2 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/Code.java @@ -0,0 +1,35 @@ +package org.n3r.idworker; + +import org.n3r.idworker.strategy.DefaultRandomCodeStrategy; + +public class Code { + private static RandomCodeStrategy strategy; + + static { + RandomCodeStrategy strategy = new DefaultRandomCodeStrategy(); + strategy.init(); + configure(strategy); + } + + public static synchronized void configure(RandomCodeStrategy custom) { + if (strategy == custom) return; + if (strategy != null) strategy.release(); + + strategy = custom; + } + + /** + * Next Unique code. + * The max length will be 1024-Integer.MAX-Integer.MAX(2147483647) which has 4+10+10+2*1=26 characters. + * The min length will be 0-0. + * + * @return unique string code. + */ + public static synchronized String next() { + long workerId = Id.getWorkerId(); + int prefix = strategy.prefix(); + int next = strategy.next(); + + return String.format("%d-%03d-%06d", workerId, prefix, next); + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/DayCode.java b/book-common/src/main/java/org/n3r/idworker/DayCode.java new file mode 100644 index 0000000..85ebe88 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/DayCode.java @@ -0,0 +1,19 @@ +package org.n3r.idworker; + +import org.n3r.idworker.strategy.DayPrefixRandomCodeStrategy; + +public class DayCode { + static RandomCodeStrategy strategy; + + static { + DayPrefixRandomCodeStrategy dayPrefixCodeStrategy = new DayPrefixRandomCodeStrategy("yyMM"); + dayPrefixCodeStrategy.setMinRandomSize(7); + dayPrefixCodeStrategy.setMaxRandomSize(7); + strategy = dayPrefixCodeStrategy; + strategy.init(); + } + + public static synchronized String next() { + return String.format("%d-%04d-%07d", Id.getWorkerId(), strategy.prefix(), strategy.next()); + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/Id.java b/book-common/src/main/java/org/n3r/idworker/Id.java new file mode 100644 index 0000000..dea7f0f --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/Id.java @@ -0,0 +1,29 @@ +package org.n3r.idworker; + +import org.n3r.idworker.strategy.DefaultWorkerIdStrategy; + +public class Id { + private static WorkerIdStrategy workerIdStrategy; + private static IdWorker idWorker; + + static { + configure(DefaultWorkerIdStrategy.instance); + } + + public static synchronized void configure(WorkerIdStrategy custom) { + if (workerIdStrategy == custom) return; + + if (workerIdStrategy != null) workerIdStrategy.release(); + workerIdStrategy = custom; + workerIdStrategy.initialize(); + idWorker = new IdWorker(workerIdStrategy.availableWorkerId()); + } + + public static long next() { + return idWorker.nextId(); + } + + public static long getWorkerId() { + return idWorker.getWorkerId(); + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/IdWorker.java b/book-common/src/main/java/org/n3r/idworker/IdWorker.java new file mode 100644 index 0000000..40c4211 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/IdWorker.java @@ -0,0 +1,91 @@ +package org.n3r.idworker; + +import java.security.SecureRandom; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class IdWorker { + protected long epoch = 1288834974657L; +// protected long epoch = 1387886498127L; // 2013-12-24 20:01:38.127 + + + protected long workerIdBits = 10L; + protected long maxWorkerId = -1L ^ (-1L << workerIdBits); + protected long sequenceBits = 11L; + + protected long workerIdShift = sequenceBits; + protected long timestampLeftShift = sequenceBits + workerIdBits; + protected long sequenceMask = -1L ^ (-1L << sequenceBits); + + protected long lastMillis = -1L; + + protected final long workerId; + protected long sequence = 0L; + protected Logger logger = LoggerFactory.getLogger(IdWorker.class); + + public IdWorker(long workerId) { + this.workerId = checkWorkerId(workerId); + + logger.debug("worker starting. timestamp left shift {}, worker id {}", timestampLeftShift, workerId); + } + + public long getEpoch() { + return epoch; + } + + private long checkWorkerId(long workerId) { + // sanity check for workerId + if (workerId > maxWorkerId || workerId < 0) { + int rand = new SecureRandom().nextInt((int) maxWorkerId + 1); + logger.warn("worker Id can't be greater than {} or less than 0, use a random {}", maxWorkerId, rand); + return rand; + } + + return workerId; + } + + public synchronized long nextId() { + long timestamp = millisGen(); + + if (timestamp < lastMillis) { + logger.error("clock is moving backwards. Rejecting requests until {}.", lastMillis); + throw new InvalidSystemClock(String.format( + "Clock moved backwards. Refusing to generate id for {} milliseconds", lastMillis - timestamp)); + } + + if (lastMillis == timestamp) { + sequence = (sequence + 1) & sequenceMask; + if (sequence == 0) + timestamp = tilNextMillis(lastMillis); + } else { + sequence = 0; + } + + lastMillis = timestamp; + long diff = timestamp - getEpoch(); + return (diff << timestampLeftShift) | + (workerId << workerIdShift) | + sequence; + } + + protected long tilNextMillis(long lastMillis) { + long millis = millisGen(); + while (millis <= lastMillis) + millis = millisGen(); + + return millis; + } + + protected long millisGen() { + return System.currentTimeMillis(); + } + + public long getLastMillis() { + return lastMillis; + } + + public long getWorkerId() { + return workerId; + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/InvalidSystemClock.java b/book-common/src/main/java/org/n3r/idworker/InvalidSystemClock.java new file mode 100644 index 0000000..24c4c1e --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/InvalidSystemClock.java @@ -0,0 +1,7 @@ +package org.n3r.idworker; + +public class InvalidSystemClock extends RuntimeException { + public InvalidSystemClock(String message) { + super(message); + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/RandomCodeStrategy.java b/book-common/src/main/java/org/n3r/idworker/RandomCodeStrategy.java new file mode 100644 index 0000000..0a4c6b8 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/RandomCodeStrategy.java @@ -0,0 +1,11 @@ +package org.n3r.idworker; + +public interface RandomCodeStrategy { + void init(); + + int prefix(); + + int next(); + + void release(); +} diff --git a/book-common/src/main/java/org/n3r/idworker/Sid.java b/book-common/src/main/java/org/n3r/idworker/Sid.java new file mode 100644 index 0000000..4c9e328 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/Sid.java @@ -0,0 +1,64 @@ +package org.n3r.idworker; + +import org.n3r.idworker.strategy.DefaultWorkerIdStrategy; +import org.n3r.idworker.utils.Utils; +import org.springframework.stereotype.Component; + +import java.text.SimpleDateFormat; +import java.util.Date; + +@Component +public class Sid { + private static WorkerIdStrategy workerIdStrategy; + private static IdWorker idWorker; + + static { + configure(DefaultWorkerIdStrategy.instance); + } + + + public static synchronized void configure(WorkerIdStrategy custom) { + if (workerIdStrategy != null) { + workerIdStrategy.release(); + } + workerIdStrategy = custom; + idWorker = new IdWorker(workerIdStrategy.availableWorkerId()) { + @Override + public long getEpoch() { + return Utils.midnightMillis(); + } + }; + } + + /** + * 一天最大毫秒86400000,最大占用27比特 + * 27+10+11=48位 最大值281474976710655(15字),YK0XXHZ827(10字) + * 6位(YYMMDD)+15位,共21位 + * + * @return 固定21位数字字符串 + */ + + public static String next() { + long id = idWorker.nextId(); + String yyMMdd = new SimpleDateFormat("yyMMdd").format(new Date()); + return yyMMdd + String.format("%014d", id); + } + + + /** + * 返回固定16位的字母数字混编的字符串。 + */ + public String nextShort() { + long id = idWorker.nextId(); + String yyMMdd = new SimpleDateFormat("yyMMdd").format(new Date()); + return yyMMdd + Utils.padLeft(Utils.encode(id), 10, '0'); + } + + public static void main(String[] args) { + String aa = new Sid().nextShort(); + String bb = new Sid().next(); + + System.out.println(aa); + System.out.println(bb); + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/Test.java b/book-common/src/main/java/org/n3r/idworker/Test.java new file mode 100644 index 0000000..1ef1ca0 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/Test.java @@ -0,0 +1,12 @@ +package org.n3r.idworker; + +public class Test { + + public static void main(String[] args) { + + for (int i = 0 ; i < 1000 ; i ++) { +// System.out.println(Sid.nextShort()); + } + } + +} diff --git a/book-common/src/main/java/org/n3r/idworker/WorkerIdStrategy.java b/book-common/src/main/java/org/n3r/idworker/WorkerIdStrategy.java new file mode 100644 index 0000000..f34d722 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/WorkerIdStrategy.java @@ -0,0 +1,9 @@ +package org.n3r.idworker; + +public interface WorkerIdStrategy { + void initialize(); + + long availableWorkerId(); + + void release(); +} diff --git a/book-common/src/main/java/org/n3r/idworker/strategy/DayPrefixRandomCodeStrategy.java b/book-common/src/main/java/org/n3r/idworker/strategy/DayPrefixRandomCodeStrategy.java new file mode 100644 index 0000000..b86bd1c --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/strategy/DayPrefixRandomCodeStrategy.java @@ -0,0 +1,41 @@ +package org.n3r.idworker.strategy; + +import java.text.SimpleDateFormat; +import java.util.Date; + +public class DayPrefixRandomCodeStrategy extends DefaultRandomCodeStrategy { + private final String dayFormat; + private String lastDay; + + public DayPrefixRandomCodeStrategy(String dayFormat) { + this.dayFormat = dayFormat; + } + + @Override + public void init() { + String day = createDate(); + if (day.equals(lastDay)) + throw new RuntimeException("init failed for day unrolled"); + + lastDay = day; + + availableCodes.clear(); + release(); + + prefixIndex = Integer.parseInt(lastDay); + if (tryUsePrefix()) return; + + throw new RuntimeException("prefix is not available " + prefixIndex); + } + + private String createDate() { + return new SimpleDateFormat(dayFormat).format(new Date()); + } + + @Override + public int next() { + if (!lastDay.equals(createDate())) init(); + + return super.next(); + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/strategy/DefaultRandomCodeStrategy.java b/book-common/src/main/java/org/n3r/idworker/strategy/DefaultRandomCodeStrategy.java new file mode 100644 index 0000000..5c9d8c7 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/strategy/DefaultRandomCodeStrategy.java @@ -0,0 +1,197 @@ +package org.n3r.idworker.strategy; + +import org.n3r.idworker.Id; +import org.n3r.idworker.RandomCodeStrategy; +import org.n3r.idworker.utils.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.ArrayDeque; +import java.util.BitSet; +import java.util.Queue; + +public class DefaultRandomCodeStrategy implements RandomCodeStrategy { + public static final int MAX_BITS = 1000000; + + Logger log = LoggerFactory.getLogger(DefaultRandomCodeStrategy.class); + + File idWorkerHome = Utils.createIdWorkerHome(); + volatile FileLock fileLock; + BitSet codesFilter; + + int prefixIndex = -1; + File codePrefixIndex; + + int minRandomSize = 6; + int maxRandomSize = 6; + + public DefaultRandomCodeStrategy() { + destroyFileLockWhenShutdown(); + } + + @Override + public void init() { + release(); + + while (++prefixIndex < 1000) { + if (tryUsePrefix()) return; + } + + throw new RuntimeException("all prefixes are used up, the world maybe ends!"); + } + + public DefaultRandomCodeStrategy setMinRandomSize(int minRandomSize) { + this.minRandomSize = minRandomSize; + return this; + } + + public DefaultRandomCodeStrategy setMaxRandomSize(int maxRandomSize) { + this.maxRandomSize = maxRandomSize; + return this; + } + + protected boolean tryUsePrefix() { + codePrefixIndex = new File(idWorkerHome, Id.getWorkerId() + ".code.prefix." + prefixIndex); + + if (!createPrefixIndexFile()) return false; + if (!createFileLock()) return false; + if (!createBloomFilter()) return false; + + log.info("get available prefix index file {}", codePrefixIndex); + + return true; + } + + private boolean createFileLock() { + if (fileLock != null) fileLock.destroy(); + fileLock = new FileLock(codePrefixIndex); + return fileLock.tryLock(); + } + + private boolean createBloomFilter() { + codesFilter = fileLock.readObject(); + if (codesFilter == null) { + log.info("create new bloom filter"); + codesFilter = new BitSet(MAX_BITS); // 2^24 + } else { + int size = codesFilter.cardinality(); + if (size >= MAX_BITS) { + log.warn("bloom filter with prefix file {} is already full", codePrefixIndex); + return false; + } + log.info("recreate bloom filter with cardinality {}", size); + } + + return true; + } + + private void destroyFileLockWhenShutdown() { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + release(); + } + }); + } + + private boolean createPrefixIndexFile() { + try { + codePrefixIndex.createNewFile(); + return codePrefixIndex.exists(); + } catch (IOException e) { + e.printStackTrace(); + log.warn("create file {} error {}", codePrefixIndex, e.getMessage()); + } + return false; + } + + @Override + public int prefix() { + return prefixIndex; + } + + static final int CACHE_CODES_NUM = 1000; + + SecureRandom secureRandom = new SecureRandom(); + Queue availableCodes = new ArrayDeque(CACHE_CODES_NUM); + + @Override + public int next() { + if (availableCodes.isEmpty()) generate(); + + return availableCodes.poll(); + } + + @Override + public synchronized void release() { + if (fileLock != null) { + fileLock.writeObject(codesFilter); + fileLock.destroy(); + fileLock = null; + } + } + + private void generate() { + for (int i = 0; i < CACHE_CODES_NUM; ++i) + availableCodes.add(generateOne()); + + fileLock.writeObject(codesFilter); + } + + private int generateOne() { + while (true) { + int code = secureRandom.nextInt(max(maxRandomSize)); + boolean existed = contains(code); + + code = !existed ? add(code) : tryFindAvailableCode(code); + if (code >= 0) return code; + + init(); + } + } + + private int tryFindAvailableCode(int code) { + int next = codesFilter.nextClearBit(code); + if (next != -1 && next < max(maxRandomSize)) return add(next); + + next = codesFilter.previousClearBit(code); + if (next != -1) return add(next); + + return -1; + } + + private int add(int code) { + codesFilter.set(code); + return code; + } + + private boolean contains(int code) { + return codesFilter.get(code); + } + + + private int max(int size) { + switch (size) { + case 1: // fall through + case 2: // fall through + case 3: // fall through + case 4: + return 10000; + case 5: + return 100000; + case 6: + return 1000000; + case 7: + return 10000000; + case 8: + return 100000000; + case 9: + return 1000000000; + default: + return Integer.MAX_VALUE; + } + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/strategy/DefaultWorkerIdStrategy.java b/book-common/src/main/java/org/n3r/idworker/strategy/DefaultWorkerIdStrategy.java new file mode 100644 index 0000000..7b940bf --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/strategy/DefaultWorkerIdStrategy.java @@ -0,0 +1,205 @@ +package org.n3r.idworker.strategy; + +import org.n3r.idworker.WorkerIdStrategy; +import org.n3r.idworker.utils.HttpReq; +import org.n3r.idworker.utils.Ip; +import org.n3r.idworker.utils.Props; +import org.n3r.idworker.utils.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.security.SecureRandom; +import java.util.Properties; +import java.util.Random; + +public class DefaultWorkerIdStrategy implements WorkerIdStrategy { + static long workerIdBits = 10L; + static long maxWorkerId = -1L ^ (-1L << workerIdBits); + static Random random = new SecureRandom(); + + public static final WorkerIdStrategy instance = new DefaultWorkerIdStrategy(); + + private final Properties props = + Props.tryProperties("idworker-client.properties", Utils.DOT_IDWORKERS); + private final String idWorkerServerUrl = + props.getProperty("server.address", "http://id.worker.server:18001"); + + String userName = System.getProperty("user.name"); + + String ipDotUsername = Ip.ip + "." + userName; + String ipudotlock = ipDotUsername + ".lock."; + int workerIdIndex = ipudotlock.length(); + long workerId; + FileLock fileLock; + + Logger logger = LoggerFactory.getLogger(DefaultWorkerIdStrategy.class); + private boolean inited; + + + private void init() { + workerId = findAvailWorkerId(); + if (workerId >= 0) { + destroyFileLockWhenShutdown(); + startSyncThread(); + } else { + syncWithWorkerIdServer(); + workerId = findAvailWorkerId(); + if (workerId < 0) workerId = increaseWithWorkerIdServer(); + } + + if (workerId < 0) workerId = tryToCreateOnIp(); + if (workerId < 0) { + logger.warn("DANGEROUS!!! Try to use random worker id."); + workerId = tryToRandomOnIp(); // Try avoiding! it could cause duplicated + } + + if (workerId < 0) { + logger.warn("the world may be ended!"); + throw new RuntimeException("the world may be ended"); + } + } + + private void destroyFileLockWhenShutdown() { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + fileLock.destroy(); + } + }); + } + + private void startSyncThread() { + new Thread() { + @Override + public void run() { + syncWithWorkerIdServer(); + } + }.start(); + } + + private long increaseWithWorkerIdServer() { + String incId = HttpReq.get(idWorkerServerUrl) + .req("/inc") + .param("ipu", ipDotUsername) + .exec(); + if (incId == null || incId.trim().isEmpty()) return -1L; + + long lid = Long.parseLong(incId); + + return checkAvail(lid); + } + + private long tryToCreateOnIp() { + long wid = Ip.lip & maxWorkerId; + + return checkAvail(wid); + } + + private long tryToRandomOnIp() { + long avaiWorkerId = -1L; + long tryTimes = -1; + + while (avaiWorkerId < 0 && ++tryTimes < maxWorkerId) { + long wid = Ip.lip & random.nextInt((int) maxWorkerId); + + avaiWorkerId = checkAvail(wid); + } + return avaiWorkerId; + } + + private long checkAvail(long wid) { + long availWorkerId = -1L; + try { + File idWorkerHome = Utils.createIdWorkerHome(); + new File(idWorkerHome, ipudotlock + String.format("%04d", wid)).createNewFile(); + availWorkerId = findAvailWorkerId(); + } catch (IOException e) { + logger.warn("checkAvail error", e); + } + + return availWorkerId; + } + + private void syncWithWorkerIdServer() { + String syncIds = HttpReq.get(idWorkerServerUrl).req("/sync") + .param("ipu", ipDotUsername).param("ids", buildWorkerIdsOfCurrentIp()) + .exec(); + if (syncIds == null || syncIds.trim().isEmpty()) return; + + String[] syncIdsArr = syncIds.split(","); + File idWorkerHome = Utils.createIdWorkerHome(); + for (String syncId : syncIdsArr) { + try { + new File(idWorkerHome, ipudotlock + syncId).createNewFile(); + } catch (IOException e) { + logger.warn("create workerid lock file error", e); + } + } + } + + private String buildWorkerIdsOfCurrentIp() { + StringBuilder sb = new StringBuilder(); + File idWorkerHome = Utils.createIdWorkerHome(); + for (File lockFile : idWorkerHome.listFiles()) { + // check the format like 10.142.1.151.lock.0001 + if (!lockFile.getName().startsWith(ipudotlock)) continue; + + String workerId = lockFile.getName().substring(workerIdIndex); + if (!workerId.matches("\\d\\d\\d\\d")) continue; + + if (sb.length() > 0) sb.append(','); + sb.append(workerId); + } + + return sb.toString(); + } + + + /** + * Find the local available worker id. + * + * @return -1 when N/A + */ + private long findAvailWorkerId() { + File idWorkerHome = Utils.createIdWorkerHome(); + + for (File lockFile : idWorkerHome.listFiles()) { + // check the format like 10.142.1.151.lock.0001 + if (!lockFile.getName().startsWith(ipudotlock)) continue; + + String workerId = lockFile.getName().substring(workerIdIndex); + if (!workerId.matches("\\d\\d\\d\\d")) continue; + + FileLock fileLock = new FileLock(lockFile); + if (!fileLock.tryLock()) { + fileLock.destroy(); + continue; + } + + this.fileLock = fileLock; + return Long.parseLong(workerId); + } + + return -1; + } + + @Override + public void initialize() { + if (inited) return; + init(); + this.inited = true; + } + + @Override + public long availableWorkerId() { + return workerId; + } + + @Override + public void release() { + if (fileLock != null) fileLock.destroy(); + inited = false; + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/strategy/FileLock.java b/book-common/src/main/java/org/n3r/idworker/strategy/FileLock.java new file mode 100644 index 0000000..d2cf66f --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/strategy/FileLock.java @@ -0,0 +1,132 @@ +package org.n3r.idworker.strategy; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.nio.channels.Channels; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.FileChannel; +import java.nio.channels.OverlappingFileLockException; + +/** + * A file lock a la flock/funlock + *

+ * The given path will be created and opened if it doesn't exist. + */ +public class FileLock { + private final File file; + private FileChannel channel; + private java.nio.channels.FileLock flock = null; + Logger logger = LoggerFactory.getLogger(FileLock.class); + + public FileLock(File file) { + this.file = file; + + try { + file.createNewFile(); // create the file if it doesn't exist + channel = new RandomAccessFile(file, "rw").getChannel(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + + /** + * Lock the file or throw an exception if the lock is already held + */ + public void lock() { + try { + synchronized (this) { + logger.trace("Acquiring lock on {}", file.getAbsolutePath()); + flock = channel.lock(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Try to lock the file and return true if the locking succeeds + */ + public boolean tryLock() { + synchronized (this) { + logger.trace("Acquiring lock on {}", file.getAbsolutePath()); + try { + // weirdly this method will return null if the lock is held by another + // process, but will throw an exception if the lock is held by this process + // so we have to handle both cases + flock = channel.tryLock(); + return flock != null; + } catch (OverlappingFileLockException e) { + return false; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Unlock the lock if it is held + */ + public void unlock() { + synchronized (this) { + logger.trace("Releasing lock on {}", file.getAbsolutePath()); + if (flock == null) return; + try { + flock.release(); + } catch (ClosedChannelException e) { + // Ignore + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + /** + * Destroy this lock, closing the associated FileChannel + */ + public void destroy() { + synchronized (this) { + unlock(); + if (!channel.isOpen()) return; + + try { + channel.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + + @SuppressWarnings("unchecked") + public T readObject() { + try { + InputStream is = Channels.newInputStream(channel); + ObjectInputStream objectReader = new ObjectInputStream(is); + return (T) objectReader.readObject(); + } catch (EOFException e) { + } catch (Exception e) { + throw new RuntimeException(e); + } + + return null; + } + + + public synchronized boolean writeObject(Object object) { + if (!channel.isOpen()) return false; + + try { + channel.position(0); + OutputStream out = Channels.newOutputStream(channel); + ObjectOutputStream objectOutput = new ObjectOutputStream(out); + objectOutput.writeObject(object); + return true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/utils/HttpReq.java b/book-common/src/main/java/org/n3r/idworker/utils/HttpReq.java new file mode 100644 index 0000000..f7d6381 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/utils/HttpReq.java @@ -0,0 +1,113 @@ +package org.n3r.idworker.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.*; + +public class HttpReq { + private final String baseUrl; + private String req; + private StringBuilder params = new StringBuilder(); + Logger logger = LoggerFactory.getLogger(HttpReq.class); + + public HttpReq(String baseUrl) { + this.baseUrl = baseUrl; + } + + public static HttpReq get(String baseUrl) { + return new HttpReq(baseUrl); + } + + public HttpReq req(String req) { + this.req = req; + return this; + } + + public HttpReq param(String name, String value) { + if (params.length() > 0) params.append('&'); + try { + params.append(name).append('=').append(URLEncoder.encode(value, "UTF-8")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + + return this; + } + + public String exec() { + HttpURLConnection http = null; + try { + http = (HttpURLConnection) new URL(baseUrl + + (req == null ? "" : req) + + (params.length() > 0 ? ("?" + params) : "")).openConnection(); + http.setRequestProperty("Accept-Charset", "UTF-8"); + HttpURLConnection.setFollowRedirects(false); + http.setConnectTimeout(5 * 1000); + http.setReadTimeout(5 * 1000); + http.connect(); + + int status = http.getResponseCode(); + String charset = getCharset(http.getHeaderField("Content-Type")); + + if (status == 200) { + return readResponseBody(http, charset); + } else { + logger.warn("non 200 respoonse :" + readErrorResponseBody(http, status, charset)); + return null; + } + } catch (Exception e) { + logger.error("exec error {}", e.getMessage()); + return null; + } finally { + if (http != null) http.disconnect(); + } + + } + + private static String readErrorResponseBody(HttpURLConnection http, int status, String charset) throws IOException { + InputStream errorStream = http.getErrorStream(); + if (errorStream != null) { + String error = toString(charset, errorStream); + return ("STATUS CODE =" + status + "\n\n" + error); + } else { + return ("STATUS CODE =" + status); + } + } + + private static String readResponseBody(HttpURLConnection http, String charset) throws IOException { + InputStream inputStream = http.getInputStream(); + + return toString(charset, inputStream); + } + + private static String toString(String charset, InputStream inputStream) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + + int length; + while ((length = inputStream.read(buffer)) != -1) { + baos.write(buffer, 0, length); + } + + return new String(baos.toByteArray(), charset); + } + + private static String getCharset(String contentType) { + if (contentType == null) return "UTF-8"; + + String charset = null; + for (String param : contentType.replace(" ", "").split(";")) { + if (param.startsWith("charset=")) { + charset = param.split("=", 2)[1]; + break; + } + } + + return charset == null ? "UTF-8" : charset; + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/utils/IPv4Utils.java b/book-common/src/main/java/org/n3r/idworker/utils/IPv4Utils.java new file mode 100644 index 0000000..fed7acc --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/utils/IPv4Utils.java @@ -0,0 +1,60 @@ +package org.n3r.idworker.utils; + +/** + * This utility provides methods to either convert an IPv4 address to its long format or a 32bit dotted format. + * + * @author Aion + * Created on 22/11/12 + */ +public class IPv4Utils { + + /** + * Returns the long format of the provided IP address. + * + * @param ipAddress the IP address + * @return the long format of ipAddress + * @throws IllegalArgumentException if ipAddress is invalid + */ + public static long toLong(String ipAddress) { + if (ipAddress == null || ipAddress.isEmpty()) { + throw new IllegalArgumentException("ip address cannot be null or empty"); + } + String[] octets = ipAddress.split(java.util.regex.Pattern.quote(".")); + if (octets.length != 4) { + throw new IllegalArgumentException("invalid ip address"); + } + long ip = 0; + for (int i = 3; i >= 0; i--) { + long octet = Long.parseLong(octets[3 - i]); + if (octet > 255 || octet < 0) { + throw new IllegalArgumentException("invalid ip address"); + } + ip |= octet << (i * 8); + } + return ip; + } + + /** + * Returns the 32bit dotted format of the provided long ip. + * + * @param ip the long ip + * @return the 32bit dotted format of ip + * @throws IllegalArgumentException if ip is invalid + */ + public static String toString(long ip) { + // if ip is bigger than 255.255.255.255 or smaller than 0.0.0.0 + if (ip > 4294967295l || ip < 0) { + throw new IllegalArgumentException("invalid ip"); + } + StringBuilder ipAddress = new StringBuilder(); + for (int i = 3; i >= 0; i--) { + int shift = i * 8; + ipAddress.append((ip & (0xff << shift)) >> shift); + if (i > 0) { + ipAddress.append("."); + } + } + return ipAddress.toString(); + } + +} \ No newline at end of file diff --git a/book-common/src/main/java/org/n3r/idworker/utils/Ip.java b/book-common/src/main/java/org/n3r/idworker/utils/Ip.java new file mode 100644 index 0000000..7dca9da --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/utils/Ip.java @@ -0,0 +1,50 @@ +package org.n3r.idworker.utils; + + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +public class Ip { + static Logger logger = LoggerFactory.getLogger(Ip.class); + + public static String ip; + public static long lip; + + static { + try { + InetAddress localHostLANAddress = getFirstNonLoopbackAddress(); + ip = localHostLANAddress.getHostAddress(); + + byte[] address = localHostLANAddress.getAddress(); + lip = ((address [0] & 0xFFL) << (3*8)) + + ((address [1] & 0xFFL) << (2*8)) + + ((address [2] & 0xFFL) << (1*8)) + + (address [3] & 0xFFL); + } catch (Exception e) { + logger.error("get ipv4 failed ", e); + } + } + + private static InetAddress getFirstNonLoopbackAddress() throws SocketException { + Enumeration en = NetworkInterface.getNetworkInterfaces(); + while (en.hasMoreElements()) { + NetworkInterface i = (NetworkInterface) en.nextElement(); + for (Enumeration en2 = i.getInetAddresses(); en2.hasMoreElements(); ) { + InetAddress addr = (InetAddress) en2.nextElement(); + if (addr.isLoopbackAddress()) continue; + + if (addr instanceof Inet4Address) { + return addr; + } + } + } + return null; + } + +} diff --git a/book-common/src/main/java/org/n3r/idworker/utils/Props.java b/book-common/src/main/java/org/n3r/idworker/utils/Props.java new file mode 100644 index 0000000..d3d43d7 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/utils/Props.java @@ -0,0 +1,70 @@ +package org.n3r.idworker.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.util.Properties; + +import static java.io.File.separator; +import static org.n3r.idworker.utils.Serializes.closeQuietly; + +public class Props { + static Logger log = LoggerFactory.getLogger(Props.class); + + public static Properties tryProperties(String propertiesFileName, String userHomeBasePath) { + Properties properties = new Properties(); + InputStream is = null; + try { + is = Props.tryResource(propertiesFileName, userHomeBasePath, Silent.ON); + if (is != null) properties.load(is); + } catch (IOException e) { + log.error("load properties error: {}", e.getMessage()); + } finally { + closeQuietly(is); + } + + return properties; + } + + + enum Silent {ON, OFF} + + public static InputStream tryResource(String propertiesFileName, String userHomeBasePath, Silent silent) { + InputStream is = currentDirResource(new File(propertiesFileName)); + if (is != null) return is; + + is = userHomeResource(propertiesFileName, userHomeBasePath); + if (is != null) return is; + + is = classpathResource(propertiesFileName); + if (is != null || silent == Silent.ON) return is; + + throw new RuntimeException("fail to find " + propertiesFileName + " in current dir or classpath"); + } + + private static InputStream userHomeResource(String pathname, String appHome) { + String filePath = System.getProperty("user.home") + separator + appHome; + File dir = new File(filePath); + if (!dir.exists()) return null; + + return currentDirResource(new File(dir, pathname)); + } + + private static InputStream currentDirResource(File file) { + if (!file.exists()) return null; + + try { + return new FileInputStream(file); + } catch (FileNotFoundException e) { + // This should not happened + log.error("read file {} error", file, e); + return null; + } + } + + public static InputStream classpathResource(String resourceName) { + return Props.class.getClassLoader().getResourceAsStream(resourceName); + } + +} diff --git a/book-common/src/main/java/org/n3r/idworker/utils/Serializes.java b/book-common/src/main/java/org/n3r/idworker/utils/Serializes.java new file mode 100644 index 0000000..859156c --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/utils/Serializes.java @@ -0,0 +1,118 @@ +package org.n3r.idworker.utils; + +import java.io.*; +import java.nio.channels.FileChannel; +import java.util.ArrayList; +import java.util.List; + +public class Serializes { + + @SuppressWarnings("unchecked") + public static List readObjects(File file) { + ArrayList objects = new ArrayList(); + ObjectInputStream objectReader = null; + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + objectReader = new ObjectInputStream(fis); + while (true) + objects.add((T) objectReader.readObject()); + + } catch (EOFException e) { + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + closeQuietly(objectReader); + closeQuietly(fis); + } + + return objects; + } + + + @SuppressWarnings("unchecked") + public static T readObject(File file) { + ObjectInputStream objectReader = null; + FileInputStream fis = null; + try { + fis = new FileInputStream(file); + objectReader = new ObjectInputStream(fis); + return (T) objectReader.readObject(); + + } catch (EOFException e) { + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + closeQuietly(objectReader); + closeQuietly(fis); + } + + return null; + } + + public static void writeObject(File file, Object object) { + ObjectOutputStream objectOutput = null; + FileOutputStream fos = null; + try { + fos = new FileOutputStream(file); + objectOutput = new ObjectOutputStream(fos); + objectOutput.writeObject(object); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + closeQuietly(objectOutput); + closeQuietly(fos); + } + } + + public static void writeObject(FileOutputStream fos, Object object) { + FileChannel channel = fos.getChannel(); + if (!channel.isOpen()) throw new RuntimeException("channel is closed"); + + try { + channel.position(0); + ObjectOutputStream objectOutput = new ObjectOutputStream(fos); + objectOutput.writeObject(object); + fos.flush(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + } + } + + public static void writeObjects(File file, Object... objects) { + ObjectOutputStream objectOutput = null; + FileOutputStream fos = null; + try { + fos = new FileOutputStream(file); + objectOutput = new ObjectOutputStream(fos); + + for (Object object : objects) + objectOutput.writeObject(object); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + closeQuietly(objectOutput); + closeQuietly(fos); + } + + } + + public static void closeQuietly(OutputStream os) { + if (os != null) try { + os.close(); + } catch (IOException e) { + // ignore + } + } + + + public static void closeQuietly(InputStream is) { + if (is != null) try { + is.close(); + } catch (IOException e) { + // ignore + } + + } +} diff --git a/book-common/src/main/java/org/n3r/idworker/utils/Utils.java b/book-common/src/main/java/org/n3r/idworker/utils/Utils.java new file mode 100644 index 0000000..30f30e5 --- /dev/null +++ b/book-common/src/main/java/org/n3r/idworker/utils/Utils.java @@ -0,0 +1,114 @@ +package org.n3r.idworker.utils; + +import java.io.*; +import java.sql.Timestamp; +import java.text.SimpleDateFormat; +import java.util.Calendar; + +public class Utils { + + public static final String DOT_IDWORKERS = ".idworkers"; + + public static ClassLoader getClassLoader() { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + return contextClassLoader != null ? contextClassLoader : Utils.class.getClassLoader(); + } + + + public static InputStream classResourceToStream(String resourceName) { + return getClassLoader().getResourceAsStream(resourceName); + } + + + public static String firstLine(String classResourceName) { + InputStream inputStream = null; + try { + inputStream = classResourceToStream(classResourceName); + BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + + return bufferedReader.readLine(); + } catch (IOException e) { + return null; + } finally { + if (inputStream != null) try { + inputStream.close(); + } catch (IOException e) { + // ignore + } + } + } + + public static String checkNotEmpty(String param, String name) { + if (param == null || param.isEmpty()) + throw new IllegalArgumentException(name + " is empty"); + + return param; + } + + + public static long midnightMillis() { + // today + Calendar date = Calendar.getInstance(); + // reset hour, minutes, seconds and millis + date.set(Calendar.HOUR_OF_DAY, 0); + date.set(Calendar.MINUTE, 0); + date.set(Calendar.SECOND, 0); + date.set(Calendar.MILLISECOND, 0); + + return date.getTimeInMillis(); + } + + public static void main(String[] args) { + // 2013-12-25 00:00:00.000 + System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Timestamp(midnightMillis()))); + System.out.println(encode(281474976710655L)); + } + + public static long decode(String s, String symbols) { + final int B = symbols.length(); + long num = 0; + for (char ch : s.toCharArray()) { + num *= B; + num += symbols.indexOf(ch); + } + return num; + } + + public static String encode(long num) { + return encode(num, defaultRange); + } + + public static String encode(long num, String symbols) { + final int B = symbols.length(); + StringBuilder sb = new StringBuilder(); + while (num != 0) { + sb.append(symbols.charAt((int) (num % B))); + num /= B; + } + return sb.reverse().toString(); + } + + // all un-clearly-recognized letters are skiped. + static String defaultRange = "0123456789ABCDFGHKMNPRSTWXYZ"; + + public static String padLeft(String str, int size, char padChar) { + if (str.length() >= size) return str; + + StringBuilder s = new StringBuilder(); + for (int i = size - str.length(); i > 0; --i) { + s.append(padChar); + } + s.append(str); + + return s.toString(); + } + + public static File createIdWorkerHome() { + String userHome = System.getProperty("user.home"); + File idWorkerHome = new File(userHome + File.separator + DOT_IDWORKERS); + idWorkerHome.mkdirs(); + if (idWorkerHome.isDirectory()) return idWorkerHome; + + throw new RuntimeException("failed to create .idworkers at user home"); + } +} diff --git a/book-common/src/main/resources/tencentcloud.properties b/book-common/src/main/resources/tencentcloud.properties new file mode 100644 index 0000000..f38b160 --- /dev/null +++ b/book-common/src/main/resources/tencentcloud.properties @@ -0,0 +1,2 @@ +tencent.cloud.secretId=AKIDvhEVWHm0xe5JGxOZXGitnRovlKcfRzIN +tencent.cloud.secretKey=qPhiTxA7oENFrCH5dvxiCQN4UdWAYgYA \ No newline at end of file diff --git a/book-mapper/pom.xml b/book-mapper/pom.xml new file mode 100644 index 0000000..ffeca0c --- /dev/null +++ b/book-mapper/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + com.imooc + imooc-red-book-dev + 1.0-SNAPSHOT + + + + book-mapper + + + 8 + 8 + UTF-8 + + + + + com.imooc + book-model + 1.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/mapper/CommentMapper.java b/book-mapper/src/main/java/com/imooc/mapper/CommentMapper.java new file mode 100644 index 0000000..690320d --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/mapper/CommentMapper.java @@ -0,0 +1,9 @@ +package com.imooc.mapper; + +import com.imooc.my.mapper.MyMapper; +import com.imooc.pojo.Comment; +import org.springframework.stereotype.Repository; + +@Repository +public interface CommentMapper extends MyMapper { +} \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/mapper/CommentMapperCustom.java b/book-mapper/src/main/java/com/imooc/mapper/CommentMapperCustom.java new file mode 100644 index 0000000..db04141 --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/mapper/CommentMapperCustom.java @@ -0,0 +1,15 @@ +package com.imooc.mapper; + +import com.imooc.vo.CommentVO; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; + +@Repository +public interface CommentMapperCustom { + + public List getCommentList(@Param("paramMap") Map map); + +} \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/mapper/FansMapper.java b/book-mapper/src/main/java/com/imooc/mapper/FansMapper.java new file mode 100644 index 0000000..a6a9b88 --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/mapper/FansMapper.java @@ -0,0 +1,9 @@ +package com.imooc.mapper; + +import com.imooc.my.mapper.MyMapper; +import com.imooc.pojo.Fans; +import org.springframework.stereotype.Repository; + +@Repository +public interface FansMapper extends MyMapper { +} \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/mapper/FansMapperCustom.java b/book-mapper/src/main/java/com/imooc/mapper/FansMapperCustom.java new file mode 100644 index 0000000..8dc3b8f --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/mapper/FansMapperCustom.java @@ -0,0 +1,20 @@ +package com.imooc.mapper; + +import com.imooc.my.mapper.MyMapper; +import com.imooc.pojo.Fans; +import com.imooc.vo.FansVO; +import com.imooc.vo.VlogerVO; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; + +@Repository +public interface FansMapperCustom extends MyMapper { + + public List queryMyFollows(@Param("paramMap") Map map); + + public List queryMyFans(@Param("paramMap") Map map); + +} \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/mapper/MyLikedVlogMapper.java b/book-mapper/src/main/java/com/imooc/mapper/MyLikedVlogMapper.java new file mode 100644 index 0000000..ce689ca --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/mapper/MyLikedVlogMapper.java @@ -0,0 +1,9 @@ +package com.imooc.mapper; + +import com.imooc.my.mapper.MyMapper; +import com.imooc.pojo.MyLikedVlog; +import org.springframework.stereotype.Repository; + +@Repository +public interface MyLikedVlogMapper extends MyMapper { +} \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/mapper/UsersMapper.java b/book-mapper/src/main/java/com/imooc/mapper/UsersMapper.java new file mode 100644 index 0000000..99e4e77 --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/mapper/UsersMapper.java @@ -0,0 +1,9 @@ +package com.imooc.mapper; + +import com.imooc.my.mapper.MyMapper; +import com.imooc.pojo.Users; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface UsersMapper extends MyMapper { +} \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/mapper/VlogMapper.java b/book-mapper/src/main/java/com/imooc/mapper/VlogMapper.java new file mode 100644 index 0000000..c179a90 --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/mapper/VlogMapper.java @@ -0,0 +1,10 @@ +package com.imooc.mapper; + +import com.imooc.my.mapper.MyMapper; +import com.imooc.pojo.Vlog; +import org.springframework.stereotype.Repository; + + +@Repository +public interface VlogMapper extends MyMapper { +} \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/mapper/VlogMapperCustom.java b/book-mapper/src/main/java/com/imooc/mapper/VlogMapperCustom.java new file mode 100644 index 0000000..14eca8b --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/mapper/VlogMapperCustom.java @@ -0,0 +1,23 @@ +package com.imooc.mapper; + +import com.imooc.vo.IndexVlogVO; +import org.apache.ibatis.annotations.Param; +import org.springframework.stereotype.Repository; + +import java.util.List; +import java.util.Map; + +@Repository +public interface VlogMapperCustom { + + public List getIndexVlogList(@Param("paramMap")Map map); + + public List getVlogDetailById(@Param("paramMap")Map map); + + public List getMyLikedVlogList(@Param("paramMap")Map map); + + public List getMyFollowVlogList(@Param("paramMap")Map map); + + public List getMyFriendVlogList(@Param("paramMap")Map map); + +} \ No newline at end of file diff --git a/book-mapper/src/main/java/com/imooc/my/mapper/MyMapper.java b/book-mapper/src/main/java/com/imooc/my/mapper/MyMapper.java new file mode 100644 index 0000000..2c1aceb --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/my/mapper/MyMapper.java @@ -0,0 +1,34 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2014-2016 abel533@gmail.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +package com.imooc.my.mapper; + +import tk.mybatis.mapper.common.Mapper; +import tk.mybatis.mapper.common.MySqlMapper; + +/** + * 继承自己的MyMapper + */ +public interface MyMapper extends Mapper, MySqlMapper { +} diff --git a/book-mapper/src/main/java/com/imooc/repository/MessageRepository.java b/book-mapper/src/main/java/com/imooc/repository/MessageRepository.java new file mode 100644 index 0000000..0891950 --- /dev/null +++ b/book-mapper/src/main/java/com/imooc/repository/MessageRepository.java @@ -0,0 +1,17 @@ +package com.imooc.repository; + +import com.imooc.mo.MessageMO; +import org.springframework.data.domain.Pageable; +import org.springframework.data.mongodb.repository.MongoRepository; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface MessageRepository extends MongoRepository { + + // 通过实现Repository,自定义条件查询 + List findAllByToUserIdEqualsOrderByCreateTimeDesc(String toUserId, + Pageable pageable); +// void deleteAllByFromUserIdAndToUserIdAndMsgType(); +} diff --git a/book-mapper/src/main/resources/mapper/CommentMapper.xml b/book-mapper/src/main/resources/mapper/CommentMapper.xml new file mode 100644 index 0000000..c4b5686 --- /dev/null +++ b/book-mapper/src/main/resources/mapper/CommentMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/CommentMapperCustom.xml b/book-mapper/src/main/resources/mapper/CommentMapperCustom.xml new file mode 100644 index 0000000..8b5b633 --- /dev/null +++ b/book-mapper/src/main/resources/mapper/CommentMapperCustom.xml @@ -0,0 +1,46 @@ + + + + + + + + \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/FansMapper.xml b/book-mapper/src/main/resources/mapper/FansMapper.xml new file mode 100644 index 0000000..0bc9b0b --- /dev/null +++ b/book-mapper/src/main/resources/mapper/FansMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/FansMapperCustom.xml b/book-mapper/src/main/resources/mapper/FansMapperCustom.xml new file mode 100644 index 0000000..4474c59 --- /dev/null +++ b/book-mapper/src/main/resources/mapper/FansMapperCustom.xml @@ -0,0 +1,45 @@ + + + + + + + + + \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/MyLikedVlogMapper.xml b/book-mapper/src/main/resources/mapper/MyLikedVlogMapper.xml new file mode 100644 index 0000000..2981ce6 --- /dev/null +++ b/book-mapper/src/main/resources/mapper/MyLikedVlogMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/UsersMapper.xml b/book-mapper/src/main/resources/mapper/UsersMapper.xml new file mode 100644 index 0000000..6330222 --- /dev/null +++ b/book-mapper/src/main/resources/mapper/UsersMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/VlogMapper.xml b/book-mapper/src/main/resources/mapper/VlogMapper.xml new file mode 100644 index 0000000..b035451 --- /dev/null +++ b/book-mapper/src/main/resources/mapper/VlogMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/book-mapper/src/main/resources/mapper/VlogMapperCustom.xml b/book-mapper/src/main/resources/mapper/VlogMapperCustom.xml new file mode 100644 index 0000000..42923ec --- /dev/null +++ b/book-mapper/src/main/resources/mapper/VlogMapperCustom.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/book-model/pom.xml b/book-model/pom.xml new file mode 100644 index 0000000..f2313be --- /dev/null +++ b/book-model/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + com.imooc + imooc-red-book-dev + 1.0-SNAPSHOT + + + book-model + + + 8 + 8 + UTF-8 + + + + + com.imooc + book-common + 1.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + mysql + mysql-connector-java + 8.0.26 + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + + tk.mybatis + mapper-spring-boot-starter + + + + com.github.pagehelper + pagehelper-spring-boot-starter + + + + + org.springframework.boot + spring-boot-starter-data-mongodb + + + + + \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/bo/CommentBO.java b/book-model/src/main/java/com/imooc/bo/CommentBO.java new file mode 100644 index 0000000..af145ae --- /dev/null +++ b/book-model/src/main/java/com/imooc/bo/CommentBO.java @@ -0,0 +1,32 @@ +package com.imooc.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class CommentBO { + + @NotBlank(message = "留言信息不完整") + private String vlogerId; + + @NotBlank(message = "留言信息不完整") + private String fatherCommentId; + + @NotBlank(message = "留言信息不完整") + private String vlogId; + + @NotBlank(message = "当前用户信息不正确,请尝试重新登录") + private String commentUserId; + + @NotBlank(message = "评论内容不能为空") + @Length(max = 50, message = "评论内容长度不能超过50") + private String content; +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/bo/RegistLoginBO.java b/book-model/src/main/java/com/imooc/bo/RegistLoginBO.java new file mode 100644 index 0000000..f57b881 --- /dev/null +++ b/book-model/src/main/java/com/imooc/bo/RegistLoginBO.java @@ -0,0 +1,23 @@ +package com.imooc.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; + +@Data +@ToString +@AllArgsConstructor +@NoArgsConstructor +public class RegistLoginBO { + + @NotBlank(message = "手机号不能为空") + @Length(min = 11, max = 11, message = "手机长度不正确") + private String mobile; + @NotBlank(message = "验证码不能为空") + private String smsCode; + +} diff --git a/book-model/src/main/java/com/imooc/bo/UpdatedUserBO.java b/book-model/src/main/java/com/imooc/bo/UpdatedUserBO.java new file mode 100644 index 0000000..45038d8 --- /dev/null +++ b/book-model/src/main/java/com/imooc/bo/UpdatedUserBO.java @@ -0,0 +1,28 @@ +package com.imooc.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class UpdatedUserBO { + private String id; + private String nickname; + private String imoocNum; + private String face; + private Integer sex; + private Date birthday; + private String country; + private String province; + private String city; + private String district; + private String description; + private String bgImg; + private Integer canImoocNumBeUpdated; +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/bo/VlogBO.java b/book-model/src/main/java/com/imooc/bo/VlogBO.java new file mode 100644 index 0000000..8e11b21 --- /dev/null +++ b/book-model/src/main/java/com/imooc/bo/VlogBO.java @@ -0,0 +1,22 @@ +package com.imooc.bo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class VlogBO { + private String id; + private String vlogerId; + private String url; + private String cover; + private String title; + private Integer width; + private Integer height; + private Integer likeCounts; + private Integer commentsCounts; +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/mo/MessageMO.java b/book-model/src/main/java/com/imooc/mo/MessageMO.java new file mode 100644 index 0000000..a389a2e --- /dev/null +++ b/book-model/src/main/java/com/imooc/mo/MessageMO.java @@ -0,0 +1,41 @@ +package com.imooc.mo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + +import java.util.Date; +import java.util.Map; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +@Document("message") +public class MessageMO { + + @Id + private String id; // 消息主键id + + @Field("fromUserId") + private String fromUserId; // 消息来自的用户id + @Field("fromNickname") + private String fromNickname; // 消息来自的用户昵称 + @Field("fromFace") + private String fromFace; // 消息来自的用户头像 + + @Field("toUserId") + private String toUserId; // 消息发送到某对象的用户id + + @Field("msgType") + private Integer msgType; // 消息类型 枚举 + @Field("msgContent") + private Map msgContent; // 消息内容 + + @Field("createTime") + private Date createTime; // 消息创建时间 +} diff --git a/book-model/src/main/java/com/imooc/pojo/Comment.java b/book-model/src/main/java/com/imooc/pojo/Comment.java new file mode 100644 index 0000000..744bce9 --- /dev/null +++ b/book-model/src/main/java/com/imooc/pojo/Comment.java @@ -0,0 +1,191 @@ +package com.imooc.pojo; + +import javax.persistence.Column; +import javax.persistence.Id; +import java.util.Date; + +public class Comment { + @Id + private String id; + + /** + * 评论的视频是哪个作者(vloger)的关联id + */ + @Column(name = "vloger_id") + private String vlogerId; + + /** + * 如果是回复留言,则本条为子留言,需要关联查询 + */ + @Column(name = "father_comment_id") + private String fatherCommentId; + + /** + * 回复的那个视频id + */ + @Column(name = "vlog_id") + private String vlogId; + + /** + * 发布留言的用户id + */ + @Column(name = "comment_user_id") + private String commentUserId; + + /** + * 留言内容 + */ + private String content; + + /** + * 留言的点赞总数 + */ + @Column(name = "like_counts") + private Integer likeCounts; + + /** + * 留言时间 + */ + @Column(name = "create_time") + private Date createTime; + + /** + * @return id + */ + public String getId() { + return id; + } + + /** + * @param id + */ + public void setId(String id) { + this.id = id; + } + + /** + * 获取评论的视频是哪个作者(vloger)的关联id + * + * @return vloger_id - 评论的视频是哪个作者(vloger)的关联id + */ + public String getVlogerId() { + return vlogerId; + } + + /** + * 设置评论的视频是哪个作者(vloger)的关联id + * + * @param vlogerId 评论的视频是哪个作者(vloger)的关联id + */ + public void setVlogerId(String vlogerId) { + this.vlogerId = vlogerId; + } + + /** + * 获取如果是回复留言,则本条为子留言,需要关联查询 + * + * @return father_comment_id - 如果是回复留言,则本条为子留言,需要关联查询 + */ + public String getFatherCommentId() { + return fatherCommentId; + } + + /** + * 设置如果是回复留言,则本条为子留言,需要关联查询 + * + * @param fatherCommentId 如果是回复留言,则本条为子留言,需要关联查询 + */ + public void setFatherCommentId(String fatherCommentId) { + this.fatherCommentId = fatherCommentId; + } + + /** + * 获取回复的那个视频id + * + * @return vlog_id - 回复的那个视频id + */ + public String getVlogId() { + return vlogId; + } + + /** + * 设置回复的那个视频id + * + * @param vlogId 回复的那个视频id + */ + public void setVlogId(String vlogId) { + this.vlogId = vlogId; + } + + /** + * 获取发布留言的用户id + * + * @return comment_user_id - 发布留言的用户id + */ + public String getCommentUserId() { + return commentUserId; + } + + /** + * 设置发布留言的用户id + * + * @param commentUserId 发布留言的用户id + */ + public void setCommentUserId(String commentUserId) { + this.commentUserId = commentUserId; + } + + /** + * 获取留言内容 + * + * @return content - 留言内容 + */ + public String getContent() { + return content; + } + + /** + * 设置留言内容 + * + * @param content 留言内容 + */ + public void setContent(String content) { + this.content = content; + } + + /** + * 获取留言的点赞总数 + * + * @return like_counts - 留言的点赞总数 + */ + public Integer getLikeCounts() { + return likeCounts; + } + + /** + * 设置留言的点赞总数 + * + * @param likeCounts 留言的点赞总数 + */ + public void setLikeCounts(Integer likeCounts) { + this.likeCounts = likeCounts; + } + + /** + * 获取留言时间 + * + * @return create_time - 留言时间 + */ + public Date getCreateTime() { + return createTime; + } + + /** + * 设置留言时间 + * + * @param createTime 留言时间 + */ + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/pojo/Fans.java b/book-model/src/main/java/com/imooc/pojo/Fans.java new file mode 100644 index 0000000..4bee518 --- /dev/null +++ b/book-model/src/main/java/com/imooc/pojo/Fans.java @@ -0,0 +1,96 @@ +package com.imooc.pojo; + +import org.springframework.data.annotation.Id; + +import javax.persistence.Column; + +public class Fans { + @Id + private String id; + + /** + * 作家用户id + */ + @Column(name = "vloger_id") + private String vlogerId; + + /** + * 粉丝用户id + */ + @Column(name = "fan_id") + private String fanId; + + /** + * 粉丝是否是vloger的朋友,如果成为朋友,则本表的双方此字段都需要设置为1,如果有一人取关,则两边都需要设置为0 + */ + @Column(name = "is_fan_friend_of_mine") + private Integer isFanFriendOfMine; + + /** + * @return id + */ + public String getId() { + return id; + } + + /** + * @param id + */ + public void setId(String id) { + this.id = id; + } + + /** + * 获取作家用户id + * + * @return vloger_id - 作家用户id + */ + public String getVlogerId() { + return vlogerId; + } + + /** + * 设置作家用户id + * + * @param vlogerId 作家用户id + */ + public void setVlogerId(String vlogerId) { + this.vlogerId = vlogerId; + } + + /** + * 获取粉丝用户id + * + * @return fan_id - 粉丝用户id + */ + public String getFanId() { + return fanId; + } + + /** + * 设置粉丝用户id + * + * @param fanId 粉丝用户id + */ + public void setFanId(String fanId) { + this.fanId = fanId; + } + + /** + * 获取粉丝是否是vloger的朋友,如果成为朋友,则本表的双方此字段都需要设置为1,如果有一人取关,则两边都需要设置为0 + * + * @return is_fan_friend_of_mine - 粉丝是否是vloger的朋友,如果成为朋友,则本表的双方此字段都需要设置为1,如果有一人取关,则两边都需要设置为0 + */ + public Integer getIsFanFriendOfMine() { + return isFanFriendOfMine; + } + + /** + * 设置粉丝是否是vloger的朋友,如果成为朋友,则本表的双方此字段都需要设置为1,如果有一人取关,则两边都需要设置为0 + * + * @param isFanFriendOfMine 粉丝是否是vloger的朋友,如果成为朋友,则本表的双方此字段都需要设置为1,如果有一人取关,则两边都需要设置为0 + */ + public void setIsFanFriendOfMine(Integer isFanFriendOfMine) { + this.isFanFriendOfMine = isFanFriendOfMine; + } +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/pojo/MyLikedVlog.java b/book-model/src/main/java/com/imooc/pojo/MyLikedVlog.java new file mode 100644 index 0000000..d1bb038 --- /dev/null +++ b/book-model/src/main/java/com/imooc/pojo/MyLikedVlog.java @@ -0,0 +1,74 @@ +package com.imooc.pojo; + +import org.springframework.data.annotation.Id; + +import javax.persistence.Column; +import javax.persistence.Table; + +@Table(name = "my_liked_vlog") +public class MyLikedVlog { + @Id + private String id; + + /** + * 用户id + */ + @Column(name = "user_id") + private String userId; + + /** + * 喜欢的短视频id + */ + @Column(name = "vlog_id") + private String vlogId; + + /** + * @return id + */ + public String getId() { + return id; + } + + /** + * @param id + */ + public void setId(String id) { + this.id = id; + } + + /** + * 获取用户id + * + * @return user_id - 用户id + */ + public String getUserId() { + return userId; + } + + /** + * 设置用户id + * + * @param userId 用户id + */ + public void setUserId(String userId) { + this.userId = userId; + } + + /** + * 获取喜欢的短视频id + * + * @return vlog_id - 喜欢的短视频id + */ + public String getVlogId() { + return vlogId; + } + + /** + * 设置喜欢的短视频id + * + * @param vlogId 喜欢的短视频id + */ + public void setVlogId(String vlogId) { + this.vlogId = vlogId; + } +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/pojo/Users.java b/book-model/src/main/java/com/imooc/pojo/Users.java new file mode 100644 index 0000000..5233311 --- /dev/null +++ b/book-model/src/main/java/com/imooc/pojo/Users.java @@ -0,0 +1,374 @@ +package com.imooc.pojo; + +import javax.persistence.Column; +import javax.persistence.Id; +import java.util.Date; + +public class Users { + @Id + private String id; + + /** + * 手机号 + */ + private String mobile; + + /** + * 昵称,媒体号 + */ + private String nickname; + + /** + * 慕课号,类似头条号,抖音号,公众号,唯一标识,需要限制修改次数,比如终生1次,每年1次,每半年1次等,可以用于付费修改。 + */ + @Column(name = "imooc_num") + private String imoocNum; + + /** + * 头像 + */ + private String face; + + /** + * 性别 1:男 0:女 2:保密 + */ + private Integer sex; + + /** + * 生日 + */ + private Date birthday; + + /** + * 国家 + */ + private String country; + + /** + * 省份 + */ + private String province; + + /** + * 城市 + */ + private String city; + + /** + * 区县 + */ + private String district; + + /** + * 简介 + */ + private String description; + + /** + * 个人介绍的背景图 + */ + @Column(name = "bg_img") + private String bgImg; + + /** + * 慕课号能否被修改,1:默认,可以修改;0,无法修改 + */ + @Column(name = "can_imooc_num_be_updated") + private Integer canImoocNumBeUpdated; + + /** + * 创建时间 创建时间 + */ + @Column(name = "created_time") + private Date createdTime; + + /** + * 更新时间 更新时间 + */ + @Column(name = "updated_time") + private Date updatedTime; + + /** + * @return id + */ + public String getId() { + return id; + } + + /** + * @param id + */ + public void setId(String id) { + this.id = id; + } + + /** + * 获取手机号 + * + * @return mobile - 手机号 + */ + public String getMobile() { + return mobile; + } + + /** + * 设置手机号 + * + * @param mobile 手机号 + */ + public void setMobile(String mobile) { + this.mobile = mobile; + } + + /** + * 获取昵称,媒体号 + * + * @return nickname - 昵称,媒体号 + */ + public String getNickname() { + return nickname; + } + + /** + * 设置昵称,媒体号 + * + * @param nickname 昵称,媒体号 + */ + public void setNickname(String nickname) { + this.nickname = nickname; + } + + /** + * 获取慕课号,类似头条号,抖音号,公众号,唯一标识,需要限制修改次数,比如终生1次,每年1次,每半年1次等,可以用于付费修改。 + * + * @return imooc_num - 慕课号,类似头条号,抖音号,公众号,唯一标识,需要限制修改次数,比如终生1次,每年1次,每半年1次等,可以用于付费修改。 + */ + public String getImoocNum() { + return imoocNum; + } + + /** + * 设置慕课号,类似头条号,抖音号,公众号,唯一标识,需要限制修改次数,比如终生1次,每年1次,每半年1次等,可以用于付费修改。 + * + * @param imoocNum 慕课号,类似头条号,抖音号,公众号,唯一标识,需要限制修改次数,比如终生1次,每年1次,每半年1次等,可以用于付费修改。 + */ + public void setImoocNum(String imoocNum) { + this.imoocNum = imoocNum; + } + + /** + * 获取头像 + * + * @return face - 头像 + */ + public String getFace() { + return face; + } + + /** + * 设置头像 + * + * @param face 头像 + */ + public void setFace(String face) { + this.face = face; + } + + /** + * 获取性别 1:男 0:女 2:保密 + * + * @return sex - 性别 1:男 0:女 2:保密 + */ + public Integer getSex() { + return sex; + } + + /** + * 设置性别 1:男 0:女 2:保密 + * + * @param sex 性别 1:男 0:女 2:保密 + */ + public void setSex(Integer sex) { + this.sex = sex; + } + + /** + * 获取生日 + * + * @return birthday - 生日 + */ + public Date getBirthday() { + return birthday; + } + + /** + * 设置生日 + * + * @param birthday 生日 + */ + public void setBirthday(Date birthday) { + this.birthday = birthday; + } + + /** + * 获取国家 + * + * @return country - 国家 + */ + public String getCountry() { + return country; + } + + /** + * 设置国家 + * + * @param country 国家 + */ + public void setCountry(String country) { + this.country = country; + } + + /** + * 获取省份 + * + * @return province - 省份 + */ + public String getProvince() { + return province; + } + + /** + * 设置省份 + * + * @param province 省份 + */ + public void setProvince(String province) { + this.province = province; + } + + /** + * 获取城市 + * + * @return city - 城市 + */ + public String getCity() { + return city; + } + + /** + * 设置城市 + * + * @param city 城市 + */ + public void setCity(String city) { + this.city = city; + } + + /** + * 获取区县 + * + * @return district - 区县 + */ + public String getDistrict() { + return district; + } + + /** + * 设置区县 + * + * @param district 区县 + */ + public void setDistrict(String district) { + this.district = district; + } + + /** + * 获取简介 + * + * @return description - 简介 + */ + public String getDescription() { + return description; + } + + /** + * 设置简介 + * + * @param description 简介 + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * 获取个人介绍的背景图 + * + * @return bg_img - 个人介绍的背景图 + */ + public String getBgImg() { + return bgImg; + } + + /** + * 设置个人介绍的背景图 + * + * @param bgImg 个人介绍的背景图 + */ + public void setBgImg(String bgImg) { + this.bgImg = bgImg; + } + + /** + * 获取慕课号能否被修改,1:默认,可以修改;0,无法修改 + * + * @return can_imooc_num_be_updated - 慕课号能否被修改,1:默认,可以修改;0,无法修改 + */ + public Integer getCanImoocNumBeUpdated() { + return canImoocNumBeUpdated; + } + + /** + * 设置慕课号能否被修改,1:默认,可以修改;0,无法修改 + * + * @param canImoocNumBeUpdated 慕课号能否被修改,1:默认,可以修改;0,无法修改 + */ + public void setCanImoocNumBeUpdated(Integer canImoocNumBeUpdated) { + this.canImoocNumBeUpdated = canImoocNumBeUpdated; + } + + /** + * 获取创建时间 创建时间 + * + * @return created_time - 创建时间 创建时间 + */ + public Date getCreatedTime() { + return createdTime; + } + + /** + * 设置创建时间 创建时间 + * + * @param createdTime 创建时间 创建时间 + */ + public void setCreatedTime(Date createdTime) { + this.createdTime = createdTime; + } + + /** + * 获取更新时间 更新时间 + * + * @return updated_time - 更新时间 更新时间 + */ + public Date getUpdatedTime() { + return updatedTime; + } + + /** + * 设置更新时间 更新时间 + * + * @param updatedTime 更新时间 更新时间 + */ + public void setUpdatedTime(Date updatedTime) { + this.updatedTime = updatedTime; + } +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/pojo/Vlog.java b/book-model/src/main/java/com/imooc/pojo/Vlog.java new file mode 100644 index 0000000..1db34b1 --- /dev/null +++ b/book-model/src/main/java/com/imooc/pojo/Vlog.java @@ -0,0 +1,283 @@ +package com.imooc.pojo; + +import javax.persistence.Column; +import javax.persistence.Id; +import java.util.Date; + +public class Vlog { + @Id + private String id; + + /** + * 对应用户表id,vlog视频发布者 + */ + @Column(name = "vloger_id") + private String vlogerId; + + /** + * 视频播放地址 + */ + private String url; + + /** + * 视频封面 + */ + private String cover; + + /** + * 视频标题,可以为空 + */ + private String title; + + /** + * 视频width + */ + private Integer width; + + /** + * 视频height + */ + private Integer height; + + /** + * 点赞总数 + */ + @Column(name = "like_counts") + private Integer likeCounts; + + /** + * 评论总数 + */ + @Column(name = "comments_counts") + private Integer commentsCounts; + + /** + * 是否私密,用户可以设置私密,如此可以不公开给比人看 + */ + @Column(name = "is_private") + private Integer isPrivate; + + /** + * 创建时间 创建时间 + */ + @Column(name = "created_time") + private Date createdTime; + + /** + * 更新时间 更新时间 + */ + @Column(name = "updated_time") + private Date updatedTime; + + /** + * @return id + */ + public String getId() { + return id; + } + + /** + * @param id + */ + public void setId(String id) { + this.id = id; + } + + /** + * 获取对应用户表id,vlog视频发布者 + * + * @return vloger_id - 对应用户表id,vlog视频发布者 + */ + public String getVlogerId() { + return vlogerId; + } + + /** + * 设置对应用户表id,vlog视频发布者 + * + * @param vlogerId 对应用户表id,vlog视频发布者 + */ + public void setVlogerId(String vlogerId) { + this.vlogerId = vlogerId; + } + + /** + * 获取视频播放地址 + * + * @return url - 视频播放地址 + */ + public String getUrl() { + return url; + } + + /** + * 设置视频播放地址 + * + * @param url 视频播放地址 + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * 获取视频封面 + * + * @return cover - 视频封面 + */ + public String getCover() { + return cover; + } + + /** + * 设置视频封面 + * + * @param cover 视频封面 + */ + public void setCover(String cover) { + this.cover = cover; + } + + /** + * 获取视频标题,可以为空 + * + * @return title - 视频标题,可以为空 + */ + public String getTitle() { + return title; + } + + /** + * 设置视频标题,可以为空 + * + * @param title 视频标题,可以为空 + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * 获取视频width + * + * @return width - 视频width + */ + public Integer getWidth() { + return width; + } + + /** + * 设置视频width + * + * @param width 视频width + */ + public void setWidth(Integer width) { + this.width = width; + } + + /** + * 获取视频height + * + * @return height - 视频height + */ + public Integer getHeight() { + return height; + } + + /** + * 设置视频height + * + * @param height 视频height + */ + public void setHeight(Integer height) { + this.height = height; + } + + /** + * 获取点赞总数 + * + * @return like_counts - 点赞总数 + */ + public Integer getLikeCounts() { + return likeCounts; + } + + /** + * 设置点赞总数 + * + * @param likeCounts 点赞总数 + */ + public void setLikeCounts(Integer likeCounts) { + this.likeCounts = likeCounts; + } + + /** + * 获取评论总数 + * + * @return comments_counts - 评论总数 + */ + public Integer getCommentsCounts() { + return commentsCounts; + } + + /** + * 设置评论总数 + * + * @param commentsCounts 评论总数 + */ + public void setCommentsCounts(Integer commentsCounts) { + this.commentsCounts = commentsCounts; + } + + /** + * 获取是否私密,用户可以设置私密,如此可以不公开给比人看 + * + * @return is_private - 是否私密,用户可以设置私密,如此可以不公开给比人看 + */ + public Integer getIsPrivate() { + return isPrivate; + } + + /** + * 设置是否私密,用户可以设置私密,如此可以不公开给比人看 + * + * @param isPrivate 是否私密,用户可以设置私密,如此可以不公开给比人看 + */ + public void setIsPrivate(Integer isPrivate) { + this.isPrivate = isPrivate; + } + + /** + * 获取创建时间 创建时间 + * + * @return created_time - 创建时间 创建时间 + */ + public Date getCreatedTime() { + return createdTime; + } + + /** + * 设置创建时间 创建时间 + * + * @param createdTime 创建时间 创建时间 + */ + public void setCreatedTime(Date createdTime) { + this.createdTime = createdTime; + } + + /** + * 获取更新时间 更新时间 + * + * @return updated_time - 更新时间 更新时间 + */ + public Date getUpdatedTime() { + return updatedTime; + } + + /** + * 设置更新时间 更新时间 + * + * @param updatedTime 更新时间 更新时间 + */ + public void setUpdatedTime(Date updatedTime) { + this.updatedTime = updatedTime; + } +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/vo/CommentVO.java b/book-model/src/main/java/com/imooc/vo/CommentVO.java new file mode 100644 index 0000000..fa43ef6 --- /dev/null +++ b/book-model/src/main/java/com/imooc/vo/CommentVO.java @@ -0,0 +1,28 @@ +package com.imooc.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class CommentVO { + private String id; + private String commentId; + private String vlogerId; + private String fatherCommentId; + private String vlogId; + private String commentUserId; + private String commentUserNickname; + private String commentUserFace; + private String content; + private Integer likeCounts; + private String replyedUserNickname; + private Date createTime; + private Integer isLike = 0; +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/vo/FansVO.java b/book-model/src/main/java/com/imooc/vo/FansVO.java new file mode 100644 index 0000000..f741355 --- /dev/null +++ b/book-model/src/main/java/com/imooc/vo/FansVO.java @@ -0,0 +1,17 @@ +package com.imooc.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class FansVO { + private String fanId; + private String nickname; + private String face; + private boolean isFriend = false; +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/vo/IndexVlogVO.java b/book-model/src/main/java/com/imooc/vo/IndexVlogVO.java new file mode 100644 index 0000000..fb8d9a3 --- /dev/null +++ b/book-model/src/main/java/com/imooc/vo/IndexVlogVO.java @@ -0,0 +1,28 @@ +package com.imooc.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class IndexVlogVO { + private String vlogId; + private String vlogerId; + private String vlogerFace; + private String vlogerName; + private String content; + private String url; + private String cover; + private Integer width; + private Integer height; + private Integer likeCounts; + private Integer commentsCounts; + private Integer isPrivate; + private boolean isPlay = false; + private boolean doIFollowVloger = false; + private boolean doILikeThisVlog = false; +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/vo/UsersVO.java b/book-model/src/main/java/com/imooc/vo/UsersVO.java new file mode 100644 index 0000000..eba4009 --- /dev/null +++ b/book-model/src/main/java/com/imooc/vo/UsersVO.java @@ -0,0 +1,42 @@ +package com.imooc.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import javax.persistence.Column; +import javax.persistence.Id; +import javax.validation.constraints.NotBlank; +import java.util.Date; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class UsersVO { + private String id; + private String mobile; + private String nickname; + private String imoocNum; + private String face; + private Integer sex; + private Date birthday; + private String country; + private String province; + private String city; + private String district; + private String description; + private String bgImg; + private Integer canImoocNumBeUpdated; + private Date createdTime; + private Date updatedTime; + + private String userToken; // 用户token,传递给前端 + + private Integer myFollowsCounts; //我关注的 + private Integer myFansCounts; //我的粉丝数量 + //private Integer myLikedVlogCounts; + private Integer totalLikeMeCounts; + +} \ No newline at end of file diff --git a/book-model/src/main/java/com/imooc/vo/VlogerVO.java b/book-model/src/main/java/com/imooc/vo/VlogerVO.java new file mode 100644 index 0000000..82bd507 --- /dev/null +++ b/book-model/src/main/java/com/imooc/vo/VlogerVO.java @@ -0,0 +1,17 @@ +package com.imooc.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@ToString +public class VlogerVO { + private String vlogerId; + private String nickname; + private String face; + private boolean isFollowed = true; +} \ No newline at end of file diff --git a/book-service/pom.xml b/book-service/pom.xml new file mode 100644 index 0000000..c8a169c --- /dev/null +++ b/book-service/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + com.imooc + imooc-red-book-dev + 1.0-SNAPSHOT + + + + book-service + + + 8 + 8 + UTF-8 + + + + + com.imooc + book-mapper + 1.0-SNAPSHOT + + + + \ No newline at end of file diff --git a/book-service/src/main/java/com/imooc/base/BaseInfoProperties.java b/book-service/src/main/java/com/imooc/base/BaseInfoProperties.java new file mode 100644 index 0000000..34decce --- /dev/null +++ b/book-service/src/main/java/com/imooc/base/BaseInfoProperties.java @@ -0,0 +1,57 @@ +package com.imooc.base; + +import com.github.pagehelper.PageInfo; +//import com.imooc.utils.PagedGridResult; +import com.imooc.utils.PagedGridResult; +import com.imooc.utils.RedisOperator; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.List; + +public class BaseInfoProperties { + + @Autowired + public RedisOperator redis; + + public static final Integer COMMON_START_PAGE = 1; + public static final Integer COMMON_START_PAGE_ZERO = 0; + 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_VLOG_COMMENT_COUNTS = "redis_vlog_comment_counts"; + // 短视频的评论喜欢数量 + public static final String REDIS_VLOG_COMMENT_LIKED_COUNTS = "redis_vlog_comment_liked_counts"; + // 用户点赞评论 + public static final String REDIS_USER_LIKE_COMMENT = "redis_user_like_comment"; + + + //我的关注总数 + 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 static final String REDIS_FANS_AND_VLOGGER_RELATIONSHIP = "redis_fans_and_vlogger_relationship"; + + // 用户是否喜欢/点赞视频,取代数据库的关联关系,1:喜欢,0:不喜欢(默认) redis_user_like_vlog:{userId}:{vlogId} + public static final String REDIS_USER_LIKE_VLOG = "redis_user_like_vlog"; + + 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-service/src/main/java/com/imooc/base/RabbitMQConfig.java b/book-service/src/main/java/com/imooc/base/RabbitMQConfig.java new file mode 100644 index 0000000..6f0c519 --- /dev/null +++ b/book-service/src/main/java/com/imooc/base/RabbitMQConfig.java @@ -0,0 +1,51 @@ +package com.imooc.base;//package com.imooc; + +import org.springframework.amqp.core.*; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RabbitMQConfig { + + /** + * 根据模型编写代码: + * 1. 定义交换机 + * 2. 定义队列 + * 3. 创建交换机 + * 4. 创建队列 + * 5. 队列和交换机的绑定 + */ + + public static final String EXCHANGE_MSG = "exchange_msg"; + + public static final String QUEUE_SYS_MSG = "queue_sys_msg"; + + @Bean(EXCHANGE_MSG) + public Exchange exchange() { + return ExchangeBuilder // 构建交换机 + .topicExchange(EXCHANGE_MSG) // 使用topic类型,参考:https://www.rabbitmq.com/getstarted.html + .durable(true) // 设置持久化,重启mq后依然存在 + .build(); + } + + @Bean(QUEUE_SYS_MSG) + public Queue queue() { + return new Queue(QUEUE_SYS_MSG); + } + + @Bean + public Binding binding(@Qualifier(EXCHANGE_MSG) Exchange exchange, + @Qualifier(QUEUE_SYS_MSG) Queue queue) { + + return BindingBuilder + .bind(queue) + .to(exchange) + .with("sys.msg.*") // 定义路由规则(requestMapping) + .noargs(); + + // FIXME: * 和 # 分别代表什么意思? + } + + +} diff --git a/book-service/src/main/java/com/imooc/service/CommentService.java b/book-service/src/main/java/com/imooc/service/CommentService.java new file mode 100644 index 0000000..16eb4f6 --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/CommentService.java @@ -0,0 +1,35 @@ +package com.imooc.service; + +import com.imooc.bo.CommentBO; +import com.imooc.pojo.Comment; +import com.imooc.pojo.Vlog; +import com.imooc.utils.PagedGridResult; +import com.imooc.vo.CommentVO; + +public interface CommentService { + + /** + * 发表评论 + */ + public CommentVO createComment(CommentBO commentBO); + + /** + * 查询评论的列表 + */ + public PagedGridResult queryVlogComments(String vlogId, + String userId, + Integer page, + Integer pageSize); + + /** + * 删除评论 + */ + public void deleteComment(String commentUserId, + String commentId, + String vlogId); + + /** + * 根据主键查询comment + */ + public Comment getComment(String id); +} diff --git a/book-service/src/main/java/com/imooc/service/FansService.java b/book-service/src/main/java/com/imooc/service/FansService.java new file mode 100644 index 0000000..f74c7c9 --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/FansService.java @@ -0,0 +1,35 @@ +package com.imooc.service; + +import com.imooc.utils.PagedGridResult; + +public interface FansService { + + /** + * 关注 + */ + public void doFollow(String myId, String vlogerId); + + /** + * 取关 + */ + public void doCancel(String myId, String vlogerId); + + /** + * 查询用户是否关注博主 + */ + public boolean queryDoIFollowVloger(String myId, String vlogerId); + + /** + * 查询我关注的博主列表 + */ + public PagedGridResult queryMyFollows(String myId, + Integer page, + Integer pageSize); + + /** + * 查询我的粉丝列表 + */ + public PagedGridResult queryMyFans(String myId, + Integer page, + Integer pageSize); +} diff --git a/book-service/src/main/java/com/imooc/service/MsgService.java b/book-service/src/main/java/com/imooc/service/MsgService.java new file mode 100644 index 0000000..18cf99c --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/MsgService.java @@ -0,0 +1,27 @@ +package com.imooc.service; + +import com.imooc.bo.VlogBO; +import com.imooc.mo.MessageMO; + +import javax.validation.constraints.Max; +import java.util.List; +import java.util.Map; + +public interface MsgService { + + /** + * 创建消息 + */ + public void createMsg(String fromUserId, + String toUserId, + Integer type, + Map msgContent); + + /** + * 查询消息列表 + */ + public List queryList(String toUserId, + Integer page, + Integer pageSize); + +} diff --git a/book-service/src/main/java/com/imooc/service/UserService.java b/book-service/src/main/java/com/imooc/service/UserService.java new file mode 100644 index 0000000..6c94292 --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/UserService.java @@ -0,0 +1,38 @@ +package com.imooc.service; + +import com.imooc.bo.UpdatedUserBO; +import com.imooc.pojo.Users; + +/** + * @author vercen + * @version 1.0 + * @date 2023/5/25 21:02 + */ +public interface UserService { + /** + * 判断用户是否存在,如果存在则返回用户信息 + */ + public Users queryMobileIsExist(String mobile); + + + /** + * 创建用户信息,并且返回用户对象 + */ + public Users createUser(String mobile); + + /** + * 根据用户主键查询用户信息 + */ + public Users getUser(String userId); + + /** + * 用户信息修改 + */ + public Users updateUserInfo(UpdatedUserBO updatedUserBO); + + /** + * 用户信息修改 + */ + public Users updateUserInfo(UpdatedUserBO updatedUserBO, Integer type); + +} diff --git a/book-service/src/main/java/com/imooc/service/VlogService.java b/book-service/src/main/java/com/imooc/service/VlogService.java new file mode 100644 index 0000000..c599a8b --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/VlogService.java @@ -0,0 +1,156 @@ +package com.imooc.service; + +import com.imooc.bo.VlogBO; +import com.imooc.pojo.Vlog; +import com.imooc.utils.PagedGridResult; +import com.imooc.vo.IndexVlogVO; + +public interface VlogService { + + /** + * 新增vlog视频 + */ + public void createVlog(VlogBO vlogBO); + + /** + * 查询首页/搜索的vlog列表 + */ + public PagedGridResult getIndexVlogList(String userId, + String search, + Integer page, + Integer pageSize); + + /** + * 根据视频主键查询vlog + */ + public IndexVlogVO getVlogDetailById(String userId, String vlogId); + + /** + * 用户把视频改为公开/私密的视频 + */ + public void changeToPrivateOrPublic(String userId, + String vlogId, + Integer yesOrNo); + + /** + * 查询用的公开/私密的视频列表 + */ + public PagedGridResult queryMyVlogList(String userId, + Integer page, + Integer pageSize, + Integer yesOrNo); + + /** + * 用户点赞/喜欢视频 + */ + public void userLikeVlog(String userId, String vlogId); + + /** + * 用户取消点赞/喜欢视频 + */ + public void userUnLikeVlog(String userId, String vlogId); + + /** + * 获得用户点赞视频的总数 + */ + public Integer getVlogBeLikedCounts(String vlogId); + + /** + * 查询用户点赞过的短视频 + */ + public PagedGridResult getMyLikedVlogList(String userId, + Integer page, + Integer pageSize); + + /** + * 查询用户关注的博主发布的短视频列表 + */ + public PagedGridResult getMyFollowVlogList(String myId, + Integer page, + Integer pageSize); + + /** + * 查询朋友发布的短视频列表 + */ + public PagedGridResult getMyFriendVlogList(String myId, + Integer page, + Integer pageSize); + + /** + * 根据主键查询vlog + */ + public Vlog getVlog(String id); + + /** + * 把counts输入数据库 + */ + public void flushCounts(String vlogId, Integer counts); +// /** +// * 查询首页/搜索的vlog列表 +// */ +// public PagedGridResult getIndexVlogList(String userId, +// String search, +// Integer page, +// Integer pageSize); +// +// /** +// * 根据视频主键查询vlog +// */ +// public IndexVlogVO getVlogDetailById(String userId, String vlogId); +// +// /** +// * 用户把视频改为公开/私密的视频 +// */ +// public void changeToPrivateOrPublic(String userId, +// String vlogId, +// Integer yesOrNo); +// +// /** +// * 查询用的公开/私密的视频列表 +// */ +// public PagedGridResult queryMyVlogList(String userId, +// Integer page, +// Integer pageSize, +// Integer yesOrNo); +// +// /** +// * 用户点赞/喜欢视频 +// */ +// public void userLikeVlog(String userId, String vlogId); +// +// /** +// * 用户取消点赞/喜欢视频 +// */ +// public void userUnLikeVlog(String userId, String vlogId); +// +// /** +// * 获得用户点赞视频的总数 +// */ +// public Integer getVlogBeLikedCounts(String vlogId); +// +// /** +// * 查询用户点赞过的短视频 +// */ +// public PagedGridResult getMyLikedVlogList(String userId, +// Integer page, +// Integer pageSize); +// +// /** +// * 查询用户关注的博主发布的短视频列表 +// */ +// public PagedGridResult getMyFollowVlogList(String myId, +// Integer page, +// Integer pageSize); +// +// /** +// * 查询朋友发布的短视频列表 +// */ +// public PagedGridResult getMyFriendVlogList(String myId, +// Integer page, +// Integer pageSize); +// +// /** +// * 根据主键查询vlog +// */ +// public Vlog getVlog(String id); +} diff --git a/book-service/src/main/java/com/imooc/service/impl/CommentServiceImpl.java b/book-service/src/main/java/com/imooc/service/impl/CommentServiceImpl.java new file mode 100644 index 0000000..d113da4 --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/impl/CommentServiceImpl.java @@ -0,0 +1,150 @@ +package com.imooc.service.impl; + +import com.github.pagehelper.PageHelper; +import com.imooc.base.BaseInfoProperties; +import com.imooc.bo.CommentBO; +import com.imooc.enums.MessageEnum; +import com.imooc.enums.YesOrNo; +import com.imooc.mapper.CommentMapper; +import com.imooc.mapper.CommentMapperCustom; +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.utils.PagedGridResult; +import com.imooc.vo.CommentVO; +import org.apache.commons.lang3.StringUtils; +import org.n3r.idworker.Sid; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class CommentServiceImpl extends BaseInfoProperties implements CommentService { +// + @Autowired + private CommentMapper commentMapper; +// + @Autowired + private CommentMapperCustom commentMapperCustom; +// + @Autowired + private VlogService vlogService; + @Autowired + private MsgService msgService; +// + @Autowired + private Sid sid; +// + @Override + public CommentVO createComment(CommentBO commentBO) { + + String commentId = sid.nextShort(); + + Comment comment = new Comment(); + comment.setId(commentId); + + comment.setVlogId(commentBO.getVlogId()); + comment.setVlogerId(commentBO.getVlogerId()); + + comment.setCommentUserId(commentBO.getCommentUserId()); + comment.setFatherCommentId(commentBO.getFatherCommentId()); + comment.setContent(commentBO.getContent()); + + comment.setLikeCounts(0); + comment.setCreateTime(new Date()); + + commentMapper.insert(comment); + + // redis操作放在service中,评论总数的累加 + redis.increment(REDIS_VLOG_COMMENT_COUNTS + ":" + commentBO.getVlogId(), 1); + + // 留言后的最新评论需要返回给前端进行展示 + CommentVO commentVO = new CommentVO(); + BeanUtils.copyProperties(comment, commentVO); + + + + // 系统消息:评论/回复 + Vlog vlog = vlogService.getVlog(commentBO.getVlogId()); + Map msgContent = new HashMap(); + msgContent.put("vlogId", vlog.getId()); + msgContent.put("vlogCover", vlog.getCover()); + msgContent.put("commentId", commentId); + msgContent.put("commentContent", commentBO.getContent()); + Integer type = MessageEnum.COMMENT_VLOG.type; + if (StringUtils.isNotBlank(commentBO.getFatherCommentId()) && + !commentBO.getFatherCommentId().equalsIgnoreCase("0") ) { + type = MessageEnum.REPLY_YOU.type; + } + + msgService.createMsg(commentBO.getCommentUserId(), + commentBO.getVlogerId(), + type, + msgContent); +// +// +// + return commentVO; + } +// + @Override + public PagedGridResult queryVlogComments(String vlogId, + String userId, + Integer page, + Integer pageSize) { + + Map map = new HashMap<>(); + map.put("vlogId", vlogId); + + PageHelper.startPage(page, pageSize); + + List list = commentMapperCustom.getCommentList(map); + + for (CommentVO cv:list) { + String commentId = cv.getCommentId(); + + // 当前短视频的某个评论的点赞总数 + String countsStr = redis.getHashValue(REDIS_VLOG_COMMENT_LIKED_COUNTS, commentId); + Integer counts = 0; + if (StringUtils.isNotBlank(countsStr)) { + counts = Integer.valueOf(countsStr); + } + cv.setLikeCounts(counts); + + // 判断当前用户是否点赞过该评论 + String doILike = redis.hget(REDIS_USER_LIKE_COMMENT, userId + ":" + commentId); + if (StringUtils.isNotBlank(doILike) && doILike.equalsIgnoreCase("1")) { + cv.setIsLike(YesOrNo.YES.type); + } + } + + return setterPagedGrid(list, page); + } + + @Override + public void deleteComment(String commentUserId, + String commentId, + String vlogId) { + + Comment pendingDelete = new Comment(); + pendingDelete.setId(commentId); + pendingDelete.setCommentUserId(commentUserId); + + commentMapper.delete(pendingDelete); + + // 评论总数的累减 + redis.decrement(REDIS_VLOG_COMMENT_COUNTS + ":" + vlogId, 1); + } + + @Override + public Comment getComment(String id) { + return commentMapper.selectByPrimaryKey(id); + } +} diff --git a/book-service/src/main/java/com/imooc/service/impl/FansServiceImpl.java b/book-service/src/main/java/com/imooc/service/impl/FansServiceImpl.java new file mode 100644 index 0000000..a9c866e --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/impl/FansServiceImpl.java @@ -0,0 +1,173 @@ +package com.imooc.service.impl; + +import com.github.pagehelper.PageHelper; +import com.imooc.base.BaseInfoProperties; +import com.imooc.base.RabbitMQConfig; +import com.imooc.enums.MessageEnum; +import com.imooc.enums.YesOrNo; +import com.imooc.mapper.FansMapper; +import com.imooc.mapper.FansMapperCustom; +import com.imooc.mo.MessageMO; +import com.imooc.pojo.Fans; +import com.imooc.service.FansService; +import com.imooc.service.MsgService; +import com.imooc.utils.JsonUtils; +import com.imooc.utils.PagedGridResult; +import com.imooc.vo.FansVO; +import com.imooc.vo.VlogerVO; +import org.apache.commons.lang3.StringUtils; +import org.n3r.idworker.Sid; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tk.mybatis.mapper.entity.Example; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class FansServiceImpl extends BaseInfoProperties implements FansService { + + @Autowired + private FansMapper fansMapper; + @Autowired + private FansMapperCustom fansMapperCustom; +// + @Autowired + private MsgService msgService; + + @Autowired + public RabbitTemplate rabbitTemplate; +// + @Autowired + private Sid sid; +// + @Transactional + @Override + public void doFollow(String myId, String vlogerId) { + + String fid = sid.nextShort(); + + Fans fans = new Fans(); + fans.setId(fid); + fans.setFanId(myId); + fans.setVlogerId(vlogerId); + + // 判断对方是否关注我,如果关注我,那么双方都要互为朋友关系 + Fans vloger = queryFansRelationship(vlogerId, myId); + if (vloger != null) { + fans.setIsFanFriendOfMine(YesOrNo.YES.type); + + vloger.setIsFanFriendOfMine(YesOrNo.YES.type); + fansMapper.updateByPrimaryKeySelective(vloger); + } else { + fans.setIsFanFriendOfMine(YesOrNo.NO.type); + } + + fansMapper.insert(fans); + + + // 系统消息:关注 + // msgService.createMsg(myId, vlogerId, MessageEnum.FOLLOW_YOU.type, null); + //优化使用mQEXCHANGE_MSG + + MessageMO messageMO = new MessageMO(); + + messageMO.setFromUserId(myId); + messageMO.setToUserId(vlogerId); + rabbitTemplate.convertAndSend(RabbitMQConfig.EXCHANGE_MSG, "sys.msg."+ MessageEnum.FOLLOW_YOU.enValue, JsonUtils.objectToJson(messageMO)); + } + + public Fans queryFansRelationship(String fanId, String vlogerId) { + Example example = new Example(Fans.class); + Example.Criteria criteria = example.createCriteria(); + criteria.andEqualTo("vlogerId", vlogerId); + criteria.andEqualTo("fanId", fanId); + + List list = fansMapper.selectByExample(example); + + Fans fan = null; + if (list != null && list.size() > 0 && !list.isEmpty()) { + fan = (Fans)list.get(0); + } + + return fan; + } + + @Transactional + @Override + public void doCancel(String myId, String vlogerId) { + + // 判断我们是否朋友关系,如果是,则需要取消双方的关系 + Fans fan = queryFansRelationship(myId, vlogerId); + if (fan != null && fan.getIsFanFriendOfMine() == YesOrNo.YES.type) { + // 抹除双方的朋友关系,自己的关系删除即可 + Fans pendingFan = queryFansRelationship(vlogerId, myId); + pendingFan.setIsFanFriendOfMine(YesOrNo.NO.type); + fansMapper.updateByPrimaryKeySelective(pendingFan); + } + + // 删除自己的关注关联表记录 + fansMapper.delete(fan); + } + + @Override + public boolean queryDoIFollowVloger(String myId, String vlogerId) { + Fans vloger = queryFansRelationship(myId, vlogerId); + return vloger != null; + } + + @Override + public PagedGridResult queryMyFollows(String myId, + Integer page, + Integer pageSize) { + Map map = new HashMap<>(); + map.put("myId", myId); + + PageHelper.startPage(page, pageSize); + + List list = fansMapperCustom.queryMyFollows(map); + + return setterPagedGrid(list, page); + } + + @Override + public PagedGridResult queryMyFans(String myId, + Integer page, + Integer pageSize) { + + /** + * <判断粉丝是否是我的朋友(互粉互关)> + * 普通做法: + * 多表关联+嵌套关联查询,这样会违反多表关联的规范,不可取,高并发下回出现性能问题 + * + * 常规做法: + * 1. 避免过多的表关联查询,先查询我的粉丝列表,获得fansList + * 2. 判断粉丝关注我,并且我也关注粉丝 -> 循环fansList,获得每一个粉丝,再去数据库查询我是否关注他 + * 3. 如果我也关注他(粉丝),说明,我俩互为朋友关系(互关互粉),则标记flag为true,否则false + * + * 高端做法: + * 1. 关注/取关的时候,关联关系保存在redis中,不要依赖数据库 + * 2. 数据库查询后,直接循环查询redis,避免第二次循环查询数据库的尴尬局面 + */ + + + Map map = new HashMap<>(); + map.put("myId", myId); + + PageHelper.startPage(page, pageSize); + + List list = fansMapperCustom.queryMyFans(map); + + for (FansVO f : list) { + String relationship = redis.get(REDIS_FANS_AND_VLOGGER_RELATIONSHIP + ":" + myId + ":" + f.getFanId()); + if (StringUtils.isNotBlank(relationship) && relationship.equalsIgnoreCase("1")) { + f.setFriend(true); + } + } + + return setterPagedGrid(list, page); + } +} diff --git a/book-service/src/main/java/com/imooc/service/impl/MsgServiceImpl.java b/book-service/src/main/java/com/imooc/service/impl/MsgServiceImpl.java new file mode 100644 index 0000000..bea69a4 --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/impl/MsgServiceImpl.java @@ -0,0 +1,89 @@ +package com.imooc.service.impl; + +import com.imooc.base.BaseInfoProperties; +import com.imooc.enums.MessageEnum; +import com.imooc.mo.MessageMO; +import com.imooc.pojo.Users; +import com.imooc.repository.MessageRepository; +import com.imooc.service.MsgService; +import com.imooc.service.UserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class MsgServiceImpl extends BaseInfoProperties implements MsgService { + + @Autowired + private MessageRepository messageRepository; + + @Autowired + private UserService userService; + + @Override + public void createMsg(String fromUserId, + String toUserId, + Integer type, + Map msgContent) { + + Users fromUser = userService.getUser(fromUserId); + + MessageMO messageMO = new MessageMO(); + + messageMO.setFromUserId(fromUserId); + messageMO.setFromNickname(fromUser.getNickname()); + messageMO.setFromFace(fromUser.getFace()); + + messageMO.setToUserId(toUserId); + + messageMO.setMsgType(type); + if (msgContent != null) { + messageMO.setMsgContent(msgContent); + } + + messageMO.setCreateTime(new Date()); + + messageRepository.save(messageMO); + } +// + @Override + public List queryList(String toUserId, + Integer page, + Integer pageSize) { + + Pageable pageable = PageRequest.of(page, + pageSize, + Sort.Direction.DESC, + "createTime"); + + List list = messageRepository + .findAllByToUserIdEqualsOrderByCreateTimeDesc(toUserId, + pageable); + for (MessageMO msg : list) { + // 如果类型是关注消息,则需要查询我之前有没有关注过他,用于在前端标记“互粉”“互关” + if (msg.getMsgType() != null && msg.getMsgType() == MessageEnum.FOLLOW_YOU.type) { + Map map = msg.getMsgContent(); + if (map == null) { + map = new HashMap(); + } + + String relationship = redis.get(REDIS_FANS_AND_VLOGGER_RELATIONSHIP + ":" + msg.getToUserId() + ":" + msg.getFromUserId()); + if (StringUtils.isNotBlank(relationship) && relationship.equalsIgnoreCase("1")) { + map.put("isFriend", true); + } else { + map.put("isFriend", false); + } + msg.setMsgContent(map); + } + } + return list; + } +} diff --git a/book-service/src/main/java/com/imooc/service/impl/UserServiceImpl.java b/book-service/src/main/java/com/imooc/service/impl/UserServiceImpl.java new file mode 100644 index 0000000..78a25a9 --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/impl/UserServiceImpl.java @@ -0,0 +1,125 @@ +package com.imooc.service.impl; + +import com.imooc.bo.UpdatedUserBO; +import com.imooc.enums.Sex; +import com.imooc.enums.UserInfoModifyType; +import com.imooc.enums.YesOrNo; +import com.imooc.exceptions.GraceException; +import com.imooc.grace.result.ResponseStatusEnum; +import com.imooc.mapper.UsersMapper; +import com.imooc.pojo.Users; +import com.imooc.service.UserService; +import com.imooc.utils.DateUtil; +import com.imooc.utils.DesensitizationUtil; +import org.n3r.idworker.Sid; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tk.mybatis.mapper.entity.Example; + +import java.util.Date; + +/** + * @author vercen + * @version 1.0 + * @date 2023/5/25 21:02 + */ +@Service +public class UserServiceImpl implements UserService { + @Autowired + private UsersMapper usersMapper; + + @Autowired + private Sid sid; + private static final String USER_FACE1 = "http://122.152.205.72:88/group1/M00/00/05/CpoxxF6ZUySASMbOAABBAXhjY0Y649.png"; + + @Override + public Users queryMobileIsExist(String mobile) { + Example userExample = new Example(Users.class); + Example.Criteria criteria = userExample.createCriteria(); + criteria.andEqualTo("mobile", mobile); + Users user = usersMapper.selectOneByExample(userExample); + return user; + } + + @Override + public Users createUser(String mobile) { + // 获得全局唯一主键 + String userId = sid.nextShort(); + + Users user = new Users(); + user.setId(userId); + + user.setMobile(mobile); + user.setNickname("用户:" + DesensitizationUtil.commonDisplay(mobile)); + user.setImoocNum("用户:" + DesensitizationUtil.commonDisplay(mobile)); + user.setFace(USER_FACE1); + + user.setBirthday(DateUtil.stringToDate("1900-01-01")); + user.setSex(Sex.secret.type); + + user.setCountry("中国"); + user.setProvince(""); + user.setCity(""); + user.setDistrict(""); + user.setDescription("这家伙很懒,什么都没留下~"); + user.setCanImoocNumBeUpdated(YesOrNo.YES.type); + + user.setCreatedTime(new Date()); + user.setUpdatedTime(new Date()); + + usersMapper.insert(user); + + return user; + } + + @Override + public Users getUser(String userId) { + Users users = usersMapper.selectByPrimaryKey(userId); + return users; + } + + @Transactional + @Override + public Users updateUserInfo(UpdatedUserBO updatedUserBO) { + + Users users = new Users(); + BeanUtils.copyProperties(updatedUserBO, users); + usersMapper.updateByPrimaryKeySelective(users); + return getUser(updatedUserBO.getId()); + } + + @Transactional + @Override + public Users updateUserInfo(UpdatedUserBO updatedUserBO, Integer type) { + + Example example = new Example(Users.class); + Example.Criteria criteria = example.createCriteria(); + if (type == UserInfoModifyType.NICKNAME.type) { + criteria.andEqualTo("nickname", updatedUserBO.getNickname()); + Users user = usersMapper.selectOneByExample(example); + if (user != null) { + GraceException.display(ResponseStatusEnum.USER_INFO_UPDATED_NICKNAME_EXIST_ERROR); + } + } + + if (type == UserInfoModifyType.IMOOCNUM.type) { + criteria.andEqualTo("imoocNum", updatedUserBO.getImoocNum()); + Users user = usersMapper.selectOneByExample(example); + if (user != null) { + GraceException.display(ResponseStatusEnum.USER_INFO_UPDATED_NICKNAME_EXIST_ERROR); + } + + Users tempUser = getUser(updatedUserBO.getId()); + if (tempUser.getCanImoocNumBeUpdated() == YesOrNo.NO.type) { + GraceException.display(ResponseStatusEnum.USER_INFO_CANT_UPDATED_IMOOCNUM_ERROR); + } + + updatedUserBO.setCanImoocNumBeUpdated(YesOrNo.NO.type); + } + + return updateUserInfo(updatedUserBO); + + } +} diff --git a/book-service/src/main/java/com/imooc/service/impl/VlogServiceImpl.java b/book-service/src/main/java/com/imooc/service/impl/VlogServiceImpl.java new file mode 100644 index 0000000..4917d9c --- /dev/null +++ b/book-service/src/main/java/com/imooc/service/impl/VlogServiceImpl.java @@ -0,0 +1,344 @@ +package com.imooc.service.impl; + +import com.github.pagehelper.PageHelper; +import com.imooc.base.BaseInfoProperties; +import com.imooc.bo.VlogBO; +import com.imooc.enums.MessageEnum; +import com.imooc.enums.YesOrNo; +import com.imooc.mapper.MyLikedVlogMapper; +import com.imooc.mapper.VlogMapper; +import com.imooc.mapper.VlogMapperCustom; +import com.imooc.pojo.MyLikedVlog; +import com.imooc.pojo.Vlog; +import com.imooc.service.FansService; +import com.imooc.service.MsgService; +import com.imooc.service.VlogService; +import com.imooc.utils.PagedGridResult; +import com.imooc.vo.IndexVlogVO; +import org.apache.commons.lang3.StringUtils; +import org.n3r.idworker.Sid; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tk.mybatis.mapper.entity.Example; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class VlogServiceImpl extends BaseInfoProperties implements VlogService { + + @Autowired + private VlogMapper vlogMapper; + + @Autowired + private VlogMapperCustom vlogMapperCustom; + + @Autowired + private MyLikedVlogMapper myLikedVlogMapper; + + @Autowired + private FansService fansService; + @Autowired + private MsgService msgService; + + @Autowired + private Sid sid; + + @Transactional + @Override + public void createVlog(VlogBO vlogBO) { + + String vid = sid.nextShort(); + + Vlog vlog = new Vlog(); + BeanUtils.copyProperties(vlogBO, vlog); + + vlog.setId(vid); + + vlog.setLikeCounts(0); + vlog.setCommentsCounts(0); + vlog.setIsPrivate(YesOrNo.NO.type); + + vlog.setCreatedTime(new Date()); + vlog.setUpdatedTime(new Date()); + + vlogMapper.insert(vlog); + } + + @Override + public PagedGridResult getIndexVlogList(String userId, + String search, + Integer page, + Integer pageSize) { + + PageHelper.startPage(page, pageSize); + + Map map = new HashMap<>(); + if (StringUtils.isNotBlank(search)) { + map.put("search", search); + } + List list = vlogMapperCustom.getIndexVlogList(map); + + for (IndexVlogVO v : list) { + String vlogerId = v.getVlogerId(); + String vlogId = v.getVlogId(); + + if (StringUtils.isNotBlank(userId)) { + // 用户是否关注该博主 + boolean doIFollowVloger = fansService.queryDoIFollowVloger(userId, vlogerId); + v.setDoIFollowVloger(doIFollowVloger); + + // 判断当前用户是否点赞过视频 + v.setDoILikeThisVlog(doILikeVlog(userId, vlogId)); + } + + // 获得当前视频被点赞过的总数 + v.setLikeCounts(getVlogBeLikedCounts(vlogId)); + } + +// return list; + return setterPagedGrid(list, page); + } + + private IndexVlogVO setterVO(IndexVlogVO v, String userId) { + String vlogerId = v.getVlogerId(); + String vlogId = v.getVlogId(); + + if (StringUtils.isNotBlank(userId)) { + // 用户是否关注该博主 + boolean doIFollowVloger = fansService.queryDoIFollowVloger(userId, vlogerId); + v.setDoIFollowVloger(doIFollowVloger); + + // 判断当前用户是否点赞过视频 + v.setDoILikeThisVlog(doILikeVlog(userId, vlogId)); + } + + // 获得当前视频被点赞过的总数 + v.setLikeCounts(getVlogBeLikedCounts(vlogId)); + + return v; + } +// + @Override + public Integer getVlogBeLikedCounts(String vlogId) { + String countsStr = redis.get(REDIS_VLOG_BE_LIKED_COUNTS + ":" + vlogId); + if (StringUtils.isBlank(countsStr)) { + countsStr = "0"; + } + return Integer.valueOf(countsStr); + } +// + private boolean doILikeVlog(String myId, String vlogId) { + + String doILike = redis.get(REDIS_USER_LIKE_VLOG + ":" + myId + ":" + vlogId); + boolean isLike = false; + if (StringUtils.isNotBlank(doILike) && doILike.equalsIgnoreCase("1")) { + isLike = true; + } + return isLike; + } +// + @Override + public IndexVlogVO getVlogDetailById(String userId, String vlogId) { + + Map map = new HashMap<>(); + map.put("vlogId", vlogId); + + List list = vlogMapperCustom.getVlogDetailById(map); + + if (list != null && list.size() > 0 && !list.isEmpty()) { + IndexVlogVO vlogVO = list.get(0); +// return vlogVO; + return setterVO(vlogVO, userId); + } + + return null; + } + + @Transactional + @Override + public void changeToPrivateOrPublic(String userId, + String vlogId, + Integer yesOrNo) { + Example example = new Example(Vlog.class); + Example.Criteria criteria = example.createCriteria(); + criteria.andEqualTo("id", vlogId); + criteria.andEqualTo("vlogerId", userId); + + Vlog pendingVlog = new Vlog(); + pendingVlog.setIsPrivate(yesOrNo); + + vlogMapper.updateByExampleSelective(pendingVlog, example); + } + + @Override + public PagedGridResult queryMyVlogList(String userId, + Integer page, + Integer pageSize, + Integer yesOrNo) { + + Example example = new Example(Vlog.class); + Example.Criteria criteria = example.createCriteria(); + criteria.andEqualTo("vlogerId", userId); + criteria.andEqualTo("isPrivate", yesOrNo); + + PageHelper.startPage(page, pageSize); + List list = vlogMapper.selectByExample(example); + + return setterPagedGrid(list, page); + } + + @Transactional + @Override + public void userLikeVlog(String userId, String vlogId) { + + String rid = sid.nextShort(); + + MyLikedVlog likedVlog = new MyLikedVlog(); + likedVlog.setId(rid); + likedVlog.setVlogId(vlogId); + likedVlog.setUserId(userId); + + myLikedVlogMapper.insert(likedVlog); +// System.out.println(vlogId); +// +// Vlog vlog = new Vlog(); +// vlog.setId(vlogId); +// Vlog vlog1 = vlogMapper.selectOne(vlog); +// System.out.println(vlog1.toString()); +//// System.out.println(vlog1.getCreatedTime()); + +// Vlog vlog2 = vlogMapper.selectByPrimaryKey(vlogId); +// System.out.println(vlog2.toString()); + +// Vlog vlog1 = vlogMapper.selectOne(new Vlog().setId(vlogId)); +// Vlog vlog = vlog1 +// System.out.println(vlog); + + // 系统消息:点赞短视频 + Vlog vlog = this.getVlog(vlogId); + Map msgContent = new HashMap(); + msgContent.put("vlogId", vlogId); + msgContent.put("vlogCover", vlog.getCover()); + msgService.createMsg(userId, + vlog.getVlogerId(), + MessageEnum.LIKE_VLOG.type, + msgContent); + } +// + @Override + public Vlog getVlog(String id) { + Vlog vlog = vlogMapper.selectByPrimaryKey(id); +// Vlog vlog = new Vlog(); +// vlog.setId(id); +// Vlog vlog1 = vlogMapper.selectOne(vlog); + return vlog; + } + + @Transactional + @Override + public void flushCounts(String vlogId, Integer counts) { + + Vlog vlog = new Vlog(); + vlog.setId(vlogId); + vlog.setLikeCounts(counts); +// System.out.println(vlog.toString()); + int i = vlogMapper.updateByPrimaryKeySelective(vlog); +// System.out.println(i); + + } + + // + @Transactional + @Override + public void userUnLikeVlog(String userId, String vlogId) { + + MyLikedVlog likedVlog = new MyLikedVlog(); + likedVlog.setVlogId(vlogId); + likedVlog.setUserId(userId); + + myLikedVlogMapper.delete(likedVlog); + } + + @Override + public PagedGridResult getMyLikedVlogList(String userId, + Integer page, + Integer pageSize) { + PageHelper.startPage(page, pageSize); + Map map = new HashMap<>(); + map.put("userId", userId); + List list = vlogMapperCustom.getMyLikedVlogList(map); + + return setterPagedGrid(list, page); + } + + @Override + public PagedGridResult getMyFollowVlogList(String myId, + Integer page, + Integer pageSize) { + PageHelper.startPage(page, pageSize); + + Map map = new HashMap<>(); + map.put("myId", myId); + + List list = vlogMapperCustom.getMyFollowVlogList(map); + + for (IndexVlogVO v : list) { + String vlogerId = v.getVlogerId(); + String vlogId = v.getVlogId(); + + if (StringUtils.isNotBlank(myId)) { + // 用户必定关注该博主 + v.setDoIFollowVloger(true); + + // 判断当前用户是否点赞过视频 + v.setDoILikeThisVlog(doILikeVlog(myId, vlogId)); + } + + // 获得当前视频被点赞过的总数 + v.setLikeCounts(getVlogBeLikedCounts(vlogId)); + } + + return setterPagedGrid(list, page); + } +// + @Override + public PagedGridResult getMyFriendVlogList(String myId, + Integer page, + Integer pageSize) { + + PageHelper.startPage(page, pageSize); + + Map map = new HashMap<>(); + map.put("myId", myId); + + List list = vlogMapperCustom.getMyFriendVlogList(map); + + for (IndexVlogVO v : list) { + String vlogerId = v.getVlogerId(); + String vlogId = v.getVlogId(); + + if (StringUtils.isNotBlank(myId)) { + // 用户必定关注该博主 + v.setDoIFollowVloger(true); + + // 判断当前用户是否点赞过视频 + v.setDoILikeThisVlog(doILikeVlog(myId, vlogId)); + } + + // 获得当前视频被点赞过的总数 + v.setLikeCounts(getVlogBeLikedCounts(vlogId)); + } + + return setterPagedGrid(list, page); + } + +// @Override +// public Vlog getVlog(String id) { +// return null; +// } +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..a649f5d --- /dev/null +++ b/pom.xml @@ -0,0 +1,192 @@ + + + 4.0.0 + + com.imooc + imooc-red-book-dev + 1.0-SNAPSHOT + + pom + + book-common + book-model + book-mapper + book-service + book-api + + + + org.springframework.boot + spring-boot-starter-parent + 2.5.4 + + + + + UTF-8 + UTF-8 + 1.8 + + 8.0.26 + 2.1.0 + 2.1.5 + 1.2.12 + + 4.2.2 + 2.10.2 + + 1.11 + 3.4 + 1.4 + 28.2-jre + + 1.7.21 + 2.10.6 + + + + + + + + + org.springframework.cloud + spring-cloud-dependencies + 2020.0.4 + pom + import + + + + + com.alibaba.cloud + spring-cloud-alibaba-dependencies + 2.2.6.RELEASE + pom + import + + + + + mysql + mysql-connector-java + ${mysql-connector-java.version} + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + ${mybatis-spring-boot-starter.version} + + + + tk.mybatis + mapper-spring-boot-starter + ${mapper-spring-boot-starter.version} + + + + com.github.pagehelper + pagehelper-spring-boot-starter + ${pagehelper-spring-boot-starter.version} + + + + + + + + + + + com.fasterxml.jackson.core + jackson-core + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + + + + + commons-codec + commons-codec + ${commons-codec.version} + + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + + + commons-fileupload + commons-fileupload + ${commons-fileupload.version} + + + + + com.google.guava + guava + ${google-guava.version} + + + + + joda-time + joda-time + ${joda-time.version} + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + 2.0.9 + + + com.github.xiaoymin + knife4j-springdoc-ui + 3.0.3 + + + + + io.minio + minio + 8.2.1 + + + + + + + ${project.artifactId} + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + + + + + \ No newline at end of file