业务系统
litemall开源商城
dbblog开源博客
2024最新同城上门家政按摩H5小程序源码 | 全开源无需授权【安装教程】
MES智能制造、WMS仓储管理、TMS运维管理
智能农业
智慧食堂
智慧工地
本文档使用 MrDoc 发布
-
+
首页
dbblog开源博客
开源地址:https://gitee.com/571002217/dbblog?_from=gitee_search ## 使用技术 SpringBoot 2.x 后台基本框架 Vue 2.x 前端基本框架 ElementUI:后台管理页面UI库 IView:前端UI库 ElasticSearch 搜索层 RabbitMQ 消息队列 Shiro 鉴权层 Redis 缓存层 Swagger 文档 Mybaits-Plus 好用的mybatis框架 lombox getter setter插件 druid 数据库连接池 jasypt 加密 七牛云 图床 ## 后台模块 ~~~ dbblog ├── dbblog-auth # 鉴权模块:shiro │ ├── pom.xml │ └── src ├── dbblog-core # 核心模块:配置文件,Entity类,mapper类,工具类,异常过滤等 │ ├── pom.xml │ └── src ├── dbblog-manage # 后台管理界面Service │ ├── pom.xml │ └── src ├── dbblog-portal # 前端界面Service │ ├── pom.xml │ └── src ├── dbblog-search # 搜索模块:elasticSearch │ ├── pom.xml └── └── src ~~~ ## 后台依赖关系 `dbblog-core -> dbblog-auth -> dbblog-manage -> dbblog-portal -> dbblog-search` > 采用多模块的形式,便于后续SpringCloud微服务的改造升级 [========] 😇 ==从以下6个方面进行代码解析:1增、2删、3改、4查、5封装(数据统一返回、异常是否封装)、6其他技术方面== ## 1、增 - 前端发起请求 ~~~ this.$http({ url: this.$http.adornUrl(`/admin/operation/category/${!this.dataForm.categoryId ? 'save' : 'update'}`), method: 'post', data: this.$http.adornData({ 'id': this.dataForm.categoryId || undefined, 'name': this.dataForm.name, 'type': this.dataForm.type, 'rank': this.dataForm.rank, 'parentId': this.dataForm.parentId }) }) ~~~ - 后端接收 ~~~ @RequestMapping("/save") @RequiresPermissions("operation:category:save") @CacheEvict(allEntries = true) public Result save(@RequestBody Category category){ // 数据校验 ValidatorUtils.validateEntity(category); verifyCategory(category); categoryService.save(category); return Result.ok(); } ~~~ > @CacheEvict(allEntries = true) > 指定allEntries = true清空namespace下的所有缓存元素 - 数据校验 ~~~java /** * 数据校验 * @param category */ private void verifyCategory(Category category) { //上级分类级别 int parentRank = CategoryRankEnum.ROOT.getValue(); if (category.getParentId() != CategoryRankEnum.FIRST.getValue() && category.getParentId() != CategoryRankEnum.ROOT.getValue()) { Category parentCategory = categoryService.getById(category.getParentId()); parentRank = parentCategory.getRank(); } // 一级 if (category.getRank() == CategoryRankEnum.FIRST.getValue()) { if (category.getParentId() != CategoryRankEnum.ROOT.getValue()){ throw new MyException("上级目录只能为根目录"); } } //二级 if (category.getRank() == CategoryRankEnum.SECOND.getValue()) { if (parentRank != CategoryRankEnum.FIRST.getValue()) { throw new MyException("上级目录只能为一级类型"); } } //三级 if (category.getRank() == CategoryRankEnum.THIRD.getValue()) { if (parentRank != CategoryRankEnum.SECOND.getValue()) { throw new MyException("上级目录只能为二级类型"); } } } ~~~ - service 直接调用了 `categoryService.save(category);` 进行了保存。 ## 2、删 - 发送请求 ~~~ deleteHandle (id) { this.$confirm(`确定对[id=${id}]进行删除操作?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { this.$http({ url: this.$http.adornUrl('/admin/operation/category/delete/' + id), method: 'delete', data: this.$http.adornData() }).then(({data}) => { if (data && data.code === 200) { this.$message({ message: '操作成功', type: 'success', duration: 1500, onClose: () => { this.getDataList() } }) } else { this.$message.error(data.msg) } }) }) } ~~~ - 后端处理 ~~~ @DeleteMapping("/delete/{id}") @RequiresPermissions("operation:category:delete") @CacheEvict(allEntries = true) public Result delete(@PathVariable Integer id){ //判断是否有子菜单或按钮 List<Category> categoryList = categoryService.queryListParentId(id); if(categoryList.size() > 0){ return Result.error("请先删除子级别"); } // 判断是否有文章 if(articleService.checkByCategory(id)) { return Result.error("该类别下有文章,无法删除"); } // 判断是否有图书 if(bookService.checkByCategory(id)){ return Result.error("该类别下有图书,无法删除"); } // 判断是否有笔记 if(bookNoteService.checkByCategory(id)) { return Result.error("该类别下有笔记,无法删除"); } categoryService.removeById(id); return Result.ok(); } ~~~ service层直接处理了。 ## 改 - 前端和新增一样 - 后端处理 ~~~ RequestMapping("/update") @RequiresPermissions("operation:category:update") @CacheEvict(allEntries = true) public Result update(@RequestBody Category category){ categoryService.updateById(category); return Result.ok(); } ~~~ 直接调用service的自带方法updateById 处理了。 ## 查 #### 无分页 - controller ~~~ @RequestMapping("/list") @RequiresPermissions("operation:category:list") public Result list(@RequestParam Map<String, Object> params){ List<Category> categoryList = categoryService.queryWithParentName(params); return Result.ok().put("categoryList",categoryList); } ~~~ - service实现层 ~~~ @Override public List<Category> queryWithParentName(Map<String, Object> params) { return baseMapper.queryAll(params); } ~~~ - mapper接口 ~~~ List<Category> queryAll(Map<String, Object> params); ~~~ - mapper实现 >i 接收了一个Map做为查询参数 ~~~ <select id="queryAll" parameterType="Map" resultType="cn.dblearn.blog.entity.operation.Category"> select t.*,s.name as parentName from category t left join category s on t.parent_id = s.id <where> <if test="name != null and name != ''"> t.name like concat('%',#{name},'%') </if> <if test="type !=null and type !=''"> t.type = #{type} </if> </where> </select> ~~~ #### 分页查询 - 分页直接引用分页组件: ~~~ <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[10, 20, 50, 100]" :page-size="pageSize" :total="totalPage" layout="total, sizes, prev, pager, next, jumper"> </el-pagination> ~~~ - 发送前端请求,是一个get请求 ~~~ getDataList () { this.dataListLoading = true this.$http({ url: this.$http.adornUrl('/admin/article/list'), method: 'get', params: this.$http.adornParams({ 'page': this.pageIndex, 'limit': this.pageSize, 'title': this.dataForm.title }) }).then(({data}) => { if (data && data.code === 200) { this.dataList = data.page.list this.totalPage = data.page.totalCount } else { this.dataList = [] this.totalPage = 0 } this.dataListLoading = false }) }, ~~~ - 后端接收 ~~~ @GetMapping("/list") @RequiresPermissions("article:list") public Result listArticle(@RequestParam Map<String, Object> params) { PageUtils page = articleService.queryPage(params); return Result.ok().put("page",page); } ~~~ - service处理 ~~~ @Override public PageUtils queryPage(Map<String, Object> params) { Page<ArticleVO> page = new Query<ArticleVO>(params).getPage(); List<ArticleVO> articleList = baseMapper.listArticleVo(page, params); // 查询所有分类 List<Category> categoryList = categoryService.list(new QueryWrapper<Category>().lambda().eq(Category::getType,ModuleEnum.ARTICLE.getValue())); // 封装ArticleVo Optional.ofNullable(articleList).ifPresent((articleVos -> articleVos.forEach(articleVo -> { // 设置类别 articleVo.setCategoryListStr(categoryService.renderCategoryArr(articleVo.getCategoryId(),categoryList)); // 设置标签列表 articleVo.setTagList(tagService.listByLinkId(articleVo.getId(),ModuleEnum.ARTICLE.getValue())); }))); page.setRecords(articleList); return new PageUtils(page); } ~~~ - mapper接口 ~~~ List<ArticleVO> listArticleVo(Page<ArticleVO> page, @Param("params") Map<String, Object> params); ~~~ - mapper实现 ~~~ <select id="listArticleVo" resultType="cn.dblearn.blog.entity.article.vo.ArticleVO"> select * from article <where> <if test="params.title!=null and params.title !=''"> and title like concat('%',#{params.title},'%') </if> </where> order by create_time desc </select> ~~~ ## 封装 - 结果集的封装 ~~~ public class Result extends HashMap<String, Object> { public Result() { put("code", 200); put("msg", "success"); } public static Result ok() { return new Result(); } public static Result error() { return error(ErrorEnum.UNKNOWN); } public static Result error(ErrorEnum eEnum) { return new Result().put("code", eEnum.getCode()).put("msg", eEnum.getMsg()); } public static Result error(String msg) { return new Result().put("msg",msg).put("code", ErrorEnum.UNKNOWN.getCode()); } public static Result error(Integer code , String msg){ return new Result().put("code",code).put("msg",msg); } public static Result exception() { return exception(ErrorEnum.UNKNOWN); } public static Result exception(ErrorEnum eEnum) { return new Result().put("code", eEnum.getCode()).put("msg", eEnum.getMsg()); } /** * 封装业务数据 * * @param key * @param value * @return */ @Override public Result put(String key, Object value) { super.put(key, value); //将HashMap对象本身返回 return this; } } ~~~ - 异常封装 ~~~java public class MyException extends RuntimeException{ private String msg; private int code = 500; public MyException(){ super(ErrorEnum.UNKNOWN.getMsg()); this.msg=ErrorEnum.UNKNOWN.getMsg(); } public MyException(ErrorEnum eEnum,Throwable e){ super(eEnum.getMsg(),e); this.msg=eEnum.getMsg(); this.code=eEnum.getCode(); } public MyException(ErrorEnum eEnum){ this.msg=eEnum.getMsg(); this.code=eEnum.getCode(); } public MyException(String exception){ this.msg=exception; } } ~~~ - 异常处理器 ~~~java @RestControllerAdvice @Slf4j public class MyExceptionHandler { /** * 处理自定义异常 * @param e * @return */ @ExceptionHandler(MyException.class) public Result handleMyException(MyException e){ Result result=new Result(); result.put("code",e.getCode()); result.put("msg",e.getMsg()); return result; } @ExceptionHandler(NoHandlerFoundException.class) public Result handlerNoFoundException(Exception e){ log.error(e.getMessage(),e); return Result.exception(ErrorEnum.PATH_NOT_FOUND); } @ExceptionHandler(DuplicateKeyException.class) public Result handleDuplicateKeyException(DuplicateKeyException e){ log.error(e.getMessage(),e); return Result.exception(ErrorEnum.DUPLICATE_KEY); } @ExceptionHandler(AuthorizationException.class) public Result handleAuthorizationException(AuthorizationException e){ log.error(e.getMessage(),e); return Result.exception(ErrorEnum.NO_AUTH); } @ExceptionHandler(Exception.class) public Result handleException(Exception e){ log.error(e.getMessage(),e); return Result.exception(); } } ~~~ ## 其他技术 - 使用了es和rabbitmq 使用注解 `@RefreshEsMqSender(sender = "dbblog-manage-updateArticle")` 在发送博客或更新博客的时候,在方法上面添加了 `@RefreshEsMqSender(sender = "dbblog-manage-updateArticle")`的注解,会触发 Aspect 进行该方法的增加。 - Aspect类 ~~~java @Aspect @Component public class RefreshEsMqAspect { @Resource private RabbitMqUtils rabbitMqUtils; @Pointcut("@annotation(cn.dblearn.blog.common.mq.annotation.RefreshEsMqSender)") public void pointCut() { } @Around("pointCut()") public Object around(ProceedingJoinPoint point) throws Throwable { //执行方法 Object result = point.proceed(); MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); RefreshEsMqSender senderAnnotation = method.getAnnotation(RefreshEsMqSender.class); // 发送刷新信息 rabbitMqUtils.send(RabbitMqConstants.REFRESH_ES_INDEX_QUEUE,senderAnnotation.sender()+" "+senderAnnotation.msg()); return result; } } ~~~ 上面的增强方法执行之后,主要的一点就是 使用 rabbitMqUtils发送了一条rabbitmq消息出去。 - 自定义注解 ~~~ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RefreshEsMqSender { String sender(); String msg() default "send refresh msg to ElasticSearch"; } ~~~ 消息发送到了rabbit上面,添加了@RabbitListener注解的方法会监听数据接收 ~~~java @RabbitListener(queues=RabbitMqConstants.REFRESH_ES_INDEX_QUEUE) public void refresh(Message message, Channel channel){ try { //手动确认消息已经被消费 channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); articleRepository.deleteAll(); List<Article> list = articleService.list(new QueryWrapper<Article>().lambda().eq(Article::getPublish,true)); articleRepository.saveAll(list); log.info(message.toString()); } catch (IOException e) { e.printStackTrace(); } } ~~~ 可以看到这里 作者处理的方式比较 简单粗暴,直接 deleteAll 了所有的索引,然后saveAll 重建。
superadmin
2023年10月15日 16:59
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码