首页 > 其他分享 >Github开源项目详解--Mall(一)

Github开源项目详解--Mall(一)

时间:2023-01-27 10:32:04浏览次数:78  
标签:Github return String -- mall Mall CommonResult message public


前言

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

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

项目地址:

​https://github.com/YuyanCai/mall​

从0开始一个开源项目

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

项目架构

mall-admin-web

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

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容器化部署。

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

前端项目为mall-admin-web

后端项目为mall

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

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

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

Github1s

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

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

前端代码架构

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

所以前端没啥大问题。

后端代码架构

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

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

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

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

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

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

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

总结

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

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

后台架构

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…都可以自动继承到子模块

common模块

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

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

一、枚举

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

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

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

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

项目中用到的是枚举的高级用法,向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的泛型_小蜗牛耶的博客

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 {

@Bean
public 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,return,String,--,mall,Mall,CommonResult,message,public
From: https://blog.51cto.com/u_14519396/6023991

相关文章

  • 谷粒商城--SPU和SKU(分组与属性关联、发布商品、仓库服务)
    分组与属性关联、发布商品、仓库服务​​分组与属性关联​​​​显示属性​​​​移除属性​​​​查询分组未关联的属性​​​​添加属性关联​​​​发布商品​​​​调试......
  • 谷粒商城--整合Elasticsearch和商品的上架
    整合Elasticsearch和商品的上架​​一、整合ES​​​​ES常用概念​​​​索引,类型,文档是什么?​​​​倒排索引​​​​相关度分数score的计算​​​​安装ES和Kibana​​​......
  • Javase多态(对多态的理解、多态的体现)
    前言刚开始学Java的时候对多态这个概念一直没弄太明白,随着后面的学习无意中都在用着多态,又看了一些文章决定整理一下!举例创建一个Person类,Student类继承Person类,并分别创建......
  • 统一观测丨如何使用 Prometheus 监控 MySQL
     MySQL作为最流行的关系型数据库管理系统之一,非常多系统的后端存储都有着MySQL的身影,可谓是广泛应用于各行各业。与此同时,数据库作为应用服务的核心组件,直接影响着应......
  • 多项式模板
    多项式模板\(\text{导数运算法则}\)$(x\pmy)'=x'\pmy'$$(ax)'=ax'$(\(a\)为常数)\((xy)'=x'y+xy'\)$(\displaystyle\frac{x}{y})=\displaystyle......
  • 统一观测丨如何使用 Prometheus 监控 MySQL
     MySQL作为最流行的关系型数据库管理系统之一,非常多系统的后端存储都有着MySQL的身影,可谓是广泛应用于各行各业。与此同时,数据库作为应用服务的核心组件,直接影响着应......
  • 第五十七章 历史监视器 - 汇总
    第五十七章历史监视器-汇总汇总ApplicationMonitor执行的%Monitor.System.HistoryPerf和%Monitor.System.HistorySys类也在每天结束时创建每小时和每天的摘要。......
  • 【学习笔记】组合数学学习笔记
    参考资料:《组合数学》,OI-Wiki排列组合四个计数原理加法原理:并列的方案数加和。乘法原理:叠加的方案数相乘。减法原理:正难则反,补集转换。除法原理:目测用处不大......
  • ximo脱壳-手脱tElock 0.98b1壳、exe32pack壳、WinUpack加的壳
    一、手脱tElock0.98b1壳用最后一次异常法首先使用OD载入程序  然后不停的用shift+F9,直到程序运行起来,记录下程序运行起来的前一次的次数,然后执行到那一次 上图......
  • 以SQL SERVER和C#聊聊微软生态趋势中产品跨生态组合
    众所周知,老牌大厂微软intel都在裁人,国内短周期的互联网大厂(菊厂,狗厂,福报厂,开水团,鹅厂)更是如此。拿微软来说很多基础软件产品,SQLSERVER,C#开发语言,F#开发语言,Access,POWERBI......