¶支付
¶微信的native支付和支付宝的网页支付
-
使用了一个第三方jar包best-pay-sdk,该SDK帮我们集成了微信支付、支付宝支付。主要的步骤是引入依赖,支付宝和微信的配置(appId,私钥,公钥,回调地址,异步通知地址),支付宝要开启沙箱支付,因为微信是需要企业资质才能进行native支付
@Component public class BestPayConfig { @Autowired AlipayAccountConfig alipayAccountConfig; @Bean public BestPayService bestPayService(){ //支付宝配置 AliPayConfig aliPayConfig = new AliPayConfig(); aliPayConfig.setAppId(alipayAccountConfig.getAppId()); aliPayConfig.setPrivateKey(alipayAccountConfig.getPrivateKey()); aliPayConfig.setAliPayPublicKey(alipayAccountConfig.getAliPayPublicKey()); aliPayConfig.setReturnUrl(alipayAccountConfig.getReturnUrl()); aliPayConfig.setNotifyUrl(alipayAccountConfig.getNotifyUrl()); aliPayConfig.setSandbox(alipayAccountConfig.isSandbox()); //支付类, 所有方法都在这个类里 BestPayServiceImpl bestPayService = new BestPayServiceImpl(); bestPayService.setAliPayConfig(aliPayConfig); return bestPayService; } }
这里面的配置使用软配置,这是一个好习惯,然后在application.yml里面进行内容配置,例如
/** * Created by RD */ @Component @ConfigurationProperties(prefix = "alipay") @Data public class AlipayAccountConfig { private String appId; private String privateKey; private String aliPayPublicKey; private String returnUrl; private String notifyUrl; private boolean sandbox; }
alipay: appId: 2021000117610811 privateKey: xxxxx aliPayPublicKey: xxxxx returnUrl: http://127.0.0.1 notifyUrl: http://67dg79.natappfree.cc/pay/notify sandbox: true
当然,这里还用到了内网穿透的一部分知识,以后还用到的时候再进行总结
¶枚举类也是解决硬编码问题的一种常用方法
@Getter
public enum PayPlatformEnum {
//1-支付宝,2-微信
ALIPAY(1),
WX(2),
;
Integer code;
PayPlatformEnum(Integer code) {
this.code = code;
}
}
¶商城
-
对于一些常用的常量,可以新建一个常量类
public class MallConst { public static final String CURRENT_USER = "currentUser"; public static final Integer ROOT_PARENT_ID = 0; }
-
对于需要返回给前端的数据,可以单独提出一个vo对象,这样原始的pojo只用来建表,同时,将需要返回给前端的数据可以封装成一个接受类vo对象,用于提示返回的状态,因为每一个单独的vo对象数据类型不一样,可以采用泛型程序设计,如下:
@Data @JsonInclude(value = JsonInclude.Include.NON_NULL) public class ResponseVo<T> { private Integer status; private String msg; private T data; public ResponseVo(Integer status, String msg) { this.status = status; this.msg = msg; } public ResponseVo(Integer status,T data){ this.status =status; this.data=data; } public static <T> ResponseVo<T> successByMsg(String msg){ return new ResponseVo<>(ResponseEnum.SUCCESS.getCode(),msg); } public static <T> ResponseVo<T> success(){ return new ResponseVo<>(ResponseEnum.SUCCESS.getCode(),ResponseEnum.SUCCESS.getDesc()); } public static <T> ResponseVo<T> success(T data){ return new ResponseVo<>(ResponseEnum.SUCCESS.getCode(),data); } public static <T> ResponseVo<T> error(ResponseEnum responseEnum){ return new ResponseVo<>(responseEnum.getCode(),responseEnum.getDesc()); } public static <T> ResponseVo<T> error(ResponseEnum responseEnum,String msg){ return new ResponseVo<>(responseEnum.getCode(),msg); } public static <T> ResponseVo<T> error(ResponseEnum responseEnum, BindingResult bindingResult){ return new ResponseVo<>(responseEnum.getCode(), Objects.requireNonNull(bindingResult.getFieldError()).getField()+" "+bindingResult.getFieldError().getDefaultMessage()); } }
-
可以单独提出一个提交类,用于封装需要提交的参数,并可以用
@Valid
进行字段的校验,同时,对待参数校验可能发生的异常,可以采用统一异常处理(@ControllerAdvice
),例如@ControllerAdvice public class RuntimeExceptionHandler { @ExceptionHandler(RuntimeException.class) @ResponseBody @ResponseStatus(HttpStatus.FORBIDDEN) public ResponseVo handle(RuntimeException e){ return ResponseVo.error(ResponseEnum.ERROR,e.getMessage()); } @ExceptionHandler(UserLoginException.class) @ResponseBody public ResponseVo userLoginHandle(){ return ResponseVo.error(ResponseEnum.NEED_LOGIN); } @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseBody public ResponseVo notValidExceptionHandle(MethodArgumentNotValidException e){ var bindingResult = e.getBindingResult(); return ResponseVo.error(ResponseEnum.PARAM_ERROR, Objects.requireNonNull(bindingResult.getFieldError()).getField()+" "+bindingResult.getFieldError().getDefaultMessage()); } }
@PostMapping("/user/login") public ResponseVo login(@Valid @RequestBody UserLoginForm userLoginForm, HttpSession session) { val userResponseVo = userService.login(userLoginForm.getUsername(), userLoginForm.getPassword()); session.setAttribute(MallConst.CURRENT_USER, userResponseVo.getData()); return userResponseVo; }
-
可以使用
BeanUtils.copyProperties
对vo对象和pojo对象进行相同属性的赋值private ProductVo product2ProductVo(Product product) { var productVo = new ProductVo(); BeanUtils.copyProperties(product, productVo); return productVo; }
-
对于用户的密码可以使用
springboot
自带的DigestUtils
类下的md5
进行加密user.setPassword(DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8)));
¶RabbitMq的使用
创建队列,支付端发送消息,商城接受消息,像这种中间件都需要引入jar包,在application.yml
中进行诸如地址密码用户名端口等参数配置
¶MySQL的使用技巧
对于sql的操作,尽量选择少操作数据库,对于需要操纵的数据,可以选择一次性查出来,然后再对数据进行筛选,这样效率会高一点,对于一些已经有了的数据,可以使用in表达式进行查询,避免for循环带来的查询效率损失
<select id="selectByOrderNoSet" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from mall_order_item
<where>
<if test="!orderNoSet.isEmpty()">
order_no in
<foreach collection="orderNoSet" item = "item" index="index" open="(" separator="," close=")">
#{item}
</foreach>
</if>
</where>
</select>
提高数据库的查询效率还可以使用map集合,例如
Set<Integer> productIdSet = cartList.stream()
.map(Cart::getProductId)
.collect(Collectors.toSet());
List<Product> productList = productMapper.selectByProductIdSet(productIdSet);
Map<Integer, Product> map = productList.stream()
.collect(Collectors.toMap(Product::getId, product -> product));
List<OrderItem> orderItemList = new ArrayList<>();
Long orderNo = generateOrderNo();
for (Cart cart : cartList) {
//根据productId查数据库
Product product = map.get(cart.getProductId());
...
...
¶Redis的使用技巧
Redis是不支持事务回滚的,详情可以看Redis事务不支持回滚
¶Stream的使用技巧
对于程序中的集合操作使用是很频繁的,使用Stream可以大量减少代码,代码呈现也更加美观,stream的使用看这个就够了
@Override
public ResponseVo<Integer> sum(Integer uid) {
Integer sum = listForCart(uid).stream()
.map(Cart::getQuantity)
.reduce(0, Integer::sum);
return ResponseVo.success(sum);
}
List<CategoryVo> categoryVoList = categories.stream()
.filter(e -> e.getParentId().equals(ROOT_PARENT_ID))
.map(this::category2CategoryVo)
.sorted(Comparator.comparing(CategoryVo::getSortOrder).reversed())
.collect(Collectors.toList());