文章目录
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200528165436850.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1pIT1VKSUFOX1RBTks=,size_16,color_FFFFFF,t_70)
- 前端采用
Metronic
模板 - 在接入层进行一些异常处理,并定义前后端分离数据交互的一个格式
- 业务层进行业务的处理,并进行后端数据的组合交给前端
- 数据层:使用事务、数据Dao进行持久化
- 数据库:
MySQL
,redis
通用工具的设计
1、后端传给前端的数据
为了实现前后端分离,后端传给前端的数据,需要封装成一个统一的格式,之后前端拿到后端传过来的数据,对数据进行解析,然后对页面进行渲染。
定义一个类,对数据格式进行统一的封装
- status: 状态,数据是否处理成功
- data:后端传给前端的数据,前端对这个数据进行解析,若status为success则返回正常的数据,若status为fail则返回错误的异常信息
public class CommonReturnType {
//表明对应请求的返回的处理结果 “success"或"fail"
private String status;
//若status=success则data内返回前端需要的数据
//若status=fail,则data内使用通用的错误吗格式
private Object data;
后端返回给前端的数据,只有这一种格式, status+data
,如下所示
status状态位success
{
"status": "success",
"data": {
"id": 3,
"title": "剑指Offer树部分解析",
"price": 123.00,
"stock": 4,
"description": "测试",
"sales": 0,
"imgUrl": "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1590575581472&di=b9767ddbccb08a760b3a913d09a72a21&imgtype=0&src=http%3A%2F%2Fimg1.imgtn.bdimg.com%2Fit%2Fu%3D3175508956%2C2902264390%26fm%3D214%26gp%3D0.jpg",
"promoPrice": null,
"startDate": null,
"promoId": null,
"promoStatus": 0
}
}
status状态位fail
{
"status": "fail",
"data": {
"errCode": 20001,
"errMsg": "用户不存在"
}
}
封装错误信息枚举类
在后端返回给前端数据时,当返回错误信息时,需要返回错误信息,那么我们可以将错误信息进行
集中管理
这样就可以方便管理
当发生错误信息的时候,将异常抛出
throw BusineessException(EmBusinessError)
,可以对这个异常进行拦截,通过对信息进行封装,解析数据,返回CommonReturnType.create()
CommonError
//错误接口
public interface CommonError {
//接口中定义errMsg errCode等方法
public int getErrCode();
public String getErrMsg();
//自定义错误消息
public CommonError setErrMsg(String errMsg);
}
错误异常枚举类
EmBusinessError
public enum EmBusinessError implements CommonError {
//通用错误类型 00001
PARAMETER_VALIDATION_ERROR(10001,"参数不合法"),
UNKWON_ERROR(10002,"未知错误"),
//2000开头为用户信息相关错误定义
USER_NOT_EXIST(20001,"用户不存在"),
USER_NOT_LOGIN(20003,"用户还未登录"),
USER_LOGIN_FAIL(20002,"用户或手机号密码不正确"),
ITEM_NOT_EXIST(30001,"商品不存在"),
//4000开头为交易信息错误
STOCK_NOT_ENOUGH(40001,"库存不足");
private int errCode;
private String errMsg;
EmBusinessError(int errCode, String errMsg) {
this.errCode = errCode;
this.errMsg = errMsg;
}
@Override
public int getErrCode() {
return this.errCode;
}
@Override
public String getErrMsg() {
return this.errMsg;
}
@Override
public CommonError setErrMsg(String errMsg) {
this.errMsg=errMsg;
return this;
}
}
业务异常类实现
public class BusinessException extends Exception implements CommonError{
private CommonError commonError;
//直接接受EmBusinessError的传参用于构造业务异常
public BusinessException(CommonError commonError) {
super();//继承异常类中的消息
this.commonError = commonError;
}
//接受自定义errMsg的方式构造业务异常
public BusinessException(CommonError commonError,String errorMsg) {
super();
this.commonError = commonError;
this.commonError.setErrMsg(errorMsg);
}
@Override
public int getErrCode() {
return this.commonError.getErrCode();
}
@Override
public String getErrMsg() {
return this.commonError.getErrMsg();
}
@Override
public CommonError setErrMsg(String errMsg) {
this.commonError.setErrMsg(errMsg);
return this;
}
}
BaseCOntroller对业务报错的信息进行统一的解析返回给前端数据
通过对业务错误异常信息进行统一处理,从而对返回给前端
json
统一的格式
@ExceptionHandler(Exception.class)
:注解表明该类用于处理异常
@ResponseStatus(HttpStatus.OK)
:
@ResponseBosy
:在响应体中返回数据:Marks a method or exception class with the status {@link #code} and
@Controller
public class BaseController {
//定义ajax请求中的contentType
public static final String CONTENT_TYPE_FORMED = "application/x-www-form-urlencoded";
/*
定义一个exceptionhandler解决没有被controller层吸收的异常
*/
@ExceptionHandler(Exception.class) //用于处理错误
@ResponseStatus(HttpStatus.OK) //状态码为200
@ResponseBody
public Object handlerException(HttpServletRequest request, Exception ex) {
//定义返回的数据
Map<String, Object> responseData = new HashMap<>();
//对业务异常进行解析
if (ex instanceof BusinessException) {
//ex抛出的信息太多,建议把Exception转化为自定义的BusinessException
BusinessException businessException = (BusinessException) ex;
responseData.put("errCode", businessException.getErrCode());
responseData.put("errMsg", businessException.getErrMsg());
} else {
responseData.put("errCode", EmBusinessError.UNKWON_ERROR.getErrCode());
responseData.put("errMsg", EmBusinessError.UNKWON_ERROR.getErrMsg());
}
//!!!!
//返回异常,返回给前端数据
return CommonReturnType.create(responseData,"fail");
}
}
跨域实现请求 @CrossOrigin
在调试的过程中,为了实现跨域的服务,可以加上
@CrossOrigin
这个注解
@CrossOrigin(origins = {"*"},allowCredentials = "true")//允许跨域请求
//origins:所有支持域的集合,
//allowCredentials:是否允许cookie随请求发送,使用时必须指定具体的域
同时,前端也需要进行配置
② jquery需要再每次使用ajax时增加如下配置
xhrFields:{
withCredentials:true
}
自动生成mapper
我们可以根据数据库中的表,使用一定的配置,生成基础的Mapper,然后在其基础上进行改进
pom依赖
<!--配置mybatis自动生成器-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.5</version>
<dependencies>
<dependency>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-core</artifactId>
<version>1.3.5</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.41</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>mybatis.generator</id>
<phase>package</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<!--允许移动生成的文件-->
<verbose>true</verbose>
<!--允许自动覆盖文件-->
<!--慎用-->
<overwrite>false</overwrite>
<configurationFile>
src/main/resources/mybatis-generator.xml
</configurationFile>
</configuration>
</plugin>
配置文件 mybatis-generator.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
"http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
<context id="DB2Tables" targetRuntime="MyBatis3">
<!--数据库地址账号密码-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver"
<!--连接数据的名字-->
connectionURL="jdbc:mysql://47.98.144.17:3306/miaosha"
userId="root"
password="123456">
</jdbcConnection>
<javaTypeResolver >
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--生成DataObkect类存放地址-->
<javaModelGenerator targetPackage="com.zj.miaoshaproject.dataObject" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--生成映射文件存放地址-->
<sqlMapGenerator targetPackage="mapping" targetProject="src/main/resources">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!--生成Dao类存放位置-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.zj.miaoshaproject.dao" targetProject="src/main/java">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!--生成对应表及类名-->
<table tableName="user_info" domainObjectName="UserDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="user_password" domainObjectName="UserPasswordDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="item" domainObjectName="ItemDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="item_stock" domainObjectName="ItemStockDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="order_info" domainObjectName="OrderDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="sequence_info" domainObjectName="SequenceDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
<table tableName="promo" domainObjectName="PromoDO" enableCountByExample="false"
enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false"
selectByExampleQueryId="false"></table>
</context>
</generatorConfiguration>
各层Model(以及各个Model转换)
在开发中,每层都有其对应的模型,这样是根据功能来的,每层的数据有 不同作用
前端层次VO
:是给用户可以显示的数据(需要根据业务单独抽象出来)后端层次Model
:业务层次,需要根据业务对模型进行聚合,方便操作数据库层次DO
:数据库层次,这个就是为了方便对数据库中的字段进行操作
对于不同数据的组合,需要对数据进行转换,为了方便进行转换,可以使用
BeanUtils.copy(source,desitination)
注意各个字段要匹配,否则会出现赋值为空的情况
对字段进行校验
为了方便对各个字段进行校验,可以使用
javax.validation.Validation
提供的校验功能
校验器
package com.zj.miaoshaproject.validator;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
@Component
public class ValidatorImpl implements InitializingBean {
private Validator validator;
/**
*
* @param bean 需要校验的bean
* @return
*/
public ValidationResult validate(Object bean) {
//构造校验的结果
ValidationResult validationResult = new ValidationResult();
//对bean进行校验
Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(bean);
//集合大于0 说明该校验的字段有错
if (constraintViolationSet.size() > 0) {
validationResult.setHasErroes(true);
//将map集合中的错误遍历
constraintViolationSet.forEach(objectConstraintViolation -> {
String propertyName = objectConstraintViolation.getPropertyPath().toString();
String errMsg = objectConstraintViolation.getMessage();
validationResult.getErrorMsgMap().put(propertyName, errMsg);
});
}
//返回校验的结果
return validationResult;
}
//实例化validator
@Override
public void afterPropertiesSet() throws Exception {
//通过工厂的方式使得validator实例化
this.validator = Validation.buildDefaultValidatorFactory().getValidator();
}
}
校验结果的封装
package com.zj.miaoshaproject.validator;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
//校验的结果
public class ValidationResult {
//校验结果是否有错
private boolean hasErroes = false;
//存放错误的信息
private Map<String, String> errorMsgMap = new HashMap<>();
//实现通用的格式化字符串星系获取错误
public String getErrMsg() {
return StringUtils.join(errorMsgMap.values().toArray(), ",");
}
//
public boolean isHasErroes() {
return hasErroes;
}
public void setHasErroes(boolean hasErroes) {
this.hasErroes = hasErroes;
}
public Map<String, String> getErrorMsgMap() {
return errorMsgMap;
}
public void setErrorMsgMap(Map<String, String> errorMsgMap) {
this.errorMsgMap = errorMsgMap;
}
}
可以在服务层Model上加上注解进行校验
public class ItemModel {
private Integer id;
//商品名称
@NotBlank(message = "商品名称不能为空")
private String title;
//商品价格
@NotNull(message = "商品价格不能为了拍卖行")
@Min(value = 0,message = "商品价格必须大于0")
private BigDecimal price;
//商品库存
@NotNull(message = "库存不能不填")
private Integer stock;
在需要的地方进行校验
@Override
@Transactional
public ItemModel createItem(ItemModel itemModel) throws BusinessException {
ValidationResult validationResult = validator.validate(itemModel);
//校验入参
if(validationResult.isHasErroes()){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,validationResult.getErrMsg());
}
//转换ItemModel--->dataObject
ItemDO itemDO = convertItemDOFromItemModel(itemModel);