第2章 分布式文件存储FastDFS

学习目标理

  • 理解FastDFS工作流程

    1. 分布式文件管理系统
    2. 文件上传
    3. 文件下载
    4. 文件删除
    5. 文件缓存控制
  • 搭建文件上传微服务

  • 相册管理(实战)
  • 规格参数管理(实战)
  • 商品分类管理(实战)

    1 FastDFS

    1.1 FastDFS简介

    1.1.1 FastDFS体系结构

    FastDFS是一个开源的轻量级分布式文件系统,它对文件进行管理,功能包括:文件存储、文件同步、文件访问(文件上传、文件下载)等,解决了大容量存储和负载均衡的问题。特别适合以文件为载体的在线服务,如相册网站、视频网站等等。
    FastDFS为互联网量身定制,充分考虑了冗余备份、负载均衡、线性扩容等机制,并注重高可用、高性能等指标,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
    FastDFS 架构包括 Tracker server 和 Storage server。客户端请求 Tracker server 进行文件上传、下载,通过Tracker server 调度最终由 Storage server 完成文件上传和下载。
    Tracker server 作用是负载均衡和调度,通过 Tracker server 在文件上传时可以根据一些策略找到Storage server 提供文件上传服务。可以将 tracker 称为追踪服务器或调度服务器。Storage server 作用是文件存储,客户端上传的文件最终存储在 Storage 服务器上,Storageserver 没有实现自己的文件系统而是利用操作系统的文件系统来管理文件。可以将storage称为存储服务器。
    第2天 分布式文件存储FastDFS - 图1

    1.1.2 上传流程

    第2天 分布式文件存储FastDFS - 图2
    客户端上传文件后存储服务器将文件 ID 返回给客户端,此文件 ID 用于以后访问该文件的索引信息。文件索引信息包括:组名,虚拟磁盘路径,数据两级目录,文件名。
    第2天 分布式文件存储FastDFS - 图3
    组名:文件上传后所在的 storage 组名称,在文件上传成功后有storage 服务器返回,需要客户端自行保存。
    虚拟磁盘路径:storage 配置的虚拟路径,与磁盘选项store_path对应。如果配置了
    store_path0 则是 M00,如果配置了 store_path1 则是 M01,以此类推。
    数据两级目录:storage 服务器在每个虚拟磁盘路径下创建的两级目录,用于存储数据
    文件。
    *文件名
    :与文件上传时不同。是由存储服务器根据特定信息生成,文件名包含:源存储
    服务器 IP 地址、文件创建时间戳、文件大小、随机数和文件拓展名等信息。

    1.2 FastDFS搭建

    1.2.1 安装FastDFS镜像

    我们使用Docker搭建FastDFS的开发环境,虚拟机中已经下载了fastdfs的镜像,可以通过docker images查看,如下图:
    第2天 分布式文件存储FastDFS - 图4
    拉取镜像(已经下载了该镜像,大家无需下载了)

    1. docker pull morunchang/fastdfs

    运行tracker

    1. docker run -d --name tracker --net=host morunchang/fastdfs sh tracker.sh

    运行storage

    1. docker run -d --name storage --net=host -e TRACKER_IP=192.168.211.132:22122 -e GROUP_NAME=group1 morunchang/fastdfs sh storage.sh
  • 使用的网络模式是–net=host, 192.168.211.132是宿主机的IP

  • group1是组名,即storage的组
  • 如果想要增加新的storage服务器,再次运行该命令,注意更换 新组名

    1.2.2 配置Nginx

    Nginx在这里主要提供对FastDFS图片访问的支持,Docker容器中已经集成了Nginx,我们需要修改nginx的配置,进入storage的容器内部,修改nginx.conf
    1. docker exec -it storage /bin/bash
    进入后
    1. vi /etc/nginx/conf/nginx.conf
    添加以下内容
    第2天 分布式文件存储FastDFS - 图5
    上图配置如下:
    1. location ~ /M00 {
    2. root /data/fast_data/data;
    3. ngx_fastdfs_module;
    4. }
    禁止缓存(该步骤不需要做!保留缓存更好!):
    1. add_header Cache-Control no-store;
    退出容器
    1. exit
    重启storage容器
    1. docker restart storage
    查看启动容器docker ps
    1. 9f2391f73d97 morunchang/fastdfs "sh storage.sh" 12 minutes ago Up 12 seconds storage
    2. e22a3c7f95ea morunchang/fastdfs "sh tracker.sh" 13 minutes ago Up 13 minutes tracker
    开启启动设置
    1. docker update --restart=always tracker
    2. docker update --restart=always storage

    1.3 文件存储微服务

    创建文件管理微服务changgou-service-file,该工程主要用于实现文件上传以及文件删除等功能。

    1.3.1 pom.xml依赖

    修改pom.xml,引入依赖
    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <parent>
    6. <artifactId>changgou-service</artifactId>
    7. <groupId>com.changgou</groupId>
    8. <version>1.0-SNAPSHOT</version>
    9. </parent>
    10. <modelVersion>4.0.0</modelVersion>
    11. <artifactId>changgou-service-file</artifactId>
    12. <description>文件上传工程</description>
    13. <!--依赖包-->
    14. <dependencies>
    15. <dependency>
    16. <groupId>net.oschina.zcx7878</groupId>
    17. <artifactId>fastdfs-client-java</artifactId>
    18. <version>1.27.0.0</version>
    19. </dependency>
    20. <dependency>
    21. <groupId>com.changgou</groupId>
    22. <artifactId>changgou-common</artifactId>
    23. <version>1.0-SNAPSHOT</version>
    24. </dependency>
    25. </dependencies>
    26. </project>

    1.3.2 FastDFS配置

    在resources文件夹下创建fasfDFS的配置文件fdfs_client.conf
    1. connect_timeout=60
    2. network_timeout=60
    3. charset=UTF-8
    4. http.tracker_http_port=8080
    5. tracker_server=192.168.211.132:22122
    connect_timeout:连接超时时间,单位为秒。
    network_timeout:通信超时时间,单位为秒。发送或接收数据时。假设在超时时间后还不能发送或接收数据,则本次网络通信失败
    charset: 字符集
    http.tracker_http_port :.tracker的http端口
    tracker_server: tracker服务器IP和端口设置

    1.3.3 application.yml配置

    在resources文件夹下创建application.yml
    1. spring:
    2. servlet:
    3. multipart:
    4. max-file-size: 10MB
    5. max-request-size: 10MB
    6. application:
    7. name: file
    8. server:
    9. port: 18082
    10. eureka:
    11. client:
    12. service-url:
    13. defaultZone: http://127.0.0.1:7001/eureka
    14. instance:
    15. prefer-ip-address: true
    16. feign:
    17. hystrix:
    18. enabled: true
    max-file-size是单个文件大小,max-request-size是设置总上传的数据大小

    1.3.4 启动类

    创建com.changgou包,创建启动类FileApplication
    1. @SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
    2. @EnableEurekaClient
    3. public class FileApplication {
    4. public static void main(String[] args) {
    5. SpringApplication.run(FileApplication.class);
    6. }
    7. }
    这里禁止了DataSource的加载创建。

    1.4 文件上传

    1.4.1 文件信息封装

    文件上传一般都有文件的名字、文件的内容、文件的扩展名、文件的md5值、文件的作者等相关属性,我们可以创建一个对象封装这些属性,代码如下:
    创建com.changgou.file.FastDFSFile代码如下:
    1. public class FastDFSFile implements Serializable {
    2. //文件名字
    3. private String name;
    4. //文件内容
    5. private byte[] content;
    6. //文件扩展名
    7. private String ext;
    8. //文件MD5摘要值
    9. private String md5;
    10. //文件创建作者
    11. private String author;
    12. public FastDFSFile(String name, byte[] content, String ext, String md5, String author) {
    13. this.name = name;
    14. this.content = content;
    15. this.ext = ext;
    16. this.md5 = md5;
    17. this.author = author;
    18. }
    19. public FastDFSFile(String name, byte[] content, String ext) {
    20. this.name = name;
    21. this.content = content;
    22. this.ext = ext;
    23. }
    24. public FastDFSFile() {
    25. }
    26. //..get..set..toString
    27. }
    (可选)测试文件相关操作:
    1. package com.changgou.file.test;
    2. import org.csource.fastdfs.*;
    3. import org.junit.Test;
    4. import java.io.*;
    5. import java.net.InetSocketAddress;
    6. /**
    7. * 描述
    8. *
    9. * @author 三国的包子
    10. * @version 1.0
    11. * @package PACKAGE_NAME *
    12. * @since 1.0
    13. */
    14. public class FastdfsClientTest {
    15. /**
    16. * 文件上传
    17. *
    18. * @throws Exception
    19. */
    20. @Test
    21. public void upload() throws Exception {
    22. //加载全局的配置文件
    23. ClientGlobal.init("C:\\Users\\Administrator\\IdeaProjects\\beike\\changgou\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf");
    24. //创建TrackerClient客户端对象
    25. TrackerClient trackerClient = new TrackerClient();
    26. //通过TrackerClient对象获取TrackerServer信息
    27. TrackerServer trackerServer = trackerClient.getConnection();
    28. //获取StorageClient对象
    29. StorageClient storageClient = new StorageClient(trackerServer, null);
    30. //执行文件上传
    31. String[] jpgs = storageClient.upload_file("C:\\Users\\Administrator\\Pictures\\5b13cd6cN8e12d4aa.jpg", "jpg", null);
    32. for (String jpg : jpgs) {
    33. System.out.println(jpg);
    34. }
    35. }
    36. @Test
    37. public void delete() throws Exception {
    38. //加载全局的配置文件
    39. ClientGlobal.init("C:\\Users\\Administrator\\IdeaProjects\\beike\\changgou\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf");
    40. //创建TrackerClient客户端对象
    41. TrackerClient trackerClient = new TrackerClient();
    42. //通过TrackerClient对象获取TrackerServer信息
    43. TrackerServer trackerServer = trackerClient.getConnection();
    44. //获取StorageClient对象
    45. StorageClient storageClient = new StorageClient(trackerServer, null);
    46. //执行文件上传
    47. int group1 = storageClient.delete_file("group1", "M00/00/00/wKjThF1VEiyAJ0xzAANdC6JX9KA522.jpg");
    48. System.out.println(group1);
    49. }
    50. @Test
    51. public void download() throws Exception {
    52. //加载全局的配置文件
    53. ClientGlobal.init("C:\\Users\\Administrator\\IdeaProjects\\beike\\changgou\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf");
    54. //创建TrackerClient客户端对象
    55. TrackerClient trackerClient = new TrackerClient();
    56. //通过TrackerClient对象获取TrackerServer信息
    57. TrackerServer trackerServer = trackerClient.getConnection();
    58. //获取StorageClient对象
    59. StorageClient storageClient = new StorageClient(trackerServer, null);
    60. //执行文件上传
    61. byte[] bytes = storageClient.download_file("group1", "M00/00/00/wKjThF1VFfKAJRJDAANdC6JX9KA980.jpg");
    62. File file = new File("D:\\ceshi\\1234.jpg");
    63. FileOutputStream fileOutputStream = new FileOutputStream(file);
    64. BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
    65. bufferedOutputStream.write(bytes);
    66. bufferedOutputStream.close();
    67. fileOutputStream.close();
    68. }
    69. //获取文件的信息数据
    70. @Test
    71. public void getFileInfo() throws Exception {
    72. //加载全局的配置文件
    73. ClientGlobal.init("C:\\Users\\Administrator\\IdeaProjects\\beike\\changgou\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf");
    74. //创建TrackerClient客户端对象
    75. TrackerClient trackerClient = new TrackerClient();
    76. //通过TrackerClient对象获取TrackerServer信息
    77. TrackerServer trackerServer = trackerClient.getConnection();
    78. //获取StorageClient对象
    79. StorageClient storageClient = new StorageClient(trackerServer, null);
    80. //执行文件上传
    81. FileInfo group1 = storageClient.get_file_info("group1", "M00/00/00/wKjThF1VFfKAJRJDAANdC6JX9KA980.jpg");
    82. System.out.println(group1);
    83. }
    84. }

    1.4.2 文件操作

    创建com.changgou.util.FastDFSClient类,在该类中实现FastDFS信息获取以及文件的相关操作,代码如下:
    (1)初始化Tracker信息
    com.changgou.util.FastDFSClient类中初始化Tracker信息,在类中添加如下静态块:
    1. /***
    2. * 初始化tracker信息
    3. */
    4. static {
    5. try {
    6. //获取tracker的配置文件fdfs_client.conf的位置
    7. String filePath = new ClassPathResource("fdfs_client.conf").getPath();
    8. //加载tracker配置信息
    9. ClientGlobal.init(filePath);
    10. } catch (Exception e) {
    11. e.printStackTrace();
    12. }
    13. }
    (2)文件上传
    在类中添加如下方法实现文件上传:
    1. /****
    2. * 文件上传
    3. * @param file : 要上传的文件信息封装->FastDFSFile
    4. * @return String[]
    5. * 1:文件上传所存储的组名
    6. * 2:文件存储路径
    7. */
    8. public static String[] upload(FastDFSFile file){
    9. //获取文件作者
    10. NameValuePair[] meta_list = new NameValuePair[1];
    11. meta_list[0] =new NameValuePair(file.getAuthor());
    12. /***
    13. * 文件上传后的返回值
    14. * uploadResults[0]:文件上传所存储的组名,例如:group1
    15. * uploadResults[1]:文件存储路径,例如:M00/00/00/wKjThF0DBzaAP23MAAXz2mMp9oM26.jpeg
    16. */
    17. String[] uploadResults = null;
    18. try {
    19. //创建TrackerClient客户端对象
    20. TrackerClient trackerClient = new TrackerClient();
    21. //通过TrackerClient对象获取TrackerServer信息
    22. TrackerServer trackerServer = trackerClient.getConnection();
    23. //获取StorageClient对象
    24. StorageClient storageClient = new StorageClient(trackerServer, null);
    25. //执行文件上传
    26. uploadResults = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
    27. } catch (Exception e) {
    28. e.printStackTrace();
    29. }
    30. return uploadResults;
    31. }
    (3)获取文件信息
    在类中添加如下方法实现获取文件信息:
    1. /***
    2. * 获取文件信息
    3. * @param groupName:组名
    4. * @param remoteFileName:文件存储完整名
    5. */
    6. public static FileInfo getFile(String groupName,String remoteFileName){
    7. try {
    8. //创建TrackerClient对象
    9. TrackerClient trackerClient = new TrackerClient();
    10. //通过TrackerClient获得TrackerServer信息
    11. TrackerServer trackerServer =trackerClient.getConnection();
    12. //通过TrackerServer获取StorageClient对象
    13. StorageClient storageClient = new StorageClient(trackerServer,null);
    14. //获取文件信息
    15. return storageClient.get_file_info(groupName,remoteFileName);
    16. } catch (Exception e) {
    17. e.printStackTrace();
    18. }
    19. return null;
    20. }
    (4)文件下载
    在类中添加如下方法实现文件下载:
    1. //下载图片
    2. public static byte[] downFile(String groupName, String remoteFileName) throws Exception {
    3. //1.创建一个配置文件 用于填写服务端的ip和端口
    4. //2.加载配置文件 建立链接
    5. // ClientGlobal.init("C:\\Users\\admin\\IdeaProjects\\98\\changgou98\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf");
    6. //3.创建trackerClient
    7. TrackerClient trackerClient = new TrackerClient();
    8. //4.根据trackerclient获取到链接对象 trackerServer
    9. TrackerServer trackerServer = trackerClient.getConnection();
    10. //5.创建storageServer对象 设置null
    11. //6.创建stroageClient --->提供了很多的操作图片的API的代码(上传图片,下载 ,删除)
    12. StorageClient storageClient = new StorageClient(trackerServer, null);
    13. //7.下在图片
    14. //参数1 指定要下载的组名
    15. //参数2 指定要下载的远程文件路径
    16. byte[] group1s = storageClient.download_file(groupName, remoteFileName);
    17. return group1s;
    18. }
    (5)文件删除实现
    1. /***
    2. * 文件删除实现
    3. * @param groupName:组名
    4. * @param remoteFileName:文件存储完整名
    5. */
    6. //删除图片
    7. public static boolean deleteFile(String groupName, String remoteFileName) throws Exception {
    8. //1.创建一个配置文件 用于填写服务端的ip和端口
    9. //2.加载配置文件 建立链接
    10. //ClientGlobal.init("C:\\Users\\admin\\IdeaProjects\\98\\changgou98\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf");
    11. //3.创建trackerClient
    12. TrackerClient trackerClient = new TrackerClient();
    13. //4.根据trackerclient获取到链接对象 trackerServer
    14. TrackerServer trackerServer = trackerClient.getConnection();
    15. //5.创建storageServer对象 设置null
    16. //6.创建stroageClient --->提供了很多的操作图片的API的代码(上传图片,下载 ,删除)
    17. StorageClient storageClient = new StorageClient(trackerServer, null);
    18. int group1 = storageClient.delete_file(groupName, remoteFileName);
    19. if (group1 == 0) {
    20. return true;
    21. } else {
    22. return false;
    23. }
    24. }
    (6)获取组信息(了解)
    1. /***
    2. * 获取组信息
    3. * @param groupName :组名
    4. */
    5. public static StorageServer getStorages(String groupName){
    6. try {
    7. //创建TrackerClient对象
    8. TrackerClient trackerClient = new TrackerClient();
    9. //通过TrackerClient获取TrackerServer对象
    10. TrackerServer trackerServer = trackerClient.getConnection();
    11. //通过trackerClient获取Storage组信息
    12. return trackerClient.getStoreStorage(trackerServer,groupName);
    13. } catch (Exception e) {
    14. e.printStackTrace();
    15. }
    16. return null;
    17. }
    (7)根据文件组名和文件存储路径获取Storage服务的IP、端口信息(了解)
    1. /***
    2. * 根据文件组名和文件存储路径获取Storage服务的IP、端口信息
    3. * @param groupName :组名
    4. * @param remoteFileName :文件存储完整名
    5. */
    6. public static ServerInfo[] getServerInfo(String groupName, String remoteFileName){
    7. try {
    8. //创建TrackerClient对象
    9. TrackerClient trackerClient = new TrackerClient();
    10. //通过TrackerClient获取TrackerServer对象
    11. TrackerServer trackerServer = trackerClient.getConnection();
    12. //获取服务信息
    13. return trackerClient.getFetchStorages(trackerServer,groupName,remoteFileName);
    14. } catch (Exception e) {
    15. e.printStackTrace();
    16. }
    17. return null;
    18. }
    (8)获取Tracker服务地址(了解)
    1. /***
    2. * 获取Tracker服务地址
    3. */
    4. public static String getTrackerUrl(){
    5. try {
    6. //创建TrackerClient对象
    7. TrackerClient trackerClient = new TrackerClient();
    8. //通过TrackerClient获取TrackerServer对象
    9. TrackerServer trackerServer = trackerClient.getConnection();
    10. //获取Tracker地址
    11. return "http://"+trackerServer.getInetSocketAddress().getHostString()+":"+ClientGlobal.getG_tracker_http_port();
    12. } catch (IOException e) {
    13. e.printStackTrace();
    14. }
    15. return null;
    16. }
    (9)优化
    我们可以发现,上面所有方法中都会涉及到获取TrackerServer或者StorageClient,我们可以把它们单独抽取出去,分别在类中添加如下2个方法:
    1. /***
    2. * 获取TrackerServer
    3. */
    4. public static TrackerServer getTrackerServer() throws Exception{
    5. //创建TrackerClient对象
    6. TrackerClient trackerClient = new TrackerClient();
    7. //通过TrackerClient获取TrackerServer对象
    8. TrackerServer trackerServer = trackerClient.getConnection();
    9. return trackerServer;
    10. }
    11. /***
    12. * 获取StorageClient
    13. * @return
    14. * @throws Exception
    15. */
    16. public static StorageClient getStorageClient() throws Exception{
    17. //获取TrackerServer
    18. TrackerServer trackerServer = getTrackerServer();
    19. //通过TrackerServer创建StorageClient
    20. StorageClient storageClient = new StorageClient(trackerServer,null);
    21. return storageClient;
    22. }
    修改其他方法,在需要使用TrackerServer和StorageClient的时候,直接调用上面的方法,完整代码如下:
    1. package com.changgou.file.util;
    2. import com.changgou.file.FastDFSFile;
    3. import org.csource.common.MyException;
    4. import org.csource.common.NameValuePair;
    5. import org.csource.fastdfs.ClientGlobal;
    6. import org.csource.fastdfs.StorageClient;
    7. import org.csource.fastdfs.TrackerClient;
    8. import org.csource.fastdfs.TrackerServer;
    9. import org.springframework.core.io.ClassPathResource;
    10. import java.io.ByteArrayInputStream;
    11. import java.io.File;
    12. import java.io.FileOutputStream;
    13. import java.io.IOException;
    14. /**
    15. * @author ljh
    16. * @version 1.0
    17. * @date 2020/9/22 11:57
    18. * @description 标题
    19. * @package com.changgou.file.util
    20. */
    21. public class FastDFSClient {
    22. static {
    23. try {
    24. ClassPathResource resource = new ClassPathResource("fdfs_client.conf");
    25. ClientGlobal.init(resource.getPath());
    26. } catch (IOException e) {
    27. e.printStackTrace();
    28. } catch (MyException e) {
    29. e.printStackTrace();
    30. }
    31. }
    32. //上传图片
    33. public static String[] upload(FastDFSFile file) throws Exception {
    34. //1.创建一个配置文件 用于填写服务端的ip和端口
    35. //2.加载配置文件 建立链接
    36. //3.创建trackerClient
    37. TrackerClient trackerClient = new TrackerClient();
    38. //4.根据trackerclient获取到链接对象 trackerServer
    39. TrackerServer trackerServer = trackerClient.getConnection();
    40. //5.创建storageServer对象 设置null
    41. //6.创建stroageClient --->提供了很多的操作图片的API的代码(上传图片,下载 ,删除)
    42. StorageClient storageClient = new StorageClient(trackerServer, null);
    43. //参数1 指定要上传图片的本地的图片的绝对路径
    44. //参数2 指定要上传图片的图片的扩展名(jpg/png)不要带点
    45. //参数3 指定元数据 指的是 图片的高度 日期,像素...... 可以不给,
    46. NameValuePair[] meta_list = new NameValuePair[]{
    47. //像素 高度 大小
    48. new NameValuePair(file.getName())
    49. };
    50. String[] jpgs = storageClient.upload_file(file.getContent(), file.getExt(), meta_list);
    51. return jpgs;
    52. }
    53. //下载图片
    54. public static byte[] downFile(String groupName, String remoteFileName) throws Exception {
    55. //1.创建一个配置文件 用于填写服务端的ip和端口
    56. //2.加载配置文件 建立链接
    57. // ClientGlobal.init("C:\\Users\\admin\\IdeaProjects\\98\\changgou98\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf");
    58. //3.创建trackerClient
    59. TrackerClient trackerClient = new TrackerClient();
    60. //4.根据trackerclient获取到链接对象 trackerServer
    61. TrackerServer trackerServer = trackerClient.getConnection();
    62. //5.创建storageServer对象 设置null
    63. //6.创建stroageClient --->提供了很多的操作图片的API的代码(上传图片,下载 ,删除)
    64. StorageClient storageClient = new StorageClient(trackerServer, null);
    65. //7.下在图片
    66. //参数1 指定要下载的组名
    67. //参数2 指定要下载的远程文件路径
    68. byte[] group1s = storageClient.download_file(groupName, remoteFileName);
    69. return group1s;
    70. }
    71. //删除图片
    72. public static boolean deleteFile(String groupName, String remoteFileName) throws Exception {
    73. //1.创建一个配置文件 用于填写服务端的ip和端口
    74. //2.加载配置文件 建立链接
    75. //ClientGlobal.init("C:\\Users\\admin\\IdeaProjects\\98\\changgou98\\changgou-parent\\changgou-service\\changgou-service-file\\src\\main\\resources\\fdfs_client.conf");
    76. //3.创建trackerClient
    77. TrackerClient trackerClient = new TrackerClient();
    78. //4.根据trackerclient获取到链接对象 trackerServer
    79. TrackerServer trackerServer = trackerClient.getConnection();
    80. //5.创建storageServer对象 设置null
    81. //6.创建stroageClient --->提供了很多的操作图片的API的代码(上传图片,下载 ,删除)
    82. StorageClient storageClient = new StorageClient(trackerServer, null);
    83. int group1 = storageClient.delete_file(groupName, remoteFileName);
    84. if (group1 == 0) {
    85. return true;
    86. } else {
    87. return false;
    88. }
    89. }
    90. //获取图片的信息 //todo
    91. }

    1.4.3 文件上传

    创建一个FileController,在该控制器中实现文件上传操作,代码如下:
    1. @RestController
    2. public class FileController {
    3. @Value("${pic.url}")
    4. private String picPath;
    5. //1.请求路径
    6. //2.参数
    7. //3.返回值
    8. /**
    9. * 图片上传
    10. *
    11. * @param file
    12. * @return
    13. */
    14. @PostMapping("/upload")
    15. public String upload(MultipartFile file) {
    16. try {
    17. if (!file.isEmpty()) {
    18. //1.获取到文件本身的字节数组
    19. byte[] content = file.getBytes();
    20. //2.获取文件的名称 --》获取图片的后缀
    21. String name = file.getOriginalFilename();//1234.jpg
    22. //3.上传到fastdfs上
    23. //[0] =group1
    24. //[1] =M00/00/00/wKjThF-qeNyATiVHAAAl8vdCW2Y824.png
    25. String[] upload = FastDFSClient.upload(new FastDFSFile(
    26. name,//文件名
    27. content,//文件的本身的字节数组
    28. StringUtils.getFilenameExtension(name)
    29. ));
    30. //4.拼接Ulr
    31. // http://192.168.211.132:8080/group1/M00/00/00/wKjThF-qeNyATiVHAAAl8vdCW2Y824.png
    32. String realPath = picPath+"/"+upload[0]+"/"+upload[1];
    33. //5.返回url给页面
    34. return realPath;
    35. }
    36. } catch (Exception e) {
    37. e.printStackTrace();
    38. }
    39. return null;
    40. }
    41. }
    配置:yaml
    第2天 分布式文件存储FastDFS - 图6

    1.5 Postman测试文件上传

    步骤:
    1、选择post请求方式,输入请求地址 http://localhost:18082/upload
    2、填写Headers
    1. KeyContent-Type
    2. Valuemultipart/form-data
    3、填写body
    选择form-data 然后选择文件file 点击添加文件,最后发送即可。
    第2天 分布式文件存储FastDFS - 图7
    访问[http://192.168.211.132:8080/group1/M00/00/00/wKjThF0DBzaAP23MAAXz2mMp9oM26.jpeg](http://192.168.211.132:8080/group1/M00/00/00/wKjThF0DBzaAP23MAAXz2mMp9oM26.jpeg)如下图
    第2天 分布式文件存储FastDFS - 图8
    注意,这里每次访问的端口是8080端口,访问的端口其实是storage容器的nginx端口,如果想修改该端口可以直接进入到storage容器,然后修改即可。
    1. docker exec -it storage /bin/bash
    2. vi /etc/nginx/conf/nginx.conf
    第2天 分布式文件存储FastDFS - 图9
    修改后重启storage即可根据自己修改的端口访问图片了。

    2 相册管理(实战)

    2.1 需求分析

    相册是用于存储图片的管理单元,我们通常会将商品的图片先上传到相册中,在添加商品时可以直接在相册中选择,获取相册中的图片地址,保存到商品表中。
    前端交互方式见管理后台的静态原型

    2.2 表结构分析

    tb_album 表(相册表
字段名称 字段含义 字段类型 备注
id 编号 BIGINT(20) 主键
title 相册名称 VARCHAR(100)
image 相册封面 VARCHAR(100)
image_items 图片列表 TEXT

表中image_items数据如下示例:

  1. [
  2. {
  3. "url": "http://localhost:9101/img/1.jpg",
  4. "uid": 1548143143154,
  5. "status": "success"
  6. },
  7. {
  8. "url": "http://localhost:9101/img/7.jpg",
  9. "uid": 1548143143155,
  10. "status": "success"
  11. }
  12. ]

2.3 代码实现

2.3.1 Pojo

在changgou-service-goods-api工程中创建com.changgou.goods.pojo.Album,代码如下:

  1. @Table(name="tb_album")
  2. public class Album implements Serializable{
  3. @Id
  4. @GeneratedValue(strategy = GenerationType.IDENTITY)
  5. @Column(name = "id")
  6. private Long id;//编号
  7. @Column(name = "title")
  8. private String title;//相册名称
  9. @Column(name = "image")
  10. private String image;//相册封面
  11. @Column(name = "image_items")
  12. private String imageItems;//图片列表
  13. //get...set...toString..
  14. }

2.3.2 Dao

在changgou-service-goods中创建com.changgou.goods.dao.AlbumMapper接口,代码如下:

  1. public interface AlbumMapper extends Mapper<Album> {
  2. }

2.3.3 业务层

(1)业务层接口
在changgou-service-goods中创建com.changgou.goods.service.AlbumService接口,并添加常用方法,代码如下:

  1. public interface AlbumService {
  2. /***
  3. * Album多条件分页查询
  4. * @param album
  5. * @param page
  6. * @param size
  7. * @return
  8. */
  9. PageInfo<Album> findPage(Album album, int page, int size);
  10. /***
  11. * Album分页查询
  12. * @param page
  13. * @param size
  14. * @return
  15. */
  16. PageInfo<Album> findPage(int page, int size);
  17. /***
  18. * Album多条件搜索方法
  19. * @param album
  20. * @return
  21. */
  22. List<Album> findList(Album album);
  23. /***
  24. * 删除Album
  25. * @param id
  26. */
  27. void delete(Long id);
  28. /***
  29. * 修改Album数据
  30. * @param album
  31. */
  32. void update(Album album);
  33. /***
  34. * 新增Album
  35. * @param album
  36. */
  37. void add(Album album);
  38. /**
  39. * 根据ID查询Album
  40. * @param id
  41. * @return
  42. */
  43. Album findById(Long id);
  44. /***
  45. * 查询所有Album
  46. * @return
  47. */
  48. List<Album> findAll();
  49. }

(2)业务层实现类
在changgou-service-goods中创建com.changgou.goods.service.impl.AlbumServiceImpl,并实现接口方法,代码如下:

  1. @Service
  2. public class AlbumServiceImpl implements AlbumService {
  3. @Autowired
  4. private AlbumMapper albumMapper;
  5. /**
  6. * Album条件+分页查询
  7. * @param album 查询条件
  8. * @param page 页码
  9. * @param size 页大小
  10. * @return 分页结果
  11. */
  12. @Override
  13. public PageInfo<Album> findPage(Album album, int page, int size){
  14. //分页
  15. PageHelper.startPage(page,size);
  16. //搜索条件构建
  17. Example example = createExample(album);
  18. //执行搜索
  19. return new PageInfo<Album>(albumMapper.selectByExample(example));
  20. }
  21. /**
  22. * Album分页查询
  23. * @param page
  24. * @param size
  25. * @return
  26. */
  27. @Override
  28. public PageInfo<Album> findPage(int page, int size){
  29. //静态分页
  30. PageHelper.startPage(page,size);
  31. //分页查询
  32. return new PageInfo<Album>(albumMapper.selectAll());
  33. }
  34. /**
  35. * Album条件查询
  36. * @param album
  37. * @return
  38. */
  39. @Override
  40. public List<Album> findList(Album album){
  41. //构建查询条件
  42. Example example = createExample(album);
  43. //根据构建的条件查询数据
  44. return albumMapper.selectByExample(example);
  45. }
  46. /**
  47. * Album构建查询对象
  48. * @param album
  49. * @return
  50. */
  51. public Example createExample(Album album){
  52. Example example=new Example(Album.class);
  53. Example.Criteria criteria = example.createCriteria();
  54. if(album!=null){
  55. // 编号
  56. if(!StringUtils.isEmpty(album.getId())){
  57. criteria.andEqualTo("id",album.getId());
  58. }
  59. // 相册名称
  60. if(!StringUtils.isEmpty(album.getTitle())){
  61. criteria.andLike("title","%"+album.getTitle()+"%");
  62. }
  63. // 相册封面
  64. if(!StringUtils.isEmpty(album.getImage())){
  65. criteria.andEqualTo("image",album.getImage());
  66. }
  67. // 图片列表
  68. if(!StringUtils.isEmpty(album.getImageItems())){
  69. criteria.andEqualTo("imageItems",album.getImageItems());
  70. }
  71. }
  72. return example;
  73. }
  74. /**
  75. * 删除
  76. * @param id
  77. */
  78. @Override
  79. public void delete(Long id){
  80. albumMapper.deleteByPrimaryKey(id);
  81. }
  82. /**
  83. * 修改Album
  84. * @param album
  85. */
  86. @Override
  87. public void update(Album album){
  88. albumMapper.updateByPrimaryKey(album);
  89. }
  90. /**
  91. * 增加Album
  92. * @param album
  93. */
  94. @Override
  95. public void add(Album album){
  96. albumMapper.insert(album);
  97. }
  98. /**
  99. * 根据ID查询Album
  100. * @param id
  101. * @return
  102. */
  103. @Override
  104. public Album findById(Long id){
  105. return albumMapper.selectByPrimaryKey(id);
  106. }
  107. /**
  108. * 查询Album全部数据
  109. * @return
  110. */
  111. @Override
  112. public List<Album> findAll() {
  113. return albumMapper.selectAll();
  114. }
  115. }

2.3.4 控制层

在changgou-service-service工程中创建com.changgou.goods.controller.AlbumController,代码如下:

  1. @RestController
  2. @RequestMapping("/album")
  3. @CrossOrigin
  4. public class AlbumController {
  5. @Autowired
  6. private AlbumService albumService;
  7. /***
  8. * Album分页条件搜索实现
  9. * @param album
  10. * @param page
  11. * @param size
  12. * @return
  13. */
  14. @PostMapping(value = "/search/{page}/{size}" )
  15. public Result<PageInfo> findPage(@RequestBody(required = false) Album album, @PathVariable int page, @PathVariable int size){
  16. //执行搜索
  17. PageInfo<Album> pageInfo = albumService.findPage(album, page, size);
  18. return new Result(true,StatusCode.OK,"查询成功",pageInfo);
  19. }
  20. /***
  21. * Album分页搜索实现
  22. * @param page:当前页
  23. * @param size:每页显示多少条
  24. * @return
  25. */
  26. @GetMapping(value = "/search/{page}/{size}" )
  27. public Result<PageInfo> findPage(@PathVariable int page, @PathVariable int size){
  28. //分页查询
  29. PageInfo<Album> pageInfo = albumService.findPage(page, size);
  30. return new Result<PageInfo>(true,StatusCode.OK,"查询成功",pageInfo);
  31. }
  32. /***
  33. * 多条件搜索品牌数据
  34. * @param album
  35. * @return
  36. */
  37. @PostMapping(value = "/search" )
  38. public Result<List<Album>> findList(@RequestBody(required = false) Album album){
  39. List<Album> list = albumService.findList(album);
  40. return new Result<List<Album>>(true,StatusCode.OK,"查询成功",list);
  41. }
  42. /***
  43. * 根据ID删除品牌数据
  44. * @param id
  45. * @return
  46. */
  47. @DeleteMapping(value = "/{id}" )
  48. public Result delete(@PathVariable Long id){
  49. albumService.delete(id);
  50. return new Result(true,StatusCode.OK,"删除成功");
  51. }
  52. /***
  53. * 修改Album数据
  54. * @param album
  55. * @param id
  56. * @return
  57. */
  58. @PutMapping(value="/{id}")
  59. public Result update(@RequestBody Album album,@PathVariable Long id){
  60. //设置主键值
  61. album.setId(id);
  62. //修改数据
  63. albumService.update(album);
  64. return new Result(true,StatusCode.OK,"修改成功");
  65. }
  66. /***
  67. * 新增Album数据
  68. * @param album
  69. * @return
  70. */
  71. @PostMapping
  72. public Result add(@RequestBody Album album){
  73. albumService.add(album);
  74. return new Result(true,StatusCode.OK,"添加成功");
  75. }
  76. /***
  77. * 根据ID查询Album数据
  78. * @param id
  79. * @return
  80. */
  81. @GetMapping("/{id}")
  82. public Result<Album> findById(@PathVariable Long id){
  83. //根据ID查询
  84. Album album = albumService.findById(id);
  85. return new Result<Album>(true,StatusCode.OK,"查询成功",album);
  86. }
  87. /***
  88. * 查询Album全部数据
  89. * @return
  90. */
  91. @GetMapping
  92. public Result<Album> findAll(){
  93. List<Album> list = albumService.findAll();
  94. return new Result<Album>(true, StatusCode.OK,"查询成功",list) ;
  95. }
  96. }

3 规格参数模板(实战)

3.1 需求分析

规格参数模板是用于管理规格参数的单元。规格是例如颜色、手机运行内存等信息,参数是例如系统:安卓(Android)后置摄像头像素:2000万及以上 热点:快速充电等信息 。
前端交互方式见管理后台的静态原型

3.2 表结构分析

规格参数模板相关的表有3个
tb_template 表(模板表)

字段名称 字段含义 字段类型 字段长度 备注
id ID INT
name 模板名称 VARCHAR
spec_num 规格数量 INT
para_num 参数数量 INT

tb_spec 表( 规格表)

字段名称 字段含义 字段类型 字段长度 备注
id ID INT
name 名称 VARCHAR
options 规格选项 VARCHAR
seq 排序 INT
template_id 模板ID INT

tb_para 表(参数表)

字段名称 字段含义 字段类型 字段长度 备注
id id INT
name 名称 VARCHAR
options 选项 VARCHAR
seq 排序 INT
template_id 模板ID INT

模板与规格是一对多关系 ,模板与参数是一对多关系

3.3 模板管理

3.3.1 Pojo

在changgou-service-goods-api工程中创建com.changgou.goods.pojo.Template,代码如下:

  1. @Table(name="tb_template")
  2. public class Template implements Serializable{
  3. @Id
  4. @GeneratedValue(strategy = GenerationType.IDENTITY)
  5. @Column(name = "id")
  6. private Integer id;//ID
  7. @Column(name = "name")
  8. private String name;//模板名称
  9. @Column(name = "spec_num")
  10. private Integer specNum;//规格数量
  11. @Column(name = "para_num")
  12. private Integer paraNum;//参数数量
  13. //..get..set..toString
  14. }

3.3.2 Dao

在changgou-service-goods中创建com.changgou.goods.dao.TemplateMapper,代码如下:

  1. public interface TemplateMapper extends Mapper<Template> {
  2. }

3.3.3 业务层

(1)业务层接口
在changgou-service-goods中创建com.changgou.goods.service.TemplateService接口,并添加相关方法,代码如下:

  1. public interface TemplateService {
  2. /***
  3. * Template多条件分页查询
  4. * @param template
  5. * @param page
  6. * @param size
  7. * @return
  8. */
  9. PageInfo<Template> findPage(Template template, int page, int size);
  10. /***
  11. * Template分页查询
  12. * @param page
  13. * @param size
  14. * @return
  15. */
  16. PageInfo<Template> findPage(int page, int size);
  17. /***
  18. * Template多条件搜索方法
  19. * @param template
  20. * @return
  21. */
  22. List<Template> findList(Template template);
  23. /***
  24. * 删除Template
  25. * @param id
  26. */
  27. void delete(Integer id);
  28. /***
  29. * 修改Template数据
  30. * @param template
  31. */
  32. void update(Template template);
  33. /***
  34. * 新增Template
  35. * @param template
  36. */
  37. void add(Template template);
  38. /**
  39. * 根据ID查询Template
  40. * @param id
  41. * @return
  42. */
  43. Template findById(Integer id);
  44. /***
  45. * 查询所有Template
  46. * @return
  47. */
  48. List<Template> findAll();
  49. }

(2)业务层接口实现类
在changgou-service-goods中创建com.changgou.goods.service.impl.TemplateServiceImpl实现类,并实现对应的方法,代码如下:

  1. @Service
  2. public class TemplateServiceImpl implements TemplateService {
  3. @Autowired
  4. private TemplateMapper templateMapper;
  5. /**
  6. * Template条件+分页查询
  7. * @param template 查询条件
  8. * @param page 页码
  9. * @param size 页大小
  10. * @return 分页结果
  11. */
  12. @Override
  13. public PageInfo<Template> findPage(Template template, int page, int size){
  14. //分页
  15. PageHelper.startPage(page,size);
  16. //搜索条件构建
  17. Example example = createExample(template);
  18. //执行搜索
  19. return new PageInfo<Template>(templateMapper.selectByExample(example));
  20. }
  21. /**
  22. * Template分页查询
  23. * @param page
  24. * @param size
  25. * @return
  26. */
  27. @Override
  28. public PageInfo<Template> findPage(int page, int size){
  29. //静态分页
  30. PageHelper.startPage(page,size);
  31. //分页查询
  32. return new PageInfo<Template>(templateMapper.selectAll());
  33. }
  34. /**
  35. * Template条件查询
  36. * @param template
  37. * @return
  38. */
  39. @Override
  40. public List<Template> findList(Template template){
  41. //构建查询条件
  42. Example example = createExample(template);
  43. //根据构建的条件查询数据
  44. return templateMapper.selectByExample(example);
  45. }
  46. /**
  47. * Template构建查询对象
  48. * @param template
  49. * @return
  50. */
  51. public Example createExample(Template template){
  52. Example example=new Example(Template.class);
  53. Example.Criteria criteria = example.createCriteria();
  54. if(template!=null){
  55. // ID
  56. if(!StringUtils.isEmpty(template.getId())){
  57. criteria.andEqualTo("id",template.getId());
  58. }
  59. // 模板名称
  60. if(!StringUtils.isEmpty(template.getName())){
  61. criteria.andLike("name","%"+template.getName()+"%");
  62. }
  63. // 规格数量
  64. if(!StringUtils.isEmpty(template.getSpecNum())){
  65. criteria.andEqualTo("specNum",template.getSpecNum());
  66. }
  67. // 参数数量
  68. if(!StringUtils.isEmpty(template.getParaNum())){
  69. criteria.andEqualTo("paraNum",template.getParaNum());
  70. }
  71. }
  72. return example;
  73. }
  74. /**
  75. * 删除
  76. * @param id
  77. */
  78. @Override
  79. public void delete(Integer id){
  80. templateMapper.deleteByPrimaryKey(id);
  81. }
  82. /**
  83. * 修改Template
  84. * @param template
  85. */
  86. @Override
  87. public void update(Template template){
  88. templateMapper.updateByPrimaryKey(template);
  89. }
  90. /**
  91. * 增加Template
  92. * @param template
  93. */
  94. @Override
  95. public void add(Template template){
  96. templateMapper.insert(template);
  97. }
  98. /**
  99. * 根据ID查询Template
  100. * @param id
  101. * @return
  102. */
  103. @Override
  104. public Template findById(Integer id){
  105. return templateMapper.selectByPrimaryKey(id);
  106. }
  107. /**
  108. * 查询Template全部数据
  109. * @return
  110. */
  111. @Override
  112. public List<Template> findAll() {
  113. return templateMapper.selectAll();
  114. }
  115. }

3.3.4 控制层

在changgou-service-goods中创建com.changgou.goods.controller.TemplateController,代码如下:

  1. @RestController
  2. @RequestMapping("/template")
  3. @CrossOrigin
  4. public class TemplateController {
  5. @Autowired
  6. private TemplateService templateService;
  7. /***
  8. * Template分页条件搜索实现
  9. * @param template
  10. * @param page
  11. * @param size
  12. * @return
  13. */
  14. @PostMapping(value = "/search/{page}/{size}" )
  15. public Result<PageInfo> findPage(@RequestBody(required = false) Template template, @PathVariable int page, @PathVariable int size){
  16. //执行搜索
  17. PageInfo<Template> pageInfo = templateService.findPage(template, page, size);
  18. return new Result(true,StatusCode.OK,"查询成功",pageInfo);
  19. }
  20. /***
  21. * Template分页搜索实现
  22. * @param page:当前页
  23. * @param size:每页显示多少条
  24. * @return
  25. */
  26. @GetMapping(value = "/search/{page}/{size}" )
  27. public Result<PageInfo> findPage(@PathVariable int page, @PathVariable int size){
  28. //分页查询
  29. PageInfo<Template> pageInfo = templateService.findPage(page, size);
  30. return new Result<PageInfo>(true,StatusCode.OK,"查询成功",pageInfo);
  31. }
  32. /***
  33. * 多条件搜索品牌数据
  34. * @param template
  35. * @return
  36. */
  37. @PostMapping(value = "/search" )
  38. public Result<List<Template>> findList(@RequestBody(required = false) Template template){
  39. List<Template> list = templateService.findList(template);
  40. return new Result<List<Template>>(true,StatusCode.OK,"查询成功",list);
  41. }
  42. /***
  43. * 根据ID删除品牌数据
  44. * @param id
  45. * @return
  46. */
  47. @DeleteMapping(value = "/{id}" )
  48. public Result delete(@PathVariable Integer id){
  49. templateService.delete(id);
  50. return new Result(true,StatusCode.OK,"删除成功");
  51. }
  52. /***
  53. * 修改Template数据
  54. * @param template
  55. * @param id
  56. * @return
  57. */
  58. @PutMapping(value="/{id}")
  59. public Result update(@RequestBody Template template,@PathVariable Integer id){
  60. //设置主键值
  61. template.setId(id);
  62. //修改数据
  63. templateService.update(template);
  64. return new Result(true,StatusCode.OK,"修改成功");
  65. }
  66. /***
  67. * 新增Template数据
  68. * @param template
  69. * @return
  70. */
  71. @PostMapping
  72. public Result add(@RequestBody Template template){
  73. templateService.add(template);
  74. return new Result(true,StatusCode.OK,"添加成功");
  75. }
  76. /***
  77. * 根据ID查询Template数据
  78. * @param id
  79. * @return
  80. */
  81. @GetMapping("/{id}")
  82. public Result<Template> findById(@PathVariable Integer id){
  83. //根据ID查询
  84. Template template = templateService.findById(id);
  85. return new Result<Template>(true,StatusCode.OK,"查询成功",template);
  86. }
  87. /***
  88. * 查询Template全部数据
  89. * @return
  90. */
  91. @GetMapping
  92. public Result<Template> findAll(){
  93. List<Template> list = templateService.findAll();
  94. return new Result<Template>(true, StatusCode.OK,"查询成功",list) ;
  95. }
  96. }

3.4 规格管理

3.4.1 Pojo

在changgou-service-goods-api中创建com.changgou.goods.pojo.Spec,代码如下:

  1. @Table(name="tb_spec")
  2. public class Spec implements Serializable{
  3. @Id
  4. @GeneratedValue(strategy = GenerationType.IDENTITY)
  5. @Column(name = "id")
  6. private Integer id;//ID
  7. @Column(name = "name")
  8. private String name;//名称
  9. @Column(name = "options")
  10. private String options;//规格选项
  11. @Column(name = "seq")
  12. private Integer seq;//排序
  13. @Column(name = "template_id")
  14. private Integer templateId;//模板ID
  15. //get..set..toString
  16. }

3.4.2 Dao

在changgou-service-goods中创建com.changgou.goods.dao.SpecMapper,代码如下:

  1. public interface SpecMapper extends Mapper<Spec> {
  2. }

3.4.3 业务层

(1)业务层接口
在changgou-service-goods中创建com.changgou.goods.service.SpecService接口,并实现对应的方法,代码如下:

  1. public interface SpecService {
  2. /***
  3. * Spec多条件分页查询
  4. * @param spec
  5. * @param page
  6. * @param size
  7. * @return
  8. */
  9. PageInfo<Spec> findPage(Spec spec, int page, int size);
  10. /***
  11. * Spec分页查询
  12. * @param page
  13. * @param size
  14. * @return
  15. */
  16. PageInfo<Spec> findPage(int page, int size);
  17. /***
  18. * Spec多条件搜索方法
  19. * @param spec
  20. * @return
  21. */
  22. List<Spec> findList(Spec spec);
  23. /***
  24. * 删除Spec
  25. * @param id
  26. */
  27. void delete(Integer id);
  28. /***
  29. * 修改Spec数据
  30. * @param spec
  31. */
  32. void update(Spec spec);
  33. /***
  34. * 新增Spec
  35. * @param spec
  36. */
  37. void add(Spec spec);
  38. /**
  39. * 根据ID查询Spec
  40. * @param id
  41. * @return
  42. */
  43. Spec findById(Integer id);
  44. /***
  45. * 查询所有Spec
  46. * @return
  47. */
  48. List<Spec> findAll();
  49. }

(2)业务层实现类
在changgou-service-goods中创建com.changgou.goods.service.impl.SpecServiceImpl,代码如下:

  1. @Service
  2. public class SpecServiceImpl implements SpecService {
  3. @Autowired
  4. private SpecMapper specMapper;
  5. @Autowired
  6. private TemplateMapper templateMapper;
  7. /**
  8. * Spec条件+分页查询
  9. * @param spec 查询条件
  10. * @param page 页码
  11. * @param size 页大小
  12. * @return 分页结果
  13. */
  14. @Override
  15. public PageInfo<Spec> findPage(Spec spec, int page, int size){
  16. //分页
  17. PageHelper.startPage(page,size);
  18. //搜索条件构建
  19. Example example = createExample(spec);
  20. //执行搜索
  21. return new PageInfo<Spec>(specMapper.selectByExample(example));
  22. }
  23. /**
  24. * Spec分页查询
  25. * @param page
  26. * @param size
  27. * @return
  28. */
  29. @Override
  30. public PageInfo<Spec> findPage(int page, int size){
  31. //静态分页
  32. PageHelper.startPage(page,size);
  33. //分页查询
  34. return new PageInfo<Spec>(specMapper.selectAll());
  35. }
  36. /**
  37. * Spec条件查询
  38. * @param spec
  39. * @return
  40. */
  41. @Override
  42. public List<Spec> findList(Spec spec){
  43. //构建查询条件
  44. Example example = createExample(spec);
  45. //根据构建的条件查询数据
  46. return specMapper.selectByExample(example);
  47. }
  48. /**
  49. * Spec构建查询对象
  50. * @param spec
  51. * @return
  52. */
  53. public Example createExample(Spec spec){
  54. Example example=new Example(Spec.class);
  55. Example.Criteria criteria = example.createCriteria();
  56. if(spec!=null){
  57. // ID
  58. if(!StringUtils.isEmpty(spec.getId())){
  59. criteria.andEqualTo("id",spec.getId());
  60. }
  61. // 名称
  62. if(!StringUtils.isEmpty(spec.getName())){
  63. criteria.andLike("name","%"+spec.getName()+"%");
  64. }
  65. // 规格选项
  66. if(!StringUtils.isEmpty(spec.getOptions())){
  67. criteria.andEqualTo("options",spec.getOptions());
  68. }
  69. // 排序
  70. if(!StringUtils.isEmpty(spec.getSeq())){
  71. criteria.andEqualTo("seq",spec.getSeq());
  72. }
  73. // 模板ID
  74. if(!StringUtils.isEmpty(spec.getTemplateId())){
  75. criteria.andEqualTo("templateId",spec.getTemplateId());
  76. }
  77. }
  78. return example;
  79. }
  80. /**
  81. * 删除
  82. * @param id
  83. */
  84. @Override
  85. public void delete(Integer id){
  86. //查询模板
  87. Spec spec = specMapper.selectByPrimaryKey(id);
  88. //变更模板数量
  89. updateSpecNum(spec,-1);
  90. //删除指定规格
  91. specMapper.deleteByPrimaryKey(id);
  92. }
  93. /**
  94. * 修改Spec
  95. * @param spec
  96. */
  97. @Override
  98. public void update(Spec spec){
  99. specMapper.updateByPrimaryKey(spec);
  100. }
  101. /**
  102. * 增加Spec
  103. * @param spec
  104. */
  105. @Override
  106. public void add(Spec spec){
  107. specMapper.insert(spec);
  108. //变更模板数量
  109. updateSpecNum(spec,1);
  110. }
  111. /**
  112. * 根据ID查询Spec
  113. * @param id
  114. * @return
  115. */
  116. @Override
  117. public Spec findById(Integer id){
  118. return specMapper.selectByPrimaryKey(id);
  119. }
  120. /**
  121. * 查询Spec全部数据
  122. * @return
  123. */
  124. @Override
  125. public List<Spec> findAll() {
  126. return specMapper.selectAll();
  127. }
  128. /**
  129. * 修改模板统计数据
  130. * @param spec:操作的模板
  131. * @param count:变更的数量
  132. */
  133. public void updateSpecNum(Spec spec,int count){
  134. //修改模板数量统计
  135. Template template = templateMapper.selectByPrimaryKey(spec.getTemplateId());
  136. template.setSpecNum(template.getSpecNum()+count);
  137. templateMapper.updateByPrimaryKeySelective(template);
  138. }
  139. }

这里注意,每次执行增加和删除的时候,需要调用模板,修改统计数据,另外大家思考下,如果是修改呢,是否会对模板统计数据造成变更呢?

3.4.4 控制层

在changgou-service-goods中创建com.changgou.goods.controller.SpecController,代码如下:

  1. @RestController
  2. @RequestMapping("/spec")
  3. @CrossOrigin
  4. public class SpecController {
  5. @Autowired
  6. private SpecService specService;
  7. /***
  8. * Spec分页条件搜索实现
  9. * @param spec
  10. * @param page
  11. * @param size
  12. * @return
  13. */
  14. @PostMapping(value = "/search/{page}/{size}" )
  15. public Result<PageInfo> findPage(@RequestBody(required = false) Spec spec, @PathVariable int page, @PathVariable int size){
  16. //执行搜索
  17. PageInfo<Spec> pageInfo = specService.findPage(spec, page, size);
  18. return new Result(true,StatusCode.OK,"查询成功",pageInfo);
  19. }
  20. /***
  21. * Spec分页搜索实现
  22. * @param page:当前页
  23. * @param size:每页显示多少条
  24. * @return
  25. */
  26. @GetMapping(value = "/search/{page}/{size}" )
  27. public Result<PageInfo> findPage(@PathVariable int page, @PathVariable int size){
  28. //分页查询
  29. PageInfo<Spec> pageInfo = specService.findPage(page, size);
  30. return new Result<PageInfo>(true,StatusCode.OK,"查询成功",pageInfo);
  31. }
  32. /***
  33. * 多条件搜索品牌数据
  34. * @param spec
  35. * @return
  36. */
  37. @PostMapping(value = "/search" )
  38. public Result<List<Spec>> findList(@RequestBody(required = false) Spec spec){
  39. List<Spec> list = specService.findList(spec);
  40. return new Result<List<Spec>>(true,StatusCode.OK,"查询成功",list);
  41. }
  42. /***
  43. * 根据ID删除品牌数据
  44. * @param id
  45. * @return
  46. */
  47. @DeleteMapping(value = "/{id}" )
  48. public Result delete(@PathVariable Integer id){
  49. specService.delete(id);
  50. return new Result(true,StatusCode.OK,"删除成功");
  51. }
  52. /***
  53. * 修改Spec数据
  54. * @param spec
  55. * @param id
  56. * @return
  57. */
  58. @PutMapping(value="/{id}")
  59. public Result update(@RequestBody Spec spec,@PathVariable Integer id){
  60. //设置主键值
  61. spec.setId(id);
  62. //修改数据
  63. specService.update(spec);
  64. return new Result(true,StatusCode.OK,"修改成功");
  65. }
  66. /***
  67. * 新增Spec数据
  68. * @param spec
  69. * @return
  70. */
  71. @PostMapping
  72. public Result add(@RequestBody Spec spec){
  73. specService.add(spec);
  74. return new Result(true,StatusCode.OK,"添加成功");
  75. }
  76. /***
  77. * 根据ID查询Spec数据
  78. * @param id
  79. * @return
  80. */
  81. @GetMapping("/{id}")
  82. public Result<Spec> findById(@PathVariable Integer id){
  83. //根据ID查询
  84. Spec spec = specService.findById(id);
  85. return new Result<Spec>(true,StatusCode.OK,"查询成功",spec);
  86. }
  87. /***
  88. * 查询Spec全部数据
  89. * @return
  90. */
  91. @GetMapping
  92. public Result<Spec> findAll(){
  93. List<Spec> list = specService.findAll();
  94. return new Result<Spec>(true, StatusCode.OK,"查询成功",list) ;
  95. }
  96. }

3.5 参数管理

3.5.1 Pojo

在changgou-service-goods-api中创建com.changgou.goods.pojo.Para,代码如下:

  1. @Table(name="tb_para")
  2. public class Para implements Serializable{
  3. @Id
  4. @GeneratedValue(strategy = GenerationType.IDENTITY)
  5. @Column(name = "id")
  6. private Integer id;//id
  7. @Column(name = "name")
  8. private String name;//名称
  9. @Column(name = "options")
  10. private String options;//选项
  11. @Column(name = "seq")
  12. private Integer seq;//排序
  13. @Column(name = "template_id")
  14. private Integer templateId;//模板ID
  15. //get..set..toString
  16. }

3.5.2 Dao

在changgou-service-goods中创建com.changgou.goods.dao.ParaMapper,代码如下:

  1. public interface ParaMapper extends Mapper<Para> {
  2. }

3.5.3 业务层

(1)业务层接口
在changgou-service-goods中创建com.changgou.goods.service.ParaService接口,并添加常用方法,代码如下:

  1. public interface ParaService {
  2. /***
  3. * Para多条件分页查询
  4. * @param para
  5. * @param page
  6. * @param size
  7. * @return
  8. */
  9. PageInfo<Para> findPage(Para para, int page, int size);
  10. /***
  11. * Para分页查询
  12. * @param page
  13. * @param size
  14. * @return
  15. */
  16. PageInfo<Para> findPage(int page, int size);
  17. /***
  18. * Para多条件搜索方法
  19. * @param para
  20. * @return
  21. */
  22. List<Para> findList(Para para);
  23. /***
  24. * 删除Para
  25. * @param id
  26. */
  27. void delete(Integer id);
  28. /***
  29. * 修改Para数据
  30. * @param para
  31. */
  32. void update(Para para);
  33. /***
  34. * 新增Para
  35. * @param para
  36. */
  37. void add(Para para);
  38. /**
  39. * 根据ID查询Para
  40. * @param id
  41. * @return
  42. */
  43. Para findById(Integer id);
  44. /***
  45. * 查询所有Para
  46. * @return
  47. */
  48. List<Para> findAll();
  49. }

(2)业务层接口实现类
在changgou-service-goods中创建com.changgou.goods.service.impl.ParaServiceImpl接口实现类,代码如下:

  1. @Service
  2. public class ParaServiceImpl implements ParaService {
  3. @Autowired
  4. private ParaMapper paraMapper;
  5. @Autowired
  6. private TemplateMapper templateMapper;
  7. /**
  8. * Para条件+分页查询
  9. * @param para 查询条件
  10. * @param page 页码
  11. * @param size 页大小
  12. * @return 分页结果
  13. */
  14. @Override
  15. public PageInfo<Para> findPage(Para para, int page, int size){
  16. //分页
  17. PageHelper.startPage(page,size);
  18. //搜索条件构建
  19. Example example = createExample(para);
  20. //执行搜索
  21. return new PageInfo<Para>(paraMapper.selectByExample(example));
  22. }
  23. /**
  24. * Para分页查询
  25. * @param page
  26. * @param size
  27. * @return
  28. */
  29. @Override
  30. public PageInfo<Para> findPage(int page, int size){
  31. //静态分页
  32. PageHelper.startPage(page,size);
  33. //分页查询
  34. return new PageInfo<Para>(paraMapper.selectAll());
  35. }
  36. /**
  37. * Para条件查询
  38. * @param para
  39. * @return
  40. */
  41. @Override
  42. public List<Para> findList(Para para){
  43. //构建查询条件
  44. Example example = createExample(para);
  45. //根据构建的条件查询数据
  46. return paraMapper.selectByExample(example);
  47. }
  48. /**
  49. * Para构建查询对象
  50. * @param para
  51. * @return
  52. */
  53. public Example createExample(Para para){
  54. Example example=new Example(Para.class);
  55. Example.Criteria criteria = example.createCriteria();
  56. if(para!=null){
  57. // id
  58. if(!StringUtils.isEmpty(para.getId())){
  59. criteria.andEqualTo("id",para.getId());
  60. }
  61. // 名称
  62. if(!StringUtils.isEmpty(para.getName())){
  63. criteria.andLike("name","%"+para.getName()+"%");
  64. }
  65. // 选项
  66. if(!StringUtils.isEmpty(para.getOptions())){
  67. criteria.andEqualTo("options",para.getOptions());
  68. }
  69. // 排序
  70. if(!StringUtils.isEmpty(para.getSeq())){
  71. criteria.andEqualTo("seq",para.getSeq());
  72. }
  73. // 模板ID
  74. if(!StringUtils.isEmpty(para.getTemplateId())){
  75. criteria.andEqualTo("templateId",para.getTemplateId());
  76. }
  77. }
  78. return example;
  79. }
  80. /**
  81. * 删除
  82. * @param id
  83. */
  84. @Override
  85. public void delete(Integer id){
  86. //根据ID查询
  87. Para para = paraMapper.selectByPrimaryKey(id);
  88. //修改模板统计数据
  89. updateParaNum(para,-1);
  90. paraMapper.deleteByPrimaryKey(id);
  91. }
  92. /**
  93. * 修改Para
  94. * @param para
  95. */
  96. @Override
  97. public void update(Para para){
  98. paraMapper.updateByPrimaryKey(para);
  99. }
  100. /**
  101. * 增加Para
  102. * @param para
  103. */
  104. @Override
  105. public void add(Para para){
  106. paraMapper.insert(para);
  107. //修改模板统计数据
  108. updateParaNum(para,1);
  109. }
  110. /**
  111. * 根据ID查询Para
  112. * @param id
  113. * @return
  114. */
  115. @Override
  116. public Para findById(Integer id){
  117. return paraMapper.selectByPrimaryKey(id);
  118. }
  119. /**
  120. * 查询Para全部数据
  121. * @return
  122. */
  123. @Override
  124. public List<Para> findAll() {
  125. return paraMapper.selectAll();
  126. }
  127. /**
  128. * 修改模板统计数据
  129. * @param para:操作的参数
  130. * @param count:变更的数量
  131. */
  132. public void updateParaNum(Para para, int count){
  133. //修改模板数量统计
  134. Template template = templateMapper.selectByPrimaryKey(para.getTemplateId());
  135. template.setParaNum(template.getParaNum()+count);
  136. templateMapper.updateByPrimaryKeySelective(template);
  137. }
  138. }

3.5.4 控制层

在changgou-service-goods下创建com.changgou.goods.controller.ParaController,代码如下:

  1. @RestController
  2. @RequestMapping("/para")
  3. @CrossOrigin
  4. public class ParaController {
  5. @Autowired
  6. private ParaService paraService;
  7. /***
  8. * Para分页条件搜索实现
  9. * @param para
  10. * @param page
  11. * @param size
  12. * @return
  13. */
  14. @PostMapping(value = "/search/{page}/{size}" )
  15. public Result<PageInfo> findPage(@RequestBody(required = false) Para para, @PathVariable int page, @PathVariable int size){
  16. //执行搜索
  17. PageInfo<Para> pageInfo = paraService.findPage(para, page, size);
  18. return new Result(true,StatusCode.OK,"查询成功",pageInfo);
  19. }
  20. /***
  21. * Para分页搜索实现
  22. * @param page:当前页
  23. * @param size:每页显示多少条
  24. * @return
  25. */
  26. @GetMapping(value = "/search/{page}/{size}" )
  27. public Result<PageInfo> findPage(@PathVariable int page, @PathVariable int size){
  28. //分页查询
  29. PageInfo<Para> pageInfo = paraService.findPage(page, size);
  30. return new Result<PageInfo>(true,StatusCode.OK,"查询成功",pageInfo);
  31. }
  32. /***
  33. * 多条件搜索品牌数据
  34. * @param para
  35. * @return
  36. */
  37. @PostMapping(value = "/search" )
  38. public Result<List<Para>> findList(@RequestBody(required = false) Para para){
  39. List<Para> list = paraService.findList(para);
  40. return new Result<List<Para>>(true,StatusCode.OK,"查询成功",list);
  41. }
  42. /***
  43. * 根据ID删除品牌数据
  44. * @param id
  45. * @return
  46. */
  47. @DeleteMapping(value = "/{id}" )
  48. public Result delete(@PathVariable Integer id){
  49. paraService.delete(id);
  50. return new Result(true,StatusCode.OK,"删除成功");
  51. }
  52. /***
  53. * 修改Para数据
  54. * @param para
  55. * @param id
  56. * @return
  57. */
  58. @PutMapping(value="/{id}")
  59. public Result update(@RequestBody Para para,@PathVariable Integer id){
  60. //设置主键值
  61. para.setId(id);
  62. //修改数据
  63. paraService.update(para);
  64. return new Result(true,StatusCode.OK,"修改成功");
  65. }
  66. /***
  67. * 新增Para数据
  68. * @param para
  69. * @return
  70. */
  71. @PostMapping
  72. public Result add(@RequestBody Para para){
  73. paraService.add(para);
  74. return new Result(true,StatusCode.OK,"添加成功");
  75. }
  76. /***
  77. * 根据ID查询Para数据
  78. * @param id
  79. * @return
  80. */
  81. @GetMapping("/{id}")
  82. public Result<Para> findById(@PathVariable Integer id){
  83. //根据ID查询
  84. Para para = paraService.findById(id);
  85. return new Result<Para>(true,StatusCode.OK,"查询成功",para);
  86. }
  87. /***
  88. * 查询Para全部数据
  89. * @return
  90. */
  91. @GetMapping
  92. public Result<Para> findAll(){
  93. List<Para> list = paraService.findAll();
  94. return new Result<Para>(true, StatusCode.OK,"查询成功",list) ;
  95. }
  96. }

4 商品分类(实战)

4.1 需求分析

商品分类一共分三级管理,主要作用是在网站首页中显示商品导航,以及在管理后台管理商品时使用。
前端交互方式见管理后台的静态原型

4.2 表结构分析

tb_category 表 (商品分类

字段名称 字段含义 字段类型 字段长度 备注
id 分类ID INT
name 分类名称 VARCHAR
goods_num 商品数量 INT
is_show 是否显示 CHAR 0 不显示 1显示
is_menu 是否导航 CHAR 0 不时导航 1 为导航
seq 排序 INT
parent_id 上级ID INT
template_id 模板ID INT

商品分类与模板是多对一关系

4.3 实现

4.3.1 Pojo

在changgou-service-goods-api中创建com.changgou.goods.pojo.Category,代码如下:

  1. @Table(name="tb_category")
  2. public class Category implements Serializable{
  3. @Id
  4. @GeneratedValue(strategy = GenerationType.IDENTITY)
  5. @Column(name = "id")
  6. private Integer id;//分类ID
  7. @Column(name = "name")
  8. private String name;//分类名称
  9. @Column(name = "goods_num")
  10. private Integer goodsNum;//商品数量
  11. @Column(name = "is_show")
  12. private String isShow;//是否显示
  13. @Column(name = "is_menu")
  14. private String isMenu;//是否导航
  15. @Column(name = "seq")
  16. private Integer seq;//排序
  17. @Column(name = "parent_id")
  18. private Integer parentId;//上级ID
  19. @Column(name = "template_id")
  20. private Integer templateId;//模板ID
  21. //..set..get..toString
  22. }

4.3.2 Dao

在changgou-servicegoods中创建com.changgou.goods.dao.CategoryMapper接口,代码如下:

  1. public interface CategoryMapper extends Mapper<Category> {
  2. }

4.3.3 业务层

(1)业务层接口
修改changgou-service-goods,添加com.changgou.goods.service.CategoryService接口,代码如下:

  1. public interface CategoryService {
  2. /***
  3. * Category多条件分页查询
  4. * @param category
  5. * @param page
  6. * @param size
  7. * @return
  8. */
  9. PageInfo<Category> findPage(Category category, int page, int size);
  10. /***
  11. * Category分页查询
  12. * @param page
  13. * @param size
  14. * @return
  15. */
  16. PageInfo<Category> findPage(int page, int size);
  17. /***
  18. * Category多条件搜索方法
  19. * @param category
  20. * @return
  21. */
  22. List<Category> findList(Category category);
  23. /***
  24. * 删除Category
  25. * @param id
  26. */
  27. void delete(Integer id);
  28. /***
  29. * 修改Category数据
  30. * @param category
  31. */
  32. void update(Category category);
  33. /***
  34. * 新增Category
  35. * @param category
  36. */
  37. void add(Category category);
  38. /**
  39. * 根据ID查询Category
  40. * @param id
  41. * @return
  42. */
  43. Category findById(Integer id);
  44. /***
  45. * 查询所有Category
  46. * @return
  47. */
  48. List<Category> findAll();
  49. /***
  50. * 根据父节点ID查询
  51. * @param pid:父节点ID
  52. */
  53. List<Category> findByParentId(Integer pid);
  54. }

(2)业务层接口实现类
修改changgou-service-goods,添加com.changgou.goods.service.impl.CategoryServiceImpl接口实现类,代码如下:

  1. @Service
  2. public class CategoryServiceImpl implements CategoryService {
  3. @Autowired
  4. private CategoryMapper categoryMapper;
  5. /**
  6. * Category条件+分页查询
  7. * @param category 查询条件
  8. * @param page 页码
  9. * @param size 页大小
  10. * @return 分页结果
  11. */
  12. @Override
  13. public PageInfo<Category> findPage(Category category, int page, int size){
  14. //分页
  15. PageHelper.startPage(page,size);
  16. //搜索条件构建
  17. Example example = createExample(category);
  18. //执行搜索
  19. return new PageInfo<Category>(categoryMapper.selectByExample(example));
  20. }
  21. /**
  22. * Category分页查询
  23. * @param page
  24. * @param size
  25. * @return
  26. */
  27. @Override
  28. public PageInfo<Category> findPage(int page, int size){
  29. //静态分页
  30. PageHelper.startPage(page,size);
  31. //分页查询
  32. return new PageInfo<Category>(categoryMapper.selectAll());
  33. }
  34. /**
  35. * Category条件查询
  36. * @param category
  37. * @return
  38. */
  39. @Override
  40. public List<Category> findList(Category category){
  41. //构建查询条件
  42. Example example = createExample(category);
  43. //根据构建的条件查询数据
  44. return categoryMapper.selectByExample(example);
  45. }
  46. /**
  47. * Category构建查询对象
  48. * @param category
  49. * @return
  50. */
  51. public Example createExample(Category category){
  52. Example example=new Example(Category.class);
  53. Example.Criteria criteria = example.createCriteria();
  54. if(category!=null){
  55. // 分类ID
  56. if(!StringUtils.isEmpty(category.getId())){
  57. criteria.andEqualTo("id",category.getId());
  58. }
  59. // 分类名称
  60. if(!StringUtils.isEmpty(category.getName())){
  61. criteria.andLike("name","%"+category.getName()+"%");
  62. }
  63. // 商品数量
  64. if(!StringUtils.isEmpty(category.getGoodsNum())){
  65. criteria.andEqualTo("goodsNum",category.getGoodsNum());
  66. }
  67. // 是否显示
  68. if(!StringUtils.isEmpty(category.getIsShow())){
  69. criteria.andEqualTo("isShow",category.getIsShow());
  70. }
  71. // 是否导航
  72. if(!StringUtils.isEmpty(category.getIsMenu())){
  73. criteria.andEqualTo("isMenu",category.getIsMenu());
  74. }
  75. // 排序
  76. if(!StringUtils.isEmpty(category.getSeq())){
  77. criteria.andEqualTo("seq",category.getSeq());
  78. }
  79. // 上级ID
  80. if(!StringUtils.isEmpty(category.getParentId())){
  81. criteria.andEqualTo("parentId",category.getParentId());
  82. }
  83. // 模板ID
  84. if(!StringUtils.isEmpty(category.getTemplateId())){
  85. criteria.andEqualTo("templateId",category.getTemplateId());
  86. }
  87. }
  88. return example;
  89. }
  90. /**
  91. * 删除
  92. * @param id
  93. */
  94. @Override
  95. public void delete(Integer id){
  96. categoryMapper.deleteByPrimaryKey(id);
  97. }
  98. /**
  99. * 修改Category
  100. * @param category
  101. */
  102. @Override
  103. public void update(Category category){
  104. categoryMapper.updateByPrimaryKey(category);
  105. }
  106. /**
  107. * 增加Category
  108. * @param category
  109. */
  110. @Override
  111. public void add(Category category){
  112. categoryMapper.insert(category);
  113. }
  114. /**
  115. * 根据ID查询Category
  116. * @param id
  117. * @return
  118. */
  119. @Override
  120. public Category findById(Integer id){
  121. return categoryMapper.selectByPrimaryKey(id);
  122. }
  123. /**
  124. * 查询Category全部数据
  125. * @return
  126. */
  127. @Override
  128. public List<Category> findAll() {
  129. return categoryMapper.selectAll();
  130. }
  131. /***
  132. * 根据父节点ID查询
  133. * @param pid:父节点ID
  134. */
  135. @Override
  136. public List<Category> findByParentId(Integer pid) {
  137. Category category = new Category();
  138. category.setParentId(pid);
  139. return categoryMapper.select(category);
  140. }
  141. }

4.3.4 控制层

修改changgou-service-goods,添加com.changgou.goods.controller.CategoryController,代码如下:

  1. @RestController
  2. @RequestMapping("/category")
  3. @CrossOrigin
  4. public class CategoryController {
  5. @Autowired
  6. private CategoryService categoryService;
  7. /***
  8. * Category分页条件搜索实现
  9. * @param category
  10. * @param page
  11. * @param size
  12. * @return
  13. */
  14. @PostMapping(value = "/search/{page}/{size}" )
  15. public Result<PageInfo> findPage(@RequestBody(required = false) Category category, @PathVariable int page, @PathVariable int size){
  16. //执行搜索
  17. PageInfo<Category> pageInfo = categoryService.findPage(category, page, size);
  18. return new Result(true,StatusCode.OK,"查询成功",pageInfo);
  19. }
  20. /***
  21. * Category分页搜索实现
  22. * @param page:当前页
  23. * @param size:每页显示多少条
  24. * @return
  25. */
  26. @GetMapping(value = "/search/{page}/{size}" )
  27. public Result<PageInfo> findPage(@PathVariable int page, @PathVariable int size){
  28. //分页查询
  29. PageInfo<Category> pageInfo = categoryService.findPage(page, size);
  30. return new Result<PageInfo>(true,StatusCode.OK,"查询成功",pageInfo);
  31. }
  32. /***
  33. * 多条件搜索品牌数据
  34. * @param category
  35. * @return
  36. */
  37. @PostMapping(value = "/search" )
  38. public Result<List<Category>> findList(@RequestBody(required = false) Category category){
  39. List<Category> list = categoryService.findList(category);
  40. return new Result<List<Category>>(true,StatusCode.OK,"查询成功",list);
  41. }
  42. /***
  43. * 根据ID删除品牌数据
  44. * @param id
  45. * @return
  46. */
  47. @DeleteMapping(value = "/{id}" )
  48. public Result delete(@PathVariable Integer id){
  49. categoryService.delete(id);
  50. return new Result(true,StatusCode.OK,"删除成功");
  51. }
  52. /***
  53. * 修改Category数据
  54. * @param category
  55. * @param id
  56. * @return
  57. */
  58. @PutMapping(value="/{id}")
  59. public Result update(@RequestBody Category category,@PathVariable Integer id){
  60. //设置主键值
  61. category.setId(id);
  62. //修改数据
  63. categoryService.update(category);
  64. return new Result(true,StatusCode.OK,"修改成功");
  65. }
  66. /***
  67. * 新增Category数据
  68. * @param category
  69. * @return
  70. */
  71. @PostMapping
  72. public Result add(@RequestBody Category category){
  73. categoryService.add(category);
  74. return new Result(true,StatusCode.OK,"添加成功");
  75. }
  76. /***
  77. * 根据ID查询Category数据
  78. * @param id
  79. * @return
  80. */
  81. @GetMapping("/{id}")
  82. public Result<Category> findById(@PathVariable Integer id){
  83. //根据ID查询
  84. Category category = categoryService.findById(id);
  85. return new Result<Category>(true,StatusCode.OK,"查询成功",category);
  86. }
  87. /***
  88. * 查询Category全部数据
  89. * @return
  90. */
  91. @GetMapping
  92. public Result<Category> findAll(){
  93. List<Category> list = categoryService.findAll();
  94. return new Result<Category>(true, StatusCode.OK,"查询成功",list) ;
  95. }
  96. /**
  97. * 根据父ID查询
  98. */
  99. @RequestMapping(value ="/list/{pid}")
  100. public Result<Category> findByPrantId(@PathVariable(value = "pid")Integer pid){
  101. //根据父节点ID查询
  102. List<Category> list = categoryService.findByParentId(pid);
  103. return new Result<Category>(true,StatusCode.OK,"查询成功",list);
  104. }
  105. }

5 抽取service和controller

经过上边的实战。我们发现大量的重复劳动发生了。所以能否有一个思想能解决以上的问题呢,这个思想就是:
程序员只需要继承或者实现某一个抽象类或者接口就可以直接使用所有的CRUD方法,就好比我们使用过的通用的MAPPER和通用的spring data jpa一样呢?
答案当然是肯定可以滴,接下来我们来解决下。
思路如下:
第2天 分布式文件存储FastDFS - 图10
解释:

  1. 编写接口和抽象类
  2. --- 接口定义操作行为和方法
  3. --- 抽象类提供具体操作行为和方法 与此同时通过泛型来实现操作的具体对象 由子类提供
  4. 编写子类实现类 继承抽象类
  5. --- 子类具体实现 向上调用super方法进行赋值,给予抽象类的具体操作类型即可
  6. 以上图 指定的是service的操作方式。与此同时controller的操作方式也是类似

5.1 抽取service

(1)创建changgou-core工程打包方式为pom
第2天 分布式文件存储FastDFS - 图11

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>changgou-parent</artifactId>
  7. <groupId>com.changgou</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>changgou-core</artifactId>
  12. <description>核心工程</description>
  13. <modules>
  14. <module>changgou-core-service</module>
  15. <module>changgou-core-controller</module>
  16. </modules>
  17. <packaging>pom</packaging>
  18. </project>

(2)创建changgou-core-service 打包方式为jar
第2天 分布式文件存储FastDFS - 图12

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>changgou-core</artifactId>
  7. <groupId>com.changgou</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>changgou-core-service</artifactId>
  12. <dependencies>
  13. <dependency>
  14. <groupId>com.changgou</groupId>
  15. <artifactId>changgou-common-db</artifactId>
  16. <version>1.0-SNAPSHOT</version>
  17. </dependency>
  18. </dependencies>
  19. </project>

(3)创建changgou-core-controller工程
第2天 分布式文件存储FastDFS - 图13

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <parent>
  6. <artifactId>changgou-core</artifactId>
  7. <groupId>com.changgou</groupId>
  8. <version>1.0-SNAPSHOT</version>
  9. </parent>
  10. <modelVersion>4.0.0</modelVersion>
  11. <artifactId>changgou-core-controller</artifactId>
  12. <dependencies>
  13. <dependency>
  14. <groupId>com.changgou</groupId>
  15. <artifactId>changgou-core-service</artifactId>
  16. <version>1.0-SNAPSHOT</version>
  17. </dependency>
  18. </dependencies>
  19. </project>

(4)定义接口和抽象类
第2天 分布式文件存储FastDFS - 图14
具体代码参考项目资料:
第2天 分布式文件存储FastDFS - 图15
(5)测试service:
changgou-service-goods工程中添加依赖:

  1. <dependency>
  2. <groupId>com.changgou</groupId>
  3. <artifactId>changgou-core-service</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. </dependency>

修改service接口,添加继承

  1. public interface BrandService extends CoreService<Brand>{
  2. }

修改实现类 添加构造方法提交

  1. @Service
  2. public class BrandServiceImpl extends CoreServiceImpl<Brand> implements BrandService {
  3. private BrandMapper brandMapper;
  4. // 此处的@Autowired注解,表示参数brandMapper从bean中获取
  5. @Autowired
  6. public BrandServiceImpl(BrandMapper brandMapper) {
  7. super(brandMapper, Brand.class);
  8. this.brandMapper = brandMapper;
  9. }
  10. }

测试:

  1. @SpringBootTest
  2. @RunWith(SpringRunner.class)
  3. public class GoodsApplicationTest {
  4. @Autowired
  5. private BrandService brandService;
  6. @Test
  7. public void findAll() {
  8. List<Brand> brandList = brandService.selectAll();
  9. for (Brand brand : brandList) {
  10. System.out.println(">>>>>>>" + brand.getName());
  11. }
  12. }
  13. @Test
  14. public void findAllByCondition() {
  15. Brand brand = new Brand();
  16. brand.setId(1);
  17. brand.setName("华");
  18. PageInfo<Brand> byPage = brandService.findByPage(1, 2, brand);
  19. List<Brand> list = byPage.getList();
  20. for (Brand brand111 : list) {
  21. System.out.println(">>>>>>>>>>>>>>>>>>>>>:" + brand111.getName());
  22. }
  23. }
  24. }

此时没有任何的CRUD的代码存在,即可实现基本的CRUD的功能了。

5.2 抽取controller

思路:
第2天 分布式文件存储FastDFS - 图16
操作步骤:
(1)创建工程core-controller并创建接口和抽象类
第2天 分布式文件存储FastDFS - 图17
代码具体参考资料:
第2天 分布式文件存储FastDFS - 图18
(2)测试controller
changgou-service-goods中添加依赖:

  1. <dependency>
  2. <groupId>com.changgou</groupId>
  3. <artifactId>changgou-core-controller</artifactId>
  4. <version>1.0-SNAPSHOT</version>
  5. </dependency>

修改BrandController:

  1. @RestController
  2. @RequestMapping("/brand")
  3. @CrossOrigin
  4. public class BrandController extends AbstractCoreController<Brand> {
  5. private BrandService brandService;
  6. @Autowired
  7. public BrandController(BrandService brandService) {
  8. super(brandService, Brand.class);
  9. this.brandService = brandService;
  10. }
  11. }

测试CRUD功能:
第2天 分布式文件存储FastDFS - 图19