Java网络商城项目 SpringBoot+SpringCloud+Vue 网络商城(SSM前后端分离项目)二十二(下单和微信支付)(1)

import com.leyou.common.mapper.BaseMapper;

import com.leyou.order.pojo.OrderStatus;

public interface OrderStatusMapper extends BaseMapper {

}

1.8.完善OrderService


在这里插入图片描述

package com.leyou.order.service;

import com.leyou.order.mapper.OrderDetailMapper;

import com.leyou.order.mapper.OrderMapper;

import com.leyou.order.mapper.OrderStatusMapper;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

@Service

public class OrderService {

@Autowired

private OrderMapper orderMapper;

@Autowired

private OrderDetailMapper detailMapper;

@Autowired

private OrderStatusMapper statusMapper;

}

1.9.完善OrderController


在这里插入图片描述

2.0.实现orderService的createOrder方法


在这里插入图片描述

创建订单逻辑比较复杂,需要组装订单数据,基本步骤如下:

  • 获取登录用户信息

  • 生成订单编号,初始化订单基本信息

  • 查询收货人信息

  • 查询商品信息

  • 封装OrderDetail信息

  • 计算总金额、实付金额

  • 保存订单状态信息

  • 删除购物车中已购买商品减库存

2.0.1生成订单编号(雪花算法)

雪花算法是由Twitter公司开源的snowflake(雪花)算法。

  • 雪花算法的原理

雪花算法会生成一个64位的二进制数据,为一个Long型。

(转换成字符串后长度最多19),其基本结构:

在这里插入图片描述

第一位:为未使用

第二部分:41位为毫秒级时间(41位的长度可以使用69年)

第三部分:5位datacenterld和5位workerld(10位的长度最多支持部署1024个节点)

第四部分:最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

snowflake生成的ID整体上按照时间自增排序,

并且整个分布式系统内不会产生ID碰撞(由datacenterworkerld作区分),

并且效率较高。

经测试snowflake每秒能够产生26万个ID。

  • 配置

在这里插入图片描述

ly:

worker:

workerId : 1

dataCenterId : 1

  • 加载属性

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.config;

import lombok.Data;

import org.springframework.boot.context.properties.ConfigurationProperties;

@Data

@ConfigurationProperties(prefix = “ly.worker”)

public class IdWorkerProperties {

private long workerId;

private long dataCenterId;

}

  • 编写配置类

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.config;

import com.leyou.common.utils.IdWorker;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

@EnableConfigurationProperties(IdWorkerProperties.class)

public class IdWorkerConfig {

@Bean

public IdWorker idWorker(IdWorkerProperties prop){

return new IdWorker(prop.getWorkerId(),prop.getDataCenterId());

}

}

2.0.2 准备假物流数据

我们前端页面传来的是addressld,我们需要根据id查询物流信息,

但是因为还没做物流地址管理。

所以我们准备一些假数据。

首先是实体类:

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.dto;

import lombok.Data;

@Data

public class AddressDTO {

private Long id;

private String name;//收件人姓名

private String phone;//电话

private String state;//省份

private String city;//城市

private String district;//区

private String address;//街道地址

private String zipCode;//邮编

private Boolean isDefault;

}

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.client;

import com.leyou.order.dto.AddressDTO;

import java.util.ArrayList;

import java.util.List;

public abstract class AddressClient {

public static final List addressList = new ArrayList() {

{

AddressDTO address = new AddressDTO();

address.setId(1L);

address.setAddress(“太阳系”);

address.setCity(“银河系”);

address.setDistrict(“火星”);

address.setName(“大哥”);

address.setPhone(“15800000000”);

address.setState(“阿拉比亚大陆(Arabia Terra)”);

address.setZipCode(“210000” ) ;

address.setIsDefault(true) ;

add(address);

AddressDTO address2 = new AddressDTO();address2.setId(2L);

address.setAddress(“太阳系”);

address.setCity(“银河系”);

address2.setDistrict(“天王星”);

address2.setName(“张三”);

address2.setPhone(“13600000000” );

address2.setState(“北京”);

address2.setZipCode(“100000”);

address2.setIsDefault(false);

add(address2);

}

};

public static AddressDTO findById(Long id) {

for (AddressDTO addressDTO : addressList) {

if (addressDTO.getId() == id)

return addressDTO;

}

return null;

}

}

2.0.3 在ly-item的ly-item-interface当中新增通过sku的id集合查询其所有sku的接口

在这里插入图片描述

/*

通过sku的id集合查询其所有sku

*/

@GetMapping(“sku/list/ids”)

List querySkuBySpuId(@RequestParam(“ids”) List ids);

2.0.4 在Order当中定义对应client使用上述的接口

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.client;

import com.leyou.item.api.GoodsApi;

import org.springframework.cloud.openfeign.FeignClient;

@FeignClient(“item-service”)

public interface GoodsClient extends GoodsApi {

}

2.0.5 完善OrderService相关的工具类

2.0.5.1 定义异常枚举

在这里插入图片描述

CREATE_ORDER_ERROR(500,“创建订单失败”),

2.0.5.2 定义订单状态枚举

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.enums;

public enum OrderStatusEnum {

UN_PAY(1,“未付款”),

PAYED(2,“已付款,未发货”),

DELIVERED(3,“已发货,未确认”),

SUCCESS(4,“已确认,未评价”),

CLOSED(5,“已关闭,交易失败”),

RATED(6,“已评价”),

;

private int code;

private String desc;

OrderStatusEnum(int code, String desc) {

this.code = code;

this.desc = desc;

}

public int value(){

return this.code;

}

}

2.0.5.3 在StockMapper当中编写键库存的代码

在这里插入图片描述

public interface StockMapper extends BaseMapper{

@Update(“UPDATE tb_stock SET stock = stock - #{num} WHERE sku_id = #{id} AND stock >= #{num}”)

int decreaseStock(@Param(“id”) Long id, @Param(“num”) Integer num);

}

这里减库存并没有采用先查询库存,判断充足才减库存的方案,那样会有线程安全问题,当然可以通过加锁解决。不过我们此处为了效率,并没有使用悲观锁,而是对库存采用了乐观锁方案

2.0.5.4 在商品微服务当中定义减库存的内容
  • 在ly-common当中创建CartDTO,将下面order当中CartDTO复制一份到ly-common当中

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • GoodsController当中创建decreaseStock方法

在这里插入图片描述

/*

减库存

*/

@PostMapping(“stock/decrease”)

public ResponseEntity decreaseStock(@RequestBody List carts){

goodsService.decreaseStock(carts);

return ResponseEntity.status(HttpStatus.NO_CONTENT).build();

}

  • GoodsService

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

@Transactional

public void decreaseStock(List carts) {

for (CartDTO cart : carts) {

//键库存

int count = stockMapper.decreaseStock(cart.getSkuId(), cart.getNum());

if(count != 1){

throw new LyException(ExceptionEnum.STOCK_NOT_ENOUGH);

}

}

}

  • 测试

在这里插入图片描述

package com.leyou.item.service;

import com.leyou.common.dto.CartDTO;

import com.leyou.item.api.GoodsApi;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.junit4.SpringRunner;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

@RunWith(SpringRunner.class)

@SpringBootTest

public class GoodsServiceTest {

@Autowired

private GoodsService goodsService;

@Test

public void decreaseStock(){

List cartDTOS =

Arrays.asList(

new CartDTO(2600242L, 2),

new CartDTO(2600248L, 2)

);

goodsService.decreaseStock(cartDTOS);

}

}

在这里插入图片描述

运行成功

在这里插入图片描述

在这里插入图片描述

2.0.5.5 将接口提供到API当中

在这里插入图片描述

/*

减库存

*/

@PostMapping(“stock/decrease”)

void decreaseStock(@RequestBody List carts);

2.0.6 完善OrderService的createOrder方法

在这里插入图片描述

package com.leyou.order.service;

import com.leyou.auth.entity.UserInfo;

import com.leyou.common.dto.CartDTO;

import com.leyou.common.enums.ExceptionEnum;

import com.leyou.common.exception.LyException;

import com.leyou.common.utils.IdWorker;

import com.leyou.item.pojo.Sku;

import com.leyou.order.client.AddressClient;

import com.leyou.order.client.GoodsClient;

import com.leyou.order.dto.AddressDTO;

import com.leyou.order.dto.OrderDTO;

import com.leyou.order.enums.OrderStatusEnum;

import com.leyou.order.interceptors.UserInterceptor;

import com.leyou.order.mapper.OrderDetailMapper;

import com.leyou.order.mapper.OrderMapper;

import com.leyou.order.mapper.OrderStatusMapper;

import com.leyou.order.pojo.Order;

import com.leyou.order.pojo.OrderDetail;

import com.leyou.order.pojo.OrderStatus;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.transaction.annotation.Transactional;

import java.util.*;

import java.util.stream.Collectors;

@Service

@Slf4j

public class OrderService {

@Autowired

private OrderMapper orderMapper;

@Autowired

private OrderDetailMapper detailMapper;

@Autowired

private OrderStatusMapper statusMapper;

@Autowired

private IdWorker idWorker;

@Autowired

private GoodsClient goodsClient;

@Transactional

public Long createOrder(OrderDTO orderDTO) {

//新增订单

Order order = new Order();

//订单编号

long orderId = idWorker.nextId();

order.setOrderId(orderId);

order.setCreateTime(new Date());

order.setPaymentType(orderDTO.getPaymentType());

//用户信息

UserInfo user = UserInterceptor.getLoginUser();

order.setUserId(user.getId());

order.setBuyerNick(user.getUserName());

order.setBuyerRate(false);

//收货人地址信息

//获取收货人信息

AddressDTO addr = AddressClient.findById(orderDTO.getAddressId());

order.setReceiver(addr.getName());

order.setReceiverAddress(addr.getAddress());

order.setReceiverCity(addr.getCity());

order.setReceiverDistrict(addr.getDistrict());

order.setReceiverMobile(addr.getPhone());

order.setReceiverState(addr.getState());

order.setReceiverZip(addr.getZipCode());

//金额相关

//把CartDTO 转为一个map,key是sku的id,值是num

Map<Long, Integer> numMap = orderDTO.getCarts().stream()

.collect(Collectors.toMap(CartDTO::getSkuId, CartDTO::getNum));

//获取所有的sku的id

Set ids = numMap.keySet();

//根据id查询sku

List skus = goodsClient.querySkuBySpuId(new ArrayList<>(ids));

//准备orderDetail

List details = new ArrayList<>();

long totalPay = 0L;

for (Sku sku : skus) {

//计算商品总价

totalPay += sku.getPrice() * numMap.get(sku.getId());

//封装OrderDetail

OrderDetail detail = new OrderDetail();

detail.setImage(StringUtils.substringBefore(sku.getImages(),“,”));

detail.setNum(numMap.get(sku.getId()));

detail.setOrderId(orderId);

detail.setOwnSpec(sku.getOwnSpec());

detail.setPrice(sku.getPrice());

detail.setSkuId(sku.getId());

detail.setTitle(sku.getTitle());

details.add(detail);

}

//总金额

order.setTotalPay(totalPay);

//实付金额:总金额+邮费-优惠金额

order.setActualPay(totalPay + order.getPostFee() - 0);

//把order写入数据库

int count = orderMapper.insertSelective(order);

if(count != 1){

log.error(“[创建订单] 创建订单失败,orderId:{}”,orderId);

throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);

}

//新增订单详情

count = detailMapper.insertList(details);

if(count != details.size()){

log.error(“[创建订单状态] 创建订单失败,orderId:{}”,orderId);

throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);

}

//新增订单状态

OrderStatus orderStatus = new OrderStatus();

orderStatus.setCreateTime(order.getCreateTime());

orderStatus.setOrderId(orderId);

orderStatus.setStatus(OrderStatusEnum.UN_PAY.value());

count = statusMapper.insertSelective(orderStatus);

if (count != 1) {

log.error(“[创建订单状态] 创建订单失败,orderId:{}”,orderId);

throw new LyException(ExceptionEnum.CREATE_ORDER_ERROR);

}

//减库存

List carts = orderDTO.getCarts();

goodsClient.decreaseStock(carts);

return orderId;

}

}

2.0.7 测试创建订单

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.订单

===============================================================

(1)点击支付后会跳转到支付页面


在这里插入图片描述

(2)实现上述所对应的接口(先实现查询订单)


(a)OrderController当中queryOrderById方法

在这里插入图片描述

@GetMapping(“{id}”)

public ResponseEntity queryOrderById(@PathVariable(“id”) Long id){

return ResponseEntity.ok(orderService.queryOrderById(id));

}

(b)完善OrderService当中对应的方法

在这里插入图片描述

在这里插入图片描述

ORDER_NOT_FOUND(404,“订单不存在”),

ORDER_DETAIL_NOT_FOUND(404,“订单详情不存在”),

ORDER_STATUS_NOT_FOUND(404,“订单状态不存在”),

在这里插入图片描述

public Order queryOrderById(Long id) {

//查询订单

Order order = orderMapper.selectByPrimaryKey(id);

if(order == null){

//订单不存在

throw new LyException(ExceptionEnum.ORDER_NOT_FOUND);

}

//查询订单详情

OrderDetail detail = new OrderDetail();

detail.setOrderId(id);

List details = detailMapper.select(detail);

if(CollectionUtils.isEmpty(details)){

throw new LyException(ExceptionEnum.ORDER_DETAIL_NOT_FOUND);

}

order.setOrderDetails(details);

//查询订单状态

OrderStatus orderStatus = statusMapper.selectByPrimaryKey(id);

if(orderStatus == null){

throw new LyException(ExceptionEnum.ORDER_STATUS_NOT_FOUND);

}

order.setOrderStatus(orderStatus);

return order;

}

重新运行并测试

在这里插入图片描述

刷新支付页面

成功显示对应的数据

在这里插入图片描述

3.微信支付

=================================================================

(1)官网:


https://pay.weixin.qq.com/index.php

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

填写信息

在这里插入图片描述

(2)开发流程(实现后台代码)


模式二与模式一相比,流程更为简单,不依赖设置的回调支付URL。

商户后台系统先调用微信支付的统一下单接口,微信后台系统返回链接参数code_url;

商户后台系统将code_url值生成二维码图片,用户使用微信客户端扫码后发起支付。

注意:code_url有效期为2小时,过期后扫码不能再发起支付。

流程图:

在这里插入图片描述

  • 1、商户生成订单

  • 2、商户调用微信下单接口,获取预交易的链接

  • 3、商户将链接生成二维码图片,展示给用户;(码中包含,价格,收款方,订单号)

  • 4、用户支付并确认

  • 5、支付结果通知:

  • 微信异步通知商户支付结果,商户告知微信支付接收情况

  • 商户如果没有收到通知,可以调用接口,查询支付状态

  • 6、如果支付成功,发货,修改订单状态

在前面的业务中,我们已经完成了:

  • 1、生成订单

接下来,我们需要做的是:

  • 2、调用微信接口,生成链接。

  • 3、并且生成二维码图片

1)创建PayConfig

在这里插入图片描述

package com.leyou.order.config;

import com.github.wxpay.sdk.WXPayConfig;

import lombok.Data;

import java.io.InputStream;

@Data

public class PayConfig implements WXPayConfig {

private String appID; //公众账号ID

private String mchID; //商户号

private String key; //生成签名的密钥

private int httpConnectTimeoutMs; //连接超时时间

private int httpReadTimeoutMs; //读取超时时间

private String notifyUrl;// 下单通知回调地址

@Override

public InputStream getCertStream() {

return null;

}

}

2)创建上述对应的配置文件

在这里插入图片描述

pay:

appId: wx8397f8696b538317

mchId: 1473426802

key: T6m9iK73b0kn9g5v426MKfHQH7X8rKwb

connectTimeoutMs: 5000

readTimeoutMs: 10000

notifyUrl: http://k4v7yt.natappfree.cc/notify/wxpay

3)自定义WXPayConfiguration类封装PayConfig

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.config;

import com.github.wxpay.sdk.WXPay;

import com.github.wxpay.sdk.WXPayConstants;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import javax.persistence.Basic;

@Configuration

public class WXPayConfiguration {

@Bean

@ConfigurationProperties(prefix = “ly.pay”)

public PayConfig payConfig(){

return new PayConfig();

}

@Bean

public WXPay wxPay(PayConfig payConfig){

return new WXPay(payConfig, WXPayConstants.SignType.HMACSHA256);

}

}

4)支付工具类PayHelper

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.utils;

import com.github.wxpay.sdk.WXPay;

import com.github.wxpay.sdk.WXPayConstants;

import com.leyou.common.enums.ExceptionEnum;

import com.leyou.common.exception.LyException;

import com.leyou.order.config.PayConfig;

import com.leyou.order.config.WXPayConfiguration;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import java.util.HashMap;

import java.util.Map;

@Component //将其添加到Spring当中

@Slf4j

public class PayHelper {

@Autowired

private WXPay wxPay;

@Autowired

private PayConfig config;

public String createPayUrl(Long orderId, Long totalPay, String description) {

try {

HashMap<String, String> data = new HashMap<>();

//描述

data.put(“body”, description);

//订单号

data.put(“out_trade_no”, orderId.toString());

//货币(默认就是人民币)

//data.put(“fee_type”, “CNY”);

//总金额

data.put(“total_fee”, totalPay.toString());

//调用微信支付的终端ip

data.put(“spbill_create_ip”, “127.0.0.1”);

//回调地址

data.put(“notify_url”, config.getNotifyUrl());

//交易类型为扫码支付

data.put(“trade_type”, “NATIVE”);

//利用wxPay工具,完成下单

Map<String, String> result = this.wxPay.unifiedOrder(data);

//判断通信的标识

String return_code = result.get(“return_code”);

if(WXPayConstants.FAIL.equals(return_code)){

//通信失败

log.error(“[微信下单] 微信下单通信失败,失败原因:{}”,result.get(“return_msg”));

throw new LyException(ExceptionEnum.WX_PAY_ORDER_FAIL);

}

//判断业务标识

String result_code = result.get(“result_code”);

if(WXPayConstants.FAIL.equals(result_code)){

//通信失败

log.error(“[微信下单] 微信下单业务失败,错误码:{},错误原因:{}”,result.get(“err_code”),result.get(“err_code_des”));

throw new LyException(ExceptionEnum.WX_PAY_ORDER_FAIL);

}

//下单成功,获取支付连接

String url = result.get(“code_url”);

return url;

} catch (Exception e) {

log.error(“【微信下单】创建预交易订单异常”, e);

return null;

}

}

}

5)编写OrderController的createOrderUrl方法

在这里插入图片描述

/*

创建支付链接

*/

@GetMapping(“/url/{id}”)

public ResponseEntity createPayUrl(@PathVariable(“id”) Long orderId ){

return ResponseEntity.ok(orderService.createPayUrl(orderId));

}

6)完善OrderService

在这里插入图片描述

ORDER_STATUS_ERROR(400,“订单状态异常”),

在这里插入图片描述

在这里插入图片描述

@Autowired

private PayHelper payHelper;

在这里插入图片描述

public String createPayUrl(Long orderId) {

//查询订单 //获取订单总金额

Order order = queryOrderById(orderId);

//判断订单状态

if(order.getOrderStatus().getStatus() != OrderStatusEnum.UN_PAY.value()){

//订单状态异常

throw new LyException(ExceptionEnum.ORDER_STATUS_ERROR);

}

//获取总金额

Long actualPay = order.getActualPay();

actualPay = 1L;

//获取商品描述

OrderDetail orderDetail = order.getOrderDetails().get(0);

String desc = orderDetail.getTitle();

return payHelper.createPayUrl(orderId,actualPay,desc);

}

查询启动运行并测试

在这里插入图片描述

刷新支付页面

在这里插入图片描述

返回支付链接

在这里插入图片描述

(3)生成二维码


1)二维码生成插件qrious

qrious是一款基于HTML5 Canvas的纯JS二维码生成插件。

通过qrious.js可以快速生成各种二维码,你可以控制二维码的尺寸颜色,

还可以将生成的二维码进行Base64编码。

官网

https://github.com/neocotic/qrious

在js目录下引入qrcode.min.js

在这里插入图片描述

有pay.html当中已经写好了代码

刷新页面

在这里插入图片描述

(4)修改订单状态


1)内网穿透(外网访问本地服务)

内网穿透,也即 NAT 穿透,进行 NAT 穿透是为了使具有某一个特定源 IP 地址和源端口号的数据包不被 NAT 设备屏蔽而正确路由到内网主机。

下面就相互通信的主机在网络中与 NAT 设备的相对位置介绍内网穿透方法。

UDP 内网穿透的实质是利用路由器上的NAT 系统。

NAT 是一种将私有(保留)地址转化为合法IP地址的转换技术,它被广泛应用于各种类型 Internet 接入方式和各种类型的网络中。

NAT可以完成重用地址,并且对于内部的网络结构可以实现对外隐蔽。

免费的内网穿透工具

NATAPP

官网:https://natapp.cn/

在这里插入图片描述

登录注册

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

点击复制,即可得到 authtoken 这个authtoken便是您的隧道登录凭证.如这里得到的authtoken为9ab6b9040a624f40

下载客户端

在这里插入图片描述

在这里插入图片描述

解压

在这里插入图片描述

8.运行natapp

natapp支持两种运行方式

a) config.ini方式 (推荐)

根据操作系统下载不同的config.ini文件到刚才下载的natapp.exe同级目录 详见

将第7步得到的authtoken填进去 (其他地方都不填),然后保存

#将本文件放置于natapp同级目录程序将读取[default]段

#在命令行参数模式如natapp -authtoken=xxx等厢同参数将会覆盖掉此配置

[default]

authtoken=9ab6b9040a624f40

#对应一条隧道的authtoken

c1ienttoken=

#对应客户端的c1ienttoken,将会忽略authtoken,若无请留空,

1og=none

#1og且志文件,可以是none 代表不记录或者stdout.代表直接屏幕输出﹐默认为none

1oglevel=INFO

#日志等级DEBUG,INFO,WARNING,ERROR默认为 DEBUG

http_proxy=

#代理设置如http://110.123.10.10:3128

windows下,直接双击natapp.exe 即可.

在Linux/Mac 下 需要先给执行权限

chmod a+x natapp

然后再运行

./natapp

b) cmd -authtoken= 参数方式运行.

windows ,点击开始->运行->命令行提示符 后进入 natapp.exe的目录

运行

natapp -authtoken=9ab6b9040a624f40

linux ,同样给予可执行权限之后,运行

./natapp -authtoken=9ab6b9040a624f40

注意参数输入正确性,不要有多余的空格等!

9.运行成功,都可以得到如下界面:

在这里插入图片描述

Tunnel Status Online 代表链接成功

Version 当前客户端版本,如果有新版本,会有提示

Forwarding 当前穿透 网址 或者端口

Web Interface 是本地Web管理界面,可在隧道配置打开或关闭,仅用于web开发测试

Total Connections 总连接数

Avg Conn Time 0.00ms 这里不代表,不代表,不代表 延时,需要注意!

10.将natapp分配的网址(上图Forwarding ),鼠标选定然后复制下来(选定之后单击鼠标右键),在浏览器中访问,可以看到内网穿透成功了!

在这里插入图片描述

该网址 就是可以全球访问的网址,可以发给您的小伙伴试试 😃

2)在ly-order当中重新定义一个controller

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.web;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

@RequestMapping(“notify”)

public class NotifyController {

}

3)修改MVC映射路径

在这里插入图片描述

package com.leyou.order.config;

import com.leyou.order.interceptors.UserInterceptor;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.context.properties.EnableConfigurationProperties;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.servlet.config.annotation.InterceptorRegistry;

import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration

@EnableConfigurationProperties(JwtProperties.class)

public class MvcConfig implements WebMvcConfigurer {

@Autowired

private JwtProperties prop;

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new UserInterceptor(prop))

.addPathPatterns(“/order/**”)

;

;

}

}

4)完善NotifyController并测试

在这里插入图片描述

package com.leyou.order.web;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

@RestController

@RequestMapping(“notify”)

public class NotifyController {

@GetMapping(“{id}”)

public String hello(@PathVariable(“id”) Long id){

return id.toString();

}

}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5)完善订单功能

a、完善application.yml设置对应的路径

在这里插入图片描述

b、引入处理xml 的依赖

在这里插入图片描述

com.fasterxml.jackson.dataformat

jackson-dataformat-xml

2.9.6

c、回调
  • NotifyController

在这里插入图片描述

package com.leyou.order.web;

import com.leyou.order.service.OrderService;

import io.swagger.models.Xml;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.*;

import java.util.HashMap;

import java.util.Map;

@RestController

@RequestMapping(“notify”)

public class NotifyController {

@Autowired

private OrderService orderService;

/*

微信支付的成功回调

*/

@PostMapping(value = “pay”,produces = “application/xml”)

public Map<String,String> hello(@RequestBody Map<String,String> result){

//处理回调

orderService.handleNotify(result);

//返回成功

Map<String,String> msg = new HashMap<>();

msg.put(“return_code”,“SUCCESS”);

msg.put(“return_msg”,“OK”);

return msg;

}

}

  • OrderService

在这里插入图片描述

  • 封装对应PayHelper部分代码为方法

数据的校验

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.utils;

import com.github.wxpay.sdk.WXPay;

import com.github.wxpay.sdk.WXPayConstants;

import com.leyou.common.enums.ExceptionEnum;

import com.leyou.common.exception.LyException;

import com.leyou.order.config.PayConfig;

import com.leyou.order.config.WXPayConfiguration;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import java.util.HashMap;

import java.util.Map;

@Component //将其添加到Spring当中

@Slf4j

public class PayHelper {

@Autowired

private WXPay wxPay;

@Autowired

private PayConfig config;

public String createPayUrl(Long orderId, Long totalPay, String description) {

try {

HashMap<String, String> data = new HashMap<>();

//描述

data.put(“body”, description);

//订单号

data.put(“out_trade_no”, orderId.toString());

//货币(默认就是人民币)

//data.put(“fee_type”, “CNY”);

//总金额

data.put(“total_fee”, totalPay.toString());

//调用微信支付的终端ip

data.put(“spbill_create_ip”, “127.0.0.1”);

//回调地址

data.put(“notify_url”, config.getNotifyUrl());

//交易类型为扫码支付

data.put(“trade_type”, “NATIVE”);

//利用wxPay工具,完成下单

Map<String, String> result = this.wxPay.unifiedOrder(data);

//判断通信和业务标识

isSuccess(result);

//下单成功,获取支付连接

String url = result.get(“code_url”);

return url;

} catch (Exception e) {

log.error(“【微信下单】创建预交易订单异常”, e);

return null;

}

}

public void isSuccess(Map<String, String> result) {

//判断通信的标识

String return_code = result.get(“return_code”);

if (WXPayConstants.FAIL.equals(return_code)) {

//通信失败

log.error(“[微信下单] 微信下单通信失败,失败原因:{}”, result.get(“return_msg”));

throw new LyException(ExceptionEnum.WX_PAY_ORDER_FAIL);

}

//判断业务标识

String result_code = result.get(“result_code”);

if (WXPayConstants.FAIL.equals(result_code)) {

//通信失败

log.error(“[微信下单] 微信下单业务失败,错误码:{},错误原因:{}”, result.get(“err_code”), result.get(“err_code_des”));

throw new LyException(ExceptionEnum.WX_PAY_ORDER_FAIL);

}

}

}

签名校验

在这里插入图片描述

INVALID_SIGN_ERROR(400,“无效的签名异常”),

在这里插入图片描述

public void isValidSing(Map<String, String> data) {

try {

//重新生成签名

String sign1 = WXPayUtil.generateSignature(data, config.getKey(), WXPayConstants.SignType.HMACSHA256);

String sign2 = WXPayUtil.generateSignature(data, config.getKey(), WXPayConstants.SignType.MD5);

//重新生成签名和传过来的签名做比较

String sign = data.get(“sign”);

if(!StringUtils.equals(sign,sign1) && !StringUtils.equals(sign,sign2)){

//签名有误,抛出异常

throw new LyException(ExceptionEnum.INVALID_SIGN_ERROR);

}

}catch (Exception e){

throw new LyException(ExceptionEnum.INVALID_SIGN_ERROR);

}

}

在这里插入图片描述

INVALID_ORDER_PARAM(400,“订单参数异常”),

UPDATE_ORDER_STATUS_ERROR(500,“更新订单状态失败”),

  • 继续完善OrderService当中的handleNotify

在这里插入图片描述

public void handleNotify(Map<String, String> result) {

// 数据校验

payHelper.isSuccess(result);

// 校验签名

payHelper.isValidSing(result);

// 检验金额

String totalFeeStr = result.get(“total_fee”);

String tradeNo = result.get(“out_trade_no”);

if (StringUtils.isEmpty(totalFeeStr) || StringUtils.isEmpty(tradeNo)) {

throw new LyException(ExceptionEnum.INVALID_ORDER_PARAM);

}

// 获取微信支付结果中的金额

Long totalFee = Long.valueOf(totalFeeStr);

//获取订单金额

Long orderId = Long.valueOf(tradeNo);

Order order = orderMapper.selectByPrimaryKey(orderId);

// TODO 这里应该是判断是否等于 order.getActualPay()才对,因为前面支付测试是1分钱,所以这里也是一分钱

if (totalFee != /order.getActualPay()/ 1L) {

//微信支付金额与订单金额不符

throw new LyException(ExceptionEnum.INVALID_ORDER_PARAM);

}

//修改订单状态

//

OrderStatus status = new OrderStatus();

status.setStatus(OrderStatusEnum.PAYED.value());

status.setOrderId(orderId);

status.setPaymentTime(new Date());

int count = statusMapper.updateByPrimaryKeySelective(status);

if(count != 1){

throw new LyException(ExceptionEnum.UPDATE_ORDER_STATUS_ERROR);

}

}

  • 运行测试

在这里插入图片描述

尝试支付

在这里插入图片描述

在这里插入图片描述

数据库当中也显示支付成功

在这里插入图片描述

6)查询订单状态跳转页面

a、分析页面数据

在这里插入图片描述

b、实现后台接口
OrderController当中的queryOrderState

在这里插入图片描述

@GetMapping(“/state/{id}”)

public ResponseEntity queryOrderState(@PathVariable(“id”) Long orderId ){

return ResponseEntity.ok(orderService.queryOrderState(orderId).getValue());

}

OrderService当中queryOrderState

在这里插入图片描述

  • 创建新的状态枚举

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.enums;

public enum PayState {

NOT_PAY(0),

SUCCESS(1),

FAIL(2)

;

PayState(int value){

this.value = value;

}

int value;

public int getValue(){

return value;

}

}

  • 完善OrderService当中queryOrderState方法

在这里插入图片描述

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)


前端面试题汇总


前端面试题是我面试过程中遇到的面试题,每一次面试后我都会复盘总结。我做了一个整理,并且在技术博客找到了专业的解答,大家可以参考下:

由于篇幅有限,只能分享部分面试题,完整版面试题及答案可以【点击我】阅读下载哦~无偿分享给大家

感悟

048151cead0c438e964f94ed561d629e.png?x-oss-process=image/watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA6JOd55uS5a2QaXRibHVlYm94,size_20,color_FFFFFF,t_70,g_se,x_16)

@GetMapping(“/state/{id}”)

public ResponseEntity queryOrderState(@PathVariable(“id”) Long orderId ){

return ResponseEntity.ok(orderService.queryOrderState(orderId).getValue());

}

OrderService当中queryOrderState

在这里插入图片描述

  • 创建新的状态枚举

在这里插入图片描述

在这里插入图片描述

package com.leyou.order.enums;

public enum PayState {

NOT_PAY(0),

SUCCESS(1),

FAIL(2)

;

PayState(int value){

this.value = value;

}

int value;

public int getValue(){

return value;

}

}

  • 完善OrderService当中queryOrderState方法

在这里插入图片描述

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-FAl8MK3a-1713508618150)]

[外链图片转存中…(img-gPhuBeDj-1713508618151)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

[外链图片转存中…(img-MC5WgU3a-1713508618151)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

[外链图片转存中…(img-ftufnc0Q-1713508618151)]
前端面试题汇总


前端面试题是我面试过程中遇到的面试题,每一次面试后我都会复盘总结。我做了一个整理,并且在技术博客找到了专业的解答,大家可以参考下:

由于篇幅有限,只能分享部分面试题,完整版面试题及答案可以【点击我】阅读下载哦~无偿分享给大家

感悟

春招面试的后期,运气和实力都很重要,自己也是运气比较好,为了回馈粉丝朋友们(毕竟自己也玩了这么久哈哈哈),整理个人感悟和总结以上。最后祝愿大家能够收获理想offer!!

  • 25
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值