网站建设与运营的市场,北京网站制作沈阳,如何下wordpress,如何设置淘宝友情链接地址簿管理
分析需求: 查询地址列表/新增地址/修改地址/删除地址/设置默认地址/查询默认地址 接口设计
新增地址接口 查询用户所有的地址接口 查询默认地址接口 根据id修改地址接口 根据id删除地址接口 根据id查询地址接口 设置默认地址接口 数据库设计: 收货地址簿(address_…地址簿管理
分析需求: 查询地址列表/新增地址/修改地址/删除地址/设置默认地址/查询默认地址 接口设计
新增地址接口 查询用户所有的地址接口 查询默认地址接口 根据id修改地址接口 根据id删除地址接口 根据id查询地址接口 设置默认地址接口 数据库设计: 收货地址簿(address_book表) 代码导入 功能测试 用户下单
需求分析和设计
在电商系统中,用户是通过下单的方式通知商家, 用户已经购买了商品, 需要商家备货和发货
用户下单后会产生订单相关数据, 订单数据需要有: 商品名称/商品数量/订单金额/用户名称/手机号/收货地址 接口分析 接口设计 数据库设计 代码开发
设计VO: 根据用户下单接口返回的数据设计OrderSubmitVO, 用户封装返回给前端的数据
Data
Builder
NoArgsConstructor
AllArgsConstructor
public class OrderSubmitVO implements Serializable {//订单idprivate Long id;//订单号private String orderNumber;//订单金额private BigDecimal orderAmount;//下单时间private LocalDateTime orderTime;
}设计DTO: 根据用户下单接口的参数设计OrdersSubmitDTO, 用于封装前端传递的参数
Data
public class OrdersSubmitDTO implements Serializable {//地址簿idprivate Long addressBookId;//付款方式private int payMethod;//备注private String remark;//预计送达时间JsonFormat(shape JsonFormat.Shape.STRING, pattern yyyy-MM-dd HH:mm:ss)private LocalDateTime estimatedDeliveryTime;//配送状态 1立即送出 0选择具体时间private Integer deliveryStatus;//餐具数量private Integer tablewareNumber;//餐具数量状态 1按餐量提供 0选择具体数量private Integer tablewareStatus;//打包费private Integer packAmount;//总金额private BigDecimal amount;
}Controller: 新建user/OrderController
RestController(userOrderController)
RequestMapping(/user/order)
Slf4j
Api(tags 用户端订单相关接口)
public class OrderController {Autowiredprivate OrderService orderService;PostMapping(/submit)ApiOperation(用户下单)public ResultOrderSubmitVO submit(RequestBody OrdersSubmitDTO ordersSubmitDTO) {log.info(用户下单, 数据: {}, ordersSubmitDTO);OrderSubmitVO orderSubmitVO orderService.submitOrder(ordersSubmitDTO);return Result.success(orderSubmitVO);}
} 设计Orders订单实体类, 用于封装插入到订单表的数据, 字段与订单表一致
/*** 订单*/
Data
Builder
NoArgsConstructor
AllArgsConstructor
public class Orders implements Serializable {/*** 订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消*/public static final Integer PENDING_PAYMENT 1;public static final Integer TO_BE_CONFIRMED 2;public static final Integer CONFIRMED 3;public static final Integer DELIVERY_IN_PROGRESS 4;public static final Integer COMPLETED 5;public static final Integer CANCELLED 6;/*** 支付状态 0未支付 1已支付 2退款*/public static final Integer UN_PAID 0;public static final Integer PAID 1;public static final Integer REFUND 2;private static final long serialVersionUID 1L;private Long id;//订单号private String number;//订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消 7退款private Integer status;//下单用户idprivate Long userId;//地址idprivate Long addressBookId;//下单时间private LocalDateTime orderTime;//结账时间private LocalDateTime checkoutTime;//支付方式 1微信2支付宝private Integer payMethod;//支付状态 0未支付 1已支付 2退款private Integer payStatus;//实收金额private BigDecimal amount;//备注private String remark;//用户名private String userName;//手机号private String phone;//地址private String address;//收货人private String consignee;//订单取消原因private String cancelReason;//订单拒绝原因private String rejectionReason;//订单取消时间private LocalDateTime cancelTime;//预计送达时间private LocalDateTime estimatedDeliveryTime;//配送状态 1立即送出 0选择具体时间private Integer deliveryStatus;//送达时间private LocalDateTime deliveryTime;//打包费private int packAmount;//餐具数量private int tablewareNumber;//餐具数量状态 1按餐量提供 0选择具体数量private Integer tablewareStatus;
}设计Orders订单明细实体类, 用于封装插入到订单明细表的数据, 字段与订单明细表一致
/*** 订单明细*/
Data
Builder
NoArgsConstructor
AllArgsConstructor
public class OrderDetail implements Serializable {private static final long serialVersionUID 1L;private Long id;//名称private String name;//订单idprivate Long orderId;//菜品idprivate Long dishId;//套餐idprivate Long setmealId;//口味private String dishFlavor;//数量private Integer number;//金额private BigDecimal amount;//图片private String image;
}Service: 新建OrderService接口和实现类
Service
public interface OrderService {/*** 用户下单* param ordersSubmitDTO* return*/OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO);
}
Service
public class OrderServiceImpl implements OrderService {Autowiredprivate OrderMapper orderMapper;Autowiredprivate OrderDetailMapper orderDetailMapper;Autowiredprivate AddressBookMapper addressBookMapper;Autowiredprivate ShoppingCartMapper shoppingCartMapper;/*** 用户下单** param ordersSubmitDTO* return*/Transactionalpublic OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {// 处理可能的业务异常(地址簿为空, 购物车为空)AddressBook addressBook addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());if (addressBook null) {// 地址簿为空,抛出业务异常throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);}Long userId BaseContext.getCurrentId();ShoppingCart shoppingCart new ShoppingCart();shoppingCart.setUserId(userId);ListShoppingCart list shoppingCartMapper.list(shoppingCart);if (list.size() 0 list null) {// 购物车为空,抛出业务异常throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);}// 向订单表插入1条数据Orders orders new Orders();BeanUtils.copyProperties(ordersSubmitDTO, orders);orders.setOrderTime(LocalDateTime.now());orders.setPayStatus(Orders.UN_PAID);orders.setStatus(Orders.PENDING_PAYMENT);orders.setNumber(String.valueOf(System.currentTimeMillis()));orders.setPhone(addressBook.getPhone());orders.setConsignee(addressBook.getConsignee());orders.setUserId(userId);orderMapper.insert(orders);// 向订单明细表插入n条数据ArrayListOrderDetail orderDetailList new ArrayList();for (ShoppingCart cart : list) {OrderDetail orderDetail new OrderDetail(); // 订单明细BeanUtils.copyProperties(cart, orderDetail);orderDetail.setOrderId(orders.getId()); // 设置当前明细关联的订单idorderDetailList.add(orderDetail);}orderDetailMapper.insertBatch(orderDetailList);// 清空当前用户的购物车数据shoppingCartMapper.deleteByUserId(userId);// 封装VO返回结果OrderSubmitVO orderSubmitVO OrderSubmitVO.builder().id(orders.getId()).orderTime(orders.getOrderTime()).orderNumber(orders.getNumber()).orderAmount(orders.getAmount()).build();return orderSubmitVO;}
}
主要逻辑:
处理各种业务异常(地址簿为空, 购物车数据为空)向订单表插入1条数据向订单明细表插入n条数据清空当前用户的购物车数据封装VO返回数据
虽然前端对异常情况进行了限制, 但是为了代码的健壮性, 后端还是要进行判断通过事务注解进行事务管理 Maaper: 新建OrderMapper(操作订单表) 和 OrderDetailMapper(操作订单明细表)
Mapper
public interface OrderMapper {/*** 插入订单数据* param orders*/void insert(Orders orders);
}
mapper namespacecom.sky.mapper.OrderMapperinsert idinsert useGeneratedKeystrue keyPropertyidinsert into orders (number, status, user_id, address_book_id, order_time, checkout_time, pay_method, pay_status,amount, remark, phone, address, consignee, estimated_delivery_time, delivery_status,pack_amount, tableware_number, tableware_status)values (#{number}, #{status}, #{userId}, #{addressBookId}, #{orderTime}, #{checkoutTime}, #{payMethod},#{payStatus}, #{amount}, #{remark}, #{phone},#{address}, #{consignee}, #{estimatedDeliveryTime}, #{deliveryStatus}, #{packAmount},#{tablewareNumber}, #{tablewareStatus})/insert/mapper
Mapper
public interface OrderDetailMapper {/*** 批量插入订单明细数据* param orderDetailList*/void insertBatch(ArrayListOrderDetail orderDetailList);
}
mapper namespacecom.sky.mapper.OrderDetailMapperinsert idinsertBatchinsert into order_detail (name,image,order_id,dish_id,setmeal_id,dish_flavor,number,amount)valuesforeach collectionorderDetailList itemod separator,(#{od.name},#{od.image},#{od.orderId},#{od.dishId},#{od.setmealId},#{od.dishFlavor},#{od.number},#{od.amount})/foreach/insert
/mapper 测试 订单支付
介绍
资质限制: 需要使用营业执照申请商户号, 个人账户无法开通支付权限, 有了商户号可以开通支付资质 微信支付的接入流程: 在商户平台把商户号与AppId绑定后, 就可以使用appid和商户号进行支付 微信小程序支付时序图 重要步骤
后端调用微信官方的预支付接口,拿到预支付订单,返给前端前端根据预支付订单发起微信支付微信服务器把支付结果返给后端, 后端更新支付状态 预支付交易订单(JSAPI下单): 商户系统调用该接口在微信支付服务后台生成预支付交易单, 目的是获取预支付交易会话标识 调起支付: 使用微信支付提供的小程序方法调起小程序支付, 传入预支付交易会话标识(prepay_id), 完成支付 准备工作
为了保证支付的安全, 在支付过程中要对数据加密/解密/签名, 就要使用到微信支付平台证书/商户私钥文件, 这两个文件要在商户管理后台获取 支付成功后, 微信服务器要通过我们配置的域名, 回调我们的系统程序, 通知我们用户支付的结果
内网穿透:
我们的程序处于开发阶段, 没有部署到公网上, 只在本地的局域网运行所以需要使用内网穿透技术, 获得一个临时公网ip, 供微信后台回调 软件安装: 通过官网下载, 或者使用资料中的安装包, wind双击安装即可 验证: 登录官网 cpolar - secure introspectable tunnels to localhost, 选择验证菜单, 复制 Authtoken 值 生成配置文件: 在软件安装目录, 打开cmd窗口, 执行命令 cpolar.exe authtoken XXXXXXXXX值 生成配置文件, 只需执行一次就行 启动服务: 软件安装目录, 打开cmd窗口, 执行命令 cpolar.exe http 8080 苍穹外卖后端服务运行在8080端口, 所以在命令中要指定映射端口是8080访问测试: https://1ded711e.r27.cpolar.top/doc.html#/home
代码导入
配置微信支付相关的参数 准备配置属性封装类, 用来封装配置文件中的数据, 使用时注入该类, 就可以获取配置文件中的数据
Component
ConfigurationProperties(prefix sky.wechat)
Data
public class WeChatProperties {private String appid; //小程序的appidprivate String secret; //小程序的秘钥private String mchid; //商户号private String mchSerialNo; //商户API证书的证书序列号private String privateKeyFilePath; //商户私钥文件private String apiV3Key; //证书解密的密钥private String weChatPayCertFilePath; //平台证书private String notifyUrl; //支付成功的回调地址private String refundNotifyUrl; //退款成功的回调地址} 导入代码: 新增nofity包存放PayNotifyController.java文件, 处理微信后台的消息, 其他代码按需复制不要覆盖 阅读代码
用户进行订单支付, 首先访问OrderController的支付接口, 前端要把订单号传递过来, 我们使用DTO接收
RestController(userOrderController)
RequestMapping(/user/order)
Slf4j
Api(tags 用户端订单相关接口)
public class OrderController {Autowiredprivate OrderService orderService;/*** 订单支付** param ordersPaymentDTO* return*/PutMapping(/payment)ApiOperation(订单支付)public ResultOrderPaymentVO payment(RequestBody OrdersPaymentDTO ordersPaymentDTO) throws Exception {log.info(订单支付{}, ordersPaymentDTO);OrderPaymentVO orderPaymentVO orderService.payment(ordersPaymentDTO);log.info(生成预支付交易单{}, orderPaymentVO);return Result.success(orderPaymentVO);}}
再调用orderService中payment方法, 处理支付逻辑
Service
Slf4j
public class OrderServiceImpl implements OrderService {Autowiredprivate UserMapper userMapper;Autowiredprivate WeChatPayUtil weChatPayUtil;Value(${sky.shop.address})private String shopAddress;Value(${sky.baidu.ak})private String ak;/*** 订单支付** param ordersPaymentDTO* return*/public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {// 当前登录用户idLong userId BaseContext.getCurrentId();User user userMapper.getById(userId);//调用微信支付接口生成预支付交易单JSONObject jsonObject weChatPayUtil.pay(ordersPaymentDTO.getOrderNumber(), //商户订单号new BigDecimal(0.01), //支付金额单位 元苍穹外卖订单, //商品描述user.getOpenid() //微信用户的openid);if (jsonObject.getString(code) ! null jsonObject.getString(code).equals(ORDERPAID)) {throw new OrderBusinessException(该订单已支付);}OrderPaymentVO vo jsonObject.toJavaObject(OrderPaymentVO.class);vo.setPackageStr(jsonObject.getString(package));return vo;}}通过weChatPayUtil工具类中的pay方法完成订单支付pay方法首先调用 jsapi方法完成订单数据的封装再调用 post方法请求微信后台的下单接口, 完成下单操作, 并获取预支付交易标识对支付数据进行一系列的封装, 加密和签名, 最终得的一个JSON对象, 供前端用于调起微信支付把JSON数据在封装到VO对象中, 返回给前端 前端微信支付完成后, 微信后台服务器会回调我们的系统, 通知用户支付的结果, 我们使用controller接收并处理
/*** 支付回调相关接口*/
RestController
RequestMapping(/notify)
Slf4j
public class PayNotifyController {Autowiredprivate OrderService orderService;Autowiredprivate WeChatProperties weChatProperties;/*** 支付成功回调** param request*/RequestMapping(/paySuccess)public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {//读取数据String body readData(request);log.info(支付成功回调{}, body);//数据解密String plainText decryptData(body);log.info(解密后的文本{}, plainText);JSONObject jsonObject JSON.parseObject(plainText);String outTradeNo jsonObject.getString(out_trade_no);//商户平台订单号String transactionId jsonObject.getString(transaction_id);//微信支付交易号log.info(商户平台订单号{}, outTradeNo);log.info(微信支付交易号{}, transactionId);//业务处理修改订单状态、来单提醒orderService.paySuccess(outTradeNo);//给微信响应responseToWeixin(response);}/*** 读取数据** param request* return* throws Exception*/private String readData(HttpServletRequest request) throws Exception {BufferedReader reader request.getReader();StringBuilder result new StringBuilder();String line null;while ((line reader.readLine()) ! null) {if (result.length() 0) {result.append(\n);}result.append(line);}return result.toString();}/*** 数据解密** param body* return* throws Exception*/private String decryptData(String body) throws Exception {JSONObject resultObject JSON.parseObject(body);JSONObject resource resultObject.getJSONObject(resource);String ciphertext resource.getString(ciphertext);String nonce resource.getString(nonce);String associatedData resource.getString(associated_data);AesUtil aesUtil new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));//密文解密String plainText aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),ciphertext);return plainText;}/*** 给微信响应** param response*/private void responseToWeixin(HttpServletResponse response) throws Exception {response.setStatus(200);HashMapObject, Object map new HashMap();map.put(code, SUCCESS);map.put(message, SUCCESS);response.setHeader(Content-type, ContentType.APPLICATION_JSON.toString());response.getOutputStream().write(JSONUtils.toJSONString(map).getBytes(StandardCharsets.UTF_8));response.flushBuffer();}}用户支付成功, 微信后台会返回给我们用户支付的数据, 我们进行数据读取和解密再调用orderService的paySuccess方法, 修改该订单的状态为成功订单状态修改完成后, 我们要带给微信后台一个响应, 否则微信后台会一直通知我们 修改订单状态
Service
Slf4j
public class OrderServiceImpl implements OrderService {Autowiredprivate OrderMapper orderMapper;/*** 支付成功修改订单状态** param outTradeNo*/public void paySuccess(String outTradeNo) {// 根据订单号查询订单Orders ordersDB orderMapper.getByNumber(outTradeNo);// 根据订单id更新订单的状态、支付方式、支付状态、结账时间Orders orders Orders.builder().id(ordersDB.getId()).status(Orders.TO_BE_CONFIRMED).payStatus(Orders.PAID).checkoutTime(LocalDateTime.now()).build();orderMapper.update(orders);}}Mapper
public interface OrderMapper {/*** 根据订单号查询订单** param orderNumber*/Select(select * from orders where number #{orderNumber})Orders getByNumber(String orderNumber);/*** 修改订单信息** param orders*/void update(Orders orders);
}?xml version1.0 encodingUTF-8 ?
!DOCTYPE mapper PUBLIC -//mybatis.org//DTD Mapper 3.0//ENhttp://mybatis.org/dtd/mybatis-3-mapper.dtd
mapper namespacecom.sky.mapper.OrderMapperupdate idupdate parameterTypecom.sky.entity.Ordersupdate orderssetif testcancelReason ! null and cancelReason! cancel_reason#{cancelReason},/ifif testrejectionReason ! null and rejectionReason! rejection_reason#{rejectionReason},/ifif testcancelTime ! nullcancel_time#{cancelTime},/ifif testpayStatus ! nullpay_status#{payStatus},/ifif testpayMethod ! nullpay_method#{payMethod},/ifif testcheckoutTime ! nullcheckout_time#{checkoutTime},/ifif teststatus ! nullstatus #{status},/ifif testdeliveryTime ! nulldelivery_time #{deliveryTime}/if/setwhere id #{id}/update/mapper功能测试