Toc
  1. 1. 实体类的生日是Date类型, 直接返回前端, 前端难处理
  2. 2. 根据经纬度计算距离
  3. 3. 新增代码与mybatis逆向工程冲突问题
  4. 4. 阿里云发送短信
    1. 5.2 BindingResult
  5. 1. 实体类的生日是Date类型, 直接返回前端, 前端难处理
  6. 2. 根据经纬度计算距离
  7. 3. 新增代码与mybatis逆向工程冲突问题
  8. 4. 阿里云发送短信
    1. 5.2 BindingResult
Toc
0 results found
Goblin
爬坑之路
2017/03/31 Code 笔记 业务

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;
/**
* @description 阿里云短信工具类
* @author goblin
* @date 2020-04-14
**/
@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);

//拼接模板参数(验证码)value Json格式字符串
//您本次的验证码为:${code},本验证码仅在5分钟内有效, 请勿告知他人, 如果不是您本人操作. 请忽略此信息. 欢迎您的加入!
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.id
#access.key.secret
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;

/**
* 品牌传递参数
* Created by goblin.
*/
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 = "更新品牌") //SWAGGER
@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. 那么什么时候使用这两个注解呢?

这就需要先了解这二者的不同.

  1. @Validated支持分组功能, 可以在入参验证时, 根据不同的分组采用不同的验证机制. 而@Valid不支持
  2. @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;

/**
* 品牌传递参数
* Created by goblin.
*/
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 // 嵌套验证必须用@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:

  • Code
    tags:
  • 业务
  • 笔记
    top: 0

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;
/**
* @description 阿里云短信工具类
* @author goblin
* @date 2020-04-14
**/
@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);

//拼接模板参数(验证码)value Json格式字符串
//您本次的验证码为:${code},本验证码仅在5分钟内有效, 请勿告知他人, 如果不是您本人操作. 请忽略此信息. 欢迎您的加入!
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.id
#access.key.secret
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;

/**
* 品牌传递参数
* Created by goblin.
*/
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 = "更新品牌") //SWAGGER
@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. 那么什么时候使用这两个注解呢?

这就需要先了解这二者的不同.

  1. @Validated支持分组功能, 可以在入参验证时, 根据不同的分组采用不同的验证机制. 而@Valid不支持
  2. @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;

/**
* 品牌传递参数
* Created by goblin.
*/
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 // 嵌套验证必须用@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

# 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
npm install --registry=https://registry.npm.taobao.org

# 本地开发 启动项目
npm run dev
打赏
支付宝
微信
本文作者:Goblin
版权声明:本文首发于Goblin的博客,转载请注明出处!