前言

跟着视频学了那么多技术,有没有自己尝试过做一个开源项目呢?

下面让我们一步一步分析这个最火的前后端分离项目

项目地址:

https://github.com/YuyanCai/mall

从0开始一个开源项目

  1. 看简介,知道项目是做什么的
  2. 看代码更新频率,几年没更新的最好别用
  3. 看README.md了解项目是否符合自己的技术栈
  4. 运行项目
    • 本地拉取代码
    • 看项目从整体到局部,先看项目架构 =》 看POM文件 =》看YML配置文件 =》看目录结构

项目架构

mall-admin-web

mall-admin-web是一个电商后台管理系统的前端项目,基于Vue+Element实现。主要包括商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等功能。

[外链图片转存中…(img-kDx7BIEi-1656677294008)]

mall

mall项目(50k+star)是一套电商系统,使用现阶段主流技术实现。涵盖了SpringBoot 2.3.0、MyBatis 3.4.6、Elasticsearch 7.6.2、RabbitMQ 3.7.15、Redis 5.0、MongoDB 4.2.5、Mysql5.7等技术,采用Docker容器化部署。

[外链图片转存中…(img-ufkfKlgB-1656677294011)]

从上面可以看出,这是一个前后端分离的项目

前端项目为mall-admin-web

后端项目为mall

所用技术栈也比较符合我们Java工程师

其中Mybatis不想用的话也可以用MP来代替

ES和Mongodb没接触过的话,不用从头去学,了解下怎么使用即可~

Github1s

一个开源项目,能够直接在github页面通过vscode查看项目代码

使用方法就是在项目地址中的github关键字后加上1s回车即可查看

[外链图片转存中…(img-ZpMsKSo5-1656677294012)]

前端代码架构

前端看视频学的谷粒学院很相似,

所以前端没啥大问题。

[外链图片转存中…(img-bAG9VC0r-1656677294013)]

后端代码架构

mall
├── mall-common – 工具类及通用代码
├── mall-mbg – MyBatisGenerator生成的数据库操作代码
├── mall-security – SpringSecurity封装公用模块
├── mall-admin – 后台商城管理系统接口
├── mall-search – 基于Elasticsearch的商品搜索系统
├── mall-portal – 前台商城系统接口
└── mall-demo – 框架搭建时的测试代码

[外链图片转存中…(img-2GTDsRam-1656677294015)]

简单了解下商品模块的功能

这个商品列表,也就是CRUD中的查询,只不过人家查询的条目很多

[外链图片转存中…(img-q1sJnoWk-1656677294016)]

添加商品呢,也就是CRUD中的增

[外链图片转存中…(img-a5MgcDkN-1656677294017)]

商品回收站,也就是CRUD中的删

[外链图片转存中…(img-xzOpv49T-1656677294017)]

商品的配置就属于是优化部分了,比如打开一级菜单的时候显示属于一级菜单的二级菜单

[外链图片转存中…(img-jMPN5B8j-1656677294018)]

最后是一些附加项,如商品的品牌管理,库存管理,图片管理,分类管理等等的一些CRUD操作

[外链图片转存中…(img-YdLrpeww-1656677294019)]

总结

实现功能如下,简单说就是针对商品的各种管理。如商品,类型,分类,品牌,订单…

[外链图片转存中…(img-fqSHCpz2-1656677294022)]

整体看下来,技术点是不难的。难点是细节比较多,业务逻辑可能稍微复杂,但是越难我们越要上!要对自己很有自信,一个一个的去攻破难点,这样我们才能不断的变强,与诸君共勉!

后台架构

mall
├── mall-common – 工具类及通用代码
├── mall-mbg – MyBatisGenerator生成的数据库操作代码
├── mall-security – SpringSecurity封装公用模块
├── mall-admin – 后台商城管理系统接口
├── mall-search – 基于Elasticsearch的商品搜索系统
├── mall-portal – 前台商城系统接口
└── mall-demo – 框架搭建时的测试代码

通过前面的学习我们知道了开发接口的套路

  1. 建表写sql
  2. 定义实体类
  3. dao与mapper
  4. service
  5. controller

maven工程分析

此项目采用maven来做包的依赖管理

下面简单分析下maven的结构

聚合

项目采用maven的聚合和继承

聚合是为了更快的构建项目,是表示项目与子项目之间的关系

继承则是消除不同模块同种依赖节省了不必要配置

mall这个项目,有管理商品项目、mbg项目,权限管理项目。这个时候在maven中表达这种归属关系,就可以用maven的聚合来表示,如下:

<modules><module>mall-common</module><module>mall-admin</module><module>mall-mbg</module>
</modules>

一般情况把子模块放到父模块下面,也可以在同一模块,只需要改变module的值即可

<modules><module>../mall-common</module><module>../mall-admin</module><module>../mall-mbg</module>
</modules>

继承

spring-boot-starter-actuator可以用于检测系统的健康情况、当前的Beans、系统的缓存等

spring-boot-starter-aop Spring Boot使用AOP

项目的 dependencies 元素中声明该依赖,就会自动继承到子模块中

其中spring-boot-starter-actuator、spring-boot-starter-aop…都可以自动继承到子模块

[外链图片转存中…(img-aLxaODtX-1656677294023)]

common模块

此模块定义多个微服务模块公用的工具类,异常处理类,统一返回类等公共部分

为了更好的理解,有一些前置知识需要在回顾一下:

一、枚举

​ Java 枚举是一个特殊的类,一般表示一组常量,它是线程安全的,所以定义固定的常量一般把他们定义在枚举类里

创建一个枚举类,经过编译后实际上会生成一个对应的抽象类,这个类继承了Java API中的java.lang.Enum类

还为我们生成了两个静态方法,分别是values()和 valueOf()

图中所举例子TEST1将会变成public static final R TEST1;

[外链图片转存中…(img-y0ju3K0R-1656677294024)]

项目中用到的是枚举的高级用法,向enum类添加方法与自定义属性和构造函数

public enum ResultCode implements IErrorCode {SUCCESS(200, "操作成功"),FAILED(500, "操作失败"),VALIDATE_FAILED(404, "参数检验失败"),UNAUTHORIZED(401, "暂未登录或token已经过期"),FORBIDDEN(403, "没有相关权限");private long code;private String message;private ResultCode(long code, String message) {this.code = code;this.message = message;}public long getCode() {return code;}public String getMessage() {return message;}
}

二、泛型

1.什么是泛型?

泛型是程序语言的一种特性,指类型参数化

2.为什么要有泛型?

为了使代码更灵活,因为java是强类型语言(强类型语言是一种强制类型定义的语言,即一旦某一个变量被定义类型,如果不经强制转换,那么它永远就死该数据类型。),引入泛型后可以让部分代码可变,这部分代码在使用前必须声明。还有就是减少强制类型转换

3.泛型方法

泛型方法就是方法的返回值不是确定的类型,通过一个通配符来占位,等真正用到此方法的时候在指定返回值类型。

4.泛型类

同泛型方法

在详细可看我之前发布的文章泛型篇

强哥说Java–Java的泛型_小蜗牛耶的博客-CSDN博客_强哥说java

pom

这里redis先注释,等整合的时候再用

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>mall-study</artifactId><groupId>com.caq.mall</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>mall-common</artifactId><dependencies><dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency>
<!--        <dependency>-->
<!--            <groupId>org.springframework.boot</groupId>-->
<!--            <artifactId>spring-boot-starter-data-redis</artifactId>-->
<!--        </dependency>--><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-commons</artifactId></dependency><dependency><groupId>net.logstash.logback</groupId><artifactId>logstash-logback-encoder</artifactId></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId></dependency><dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger-ui</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency></dependencies></project>

统一返回类

将后端处理好的数据以同一的格式返回给前端

格式固定,可以根据项目需求更改

这个项目写的这套返回类是很标准的,工作了我们也可以拿这个来写

/*** 通用返回对象*/
public class CommonResult<T> {/*** 状态码*/private long code;/*** 提示信息*/private String message;/*** 数据封装*/private T data;protected CommonResult() {}protected CommonResult(long code, String message, T data) {this.code = code;this.message = message;this.data = data;}/*** 成功返回结果** @param data 获取的数据*/public static <T> CommonResult<T> success(T data) {return new CommonResult<T>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);}/*** 成功返回结果** @param data 获取的数据* @param  message 提示信息*/public static <T> CommonResult<T> success(T data, String message) {return new CommonResult<T>(ResultCode.SUCCESS.getCode(), message, data);}/*** 失败返回结果* @param errorCode 错误码*/public static <T> CommonResult<T> failed(IErrorCode errorCode) {return new CommonResult<T>(errorCode.getCode(), errorCode.getMessage(), null);}/*** 失败返回结果* @param errorCode 错误码* @param message 错误信息*/public static <T> CommonResult<T> failed(IErrorCode errorCode,String message) {return new CommonResult<T>(errorCode.getCode(), message, null);}/*** 失败返回结果* @param message 提示信息*/public static <T> CommonResult<T> failed(String message) {return new CommonResult<T>(ResultCode.FAILED.getCode(), message, null);}/*** 失败返回结果*/public static <T> CommonResult<T> failed() {return failed(ResultCode.FAILED);}/*** 参数验证失败返回结果*/public static <T> CommonResult<T> validateFailed() {return failed(ResultCode.VALIDATE_FAILED);}/*** 参数验证失败返回结果* @param message 提示信息*/public static <T> CommonResult<T> validateFailed(String message) {return new CommonResult<T>(ResultCode.VALIDATE_FAILED.getCode(), message, null);}/*** 未登录返回结果*/public static <T> CommonResult<T> unauthorized(T data) {return new CommonResult<T>(ResultCode.UNAUTHORIZED.getCode(), ResultCode.UNAUTHORIZED.getMessage(), data);}/*** 未授权返回结果*/public static <T> CommonResult<T> forbidden(T data) {return new CommonResult<T>(ResultCode.FORBIDDEN.getCode(), ResultCode.FORBIDDEN.getMessage(), data);}public long getCode() {return code;}public void setCode(long code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;}
}

分页数据封装类

package com.caq.mall.common.api;import com.github.pagehelper.PageInfo;
import org.springframework.data.domain.Page;import java.util.List;/*** 分页数据封装类*/
public class CommonPage<T> {/*** 当前页码*/private Integer pageNum;/*** 每页数量*/private Integer pageSize;/*** 总页数*/private Integer totalPage;/*** 总条数*/private Long total;/*** 分页数据*/private List<T> list;/*** 将PageHelper分页后的list转为分页信息*/public static <T> CommonPage<T> restPage(List<T> list) {CommonPage<T> result = new CommonPage<T>();PageInfo<T> pageInfo = new PageInfo<T>(list);result.setTotalPage(pageInfo.getPages());result.setPageNum(pageInfo.getPageNum());result.setPageSize(pageInfo.getPageSize());result.setTotal(pageInfo.getTotal());result.setList(pageInfo.getList());return result;}/*** 将SpringData分页后的list转为分页信息*/public static <T> CommonPage<T> restPage(Page<T> pageInfo) {CommonPage<T> result = new CommonPage<T>();result.setTotalPage(pageInfo.getTotalPages());result.setPageNum(pageInfo.getNumber());result.setPageSize(pageInfo.getSize());result.setTotal(pageInfo.getTotalElements());result.setList(pageInfo.getContent());return result;}public Integer getPageNum() {return pageNum;}public void setPageNum(Integer pageNum) {this.pageNum = pageNum;}public Integer getPageSize() {return pageSize;}public void setPageSize(Integer pageSize) {this.pageSize = pageSize;}public Integer getTotalPage() {return totalPage;}public void setTotalPage(Integer totalPage) {this.totalPage = totalPage;}public List<T> getList() {return list;}public void setList(List<T> list) {this.list = list;}public Long getTotal() {return total;}public void setTotal(Long total) {this.total = total;}
}

异常处理

分别定义自定义异常、全局异常、断言

断言的作用是简化方法入参检测的代码

不使用断言的情况下我们要这样写:

public InputStream getData(String file) { if (file == null || file.length() == 0|| file.replaceAll("\\s", "").length() == 0) {throw new IllegalArgumentException("file入参不是有效的文件地址"); }

在应用 Assert 断言类后,其代码可以简化为以下的形式:

public InputStream getData(String file){ Assert.hasText(file,"file入参不是有效的文件地址");

自定义异常类:ApiException

package com.macro.mall.common.exception;import com.macro.mall.common.api.IErrorCode;/*** 自定义API异常*/
public class ApiException extends RuntimeException {private IErrorCode errorCode;public ApiException(IErrorCode errorCode) {super(errorCode.getMessage());this.errorCode = errorCode;}public ApiException(String message) {super(message);}public ApiException(Throwable cause) {super(cause);}public ApiException(String message, Throwable cause) {super(message, cause);}public IErrorCode getErrorCode() {return errorCode;}
}

断言类:Asserts

/*** 断言处理类,用于抛出各种API异常*/
public class Asserts {public static void fail(String message) {throw new ApiException(message);}public static void fail(IErrorCode errorCode) {throw new ApiException(errorCode);}
}

全局异常处理类:GlobalExceptionHandler

/*** 全局异常处理*/
@ControllerAdvice
public class GlobalExceptionHandler {@ResponseBody@ExceptionHandler(value = ApiException.class)public CommonResult handle(ApiException e) {if (e.getErrorCode() != null) {return CommonResult.failed(e.getErrorCode());}return CommonResult.failed(e.getMessage());}@ResponseBody@ExceptionHandler(value = MethodArgumentNotValidException.class)public CommonResult handleValidException(MethodArgumentNotValidException e) {BindingResult bindingResult = e.getBindingResult();String message = null;if (bindingResult.hasErrors()) {FieldError fieldError = bindingResult.getFieldError();if (fieldError != null) {message = fieldError.getField()+fieldError.getDefaultMessage();}}return CommonResult.validateFailed(message);}@ResponseBody@ExceptionHandler(value = BindException.class)public CommonResult handleValidException(BindException e) {BindingResult bindingResult = e.getBindingResult();String message = null;if (bindingResult.hasErrors()) {FieldError fieldError = bindingResult.getFieldError();if (fieldError != null) {message = fieldError.getField()+fieldError.getDefaultMessage();}}return CommonResult.validateFailed(message);}
}

Swagger

以下是固定写法

/*** Swagger基础配置*/
public abstract class BaseSwaggerConfig {@Beanpublic Docket createRestApi() {SwaggerProperties swaggerProperties = swaggerProperties();Docket docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo(swaggerProperties)).select().apis(RequestHandlerSelectors.basePackage(swaggerProperties.getApiBasePackage())).paths(PathSelectors.any()).build();//        if (swaggerProperties.isEnableSecurity()) {//            docket.securitySchemes(securitySchemes()).securityContexts(securityContexts());//        }return docket;}private ApiInfo apiInfo(SwaggerProperties swaggerProperties) {return new ApiInfoBuilder().title(swaggerProperties.getTitle()).description(swaggerProperties.getDescription()).contact(new Contact(swaggerProperties.getContactName(), swaggerProperties.getContactUrl(), swaggerProperties.getContactEmail())).version(swaggerProperties.getVersion()).build();}
}

Swagger自定义配置

/*** Swagger自定义配置*/
@Data
@EqualsAndHashCode(callSuper = false)
@Builder
public class SwaggerProperties {/*** API文档生成基础路径*/private String apiBasePackage;/*** 是否要启用登录认证*/private boolean enableSecurity;/*** 文档标题*/private String title;/*** 文档描述*/private String description;/*** 文档版本*/private String version;/*** 文档联系人姓名*/private String contactName;/*** 文档联系人网址*/private String contactUrl;/*** 文档联系人邮箱*/private String contactEmail;
}

Github开源项目详解--Mall(一)相关推荐

  1. Twain的学习记录和基于Qt的相关开源项目详解

    Twain学习记录和基于Qt的开源项目详解 twpp-qml 适用于 C++11 及更高版本的便携式.仅标头 TWAIN 框架. 原项目作者使用Qtwidget基于Twain静态传输一张图片 我基于h ...

  2. GitHub开源协议详解及常用协议介绍

    前言: GitHub有许多开源的协议,刚开始使用时不知道选择哪些协议,今天我们就一起科普一下GitHub中的协议吧! 一.协议介绍 1. None / No License 如果你在使用GitHub中 ...

  3. Android最新最全100余款开源App(对应Github开源项目)

    最近整理了一些开源的APP,以及对应的一些Github开源项目,现在做一些记录,以便以后查阅. 介绍与链接 BeautifulRefreshLayout-漂亮的美食下拉刷新 https://githu ...

  4. GitHub新手用法详解【适合新手入门-建议收藏!!!】

    目录 什么是Github,为什么使用它? 一.GitHub账号的注册与登录 二. gitbash安装详解 1.git bash的下载与安装 2.git常用命令 3. Git 和 GitHub 的绑定 ...

  5. (4.2.0)GitHub开源项目收集

    (4.2.0)GitHub开源项目收集 2016年09月24日 16:45:051543人阅读 评论(0) 收藏 举报  分类: 4.2-android开源组件(98)  版权声明:本文为博主原创文章 ...

  6. 快速学习MyBatis|实战项目详解

    作者主页:橙子! 主页 系列专栏:JavaWeb基础教程系列 精彩回顾:HTTP协议详解 文章目的:快速学习MyBatis及实战项目详解 文章目录 1.什么是MyBatis? 2. JDBC存在的缺点 ...

  7. GitHub使用教程详解——官网操作指南(翻译)

    GitHub使用教程详解--官网操作指南(翻译) GitHub使用指南 原文地址:GitHub官网 示例项目:Hello World 十分钟轻松教学 在学习计算机语言编程的过程中创建Hello Wor ...

  8. github开源项目大集合(1)

    github开源项目大集合 目前包括: Android 开源项目第一篇--个性化控件(View)篇  包括ListView.ActionBar.Menu.ViewPager.Gallery.GridV ...

  9. python怎么发布pycharm_如何使用PyCharm将代码上传到GitHub上(图文详解)

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 一丶说明 测试条件:需要有GitHub账号以及在本地安装了Git工具,无论是Linux环境还是Windows都是一样的 二丶上传配 ...

最新文章

  1. CentOS6.9编译安装Nginx1.12
  2. 学C++走游戏方向,是不是非常艰难自找苦吃?
  3. Python实现微信消息防撤回
  4. pythonmysql数据分析 tableau_python执行mysql 计算复购率+pyechart+Excel+Tableau绘制双Y轴图...
  5. Windows 平台的 Markdown 编辑器 : Typora
  6. 反射xss 测试地址_使用反射进行测试
  7. poj 3411 Paid Roads (dfs)
  8. 第一门编程语言,我应该学什么?
  9. 蓝桥杯 ALGO-150 算法训练 6-1 递归求二项式系数值 java版
  10. eclipse -- git 提示
  11. 精巧好用的DelayQueue
  12. linux so fprintf,Linux下printf、fprintf、sprintf的区别
  13. 计算机用户个人设置总是重启,联想电脑总是自动重启怎么回事
  14. 浏览器主页被篡改解决
  15. android 豁免的广播及广播白名单,浅谈BLE地址、隐私机制、白名单、定向广播
  16. Linux运维遇到的基本问题解决大全
  17. css链接的线,CSS网页技巧:实现超级链接文字上划线和下划线
  18. 改变radio默认样式
  19. html5 h5是什么,H5是什么?你真的了解什么是H5?
  20. 工程项目管理数字化系统建设持续推动施工企业数字化转型行稳致远

热门文章

  1. 2023年考生必读!PMP最强自学教程
  2. pc-com串口(DB9)管脚定义和单片机uart串口
  3. python 高斯函数拟合_在python中拟合任意高斯函数,消耗大量内存
  4. 数据通信与计算机网络答案,数据通信与计算机网络考试试题及答案
  5. PYNQ-Z2 HDMI输出图形实验
  6. 软件测试——常用的测试工具
  7. CI(CodeIgniter)框架入门
  8. [NOIP2017 普及组]跳房子 【题解】
  9. 什么是开源表单设计器?
  10. Haar特征类有哪些最新发表的毕业论文呢?