1. 实体类的生日是Date类型, 直接返回前端, 前端难处理
使用注解:
@JsonFormat(shape=JsonFormat.Shape.STRING,pattern="yyyy-MM-dd",timezone="GMT+8") private Date birthday;
|
就可在传给前端时, 格式化日期为:
{ "code": 200, "message": "操作成功", "data": { "birthday": "2020-04-08", } }
|
2. 根据经纬度计算距离
已有用户的经纬度和商家的经纬度, 通过mysql sql语句根据距离排序查询出所有商家, 并将计算出的距离返回.
SQL语句如下:
参数含义locationX-用户经度
locationY-用户纬度
lng-数据库中商家经度
lat-数据库中商家纬度
将距离计算出, 别名distance和其他数据一起返回
<select id="selectAllByDistance" resultMap="ydListWithDistanceMap"> SELECT DISTINCT *,( round(6371392.89 * acos ( cos ( radians(#{locationY,jdbcType=DECIMAL}) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(#{locationX,jdbcType=DECIMAL}) ) + sin ( radians(#{locationY,jdbcType=DECIMAL}) ) * sin( radians( lat ) ) ),2 ) )AS distance FROM yd_shop ORDER BY distance select>
|
经测试 误差与百度地图计算距离控制在三十米之内.
3. 新增代码与mybatis逆向工程冲突问题
新增的代码会被mybatis逆向工程覆盖, 如果配置关闭覆盖, 那么以后对该表做改动的时候又要重写新增的部分. 解决: 将新增的内容, 写在新建Dao中, 令xml文件继承源mapper.xml, 这样新增内容时, 就不担心被覆盖了.
mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.macro.mall.portal.dao.YdShopDao"> <resultMap id="ydListWithDistanceMap" type="com.macro.mall.portal.domain.YdShop" extends="com.macro.mall.mapper.YdShopMapper.BaseResultMap"> <result column="distance" jdbcType="DECIMAL" property="distance" /> resultMap>
<select id="selectAllByDistance" resultMap="ydListWithDistanceMap"> SELECT DISTINCT *,( round(6371392.89 * acos ( cos ( radians(#{locationY,jdbcType=DECIMAL}) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(#{locationX,jdbcType=DECIMAL}) ) + sin ( radians(#{locationY,jdbcType=DECIMAL}) ) * sin( radians( lat ) ) ),2 ) )AS distance FROM yd_shop ORDER BY distance select> mapper>
|
4. 阿里云发送短信
工具类:
package com.macro.mall.portal.util;
import com.aliyuncs.CommonRequest; import com.aliyuncs.CommonResponse; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import lombok.extern.slf4j.Slf4j;
import java.io.IOException; import java.io.InputStream; import java.util.Properties;
@Slf4j public class AliMessageUtils {
public static final String CONFIG_FILE = "rabbitmq-message.properties"; public static String REGION_ID; public static String ACCESS_KEY_ID; public static String ACCESS_KEY_SECRET; public static String SIGN_NAME; public static String TEMPLATE_CODE;
static { Properties prop = new Properties(); try (InputStream is = QiniuUtils.class.getClassLoader().getResourceAsStream("rabbitmq-message.properties")) { if (null == is) { log.error("[阿里云短信工具类-初始化]失败,请提供配置文件:{}", CONFIG_FILE); } else { prop.load(is); } } catch (IOException e) { throw new RuntimeException(e); } REGION_ID = prop.getProperty("region.id", ""); ACCESS_KEY_ID = prop.getProperty("access.key.id", ""); ACCESS_KEY_SECRET = prop.getProperty("access.key.secret", ""); SIGN_NAME = prop.getProperty("sign.name", ""); TEMPLATE_CODE = prop.getProperty("template.code", ""); log.info("[阿里云工具类-初始化]完成"); }
public static void sendSMS(String phone, String code) { DefaultProfile profile = DefaultProfile.getProfile(REGION_ID, ACCESS_KEY_ID, ACCESS_KEY_SECRET); IAcsClient client = new DefaultAcsClient(profile);
String codeSMS = "{\"code\":\"" + code + "\"}";
CommonRequest request = new CommonRequest(); request.setMethod(MethodType.POST); request.setDomain("dysmsapi.aliyuncs.com"); request.setVersion("2017-05-25"); request.setAction("SendSms"); request.putQueryParameter("RegionId", REGION_ID); request.putQueryParameter("PhoneNumbers", phone); request.putQueryParameter("SignName", SIGN_NAME); request.putQueryParameter("TemplateCode", TEMPLATE_CODE); request.putQueryParameter("TemplateParam", codeSMS);
try { CommonResponse response = client.getCommonResponse(request); System.out.println(response.getData()); } catch (ClientException e) { e.printStackTrace(); } } }
|
配置文件
rabbitmq-message.properties
region.id=cn-hangzhou
access.key.id=access.key.id
access.key.secret=access.key.secret
sign.name=签名
template.code=短信模板
|
####5. 使用@Valid+BindingResult进行controller参数校验
场景: 做后台的修改等操作时, 我们需要在后台验证传入的表单是否正确, 因为前端的验证可能是由postman等代发的, 前端表单校验不能保证绝对安全.
通常我们会定义一个要接收表单的实体类, 这里给出一个例子, 是品牌相关的类 PmsBrandParam:
package com.macro.mall.dto;
import com.macro.mall.validator.FlagValidator; import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty;
public class PmsBrandParam {
@NotEmpty(message = "名称不能为空") private String name;
@Min(value = 0, message = "排序最小为0") private Integer sort; @FlagValidator(value = {"0","1"}, message = "厂家状态不正确") private Integer factoryStatus; @FlagValidator(value = {"0","1"}, message = "显示状态不正确") private Integer showStatus; @NotEmpty(message = "品牌logo不能为空") private String logo; }
|
这里省略了部分字段, 通常这些子弹都是与数据库所需字段一一对应的, 我们要保存一个新数据时, 通过这个参数类保证数据正确.
再来看一下Controller: (这里的BindingResult, 会在下文介绍)
@ApiOperation(value = "更新品牌") @RequestMapping(value = "/update/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult update(@PathVariable("id") Long id, @Validated @RequestBody PmsBrandParam pmsBrandParam, BindingResult result) { CommonResult commonResult; int count = brandService.updateBrand(id, pmsBrandParam); if (count == 1) { commonResult = CommonResult.success(count); } else { commonResult = CommonResult.failed(); } return commonResult; }
|
在PmsBrandParam类的前面, 用到了@Validated注解, 这个注解的作用是提醒Spring Validation验证框架对参数进行验证.
其实除了@Validated, 还有一个注解很常见, @Valid. 那么什么时候使用这两个注解呢?
这就需要先了解这二者的不同.
- @Validated支持分组功能, 可以在入参验证时, 根据不同的分组采用不同的验证机制. 而@Valid不支持
- @Validated可以注解在方法, 构造函数, 方法参数上, @Valid在此基础上, 还可以注解在成员属性(字段)上
留意一下这个区别, 因为这直接影响是否支持嵌套验证的功能, 下面会详细解释
<这部分内容参考 https://blog.csdn.net/qq_27680317/article/details/79970590>
所谓嵌套循环, 指的是我们的PmsBrandParam中, 如果有一个成员变量是另一个实体类型, 而他的成员变量同样需要进行校验, 那么, 因为@Validated无法注释在该实体类上, 所以不能仅通过@Validated实现嵌套的校验.
可行的方法为:
package com.macro.mall.dto;
import com.macro.mall.validator.FlagValidator; import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty;
public class PmsBrandParam {
@NotEmpty(message = "名称不能为空") private String name;
@Min(value = 0, message = "排序最小为0") private Integer sort; @FlagValidator(value = {"0","1"}, message = "厂家状态不正确") private Integer factoryStatus; @FlagValidator(value = {"0","1"}, message = "显示状态不正确") private Integer showStatus; @NotEmpty(message = "品牌logo不能为空") private String logo; @Valid @NotNull(message = "shops不能为空") @Size(min = 1, message = "props至少要有一个自定义属性") private List shops; }
|
注意观察最后一个字段shops, 他上面因为使用了@Valid注解, 在校验时, 会同时验证他里面的字段. 我们这里给出一个YdShop的例子, 方便理解:
public class YdShop {
@NotNull(message = "id不能为空") @Min(value = 1, message = "id必须为正整数") private Long id;
@NotBlank(message = "name不能为空") private String name;
}
|
#####5.1 常用注解校验
常用校验注解:
@Null 只能是null @NotNull 不能为null 注意用在基本类型上无效,基本类型有默认初始值 @AssertFalse 必须为false @AssertTrue 必须是true
字符串/数组/集合检查:(字符串本身就是个数组) @Pattern(regexp="reg") 验证字符串满足正则 @Size(max, min) 验证字符串、数组、集合长度范围 @NotEmpty 验证字符串不为空或者null @NotBlank 验证字符串不为null或者trim()后不为空
数值检查:同时能验证一个字符串是否是满足限制的数字的字符串 @Max 规定值得上限int @Min 规定值得下限 @DecimalMax("10.8") 以传入字符串构建一个BigDecimal,规定值要小于这个值 @DecimalMin 可以用来限制浮点数大小 @Digits(int1, int2) 限制一个小数,整数精度小于int1;小数部分精度小于int2 @Digits 无参数,验证字符串是否合法 @Range(min=long1,max=long2) 检查数字是否在范围之间 这些都包括边界值
日期检查:Date/Calendar @Post 限定一个日期,日期必须是过去的日期 @Future 限定一个日期,日期必须是未来的日期
其他验证: @Vaild 递归验证,用于对象、数组和集合,会对对象的元素、数组的元素进行一一校验 @Email 用于验证一个字符串是否是一个合法的右键地址,空字符串或null算验证通过 @URL(protocol=,host=,port=,regexp=,flags=) 用于校验一个字符串是否是合法UR
|
5.2 BindingResult
title: 爬坑之路
date: 2017/3/31 18:50:25
categories:
1. 实体类的生日是Date类型, 直接返回前端, 前端难处理
使用注解:
@JsonFormat(shape=JsonFormat.Shape.STRING,pattern="yyyy-MM-dd",timezone="GMT+8") private Date birthday;
|
就可在传给前端时, 格式化日期为:
{ "code": 200, "message": "操作成功", "data": { "birthday": "2020-04-08", } }
|
2. 根据经纬度计算距离
已有用户的经纬度和商家的经纬度, 通过mysql sql语句根据距离排序查询出所有商家, 并将计算出的距离返回.
SQL语句如下:
参数含义locationX-用户经度
locationY-用户纬度
lng-数据库中商家经度
lat-数据库中商家纬度
将距离计算出, 别名distance和其他数据一起返回
<select id="selectAllByDistance" resultMap="ydListWithDistanceMap"> SELECT DISTINCT *,( round(6371392.89 * acos ( cos ( radians(#{locationY,jdbcType=DECIMAL}) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(#{locationX,jdbcType=DECIMAL}) ) + sin ( radians(#{locationY,jdbcType=DECIMAL}) ) * sin( radians( lat ) ) ),2 ) )AS distance FROM yd_shop ORDER BY distance select>
|
经测试 误差与百度地图计算距离控制在三十米之内.
3. 新增代码与mybatis逆向工程冲突问题
新增的代码会被mybatis逆向工程覆盖, 如果配置关闭覆盖, 那么以后对该表做改动的时候又要重写新增的部分. 解决: 将新增的内容, 写在新建Dao中, 令xml文件继承源mapper.xml, 这样新增内容时, 就不担心被覆盖了.
mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.macro.mall.portal.dao.YdShopDao"> <resultMap id="ydListWithDistanceMap" type="com.macro.mall.portal.domain.YdShop" extends="com.macro.mall.mapper.YdShopMapper.BaseResultMap"> <result column="distance" jdbcType="DECIMAL" property="distance" /> resultMap>
<select id="selectAllByDistance" resultMap="ydListWithDistanceMap"> SELECT DISTINCT *,( round(6371392.89 * acos ( cos ( radians(#{locationY,jdbcType=DECIMAL}) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(#{locationX,jdbcType=DECIMAL}) ) + sin ( radians(#{locationY,jdbcType=DECIMAL}) ) * sin( radians( lat ) ) ),2 ) )AS distance FROM yd_shop ORDER BY distance select> mapper>
|
4. 阿里云发送短信
工具类:
package com.macro.mall.portal.util;
import com.aliyuncs.CommonRequest; import com.aliyuncs.CommonResponse; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import lombok.extern.slf4j.Slf4j;
import java.io.IOException; import java.io.InputStream; import java.util.Properties;
@Slf4j public class AliMessageUtils {
public static final String CONFIG_FILE = "rabbitmq-message.properties"; public static String REGION_ID; public static String ACCESS_KEY_ID; public static String ACCESS_KEY_SECRET; public static String SIGN_NAME; public static String TEMPLATE_CODE;
static { Properties prop = new Properties(); try (InputStream is = QiniuUtils.class.getClassLoader().getResourceAsStream("rabbitmq-message.properties")) { if (null == is) { log.error("[阿里云短信工具类-初始化]失败,请提供配置文件:{}", CONFIG_FILE); } else { prop.load(is); } } catch (IOException e) { throw new RuntimeException(e); } REGION_ID = prop.getProperty("region.id", ""); ACCESS_KEY_ID = prop.getProperty("access.key.id", ""); ACCESS_KEY_SECRET = prop.getProperty("access.key.secret", ""); SIGN_NAME = prop.getProperty("sign.name", ""); TEMPLATE_CODE = prop.getProperty("template.code", ""); log.info("[阿里云工具类-初始化]完成"); }
public static void sendSMS(String phone, String code) { DefaultProfile profile = DefaultProfile.getProfile(REGION_ID, ACCESS_KEY_ID, ACCESS_KEY_SECRET); IAcsClient client = new DefaultAcsClient(profile);
String codeSMS = "{\"code\":\"" + code + "\"}";
CommonRequest request = new CommonRequest(); request.setMethod(MethodType.POST); request.setDomain("dysmsapi.aliyuncs.com"); request.setVersion("2017-05-25"); request.setAction("SendSms"); request.putQueryParameter("RegionId", REGION_ID); request.putQueryParameter("PhoneNumbers", phone); request.putQueryParameter("SignName", SIGN_NAME); request.putQueryParameter("TemplateCode", TEMPLATE_CODE); request.putQueryParameter("TemplateParam", codeSMS);
try { CommonResponse response = client.getCommonResponse(request); System.out.println(response.getData()); } catch (ClientException e) { e.printStackTrace(); } } }
|
配置文件
rabbitmq-message.properties
region.id=cn-hangzhou
access.key.id=access.key.id
access.key.secret=access.key.secret
sign.name=签名
template.code=短信模板
|
####5. 使用@Valid+BindingResult进行controller参数校验
场景: 做后台的修改等操作时, 我们需要在后台验证传入的表单是否正确, 因为前端的验证可能是由postman等代发的, 前端表单校验不能保证绝对安全.
通常我们会定义一个要接收表单的实体类, 这里给出一个例子, 是品牌相关的类 PmsBrandParam:
package com.macro.mall.dto;
import com.macro.mall.validator.FlagValidator; import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty;
public class PmsBrandParam {
@NotEmpty(message = "名称不能为空") private String name;
@Min(value = 0, message = "排序最小为0") private Integer sort; @FlagValidator(value = {"0","1"}, message = "厂家状态不正确") private Integer factoryStatus; @FlagValidator(value = {"0","1"}, message = "显示状态不正确") private Integer showStatus; @NotEmpty(message = "品牌logo不能为空") private String logo; }
|
这里省略了部分字段, 通常这些子弹都是与数据库所需字段一一对应的, 我们要保存一个新数据时, 通过这个参数类保证数据正确.
再来看一下Controller: (这里的BindingResult, 会在下文介绍)
@ApiOperation(value = "更新品牌") @RequestMapping(value = "/update/{id}", method = RequestMethod.POST) @ResponseBody public CommonResult update(@PathVariable("id") Long id, @Validated @RequestBody PmsBrandParam pmsBrandParam, BindingResult result) { CommonResult commonResult; int count = brandService.updateBrand(id, pmsBrandParam); if (count == 1) { commonResult = CommonResult.success(count); } else { commonResult = CommonResult.failed(); } return commonResult; }
|
在PmsBrandParam类的前面, 用到了@Validated注解, 这个注解的作用是提醒Spring Validation验证框架对参数进行验证.
其实除了@Validated, 还有一个注解很常见, @Valid. 那么什么时候使用这两个注解呢?
这就需要先了解这二者的不同.
- @Validated支持分组功能, 可以在入参验证时, 根据不同的分组采用不同的验证机制. 而@Valid不支持
- @Validated可以注解在方法, 构造函数, 方法参数上, @Valid在此基础上, 还可以注解在成员属性(字段)上
留意一下这个区别, 因为这直接影响是否支持嵌套验证的功能, 下面会详细解释
<这部分内容参考 https://blog.csdn.net/qq_27680317/article/details/79970590>
所谓嵌套循环, 指的是我们的PmsBrandParam中, 如果有一个成员变量是另一个实体类型, 而他的成员变量同样需要进行校验, 那么, 因为@Validated无法注释在该实体类上, 所以不能仅通过@Validated实现嵌套的校验.
可行的方法为:
package com.macro.mall.dto;
import com.macro.mall.validator.FlagValidator; import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty;
public class PmsBrandParam {
@NotEmpty(message = "名称不能为空") private String name;
@Min(value = 0, message = "排序最小为0") private Integer sort; @FlagValidator(value = {"0","1"}, message = "厂家状态不正确") private Integer factoryStatus; @FlagValidator(value = {"0","1"}, message = "显示状态不正确") private Integer showStatus; @NotEmpty(message = "品牌logo不能为空") private String logo; @Valid @NotNull(message = "shops不能为空") @Size(min = 1, message = "props至少要有一个自定义属性") private List shops; }
|
注意观察最后一个字段shops, 他上面因为使用了@Valid注解, 在校验时, 会同时验证他里面的字段. 我们这里给出一个YdShop的例子, 方便理解:
public class YdShop {
@NotNull(message = "id不能为空") @Min(value = 1, message = "id必须为正整数") private Long id;
@NotBlank(message = "name不能为空") private String name;
}
|
#####5.1 常用注解校验
常用校验注解:
@Null 只能是null @NotNull 不能为null 注意用在基本类型上无效,基本类型有默认初始值 @AssertFalse 必须为false @AssertTrue 必须是true
字符串/数组/集合检查:(字符串本身就是个数组) @Pattern(regexp="reg") 验证字符串满足正则 @Size(max, min) 验证字符串、数组、集合长度范围 @NotEmpty 验证字符串不为空或者null @NotBlank 验证字符串不为null或者trim()后不为空
数值检查:同时能验证一个字符串是否是满足限制的数字的字符串 @Max 规定值得上限int @Min 规定值得下限 @DecimalMax("10.8") 以传入字符串构建一个BigDecimal,规定值要小于这个值 @DecimalMin 可以用来限制浮点数大小 @Digits(int1, int2) 限制一个小数,整数精度小于int1;小数部分精度小于int2 @Digits 无参数,验证字符串是否合法 @Range(min=long1,max=long2) 检查数字是否在范围之间 这些都包括边界值
日期检查:Date/Calendar @Post 限定一个日期,日期必须是过去的日期 @Future 限定一个日期,日期必须是未来的日期
其他验证: @Vaild 递归验证,用于对象、数组和集合,会对对象的元素、数组的元素进行一一校验 @Email 用于验证一个字符串是否是一个合法的右键地址,空字符串或null算验证通过 @URL(protocol=,host=,port=,regexp=,flags=) 用于校验一个字符串是否是合法UR
|
5.2 BindingResult
####6. 不使用cnpm安装, 而是通过指定地址的方式
git clone https://github.com/PanJiaChen/vue-element-admin.git
cd vue-element-admin
npm install
npm install --registry=https://registry.npm.taobao.org
npm run dev
|