目录
实现思路
1.购物车页面实现结算功能,主要是拿到传入后台的gids
2.跳转订单页后台,主要是拿到订单页展示数据
3.订单页前台数据展示
注:使用jquery动态实现结算功能,必须勾选购物车中的商品进行结算,没有勾选无法完成结算功能!!!

测试效果如下:

关于结算按钮的绑定事件
- //计算总共几件商品
- function zg(){
- var zsl = 0;
- var index = $(".th input[type='checkbox']:checked").parents(".th").find(".num span");
- var len =index.length;
- if(len==0){
- $("#sl").text(0);
- }else{
- index.each(function(){
- zsl+=parseInt($(this).text());
- $("#sl").text(zsl);
- })
- }
- if($("#sl").text()>0){
- $(".count").css("background","#c10000");
- // TODO 绑定结算事件
- $(".count").bind("click",function () {
- alert("绑定结算事件");
- })
- }else{
- $(".count").css("background","#8e8e8e");
- $(".count").unbind("click");
- }
- }
cart.js进行整改 计算总共几件商品
- $(function(){
- /**************数量加减***************/
- $(".num .sub").click(function(){
- var num = parseInt($(this).siblings("span").text());
- if(num<=1){
- $(this).attr("disabled","disabled");
- }else{
- num--;
- $(this).siblings("span").text(num);
- //获取除了货币符号以外的数字
- var price = $(this).parents(".number").prev().text().substring(1);
- //单价和数量相乘并保留两位小数
- $(this).parents(".th").find(".sAll").text('¥'+(num*price).toFixed(2));
- jisuan();
- zg();
-
- //TODO 获取当前行的行索引
- let index = $(this).parents(".th").index()-1;
- //获取当前的checkbox中设置的隐藏域(包含了商品ID)
- let gid=$(".th").eq(index).find('div:eq(0) input[type=hidden]').val();
- update(num,gid);
- }
- });
- $(".num .add").click(function(){
- var num = parseInt($(this).siblings("span").text());
- if(num>=5){
- confirm("限购5件");
- }else{
- num++;
- $(this).siblings("span").text(num);
- var price = $(this).parents(".number").prev().text().substring(1);
- $(this).parents(".th").find(".sAll").text('¥'+(num*price).toFixed(2));
- jisuan();
- zg();
-
- //TODO 获取当前行的行索引
- let index = $(this).parents(".th").index()-1;
- //获取当前的checkbox中设置的隐藏域(包含了商品ID)
- let gid=$(".th").eq(index).find('div:eq(0) input[type=hidden]').val();
- update(num,gid);
- }
- });
- //计算总价
- function jisuan(){
- var all=0;
- var len =$(".th input[type='checkbox']:checked").length;
- if(len==0){
- $("#all").text('¥'+parseFloat(0).toFixed(2));
- }else{
- $(".th input[type='checkbox']:checked").each(function(){
- //获取小计里的数值
- var sAll = $(this).parents(".pro").siblings('.sAll').text().substring(1);
- //累加
- all+=parseFloat(sAll);
- //赋值
- $("#all").text('¥'+all.toFixed(2));
- })
- }
-
- }
- //计算总共几件商品
- function zg(){
- let gids = "";
- var zsl = 0;
- var index = $(".th input[type='checkbox']:checked").parents(".th").find(".num span");
- var len =index.length;
- if(len==0){
- $("#sl").text(0);
- }else{
- let gidarr = new Array();
- index.each(function(){
- zsl+=parseInt($(this).text());
- $("#sl").text(zsl);
-
- //TODO 获取当前行的索引
- let idx = $(this).parents(".th").index()-1;
- console.log(idx);
- //在这里需要获取当前行商品的商品ID
- gidarr.push($(".th").eq(idx).find("div:eq(0) input[type='hidden']").val());
- });
- gids = gidarr.join(",");
- }
- if($("#sl").text()>0){
- $(".count").css("background","#c10000");
- $(".count").bind("click",function () {
- //拿到gids
- location.href='/order/toOrder?gids='+gids
- });
- }else{
- $(".count").css("background","#8e8e8e");
- $(".count").unbind("click");
- }
- }
- /*****************商品全选***********************/
- $("input[type='checkbox']").on('click',function(){
- var sf = $(this).is(":checked");
- var sc= $(this).hasClass("checkAll");
- if(sf){
- if(sc){
- $("input[type='checkbox']").each(function(){
- this.checked=true;
- });
- zg();
- jisuan();
- }else{
- $(this).checked=true;
- var len = $("input[type='checkbox']:checked").length;
- var len1 = $("input").length-1;
- if(len==len1){
- $("input[type='checkbox']").each(function(){
- this.checked=true;
- });
- }
- zg();
- jisuan();
- }
- }else{
- if(sc){
- $("input[type='checkbox']").each(function(){
- this.checked=false;
- });
- zg();
- jisuan();
- }else{
- $(this).checked=false;
- var len = $(".th input[type='checkbox']:checked").length;
- var len1 = $("input").length-1;
- if(len<len1){
- $('.checkAll').attr("checked",false);
- }
- zg();
- jisuan();
- }
- }
-
- });
- /****************************proDetail 加入购物车*******************************/
- $(".btns .cart").click(function(){
- if($(".categ p").hasClass("on")){
- var num = parseInt($(".num span").text());
- var num1 = parseInt($(".goCart span").text());
- $(".goCart span").text(num+num1);
- }
- });
-
- //删除购物车商品
- $('.del').click(function(){
- //定义商品id 比如:1,2,3,4,5
- let gids = "";
- //单个删除
- if($(this).parent().parent().hasClass("th")){
- $(".mask").show();
- $(".tipDel").show();
- index = $(this).parents(".th").index()-1;
-
- //TODO 获取当前的checkbox中设置的隐藏域(包含了商品ID)
- gids=$(".th").eq(index).find('div:eq(0) input[type=hidden]').val();
- console.log(gids);
-
- $('.cer').click(function(){
- $(".mask").hide();
- $(".tipDel").hide();
- $(".th").eq(index).remove();
- $('.cer').off('click');
- if($(".th").length==0){
- $(".table .goOn").show();
- }
- del(gids);
- })
- }else{
- //选中多个一起删除
- if($(".th input[type='checkbox']:checked").length==0){
- $(".mask").show();
- $(".pleaseC").show();
- }
- else{
- $(".mask").show();
- $(".tipDel").show();
-
- //TODO 先获取所有即将被删除的商品ID
- let gidarr = new Array();
- $(".th input[type='checkbox']:checked").each(function(j){
- gidarr.push($(".th").eq(index).find('div:eq(0) input[type=hidden]').val());
- // gids += $(".th").eq(index).find('div:eq(0) input[type=hidden]').val(); 很容易报数据下标越界异常
- });
- gids = gidarr.join(",");
-
- $('.cer').click(function(){
- $(".th input[type='checkbox']:checked").each(function(j){
- index = $(this).parents('.th').index()-1;
- $(".th").eq(index).remove();
- if($(".th").length==0){
- $(".table .goOn").show();
- }
- })
- $(".mask").hide();
- $(".tipDel").hide();
- zg();
- jisuan();
-
- del(gids);
- })
- }
- }
- })
- $('.cancel').click(function(){
- $(".mask").hide();
- $(".tipDel").hide();
- })
- //改变商品规格
- // $(".pro dd").hover(function(){
- // var html='';
- // html='修改';
- // $(this).addClass("on").append(html).parents(".th").siblings(".th").find(".pro dd").removeClass("on").find('.edit').remove();
- // $(".edit").each(function(i){
- // $(this).attr("id",'edit'+i);
- // $("#edit"+i).click(function(){
- // $(".proDets").show();
- // $(".mask").show();
- // $(".changeBtn .buy").attr("data-id",i);
- // })
- // })
- // },function(){
- // $(this).removeClass("on");
- // })
- // $(".changeBtn .buy").click(function(){
- // var index = $(this).attr("data-id");
- // var result = $(".smallImg .on").find("img").attr("alt");
- // $("#edit"+index).prev().text(result);
- // $(".proDets").hide();
- // $(".mask").hide();
- // $("#edit"+index).parent("dd").removeClass("on").find(".edit").remove();
- // });
- // $(".changeBtn .cart").click(function(){
- // $(".proDets").hide();
- // $(".mask").hide();
- // })
- })
- //删除商品
- function del(gids) {
- $.post('/shopCar/delete',{
- 'gids':gids
- },function(rs){
- if(rs.code!=200)
- alert(rs.msg);
- else
- location.href='/shopCar/queryShopCar';
- },'json');
- }
-
- //修改商品数量
- function update(num,gid){
- $.post('/shopCar/update',{
- 'gid':gid,
- 'quantity':num
- },function(rs){
- if(rs.code!=200)
- alert(rs.msg);
- else
- location.href='/shopCar/queryShopCar';
- },'json');
- }
测试结果如下:

在点击结算的事件下,添加上跳转地址:
if($("#sl").text()>0){
$(".count").css("background","#c10000");
$(".count").bind("click",function () {
//拿到gids
location.href='/order/toOrder?gids='+gids
});
}else{
$(".count").css("background","#8e8e8e");
$(".count").unbind("click");
}
先生成订单表,订单信息表 t_order 、t_order_item
打开CodeGenerator.java运行里面的main方法
- package com.jwj.spbootpro.generator;
-
- import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
- import com.baomidou.mybatisplus.core.toolkit.StringPool;
- import com.baomidou.mybatisplus.core.toolkit.StringUtils;
- import com.baomidou.mybatisplus.generator.AutoGenerator;
- import com.baomidou.mybatisplus.generator.InjectionConfig;
- import com.baomidou.mybatisplus.generator.config.*;
- import com.baomidou.mybatisplus.generator.config.po.TableInfo;
- import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
- import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
-
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Scanner;
-
- public class CodeGenerator {
-
- /**
- *
- * 读取控制台内容
- *
- */
- public static String scanner(String tip) {
- Scanner scanner = new Scanner(System.in);
- StringBuilder help = new StringBuilder();
- help.append("请输入" + tip + ":");
- System.out.println(help.toString());
- if (scanner.hasNext()) {
- String ipt = scanner.next();
- if (StringUtils.isNotBlank(ipt)) {
- return ipt;
- }
- }
- throw new MybatisPlusException("请输入正确的" + tip + "!");
- }
-
- public static void main(String[] args) {
- // 代码生成器
- AutoGenerator mpg = new AutoGenerator();
-
- // 全局配置
- GlobalConfig gc = new GlobalConfig();
- String projectPath = System.getProperty("user.dir") + "/spbootpro";
- gc.setOutputDir(projectPath + "/src/main/java");
- gc.setAuthor("jwj");
- gc.setOpen(false);
- gc.setBaseColumnList(true);
- gc.setBaseResultMap(true);
- // gc.setSwagger2(true); 实体属性 Swagger2 注解
- mpg.setGlobalConfig(gc);
-
- // 数据源配置
- DataSourceConfig dsc = new DataSourceConfig();
- dsc.setUrl("jdbc:mysql://localhost:3306/spbootpro?useUnicode=true&useSSL=false&characterEncoding=utf8");
- // dsc.setSchemaName("public");
- dsc.setDriverName("com.mysql.jdbc.Driver");
- dsc.setUsername("root");
- dsc.setPassword("123456");
- mpg.setDataSource(dsc);
-
- // 包配置
- PackageConfig pc = new PackageConfig();
- //pc.setModuleName(scanner("模块名"));
- pc.setParent("com.jwj.spbootpro");
- //设置包名
- pc.setEntity("model");
- mpg.setPackageInfo(pc);
-
- // 自定义配置
- InjectionConfig cfg = new InjectionConfig() {
- @Override
- public void initMap() {
- // to do nothing
- }
- };
-
- // 如果模板引擎是 freemarker
- String templatePath = "/templates/mybatis-generator/mapper2.xml.ftl";
- // 如果模板引擎是 velocity
- // String templatePath = "/templates/mapper.xml.vm";
-
- // 自定义输出配置
- List<FileOutConfig> focList = new ArrayList<>();
- // 自定义配置会被优先输出
- focList.add(new FileOutConfig(templatePath) {
- @Override
- public String outputFile(TableInfo tableInfo) {
- // 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
- return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
- + "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
- }
- });
- /*
- cfg.setFileCreate(new IFileCreate() {
- @Override
- public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
- // 判断自定义文件夹是否需要创建
- checkDir("调用默认方法创建的目录,自定义目录用");
- if (fileType == FileType.MAPPER) {
- // 已经生成 mapper 文件判断存在,不想重新生成返回 false
- return !new File(filePath).exists();
- }
- // 允许生成模板文件
- return true;
- }
- });
- */
- cfg.setFileOutConfigList(focList);
- mpg.setCfg(cfg);
-
- // 配置模板
- TemplateConfig templateConfig = new TemplateConfig();
-
- // 配置自定义输出模板
- //指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
- templateConfig.setMapper("templates/mybatis-generator/mapper2.java");
- templateConfig.setEntity("templates/mybatis-generator/entity2.java");
- templateConfig.setService("templates/mybatis-generator/service2.java");
- templateConfig.setServiceImpl("templates/mybatis-generator/serviceImpl2.java");
- templateConfig.setController("templates/mybatis-generator/controller2.java");
- templateConfig.setXml(null);
- mpg.setTemplate(templateConfig);
-
- // 策略配置
- StrategyConfig strategy = new StrategyConfig();
- strategy.setNaming(NamingStrategy.underline_to_camel);
- strategy.setColumnNaming(NamingStrategy.underline_to_camel);
- //strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
- strategy.setEntityLombokModel(true);
- strategy.setRestControllerStyle(true);
- strategy.setEntitySerialVersionUID(false);
- // 公共父类
- //strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
- // 写于父类中的公共字段
- strategy.setSuperEntityColumns("id");
- strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
- strategy.setControllerMappingHyphenStyle(true);
- strategy.setTablePrefix("t_");
- mpg.setStrategy(strategy);
- mpg.setTemplateEngine(new FreemarkerTemplateEngine());
- mpg.execute();
- }
- }
如下图所示:

生成成功后如果有时间问题都改为Date,因为我们上一次的错误就又出现这样的情况,凡是有带时间的都改过来,就比如生成这个Order.java

思路
1.从session中获取购物车对象
2.筛选出要结算的订单项列表集合
OrderController.java
- package com.jwj.spbootpro.controller;
-
-
- import com.jwj.spbootpro.model.Goods;
- import com.jwj.spbootpro.model.User;
- import com.jwj.spbootpro.model.vo.ShopCar;
- import com.jwj.spbootpro.model.vo.ShopCarItem;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- import org.springframework.web.bind.annotation.RestController;
- import org.springframework.web.servlet.ModelAndView;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpSession;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.List;
-
- /**
- *
- * 订单信息表 前端控制器
- *
- *
- * @author jwj
- * @since 2022-11-10
- *
- * @Controller:因为我们最终是要跳转到页面
- */
- @Controller
- @RequestMapping("/order")
- public class OrderController {
-
- /**
- * 根据勾选结算的商品跳转到订单页面
- * @param user
- * @param gids
- * @param request
- * @return
- */
- @RequestMapping("/toOrder")
- public ModelAndView toOrder(User user,String gids, HttpServletRequest request){
- ModelAndView mv = new ModelAndView();
- // 拿到购物车中的所有商品
- ShopCar shopCar = getShopCar(user, request);
- // 通过gids从购物车中筛选出指定的商品
- List<ShopCarItem> list = getGoods(shopCar,gids);
- mv.addObject("goods",list);
- mv.setViewName("order.html");
- return mv;
- }
-
- /**
- * 由于用户结算商品时,不一定是将购物车中所有商品进行结算,有可能只有部分商品
- * 必须根据前端页面用户勾选的商品ID从购物车中找到对应需要结算的商品并存入
- * @param shopCar
- * @param gids
- * @return
- */
- // 通过gids从购物车中筛选出指定的商品
- private List<ShopCarItem> getGoods(ShopCar shopCar, String gids) {
- //根据逗号分割商品gids
- List<String> gidList = Arrays.asList(gids.split(","));
- //代表购物车中已有的商品集合
- List<ShopCarItem> items = shopCar.getItems(); //3个 购物车里的商品
- //定义结算商品集合
- List<ShopCarItem> itemsNew = new ArrayList<>();//2个 购物车要结算的商品
- //循环遍历购物车中的商品并与勾选结算的商品ID进行比较
- for (ShopCarItem item : items) {
- //判断购物车中的商品与勾选结算的商品ID是否一致
- if(gidList.contains(item.getGid()+"")){
- itemsNew.add(item);
- }
- }
- return itemsNew;
- }
-
- // 从session中获取购物车对象
- private ShopCar getShopCar(User user, HttpServletRequest request){
- //获取session
- HttpSession session = request.getSession();
- //从session中获取购物车对象
- ShopCar shopCar = (ShopCar)session.getAttribute(user.getId() + "_shopCar");
- //判断shopCar是否为空
- if(shopCar == null){
- //初始化购物车对象
- shopCar = new ShopCar();
- //将初始化完成的购物车对象根据用户id报错到session中
- session.setAttribute(user.getId()+"_shopCar",shopCar);
- }
- return shopCar;
- }
-
- }
order.html
<#include "common/head.html">
订单确认
<#include "common/footer.html">
展示结果如下:

结算如下所示:

实现思路
1.前台订单相关数据获取
2.后台进行下单操作
前端相关处理
1.获取收货地址、收货人以及联系电话
2.获取支付方式
获取快递方式

在Order.html添加Order.js

Order.js代码如下:
$(function () {
$(".pay").click(function () {
// 1)获取收货地址、收货人以及联系电话
let name=$('.addres').find('div.on div:eq(0)>p:eq(0)').text().trim();
let address=$('.addres').find('div.on div:eq(1)>p:eq(0)').text();
let phone=$('.addres').find('div.on div:eq(1)>p:eq(1)').text();
console.log("name=%s,address=%s,phone=%s",name,address,phone);
// 2)获取支付方式
let pay=$('.way').find('img.on').attr('value');
console.log(pay);
// 3)获取快递方式
let dis=$('.dis').find('span.on').text();
console.log(dis);
//获取勾选结算的商品ID
let gids=$('#gids').val();
//拼接请求参数
let params={
gids:gids,
address:address,
person:name,
telephone:phone,
pay:pay,
mail:dis
};
console.log(params);
$.post('/order/addOrder',params,function(rs){
if(rs.code==200){
location.href='/page/ok.html';
}else{
alert(rs.msg);
}
},'json');
});
});
建一个OrderDto.java为了处理在order.js里去写大量的js代码
OrderDto.java
- package com.jwj.spbootpro.model.dto;
-
- import com.jwj.spbootpro.model.Order;
- import lombok.Data;
-
- /**
- * @author 敢敢
- * @site www.javajwj.com
- * @company xxx公司
- * @create 2022-11-10 18:23
- */
- @Data
- public class OrderDto extends Order {
- private String gids;
- }
OrderController.java
package com.jwj.spbootpro.controller; import com.jwj.spbootpro.model.Goods; import com.jwj.spbootpro.model.User; import com.jwj.spbootpro.model.dto.OrderDto; import com.jwj.spbootpro.model.vo.ShopCar; import com.jwj.spbootpro.model.vo.ShopCarItem; import com.jwj.spbootpro.service.IOrderService; import com.jwj.spbootpro.utils.JsonResponseBody; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.Arrays; import java.util.List; /** ** 订单信息表 前端控制器 *
* * @author jwj * @since 2022-11-10 * * @Controller:因为我们最终是要跳转到页面 */ @Controller @RequestMapping("/order") public class OrderController { /** * 根据勾选结算的商品跳转到订单页面 * @param user * @param gids * @param request * @return */ @RequestMapping("/toOrder") public ModelAndView toOrder(User user,String gids, HttpServletRequest request){ ModelAndView mv = new ModelAndView(); // 拿到购物车中的所有商品 ShopCar shopCar = getShopCar(user, request); // 通过gids从购物车中筛选出指定的商品 Listlist = getGoods(shopCar,gids); mv.addObject("goods",list); mv.setViewName("order.html"); return mv; } /** * 由于用户结算商品时,不一定是将购物车中所有商品进行结算,有可能只有部分商品 * 必须根据前端页面用户勾选的商品ID从购物车中找到对应需要结算的商品并存入 * @param shopCar * @param gids * @return */ // 通过gids从购物车中筛选出指定的商品 private List getGoods(ShopCar shopCar, String gids) { //根据逗号分割商品gids List gidList = Arrays.asList(gids.split(",")); //代表购物车中已有的商品集合 List items = shopCar.getItems(); //3个 购物车里的商品 //定义结算商品集合 List itemsNew = new ArrayList<>();//2个 购物车要结算的商品 //循环遍历购物车中的商品并与勾选结算的商品ID进行比较 for (ShopCarItem item : items) { //判断购物车中的商品与勾选结算的商品ID是否一致 if(gidList.contains(item.getGid()+"")){ itemsNew.add(item); } } return itemsNew; } // 从session中获取购物车对象 private ShopCar getShopCar(User user, HttpServletRequest request){ //获取session HttpSession session = request.getSession(); //从session中获取购物车对象 ShopCar shopCar = (ShopCar)session.getAttribute(user.getId() + "_shopCar"); //判断shopCar是否为空 if(shopCar == null){ //初始化购物车对象 shopCar = new ShopCar(); //将初始化完成的购物车对象根据用户id报错到session中 session.setAttribute(user.getId()+"_shopCar",shopCar); } return shopCar; } @Autowired private IOrderService orderService; @ResponseBody @RequestMapping("/addOrder") //非ajax返回的ModelAndView,ajax返回的是JsonResponseBody public JsonResponseBody addOrder(User user, OrderDto orderDto,HttpServletRequest request){ // 1.拿到购物车 2.通过orderDto拿到gids,再拿到要结算的商品 3.数据要入订单表 4.数据要入订单项表 //获取购物车 ShopCar shopCar = this.getShopCar(user, request); //获取结算商品集合 List shopCarItems = this.getGoods(shopCar, orderDto.getGids()); //生成订单及订单项 orderDto.setUserId(user.getId()); orderService.addOrder(orderDto,shopCarItems); //从购物车中删除已结算的商品 shopCar.delete(orderDto.getGids()); //跳转支付页面 return new JsonResponseBody(); } }
IOrderService.java创建addOrder方法
- package com.jwj.spbootpro.service;
-
- import com.jwj.spbootpro.model.Order;
- import com.baomidou.mybatisplus.extension.service.IService;
- import com.jwj.spbootpro.model.dto.OrderDto;
- import com.jwj.spbootpro.model.vo.ShopCarItem;
-
- import java.util.List;
-
- /**
- *
- * 订单信息表 服务类
- *
- *
- * @author jwj
- * @since 2022-11-10
- */
- public interface IOrderService extends IService<Order> {
-
- void addOrder(OrderDto orderDto, List<ShopCarItem> shopCarItems);
- }
在我们的实现类也要实现addOrder方法
OrderServiceImpl.java
- package com.jwj.spbootpro.service.impl;
-
- import com.jwj.spbootpro.model.Order;
- import com.jwj.spbootpro.mapper.OrderMapper;
- import com.jwj.spbootpro.model.OrderItem;
- import com.jwj.spbootpro.model.dto.OrderDto;
- import com.jwj.spbootpro.model.vo.ShopCarItem;
- import com.jwj.spbootpro.service.IOrderItemService;
- import com.jwj.spbootpro.service.IOrderService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.jwj.spbootpro.utils.SnowFlake;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
-
- import java.math.BigDecimal;
- import java.util.ArrayList;
- import java.util.List;
-
- /**
- *
- * 订单信息表 服务实现类
- *
- *
- * @author jwj
- * @since 2022-11-10
- */
- @Service
- public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
- @Autowired
- private OrderMapper orderMapper;
-
- @Autowired
- private IOrderItemService orderItemService;
-
-
- // @Transactional注解:以下的方法因为同时设置到两张表的增删改的操作,要么同时成功,要么同时失败
- @Transactional
- @Override
- public void addOrder(OrderDto orderDto, List<ShopCarItem> shopCarItems) {
- // 订单表以及订单项表的数据录入
- //1) 生成订单ID SnowFlake生成雪花ID,它的作用,生成一个随机的long类型的数字;
- // 相比较于UUID的优点在于,雪花ID可以排序
- SnowFlake snowFlake = new SnowFlake(2, 3);
- Long orderId=snowFlake.nextId();
- orderDto.setOid(orderId);
- //2) 循环勾选的商品集合计算商品总价、获取商品详情集合
- //总价
- BigDecimal total=BigDecimal.valueOf(0);
- //定义商品详情集合
- List<OrderItem> orderItems=new ArrayList<>();
- OrderItem it=null;
- for (ShopCarItem shopCarItem : shopCarItems) {
- //累加小计等于总价
- total=total.add(shopCarItem.smalltotal());
- //初始化OrderItem商品详情对象
- it=new OrderItem();
- // 订单项表的ooid为自增,所以不需要设置
- it.setOid(orderId);
- it.setGid(shopCarItem.getGid());
- it.setGoodsName(shopCarItem.getGoodsName());
- it.setGoodsPrice(shopCarItem.getGoodsPrice());
- it.setQuantity(shopCarItem.getQuantity());
- orderItems.add(it);
- }
- orderDto.setTotal(total);
-
- //1.保存订单
- orderMapper.insert(orderDto);
- //2.保存订单对应的订单项
- orderItemService.saveBatch(orderItems);
-
- }
- }
给我们OrderMapper添加注解@repository,
同时还要OrderItemMapper也要添加注解@repository
SnowFlake.java 放到我们的util里面去
这是我们的雪花算法,把我们的id转成一个long类型的随机的一个数字
想比较于UUID的优点在于,雪花ID可以排序
- package com.jwj.spbootpro.utils;
-
- public class SnowFlake {
-
- /**
- * 起始的时间戳
- */
- private final static long START_STMP = 1480166465631L;
-
- /**
- * 每一部分占用的位数
- */
- private final static long SEQUENCE_BIT = 12; //序列号占用的位数
- private final static long MACHINE_BIT = 5; //机器标识占用的位数
- private final static long DATACENTER_BIT = 5;//数据中心占用的位数
-
- /**
- * 每一部分的最大值
- */
- private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);
- private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
- private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
-
- /**
- * 每一部分向左的位移
- */
- private final static long MACHINE_LEFT = SEQUENCE_BIT;
- private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
- private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;
-
- private long datacenterId; //数据中心
- private long machineId; //机器标识
- private long sequence = 0L; //序列号
- private long lastStmp = -1L;//上一次时间戳
-
- public SnowFlake(long datacenterId, long machineId) {
- if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {
- throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");
- }
- if (machineId > MAX_MACHINE_NUM || machineId < 0) {
- throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");
- }
- this.datacenterId = datacenterId;
- this.machineId = machineId;
- }
-
- /**
- * 产生下一个ID
- *
- * @return
- */
- public synchronized long nextId() {
- long currStmp = getNewstmp();
- if (currStmp < lastStmp) {
- throw new RuntimeException("Clock moved backwards. Refusing to generate id");
- }
-
- if (currStmp == lastStmp) {
- //相同毫秒内,序列号自增
- sequence = (sequence + 1) & MAX_SEQUENCE;
- //同一毫秒的序列数已经达到最大
- if (sequence == 0L) {
- currStmp = getNextMill();
- }
- } else {
- //不同毫秒内,序列号置为0
- sequence = 0L;
- }
-
- lastStmp = currStmp;
-
- return (currStmp - START_STMP) << TIMESTMP_LEFT //时间戳部分
- | datacenterId << DATACENTER_LEFT //数据中心部分
- | machineId << MACHINE_LEFT //机器标识部分
- | sequence; //序列号部分
- }
-
- private long getNextMill() {
- long mill = getNewstmp();
- while (mill <= lastStmp) {
- mill = getNewstmp();
- }
- return mill;
- }
-
- private long getNewstmp() {
- return System.currentTimeMillis();
- }
-
- public static void main(String[] args) {
- SnowFlake snowFlake = new SnowFlake(2, 3);
-
- long start = System.currentTimeMillis();
- for (int i = 0; i < 1000000; i++) {
- System.out.println(snowFlake.nextId());
- }
-
- System.out.println(System.currentTimeMillis() - start);
-
-
- }
- }
测试效果如下图所示:

我们数据库里目前是没有数据的,如下所示:

注意:这里的数据库里面要自动增长,如果没有很可能会出错。
最终跳转到结算成功页面,5s后自动跳转到订单展示页面,如下图所示:


在看一下我们数据库有没有数据,如下所示:

第一步:
1)登陆支付宝:https://open.alipay.com/
点击登录:扫码登录
2)首页找到进入管理中心 ---》开发工具推荐选择【沙箱】
3)下载安装支付宝开放平台开发助手:
下载好之后就是这样的,我下载的安卓的
4)打开本地支付宝开放平台助手---》密钥工具---》生成密钥---》以默认的方式(RSA2和KCS8)生成应用私钥和应用公钥
注意:密钥是留给自己用的 ,公钥是copy出去的
5)在沙箱应用的开发信息中选择自定义密钥生成支付宝公钥(基于应用公钥生成支付宝公钥)
第二步:配置沙箱账号(买家)并完成手动充值
第三步:下载沙箱支付宝(只支持安卓)
沙箱工具---》支付宝客户端沙箱版---》请使用浏览器中的扫码功能扫码下载
注:请使用Android手机扫码下载支付宝客户端沙箱版;如需登录,请访问沙箱账号,在商家信息中获取账密
通过这个扫码它就会自动下载,下载沙箱支付宝APP的安装包,下载好了之后,这里面默认会有100万块钱或者其他,可以通过下面这种方式,进行充值。
实现思路
1.完成支付宝沙箱支付功能接入
2.支付成功后变更订单状态
根据官方网站开发文档进行支付宝接入

先在支付宝开放平台密钥工具,生成密钥,把我们的 应用公钥复制到


注意:上面的支付宝公钥复制了之后要记得点击确定。
把我们的支付宝公钥复制到我们的代码里面AlipayConfig类里的支付宝公钥

那么下面这里我封装了一版,封装类版,配置类
AlipayConfig.java
- package com.jwj.spbootpro.config;
-
- import com.alipay.easysdk.factory.Factory;
- import com.alipay.easysdk.kernel.Config;
- import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
- import com.jwj.spbootpro.model.dto.OrderDto;
-
- /**
- * 支付宝沙箱支付
- */
- public class AlipayConfig {
-
- public String goAlipay(OrderDto orderDto){
- try {
- // 1. 设置参数(全局只需设置一次)
- Factory.setOptions(aliconfig());
- // 2. 发起API调用(subject商品标题、outTradeNo订单编号、totalAmount总金额、returnUrl异步通知地址)
- AlipayTradePagePayResponse response = Factory.Payment.Page()
- .pay(orderDto.getOid()+"",
- orderDto.getOid()+"",
- orderDto.getTotal().toString(),
- "http://localhost:8081/page/ok.html"); //支付成功之后的异步通知(跳出到自己系统的哪个位置)
- System.out.println(response.body);
- return response.body;
- } catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException(e);
- }
- }
-
- private Config aliconfig(){
- Config config=new Config();
- //沙箱支付宝地址
- config.gatewayHost="openapi.alipaydev.com";
- //协议https
- config.protocol="https";
- //应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
- config.appId="2021000121687583";
- //支付宝公钥
- config.alipayPublicKey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsc74PulHrpMlGXt53c1CYfBziNjY9W299fXA5aU3f+eKKNnjc9fmUXYusGms0MgSSYgji4SuAfl1LxtRJEwqz8L7zxOxq0JB6Y5+XFXiSpdys4f6wMJXoaRWamEzfaSkpgoJ6vO+VqJa+j77Brc//mxRtHjrcS3xW5doUBOsAAdofjwaaWgcT2cmzb3r/QEXu4vfrOdV4uuXD5GWJ7bIYTDuRsRBL8iBPtqv1OkB6184f2t0Qch0g6Pyti/cg10BB5s/i9uUCqHCtHl5CQrpubQc6hYb4mmp8ZXLvqfPAmq0fgABDQklCPdww7qML6cATsv+WZF+f5NsxVPOgYPeQwIDAQAB";
- //签名方式
- config.signType="RSA2";
- //商户私钥(应用私钥),您的PKCS8格式RSA2私钥
- config.merchantPrivateKey="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCG0psRs7/OH5gLmjpvlINXDMPOMIlLk8oqP1KdeKBHTOejsk01PyN6zqS0rzqmYHRNMCFsZnX1JvpqvZ8F0Vcw8FSzTGXGLyvAufqic5dIASb91qcF/4QSnoFzNVhIp6KSUsWlpY87bHkRZlLY3sseHu8M4HWOJmr1XidvIFpozxDmc5EgA0tHqogcth6ncW3uGXtNtKg840hejawhpEryCXNPbPK0pq1P95iWpmV+YtxTUWSfFTgjc88JPCaNSfhpPx11Up3oDHsMa+RlsKD1xaetLzjfPwoU73ymPVo0ii88z/KrzdNKfTE0Mkm4GQbID31GJ4yhYW/jsLNUQs+vAgMBAAECggEAN8TEOjVVQMkW1q3MQD15eregAxlWoXmXpZQd819jRTsNkkv93empHnJ99POK2imJ0if3m2RipK6j5SVcs0Zdv7OaBbSzYKBAg+8qOqp/yFwZqeRxoGyKUD1apLJLO3qEJ+yvLw7lyZncFpNx751w/ZukHHp4hf1kPuzceP40B6gH8h8BIJTpYItP3fVYrECjJ94Yr68OAQZ1pvlwK68b1HD68MIInHZIeZ9QYpzfMoCk/6wNYE31yYuoUpkmoENuCrA2r/G2xOQBVwgIO8pPyJ7WQCC5/jtFu8pbob8HBb2QHAUfvYj+MXtxglC8cHZmXECW7+mc61YRFCS7AlnmkQKBgQD0prghOEQdp1zCw0y2yfhmLhwfARyZUFZbOe4yuKuWitsuG3c7t8JCPiT3ifXzRnyoG3ticQxctFtfsBmGFzNxk+Y7fyexzN5EMcwD6wGIstemxumF76ts4Ycj4iiuotxbqJ/gw/xPHcL03AZTMokO1+HR78xjHLXjl0LshgK9pwKBgQCNE6arORAsnvJnC5scDsE2IYcbB13+eRKed2I5KAIMfEnXYRIscI413dujU3vR85s6ed91+vifh6wchwtdngX/eJ7V5Diz7m46hvGDH7YAe9RbSD9QaedDf2z/kotKV6m16Wlwy64DCEUlyVkaw3I7qOjrezfATQLWqbsimCZuuQKBgGvEyRVxGKhLYhQ1PaTDYVV5eE+8CKIMfo3e/et/563/6r7rwGEg+ER/5X7ZWetZIG5Y9MgPblej5yBGjWfA7ptYgDGzOIQc78fwe6M6MUnLJi3EL3gddFEZI5ON+0e6XAFQTWUQUCN1w7xi87JQb4mHVWVKEamTKxhfOE7CVZC/AoGAHA2UTugB912EBkmghmvIr+Lq4s0MO9YHhctnlMYH4kO0y0daPcwD+2Iqkse/D3jJnU8uyF8noVFxQBr6f5s0xiBONWo0fFMlSC4dSF2960q0Z5JuRKdKfXmRSyDw4c+cx7eqZ+uYprG2TlVDirbiuEpWRr0x0ON/dQA7Xh2zRsECgYEAj4iagNb8hBqJOPLBLzca3qLbUmdrGMVSEbXxyJaR6RP9C4SFeuJTh09LqVcFuss+jw7GaCJ74j1a2xQuKDF1xlfO7krb32EV0KGnrMtVwTCyT7LVcugPw+rsd97pros85jZhigARiSUA3vKpQf0bA7BEfq2RFSVKZ9fdHVsUSAM=";
- return config;
- }
- }
我们支付成功之后就要跳转到最终的那个页面了,然而我们之前是直接跳转到最终页面了,现在就不可以了。
order.js 就要变更如下: 支付会引发页面跳转,所以要经ajax请求换成页面跳转的方式
$(function(){
$('.pay').click(function(){
alert("11");
//获取收货地址、联系人、联系电话
let name=$('.addres').find('div.on div:eq(0)>p:eq(0)').text().trim();
let address=$('.addres').find('div.on div:eq(1)>p:eq(0)').text();
let phone=$('.addres').find('div.on div:eq(1)>p:eq(1)').text();
console.log("name=%s,address=%s,phone=%s",name,address,phone);
//获取支付方式
let pay=$('.way').find('img.on').attr('value');
console.log(pay);
//获取快递方式
let dis=$('.dis').find('span.on').text();
console.log(dis);
//获取勾选结算的商品ID
let gids=$('#gids').val();
//拼接请求参数
let params={
gids:gids,
address:address,
person:name,
telephone:phone,
pay:pay,
mail:dis
};
console.log(params);
// $.post('/order/addOrder',params,function(rs){
// if(rs.code==200){
// location.href='/page/ok.html';
// }else{
// alert(rs.msg);
// }
// },'json');
//一般的支付成功网址都是很长就列如:http://localhost:8080/order/addOrder?gids=sjdksjljfdsjw&djfsj&name=jssjdj
location.href="/order/addOrder?"+parseParams(params);
});
});
/**
* JSON转URL参数
* @param data
* @returns {string}
*/
function parseParams(data) {
try {
var tempArr = [];
for (var i in data) {
var key = encodeURIComponent(i);
var value = encodeURIComponent(data[i]);
tempArr.push(key + '=' + value);
}
var urlParamsStr = tempArr.join('&');
return urlParamsStr;
} catch (err) {
return '';
}
}
OrderController.java代码也要变更如下:
@Autowired
private IOrderService orderService;
@RequestMapping("/addOrder")
@ResponseBody
//非ajax返回的ModelAndView,ajax返回的是JsonResponseBody
public String addOrder(User user, OrderDto orderDto,HttpServletRequest request){
// 1.拿到购物车 2.通过orderDto拿到gids,再拿到要结算的商品 3.数据要入订单表 4.数据要入订单项表
//获取购物车
ShopCar shopCar = this.getShopCar(user, request);
//获取结算商品集合
List shopCarItems = this.getGoods(shopCar, orderDto.getGids());
//生成订单及订单项
orderDto.setUserId(user.getId());
orderService.addOrder(orderDto,shopCarItems);
//从购物车中删除已结算的商品
shopCar.delete(orderDto.getGids());
//跳转支付页面
AlipayConfig alipayConfig = new AlipayConfig();
return alipayConfig.goAlipay(orderDto);
}
测试如下:为了方便我们测试,我又把数据清空掉了





成功了

来看一下我们买家的金额

看一下我们的数据库里有没有数据


OrderController.java 代码新增如下:
- /**
- * 支付成功后回调处理订单状态,完成整个下单流程的链路
- * @param param
- * @return
- */
- @RequestMapping("/orderFinish")
- public String orderFinish(@RequestParam Map<String, String> param){
- System.out.println("异步回调开始。。。。。。");
- Boolean signVerified=false;
-
- try {
- System.out.println(param);
- signVerified = Factory.Payment.Common().verifyNotify(param);
- System.out.println(signVerified);
- System.out.println("out_trade_no:"+param.get("out_trade_no"));
- System.out.println("total_amount:"+param.get("total_amount"));
- System.out.println("trade_no:"+param.get("trade_no"));
- if(signVerified){
- //支付成功
- //根据订单编号修改订单状态
- //update t_xxx set xx=?,tt=? where id=?
- orderService.update(new UpdateWrapper<Order>()
- .set("status",1)
- .setSql("pay_date=now()")
- .eq("oid",param.get("out_trade_no")));
- //跳转到支付成功页面
- }else{
- //支付失败
- }
-
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- return "ok.html";
- }
AlipayConfig.java 变更如下:
package com.jwj.spbootpro.config;
import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.alipay.easysdk.payment.page.models.AlipayTradePagePayResponse;
import com.jwj.spbootpro.model.dto.OrderDto;
/**
* 支付宝沙箱支付
*/
public class AlipayConfig {
public String goAlipay(OrderDto orderDto){
try {
// 1. 设置参数(全局只需设置一次)
Factory.setOptions(aliconfig());
// 2. 发起API调用(subject商品标题、outTradeNo订单编号、totalAmount总金额、returnUrl异步通知地址)
AlipayTradePagePayResponse response = Factory.Payment.Page()
.pay(orderDto.getOid()+"",
orderDto.getOid()+"",
orderDto.getTotal().toString(),
// "http://localhost:8081/page/ok.html"); //支付成功之后的异步通知(跳出到自己系统的哪个位置)
"http://localhost:8081/order/orderFinish"); //支付成功之后的异步通知(跳出到自己系统的哪个位置)
System.out.println(response.body);
return response.body;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private Config aliconfig(){
Config config=new Config();
//沙箱支付宝地址
config.gatewayHost="openapi.alipaydev.com";
//协议https
config.protocol="https";
//应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号
config.appId="2021000121687583";
//支付宝公钥
config.alipayPublicKey="MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsc74PulHrpMlGXt53c1CYfBziNjY9W299fXA5aU3f+eKKNnjc9fmUXYusGms0MgSSYgji4SuAfl1LxtRJEwqz8L7zxOxq0JB6Y5+XFXiSpdys4f6wMJXoaRWamEzfaSkpgoJ6vO+VqJa+j77Brc//mxRtHjrcS3xW5doUBOsAAdofjwaaWgcT2cmzb3r/QEXu4vfrOdV4uuXD5GWJ7bIYTDuRsRBL8iBPtqv1OkB6184f2t0Qch0g6Pyti/cg10BB5s/i9uUCqHCtHl5CQrpubQc6hYb4mmp8ZXLvqfPAmq0fgABDQklCPdww7qML6cATsv+WZF+f5NsxVPOgYPeQwIDAQAB";
//签名方式
config.signType="RSA2";
//商户私钥(应用私钥),您的PKCS8格式RSA2私钥
config.merchantPrivateKey="MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCG0psRs7/OH5gLmjpvlINXDMPOMIlLk8oqP1KdeKBHTOejsk01PyN6zqS0rzqmYHRNMCFsZnX1JvpqvZ8F0Vcw8FSzTGXGLyvAufqic5dIASb91qcF/4QSnoFzNVhIp6KSUsWlpY87bHkRZlLY3sseHu8M4HWOJmr1XidvIFpozxDmc5EgA0tHqogcth6ncW3uGXtNtKg840hejawhpEryCXNPbPK0pq1P95iWpmV+YtxTUWSfFTgjc88JPCaNSfhpPx11Up3oDHsMa+RlsKD1xaetLzjfPwoU73ymPVo0ii88z/KrzdNKfTE0Mkm4GQbID31GJ4yhYW/jsLNUQs+vAgMBAAECggEAN8TEOjVVQMkW1q3MQD15eregAxlWoXmXpZQd819jRTsNkkv93empHnJ99POK2imJ0if3m2RipK6j5SVcs0Zdv7OaBbSzYKBAg+8qOqp/yFwZqeRxoGyKUD1apLJLO3qEJ+yvLw7lyZncFpNx751w/ZukHHp4hf1kPuzceP40B6gH8h8BIJTpYItP3fVYrECjJ94Yr68OAQZ1pvlwK68b1HD68MIInHZIeZ9QYpzfMoCk/6wNYE31yYuoUpkmoENuCrA2r/G2xOQBVwgIO8pPyJ7WQCC5/jtFu8pbob8HBb2QHAUfvYj+MXtxglC8cHZmXECW7+mc61YRFCS7AlnmkQKBgQD0prghOEQdp1zCw0y2yfhmLhwfARyZUFZbOe4yuKuWitsuG3c7t8JCPiT3ifXzRnyoG3ticQxctFtfsBmGFzNxk+Y7fyexzN5EMcwD6wGIstemxumF76ts4Ycj4iiuotxbqJ/gw/xPHcL03AZTMokO1+HR78xjHLXjl0LshgK9pwKBgQCNE6arORAsnvJnC5scDsE2IYcbB13+eRKed2I5KAIMfEnXYRIscI413dujU3vR85s6ed91+vifh6wchwtdngX/eJ7V5Diz7m46hvGDH7YAe9RbSD9QaedDf2z/kotKV6m16Wlwy64DCEUlyVkaw3I7qOjrezfATQLWqbsimCZuuQKBgGvEyRVxGKhLYhQ1PaTDYVV5eE+8CKIMfo3e/et/563/6r7rwGEg+ER/5X7ZWetZIG5Y9MgPblej5yBGjWfA7ptYgDGzOIQc78fwe6M6MUnLJi3EL3gddFEZI5ON+0e6XAFQTWUQUCN1w7xi87JQb4mHVWVKEamTKxhfOE7CVZC/AoGAHA2UTugB912EBkmghmvIr+Lq4s0MO9YHhctnlMYH4kO0y0daPcwD+2Iqkse/D3jJnU8uyF8noVFxQBr6f5s0xiBONWo0fFMlSC4dSF2960q0Z5JuRKdKfXmRSyDw4c+cx7eqZ+uYprG2TlVDirbiuEpWRr0x0ON/dQA7Xh2zRsECgYEAj4iagNb8hBqJOPLBLzca3qLbUmdrGMVSEbXxyJaR6RP9C4SFeuJTh09LqVcFuss+jw7GaCJ74j1a2xQuKDF1xlfO7krb32EV0KGnrMtVwTCyT7LVcugPw+rsd97pros85jZhigARiSUA3vKpQf0bA7BEfq2RFSVKZ9fdHVsUSAM=";
return config;
}
}
测试如下:


看看我们的数据库时间有没有,状态发生改变没有如下:
