学习目标

1、掌握mybatis-plus代码生成器的使用

2、掌握springcloud-alibaba-dubbo的使用

3、完成品牌管理开发

4、完成门店管理开发

5、完成用户管理开发

第一章 mybatis-plus代码生成器

AutoGenerator 是 MyBatis-Plus 的代码生成器,通过 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各个模块的代码,极大的提升了开发效率

1、CodeGenerator核心类

下面我们看下餐掌柜平台中是如何集成AutoGenerator ,首相我们找CodeGenerator类,目录如下

image.png

在CodeGenerator中我们使用了AutoGenerator,下面我们逐行解释:

  1. package com.itheima.restkeeper.generator;
  2. import com.baomidou.mybatisplus.generator.AutoGenerator;
  3. import com.baomidou.mybatisplus.generator.InjectionConfig;
  4. import com.baomidou.mybatisplus.generator.config.*;
  5. import com.baomidou.mybatisplus.generator.config.rules.DateType;
  6. import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
  7. import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
  8. import com.itheima.restkeeper.utils.EmptyUtil;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. import java.util.ResourceBundle;
  12. /**
  13. * @Description:代码生成器
  14. */
  15. public class CodeGenerator {
  16. public static void autoGenerator(){
  17. //用来获取Mybatis-Plus.properties文件的配置信息
  18. final ResourceBundle rb = ResourceBundle.getBundle("mybatis-plus-generrator");
  19. // 代码生成器
  20. AutoGenerator mpg = new AutoGenerator();
  21. // 全局配置
  22. GlobalConfig gc = new GlobalConfig();
  23. String projectPath =rb.getString("projectPath");
  24. gc.setOutputDir(projectPath + "/src/main/java");
  25. gc.setAuthor(rb.getString("author"));
  26. gc.setOpen(false);
  27. gc.setFileOverride(true);
  28. //指定时间处理类型
  29. gc.setDateType(DateType.ONLY_DATE);
  30. gc.setSwagger2(true); //实体属性 Swagger2 注解
  31. mpg.setGlobalConfig(gc);
  32. //数据源配置
  33. DataSourceConfig dsc = new DataSourceConfig();
  34. dsc.setUrl(rb.getString("url"));
  35. // dsc.setSchemaName("public");
  36. dsc.setDriverName("com.mysql.jdbc.Driver");
  37. dsc.setUsername(rb.getString("userName"));
  38. dsc.setPassword(rb.getString("password"));
  39. mpg.setDataSource(dsc);
  40. //包配置
  41. PackageConfig pc = new PackageConfig();
  42. pc.setModuleName(rb.getString("moduleName"));
  43. pc.setParent(rb.getString("parent"));
  44. pc.setController("web");
  45. pc.setService("service");
  46. pc.setServiceImpl("service.impl");
  47. pc.setEntity("pojo");
  48. pc.setMapper("mapper");
  49. mpg.setPackageInfo(pc);
  50. // 自定义配置
  51. InjectionConfig cfg = new InjectionConfig() {
  52. @Override
  53. public void initMap() {
  54. // to do nothing
  55. }
  56. };
  57. // 如果模板引擎是 freemarker
  58. String templatePath = "/templates/mapper.xml.ftl";
  59. // 自定义输出配置
  60. List<FileOutConfig> focList = new ArrayList<>();
  61. cfg.setFileOutConfigList(focList);
  62. mpg.setCfg(cfg);
  63. // 配置模板
  64. TemplateConfig templateConfig = new TemplateConfig();
  65. if ("true".equals(rb.getString("entity"))){
  66. String entityFtlPath = rb.getString("entity.ftl.path");
  67. if (!EmptyUtil.isNullOrEmpty(entityFtlPath)){
  68. templateConfig.setEntity(entityFtlPath);
  69. }
  70. }else {
  71. templateConfig.setEntity(null);
  72. }
  73. if ("true".equals(rb.getString("mapper"))){
  74. String mapperFtlPath = rb.getString("mapper.ftl.path");
  75. if (!EmptyUtil.isNullOrEmpty(mapperFtlPath)){
  76. templateConfig.setMapper(mapperFtlPath);
  77. }
  78. }else {
  79. templateConfig.setMapper(null);
  80. }
  81. if ("true".equals(rb.getString("service"))){
  82. String serviceFtlPath = rb.getString("service.ftl.path");
  83. if (!EmptyUtil.isNullOrEmpty(serviceFtlPath)){
  84. templateConfig.setService(serviceFtlPath);
  85. }
  86. }else {
  87. templateConfig.setService(null);
  88. }
  89. if ("true".equals(rb.getString("serviceImp"))){
  90. String serviceImpFtlPath = rb.getString("serviceImp.ftl.path");
  91. if (!EmptyUtil.isNullOrEmpty(serviceImpFtlPath)){
  92. templateConfig.setServiceImpl(serviceImpFtlPath);
  93. }
  94. }else {
  95. templateConfig.setServiceImpl(null);
  96. }
  97. if ("true".equals(rb.getString("controller"))){
  98. String controllerFtlPath = rb.getString("controller.ftl.path");
  99. if (!EmptyUtil.isNullOrEmpty(controllerFtlPath)){
  100. templateConfig.setController(controllerFtlPath);
  101. }
  102. }else {
  103. templateConfig.setController(null);
  104. }
  105. templateConfig.setXml(null);
  106. mpg.setTemplate(templateConfig);
  107. // 策略配置
  108. StrategyConfig strategy = new StrategyConfig();
  109. strategy.setNaming(NamingStrategy.underline_to_camel);
  110. strategy.setColumnNaming(NamingStrategy.underline_to_camel);
  111. strategy.setSuperEntityClass(rb.getString("SuperEntityClass"));
  112. strategy.setEntityLombokModel(true);
  113. strategy.setRestControllerStyle(true);
  114. // 写于父类中的公共字段
  115. String[] SuperEntityColumns = rb.getString("superEntityColumns").split(",");
  116. strategy.setSuperEntityColumns(SuperEntityColumns);
  117. strategy.setInclude(rb.getString("tableName").split(","));
  118. strategy.setControllerMappingHyphenStyle(true);
  119. String tablePrefix = rb.getString("tablePrefix");
  120. if (tablePrefix!=null){
  121. strategy.setTablePrefix(tablePrefix);
  122. }
  123. mpg.setStrategy(strategy);
  124. mpg.setTemplateEngine(new FreemarkerTemplateEngine());
  125. mpg.execute();
  126. }
  127. }

2、生成器快速入门

  1. 上面我们的定义了CodeGenerator核心类,下面我们需要在项目中使用,各位在使用代码生成器之前,需要明确我们使用的模块,这里我们所有的pojomapperservice层都定义在model-***-service类型的模块中,这里以model-shop-service为例,其他模块使用方式也类似,在使用之前我们需要定义2个资源:
  • mybatis-plus-generrator.properties:关于数据库及生成策略定义
  • templates:代码生成器模板信息

,定义信息如下:

image.png

2.1、generrator

generrator.properties此文件的作用主要是定义关于数据库及生成策略定义

  1. #数据库地址
  2. url=jdbc:mysql://192.168.112.77:3306/restkeeper-shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&tinyInt1isBit=false
  3. #数据库账号
  4. userName=root
  5. #数据库密码
  6. password=root
  7. #此处为本项目src所在路径(代码生成器输出路径)
  8. projectPath=F:/restkeeper-prent/restkeeper-super/restkeeper-model-shop/model-shop-service
  9. #设置作者
  10. author=Admin
  11. #自定义包路径
  12. parent=com.itheima
  13. #装代码的文件夹名
  14. moduleName=restkeeper
  15. #设置表前缀,不设置则默认无前缀
  16. tablePrefix =tab_
  17. #数据库表名(此处切不可为空,如果为空,则默认读取数据库的所有表名)
  18. tableName=tab_brand,tab_category,tab_dish,tab_dish_flavor,tab_order,tab_order_item,tab_printer,tab_printer_dish,tab_store,tab_table,tab_table_area
  19. #pojo的超类
  20. SuperEntityClass = com.itheima.restkeeper.basic.BasicPojo
  21. #pojo的超类公用字段
  22. superEntityColumns = id,created_time,updated_time,sharding_id,enable_flag
  23. #生成的层级
  24. entity=true
  25. entity.ftl.path=/templates/entity.java
  26. mapper=false
  27. mapper.ftl.path=/templates/mapper.java
  28. service=false
  29. service.ftl.path=/templates/service.java
  30. serviceImp=false
  31. serviceImp.ftl.path=/templates/serviceImpl.java
  32. controller=false
  33. controller.ftl.path=/templates/controller.java

2.2、templates

templates里面的内容如下,其主要作用是根据模板生成对应代码,当然这里的文件不需要各位维护,如果你想维护,请先学习freemarker模板引擎,这里不做讲解

image.png

2.3、生成器使用

直接在test目录中简历单元测试类直接执行即可

  1. package com.itheima.restkeeper;
  2. import com.itheima.restkeeper.generator.CodeGenerator;
  3. import org.junit.Test;
  4. /**
  5. * @Description:代码生成器
  6. */
  7. public class ShopGenerator {
  8. @Test
  9. public void test(){
  10. CodeGenerator.autoGenerator();
  11. }
  12. }

image.png

这时我们查看日志可以发现在model-shop-service模块中代码已经自动生成

image.png

第二章 springcloud-alibaba-dubbo

1、dubbo简介

协议方式==dubbo ==>长连接、单一链接

  1. 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进。

image.png

单一应用架构

  1. 当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。此时,用于简化增删改查工作量的数据访问框架(ORM)是关键。

垂直应用架构

  1. 当访问量逐渐增大,单一应用增加机器带来的加速度越来越小,提升效率的方法之一是将应用拆成互不相干的几个应用,以提升效率。此时,用于加速前端页面开发的Web框架(MVC)是关键。

分布式服务架构

  1. 当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式服务框架(RPC)是关键。

流动计算架构

  1. 当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键。

下面我们看下dubbo架构

image.png

image.png

名词解释

节点 角色说明
Provider 暴露服务的服务提供方【生产者】
Consumer 调用远程服务的服务消费方【消费者】
Registry 服务注册与发现的注册中心【nacos】
Monitor 统计服务的调用次数和调用时间的监控中心【监控出现问题不影响服务调用】
Container 服务运行容器

调用关系说明

  • 服务容器负责启动,加载,运行服务提供者。
  • 服务提供者在启动时,向添加中心添加自己提供的服务。
  • 服务消费者在启动时,向添加中心订阅自己所需的服务。
  • 添加中心返回服务提供者地址列表给消费者,如果有变更,添加中心将基于长连接推送变更数据给消费者。
  • 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另一台调用。
  • 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心

2、dubbo快速入门

上面我们介绍了dubbo的架构,下面我们来使用dubbo来进行开发,在开发之前,我们先看下面的图解:

image.png

从上图我们可用看出在一个标准的dubbo服务调用中,他分为3个部分

dubbo-interface

  1. 负责接口的定义,这里我们通常定义***Face结构的接口类,例如:UserFace

dubbo-producer

  1. 【生产者】负责接口的实现,这里我们通常用==@DubboService==定义***FaceImpl结构的接口类,例如:UserFaceImpl

dubbo-web

  1. 【消费者】负责调用接口,通常我们在web层使用==@DubboReference==调用接口

下面我们来构建第一个dubbo服务,我们需要在dubbo-parent中pom.xml引入下列依赖:

  1. <dependencies>
  2. <!--接口定义层-->
  3. <dependency>
  4. <groupId>com.itheima.dubbo</groupId>
  5. <artifactId>dubbo-interface</artifactId>
  6. <version>${interFace.version}</version>
  7. </dependency>
  8. <!---spring-cloud-alibaba主配置-->
  9. <dependency>
  10. <groupId>com.alibaba.cloud</groupId>
  11. <artifactId>spring-cloud-alibaba-dependencies</artifactId>
  12. <version>${spring-cloud-alibaba.version}</version>
  13. <type>pom</type>
  14. <scope>import</scope>
  15. </dependency>
  16. <!---springboot主配置-->
  17. <dependency>
  18. <groupId>org.springframework.boot</groupId>
  19. <artifactId>spring-boot-dependencies</artifactId>
  20. <version>${spring.boot.version}</version>
  21. <type>pom</type>
  22. <scope>import</scope>
  23. </dependency>
  24. </dependencies>

在dubbo-producer和dubbo-web中pom.xml导入:

  1. <dependencies>
  2. <!--接口定义层-->
  3. <dependency>
  4. <groupId>com.itheima.dubbo</groupId>
  5. <artifactId>dubbo-interface</artifactId>
  6. </dependency>
  7. <!--web支持-->
  8. <dependency>
  9. <groupId>org.springframework.boot</groupId>
  10. <artifactId>spring-boot-starter-web</artifactId>
  11. </dependency>
  12. <!--nacos支持-->
  13. <dependency>
  14. <groupId>com.alibaba.cloud</groupId>
  15. <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
  16. </dependency>
  17. <!--dubbo支持-->
  18. <dependency>
  19. <groupId>com.alibaba.cloud</groupId>
  20. <artifactId>spring-cloud-starter-dubbo</artifactId>
  21. </dependency>
  22. </dependencies>

2.1、生产者配置

生产者:dubbo-producer负责服务的提供,我们需要把他添加到nacos添加中心中,在application.yml添加:

  1. #服务配置
  2. server:
  3. #端口
  4. port: 8080
  5. #服务编码
  6. tomcat:
  7. uri-encoding: UTF-8
  8. #spring相关配置
  9. spring:
  10. #应用配置
  11. application:
  12. #应用名称
  13. name: dubbo-producer
  14. main:
  15. allow-bean-definition-overriding: true
  16. cloud:
  17. #nacos添加中心
  18. nacos:
  19. discovery:
  20. server-addr: 192.168.112.77:8848
  21. namespace: public
  22. group: SEATA_GROUP
  23. dubbo:
  24. #dubbo服务版本
  25. application:
  26. version: 1.0.0
  27. logger: slf4j
  28. #dubbo接口扫描路径
  29. scan:
  30. base-packages: com.itheima.dubbo
  31. #dubbo服务添加
  32. registry:
  33. address: spring-cloud://192.168.112.77
  34. #dubbo服务协议类型及端口,线程数【这里是默认配置】
  35. protocol:
  36. name: dubbo
  37. port: 28080
  38. threads: 200
  39. accesslog: D:/logs/dubbo-producer-01.log

这里我们实现dubbo-interface的Userface接口:

  1. package com.itheima.dubbo;
  2. import org.apache.dubbo.config.annotation.DubboService;
  3. /**
  4. * @ClassName UserFaceImpl.java
  5. * @Description 用户接口实现
  6. */
  7. @DubboService(version = "${dubbo.application.version}",timeout = 5000)
  8. public class UserFaceImpl implements UserFace {
  9. @Override
  10. public String helloUser(String userName) {
  11. return "Hello!"+userName;
  12. }
  13. }

2.2、消费者配置

消费者:dubbo-web负责服务的接口消费,我们需要把他添加到nacos添加中心中,在application.yml添加:

  1. #服务配置
  2. server:
  3. #端口
  4. port: 8081
  5. #服务编码
  6. tomcat:
  7. uri-encoding: UTF-8
  8. #spring相关配置
  9. spring:
  10. #应用配置
  11. application:
  12. #应用名称
  13. name: dubbo-web
  14. main:
  15. allow-bean-definition-overriding: true
  16. #nacos添加中心
  17. cloud:
  18. nacos:
  19. discovery:
  20. server-addr: 192.168.112.77:8848
  21. namespace: public
  22. group: SEATA_GROUP
  23. #dubbo消费端配置
  24. dubbo:
  25. application:
  26. version: 1.0.0
  27. logger: slf4j
  28. cloud:
  29. #表示要订阅服务的服务名,可以配置'*',代表订阅所有服务,不推荐使用。若需订阅多应用,使用 "," 分割。
  30. subscribed-services: dubbo-producer
  31. scan:
  32. #扫描路径
  33. base-packages: com.itheima.dubbo.web
  34. registry:
  35. address: spring-cloud://192.168.112.77
  36. #dubbo服务协议类型及端口,线程数【这里是默认配置】
  37. protocol:
  38. name: dubbo
  39. port: 28081
  40. threads: 200
  41. accesslog: D:/logs/dubbo-web-01.log

这里我们调用dubbo-interface的Userface接口:

  1. package com.itheima.dubbo.web;
  2. import com.itheima.dubbo.UserFace;
  3. import org.apache.dubbo.config.annotation.DubboReference;
  4. import org.springframework.web.bind.annotation.GetMapping;
  5. import org.springframework.web.bind.annotation.PathVariable;
  6. import org.springframework.web.bind.annotation.RestController;
  7. /**
  8. * @ClassName UserController.java
  9. * @Description 用户controller
  10. */
  11. @RestController
  12. public class UserController {
  13. @DubboReference(version = "${dubbo.application.version}",check = false)
  14. UserFace userFace;
  15. @GetMapping("{userName}")
  16. public String helloUser(@PathVariable("userName") String userName){
  17. return userFace.helloUser(userName);
  18. }
  19. }

启动dubbo-producer和dubbo-web模块,访问http://127.0.0.1:8081/itheima

image.png

3、业务模块开发

3.1、业务调用链路

在开始业务开发之前,我们首先看一下系统的调用链路,以restkeeper-model-shop模块为例,其调用的时序图如下所示:

image.png

以restkeeper-model-shop模块为例,一个标准的模块其模块结构如下

|——restkeeper-model-shop 商家服务平台
|
|———— model-shop-applet H5点餐业务dubbo接口实现【生产者】
|
|———— model-shop-interface 商家平台所有dubbo接口定义
|
|———— model-shop-job-listen 商家服务平台定时任务及监听模块【监听消费、定义任务】
|
|———— model-shop-producer 后端业务dubbo接口实现【生产者】
|
|———— model-shop-service 核心业务层【被所有生产者、消费者、监听、定时任务依赖】
|
|———— model-shop-user 用户业务依赖于model-security-service的业务实现【生产者】
|
|———— model-shop-web 对外商家服务平台web层,被restkeeper-gateway-shop系统调用【消费者者】

3.2、dubbo服务提供者

  1. restkeeper-model-shop模块中有3个【生产者】模块:model-shop-appletmodel-shop-producermodel-shop-user ,这里以model-shop-producer为例,首先查看模块依赖关系:

image.png

其中model-shop-producer模块他有以下职能:

  • dubbo微服务实现层【生产者】
  • 对象转换:从POJO对象转换为VO对象
  • 调用server【核心业务层】实现dubbo服务接口的业务逻辑

注意:当产生跨服务接口调用,例如一个dubbo接口需要多个dubbo接口来支持,我们会放到face层进行服务调用然后业务组装,如果牵涉分布式事务问题,我们会采用seata方式来解决

下面我们对model-shop-producer进行dubbo的集成,首先在model-shop-producer的pom.xml导入下列依赖:

  1. <!-- Dubbo Spring Cloud Starter -->
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-dubbo</artifactId>
  5. </dependency>

再在model-shop-producer的application.yml添加定义如下:

  1. dubbo:
  2. #dubbo应用服务定义
  3. application:
  4. #版本
  5. version: 1.0.0
  6. #日志
  7. logger: slf4j
  8. scan:
  9. #扫描路径
  10. base-packages: com.itheima.restkeeper
  11. cloud:
  12. #表示要订阅服务的服务名,可以配置'*',代表订阅所有服务,不推荐使用。若需订阅多应用,使用 "," 分割。
  13. subscribed-services: model-basic-producer,model-trading-producer
  14. registry:
  15. #注册中心
  16. address: spring-cloud://192.168.112.77
  17. #服务协议定义
  18. protocol:
  19. #服务协议名称
  20. name: dubbo
  21. #协议端口
  22. port: 27077
  23. #线程数
  24. threads: 200
  25. #dubbo调用日志
  26. accesslog: D:/logs/model-shop-producer-01.log

3.3、dubbo服务消费者

在restkeeper-model-shop模块中有1个【消费者】模块:model-shop-web,这里以model-shop-web为例,首先查看模块依赖关系:

image.png

其中model-shop-web模块他有以下职能:

  • 传入参数的接收及校验工作
  • 调用对应业务的dubbo服务,本身不负责业务逻辑的处理【消费者】
  • 返回参数的封装,以及当下层发生异常,则抛出指定的自定义异常
  • 定义swagger2的接口暴露,方便测试

下面我们对model-shop-web进行dubbo的集成,首先在model-shop-producer的pom.xml导入下列依赖:

  1. <!-- Dubbo Spring Cloud Starter -->
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring-cloud-starter-dubbo</artifactId>
  5. </dependency>

再在model-shop-web的application.yml添加定义如下:

  1. dubbo:
  2. #dubbo应用服务定义
  3. application:
  4. #版本
  5. version: 1.0.0
  6. #日志
  7. logger: slf4j
  8. cloud:
  9. #表示要订阅服务的服务名,可以配置'*',代表订阅所有服务,不推荐使用。若需订阅多应用,使用 "," 分割。
  10. subscribed-services: model-shop-producer,model-shop-applet,model-shop-user
  11. scan:
  12. #扫描路径
  13. base-packages: com.itheima.restkeeper.web
  14. registry:
  15. #添加中心
  16. address: spring-cloud://192.168.112.77
  17. #服务协议定义
  18. protocol:
  19. #服务协议名称
  20. name: dubbo
  21. #协议端口
  22. port: 27078
  23. #线程数
  24. threads: 200
  25. #dubbo调用日志
  26. accesslog: D:/logs/model-shop-web-01.log

subscribed-services属性配置,此属性为订阅服务的服务名

3.4、dubbo接口定义

  1. 可能在以往的开发中我们只是知道三层架构【mapperserviceweb】,那这里的face层是什么意思呢?大家都知道dubbo服务的调用逻辑,【消费者】调用【生成者】,那他们直接能调用的集成也就是声明统一的接口定义,在餐掌柜系统中dubbo层接口就起到此作用:
  • 定义dubbo服务接口
  • 被生产者依赖,按照face层的dubbo接口定义实现业务
  • 被消费者依赖,从face层的dubbo中选择自己的业务接口

首先我们需要定义一个dubbo接口,那我们在哪里写能?从餐掌柜maven分层构建中我们可用发现,每个以==restkeeper-model-==开头的项目都是一个二级模块,并且模块中都有一个model--interface的模块,例如:

image.png

没有错,这里就是我们定义face接口的三级模块,在定义dubbo接口的时,都需要找到类似:model-*-interface的模块去书写

3.5、service核心业务

上面我们再介绍【dubbo层接口实现】提到dubbo层接口实现会调用核心业务,这个核心也就是这里的service层,还是以restkeeper-model-shop为例:

image.png

如果模块以model-*-service的格式出现,则表示此模块为核心模块,职能:

  • pojo、mapper、service层定义
  • 使用mybatis-push持久化框架完成CRUD操作
  • 作为【核心业务层】被dubbo服务接口实现所调用
  • 提供代码生成器的支持

在定义service接口时都需要继承IService,IService为我们提供了基本的操作:

Save

  1. // 插入一条记录(选择字段,策略插入)
  2. boolean save(T entity);
  3. // 插入(批量)
  4. boolean saveBatch(Collection<T> entityList);
  5. // 插入(批量)
  6. boolean saveBatch(Collection<T> entityList, int batchSize);

SaveOrUpdate

  1. // TableId 注解存在更新记录,否插入一条记录
  2. boolean saveOrUpdate(T entity);
  3. // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
  4. boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);
  5. // 批量修改插入
  6. boolean saveOrUpdateBatch(Collection<T> entityList);
  7. // 批量修改插入
  8. boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);

Remove

  1. // 根据 entity 条件,删除记录
  2. boolean remove(Wrapper<T> queryWrapper);
  3. // 根据 ID 删除
  4. boolean removeById(Serializable id);
  5. // 根据 columnMap 条件,删除记录
  6. boolean removeByMap(Map<String, Object> columnMap);
  7. // 删除(根据ID 批量删除)
  8. boolean removeByIds(Collection<? extends Serializable> idList);

Update

  1. // 根据 UpdateWrapper 条件,更新记录 需要设置
  2. boolean update(Wrapper<T> updateWrapper);
  3. // 根据 whereWrapper 条件,更新记录
  4. boolean update(T updateEntity, Wrapper<T> whereWrapper);
  5. // 根据 ID 选择修改
  6. boolean updateById(T entity);
  7. // 根据ID 批量更新
  8. boolean updateBatchById(Collection<T> entityList);
  9. // 根据ID 批量更新
  10. boolean updateBatchById(Collection<T> entityList, int batchSize);

Get

  1. // 根据 ID 查询
  2. T getById(Serializable id);
  3. // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
  4. T getOne(Wrapper<T> queryWrapper);
  5. // 根据 Wrapper,查询一条记录
  6. T getOne(Wrapper<T> queryWrapper, boolean throwEx);
  7. // 根据 Wrapper,查询一条记录
  8. Map<String, Object> getMap(Wrapper<T> queryWrapper);
  9. // 根据 Wrapper,查询一条记录
  10. <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

List

  1. // 查询所有
  2. List<T> list();
  3. // 查询列表
  4. List<T> list(Wrapper<T> queryWrapper);
  5. // 查询(根据ID 批量查询)
  6. Collection<T> listByIds(Collection<? extends Serializable> idList);
  7. // 查询(根据 columnMap 条件)
  8. Collection<T> listByMap(Map<String, Object> columnMap);
  9. // 查询所有列表
  10. List<Map<String, Object>> listMaps();
  11. // 查询列表
  12. List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
  13. // 查询全部记录
  14. List<Object> listObjs();
  15. // 查询全部记录
  16. <V> List<V> listObjs(Function<? super Object, V> mapper);
  17. // 根据 Wrapper 条件,查询全部记录
  18. List<Object> listObjs(Wrapper<T> queryWrapper);
  19. // 根据 Wrapper 条件,查询全部记录
  20. <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

Page

  1. // 无条件分页查询
  2. IPage<T> page(IPage<T> page);
  3. // 条件分页查询
  4. IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
  5. // 无条件分页查询
  6. IPage<Map<String, Object>> pageMaps(IPage<T> page);
  7. // 条件分页查询
  8. IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

Count

  1. // 查询总记录数
  2. int count();
  3. // 根据 Wrapper 条件,查询总记录数
  4. int count(Wrapper<T> queryWrapper);

Chain

query

  1. // 链式查询 普通
  2. QueryChainWrapper<T> query();
  3. // 链式查询 lambda 式。注意:不支持 Kotlin
  4. LambdaQueryChainWrapper<T> lambdaQuery();
  5. // 示例:
  6. query().eq("column", value).one();
  7. lambdaQuery().eq(Entity::getId, value).list();

update

  1. // 链式更改 普通
  2. UpdateChainWrapper<T> update();
  3. // 链式更改 lambda 式。注意:不支持 Kotlin
  4. LambdaUpdateChainWrapper<T> lambdaUpdate();
  5. // 示例:
  6. update().eq("column", value).remove();
  7. lambdaUpdate().eq(Entity::getId, value).update(entity);

mapper继承BaseMapper的方法:

Insert

  1. // 插入一条记录int insert(T entity);

Delete

  1. // 根据 entity 条件,删除记录
  2. int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);
  3. // 删除(根据ID 批量删除)
  4. int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
  5. // 根据 ID 删除
  6. int deleteById(Serializable id);
  7. // 根据 columnMap 条件,删除记录
  8. int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

Update

  1. // 根据 whereWrapper 条件,更新记录
  2. int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);
  3. // 根据 ID 修改
  4. int updateById(@Param(Constants.ENTITY) T entity);

Select

  1. // 根据 ID 查询
  2. T selectById(Serializable id);
  3. // 根据 entity 条件,查询一条记录
  4. T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
  5. // 查询(根据ID 批量查询)
  6. List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
  7. // 根据 entity 条件,查询全部记录
  8. List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
  9. // 查询(根据 columnMap 条件)
  10. List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
  11. // 根据 Wrapper 条件,查询全部记录
  12. List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
  13. // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
  14. List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
  15. // 根据 entity 条件,查询全部记录(并翻页)
  16. IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
  17. // 根据 Wrapper 条件,查询全部记录(并翻页)
  18. IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
  19. // 根据 Wrapper 条件,查询总记录数
  20. Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

4、商家平台启动

image.png

启动运营平台后,再启动上述模块,然后启动restkeeper-vue-shop模块,

注意检查hosts的配置 :127.0.0.1 ppsk.shop.eehp.cn

访问路径:ppsk.shop.eehp.cn:8080

账号:pp@qq.com 密码 pass

启动时增加内存限制,防止内存不够:-Xmx128M -XX:MetaspaceSize=128M -XX:MaxMetaspaceSize=128M

image.png

第三章 商家平台-品牌管理

餐掌柜是一个SAAS系统,商家入驻系统后,运营平台可以为商家开通管理账号,商家可以独立维护自己的多个品牌,例如

image.png

从图中我们可以看到:商家【润润餐饮集团】下就有多个品牌,可以对品牌做管理,下面我们就来实现品牌的管理功能

1、功能区拆解

image.png

红色区域:此处为查询条件功能区,筛选列表信息

功能 说明
品牌 输入品牌名称,键盘按enter触发搜索列表
分类 从数据字典加载数据
,change触发搜索列表
状态 从数据字典加载数据,下拉框,显示【启用、禁用】,change触发搜索列表

蓝色区域:品牌信息维护功能

功能 说明
添加 新增品牌信息,包含品牌图片上传,分类、状态设置等功能
修改 修改品牌信息,包含品牌图片上传,分类、状态设置等功能
删除 删除品牌信息==【真实删除】==,删除时,会有再次确认提示
禁用、启用 禁用、启用品牌

2、数据库结构

  1. CREATE TABLE `tab_brand` (
  2. `id` bigint(18) NOT NULL COMMENT '品牌id',
  3. `brand_name` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '品牌名称',
  4. `category` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '品牌分类',
  5. `enable_flag` varchar(18) CHARACTER SET utf8 DEFAULT NULL COMMENT '是否有效',
  6. `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  7. `updated_time` datetime DEFAULT NULL COMMENT '创建时间',
  8. `sharding_id` bigint(18) DEFAULT NULL COMMENT '分库id',
  9. `enterprise_id` bigint(18) NOT NULL COMMENT '商户号',
  10. PRIMARY KEY (`id`)
  11. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='品牌管理';

自动填充

下列字段,无需手动填充,系统会自动填充下列字段

字段 注释 填充方式
id 主键 雪花算法
enterprise_id 商户号 mybatis-plus-多租户拦截
created_time 创建时间 mybatis-plus-自动填充组件
updated_time 修改时间 mybatis-plus-自动填充组件
sharding_id 分库id mybatis-plus-自动填充组件

3、功能开发

在开始业务开发之前,我们首先看一下品牌的UML图

image.png

BrandController:对BrandFace接口进行dubbo的RPC调用,为dubbo服务的消费者

AffixFace【公用功能】:附件上传dubbo接口,我们在创建品牌时,需要上传品牌图片到图片中心

BrandFace:品牌dubbo接口定义对AffixFace进行dubbo的RPC调用

BrandFaceImpl:品牌dubbo接口定义实现,这里做VO和POJO的转换

IBrandService:品牌的业务接口定义,为BrandFaceImpl提供核心业务逻辑的定义

BrandServiceImpl:品牌的业务接口定义实现

3.1、BrandFace接口

  1. 在开发中,我们以往都是先写controller层业务1口,因为我们一致提倡的是面向接口开发,而这里,我们应该先定义dubbo服务的接口,只有统一接口在做微服务调用时才能保证逻辑的清晰
  1. package com.itheima.restkeeper;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.exception.ProjectException;
  4. import com.itheima.restkeeper.req.BrandVo;
  5. import java.util.List;
  6. /**
  7. * @ClassName BrandFace.java
  8. * @Description 品牌dubbo服务定义
  9. */
  10. public interface BrandFace {
  11. /**
  12. * @Description 品牌列表
  13. * @param brandVo 查询条件
  14. * @param pageNum 页码
  15. * @param pageSize 每页条数
  16. * @return Page<BrandVo>
  17. */
  18. Page<BrandVo> findBrandVoPage(BrandVo brandVo,
  19. int pageNum,
  20. int pageSize)throws ProjectException;
  21. /**
  22. * @Description 创建品牌
  23. * @param brandVo 对象信息
  24. * @return BrandVo
  25. */
  26. BrandVo createBrand(BrandVo brandVo)throws ProjectException;
  27. /**
  28. * @Description 修改品牌
  29. * @param brandVo 对象信息
  30. * @return Boolean
  31. */
  32. Boolean updateBrand(BrandVo brandVo)throws ProjectException;
  33. /**
  34. * @Description 删除品牌
  35. * @param checkedIds 选择中对象Ids
  36. * @return Boolean
  37. */
  38. Boolean deleteBrand(String[] checkedIds)throws ProjectException;
  39. /**
  40. * @Description 查找品牌
  41. * @param brandId 选择对象信息Id
  42. * @return BrandVo
  43. */
  44. BrandVo findBrandByBrandId(Long brandId)throws ProjectException;
  45. /***
  46. * @description 查询品牌下拉框
  47. * @return: List<BrandVo>
  48. */
  49. List<BrandVo> findBrandVoList()throws ProjectException;
  50. }

3.2、BrandFaceImpl接口实现

BrandFace接口定义实现,注意这里我们对VO和POJO的转换都在此类中进行处理,下面我们看下具体实现:

  1. package com.itheima.restkeeper.face;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.AffixFace;
  4. import com.itheima.restkeeper.BrandFace;
  5. import com.itheima.restkeeper.enums.BrandEnum;
  6. import com.itheima.restkeeper.exception.ProjectException;
  7. import com.itheima.restkeeper.pojo.Brand;
  8. import com.itheima.restkeeper.req.AffixVo;
  9. import com.itheima.restkeeper.req.BrandVo;
  10. import com.itheima.restkeeper.service.IBrandService;
  11. import com.itheima.restkeeper.utils.BeanConv;
  12. import com.itheima.restkeeper.utils.EmptyUtil;
  13. import com.itheima.restkeeper.utils.ExceptionsUtil;
  14. import lombok.extern.slf4j.Slf4j;
  15. import org.apache.dubbo.config.annotation.DubboReference;
  16. import org.apache.dubbo.config.annotation.DubboService;
  17. import org.apache.dubbo.config.annotation.Method;
  18. import org.springframework.beans.factory.annotation.Autowired;
  19. import java.util.List;
  20. import java.util.stream.Collectors;
  21. /**
  22. * @ClassName BrandFaceImpl.java
  23. * @Description 品牌dubbo接口定义实现
  24. */
  25. @DubboService(version = "${dubbo.application.version}",timeout = 5000,
  26. methods ={
  27. @Method(name = "findBrandVoPage",retries = 2),
  28. @Method(name = "createBrand",retries = 0),
  29. @Method(name = "updateBrand",retries = 0),
  30. @Method(name = "deleteBrand",retries = 0)
  31. })
  32. @Slf4j
  33. public class BrandFaceImpl implements BrandFace {
  34. @Autowired
  35. IBrandService brandService;
  36. @DubboReference(version = "${dubbo.application.version}",check = false)
  37. AffixFace affixFace;
  38. @Override
  39. public Page<BrandVo> findBrandVoPage(BrandVo brandVo,
  40. int pageNum,
  41. int pageSize) throws ProjectException{
  42. try {
  43. //查询Page<Brand>分页
  44. Page<Brand> page = brandService.findBrandVoPage(brandVo, pageNum, pageSize);
  45. //转化Page<Brand>为Page<BrandVo>
  46. Page<BrandVo> pageVo = new Page<>();
  47. BeanConv.toBean(page,pageVo);
  48. //转换List<Brand>为 List<BrandVo>
  49. List<Brand> brandList = page.getRecords();
  50. List<BrandVo> brandVoList = BeanConv.toBeanList(brandList,BrandVo.class);
  51. //处理附件
  52. if (!EmptyUtil.isNullOrEmpty(pageVo)&&!EmptyUtil.isNullOrEmpty(brandVoList)){
  53. brandVoList.forEach(n->{
  54. List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(n.getId());
  55. if (!EmptyUtil.isNullOrEmpty(affixVoList)){
  56. n.setAffixVo(affixVoList.get(0));
  57. }
  58. });
  59. }
  60. //指定带有附件消息的brandVoList到pageVo中
  61. pageVo.setRecords(brandVoList);
  62. //返回结果
  63. return pageVo;
  64. } catch (Exception e) {
  65. log.error("查询品牌列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  66. throw new ProjectException(BrandEnum.PAGE_FAIL);
  67. }
  68. }
  69. @Override
  70. public BrandVo createBrand(BrandVo brandVo) throws ProjectException{
  71. try {
  72. //保存品牌
  73. BrandVo brandVoResult = BeanConv.toBean(brandService.createBrand(brandVo), BrandVo.class);
  74. //绑定附件
  75. if (!EmptyUtil.isNullOrEmpty(brandVoResult)){
  76. affixFace.bindBusinessId(
  77. AffixVo.builder()
  78. .businessId(brandVoResult.getId())
  79. .id(brandVo.getAffixVo().getId())
  80. .build());
  81. }
  82. brandVoResult.setAffixVo(AffixVo.builder()
  83. .pathUrl(brandVo.getAffixVo().getPathUrl())
  84. .businessId(brandVoResult.getId())
  85. .id(brandVo.getAffixVo().getId()).build());
  86. //返回结果
  87. return brandVoResult;
  88. } catch (Exception e) {
  89. log.error("保存品牌异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  90. throw new ProjectException(BrandEnum.CREATE_FAIL);
  91. }
  92. }
  93. @Override
  94. public Boolean updateBrand(BrandVo brandVo)throws ProjectException {
  95. try {
  96. //修改品牌
  97. Boolean flag = brandService.updateBrand(brandVo);
  98. if (flag){
  99. //查询品牌附件消息
  100. List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(brandVo.getId());
  101. List<Long> affixIds = affixVoList.stream().map(AffixVo::getId).collect(Collectors.toList());
  102. if (!affixIds.contains(brandVo.getAffixVo().getId())){
  103. //删除图片
  104. flag = affixFace.deleteAffixVoByBusinessId(brandVo.getId());
  105. //绑定新图片
  106. affixFace.bindBusinessId(AffixVo.builder()
  107. .businessId(brandVo.getId())
  108. .id(brandVo.getAffixVo().getId())
  109. .build());
  110. }
  111. }
  112. return flag;
  113. } catch (Exception e) {
  114. log.error("修改品牌列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  115. throw new ProjectException(BrandEnum.UPDATE_FAIL);
  116. }
  117. }
  118. @Override
  119. public Boolean deleteBrand(String[] checkedIds) throws ProjectException{
  120. try {
  121. //删除品牌
  122. Boolean flag = brandService.deleteBrand(checkedIds);
  123. //删除图片,是不是可以debug进去看看?
  124. for (String checkedId : checkedIds) {
  125. affixFace.deleteAffixVoByBusinessId(Long.valueOf(checkedId));
  126. }
  127. return flag ;
  128. } catch (Exception e) {
  129. log.error("删除品牌列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  130. throw new ProjectException(BrandEnum.DELETE_FAIL);
  131. }
  132. }
  133. @Override
  134. public BrandVo findBrandByBrandId(Long brandId)throws ProjectException {
  135. try {
  136. //查找品牌消息
  137. Brand brand = brandService.getById(brandId);
  138. return BeanConv.toBean(brand,BrandVo.class);
  139. } catch (Exception e) {
  140. log.error("查找品牌所有品牌异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  141. throw new ProjectException(BrandEnum.SELECT_BRAND_FAIL);
  142. }
  143. }
  144. @Override
  145. public List<BrandVo> findBrandVoList()throws ProjectException {
  146. try {
  147. //查找品牌列表,用到哪里?要不要注意状态?
  148. return BeanConv.toBeanList(brandService.findBrandVoList(),BrandVo.class);
  149. } catch (Exception e) {
  150. log.error("查询品牌列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  151. throw new ProjectException(BrandEnum.PAGE_FAIL);
  152. }
  153. }
  154. }

3.3、IBrandService业务接口

品牌的业务接口定义,为BrandFaceImpl提供核心业务逻辑的定义,此接口继承了IService接口,IService里面有很多基础的方法可以直接使用

  1. package com.itheima.restkeeper.service;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.pojo.Brand;
  4. import com.baomidou.mybatisplus.extension.service.IService;
  5. import com.itheima.restkeeper.req.BrandVo;
  6. import java.util.List;
  7. /**
  8. * @Description:品牌管理 服务类
  9. */
  10. public interface IBrandService extends IService<Brand> {
  11. /**
  12. * @Description 品牌列表
  13. * @param brandVo 查询条件
  14. * @param pageNum 页码
  15. * @param pageSize 每页条数
  16. * @return Page<BrandVo>
  17. */
  18. Page<Brand> findBrandVoPage(BrandVo brandVo, int pageNum, int pageSize);
  19. /**
  20. * @Description 创建品牌
  21. * @param brandVo 对象信息
  22. * @return
  23. */
  24. Brand createBrand(BrandVo brandVo);
  25. /**
  26. * @Description 修改品牌
  27. * @param brandVo 对象信息
  28. * @return Boolean
  29. */
  30. Boolean updateBrand(BrandVo brandVo);
  31. /**
  32. * @Description 删除品牌
  33. * @param checkedIds 选择中对象Ids
  34. * @return Boolean
  35. */
  36. Boolean deleteBrand(String[] checkedIds);
  37. /***
  38. * @description 查询品牌下拉框
  39. * @return: List<BrandVo>
  40. */
  41. List<Brand> findBrandVoList();
  42. }

3.4、BrandServiceImpl接口实现

品牌的业务接口定义实现,这里继承ServiceImpl 类,实现 IBrandService接口

  1. package com.itheima.restkeeper.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  4. import com.itheima.restkeeper.constant.SuperConstant;
  5. import com.itheima.restkeeper.pojo.Brand;
  6. import com.itheima.restkeeper.mapper.BrandMapper;
  7. import com.itheima.restkeeper.req.BrandVo;
  8. import com.itheima.restkeeper.service.IBrandService;
  9. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  10. import com.itheima.restkeeper.utils.BeanConv;
  11. import com.itheima.restkeeper.utils.EmptyUtil;
  12. import org.springframework.stereotype.Service;
  13. import java.util.ArrayList;
  14. import java.util.Arrays;
  15. import java.util.List;
  16. /**
  17. * @Description:品牌管理 服务实现类
  18. */
  19. @Service
  20. public class BrandServiceImpl extends ServiceImpl<BrandMapper, Brand> implements IBrandService {
  21. @Override
  22. public Page<Brand> findBrandVoPage(BrandVo brandVo, int pageNum, int pageSize) {
  23. //构建分页对象
  24. Page<Brand> page = new Page<>(pageNum,pageSize);
  25. //构建查询条件
  26. QueryWrapper<Brand> queryWrapper = new QueryWrapper<>();
  27. //按品牌名称查询
  28. if (!EmptyUtil.isNullOrEmpty(brandVo.getBrandName())) {
  29. queryWrapper.lambda().likeRight(Brand::getBrandName,brandVo.getBrandName());
  30. }
  31. //按品牌分类查询
  32. if (!EmptyUtil.isNullOrEmpty(brandVo.getCategory())) {
  33. queryWrapper.lambda().likeRight(Brand::getCategory,brandVo.getCategory());
  34. }
  35. //按品牌状态查询
  36. if (!EmptyUtil.isNullOrEmpty(brandVo.getEnableFlag())) {
  37. queryWrapper.lambda().eq(Brand::getEnableFlag,brandVo.getEnableFlag());
  38. }
  39. //按创建时间降序
  40. queryWrapper.lambda().orderByDesc(Brand::getCreatedTime);
  41. //执行分页查询
  42. return page(page, queryWrapper);
  43. }
  44. @Override
  45. public Brand createBrand(BrandVo brandVo) {
  46. //转换BrandVo为Brand
  47. Brand brand = BeanConv.toBean(brandVo, Brand.class);
  48. boolean flag = save(brand);
  49. if (flag){
  50. return brand;
  51. }
  52. return null;
  53. }
  54. @Override
  55. public Boolean updateBrand(BrandVo brandVo) {
  56. //转换BrandVo为Brand
  57. Brand brand = BeanConv.toBean(brandVo, Brand.class);
  58. return updateById(brand);
  59. }
  60. @Override
  61. public Boolean deleteBrand(String[] checkedIds) {
  62. //转换数组为集合
  63. List<String> ids = Arrays.asList(checkedIds);
  64. List<Long> idsLong = new ArrayList<>();
  65. ids.forEach(n->{
  66. idsLong.add(Long.valueOf(n));
  67. });
  68. //批量删除
  69. return removeByIds(idsLong);
  70. }
  71. @Override
  72. public List<Brand> findBrandVoList() {
  73. //构建查询条件
  74. QueryWrapper<Brand> queryWrapper = new QueryWrapper<>();
  75. queryWrapper.lambda().eq(Brand::getEnableFlag, SuperConstant.YES);
  76. //查询有效状态
  77. return list(queryWrapper);
  78. }
  79. }

3.5、BrandController类

此类为服务的【消费者】,对BrandFace接口进行dubbo的RPC调用

  1. package com.itheima.restkeeper.web;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.BrandFace;
  4. import com.itheima.restkeeper.basic.ResponseWrap;
  5. import com.itheima.restkeeper.enums.BrandEnum;
  6. import com.itheima.restkeeper.req.BrandVo;
  7. import com.itheima.restkeeper.utils.ResponseWrapBuild;
  8. import io.swagger.annotations.Api;
  9. import io.swagger.annotations.ApiImplicitParam;
  10. import io.swagger.annotations.ApiImplicitParams;
  11. import io.swagger.annotations.ApiOperation;
  12. import lombok.extern.slf4j.Slf4j;
  13. import org.apache.dubbo.config.annotation.DubboReference;
  14. import org.springframework.web.bind.annotation.*;
  15. import java.util.List;
  16. /**
  17. * @ClassName BrandController.java
  18. * @Description 品牌Controller
  19. */
  20. @RestController
  21. @RequestMapping("brand")
  22. @Slf4j
  23. @Api(tags = "品牌controller")
  24. public class BrandController {
  25. @DubboReference(version = "${dubbo.application.version}",check = false)
  26. BrandFace brandFace;
  27. /**
  28. * @Description 品牌列表
  29. * @param brandVo 查询条件
  30. * @return
  31. */
  32. @PostMapping("page/{pageNum}/{pageSize}")
  33. @ApiOperation(value = "查询品牌分页",notes = "查询品牌分页")
  34. @ApiImplicitParams({
  35. @ApiImplicitParam(name = "brandVo",value = "品牌查询对象",required = true,dataType = "BrandVo"),
  36. @ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",dataType = "Integer"),
  37. @ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",dataType = "Integer")
  38. })
  39. public ResponseWrap<Page<BrandVo>> findBrandVoPage(
  40. @RequestBody BrandVo brandVo,
  41. @PathVariable("pageNum") int pageNum,
  42. @PathVariable("pageSize") int pageSize) {
  43. Page<BrandVo> brandVoPage = brandFace.findBrandVoPage(brandVo, pageNum, pageSize);
  44. return ResponseWrapBuild.build(BrandEnum.SUCCEED,brandVoPage);
  45. }
  46. /**
  47. * @Description 查询品牌下拉框
  48. * @return
  49. */
  50. @GetMapping("list")
  51. @ApiOperation(value = "查询品牌下拉框",notes = "查询品牌下拉框")
  52. public ResponseWrap<List<BrandVo>> findBrandVoList() {
  53. List<BrandVo> brandVoPage = brandFace.findBrandVoList();
  54. return ResponseWrapBuild.build(BrandEnum.SUCCEED,brandVoPage);
  55. }
  56. /**
  57. * @Description 添加品牌
  58. * @param brandVo 对象信息
  59. * @return
  60. */
  61. @PostMapping
  62. @ApiOperation(value = "添加品牌",notes = "添加品牌")
  63. @ApiImplicitParam(name = "brandVo",value = "品牌对象",required = true,dataType = "BrandVo")
  64. ResponseWrap<BrandVo> createBrand(@RequestBody BrandVo brandVo) {
  65. BrandVo brandVoResult = brandFace.createBrand(brandVo);
  66. return ResponseWrapBuild.build(BrandEnum.SUCCEED,brandVoResult);
  67. }
  68. /**
  69. * @Description 修改品牌
  70. * @param brandVo 对象信息
  71. * @return
  72. */
  73. @PatchMapping
  74. @ApiOperation(value = "修改品牌",notes = "修改品牌")
  75. @ApiImplicitParam(name = "brandVo",value = "品牌对象",required = true,dataType = "BrandVo")
  76. ResponseWrap<Boolean> updateBrand(@RequestBody BrandVo brandVo) {
  77. Boolean flag = brandFace.updateBrand(brandVo);
  78. return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);
  79. }
  80. /**
  81. * @Description 删除品牌
  82. * @param brandVo 查询对象
  83. * @return
  84. */
  85. @DeleteMapping
  86. @ApiOperation(value = "删除品牌",notes = "删除品牌")
  87. @ApiImplicitParam(name = "brandVo",value = "品牌查询对象",required = true,dataType = "BrandVo")
  88. ResponseWrap<Boolean> deleteBrand(@RequestBody BrandVo brandVo ) {
  89. //获得所有选中品牌IDS
  90. String[] checkedIds = brandVo.getCheckedIds();
  91. Boolean flag = brandFace.deleteBrand(checkedIds);
  92. return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);
  93. }
  94. /**
  95. * @Description 查找品牌
  96. * @param brandId 登录名
  97. * @return
  98. */
  99. @GetMapping("{brandId}")
  100. @ApiOperation(value = "查找品牌",notes = "查找品牌")
  101. @ApiImplicitParam(paramType = "path",name = "brandId",value = "品牌Id",dataType = "Long")
  102. ResponseWrap<BrandVo> findBrandByBrandId(@PathVariable("brandId") Long brandId) {
  103. BrandVo brandVo = brandFace.findBrandByBrandId(brandId);
  104. return ResponseWrapBuild.build(BrandEnum.SUCCEED,brandVo);
  105. }
  106. @PostMapping("update-brand-enableFlag")
  107. @ApiOperation(value = "修改品牌状态",notes = "修改品牌状态")
  108. @ApiImplicitParam(name = "brandVo",value = "品牌查询对象",required = true,dataType = "BrandVo")
  109. ResponseWrap<Boolean> updateBrandEnableFlag(@RequestBody BrandVo brandVo) {
  110. Boolean flag = brandFace.updateBrand(brandVo);
  111. return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);
  112. }
  113. }

第四章 商家平台-门店管理

上一章节中,我们开发了品牌管理,为每个入住的商家提供品牌维护的功能,从下面的图中我们知道,品牌下面有对应的门店,下面我们来开发商家平台-门店管理

image.png

1、功能区拆解

image.png

image.png

红色区域:此处为查询条件功能区,筛选列表信息

功能 说明
门店名称 输入门店名称,键盘按enter触发搜索列表
状态 下拉框,显示【启用、禁用】,change触发搜索列表

蓝色区域:品牌信息维护功能

功能 说明
添加 新增门店信息,包含品牌,负责人、状态设置等功能
修改 修改门店信息,包含品牌,负责人、状态设置等功能
删除 删除门店信息==【真实删除】==,删除时,会有再次确认提示
禁用、启用 禁用、启用门店

2、数据库结构

  1. CREATE TABLE `tab_store` (
  2. `id` bigint(18) NOT NULL COMMENT '门店主键id',
  3. `brand_id` bigint(18) DEFAULT NULL COMMENT '品牌',
  4. `store_name` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '门店名称',
  5. `province` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(省)',
  6. `city` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(市)',
  7. `area` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(区)',
  8. `address` varchar(500) COLLATE utf8_bin NOT NULL COMMENT '详细地址',
  9. `manager_id` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '管理员id',
  10. `enable_flag` varchar(18) CHARACTER SET utf8 DEFAULT NULL COMMENT '是否有效',
  11. `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  12. `updated_time` datetime DEFAULT NULL COMMENT '创建时间',
  13. `sharding_id` bigint(18) DEFAULT NULL COMMENT '分库id',
  14. `enterprise_id` bigint(18) NOT NULL COMMENT '商户号',
  15. `longitude` double(9,6) DEFAULT NULL COMMENT '经度',
  16. `dimensionality` double(9,6) DEFAULT NULL COMMENT '维度',
  17. PRIMARY KEY (`id`)
  18. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='门店信息账号';

自动填充

下列字段,无需手动填充,系统会自动填充下列字段

字段 注释 填充方式
id 主键 雪花算法
enterprise_id 商户号 mybatis-plus-多租户拦截
created_time 创建时间 mybatis-plus-自动填充组件
updated_time 修改时间 mybatis-plus-自动填充组件
sharding_id 分库id mybatis-plus-自动填充组件

3、功能开发

在开始业务开发之前,我们首先看一下门店的UML图

image.png

StoreController:对StoreFace接口进行dubbo的RPC调用,为dubbo服务的消费者

AffixFace【公用功能】:附件上传dubbo接口,我们在创建门店时,需要上传品牌图片到图片中心

StoreFace:门店dubbo接口定义对AffixFace进行dubbo的RPC调用

StoreFaceImpl:门店dubbo接口定义实现,这里做VO和POJO的转换

IStoreService:门店的业务接口定义,为StoreFaceImpl提供核心业务逻辑的定义

StoreServiceImpl:门店的业务接口定义实现

3.1、StoreFace接口

StoreFace:门店dubbo接口定义:分页列表、创建门店、修改门店、删除门店、按ID查找门店、门店下拉框

  1. package com.itheima.restkeeper;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.exception.ProjectException;
  4. import com.itheima.restkeeper.req.StoreVo;
  5. import java.util.List;
  6. /**
  7. * @ClassName StoreFace.java
  8. * @Description 门店dubbo服务
  9. */
  10. public interface StoreFace {
  11. /**
  12. * @Description 品牌列表
  13. * @param storeVo 查询条件
  14. * @param pageNum 页码
  15. * @param pageSize 每页条数
  16. * @return Page<BrandVo>
  17. */
  18. Page<StoreVo> findStoreVoPage(StoreVo storeVo,
  19. int pageNum,
  20. int pageSize)throws ProjectException;
  21. /**
  22. * @Description 创建门店
  23. * @param storeVo 对象信息
  24. * @return StoreVo
  25. */
  26. StoreVo createStore(StoreVo storeVo)throws ProjectException;
  27. /**
  28. * @Description 修改门店
  29. * @param storeVo 对象信息
  30. * @return Boolean
  31. */
  32. Boolean updateStore(StoreVo storeVo)throws ProjectException;
  33. /**
  34. * @Description 删除门店
  35. * @param checkedIds 选择对象信息Id
  36. * @return Boolean
  37. */
  38. Boolean deleteStore(String[] checkedIds)throws ProjectException;
  39. /**
  40. * @Description 查找门店
  41. * @param storeId 选择对象信息Id
  42. * @return StoreVo
  43. */
  44. StoreVo findStoreByStoreId(Long storeId)throws ProjectException;
  45. /***
  46. * @description 查询门店下拉框
  47. * @return: List<StoreVo>
  48. */
  49. List<StoreVo> findStoreVoList()throws ProjectException;
  50. }

3.2、StoreFaceImpl接口实现

StoreFaceImpl:门店dubbo接口定义实现,这里做VO和POJO的转换

  1. package com.itheima.restkeeper.face;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.StoreFace;
  4. import com.itheima.restkeeper.enums.StoreEnum;
  5. import com.itheima.restkeeper.exception.ProjectException;
  6. import com.itheima.restkeeper.pojo.Store;
  7. import com.itheima.restkeeper.req.StoreVo;
  8. import com.itheima.restkeeper.service.IStoreService;
  9. import com.itheima.restkeeper.utils.BeanConv;
  10. import com.itheima.restkeeper.utils.EmptyUtil;
  11. import com.itheima.restkeeper.utils.ExceptionsUtil;
  12. import com.itheima.restkeeper.utils.ResponseWrapBuild;
  13. import lombok.extern.slf4j.Slf4j;
  14. import org.apache.dubbo.config.annotation.DubboService;
  15. import org.apache.dubbo.config.annotation.Method;
  16. import org.springframework.beans.factory.annotation.Autowired;
  17. import java.util.List;
  18. /**
  19. * @ClassName StoreFaceImpl.java
  20. * @Description 门店dubbbo服务实现
  21. */
  22. @Slf4j
  23. @DubboService(version = "${dubbo.application.version}",timeout = 5000,
  24. methods ={
  25. @Method(name = "findStoreVoPage",retries = 2),
  26. @Method(name = "createStore",retries = 0),
  27. @Method(name = "updateStore",retries = 0),
  28. @Method(name = "deleteStore",retries = 0)
  29. })
  30. public class StoreFaceImpl implements StoreFace {
  31. @Autowired
  32. IStoreService storeService;
  33. @Override
  34. public Page<StoreVo> findStoreVoPage(StoreVo storeVo,
  35. int pageNum,
  36. int pageSize) throws ProjectException{
  37. try {
  38. //构建分页对象
  39. Page<Store> page = storeService.findStoreVoPage(storeVo, pageNum, pageSize);
  40. Page<StoreVo> pageVo = new Page<>();
  41. BeanConv.toBean(page,pageVo);
  42. //结果集转换
  43. List<Store> storeList = page.getRecords();
  44. List<StoreVo> storeVoList = BeanConv.toBeanList(storeList,StoreVo.class);
  45. pageVo.setRecords(storeVoList);
  46. return pageVo;
  47. } catch (Exception e) {
  48. log.error("查询门店列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  49. throw new ProjectException(StoreEnum.PAGE_FAIL);
  50. }
  51. }
  52. @Override
  53. public StoreVo createStore(StoreVo storeVo) throws ProjectException{
  54. try {
  55. //创建门店
  56. return BeanConv.toBean( storeService.createStore(storeVo), StoreVo.class);
  57. } catch (Exception e) {
  58. log.error("保存门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  59. throw new ProjectException(StoreEnum.CREATE_FAIL);
  60. }
  61. }
  62. @Override
  63. public Boolean updateStore(StoreVo storeVo)throws ProjectException {
  64. try {
  65. //修改门店
  66. return storeService.updateStore(storeVo);
  67. } catch (Exception e) {
  68. log.error("保存门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  69. throw new ProjectException(StoreEnum.UPDATE_FAIL);
  70. }
  71. }
  72. @Override
  73. public Boolean deleteStore(String[] checkedIds) throws ProjectException{
  74. try {
  75. //删除门店
  76. return storeService.deleteStore(checkedIds);
  77. } catch (Exception e) {
  78. log.error("删除门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  79. throw new ProjectException(StoreEnum.DELETE_FAIL);
  80. }
  81. }
  82. @Override
  83. public StoreVo findStoreByStoreId(Long storeId)throws ProjectException {
  84. try {
  85. //查询门店
  86. Store store = storeService.getById(storeId);
  87. if (!EmptyUtil.isNullOrEmpty(store)){
  88. return BeanConv.toBean(store,StoreVo.class);
  89. }
  90. return null;
  91. } catch (Exception e) {
  92. log.error("查找门店所有门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  93. throw new ProjectException(StoreEnum.SELECT_STORE_FAIL);
  94. }
  95. }
  96. @Override
  97. public List<StoreVo> findStoreVoList()throws ProjectException {
  98. try {
  99. //查询门店列表,用在哪里?需要注意状态吗?
  100. return BeanConv.toBeanList(storeService.findStoreVoList(),StoreVo.class);
  101. } catch (Exception e) {
  102. log.error("查找门店所有门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  103. throw new ProjectException(StoreEnum.SELECT_STORE_LIST_FAIL);
  104. }
  105. }
  106. }

3.3、IStoreService业务接口

IStoreService:门店的业务接口定义,为StoreFaceImpl提供核心业务逻辑的定义

  1. package com.itheima.restkeeper.service;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.pojo.Store;
  4. import com.itheima.restkeeper.pojo.Store;
  5. import com.baomidou.mybatisplus.extension.service.IService;
  6. import com.itheima.restkeeper.req.StoreVo;
  7. import java.util.List;
  8. /**
  9. * @Description:门店信息账号 服务类
  10. */
  11. public interface IStoreService extends IService<Store> {
  12. /**
  13. * @Description 品牌列表
  14. * @param storeVo 查询条件
  15. * @param pageNum 页码
  16. * @param pageSize 每页条数
  17. * @return Page<BrandVo>
  18. */
  19. Page<Store> findStoreVoPage(StoreVo storeVo, int pageNum, int pageSize);
  20. /**
  21. * @Description 创建门店
  22. * @param storeVo 对象信息
  23. * @return Store
  24. */
  25. Store createStore(StoreVo storeVo);
  26. /**
  27. * @Description 修改门店
  28. * @param storeVo 对象信息
  29. * @return Boolean
  30. */
  31. Boolean updateStore(StoreVo storeVo);
  32. /**
  33. * @Description 删除门店
  34. * @param checkedIds 选择的门店ID
  35. * @return Boolean
  36. */
  37. Boolean deleteStore(String[] checkedIds);
  38. /***
  39. * @description 查询门店下拉框
  40. * @return: List<Store>
  41. */
  42. List<Store> findStoreVoList();
  43. }

3.4、StoreServiceImpl接口实现

StoreServiceImpl:门店的业务接口定义实现

  1. package com.itheima.restkeeper.service.impl;
  2. import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
  3. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  4. import com.itheima.restkeeper.basic.BasicPojo;
  5. import com.itheima.restkeeper.constant.SuperConstant;
  6. import com.itheima.restkeeper.pojo.Store;
  7. import com.itheima.restkeeper.mapper.StoreMapper;
  8. import com.itheima.restkeeper.req.StoreVo;
  9. import com.itheima.restkeeper.service.IStoreService;
  10. import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  11. import com.itheima.restkeeper.utils.BeanConv;
  12. import com.itheima.restkeeper.utils.EmptyUtil;
  13. import org.springframework.stereotype.Service;
  14. import java.util.ArrayList;
  15. import java.util.Arrays;
  16. import java.util.List;
  17. /**
  18. * @Description:门店信息账号 服务实现类
  19. */
  20. @Service
  21. public class StoreServiceImpl extends ServiceImpl<StoreMapper, Store> implements IStoreService {
  22. @Override
  23. public Page<Store> findStoreVoPage(StoreVo storeVo, int pageNum, int pageSize) {
  24. //分页条件构建
  25. Page<Store> page = new Page<>(pageNum,pageSize);
  26. QueryWrapper<Store> queryWrapper = new QueryWrapper<>();
  27. //按门店名称查询
  28. if (!EmptyUtil.isNullOrEmpty(storeVo.getStoreName())) {
  29. queryWrapper.lambda().likeRight(Store::getStoreName,storeVo.getStoreName());
  30. }
  31. //按门店状态查询
  32. if (!EmptyUtil.isNullOrEmpty(storeVo.getEnableFlag())) {
  33. queryWrapper.lambda().eq(Store::getEnableFlag,storeVo.getEnableFlag());
  34. }
  35. queryWrapper.lambda().orderByDesc(Store::getCreatedTime);
  36. return page(page, queryWrapper);
  37. }
  38. @Override
  39. public Store createStore(StoreVo storeVo) {
  40. //转换storeVo为Store
  41. Store store = BeanConv.toBean(storeVo, Store.class);
  42. boolean flag = save(store);
  43. if (flag){
  44. return store;
  45. }
  46. return null;
  47. }
  48. @Override
  49. public Boolean updateStore(StoreVo storeVo) {
  50. //转换storeVo为Store
  51. Store store = BeanConv.toBean(storeVo, Store.class);
  52. return updateById(store);
  53. }
  54. @Override
  55. public Boolean deleteStore(String[] checkedIds) {
  56. //转换数组为集合
  57. List<String> ids = Arrays.asList(checkedIds);
  58. List<Long> idsLong = new ArrayList<>();
  59. ids.forEach(n->{
  60. idsLong.add(Long.valueOf(n));
  61. });
  62. //批量删除
  63. return removeByIds(idsLong);
  64. }
  65. @Override
  66. public List<Store> findStoreVoList() {
  67. //构建查询条件
  68. QueryWrapper<Store> queryWrapper = new QueryWrapper<>();
  69. //查询有效状态
  70. queryWrapper.lambda().eq(BasicPojo::getEnableFlag,SuperConstant.YES);
  71. return list(queryWrapper);
  72. }
  73. }

3.5、StoreController类

此类为服务的【消费者】,对AffixFace和StoreFace接口进行dubbo的RPC调用

  1. package com.itheima.restkeeper.web;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.AffixFace;
  4. import com.itheima.restkeeper.StoreFace;
  5. import com.itheima.restkeeper.basic.ResponseWrap;
  6. import com.itheima.restkeeper.enums.StoreEnum;
  7. import com.itheima.restkeeper.exception.ProjectException;
  8. import com.itheima.restkeeper.req.StoreVo;
  9. import com.itheima.restkeeper.utils.EmptyUtil;
  10. import com.itheima.restkeeper.utils.ExceptionsUtil;
  11. import com.itheima.restkeeper.utils.ResponseWrapBuild;
  12. import io.swagger.annotations.Api;
  13. import io.swagger.annotations.ApiImplicitParam;
  14. import io.swagger.annotations.ApiImplicitParams;
  15. import io.swagger.annotations.ApiOperation;
  16. import lombok.extern.slf4j.Slf4j;
  17. import org.apache.dubbo.config.annotation.DubboReference;
  18. import org.springframework.web.bind.annotation.*;
  19. import java.util.List;
  20. /**
  21. * @ClassName StoreController.java
  22. * @Description 门店Controller
  23. */
  24. @RestController
  25. @RequestMapping("store")
  26. @Slf4j
  27. @Api(tags = "门店controller")
  28. public class StoreController {
  29. @DubboReference(version = "${dubbo.application.version}",check = false)
  30. StoreFace storeFace;
  31. /**
  32. * @Description 门店列表
  33. * @param storeVo 查询条件
  34. * @return
  35. */
  36. @PostMapping("page/{pageNum}/{pageSize}")
  37. @ApiOperation(value = "查询门店分页",notes = "查询门店分页")
  38. @ApiImplicitParams({
  39. @ApiImplicitParam(name = "storeVo",value = "门店查询对象",required = true,dataType = "StoreVo"),
  40. @ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",dataType = "Integer"),
  41. @ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",dataType = "Integer")
  42. })
  43. public ResponseWrap<Page<StoreVo>> findStoreVoPage(
  44. @RequestBody StoreVo storeVo,
  45. @PathVariable("pageNum") int pageNum,
  46. @PathVariable("pageSize") int pageSize) {
  47. Page<StoreVo> storeVoPage = storeFace.findStoreVoPage(storeVo, pageNum, pageSize);
  48. return ResponseWrapBuild.build(StoreEnum.SUCCEED,storeVoPage);
  49. }
  50. /**
  51. * @Description 添加门店
  52. * @param storeVo 对象信息
  53. * @return
  54. */
  55. @PostMapping
  56. @ApiOperation(value = "添加门店",notes = "添加门店")
  57. @ApiImplicitParam(name = "storeVo",value = "门店对象",required = true,dataType = "StoreVo")
  58. ResponseWrap<StoreVo> createStore(@RequestBody StoreVo storeVo) {
  59. StoreVo storeVoResult = storeFace.createStore(storeVo);
  60. return ResponseWrapBuild.build(StoreEnum.SUCCEED,storeVoResult);
  61. }
  62. /**
  63. * @Description 修改门店
  64. * @param storeVo 对象信息
  65. * @return
  66. */
  67. @PatchMapping
  68. @ApiOperation(value = "修改门店",notes = "修改门店")
  69. @ApiImplicitParam(name = "storeVo",value = "门店对象",required = true,dataType = "StoreVo")
  70. ResponseWrap<Boolean> updateStore(@RequestBody StoreVo storeVo) {
  71. Boolean flag = storeFace.updateStore(storeVo);
  72. return ResponseWrapBuild.build(StoreEnum.SUCCEED,flag);
  73. }
  74. /**
  75. * @Description 删除门店
  76. * @param storeVo 查询对象
  77. * @return
  78. */
  79. @DeleteMapping
  80. @ApiOperation(value = "删除门店",notes = "删除门店")
  81. @ApiImplicitParam(name = "storeVo",value = "门店查询对象",required = true,dataType = "StoreVo")
  82. ResponseWrap<Boolean> deleteStore(@RequestBody StoreVo storeVo ) {
  83. //获得所有选择的门店IDS
  84. String[] checkedIds = storeVo.getCheckedIds();
  85. Boolean flag = storeFace.deleteStore(checkedIds);
  86. return ResponseWrapBuild.build(StoreEnum.SUCCEED,flag);
  87. }
  88. /**
  89. * @Description 查找门店
  90. * @param storeId 门店id
  91. * @return
  92. */
  93. @GetMapping("{storeId}")
  94. @ApiOperation(value = "查找门店",notes = "查找门店")
  95. @ApiImplicitParam(paramType = "path",name = "storeId",value = "门店Id",dataType = "Long")
  96. ResponseWrap<StoreVo> findStoreByStoreId(@PathVariable("storeId") Long storeId) {
  97. StoreVo storeVo = storeFace.findStoreByStoreId(storeId);
  98. return ResponseWrapBuild.build(StoreEnum.SUCCEED,storeVo);
  99. }
  100. /**
  101. * @Description 查找门店
  102. * @return
  103. */
  104. @GetMapping("list")
  105. @ApiOperation(value = "查找门店列表",notes = "查找门店列表")
  106. ResponseWrap<List<StoreVo>> findStoreVoList() {
  107. List<StoreVo> list = storeFace.findStoreVoList();
  108. return ResponseWrapBuild.build(StoreEnum.SUCCEED,list);
  109. }
  110. /**
  111. * @Description 修改门店状态
  112. * @return
  113. */
  114. @PostMapping("update-store-enableFlag")
  115. @ApiOperation(value = "修改门店状态",notes = "修改门店状态")
  116. ResponseWrap<Boolean> updateStoreEnableFlag(@RequestBody StoreVo storeVo) {
  117. Boolean flag = storeFace.updateStore(storeVo);
  118. return ResponseWrapBuild.build(StoreEnum.SUCCEED,flag);
  119. }
  120. }

第五章 商家平台-用户管理

  1. 在梳理【商家平台-用户管理】前,我们先看下商家平台管理员账号的开通流程:商家申请入驻平台后,【商家运营平台】为商户开通管理员账号,开通后,商户管理可以登录系统,并且管理门店员账号功能

image.png

1、功能区拆解

image.png

image.png

红色区域:此处为查询条件功能区,筛选列表信息

功能 说明
登录名 输入登录名,键盘按enter触发搜索列表
手机 输入手机,键盘按enter触发搜索列表
状态 下拉框,显示【启用、禁用】,change触发搜索列表

蓝色区域:员工账号维护功能

功能 说明
添加 新增用户信息,包含头像上传,角色、打折、优惠比例设置等功能
修改 修改用户信息,包含头像重新上传,角色、打折、优惠比例设置择等功能
删除 删除用户信息==【真实删除】==,删除时,会有再次确认提示
禁用、启用 禁用启用账户
重置密码 对用户账号密码重置

2、数据库结构

员工表结构如下

  1. CREATE TABLE `tab_user` (
  2. `id` bigint(18) NOT NULL COMMENT '主键',
  3. `store_id` bigint(32) DEFAULT NULL COMMENT '门店Id',
  4. `enterprise_id` bigint(18) NOT NULL COMMENT '商户号',
  5. `username` varchar(36) DEFAULT NULL COMMENT '登录名称',
  6. `real_name` varchar(36) DEFAULT NULL COMMENT '真实姓名',
  7. `password` varchar(150) DEFAULT NULL COMMENT '密码',
  8. `sex` varchar(11) DEFAULT NULL COMMENT '性别',
  9. `mobil` varchar(36) DEFAULT NULL COMMENT '电话',
  10. `email` varchar(36) DEFAULT NULL COMMENT '邮箱',
  11. `discount_limit` decimal(10,2) DEFAULT NULL COMMENT '折扣上线',
  12. `reduce_limit` decimal(10,2) DEFAULT NULL COMMENT '减免金额上线',
  13. `duties` varchar(36) DEFAULT NULL COMMENT '职务',
  14. `sort_no` int(11) DEFAULT NULL COMMENT '排序',
  15. `enable_flag` varchar(18) DEFAULT NULL COMMENT '是否有效',
  16. `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  17. `updated_time` datetime DEFAULT NULL COMMENT '创建时间',
  18. `sharding_id` bigint(18) DEFAULT NULL COMMENT '分库id',
  19. PRIMARY KEY (`id`)
  20. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='用户表';

自动填充

下列字段,无需手动填充,系统会自动填充下列字段

字段 注释 填充方式
id 主键 雪花算法
enterprise_id 商户号 mybatis-plus-多租户拦截
created_time 创建时间 mybatis-plus-自动填充组件
updated_time 修改时间 mybatis-plus-自动填充组件
sharding_id 分库id mybatis-plus-自动填充组件

3、功能开发

商家平台的用户管理,其核心业务实现已在model-security-service模块中实现,这里我们只需要使用即可,在开始业务开发之前,我们首先看一下用户管理的UML图

image.png

UserController【model-shop-web】:对UserFace接口进行dubbo的RPC调用,为dubbo服务的消费者

AffixFace【公用功能】:附件上传dubbo接口,我们在创建用户时,需要上传用户头像到图片中心

UserFace【**model-shop-interface**】:用户dubbo接口定义和AffixFace接口进行dubbo的RPC调用,为dubbo服务的消费者

UserFaceImpl【**model-shop-user**】:用户dubbo接口定义实现,这里做VO和POJO的转换

IUserService【公用功能】:用户的业务接口定义,为UserFaceImpl提供核心业务逻辑的定义

UserServiceImpl【公用功能】:用户的业务接口定义实现

3.1、UserFace接口

UserFace:用户dubbo接口定义

  1. package com.itheima.restkeeper;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.exception.ProjectException;
  4. import com.itheima.restkeeper.req.UserVo;
  5. import java.util.List;
  6. /**
  7. * @Description:用户dubbo接口
  8. */
  9. public interface UserFace {
  10. /**
  11. * @param userVo 查询条件
  12. * @param pageNum 当前页
  13. * @param pageSize 每页条数
  14. * @return Page<UserVo>
  15. * @Description 用户列表
  16. */
  17. Page<UserVo> findUserVoPage(UserVo userVo,
  18. int pageNum,
  19. int pageSize)throws ProjectException;
  20. /**
  21. * @param userVo 对象信息
  22. * @return UserVo
  23. * @Description 创建用户
  24. */
  25. UserVo createUser(UserVo userVo)throws ProjectException;
  26. /**
  27. * @param userVo 对象信息
  28. * @return Boolean
  29. * @Description 修改用户
  30. */
  31. Boolean updateUser(UserVo userVo)throws ProjectException;
  32. /**
  33. * @param checkedIds 选择对象信息Id
  34. * @return Boolean
  35. * @Description 删除用户
  36. */
  37. Boolean deleteUser(String[] checkedIds)throws ProjectException;
  38. /**
  39. * @param userId 选择对象信息Id
  40. * @return
  41. * @Description 查找用户
  42. */
  43. UserVo findUserByUserId(Long userId)throws ProjectException;
  44. /**
  45. * @return List<UserVo>
  46. * @Description 查找用户list
  47. */
  48. List<UserVo> findUserVoList()throws ProjectException;
  49. }

3.2、UserFaceImpl接口实现

UserFaceImpl:用户dubbo接口定义实现,这里做VO和POJO的转换

  1. package com.itheima.restkeeper.face;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.AffixFace;
  4. import com.itheima.restkeeper.UserFace;
  5. import com.itheima.restkeeper.enums.UserEnum;
  6. import com.itheima.restkeeper.exception.ProjectException;
  7. import com.itheima.restkeeper.pojo.Role;
  8. import com.itheima.restkeeper.pojo.User;
  9. import com.itheima.restkeeper.req.AffixVo;
  10. import com.itheima.restkeeper.req.UserVo;
  11. import com.itheima.restkeeper.service.IUserAdapterService;
  12. import com.itheima.restkeeper.service.IUserService;
  13. import com.itheima.restkeeper.utils.BeanConv;
  14. import com.itheima.restkeeper.utils.EmptyUtil;
  15. import com.itheima.restkeeper.utils.ExceptionsUtil;
  16. import com.itheima.restkeeper.utils.ResponseWrapBuild;
  17. import lombok.extern.slf4j.Slf4j;
  18. import org.apache.dubbo.config.annotation.DubboReference;
  19. import org.apache.dubbo.config.annotation.DubboService;
  20. import org.apache.dubbo.config.annotation.Method;
  21. import org.springframework.beans.factory.annotation.Autowired;
  22. import java.util.ArrayList;
  23. import java.util.List;
  24. import java.util.stream.Collectors;
  25. /**
  26. * @ClassName UserFaceImpl.java
  27. * @Description 用户dubbo服务实现
  28. */
  29. @Slf4j
  30. @DubboService(version = "${dubbo.application.version}",timeout = 5000,
  31. methods ={
  32. @Method(name = "findUserVoPage",retries = 2),
  33. @Method(name = "createUser",retries = 0),
  34. @Method(name = "updateUser",retries = 0),
  35. @Method(name = "deleteUser",retries = 0)
  36. })
  37. public class UserFaceImpl implements UserFace {
  38. @DubboReference(version = "${dubbo.application.version}",check = false)
  39. AffixFace affixFace;
  40. @Autowired
  41. IUserService userService;
  42. @Autowired
  43. IUserAdapterService userAdapterService;
  44. @Override
  45. public Page<UserVo> findUserVoPage(UserVo userVo,
  46. int pageNum,
  47. int pageSize)throws ProjectException {
  48. try {
  49. //查询用户分页
  50. Page<User> page = userService.findUserVoPage(userVo, pageNum, pageSize);
  51. Page<UserVo> pageVo = new Page<>();
  52. BeanConv.toBean(page,pageVo);
  53. //结果集转换
  54. List<User> userList = page.getRecords();
  55. List<UserVo> userVoList = BeanConv.toBeanList(userList,UserVo.class);
  56. if (!EmptyUtil.isNullOrEmpty(userList)){
  57. userVoList.forEach(n->{
  58. //处理附件
  59. List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(n.getId());
  60. if (!EmptyUtil.isNullOrEmpty(affixVoList)){
  61. n.setAffixVo(affixVoList.get(0));
  62. }
  63. //处理角色
  64. List<Role> roles = userAdapterService.findRoleByUserId(n.getId());
  65. List<String> roleIdList = new ArrayList<>();
  66. for (Role role : roles) {
  67. roleIdList.add(String.valueOf(role.getId()));
  68. }
  69. String[] roleIds = new String[roleIdList.size()];
  70. roleIdList.toArray(roleIds);
  71. n.setHasRoleIds(roleIds);
  72. });
  73. }
  74. pageVo.setRecords(userVoList);
  75. //返回结果
  76. return pageVo;
  77. } catch (Exception e) {
  78. log.error("查询用户列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  79. throw new ProjectException(UserEnum.PAGE_FAIL);
  80. }
  81. }
  82. @Override
  83. public UserVo createUser(UserVo userVo)throws ProjectException {
  84. try {
  85. //保存用户
  86. UserVo userVoResult = BeanConv.toBean(userService.createUser(userVo), UserVo.class);
  87. //绑定附件
  88. if (!EmptyUtil.isNullOrEmpty(userVoResult)){
  89. affixFace.bindBusinessId(AffixVo.builder()
  90. .businessId(userVoResult.getId())
  91. .id(userVo.getAffixVo().getId())
  92. .build());
  93. }
  94. userVoResult.setAffixVo(AffixVo.builder()
  95. .pathUrl(userVo.getAffixVo().getPathUrl())
  96. .businessId(userVoResult.getId())
  97. .id(userVo.getAffixVo().getId()).build());
  98. //处理角色
  99. userVoResult.setHasRoleIds(userVo.getHasRoleIds());
  100. return userVoResult;
  101. } catch (Exception e) {
  102. log.error("保存用户异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  103. throw new ProjectException(UserEnum.CREATE_FAIL);
  104. }
  105. }
  106. @Override
  107. public Boolean updateUser(UserVo userVo) throws ProjectException{
  108. try {
  109. //修改用户
  110. Boolean flag = userService.updateUser(userVo);
  111. if (flag){
  112. List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(userVo.getId());
  113. List<Long> affixIds = affixVoList.stream().map(AffixVo::getId).collect(Collectors.toList());
  114. if (!affixIds.contains(userVo.getAffixVo().getId())){
  115. //删除图片
  116. flag = affixFace.deleteAffixVoByBusinessId(userVo.getId());
  117. //绑定新图片
  118. affixFace.bindBusinessId(AffixVo.builder()
  119. .businessId(userVo.getId())
  120. .id(userVo.getAffixVo().getId())
  121. .build());
  122. }
  123. }
  124. return flag;
  125. } catch (Exception e) {
  126. log.error("保存用户异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  127. throw new ProjectException(UserEnum.UPDATE_FAIL);
  128. }
  129. }
  130. @Override
  131. public Boolean deleteUser(String[] checkedIds)throws ProjectException {
  132. try {
  133. //删除用户
  134. boolean flag = userService.deleteUser(checkedIds);
  135. if (flag){
  136. //删除图片
  137. for (String checkedId : checkedIds) {
  138. affixFace.deleteAffixVoByBusinessId(Long.valueOf(checkedId));
  139. }
  140. }
  141. return flag;
  142. } catch (Exception e) {
  143. log.error("删除用户异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  144. throw new ProjectException(UserEnum.DELETE_FAIL);
  145. }
  146. }
  147. @Override
  148. public UserVo findUserByUserId(Long userId)throws ProjectException {
  149. try {
  150. //查找用户
  151. User user = userService.getById(userId);
  152. UserVo userVo = BeanConv.toBean(user, UserVo.class);
  153. if (!EmptyUtil.isNullOrEmpty(user)){
  154. //处理附件
  155. List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(user.getId());
  156. if (!EmptyUtil.isNullOrEmpty(affixVoList)){
  157. userVo.setAffixVo(affixVoList.get(0));
  158. }
  159. return userVo;
  160. }
  161. return null;
  162. } catch (Exception e) {
  163. log.error("查找用户所有角色异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  164. throw new ProjectException(UserEnum.SELECT_USER_FAIL);
  165. }
  166. }
  167. @Override
  168. public List<UserVo> findUserVoList()throws ProjectException {
  169. try {
  170. //查找用户下拉框
  171. return BeanConv.toBeanList(userService.findUserVoList(),UserVo.class);
  172. } catch (Exception e) {
  173. log.error("查找用户list异常:{}", ExceptionsUtil.getStackTraceAsString(e));
  174. throw new ProjectException(UserEnum.SELECT_USER_LIST_FAIL);
  175. }
  176. }
  177. }

3.3、UserController类

UserController:对AffixFace和UserFace接口进行dubbo的RPC调用,为dubbo服务的消费者

  1. package com.itheima.restkeeper.web;
  2. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  3. import com.itheima.restkeeper.AffixFace;
  4. import com.itheima.restkeeper.UserFace;
  5. import com.itheima.restkeeper.basic.ResponseWrap;
  6. import com.itheima.restkeeper.enums.UserEnum;
  7. import com.itheima.restkeeper.exception.ProjectException;
  8. import com.itheima.restkeeper.req.AffixVo;
  9. import com.itheima.restkeeper.req.UserVo;
  10. import com.itheima.restkeeper.utils.EmptyUtil;
  11. import com.itheima.restkeeper.utils.ExceptionsUtil;
  12. import com.itheima.restkeeper.utils.ResponseWrapBuild;
  13. import io.swagger.annotations.Api;
  14. import io.swagger.annotations.ApiImplicitParam;
  15. import io.swagger.annotations.ApiImplicitParams;
  16. import io.swagger.annotations.ApiOperation;
  17. import lombok.extern.slf4j.Slf4j;
  18. import org.apache.dubbo.config.annotation.DubboReference;
  19. import org.springframework.beans.factory.annotation.Autowired;
  20. import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  21. import org.springframework.web.bind.annotation.*;
  22. import java.util.List;
  23. import java.util.stream.Collectors;
  24. /**
  25. * @ClassName UserController.java
  26. * @Description 用户Controller
  27. */
  28. @RestController
  29. @RequestMapping("user")
  30. @Slf4j
  31. @Api(tags = "用户controller")
  32. public class UserController {
  33. @DubboReference(version = "${dubbo.application.version}",check = false)
  34. UserFace userFace;
  35. @Autowired
  36. BCryptPasswordEncoder bCryptPasswordEncoder;
  37. /**
  38. * @Description 用户列表
  39. * @param userVo 查询条件
  40. * @return
  41. */
  42. @PostMapping("page/{pageNum}/{pageSize}")
  43. @ApiOperation(value = "查询用户分页",notes = "查询用户分页")
  44. @ApiImplicitParams({
  45. @ApiImplicitParam(name = "userVo",value = "用户查询对象",required = true,dataType = "UserVo"),
  46. @ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",dataType = "Integer"),
  47. @ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",dataType = "Integer")
  48. })
  49. public ResponseWrap<Page<UserVo>> findUserVoPage(
  50. @RequestBody UserVo userVo,
  51. @PathVariable("pageNum") int pageNum,
  52. @PathVariable("pageSize") int pageSize) throws ProjectException {
  53. Page<UserVo> userVoPage = userFace.findUserVoPage(userVo, pageNum, pageSize);
  54. return ResponseWrapBuild.build(UserEnum.SUCCEED,userVoPage);
  55. }
  56. /**
  57. * @Description 注册用户
  58. * @param userVo 对象信息
  59. * @return
  60. */
  61. @PostMapping
  62. @ApiOperation(value = "注册用户",notes = "注册用户")
  63. @ApiImplicitParam(name = "userVo",value = "用户对象",required = true,dataType = "UserVo")
  64. ResponseWrap<UserVo> registerUser(@RequestBody UserVo userVo) throws ProjectException {
  65. String plainPassword = userVo.getPassword();
  66. //必须要加{bcrypt}要不认证不通过
  67. String password = "{bcrypt}"+bCryptPasswordEncoder.encode(plainPassword);
  68. userVo.setPassword(password);
  69. return ResponseWrapBuild.build(UserEnum.SUCCEED,userFace.createUser(userVo));
  70. }
  71. /**
  72. * @Description 修改用户
  73. * @param userVo 对象信息
  74. * @return
  75. */
  76. @PatchMapping
  77. @ApiOperation(value = "修改用户",notes = "修改用户")
  78. @ApiImplicitParam(name = "userVo",value = "用户对象",required = true,dataType = "UserVo")
  79. ResponseWrap<Boolean> updateUser(@RequestBody UserVo userVo) throws ProjectException {
  80. Boolean flag = userFace.updateUser(userVo);
  81. return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);
  82. }
  83. /**
  84. * @Description 删除用户
  85. * @param userVo 查询对象
  86. * @return
  87. */
  88. @DeleteMapping
  89. @ApiOperation(value = "删除用户",notes = "删除用户")
  90. @ApiImplicitParam(name = "userVo",value = "用户查询对象",required = true,dataType = "UserVo")
  91. ResponseWrap<Boolean> deleteUser(@RequestBody UserVo userVo ) throws ProjectException {
  92. //获得所有选中的用户IDS
  93. String[] checkedIds = userVo.getCheckedIds();
  94. Boolean flag = userFace.deleteUser(checkedIds);
  95. return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);
  96. }
  97. /**
  98. * @Description 查找用户
  99. * @param userId 登录名
  100. * @return
  101. */
  102. @GetMapping("select-by-userId/{userId}")
  103. @ApiOperation(value = "查找用户",notes = "查找用户")
  104. @ApiImplicitParam(paramType = "path",name = "userId",value = "用户Id",example = "1",dataType = "Long")
  105. ResponseWrap<UserVo> findUserByUserId(@PathVariable("userId") Long userId) throws ProjectException {
  106. UserVo userVo = userFace.findUserByUserId(userId);
  107. return ResponseWrapBuild.build(UserEnum.SUCCEED,userVo);
  108. }
  109. /**
  110. * @Description 查找用户list
  111. * @return
  112. */
  113. @GetMapping("select-list")
  114. @ApiOperation(value = "查找用户list",notes = "查找用户list")
  115. ResponseWrap<List<UserVo>> findUserVoList() throws ProjectException {
  116. List<UserVo> list = userFace.findUserVoList();
  117. return ResponseWrapBuild.build(UserEnum.SUCCEED,list);
  118. }
  119. @PostMapping("update-user-enableFlag")
  120. @ApiOperation(value = "修改用户状态",notes = "修改用户状态")
  121. @ApiImplicitParam(name = "userVo",value = "用户查询对象",required = true,dataType = "UserVo")
  122. ResponseWrap<Boolean> updateUserEnableFlag(@RequestBody UserVo userVo) throws ProjectException {
  123. Boolean flag = userFace.updateUser(userVo);
  124. return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);
  125. }
  126. @PostMapping("rest-password")
  127. @ApiOperation(value = "重置用户密码",notes = "重置用户密码")
  128. @ApiImplicitParam(name = "userVo",value = "用户对象",required = true,dataType = "UserVo")
  129. ResponseWrap<Boolean> restPssword(@RequestBody UserVo userVo) throws ProjectException {
  130. //必须要加{bcrypt}要不认证不通过
  131. String password = "{bcrypt}"+bCryptPasswordEncoder.encode("88488");
  132. userVo.setPassword(password);
  133. Boolean flag = userFace.updateUser(userVo);
  134. return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);
  135. }
  136. }

Spring validation:

详细参看:https://blog.csdn.net/ilove_story/article/details/109260847

引入相关依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-validation</artifactId>
  4. </dependency>

约束性注解(简单)说明

@AssertFalse 可以为null,如果不为null的话必须为false

@AssertTrue 可以为null,如果不为null的话必须为true

@DecimalMax 设置不能超过最大值

@DecimalMin 设置不能超过最小值

@Digits 设置必须是数字且数字整数的位数和小数的位数必须在指定范围内

@Future 日期必须在当前日期的未来

@Past 日期必须在当前日期的过去

@Max 最大不得超过此最大值

@Min 最大不得小于此最小值

@NotNull 不能为null,可以是空

@Null 必须为null

@Pattern 必须满足指定的正则表达式

@Size 集合、数组、map等的size()值必须在指定范围内

@Email 必须是email格式

@Length 长度必须在指定范围内

@NotBlank 字符串不能为null,字符串trim()后也不能等于“”

@NotEmpty 不能为null,集合、数组、map等size()不能为0;字符串trim()后可以等于“”

@Range 值必须在指定范围内

@URL 必须是一个URL

@Validated的使用时机

@Validated的使用位置较多(可详见源码),但其主流的使用位置却是以下两种:

1、在Controller层中,放在模型参数对象前。
当Controller层中参数是一个对象模型时,只有将@Validated直接放在该模型前,该模型内部的字段才会被
校验(如果有对该模型的字段进行约束的话)。

2、在Controller层中,放在类上。
当一些约束是直接出现在Controller层中的参数前时,只有将@Validated放在类上时,参数前的约束才会生效。

自定义异常

  1. package com.itheima.restkeeper.web;
  2. import com.alibaba.fastjson.JSON;
  3. import com.itheima.restkeeper.basic.ResponseWrap;
  4. import com.itheima.restkeeper.enums.BasicEnum;
  5. import com.itheima.restkeeper.exception.ProjectException;
  6. import com.itheima.restkeeper.utils.ExceptionsUtil;
  7. import com.itheima.restkeeper.utils.ResponseWrapBuild;
  8. import lombok.extern.log4j.Log4j2;
  9. import lombok.extern.slf4j.Slf4j;
  10. import org.apache.dubbo.rpc.RpcException;
  11. import org.springframework.web.bind.MethodArgumentNotValidException;
  12. import org.springframework.web.bind.annotation.ControllerAdvice;
  13. import org.springframework.web.bind.annotation.ExceptionHandler;
  14. import javax.servlet.http.HttpServletResponse;
  15. import javax.validation.ConstraintViolationException;
  16. import java.io.IOException;
  17. import java.util.HashMap;
  18. import java.util.Map;
  19. /**
  20. * @ClassName BaseController.java
  21. * @Description 基础的controller
  22. * ControllerAdvice:对controller层的增强,其他的controller则不需要继承,也会被拦截处理
  23. */
  24. @ControllerAdvice
  25. @Slf4j
  26. public class BaseController {
  27. //表示当请求发生异常时,被ExceptionHandler注释的方法会去处理
  28. @ExceptionHandler
  29. public void ExceptionHandler(Exception ex, HttpServletResponse response) throws IOException {
  30. ResponseWrap<Object> responseWrap = null;
  31. //自定义异常
  32. if (ex instanceof ProjectException){
  33. ProjectException projectException = (ProjectException) ex;
  34. responseWrap = ResponseWrapBuild.build(projectException.getBasicEnumIntface(), null);
  35. //远程调用异常
  36. }else if (ex instanceof RpcException){
  37. responseWrap = ResponseWrapBuild.build(BasicEnum.DUBBO_FAIL, null);
  38. //参数校验异常
  39. }else if (ex instanceof MethodArgumentNotValidException){
  40. MethodArgumentNotValidException manvExceptionex = (MethodArgumentNotValidException) ex;
  41. log.warn("@Validated约束在参数模型前,校验该模型的字段时发生异常:{} ", manvExceptionex.getMessage());
  42. responseWrap = ResponseWrapBuild.build(BasicEnum.VALID_EXCEPTION, manvExceptionex.getMessage());
  43. }else {
  44. //系统异常
  45. responseWrap = ResponseWrapBuild.build(BasicEnum.SYSYTEM_FAIL, null);
  46. log.error("系统异常:{}",ExceptionsUtil.getStackTraceAsString(ex));
  47. }
  48. //编码防止中文问题
  49. response.setContentType("application/json;charset =utf-8");
  50. response.getWriter().write(JSON.toJSONString(responseWrap));
  51. }
  52. }

课堂讨论

1、mybatis的执行原理你还记得吗?说说你知道的mybatis的常用功能和标签

2、说说你对dubbo的理解,我们还需要掌握哪些关键组件及属性

  1. 整体架构:[https://dubbo.apache.org/zh/docsv2.7/dev/design/](https://dubbo.apache.org/zh/docsv2.7/dev/design/)
  2. @DubboService属性: [https://dubbo.apache.org/zh/docsv2.7/user/references/xml/dubbo-service/](https://dubbo.apache.org/zh/docsv2.7/user/references/xml/dubbo-service/)
  3. @DubboReference属性: [https://dubbo.apache.org/zh/docsv2.7/user/references/xml/dubbo-reference/](https://dubbo.apache.org/zh/docsv2.7/user/references/xml/dubbo-reference/)
  4. 协议类型:[https://dubbo.apache.org/zh/docsv2.7/user/references/protocol/](https://dubbo.apache.org/zh/docsv2.7/user/references/protocol/)
  5. 集群容错:[https://dubbo.apache.org/zh/docsv2.7/user/examples/fault-tolerent-strategy/](https://dubbo.apache.org/zh/docsv2.7/user/examples/fault-tolerent-strategy/)
  6. 负载均衡:[https://dubbo.apache.org/zh/docsv2.7/user/examples/loadbalance/](https://dubbo.apache.org/zh/docsv2.7/user/examples/loadbalance/)
  7. 分组聚合:[https://dubbo.apache.org/zh/docsv2.7/user/examples/group-merger/](https://dubbo.apache.org/zh/docsv2.7/user/examples/group-merger/)
  8. 上下文信息:[https://dubbo.apache.org/zh/docsv2.7/user/examples/context/](https://dubbo.apache.org/zh/docsv2.7/user/examples/context/)
  9. 隐式传参:[https://dubbo.apache.org/zh/docsv2.7/user/examples/attachment/](https://dubbo.apache.org/zh/docsv2.7/user/examples/attachment/)

3、springcloud-alibaba-dubbo在餐掌柜中的链路如何调用的?

课后任务

1、手绘:餐掌柜业务调用链路

2、完成当天课堂讨论,同步到git【☆☆☆☆☆】

3、完成2-5道sql练习【☆☆☆☆】

4、运营平台采用怎样的maven分层构建,每个模块有什么作用?

5、图片上传在项目中是如何使用的,图片数据表结构如何?

6、为什么在商家的用户管理中不用写UserService接口?这么做有什么好处?