restkeeper-day-02

  • 第一章 Mybatis-Plus代码生成器
    • 1、CodeGenerator核心类
    • 2、生成器快速入门
      • 2.1、generrator
      • 2.2、templates
      • 2.3、生成器使用
  • 第二章 springcloud-alibaba-dubbo
    • 1、dubbo快速入门
    • 2、dubbo快速集成
      • 2.1、生产者配置
      • 2.2、消费者配置
    • 3、业务模块开发
      • 3.1、业务调用链路
      • 3.2、dubbo生产者
      • 3.3、dubbo消费者
      • 3.4、dubbo接口
      • 3.5、service核心业务
  • 第三章 商家平台-品牌管理
    • 1、功能区拆解
    • 2、数据库结构
    • 3、功能开发
      • 3.1、BrandFace接口
      • 3.2、BrandFaceImpl接口实现
      • 3.3、IBrandService业务接口
      • 3.4、BrandServiceImpl接口实现
      • 3.5、BrandController类
  • 第四章 商家平台-门店管理
    • 1、功能区拆解
    • 2、数据库结构
    • 3、功能开发
      • 3.1、StoreFace接口
      • 3.2、StoreFaceImpl接口实现
      • 3.3、IStoreService业务接口
      • 3.4、StoreServiceImpl接口实现
      • 3.5、StoreController类
  • 第五章 商家平台-用户管理
    • 1、功能区拆解
    • 2、数据库结构
    • 3、功能开发
      • 3.1、UserFace接口
      • 3.2、UserFaceImpl接口实现
      • 3.3、UserController类

第一章 Mybatis-Plus代码生成器

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

1、CodeGenerator核心类

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7J8TPWx7-1636200518327)(image/image-20210508200801348.png)]

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

package com.itheima.restkeeper.generator;import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.itheima.restkeeper.utils.EmptyUtil;import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;/*** @Description:代码生成器*/
public class CodeGenerator {public static void autoGenerator(){//用来获取Mybatis-Plus.properties文件的配置信息final ResourceBundle rb = ResourceBundle.getBundle("mybatis-plus-generrator");// 代码生成器AutoGenerator mpg = new AutoGenerator();// 全局配置GlobalConfig gc = new GlobalConfig();String projectPath =rb.getString("projectPath");gc.setOutputDir(projectPath + "/src/main/java");gc.setAuthor(rb.getString("author"));gc.setOpen(false);gc.setFileOverride(true);//指定时间处理类型gc.setDateType(DateType.ONLY_DATE);gc.setSwagger2(true); //实体属性 Swagger2 注解mpg.setGlobalConfig(gc);//数据源配置DataSourceConfig dsc = new DataSourceConfig();dsc.setUrl(rb.getString("url"));// dsc.setSchemaName("public");dsc.setDriverName("com.mysql.jdbc.Driver");dsc.setUsername(rb.getString("userName"));dsc.setPassword(rb.getString("password"));mpg.setDataSource(dsc);//包配置PackageConfig pc = new PackageConfig();pc.setModuleName(rb.getString("moduleName"));pc.setParent(rb.getString("parent"));pc.setController("web");pc.setService("service");pc.setServiceImpl("service.impl");pc.setEntity("pojo");pc.setMapper("mapper");mpg.setPackageInfo(pc);// 自定义配置InjectionConfig cfg = new InjectionConfig() {@Overridepublic void initMap() {// to do nothing}};// 如果模板引擎是 freemarkerString templatePath = "/templates/mapper.xml.ftl";// 自定义输出配置List<FileOutConfig> focList = new ArrayList<>();cfg.setFileOutConfigList(focList);mpg.setCfg(cfg);// 配置模板TemplateConfig templateConfig = new TemplateConfig();if ("true".equals(rb.getString("entity"))){String entityFtlPath = rb.getString("entity.ftl.path");if (!EmptyUtil.isNullOrEmpty(entityFtlPath)){templateConfig.setEntity(entityFtlPath);}}else {templateConfig.setEntity(null);}if ("true".equals(rb.getString("mapper"))){String mapperFtlPath = rb.getString("mapper.ftl.path");if (!EmptyUtil.isNullOrEmpty(mapperFtlPath)){templateConfig.setMapper(mapperFtlPath);}}else {templateConfig.setMapper(null);}if ("true".equals(rb.getString("service"))){String serviceFtlPath = rb.getString("service.ftl.path");if (!EmptyUtil.isNullOrEmpty(serviceFtlPath)){templateConfig.setService(serviceFtlPath);}}else {templateConfig.setService(null);}if ("true".equals(rb.getString("serviceImp"))){String serviceImpFtlPath = rb.getString("serviceImp.ftl.path");if (!EmptyUtil.isNullOrEmpty(serviceImpFtlPath)){templateConfig.setServiceImpl(serviceImpFtlPath);}}else {templateConfig.setServiceImpl(null);}if ("true".equals(rb.getString("controller"))){String controllerFtlPath = rb.getString("controller.ftl.path");if (!EmptyUtil.isNullOrEmpty(controllerFtlPath)){templateConfig.setController(controllerFtlPath);}}else {templateConfig.setController(null);}templateConfig.setXml(null);mpg.setTemplate(templateConfig);// 策略配置StrategyConfig strategy = new StrategyConfig();strategy.setNaming(NamingStrategy.underline_to_camel);strategy.setColumnNaming(NamingStrategy.underline_to_camel);strategy.setSuperEntityClass(rb.getString("SuperEntityClass"));strategy.setEntityLombokModel(true);strategy.setRestControllerStyle(true);// 写于父类中的公共字段String[] SuperEntityColumns = rb.getString("superEntityColumns").split(",");strategy.setSuperEntityColumns(SuperEntityColumns);strategy.setInclude(rb.getString("tableName").split(","));strategy.setControllerMappingHyphenStyle(true);String tablePrefix = rb.getString("tablePrefix");if (tablePrefix!=null){strategy.setTablePrefix(tablePrefix);}mpg.setStrategy(strategy);mpg.setTemplateEngine(new FreemarkerTemplateEngine());mpg.execute();}
}

2、生成器快速入门

​ 上面我们的定义了CodeGenerator核心类,下面我们需要在项目中使用,各位在使用代码生成器之前,需要明确我们使用的模块,这里我们所有的pojo、mapper、service层都定义在model-***-service类型的模块中,这里以model-shop-service为例,其他模块使用方式也类似,在使用之前我们需要定义2个资源:

  • mybatis-plus-generrator.properties:关于数据库及生成策略定义
  • templates:代码生成器模板信息

,定义信息如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RfsNrR4R-1636200518329)(image/image-20210508201927002.png)]

2.1、generrator

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

#数据库地址
url=jdbc:mysql://192.168.112.77:3306/restkeeper-shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&tinyInt1isBit=false
#数据库账号
userName=root
#数据库密码
password=root
#此处为本项目src所在路径(代码生成器输出路径)
projectPath=F:/restkeeper-prent/restkeeper-super/restkeeper-model-shop/model-shop-service
#设置作者
author=Admin
#自定义包路径
parent=com.itheima
#装代码的文件夹名
moduleName=restkeeper
#设置表前缀,不设置则默认无前缀
tablePrefix =tab_
#数据库表名(此处切不可为空,如果为空,则默认读取数据库的所有表名)
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
#pojo的超类
SuperEntityClass = com.itheima.restkeeper.basic.BasicPojo
#pojo的超类公用字段
superEntityColumns = id,created_time,updated_time,sharding_id,enable_flag
#生成的层级
entity=true
entity.ftl.path=/templates/entity.java
mapper=false
mapper.ftl.path=/templates/mapper.java
service=false
service.ftl.path=/templates/service.java
serviceImp=false
serviceImp.ftl.path=/templates/serviceImpl.java
controller=false
controller.ftl.path=/templates/controller.java

2.2、templates

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IbPLmAMC-1636200518330)(image/image-20210508201927002.png)]

2.3、生成器使用

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

package com.itheima.restkeeper;import com.itheima.restkeeper.generator.CodeGenerator;
import org.junit.Test;/*** @Description:代码生成器*/
public class ShopGenerator {@Testpublic void test(){CodeGenerator.autoGenerator();}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eCQ77AUS-1636200518331)(image/image-20210508203121916.png)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qXNOe1Yb-1636200518332)(image/image-20210508203305294.png)]

第二章 springcloud-alibaba-dubbo

1、dubbo快速入门

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3oBinY3a-1636200518333)(image/dubbo-architecture-roadmap.jpg)]

单一应用架构

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

垂直应用架构

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

分布式服务架构

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

流动计算架构

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

下面我们看下dubbo架构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e2qtpAww-1636200518334)(image/dubbo-architecture.jpg)]

名词解释

节点 角色说明
Provider 暴露服务的服务提供方
Consumer 调用远程服务的服务消费方
Registry 服务注册与发现的注册中心
Monitor 统计服务的调用次数和调用时间的监控中心
Container 服务运行容器

调用关系说明

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

2、dubbo快速集成

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TYNXwMHb-1636200518335)(image/image-20210510090307452.png)]

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

dubbo-interface

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

dubbo-producer

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

dubbo-web

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

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

<dependencies><!--接口定义层--><dependency><groupId>com.itheima.dubbo</groupId><artifactId>dubbo-interface</artifactId><version>${interFace.version}</version></dependency><!---spring-cloud-alibaba主配置--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency><!---springboot主配置--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring.boot.version}</version><type>pom</type><scope>import</scope></dependency>
</dependencies>

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

<dependencies><!--接口定义层--><dependency><groupId>com.itheima.dubbo</groupId><artifactId>dubbo-interface</artifactId></dependency><!--web支持--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--nacos支持--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!--dubbo支持--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId></dependency>
</dependencies>

2.1、生产者配置

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

#服务配置
server:#端口port: 8080#服务编码tomcat:uri-encoding: UTF-8
#spring相关配置
spring:#应用配置application:#应用名称name: dubbo-producermain:allow-bean-definition-overriding: truecloud:#nacos添加中心nacos:discovery:server-addr: 192.168.112.77:8848namespace: publicgroup: SEATA_GROUP
dubbo:#dubbo服务版本application:version: 1.0.0logger: slf4j#dubbo接口扫描路径scan:base-packages: com.itheima.dubbo#dubbo服务添加registry:address: spring-cloud://192.168.112.77#dubbo服务协议类型及端口,线程数【这里是默认配置】protocol:name: dubboport: 28080threads: 200accesslog: D:/logs/dubbo-producer-01.log

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

package com.itheima.dubbo;import org.apache.dubbo.config.annotation.DubboService;/*** @ClassName UserFaceImpl.java* @Description 用户接口实现*/
@DubboService(version = "${dubbo.application.version}",timeout = 5000)
public class UserFaceImpl implements UserFace {@Overridepublic String helloUser(String userName) {return "Hello!"+userName;}
}

2.2、消费者配置

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

#服务配置
server:#端口port: 8081#服务编码tomcat:uri-encoding: UTF-8
#spring相关配置
spring:#应用配置application:#应用名称name: dubbo-webmain:allow-bean-definition-overriding: true#nacos添加中心cloud:nacos:discovery:server-addr: 192.168.112.77:8848namespace: publicgroup: SEATA_GROUP
#dubbo消费端配置
dubbo:application:version: 1.0.0logger: slf4jcloud:#表示要订阅服务的服务名,可以配置'*',代表订阅所有服务,不推荐使用。若需订阅多应用,使用 "," 分割。subscribed-services: dubbo-producerscan:#扫描路径base-packages: com.itheima.dubbo.webregistry:address: spring-cloud://192.168.112.77#dubbo服务协议类型及端口,线程数【这里是默认配置】protocol:name: dubboport: 28081threads: 200accesslog: D:/logs/dubbo-web-01.log

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

package com.itheima.dubbo.web;import com.itheima.dubbo.UserFace;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;/*** @ClassName UserController.java* @Description 用户controller*/
@RestController
public class UserController {@DubboReference(version = "${dubbo.application.version}",check = false)UserFace userFace;@GetMapping("{userName}")public String helloUser(@PathVariable("userName") String userName){return userFace.helloUser(userName);}}

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lo23DrDM-1636200518336)(image/image-20210510203502661.png)]

3、业务模块开发

3.1、业务调用链路

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FoGKQXa-1636200518336)(image/image-20210501145955490-1620973820360.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生产者

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oVXZ3XsE-1636200518340)(image/image-20210514150936530.png)]

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

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

注意:餐掌柜中为避免模块职能混乱,禁止生产者模块调用生产者模块,当产生跨服务接口调用,例如一个接口需要多个接口来支持,我们会放到web层进行服务调用然后业务组装,如果牵涉分布式事务问题,我们会采用seata方式来解决

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

<!-- Dubbo Spring Cloud Starter -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

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

dubbo:#dubbo应用服务定义application:#版本version: 1.0.0#日志logger: slf4jscan:#扫描路径base-packages: com.itheima.restkeeperregistry:#添加中心:这里采用nacos添加中心address: spring-cloud://192.168.112.77#服务协议定义protocol:#服务协议名称name: dubbo#协议端口port: 27077#线程数threads: 200#dubbo调用日志accesslog: D:/logs/model-shop-producer-01.log

3.3、dubbo消费者

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dj2ZTdpR-1636200518341)(image/image-20210514154417356.png)]

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

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

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

<!-- Dubbo Spring Cloud Starter -->
<dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-dubbo</artifactId>
</dependency>

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

dubbo:#dubbo应用服务定义application:#版本version: 1.0.0#日志logger: slf4jcloud:#表示要订阅服务的服务名,可以配置'*',代表订阅所有服务,不推荐使用。若需订阅多应用,使用 "," 分割。subscribed-services: model-shop-producer,model-basic-producer,model-shop-applet,model-shop-user,model-trading-producerscan:#扫描路径base-packages: com.itheima.restkeeper.webregistry:#添加中心address: spring-cloud://192.168.112.77#服务协议定义protocol:#服务协议名称name: dubbo#协议端口port: 27078#线程数threads: 200#dubbo调用日志accesslog: D:/logs/model-shop-web-01.log

相比【生产者】来说【消费者】就多subscribed-services属性配置,此属性为订阅服务的服务名

3.4、dubbo接口

​ 可能在以往的开发中我们只是知道三层架构【mapper、service、web】,那这里的face层是什么意思呢?大家都知道dubbo服务的调用逻辑,【生产者】调用【提供者】,那他们直接能调用的集成也就是声明统一的接口定义,在餐掌柜系统中dubbo层接口就起到此作用:

  • 定义dubbo服务接口
  • 被生产者依赖,按照face层的dubbo接口定义实现业务
  • 被消费者依赖,从face层的dubbo中选择自己的业务接口

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hGcqyLhZ-1636200518342)(image/image-20210501155101989-1620979167147.png)]

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

3.5、service核心业务

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ULWx2KIN-1636200518343)(image/image-20210501172119155.png)]

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

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

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

Save

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

SaveOrUpdate

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

Remove

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

Update

// 根据 UpdateWrapper 条件,更新记录 需要设置
sqlsetboolean update(Wrapper<T> updateWrapper);
// 根据 whereWrapper 条件,更新记录
boolean update(T updateEntity, Wrapper<T> whereWrapper);
// 根据 ID 选择修改
boolean updateById(T entity);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList);
// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);

Get

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

List

// 查询所有
List<T> list();
// 查询列表
List<T> list(Wrapper<T> queryWrapper);
// 查询(根据ID 批量查询)
Collection<T> listByIds(Collection<? extends Serializable> idList);
// 查询(根据 columnMap 条件)
Collection<T> listByMap(Map<String, Object> columnMap);
// 查询所有列表
List<Map<String, Object>> listMaps();
// 查询列表
List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper);
// 查询全部记录
List<Object> listObjs();
// 查询全部记录
<V> List<V> listObjs(Function<? super Object, V> mapper);
// 根据 Wrapper 条件,查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);

Page

// 无条件分页查询
IPage<T> page(IPage<T> page);
// 条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);
// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);
// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);

Count

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

Chain

query

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

update

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

mapper继承BaseMapper的方法:

Insert

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

Delete

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

Update

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

Select

// 根据 ID 查询
T selectById(Serializable id);
// 根据 entity 条件,查询一条记录
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 查询(根据ID 批量查询)
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
// 根据 entity 条件,查询全部记录
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 查询(根据 columnMap 条件)
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
// 根据 Wrapper 条件,查询全部记录
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 entity 条件,查询全部记录(并翻页)
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
// 根据 Wrapper 条件,查询总记录数
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9OvW3cUp-1636200518343)(image/image-20210514095053922.png)]

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

1、功能区拆解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nkoi9EIW-1636200518344)(image/image-20210514104317957.png)]

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

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

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

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

2、数据库结构

CREATE TABLE `tab_brand` (`id` bigint(18) NOT NULL COMMENT '商户id',`brand_name` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '品牌名称',`category` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '品牌分类',`enable_flag` varchar(18) CHARACTER SET utf8 DEFAULT NULL COMMENT '是否有效',`created_time` datetime DEFAULT NULL COMMENT '创建时间',`updated_time` datetime DEFAULT NULL COMMENT '创建时间',`sharding_id` bigint(18) DEFAULT NULL COMMENT '分库id',`enterprise_id` bigint(18) NOT NULL COMMENT '商户号',PRIMARY KEY (`id`)
) 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图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7vmzJKvH-1636200518344)(image/image-20210514152129567.png)]

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

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

**BrandFace:**品牌dubbo接口定义

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

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

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

3.1、BrandFace接口

​ 在开发中,我们以往都是先写controller层业务,而在实际开发中我们应该先定义服务接口,因为我们一致提倡的是面向接口开发,而这里,我们应该先定义dubbo服务的接口,只有统一接口在做微服务调用时才能保证逻辑的清晰

package com.itheima.restkeeper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.req.BrandVo;import java.util.List;/*** @ClassName BrandFace.java* @Description 品牌服务*/
public interface BrandFace {/*** @Description 品牌列表* @param brandVo 查询条件* @param pageNum 页码* @param pageSize 每页条数* @return Page<BrandVo>*/Page<BrandVo> findBrandVoPage(BrandVo brandVo, int pageNum, int pageSize);/*** @Description 创建品牌* @param brandVo 对象信息* @return BrandVo*/BrandVo createBrand(BrandVo brandVo);/*** @Description 修改品牌* @param brandVo 对象信息* @return Boolean*/Boolean updateBrand(BrandVo brandVo);/*** @Description 删除品牌* @param checkedIds 选择中对象Ids* @return Boolean*/Boolean deleteBrand(String[] checkedIds);/*** @Description 查找品牌* @param brandId 选择对象信息Id* @return BrandVo*/BrandVo findBrandByBrandId(Long brandId);/**** @description 查询品牌下拉框* @return: List<BrandVo>*/List<BrandVo> findBrandVoList();}

3.2、BrandFaceImpl接口实现

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

package com.itheima.restkeeper.face;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.BrandFace;
import com.itheima.restkeeper.pojo.Brand;
import com.itheima.restkeeper.req.BrandVo;
import com.itheima.restkeeper.service.IBrandService;
import com.itheima.restkeeper.utils.BeanConv;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.annotation.Method;
import org.springframework.beans.factory.annotation.Autowired;import java.util.List;/*** @ClassName BrandFaceImpl.java* @Description 品牌dubbo接口定义实现*/
@DubboService(version = "${dubbo.application.version}",timeout = 5000,methods ={@Method(name = "findBrandVoPage",retries = 2),@Method(name = "createBrand",retries = 0),@Method(name = "updateBrand",retries = 0),@Method(name = "deleteBrand",retries = 0)})
public class BrandFaceImpl implements BrandFace {@AutowiredIBrandService brandService;@Overridepublic Page<BrandVo> findBrandVoPage(BrandVo brandVo, int pageNum, int pageSize) {//查询Page<Brand>品牌分页Page<Brand> page = brandService.findBrandVoPage(brandVo, pageNum, pageSize);//转化Page<Brand>为Page<BrandVo>Page<BrandVo> pageVo = new Page<>();BeanConv.toBean(page,pageVo);//转换List<Brand>为 List<BrandVo>List<Brand> brandList = page.getRecords();List<BrandVo> brandVoList = BeanConv.toBeanList(brandList,BrandVo.class);pageVo.setRecords(brandVoList);//返回结果return pageVo;}@Overridepublic BrandVo createBrand(BrandVo brandVo) {return BeanConv.toBean( brandService.createBrand(brandVo), BrandVo.class);}@Overridepublic Boolean updateBrand(BrandVo brandVo) {return brandService.updateBrand(brandVo);}@Overridepublic Boolean deleteBrand(String[] checkedIds) {return brandService.deleteBrand(checkedIds);}@Overridepublic BrandVo findBrandByBrandId(Long brandId) {return BeanConv.toBean(brandService.getById(brandId),BrandVo.class)}@Overridepublic List<BrandVo> findBrandVoList() {return BeanConv.toBeanList(brandService.findBrandVoList(),BrandVo.class);}
}

3.3、IBrandService业务接口

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

package com.itheima.restkeeper.service;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.pojo.Brand;
import com.baomidou.mybatisplus.extension.service.IService;
import com.itheima.restkeeper.req.BrandVo;import java.util.List;/*** @Description:品牌管理 服务类*/
public interface IBrandService extends IService<Brand> {/*** @Description 品牌列表* @param brandVo 查询条件* @param pageNum 页码* @param pageSize 每页条数* @return Page<BrandVo>*/Page<Brand> findBrandVoPage(BrandVo brandVo, int pageNum, int pageSize);/*** @Description 创建品牌* @param brandVo 对象信息* @return*/Brand createBrand(BrandVo brandVo);/*** @Description 修改品牌* @param brandVo 对象信息* @return Boolean*/Boolean updateBrand(BrandVo brandVo);/*** @Description 删除品牌* @param checkedIds 选择中对象Ids* @return Boolean*/Boolean deleteBrand(String[] checkedIds);/**** @description 查询品牌下拉框* @return: List<BrandVo>*/List<Brand> findBrandVoList();
}

3.4、BrandServiceImpl接口实现

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

package com.itheima.restkeeper.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.basic.BasicPojo;
import com.itheima.restkeeper.constant.SuperConstant;
import com.itheima.restkeeper.pojo.Brand;
import com.itheima.restkeeper.mapper.BrandMapper;
import com.itheima.restkeeper.req.BrandVo;
import com.itheima.restkeeper.service.IBrandService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.restkeeper.utils.BeanConv;
import com.itheima.restkeeper.utils.EmptyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** @Description:品牌管理 服务实现类*/
@Service
public class BrandServiceImpl extends ServiceImpl<BrandMapper, Brand> implements IBrandService {@Overridepublic Page<Brand> findBrandVoPage(BrandVo brandVo, int pageNum, int pageSize) {//构建分页对象Page<Brand> page = new Page<>(pageNum,pageSize);//构建查询条件QueryWrapper<Brand> queryWrapper = new QueryWrapper<>();//按品牌名称查询if (!EmptyUtil.isNullOrEmpty(brandVo.getBrandName())) {queryWrapper.lambda().likeRight(Brand::getBrandName,brandVo.getBrandName());}//按品牌分类查询if (!EmptyUtil.isNullOrEmpty(brandVo.getCategory())) {queryWrapper.lambda().likeRight(Brand::getCategory,brandVo.getCategory());}//按品牌状态查询if (!EmptyUtil.isNullOrEmpty(brandVo.getEnableFlag())) {queryWrapper.lambda().eq(Brand::getEnableFlag,brandVo.getEnableFlag());}//按创建时间降序queryWrapper.lambda().orderByDesc(Brand::getCreatedTime);//执行分页查询return page(page, queryWrapper);}@Overridepublic Brand createBrand(BrandVo brandVo) {//转换BrandVo为BrandBrand brand = BeanConv.toBean(brandVo, Brand.class);//执行保存boolean flag = save(brand);if (flag){return brand;}return null;}@Overridepublic Boolean updateBrand(BrandVo brandVo) {//转换BrandVo为BrandBrand brand = BeanConv.toBean(brandVo, Brand.class);//按ID执行修改return updateById(brand);}@Overridepublic Boolean deleteBrand(String[] checkedIds) {//转换数组为集合List<String> ids = Arrays.asList(checkedIds);List<Long> idsLong = new ArrayList<>();ids.forEach(n->{idsLong.add(Long.valueOf(n));});//批量删除return removeByIds(idsLong);}@Overridepublic List<Brand> findBrandVoList() {//构建查询条件QueryWrapper<Brand> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(Brand::getEnableFlag, SuperConstant.YES);//查询有效状态return list(queryWrapper);}
}

3.5、BrandController类

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

package com.itheima.restkeeper.web;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.AffixFace;
import com.itheima.restkeeper.BrandFace;
import com.itheima.restkeeper.basic.ResponseWrap;
import com.itheima.restkeeper.enums.BrandEnum;
import com.itheima.restkeeper.exception.ProjectException;
import com.itheima.restkeeper.req.AffixVo;
import com.itheima.restkeeper.req.BrandVo;
import com.itheima.restkeeper.utils.EmptyUtil;
import com.itheima.restkeeper.utils.ExceptionsUtil;
import com.itheima.restkeeper.utils.ResponseWrapBuild;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.*;import java.util.List;
import java.util.stream.Collectors;/*** @ClassName BrandController.java* @Description 品牌Controller*/
@RestController
@RequestMapping("brand")
@Slf4j
@Api(tags = "品牌controller")
public class BrandController {@DubboReference(version = "${dubbo.application.version}",check = false)BrandFace brandFace;@DubboReference(version = "${dubbo.application.version}",check = false)AffixFace affixFace;/*** @Description 品牌列表* @param brandVo 查询条件* @return*/@PostMapping("page/{pageNum}/{pageSize}")@ApiOperation(value = "查询品牌分页",notes = "查询品牌分页")@ApiImplicitParams({@ApiImplicitParam(name = "brandVo",value = "品牌查询对象",required = true,dataType = "BrandVo"),@ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",dataType = "Integer"),@ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",dataType = "Integer")})public ResponseWrap<Page<BrandVo>> findBrandVoPage(@RequestBody BrandVo brandVo,@PathVariable("pageNum") int pageNum,@PathVariable("pageSize") int pageSize) throws ProjectException {try {//查询分页Page<BrandVo> brandVoPage = brandFace.findBrandVoPage(brandVo, pageNum, pageSize);//如果结果不为空,则为分页指定附件信息if (!EmptyUtil.isNullOrEmpty(brandVoPage)&&!EmptyUtil.isNullOrEmpty(brandVoPage.getRecords())){List<BrandVo> brandVoList = brandVoPage.getRecords();//循环处理结果集brandVoList.forEach(n->{List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(n.getId());if (!EmptyUtil.isNullOrEmpty(affixVoList)){n.setAffixVo(affixVoList.get(0));}});brandVoPage.setRecords(brandVoList);}//返回分页结果return ResponseWrapBuild.build(BrandEnum.SUCCEED,brandVoPage);} catch (Exception e) {log.error("查询品牌列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(BrandEnum.PAGE_FAIL);}}/*** @Description 查询品牌下拉框* @return*/@GetMapping("list")@ApiOperation(value = "查询品牌下拉框",notes = "查询品牌下拉框")public ResponseWrap<List<BrandVo>> findBrandVoList() throws ProjectException {try {//查询下拉框List<BrandVo> brandVos = brandFace.findBrandVoList();//返回结果return ResponseWrapBuild.build(BrandEnum.SUCCEED,brandVos);} catch (Exception e) {log.error("查询品牌列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(BrandEnum.PAGE_FAIL);}}/*** @Description 添加品牌* @param brandVo 对象信息* @return*/@PostMapping@ApiOperation(value = "添加品牌",notes = "添加品牌")@ApiImplicitParam(name = "brandVo",value = "品牌对象",required = true,dataType = "BrandVo")ResponseWrap<BrandVo> createBrand(@RequestBody BrandVo brandVo) throws ProjectException {try {//添加品牌BrandVo brandVoResult = brandFace.createBrand(brandVo);//绑定附件if (!EmptyUtil.isNullOrEmpty(brandVoResult)){affixFace.bindBusinessId(AffixVo.builder().businessId(brandVoResult.getId()).id(brandVo.getAffixVo().getId()).build());}//指定附件信息brandVoResult.setAffixVo(AffixVo.builder().pathUrl(brandVo.getAffixVo().getPathUrl()).businessId(brandVoResult.getId()).id(brandVo.getAffixVo().getId()).build());//返回结果return ResponseWrapBuild.build(BrandEnum.SUCCEED,brandVoResult);} catch (Exception e) {log.error("保存品牌异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(BrandEnum.CREATE_FAIL);}}/*** @Description 修改品牌* @param brandVo 对象信息* @return*/@PatchMapping@ApiOperation(value = "修改品牌",notes = "修改品牌")@ApiImplicitParam(name = "brandVo",value = "品牌对象",required = true,dataType = "BrandVo")ResponseWrap<Boolean> updateBrand(@RequestBody BrandVo brandVo) throws ProjectException {try {//按ID修改品牌信息Boolean flag = brandFace.updateBrand(brandVo);//修改成功后,处理附件图片信息if (flag){List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(brandVo.getId());List<Long> affixIds = affixVoList.stream().map(AffixVo::getId).collect(Collectors.toList());if (!affixIds.contains(brandVo.getAffixVo().getId())){//删除图片flag = affixFace.deleteAffixVoByBusinessId(brandVo.getId());//绑定新图片affixFace.bindBusinessId(AffixVo.builder().businessId(brandVo.getId()).id(brandVo.getAffixVo().getId()).build());}}//返回结果return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);} catch (Exception e) {log.error("保存品牌异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(BrandEnum.UPDATE_FAIL);}}/*** @Description 删除品牌* @param brandVo 查询对象* @return*/@DeleteMapping@ApiOperation(value = "删除品牌",notes = "删除品牌")@ApiImplicitParam(name = "brandVo",value = "品牌查询对象",required = true,dataType = "BrandVo")ResponseWrap<Boolean> deleteBrand(@RequestBody BrandVo brandVo ) throws ProjectException {try {//删除选中节点String[] checkedIds = brandVo.getCheckedIds();Boolean flag = brandFace.deleteBrand(checkedIds);//删除图片for (String checkedId : checkedIds) {affixFace.deleteAffixVoByBusinessId(Long.valueOf(checkedId));}//返回结果return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);} catch (Exception e) {log.error("删除品牌异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(BrandEnum.DELETE_FAIL);}}/*** @Description 查找品牌* @param brandId 品牌ID* @return*/@GetMapping("{brandId}")@ApiOperation(value = "查找品牌",notes = "查找品牌")@ApiImplicitParam(paramType = "path",name = "brandId",value = "品牌Id",dataType = "Long")ResponseWrap<BrandVo> findBrandByBrandId(@PathVariable("brandId") Long brandId) throws ProjectException {//品牌是否为空判断if (EmptyUtil.isNullOrEmpty(brandId)){throw new ProjectException(BrandEnum.SELECT_BRAND_FAIL);}try {//安装ID查询品牌信息BrandVo brandVo = brandFace.findBrandByBrandId(brandId);//返回结果return ResponseWrapBuild.build(BrandEnum.SUCCEED,brandVo);} catch (Exception e) {log.error("查找品牌所有品牌异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(BrandEnum.SELECT_BRAND_FAIL);}}@PostMapping("update-brand-enableFlag")@ApiOperation(value = "修改品牌状态",notes = "修改品牌状态")@ApiImplicitParam(name = "brandVo",value = "品牌查询对象",required = true,dataType = "BrandVo")ResponseWrap<Boolean> updateBrandEnableFlag(@RequestBody BrandVo brandVo) throws ProjectException {try {//修改品牌状态Boolean flag = brandFace.updateBrand(brandVo);//返回结果return ResponseWrapBuild.build(BrandEnum.SUCCEED,flag);} catch (Exception e) {log.error("修改品牌状态:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(BrandEnum.UPDATE_FAIL);}}}

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lloCdWkU-1636200518345)(image/image-20210514095053922.png)]

1、功能区拆解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-inpYBIuj-1636200518346)(image/image-20210514224731984.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i1w9039W-1636200518346)(image/image-20210514225020210.png)]

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

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

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

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

2、数据库结构

CREATE TABLE `tab_store` (`id` bigint(18) NOT NULL COMMENT '门店主键id',`brand_id` bigint(18) DEFAULT NULL COMMENT '品牌',`store_name` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '门店名称',`province` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(省)',`city` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(市)',`area` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '地址(区)',`address` varchar(500) COLLATE utf8_bin NOT NULL COMMENT '详细地址',`manager_id` varchar(32) COLLATE utf8_bin DEFAULT NULL COMMENT '管理员id',`enable_flag` varchar(18) CHARACTER SET utf8 DEFAULT NULL COMMENT '是否有效',`created_time` datetime DEFAULT NULL COMMENT '创建时间',`updated_time` datetime DEFAULT NULL COMMENT '创建时间',`sharding_id` bigint(18) DEFAULT NULL COMMENT '分库id',`enterprise_id` bigint(18) NOT NULL COMMENT '商户号',`longitude` double(9,6) DEFAULT NULL COMMENT '经度',`dimensionality` double(9,6) DEFAULT NULL COMMENT '维度',PRIMARY KEY (`id`)
) 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图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5W4sKJXx-1636200518347)(image/image-20210514230138101.png)]

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

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

StoreFace:门店dubbo接口定义

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

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

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

3.1、StoreFace接口

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

package com.itheima.restkeeper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.req.StoreVo;import java.util.List;/*** @ClassName StoreFace.java* @Description 门店dubbo服务*/
public interface StoreFace {/*** @Description 品牌列表* @param storeVo 查询条件* @param pageNum 页码* @param pageSize 每页条数* @return Page<BrandVo>*/Page<StoreVo> findStoreVoPage(StoreVo storeVo, int pageNum, int pageSize);/*** @Description 创建门店* @param storeVo 对象信息* @return StoreVo*/StoreVo createStore(StoreVo storeVo);/*** @Description 修改门店* @param storeVo 对象信息* @return Boolean*/Boolean updateStore(StoreVo storeVo);/*** @Description 删除门店* @param checkedIds 选择对象信息Id* @return Boolean*/Boolean deleteStore(String[] checkedIds);/*** @Description 查找门店* @param storeId 选择对象信息Id* @return StoreVo*/StoreVo findStoreByStoreId(Long storeId);/**** @description 查询门店下拉框* @return: List<StoreVo>*/List<StoreVo> findStoreVoList();
}

3.2、StoreFaceImpl接口实现

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

package com.itheima.restkeeper.face;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.StoreFace;
import com.itheima.restkeeper.pojo.Store;
import com.itheima.restkeeper.req.StoreVo;
import com.itheima.restkeeper.service.IStoreService;
import com.itheima.restkeeper.utils.BeanConv;
import com.itheima.restkeeper.utils.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.annotation.Method;
import org.springframework.beans.factory.annotation.Autowired;import java.util.List;/*** @ClassName StoreFaceImpl.java* @Description 门店dubbbo服务实现*/
@Slf4j
@DubboService(version = "${dubbo.application.version}",timeout = 5000,methods ={@Method(name = "findStoreVoPage",retries = 2),@Method(name = "createStore",retries = 0),@Method(name = "updateStore",retries = 0),@Method(name = "deleteStore",retries = 0)})
public class StoreFaceImpl implements StoreFace {@AutowiredIStoreService storeService;@Overridepublic Page<StoreVo> findStoreVoPage(StoreVo storeVo, int pageNum, int pageSize) {//查询Page<Store>门店分页Page<Store> page = storeService.findStoreVoPage(storeVo, pageNum, pageSize);//转化Page<Store>为Page<StoreVo>Page<StoreVo> pageVo = new Page<>();BeanConv.toBean(page,pageVo);//转换List<Store>为 List<StoreVo>List<Store> storeList = page.getRecords();List<StoreVo> storeVoList = BeanConv.toBeanList(storeList,StoreVo.class);pageVo.setRecords(storeVoList);//返回结果return pageVo;}@Overridepublic StoreVo createStore(StoreVo storeVo) {return BeanConv.toBean( storeService.createStore(storeVo), StoreVo.class);}@Overridepublic Boolean updateStore(StoreVo storeVo) {return storeService.updateStore(storeVo);}@Overridepublic Boolean deleteStore(String[] checkedIds) {return storeService.deleteStore(checkedIds);}@Overridepublic StoreVo findStoreByStoreId(Long storeId) {return BeanConv.toBean(storeService.getById(storeId),StoreVo.class);}@Overridepublic List<StoreVo> findStoreVoList() {return BeanConv.toBeanList(storeService.findStoreVoList(),StoreVo.class);}
}

3.3、IStoreService业务接口

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

package com.itheima.restkeeper.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.basic.BasicPojo;
import com.itheima.restkeeper.constant.SuperConstant;
import com.itheima.restkeeper.pojo.Store;
import com.itheima.restkeeper.mapper.StoreMapper;
import com.itheima.restkeeper.req.StoreVo;
import com.itheima.restkeeper.service.IStoreService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.restkeeper.utils.BeanConv;
import com.itheima.restkeeper.utils.EmptyUtil;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** @Description:门店信息账号 服务实现类*/
@Service
public class StoreServiceImpl extends ServiceImpl<StoreMapper, Store> implements IStoreService {@Overridepublic Page<Store> findStoreVoPage(StoreVo storeVo, int pageNum, int pageSize) {//构建分页对象Page<Store> page = new Page<>(pageNum,pageSize);QueryWrapper<Store> queryWrapper = new QueryWrapper<>();//按门店名称查询if (!EmptyUtil.isNullOrEmpty(storeVo.getStoreName())) {queryWrapper.lambda().likeRight(Store::getStoreName,storeVo.getStoreName());}//按门店状态查询if (!EmptyUtil.isNullOrEmpty(storeVo.getEnableFlag())) {queryWrapper.lambda().eq(Store::getEnableFlag,storeVo.getEnableFlag());}//按创建时间降序queryWrapper.lambda().orderByDesc(Store::getCreatedTime);//执行分页查询return page(page, queryWrapper);}@Overridepublic Store createStore(StoreVo storeVo) {//转换StoreVo为StoreStore store = BeanConv.toBean(storeVo, Store.class);//执行保存boolean flag = save(store);if (flag){return store;}return null;}@Overridepublic Boolean updateStore(StoreVo storeVo) {//转换StoreVo为StoreStore store = BeanConv.toBean(storeVo, Store.class);//按ID执行修改return updateById(store);}@Overridepublic Boolean deleteStore(String[] checkedIds) {//转换数组为集合List<String> ids = Arrays.asList(checkedIds);List<Long> idsLong = new ArrayList<>();ids.forEach(n->{idsLong.add(Long.valueOf(n));});//批量删除return removeByIds(idsLong);}@Overridepublic List<Store> findStoreVoList() {//构建查询条件QueryWrapper<Store> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(BasicPojo::getEnableFlag,SuperConstant.YES);//查询有效状态return list(queryWrapper);}}

3.4、StoreServiceImpl接口实现

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

package com.itheima.restkeeper.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.basic.BasicPojo;
import com.itheima.restkeeper.constant.SuperConstant;
import com.itheima.restkeeper.pojo.Brand;
import com.itheima.restkeeper.pojo.Store;
import com.itheima.restkeeper.pojo.Store;
import com.itheima.restkeeper.mapper.StoreMapper;
import com.itheima.restkeeper.req.StoreVo;
import com.itheima.restkeeper.service.IStoreService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itheima.restkeeper.utils.BeanConv;
import com.itheima.restkeeper.utils.EmptyUtil;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;/*** @Description:门店信息账号 服务实现类*/
@Service
public class StoreServiceImpl extends ServiceImpl<StoreMapper, Store> implements IStoreService {@Overridepublic Page<Store> findStoreVoPage(StoreVo storeVo, int pageNum, int pageSize) {//分页条件构建Page<Store> page = new Page<>(pageNum,pageSize);QueryWrapper<Store> queryWrapper = new QueryWrapper<>();//按门店名称查询if (!EmptyUtil.isNullOrEmpty(storeVo.getStoreName())) {queryWrapper.lambda().likeRight(Store::getStoreName,storeVo.getStoreName());}//按门店状态查询if (!EmptyUtil.isNullOrEmpty(storeVo.getEnableFlag())) {queryWrapper.lambda().eq(Store::getEnableFlag,storeVo.getEnableFlag());}queryWrapper.lambda().orderByDesc(Store::getCreatedTime);return page(page, queryWrapper);}@Overridepublic Store createStore(StoreVo storeVo) {Store store = BeanConv.toBean(storeVo, Store.class);boolean flag = save(store);if (flag){return store;}return null;}@Overridepublic Boolean updateStore(StoreVo storeVo) {Store store = BeanConv.toBean(storeVo, Store.class);return updateById(store);}@Overridepublic Boolean deleteStore(String[] checkedIds) {List<String> ids = Arrays.asList(checkedIds);List<Long> idsLong = new ArrayList<>();ids.forEach(n->{idsLong.add(Long.valueOf(n));});return removeByIds(idsLong);}@Overridepublic List<Store> findStoreVoList() {QueryWrapper<Store> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(BasicPojo::getEnableFlag,SuperConstant.YES);return list(queryWrapper);}}

3.5、StoreController类

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

package com.itheima.restkeeper.web;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.AffixFace;
import com.itheima.restkeeper.StoreFace;
import com.itheima.restkeeper.basic.ResponseWrap;
import com.itheima.restkeeper.enums.StoreEnum;
import com.itheima.restkeeper.exception.ProjectException;
import com.itheima.restkeeper.req.StoreVo;
import com.itheima.restkeeper.utils.EmptyUtil;
import com.itheima.restkeeper.utils.ExceptionsUtil;
import com.itheima.restkeeper.utils.ResponseWrapBuild;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.web.bind.annotation.*;import java.util.List;/*** @ClassName StoreController.java* @Description 门店Controller*/
@RestController
@RequestMapping("store")
@Slf4j
@Api(tags = "门店controller")
public class StoreController {@DubboReference(version = "${dubbo.application.version}",check = false)StoreFace storeFace;@DubboReference(version = "${dubbo.application.version}",check = false)AffixFace affixFace;/*** @Description 门店列表* @param storeVo 查询条件* @return*/@PostMapping("page/{pageNum}/{pageSize}")@ApiOperation(value = "查询门店分页",notes = "查询门店分页")@ApiImplicitParams({@ApiImplicitParam(name = "storeVo",value = "门店查询对象",required = true,dataType = "StoreVo"),@ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",dataType = "Integer"),@ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",dataType = "Integer")})public ResponseWrap<Page<StoreVo>> findStoreVoPage(@RequestBody StoreVo storeVo,@PathVariable("pageNum") int pageNum,@PathVariable("pageSize") int pageSize) throws ProjectException {try {//分页查询Page<StoreVo> storeVoPage = storeFace.findStoreVoPage(storeVo, pageNum, pageSize);//返回结果return ResponseWrapBuild.build(StoreEnum.SUCCEED,storeVoPage);} catch (Exception e) {log.error("查询门店列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(StoreEnum.PAGE_FAIL);}}/*** @Description 添加门店* @param storeVo 对象信息* @return*/@PostMapping@ApiOperation(value = "添加门店",notes = "添加门店")@ApiImplicitParam(name = "storeVo",value = "门店对象",required = true,dataType = "StoreVo")ResponseWrap<StoreVo> createStore(@RequestBody StoreVo storeVo) throws ProjectException {try {//添加门店StoreVo storeVoResult = storeFace.createStore(storeVo);//返回结果return ResponseWrapBuild.build(StoreEnum.SUCCEED,storeVoResult);} catch (Exception e) {log.error("保存门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(StoreEnum.CREATE_FAIL);}}/*** @Description 修改门店* @param storeVo 对象信息* @return*/@PatchMapping@ApiOperation(value = "修改门店",notes = "修改门店")@ApiImplicitParam(name = "storeVo",value = "门店对象",required = true,dataType = "StoreVo")ResponseWrap<Boolean> updateStore(@RequestBody StoreVo storeVo) throws ProjectException {try {//修改门店Boolean flag = storeFace.updateStore(storeVo);//返回结果return ResponseWrapBuild.build(StoreEnum.SUCCEED,flag);} catch (Exception e) {log.error("保存门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(StoreEnum.UPDATE_FAIL);}}/*** @Description 删除门店* @param storeVo 查询对象* @return*/@DeleteMapping@ApiOperation(value = "删除门店",notes = "删除门店")@ApiImplicitParam(name = "storeVo",value = "门店查询对象",required = true,dataType = "StoreVo")ResponseWrap<Boolean> deleteStore(@RequestBody StoreVo storeVo ) throws ProjectException {try {//删除门店String[] checkedIds = storeVo.getCheckedIds();Boolean flag = storeFace.deleteStore(checkedIds);//返回结果return ResponseWrapBuild.build(StoreEnum.SUCCEED,flag);} catch (Exception e) {log.error("删除门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(StoreEnum.DELETE_FAIL);}}/*** @Description 查找门店* @param storeId 门店id* @return*/@GetMapping("{storeId}")@ApiOperation(value = "查找门店",notes = "查找门店")@ApiImplicitParam(paramType = "path",name = "storeId",value = "门店Id",dataType = "Long")ResponseWrap<StoreVo> findStoreByStoreId(@PathVariable("storeId") Long storeId) throws ProjectException {try {//按ID查询门店StoreVo storeVo = storeFace.findStoreByStoreId(storeId);//返回结果return ResponseWrapBuild.build(StoreEnum.SUCCEED,storeVo);} catch (Exception e) {log.error("查找门店所有门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(StoreEnum.SELECT_STORE_FAIL);}}/*** @Description 查找门店* @return*/@GetMapping("list")@ApiOperation(value = "查找门店下拉列表",notes = "查找门店下拉列表")ResponseWrap<List<StoreVo>> findStoreVoList() throws ProjectException {try {//查询所有有效门店List<StoreVo> list = storeFace.findStoreVoList();//返回结果return ResponseWrapBuild.build(StoreEnum.SUCCEED,list);} catch (Exception e) {log.error("查找门店所有门店异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(StoreEnum.SELECT_STORE_LIST_FAIL);}}@PostMapping("update-store-enableFlag")@ApiOperation(value = "修改门店状态",notes = "修改门店状态")ResponseWrap<Boolean> updateStoreEnableFlag(@RequestBody StoreVo storeVo) throws ProjectException {try {//修改门店状态Boolean flag = storeFace.updateStore(storeVo);//返回结果return ResponseWrapBuild.build(StoreEnum.SUCCEED,flag);} catch (Exception e) {log.error("修改门店状态:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(StoreEnum.UPDATE_FAIL);}}}

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WvTryYQM-1636200518348)(image/image-20210515144627320.png)]

1、功能区拆解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YKcAQC8z-1636200518348)(image/image-20210515151404218.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-djPmFgp4-1636200518349)(image/image-20210515151437786.png)]

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

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

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

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

2、数据库结构

员工表结构如下

CREATE TABLE `tab_user` (`id` bigint(18) NOT NULL COMMENT '主键',`store_id` bigint(32) DEFAULT NULL COMMENT '门店Id',`enterprise_id` bigint(18) NOT NULL COMMENT '商户号',`username` varchar(36) DEFAULT NULL COMMENT '登录名称',`real_name` varchar(36) DEFAULT NULL COMMENT '真实姓名',`password` varchar(150) DEFAULT NULL COMMENT '密码',`sex` varchar(11) DEFAULT NULL COMMENT '性别',`mobil` varchar(36) DEFAULT NULL COMMENT '电话',`email` varchar(36) DEFAULT NULL COMMENT '邮箱',`discount_limit` decimal(10,2) DEFAULT NULL COMMENT '折扣上线',`reduce_limit` decimal(10,2) DEFAULT NULL COMMENT '减免金额上线',`duties` varchar(36) DEFAULT NULL COMMENT '职务',`sort_no` int(11) DEFAULT NULL COMMENT '排序',`enable_flag` varchar(18) DEFAULT NULL COMMENT '是否有效',`created_time` datetime DEFAULT NULL COMMENT '创建时间',`updated_time` datetime DEFAULT NULL COMMENT '创建时间',`sharding_id` bigint(18) DEFAULT NULL COMMENT '分库id',PRIMARY KEY (`id`)
) 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图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tglqWQot-1636200518349)(image/image-20210515153606074.png)]

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

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

UserFace【model-shop-interface】:用户dubbo接口定义

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

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

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

3.1、UserFace接口

UserFace:用户dubbo接口定义

package com.itheima.restkeeper;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.req.UserVo;import java.util.List;/*** @Description:用户dubbo接口*/
public interface UserFace {/*** @param userVo   查询条件* @param pageNum  当前页* @param pageSize 每页条数* @return Page<UserVo>* @Description 用户列表*/Page<UserVo> findUserVoPage(UserVo userVo, int pageNum, int pageSize);/*** @param userVo 对象信息* @return UserVo* @Description 创建用户*/UserVo createUser(UserVo userVo);/*** @param userVo 对象信息* @return Boolean* @Description 修改用户*/Boolean updateUser(UserVo userVo);/*** @param checkedIds 选择对象信息Id* @return Boolean* @Description 删除用户*/Boolean deleteUser(String[] checkedIds);/*** @param userId 选择对象信息Id* @return* @Description 查找用户*/UserVo findUserByUserId(Long userId);/*** @return List<UserVo>* @Description 查找用户list*/List<UserVo> findUserVoList();
}

3.2、UserFaceImpl接口实现

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

package com.itheima.restkeeper.face;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.UserFace;
import com.itheima.restkeeper.pojo.Role;
import com.itheima.restkeeper.pojo.User;
import com.itheima.restkeeper.req.UserVo;
import com.itheima.restkeeper.service.IUserAdapterService;
import com.itheima.restkeeper.service.IUserService;
import com.itheima.restkeeper.utils.BeanConv;
import com.itheima.restkeeper.utils.EmptyUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.apache.dubbo.config.annotation.Method;
import org.springframework.beans.factory.annotation.Autowired;import java.util.ArrayList;
import java.util.List;/*** @ClassName UserFaceImpl.java* @Description TODO*/
@Slf4j
@DubboService(version = "${dubbo.application.version}",timeout = 5000,methods ={@Method(name = "findUserVoPage",retries = 2),@Method(name = "createUser",retries = 0),@Method(name = "updateUser",retries = 0),@Method(name = "deleteUser",retries = 0),@Method(name = "updateUserEnableFlag",retries = 0)})
public class UserFaceImpl implements UserFace {@AutowiredIUserService userService;@AutowiredIUserAdapterService userAdapterService;@Overridepublic Page<UserVo> findUserVoPage(UserVo userVo, int pageNum, int pageSize) {//查询Page<User>用户分页Page<User> page = userService.findUserVoPage(userVo, pageNum, pageSize);//转化Page<User>为Page<UserVo>Page<UserVo> pageVo = new Page<>();BeanConv.toBean(page,pageVo);//转换List<User>为 List<UserVo>List<User> userList = page.getRecords();List<UserVo> userVoList = BeanConv.toBeanList(userList,UserVo.class);//查询角色信息if (!EmptyUtil.isNullOrEmpty(userList)){userVoList.forEach(n->{List<Role> roles = userAdapterService.findRoleByUserId(n.getId());List<String> roleIdList = new ArrayList<>();for (Role role : roles) {roleIdList.add(String.valueOf(role.getId()));}String[] roleIds = new String[roleIdList.size()];roleIdList.toArray(roleIds);n.setHasRoleIds(roleIds);});}//返回结果pageVo.setRecords(userVoList);return pageVo;}@Overridepublic UserVo createUser(UserVo userVo) {return BeanConv.toBean( userService.createUser(userVo),UserVo.class);}@Overridepublic Boolean updateUser(UserVo userVo) {return userService.updateUser(userVo);}@Overridepublic Boolean deleteUser(String[] checkedIds) {return userService.deleteUser(checkedIds);}@Overridepublic UserVo findUserByUserId(Long userId) {return BeanConv.toBean(userService.getById(userId),UserVo.class);}@Overridepublic List<UserVo> findUserVoList() {return BeanConv.toBeanList(userService.findUserVoList(),UserVo.class);}}

3.3、UserController类

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

package com.itheima.restkeeper.web;import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itheima.restkeeper.AffixFace;
import com.itheima.restkeeper.UserFace;
import com.itheima.restkeeper.basic.ResponseWrap;
import com.itheima.restkeeper.enums.UserEnum;
import com.itheima.restkeeper.exception.ProjectException;
import com.itheima.restkeeper.req.AffixVo;
import com.itheima.restkeeper.req.UserVo;
import com.itheima.restkeeper.utils.EmptyUtil;
import com.itheima.restkeeper.utils.ExceptionsUtil;
import com.itheima.restkeeper.utils.ResponseWrapBuild;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;import java.util.List;
import java.util.stream.Collectors;/*** @ClassName UserController.java* @Description 用户Controller*/
@RestController
@RequestMapping("user")
@Slf4j
@Api(tags = "用户controller")
public class UserController {@DubboReference(version = "${dubbo.application.version}",check = false)UserFace userFace;@DubboReference(version = "${dubbo.application.version}",check = false)AffixFace affixFace;@AutowiredBCryptPasswordEncoder bCryptPasswordEncoder;/*** @Description 用户列表* @param userVo 查询条件* @return*/@PostMapping("page/{pageNum}/{pageSize}")@ApiOperation(value = "查询用户分页",notes = "查询用户分页")@ApiImplicitParams({@ApiImplicitParam(name = "userVo",value = "用户查询对象",required = true,dataType = "UserVo"),@ApiImplicitParam(paramType = "path",name = "pageNum",value = "页码",dataType = "Integer"),@ApiImplicitParam(paramType = "path",name = "pageSize",value = "每页条数",dataType = "Integer")})public ResponseWrap<Page<UserVo>> findUserVoPage(@RequestBody UserVo userVo,@PathVariable("pageNum") int pageNum,@PathVariable("pageSize") int pageSize) throws ProjectException {try {//查询用户分页Page<UserVo> userVoPage = userFace.findUserVoPage(userVo, pageNum, pageSize);//用户不为空,则处理用户头像附件if (!EmptyUtil.isNullOrEmpty(userVoPage)&&!EmptyUtil.isNullOrEmpty(userVoPage.getRecords())){List<UserVo> userVoList = userVoPage.getRecords();userVoList.forEach(n->{List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(n.getId());if (!EmptyUtil.isNullOrEmpty(affixVoList)){n.setAffixVo(affixVoList.get(0));}});//回填用户头像附件userVoPage.setRecords(userVoList);}return ResponseWrapBuild.build(UserEnum.SUCCEED,userVoPage);} catch (Exception e) {log.error("查询用户列表异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.PAGE_FAIL);}}/*** @Description 注册用户* @param userVo 对象信息* @return*/@PostMapping@ApiOperation(value = "注册用户",notes = "注册用户")@ApiImplicitParam(name = "userVo",value = "用户对象",required = true,dataType = "UserVo")ResponseWrap<UserVo> registerUser(@RequestBody UserVo userVo) throws ProjectException {try {String plainPassword = userVo.getPassword();//明文转密文密码,必须要加{bcrypt}要不认证不通过String password = "{bcrypt}"+bCryptPasswordEncoder.encode(plainPassword);userVo.setPassword(password);//注册用户UserVo userVoResult = userFace.createUser(userVo);//绑定附件if (!EmptyUtil.isNullOrEmpty(userVoResult)){affixFace.bindBusinessId(AffixVo.builder().businessId(userVoResult.getId()).id(userVo.getAffixVo().getId()).build());}//回填用户头像附加userVoResult.setAffixVo(AffixVo.builder().pathUrl(userVo.getAffixVo().getPathUrl()).businessId(userVoResult.getId()).id(userVo.getAffixVo().getId()).build());//补全角色信息userVoResult.setHasRoleIds(userVo.getHasRoleIds());return ResponseWrapBuild.build(UserEnum.SUCCEED,userVoResult);} catch (Exception e) {log.error("保存用户异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.CREATE_FAIL);}}/*** @Description 修改用户* @param userVo 对象信息* @return*/@PatchMapping@ApiOperation(value = "修改用户",notes = "修改用户")@ApiImplicitParam(name = "userVo",value = "用户对象",required = true,dataType = "UserVo")ResponseWrap<Boolean> updateUser(@RequestBody UserVo userVo) throws ProjectException {try {//修改用户信息Boolean flag = userFace.updateUser(userVo);//修改成功,修改用户头像附加if (flag){List<AffixVo> affixVoList = affixFace.findAffixVoByBusinessId(userVo.getId());List<Long> affixIds = affixVoList.stream().map(AffixVo::getId).collect(Collectors.toList());if (!affixIds.contains(userVo.getAffixVo().getId())){//删除图片flag = affixFace.deleteAffixVoByBusinessId(userVo.getId());//绑定新图片affixFace.bindBusinessId(AffixVo.builder().businessId(userVo.getId()).id(userVo.getAffixVo().getId()).build());}}//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);} catch (Exception e) {log.error("保存用户异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.UPDATE_FAIL);}}/*** @Description 删除用户* @param userVo 查询对象* @return*/@DeleteMapping@ApiOperation(value = "删除用户",notes = "删除用户")@ApiImplicitParam(name = "userVo",value = "用户查询对象",required = true,dataType = "UserVo")ResponseWrap<Boolean> deleteUser(@RequestBody UserVo userVo ) throws ProjectException {try {//删除选择用户String[] checkedIds = userVo.getCheckedIds();Boolean flag = userFace.deleteUser(checkedIds);//删除图片for (String checkedId : checkedIds) {affixFace.deleteAffixVoByBusinessId(Long.valueOf(checkedId));}//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);} catch (Exception e) {log.error("删除用户异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.DELETE_FAIL);}}/*** @Description 查找用户* @param userId 登录名* @return*/@GetMapping("select-by-userId/{userId}")@ApiOperation(value = "查找用户",notes = "查找用户")@ApiImplicitParam(paramType = "path",name = "userId",value = "用户Id",example = "1",dataType = "Long")ResponseWrap<UserVo> findUserByUserId(@PathVariable("userId") Long userId) throws ProjectException {try {//查找用户信息UserVo userVo = userFace.findUserByUserId(userId);//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,userVo);} catch (Exception e) {log.error("查找用户所有角色异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.SELECT_USER_FAIL);}}/*** @Description 查找用户list* @return*/@GetMapping("select-list")@ApiOperation(value = "查找用户list",notes = "查找用户list")ResponseWrap<List<UserVo>> findUserVoList() throws ProjectException {try {//查询所有有效的用户信息List<UserVo> list = userFace.findUserVoList();//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,list);} catch (Exception e) {log.error("查找用户list异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.SELECT_USER_LIST_FAIL);}}@PostMapping("update-user-enableFlag")@ApiOperation(value = "修改用户状态",notes = "修改用户状态")@ApiImplicitParam(name = "userVo",value = "用户查询对象",required = true,dataType = "UserVo")ResponseWrap<Boolean> updateUserEnableFlag(@RequestBody UserVo userVo) throws ProjectException {try {//修改用户状态Boolean flag = userFace.updateUser(userVo);//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);} catch (Exception e) {log.error("修改用户状态异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.UPDATE_FAIL);}}@PostMapping("rest-password")@ApiOperation(value = "重置用户密码",notes = "重置用户密码")@ApiImplicitParam(name = "userVo",value = "用户对象",required = true,dataType = "UserVo")ResponseWrap<Boolean> restPssword(@RequestBody UserVo userVo) throws ProjectException {//必须要加{bcrypt}要不认证不通过String password = "{bcrypt}"+bCryptPasswordEncoder.encode("88488");userVo.setPassword(password);try {//修改用户密码Boolean flag = userFace.updateUser(userVo);//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);} catch (Exception e) {log.error("重置用户密码:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.UPDATE_FAIL);}}}eWrap<UserVo> findUserByUserId(@PathVariable("userId") Long userId) throws ProjectException {try {//查找用户信息UserVo userVo = userFace.findUserByUserId(userId);//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,userVo);} catch (Exception e) {log.error("查找用户所有角色异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.SELECT_USER_FAIL);}}/*** @Description 查找用户list* @return*/@GetMapping("select-list")@ApiOperation(value = "查找用户list",notes = "查找用户list")ResponseWrap<List<UserVo>> findUserVoList() throws ProjectException {try {//查询所有有效的用户信息List<UserVo> list = userFace.findUserVoList();//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,list);} catch (Exception e) {log.error("查找用户list异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.SELECT_USER_LIST_FAIL);}}@PostMapping("update-user-enableFlag")@ApiOperation(value = "修改用户状态",notes = "修改用户状态")@ApiImplicitParam(name = "userVo",value = "用户查询对象",required = true,dataType = "UserVo")ResponseWrap<Boolean> updateUserEnableFlag(@RequestBody UserVo userVo) throws ProjectException {try {//修改用户状态Boolean flag = userFace.updateUser(userVo);//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);} catch (Exception e) {log.error("修改用户状态异常:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.UPDATE_FAIL);}}@PostMapping("rest-password")@ApiOperation(value = "重置用户密码",notes = "重置用户密码")@ApiImplicitParam(name = "userVo",value = "用户对象",required = true,dataType = "UserVo")ResponseWrap<Boolean> restPssword(@RequestBody UserVo userVo) throws ProjectException {//必须要加{bcrypt}要不认证不通过String password = "{bcrypt}"+bCryptPasswordEncoder.encode("88488");userVo.setPassword(password);try {//修改用户密码Boolean flag = userFace.updateUser(userVo);//返回结果return ResponseWrapBuild.build(UserEnum.SUCCEED,flag);} catch (Exception e) {log.error("重置用户密码:{}", ExceptionsUtil.getStackTraceAsString(e));throw new ProjectException(UserEnum.UPDATE_FAIL);}}}

restkeeper相关推荐

  1. Jenkins从下载到部署项目的流程

    Jenkins安装配置 1.1 Jenkins介绍 Jenkins 是一款流行的开源持续集成(Continuous Integration)工具,广泛用于项目开发,具有自动化构建.测试和部署等功能.官 ...

  2. 餐掌柜SaaS项目实战-2022分布式微服务

    首先我们应该理解什么是SAAS系统.SaaS是Software-as-a-Service(软件即服务)的简称,随着互联网技术的发展和应用软件的成熟, 在21世纪开始兴起的一种完全创新的软件应用模式. ...

  3. Java 前后端分离业务封装 对后端返回值进行封装 PageVO封装

    遇到前后端业务需要不一致时对Controller返回结果进行封装 后端返回结果 前后端分离后 web 端要求结果 {"counts": 2694,"pagesize&qu ...

  4. 【项目】基于SaaS的餐掌柜项目实战 阶段一 基于SaaS的餐掌柜项目实战 第1章 基础架构搭建 1 餐掌柜需求分析

    [项目]基于SaaS的餐掌柜项目实战 文章目录 [项目]基于SaaS的餐掌柜项目实战 阶段一 基于SaaS的餐掌柜项目实战 第1章 基础架构搭建 1 餐掌柜需求分析 1.1 餐饮行业分析 1.2 餐饮 ...

最新文章

  1. 据说这是大多数人【减肥】的真实写照
  2. git 合并代码_git的几种实用操作(合并代码与暂存复原代码)
  3. Ember——在构建Ember应用程序时,我们会使用到六个主要部件:应用程序(Application)、模型(Model)、视图(View)、模板(Template)、路由(...
  4. Caffe常用层参数介绍
  5. android通过Jni加载so库遇到UnsatisfiedLinkError问题!!!
  6. vue-day01-vue模板语法
  7. 可视化流程设计器 Activiti Designer
  8. windows系统bat批处理 mysql 脚本启动关闭
  9. 【BZOJ 3172】单词
  10. 数据结构 WSADATA
  11. kinetis FTM 分析笔记
  12. Docker数据卷映射
  13. 记YY的一次面试经历
  14. 聊天气泡图片的动态拉伸、适配与镜像
  15. 编程列入高考-青少儿编程学习-Python那些事
  16. 永磁同步电机的标么值系统
  17. 台湾评论大陆量子计算机,台湾媒体盛赞大陆工业科研水平 称台湾人对大陆懵懂无知...
  18. [附源码]计算机毕业设计JAVA电影影评网
  19. MySQL导入数据报Got a packet bigger than‘max_allowed_packet’bytes错误的解决方法
  20. 刷题日记 acwing 2058笨拙的手指 位运算的运用,迭代器的使用,秦九稍(别的进制转化为10进制)

热门文章

  1. Elasticsearch安装拼音插件结合IK中文分词+拼音(在线+离线)
  2. The server encountered an internal error that prevented it from fulfilling this request 的解决方法
  3. 二叉树前序中序后序javascript_C++实现二叉树(建树,前序,中序,后序)递归和非递归实现...
  4. tensorflow2视力表E字符(学习笔记)--制作自己的数据集
  5. 程序员常用DOS命令
  6. gulp--解决watching xx.html : watch task has to be a function的问题
  7. 关于在ubuntu下用docker部署Django卡在 Watching for file changes with StatReloader 不动了 以及 run后无法访问web 的问题
  8. 算法整理八——回溯算法
  9. union和unionall区别
  10. 用Python写一个超级玛丽游戏