当前位置: 首页 > news >正文

卖狗人怎么做网站/营销型网站建设企业

卖狗人怎么做网站,营销型网站建设企业,网站如何隐藏统计数量,有了域名和空间怎么做网站内容注意事项 1.实现京淘项目权限控制 Springmvc 拦截器说明 SpringMVC程序调用流程 WebMvcConfigurer这个配置类继承的接口相当于web.xml配置文件 在拦截其中获取user对象传到request对象中到控制层,在取出来简化代码 ThreadLocal(本地线程变量):进一步简化request传参…

注意事项

1.实现京淘项目权限控制
Springmvc 拦截器说明
SpringMVC程序调用流程
WebMvcConfigurer这个配置类继承的接口相当于web.xml配置文件
在拦截其中获取user对象传到request对象中到控制层,在取出来简化代码
ThreadLocal(本地线程变量):进一步简化request传参不能随便定义reques对象,所以会用到ThreadLocal
2.京淘订单模块实现
订单表 订单商品表 订单物流表
多表:一对一 一对多插入 查询操作
根据物理模型图标识的字段说明表关系
@TableField(exist=false) //入库操作忽略该字段 因为为逻辑属性

设置主键为 时间戳 或者uuid
SpringMVC中参数格式:简单参数 对象参数 对象类型引用(为了解决参数同名)
3张表同时入库
Ajax中 表单序列化传参 serialize

用户id+时间戳拼接的字符串所以用String类型接收
超时订单的处理 任务调度的3种方式 Timer Java线程池 quartz

1.实现京淘项目权限控制

1.1 需求分析

说明:如果用户没有进行登录操作时,访问购物车/订单等敏感操作时将不允许访问,应该重定向到系统的登录页面,登录成功之后才能进行访问,例如京东商城.
在这里插入图片描述

知识点:
1.AOP : 对原有的方法进行扩展。(一般做service层的业务扩展)
2.拦截器: 控制了程序的执行轨迹,满足条件时才会执行任务,控制的是request对象/response对象 。(一般拦截用户请求)
3.数据传输: request对象 /ThreadLocal(本地线程变量)
总结:如图所示,Aop处于后端服务器业务里面进行拦截,而SpringMvc拦截器是在用户发送的请求时就进行拦截。这个地方明显需要拦截用户请求,所以这里用到是的拦截器。

在这里插入图片描述

1.2 关于拦截器说明

1.2.1 SpringMVC程序调用流程

1.用户发送请求 --> 前端控制器
2.前端控制器(若配置了拦截此路径)会拦截此请求,然后调用处理器映射器
3.处理器映射器确定要调用哪个处理器,并可能执行处理器链(过滤器、拦截器),最后将处理器地址返回给前端控制器
4.前端控制器拿着地址调用处理器适配器,由其去调用具体的处理器(业务处理方法实现)
5.处理器会返回页面以及数据(Model And View)给处理器适配器
6.处理器适配器将其再返回给前端控制器
7.前端控制器调用视图解析器,将MV解析成JSP或者HTML,返回给前端控制器
8.最后前端控制器将解析完的页面返回给用户
在这里插入图片描述

1.2.2 拦截器工作原理

流程:用户发送请求首先被pre进行拦截,之后处理完业务代码被post进行拦截,最后视图渲染后被after拦截返回给客户端。(注意一旦after执行完成,请求就不会在受到服务器的控制,直接返回给客户端)

pre拦截: 要么拦截返回要么通过(可以改变用户的拦截轨迹)
post:一般用于一些记录和收尾操作 比如:返回值,报的异常等(不能改变用户的拦截轨迹)
after : 用于释放资源 随用随消 比如还线程,还连接(不能改变用户的拦截轨迹)

在这里插入图片描述

1.3 拦截器业务实现

1.3.1 定义拦截器UserInterceptor(web)

说明:
1.拦截器拦截的是web项目的购物车和订单敏感操作,所以应该放在web端.
2.定义一个类实现HandlerInterceptor接口,这个接口提供了拦截的方法。

如何查看继承的接口方法???
双击选中继承的接口-----ctrl鼠标点击进入------选中接口名+ctrl+0查看接口的所有方法。
在这里插入图片描述
分析HandlerInterceptor接口源码 default属性:
jdk1.8新特性,接口中可以有普通方法,用static或者默认的default修饰 。子类可以不实现,想用的话直接实现即可。这个地方是spring版本升级为 5版本时更新的源码中添加的。

在这里插入图片描述

package com.jt.interceptor;import com.jt.pojo.User;
import com.jt.util.CookieUtil;
import com.jt.util.ObjectMapperUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import redis.clients.jedis.JedisCluster;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;/** 如何使用这个拦截器对象呢?* 1.交给spring容器管理此对象* 2.需要在web.xml文件进行配置,因为现在不用这个文件了,在之前写的伪静态配置类中 继承的接口相当于web.xml文件,*    所以在MvcConfigurer这个配置类进行配置。*/
@Component
public class UserInterceptor implements HandlerInterceptor {//需要实现拦截器接口@Autowiredprivate JedisCluster jedisCluster; //注入集群对象//Spring版本升级 spring 4 必须实现所有的方法  spring 5 只需要重写指定的方法即可./*** 需求:   拦截/cart开头的所有的请求进行拦截,并且校验用户是否登录.....* 拦截器选择: preHandler* 如何判断用户是否登录:  1.检查cookie信息   2.检查Redis中是否有记录.*          true : 请求应该放行*          false: 请求应该拦截 则配合重定向的语法实现页面跳转到登录页面 使得程序流转起来*/@Override //直接在页面输入继承的方法名preHandle,会提示方法,直接enter方法会自动重写在此页面public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//1.判断用户是否登录  检查cookie是否有值String ticket = CookieUtil.getCookieValue("JT_TICKET",request); //通过之前封装的工具Api获取cookie的值value(即ticket凭证)//2.校验ticket是否有值if(!StringUtils.isEmpty(ticket)){ //为空说明没有登录需要跳转校验   不为空说明有凭证需要进一步校验//3.判断redis中是否有值.if(jedisCluster.exists(ticket)){  //根据key判断,因为map集合key是唯一的/***  4. 如何动态获取userId,因为之前单点登录把用户的信息对象转化为json存入到了redis中,*  所以可以根据k(ticket)获取v(json类型数据对象),在把json转为为对象通过get方法获取属性。*  但是购物车的crud4个方法都要用到用户id,每个方法都要写获取用户id的方法太麻烦(即使用工具Api也需要创建类)。pre拦截器处于发送请求*  和处理业务控制层之间的位置,所以可以在拦截的时候获取到这个用户对象,之后想办法把它传到控制层,这样控制层直接可以通过对象调用userid属性了。*/String userJSON = jedisCluster.get(ticket);//动态获取json信息User user = ObjectMapperUtil.toObject(userJSON,User.class);//把json还原为user对象//因为这个pre拦截器到控制层是通过请求进行连接的,所以可以把对象传到request请求对象中携带到控制层,控制层在通过HttpServletRequest request获取request.setAttribute("JT_USER",user);//存入到request对象的map集合中  k-v(字符串   Object任意类型)return true;  //如果有则放行  true代表放行}}//主机名相同相对路径 主机名不同绝对路径response.sendRedirect("/user/login.html");//表示重定向 重定向用的是response对象发送url(重定向到哪的网址 用法查看第二阶段)return false;//表示拦截}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {//用完之后销毁数据request.removeAttribute("JT_USER");}}

在这里插入图片描述

1.3.2 编译配置类MvcConfigurer(web)

说明:使得拦截器生效。
在这里插入图片描述

package com.jt.config;import com.jt.interceptor.UserInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration                        //web.xml配置文件
public class MvcConfigurer implements WebMvcConfigurer{//开启匹配后缀型配置@Overridepublic void configurePathMatch(PathMatchConfigurer configurer) {//开启后缀类型的匹配.  xxxx.htmlconfigurer.setUseSuffixPatternMatch(true);}@Autowiredprivate UserInterceptor userInterceptor;//注入拦截器对象//添加拦截器功能@Overridepublic void addInterceptors(InterceptorRegistry registry) {//     把拦截器加进来                    拦截什么样的请求和功能,1个*代表拦截1级目录,2个代表多级目录。registry.addInterceptor(userInterceptor).addPathPatterns("/cart/**","/order/**");}
}

1.3.3 CartController动态获取UserId(web)

说明:
1.之前在购物车操作中为了降低难度,所以用户id写死了,现在改为动态获取。
2.在拦截器对象中用pre拦截器实现,获取后存入request对象,这样代码更加简洁。原因写在代码注释中。
3.用完User对象后要进行销毁。在拦截器对象中用after拦截器中实现。

步骤:
获取用户对象
在这里插入图片描述
crud4个方法都要修改为动态获取。
在这里插入图片描述

package com.jt.controller;import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.Cart;
import com.jt.pojo.User;
import com.jt.service.DubboCartService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;
import java.util.List;@Controller
@RequestMapping("/cart")
public class CartController {@Reference(check = false,timeout = 3000)//消费者启动时不会效验是否有提供者。private DubboCartService cartService;/*** 删除购物车数据 使用对象接收resful参数* url地址:http://www.jt.com/cart/delete/562379.html* 参数:   562379   获取itemId* 返回值:  重定向到购物车的展现页面*/@RequestMapping("/delete/{itemId}")public String deleteCart(Cart cart, HttpServletRequest request){User user=(User) request.getAttribute("JT_USER");//返回值为object所以需要强转Long userId =user.getId();//user对象用完后应该删除掉(用的时候传 不用用该删除),所以可以再after拦截器中进行删除cart.setUserId(userId);cartService.deleteCart(cart);return "redirect:/cart/show.html";}//普通的方式接收参数:/*** 购物车删除操作* url地址: http://www.jt.com/cart/delete/562379.html* 参数:    获取itemId* 返回值:  重定向到购物车的展现页面*//* @RequestMapping("/delete/{itemId}")public String deleteCarts(@PathVariable Long itemId){Long userId = 7L;cartService.deleteCart(userId,itemId);return "redirect:/cart/show.html";}*//*** 业务需求: 完成购物车入库操作* url地址: http://www.jt.com/cart/add/562379.html* 参数:    form表单提交的数据/itemId   对象接收* 返回值:  重定向到购物车列表页面*/@RequestMapping("/add/{itemId}")public String addCart(Cart cart, HttpServletRequest request){//因为k和属性itemId保持一致 可以直接用对象来接收User user=(User) request.getAttribute("JT_USER");//返回值为object所以需要强转Long userId =user.getId();//user对象用完后应该删除掉(用的时候传 不用用该删除),所以可以再after拦截器中进行删除cart.setUserId(userId);cartService.addCart(cart);//因为没有用到ajax所以重定向用 redirect :/cart/show  又因为页面跳转是伪静态的方式所以路径需要拼接.htmlreturn "redirect:/cart/show.html";}/*** 业务说明: 完成购物车数量的更新操作* url地址: http://www.jt.com/cart/update/num/562379/9* 参数: itemId/num* 返回值:  void*/@RequestMapping("/update/num/{itemId}/{num}")@ResponseBody  //ajax结束的标识符.(虽然没有返回值但是请求时ajax发送的)public void updateCartNum(Cart cart, HttpServletRequest request){   //如果{name}即key 与po属性的名称一致,则可以自动的赋值.  controller规则User user=(User) request.getAttribute("JT_USER");//返回值为object所以需要强转Long userId =user.getId();//user对象用完后应该删除掉(用的时候传 不用用该删除),所以可以再after拦截器中进行删除cart.setUserId(userId);cartService.updateCartNum(cart);}/* //常规获取参数:public void updateCartNum(@PathVariable Long itemId, @PathVariable Integer num){Long userId = 7L;Cart cart =new Cart();cartService.updateCartNum(userId,itemId,num);}
*//**业务需求: 根据userId查询购物车数据* 1.购物车列表数据展现* url地址: http://www.jt.com/cart/show.html* 参数:    动态获取userId  暂时没有* 返回值:  页面逻辑名称  cart.jsp* 页面取值: ${cartList}* 应该将数据添加到域对象中 Request域  model工具API操作request对象**/@RequestMapping("/show") //伪静态拦截的是后缀跟这个路径没有关系public String show(Model model, HttpServletRequest request){//1.暂时将userId写死    7L// long userId=7在int范围内可以转(自动转换),现在是包装类型(基本类型和包装类型之间不能自动类型转换,必须是同为包装或者同为基本类型),转换通过自动装箱功能(要求类型相同比如:都是long类型,所以加L转换为long类型实现自动装箱)User user=(User) request.getAttribute("JT_USER");//返回值为object所以需要强转Long userId =user.getId();//user对象用完后应该删除掉(用的时候传 不用用该删除),所以可以再after拦截器中进行删除//由页面取值看出查询的结果为list集合类型List<Cart> cartList = cartService.findCartListByUserId(userId);model.addAttribute("cartList",cartList);return "cart";}
}

销毁对象
在这里插入图片描述

1.3.4 访问测试

说明:在没有登录状态下不允许操作购物车和订单敏感数据,需要先进行登录。
在这里插入图片描述
在这里插入图片描述

1.4 ThreadLocal

1.4.1 request对象传值的缺点

使用request对象缺点:不能再程序的任意地方用到request对象。
解释:在controller中用只要通过@Controller注解进行标示,那么可以在controller的任意一个方法中拿到request对象(HttpServletRequest request,配置类是因为实现了接口重写了方法),但是程序执行到业务层不能直接在方法中通过定义参数HttpServletRequest request拿到对象,只能在请求的调用过程中把控制层的参数当做参数传过来 对象.方法名(request参数名),这样虽然能用但是不方便。所以就有了另一种API ,ThreadLocal
在这里插入图片描述

1.4.2 ThreadLocal(本地线程变量)介绍

1.名称: 本地线程变量
2.作用: 可以在同一个线程内,实现数据的共享.
说明:把请求当做线程来调用,一个请求一个线程。
在这里插入图片描述

1.4.3 入门案例

/** 案例要求:定义方法a  方法b,在方法a中定义一个参数 int a = 100,* 需要在方法b中对方法a中的参数int a=100进行乘以100计算。*//** 常规写法:*  在a方法中调用方法b,把参数传到方法b中进行计算。* 但是现在要求方法b中不允许定义参数,a方法也就没办法调用b方法传参,* 那么方法b如何对参数:int b  = 100*a;进行计算呢????* 本地线程变量。*/    public void a(){int a = 100;b(a);}public void b(int a){//不允许定义参数的形式,用线程的方式int b  = 100*a;}/** 本地线程变量实现:*  通过定义一个第三方公共的对象(本地线程对象),把a方法中的值存入,在b方法中* 取出来。因为a方法调用b方法是同一个线程所以可以实现调用。* 因为每次请求都会创建新的对象,所以不会出现线程安全问题。*/    private ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public void a(){int a = 100;threadLocal.set(a);b();}public void b(){//不允许定义参数的形式,用线程的方式int a = threadLocal.get();int b  = 100*a;}

1.4.4 创建UserThreadLocal工具API

在这里插入图片描述

package com.jt.thread;import com.jt.pojo.User;public class UserThreadLocal {//static不会影响线程 Thread创建时跟随线程//private static ThreadLocal<Map<k,v>> thread = new ThreadLocal<>();如果想要存入多个对象可以先把数据存入Map集合在存入这个对象,泛型为Map集合k v都要指定泛型private static ThreadLocal<User> thread = new ThreadLocal<>();public static void set(User user){  //赋值thread.set(user);}public static User get(){           //取值return thread.get();}public static void remove(){        //移除thread.remove();}}

1.4.5 重构User拦截器对象传值

在这里插入图片描述

1.4.6 重构CartController动态获取UserId

c r u d4个方法修改
在这里插入图片描述

1.4.7 重构User拦截器对象销毁

在这里插入图片描述

1.4.8 启动测试

购物车的crud仍能正常操作。
在这里插入图片描述

1.4.9 练习题

问: 在JT-WEB拦截器中使用ThreadLocal进行数据传参,
问题1:JT-WEB的Controller中能否获取数据? 可以
问题2:jt-cart的Service层中能否获取数据 不可以
因为:jt-web服务器与jt-cart的服务器的通讯是通过Dubbo框架的RPC实现的. RPC相当于开启了一个新的线程,所以无法通讯.

1.4.10 关于ThreadLocal总结

1.在同一个线程内可以使用ThreadLocal
2.“一般条件下” 在同一个tomcat内的线程为同一个线程.

2.京淘订单模块实现

2.1 订单模块的表设计说明

用到的表:订单表 订单商品表 订单物流表
订单和订单商品表:一对多 pk fk标识同一字段
订单和订单物流表:一对一 pk 标识不同字段
由下图可以看出 order id和订单物流id相同,又和订单商品有对应关系。

解释:pk主键 fk外键 ak也是外键,只是写法不同
主键:唯一且不为空 并且有标识作用
外键:保存另一张表的主键在自己表作为一列,如果表示外键数据库会自动维护这层关系。
定单表: pk标识 order_id表示为订单表主键id,用户id为外键约束()
订单物流表: pk fk都标识order_id说明 这个id既是订单表的主键又是外键,所以为1对一关系。因为之前第二阶段 表一对一 可以在任意一方保存另一张表的主键,既然是一对一那么主键和外键id应该是一摸一样的,何必多写一列呢,直接把外键充当主键来用,不用在写自己的主键了。所以表示同一字段一定为一对一关系。
订单商品表:同样pk fk标识的是不同的字段,不能省略为一行。所以为一对多关系

总结:实在不行一个一个的查看表的数据是否一 一对应,如果一 一对应肯定为一对多,如果一个表的主键对应的外键有多个相同的,一定为一对多。如果保存在第三张表关系 一定为多对多。

在这里插入图片描述

2.2 项目创建jt-order

2.2.1 创建项目

在这里插入图片描述

2.2.2 添加继承/依赖/插件(order)

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><artifactId>jt-order</artifactId><parent><artifactId>jt</artifactId><groupId>com.jt</groupId><version>1.0-SNAPSHOT</version></parent><!--添加依赖项--><dependencies><dependency><groupId>com.jt</groupId><artifactId>jt-common</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies><!--添加插件--><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

2.2.3 添加主启动类(order)

在这里插入图片描述

2.2.4 添加配置文件(order)

在这里插入图片描述

server:port: 8095servlet:context-path: /
spring:datasource:#引入druid数据源#type: com.alibaba.druid.pool.DruidDataSource#driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/jtdb?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=trueusername: rootpassword: root#关于Dubbo配置
dubbo:scan:basePackages: com.jt    #指定dubbo的包路径 为了扫描此项目下的dubbo注解(@service),可以指定大点的范围com,jtapplication:              #应用名称name: provider-order     #一个接口对应一个服务名称(一个接口可以有多个实现,但是如果实现同一个接口则提供的服务也应该是同一个。 eg:老王 老李都卖菜则实现同一个接口,老孙卖肉则和老王老李实现不同的借口)registry: #注册中心 2181连接的是从机 backup(备用) 用户获取数据从机中获取 主机只负责监控整个集群 实现数据同步,所以这个地方要连接从机而不是主机address: zookeeper://192.168.126.129:2181?backup=192.168.126.129:2182,192.168.126.129:2183protocol:  #指定协议 name:dubbo固定写法name: dubbo  #使用dubbo协议(tcp-ip)  web-controller直接调用sso-Serviceport: 20883  #每一个服务都有自己特定的端口 不能重复.mvc:view:prefix: /WEB-INF/views/suffix: .jsp
#mybatis-plush配置
mybatis-plus:type-aliases-package: com.jt.pojomapper-locations: classpath:/mybatis/mappers/*.xmlconfiguration:map-underscore-to-camel-case: truelogging:level: com.jt.mapper: debug

2.2.5 添加pojo(common)

说明:将课前资料的pojo复制到common中

在这里插入图片描述
order:

package com.jt.pojo;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;import java.util.Date;
import java.util.List;@TableName("tb_order")
@Data
@Accessors(chain=true)
public class Order extends BasePojo{@TableField(exist=false)	//入库操作忽略该字段  封装订单物流信息  一对一private OrderShipping orderShipping;@TableField(exist=false)	//入库操作忽略该字段  封装订单商品信息  一对多 private List<OrderItem> orderItems;@TableIdprivate String orderId;private String payment;private Integer paymentType;private String postFee;private Integer status;private Date paymentTime;private Date consignTime;private Date endTime;private Date closeTime;private String shippingName;private String shippingCode;private Long userId;private String buyerMessage;private String buyerNick;private Integer buyerRate;}

OrderItem :

package com.jt.pojo;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@TableName("tb_order_item")
@Data
@Accessors(chain=true)
public class OrderItem extends BasePojo{@TableIdprivate String itemId;private String orderId;private Integer num;private String title;private Long price;private Long totalFee;private String picPath;
}

OrderShipping :

package com.jt.pojo;import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.experimental.Accessors;
@TableName("tb_order_shipping")
@Data
@Accessors(chain=true)
public class OrderShipping extends BasePojo{@TableIdprivate String orderId;private String receiverName;private String receiverPhone;private String receiverMobile;private String receiverState;private String receiverCity;private String receiverDistrict;private String receiverAddress;private String receiverZip;}

2.2.6 添加DubboOrderService接口(common)

在这里插入图片描述

2.2.7 添加DubboOrderServiceImpl实现类(order)

在这里插入图片描述

package com.jt.service;import com.alibaba.dubbo.config.annotation.Service;
import com.jt.mapper.OrderItemMapper;
import com.jt.mapper.OrderMapper;
import com.jt.mapper.OrderShippingMapper;
import org.springframework.beans.factory.annotation.Autowired;@Service
public class DubboOrderServiceImpl implements  DubboOrderService{@Autowiredprivate OrderMapper orderMapper; //订单@Autowiredprivate OrderItemMapper orderItemMapper;//订单商品@Autowiredprivate OrderShippingMapper orderShippingMapper;//订单物流
}

2.2.8 添加DubboOrderMapper接口(order)

说明:因为有3个表
在这里插入图片描述
OrderMapper:

package com.jt.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.Order;public interface OrderMapper extends BaseMapper<Order>{}

OrderItemMapper:

package com.jt.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.OrderItem;public interface OrderItemMapper extends BaseMapper<OrderItem>{}

OrderShippingMapper:

package com.jt.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jt.pojo.OrderShipping;public interface OrderShippingMapper extends BaseMapper<OrderShipping>{}

2.2.9 访问测试

在这里插入图片描述

2.3 订单确认页面跳转

2.3.1 页面说明

说明:当用户点击去结算时,应该跳转到订单确认页面 order-cart.jsp,并展现用户的购物车以及订单自身相关信息.之后提交订单即可.
在这里插入图片描述

2.3.2 页面url分析

伪静态方式

在这里插入图片描述

2.3.3 页面js分析

说明:复制url中固定路径ctrl+h,可以看到请求在购物车页面发送请求。但是他要跳转到的页面没办法看。这个快捷键作用是定位到谁发送的请求,现在要知道跳转那个页面 只能是熟悉这个项目的人告诉你。
在这里插入图片描述
要跳转到order-cart.jsp页面
在这里插入图片描述

2.2.4 编辑OrderController(web)

说明: 因为订单确认页面只缺少购物车信息,所以只用到了DubboCartService购物车的service层对象,而查询购物车的方法已经写好了,所以只需要通过本地线程变量获取用户名查询购物车信息 携带到页面即可。
在这里插入图片描述

package com.jt.controller;import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.Cart;
import com.jt.service.DubboCartService;
import com.jt.service.DubboOrderService;
import com.jt.thread.UserThreadLocal;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;import java.util.List;@Controller
@RequestMapping("/order")
public class OrderController {@Reference(check = false,timeout = 3000)private DubboOrderService dubboOrderService;//注入订单的对象@Reference(check = false,timeout = 3000)private DubboCartService cartService;//查询购物车信息注入购物车的service层/*** 订单确认页面跳转:并把购物车信息以及订单自身页面信息显示到订单确认页面,这个页面只缺少购物车信息,所以只用到了DubboCartService对象* 1.url地址:http://www.jt.com/order/create.html* 2.参数说明:  暂时没有* 3.返回值:  order-cart.jsp页面* 4.页面的取值:  ${carts}*/@RequestMapping("/create")public String findCartByUserId(Model model){ //request域//1.根据useId查询购物车信息Long userId = UserThreadLocal.get().getId();//通过本地线程标量 封装的Api获取用户名List<Cart> cartList = cartService.findCartListByUserId(userId);//直接调用之前订单模块业务层写好的方法即可model.addAttribute("carts",cartList);//key与页面保持一致return "order-cart";}
}

2.2.5 页面效果展现

在这里插入图片描述

2.3 订单表说明

2.3.1 订单表

在这里插入图片描述

2.3.2 订单表注意事项

说明:
1.订单的ID号不是主键自增.
2.订单状态信息由 1-6 注意状态说明.

问题: 为什么这里数据库的主键没有设置主键自增(MP形式在注解中设置),而是通过在程序中进行设定???
:为了在高并发情况下降低数据库的访问压力。数据库主键自增的实现流程是: 在一条数据插入到数据库时,需要查询上一条数据的id 在进行加1,这样每次都要查询一次id 在进行计算,在高并发情况下操作数据库的时间提供,速度降低(一般数据库很少出问题 比较稳定)。

解决:可以由程序自己设定主键,只要保证它是唯一不为空的即可。比如在此订单模块采用:“登录用户id+当前时间戳” 或者UUID(2^128)
解释:通过用户登录的id拼接当前时间戳(时间戳单位是毫秒 用户的id又能保证为同一用户,因为同一个用户不可能在同一毫秒内提交多个订单,所以可以保证id值唯一) 或者采用UUID的方式生成随机数(UUID的取值范围为2^128次方,几乎不可能重复,所以可以保证id唯一)。

时间戳(毫秒):时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数。

在这里插入图片描述

在这里插入图片描述

2.4 SpringMVC中参数格式说明

2.4.1 简单参数传值问题

说明:页面传参中 name属性必须与mvc中的数据保持一致.
1.页面信息

<input  type="text" name="name" value="二郎神"/>
<input  type="text" name="age" value="3500"/>

2.服务端接收

public String  saveUser(String name,Integer age){....}

2.4.2 利用对象的方式接收参数

1.页面信息

<input  type="text" name="name" value="二郎神"/>
<input  type="text" name="age" value="3500"/>

2.服务端接收(pojo对象要有对应的set get方法)
问题:为什么这个方法里面可以用User对象类型来接收,也没有new User() 那么这个对象哪来的???
:创建对象的方式,可以用new /可以用反射。具体执行流程是,当程序加载到控制层的有@controller注解标示的方法时,会通过反射实例化这个参数对象,在通过对象调用set()方法进行赋值操作,所以说方法的参数用对象接收时需要有对应的set方法。

public String  saveUser(User user){....}public class User{private Integer name;private String age;
}

2.4.3 mvc为对象的引用赋值(第三种)

需求:如果页面的name属性传的值都为二郎神
问题:如何解决重名提交的问题???
方案: 可以将对象进行封装,之后采用对象引用赋值的方式实现该功能.
注意事项: 属性的名称必须一致,否则赋值失败.

执行流程:首先整个页面的参数都用一个User对象来接收,当执行到dog.name时发现User对象中有这个dog对象的引用属性,会根据.age属性调用这个Dog类里面对应的属性实现接收参数。

1.页面信息

<input  type="text" name="name" value="二郎神"/>
<input  type="text" name="age" value="3500"/>
<input  type="text" name="dog.name" value="啸天犬"/>
<input  type="text" name="dog.age" value="5000"/>

2.服务端接收

public class User { private String name;private Integer age;private Dog dog;
}public class Dog{private String name;private Integer age;
}
public String  saveUser(User user){....}

2.4.4 订单对象定义(订单表)

mp规则:pojo里面的属性和数据库表里面的字段一 一对应,而这2个是由业务逻辑写的属性和数据库的字段没有关系。可以使用这个注解@TableField(exist=false) 表示该属性与数据库表中的字段没有关系,操作数据库表时不会使用该字段。
在这里插入图片描述

2.5 订单入库

2.5.1 业务说明

说明:当用户点击提交订单时要求实现三张表同时入库业务功能,并且保证orderId的值是一致的.
在这里插入图片描述

2.5.2 页面url、参数分析

1.页面URL请求
在这里插入图片描述

2.页面参数
说明:可以看到部分参数传递方式为:对象类型的引用赋值。因为订单表与订单商品表是一对多的关系,所以查询的结果为List集合对象,所以在User对象中保存订单商品表的引用对象为List< OrderItem> orderItems;类型,取值就变为通过下标去到具体的对象,再根据对象赋值属性。订单表与订单物流表是一对一关系 数据库查询的结果一次为一条,在user对象中存的引用是OrderShipping orderShipping;单个的对象接收。

在这里插入图片描述

2.5.3 页面JS分析

说明:当订单入库成功后时需要 把id返回给页面,之后 再次发送请求跳转到提交成功页面。
在这里插入图片描述

jQuery.ajax( {type : "POST",dataType : "json",  //发送请求服务器回调函数返回值的类型url : "/order/submit",//key=value&key2=value2&....客户端向服务器提交的参数太多 一个一个写太麻烦data : $("#orderForm").serialize(), //表单序列化,通过程序的方式,把所有的结构进行动态拼接,然后把参数传过来。简化参数多个// data: {"key":"value","key2":"value2".....}// data:  id=1&name="xxx"&age=18......cache : false, //不使用缓存success : function(result) {if(result.status == 200){//提交成功跳转到正确页面location.href = "/order/success.html?id="+result.data;}else{//提交失败提示用户$("#submit_message").html("订单提交失败,请稍后重试...").show();}},error : function(error) {$("#submit_message").html("亲爱的用户请不要频繁点击, 请稍后重试...").show();}});

2.5.4 编辑OrderController(web)

在这里插入图片描述

  package com.jt.controller;import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.Cart;
import com.jt.pojo.Order;
import com.jt.service.DubboCartService;
import com.jt.service.DubboOrderService;
import com.jt.thread.UserThreadLocal;
import com.jt.vo.SysResult;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;@Controller
@RequestMapping("/order")
public class OrderController {@Reference(check = false,timeout = 3000)private DubboOrderService dubboOrderService;//注入订单的对象@Reference(check = false,timeout = 3000)private DubboCartService cartService;//查询购物车信息注入购物车的service层/*** 订单提交* url: http://www.jt.com/order/submit* 参数: 整个form表单* 返回值: SysResult对象   携带返回值orderId* 业务说明:*   当订单入库之后,需要返回orderId.让用户查询跳转到订单入库成功页面.*/@RequestMapping("/submit")@ResponseBodypublic SysResult saveOrder(Order order){//获取用户id 先根据用户id找到具体的订单  在进行入库Long userId = UserThreadLocal.get().getId();order.setUserId(userId);//返回订单idString orderId = dubboOrderService.saveOrder(order);//判断查询的 orderId是否有值if(StringUtils.isEmpty(orderId))return SysResult.fail();elsereturn SysResult.success(orderId);}
}

2.5.5 编辑DubboOrderService(order)

在这里插入图片描述

package com.jt.service;import com.alibaba.dubbo.config.annotation.Service;
import com.jt.mapper.OrderItemMapper;
import com.jt.mapper.OrderMapper;
import com.jt.mapper.OrderShippingMapper;
import com.jt.pojo.Order;
import com.jt.pojo.OrderItem;
import com.jt.pojo.OrderShipping;
import org.springframework.beans.factory.annotation.Autowired;import java.util.List;@Service
public class DubboOrderServiceImpl implements  DubboOrderService{@Autowiredprivate OrderMapper orderMapper; //订单@Autowiredprivate OrderItemMapper orderItemMapper;//订单商品@Autowiredprivate OrderShippingMapper orderShippingMapper;//订单物流/*** Order{order订单本身/order物流信息/order商品信息}* 难点:  操作3张表完成入库操作* 主键信息: orderId 一对一 2张表的主键相同  一对多 在多的一方表中保存另一张表的主键 并设置为外键会自动维护关系*  3张表的 orderId相同,订单表 订单物流表的主键都为orderId  而订单商品表主键为itemId* @param order* @return*/@Overridepublic String saveOrder(Order order) {//1.拼接OrderId = 用户id+时间戳  注意他俩是拼接为字符串的方式,所以""不要放在最后面(变为先加在拼接了)String orderId ="" + order.getUserId() + System.currentTimeMillis();//2.完成订单入库        一般商品的订单未入库为 1 未付款状态order.setOrderId(orderId).setStatus(1);orderMapper.insert(order);//3.完成订单物流入库OrderShipping orderShipping = order.getOrderShipping();//因为为了区分参数重名问题 订单物流对象存入到了order对象中orderShipping.setOrderId(orderId);//一对一订单物流表的主键和订单表的主键一致orderShippingMapper.insert(orderShipping);//4.完成订单商品入库 一个订单中可以用多件商品信息List<OrderItem> orderItems = order.getOrderItems();//批量入库 通过动态SQL拼接  sql: insert into xxx(xxx,xx,xx)values (xx,xx,xx),(xx,xx,xx)....for (OrderItem orderItem : orderItems){orderItem.setOrderId(orderId);orderItemMapper.insert(orderItem);}System.out.println("订单入库成功!!!!");//打桩//返回订单编号return orderId;}
}

2.5.6 测试效果

说明:因为订单入库成功后需要返回订单id给前端页面,在页面js中需要再次发送请求进行跳转到订单提交成功页面,并根据订单id进行订单信息回显。这个地方只是做了入库,入库成功后页面发送请求跳转新页面的后台没写 所以404报错。
在这里插入图片描述

根据打桩提示,表示入库成功。
在这里插入图片描述

在这里插入图片描述

2.6 订单成功跳转

2.6.1 业务分析

业务说明: 根据orderId号,检索订单的数据信息.要求利用Order对象将所有的数据一起返回。

解释,点击提交订单发送的是2个请求:订单入库和订单成功页面回显,之前点击没显示是因为入库还没有成功 不可能发送入库成功时跳转 新页面的请求。
在这里插入图片描述
在这里插入图片描述

2.6.2 页面js分析

在需要跳转的页面为success.jsp
在这里插入图片描述

在这里插入图片描述

2.6.3 编辑OrderController(web)

在这里插入图片描述

package com.jt.controller;import com.alibaba.dubbo.config.annotation.Reference;
import com.jt.pojo.Cart;
import com.jt.pojo.Order;
import com.jt.service.DubboCartService;
import com.jt.service.DubboOrderService;
import com.jt.thread.UserThreadLocal;
import com.jt.vo.SysResult;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import java.util.List;@Controller
@RequestMapping("/order")
public class OrderController {@Reference(check = false,timeout = 3000)private DubboOrderService dubboOrderService;//注入订单的对象@Reference(check = false,timeout = 3000)private DubboCartService cartService;//查询购物车信息注入购物车的service层/*** 实现订单信息的查询  订单表 订单物流  订单商品* url: http://www.jt.com/order/success.html?id=191600674802663* 参数: id 订单的编号* 返回值: success.html* 页面取值: ${order.orderId}*/@RequestMapping("/success")//因为这个id是 订单orderId 用户id+时间戳拼接的字符串所以用String类型接收public String findOrderById(String id,Model model){Order order = dubboOrderService.findOrderById(id);model.addAttribute("order",order );return "success";}
}

2.6.4 编辑DubboOrderService(order)

在这里插入图片描述

package com.jt.service;import com.alibaba.dubbo.config.annotation.Service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jt.mapper.OrderItemMapper;
import com.jt.mapper.OrderMapper;
import com.jt.mapper.OrderShippingMapper;
import com.jt.pojo.Order;
import com.jt.pojo.OrderItem;
import com.jt.pojo.OrderShipping;
import org.springframework.beans.factory.annotation.Autowired;import java.util.List;@Service
public class DubboOrderServiceImpl implements  DubboOrderService{@Autowiredprivate OrderMapper orderMapper; //订单@Autowiredprivate OrderItemMapper orderItemMapper;//订单商品@Autowiredprivate OrderShippingMapper orderShippingMapper;//订单物流@Overridepublic Order findOrderById(String id) {//1.查询订单信息Order order  = orderMapper.selectById(id);//保存到Order pojo对象//2.查询订单物流信息OrderShipping orderShipping = orderShippingMapper.selectById(id); //保存到OrderShipping pojo对象//3.查询订单商品  因为订单商品表的orderId不是主键 所以不能用selectById(id方法 只能用普通方式查询QueryWrapper<OrderItem> queryWrapper = new QueryWrapper<>();queryWrapper.eq("order_id",id);List<OrderItem> lists =orderItemMapper.selectList(queryWrapper); //保存到OrderItem pojo对象/*** 因为 3个查询的结果分别保存在 订单表 订单物流表 订单商品表* 现在需要通过对象引用防止页面传参同名问题 所以需要set方法把订单物流表 订单商品表保存到订单表的属性中*/return order.setOrderItems(lists).setOrderShipping(orderShipping);}
}

注意事务控制
在这里插入图片描述

2.6.5 页面效果展现

说明:点击提交订单 跳转到订单提交成功页面并回显订单中的数据。
在这里插入图片描述

2.7 超时订单的处理

2.7.1 业务说明

说明:如果订单提交之后如果30分钟没有完成付款,则将订单状态(该属性在order订单表)改为6,表示订单交易失败。
(private Integer status;//状态:1、未付款 2、已付款 3、未发货 4、已发货 5、交易成功 6、交易失败)
问题:如何实现每个订单30分钟超时呀???

思路1: 利用数据库的计时的函数每当order入库之后,可以添加一个函数30分钟之后修改状态。
该方法不友好,100万的订单刚刚入库, 100万个监听的事件。

思路2: 利用消息队列的方式实现 ,redis开启线程向redis中存储数据之后设定超时时间,当key一旦失效则修改数据库状态。
Redis主要做缓存使用,不合适。

思路3:
开启单独的一个线程(异步),每隔1分钟查询一次数据库,修改超时的订单处理即可.

任务调度实现方式:
1)借助Java中的Timer对象去实现(缺点数据量访问大时每个都会创建线程,内存可能会崩掉,用下面2种)
2)借助Java线程池中的任务调度对象(ScheduledExecutorService )去实现
3)借助第三方框架去实现(quartz)

2.7.2 Quartz介绍

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个,百个,甚至是好几万个Jobs这样复杂的程序。Jobs可以做成标准的Java组件或 EJBs。Quartz的最新版本为Quartz 2.3.2。
在这里插入图片描述
组件说明:
1. Job 是用户自定义的任务.
2. JobDetail 负责封装任务的工具API.如果任务需要被执行,则必须经过jobDetail封装.
3. 调度器: 负责时间监控,当任务的执行时间一到则交给触发器处理
4. 触发器: 当接收到调度器的命令则开启新的线程执行job…

2.7.3 导入jar包

在这里插入图片描述

 <!--添加Quartz的支持 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-quartz</artifactId></dependency>

2.7.4 编辑配置类

在这里插入图片描述

package com.jt.config;import com.jt.quartz.OrderQuartz;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class OrderQuartzConfig {/*** 思想说明:* 	如果需要执行定时任务需要考虑的问题* 	1.任务多久执行一次.  1分钟执行一次(线程一分钟启动一次检查是否超时)* 	2.定时任务应该执行什么**///定义任务详情@Beanpublic JobDetail orderjobDetail() {//指定job的名称和持久化保存任务return JobBuilder.newJob(OrderQuartz.class)		//1.定义执行的任务.withIdentity("orderQuartz")	//2.任务指定名称.storeDurably().build();}//定义触发器@Beanpublic Trigger orderTrigger() {/*SimpleScheduleBuilder builder = SimpleScheduleBuilder.simpleSchedule().withIntervalInMinutes(1)	//定义时间周期.repeatForever();*/CronScheduleBuilder scheduleBuilder= CronScheduleBuilder.cronSchedule("0 0/1 * * * ?"); //时 分 秒return TriggerBuilder.newTrigger().forJob(orderjobDetail())	//执行的任务.withIdentity("orderQuartz")	//任务的名称.withSchedule(scheduleBuilder).build();}
}

2.7.5 编辑定时任务

在这里插入图片描述

package com.jt.quartz;import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.jt.mapper.OrderMapper;
import com.jt.pojo.Order;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;import java.util.Calendar;
import java.util.Date;//准备订单定时任务
@Component
public class OrderQuartz extends QuartzJobBean{@Autowiredprivate OrderMapper orderMapper;/*** 如果用户30分钟之内没有完成支付,则将订单的状态status由1改为6.* 条件判断的依据:  now()-创建时间 > 30分钟   <==>  created < now()-30** sql: update tb_order set status=6,updated=#{updated} where status=1 and created< #{timeOut}* @param context* @throws JobExecutionException*/@Override@Transactionalprotected void executeInternal(JobExecutionContext context) throws JobExecutionException {//1.利用java工具API完成计算  (用法查看java Api)Calendar calendar = Calendar.getInstance();  //获取当前的时间calendar.add(Calendar.MINUTE,-1);// 设置多长时间超时 比如1分钟 单位可以自己指定为分钟Date timeOut = calendar.getTime();	//获取超时时间(created < now()-1)Order order = new Order();order.setStatus(6);/*** order.setUpdated(new Date());因为mp时间自动填充 所以不需要设置更新时间* UPDATE tb_order SET updated=?, status=? WHERE (status = ? AND created < ?)* 如果订单状态为1 且订单创建时间created < now()-30则证明超时执行sql把状态修改为6 且更新订单时间* 订单创建时间哪来的???? 条件构造器是指sql执行的条件  传入对象是修改的值。* created是订单入库写sql时已经存入到数据库表里面的时间,所以直接进行比较即可**/UpdateWrapper<Order> updateWrapper = new UpdateWrapper<>();updateWrapper.eq("status", "1").lt("created",timeOut);orderMapper.update(order, updateWrapper);System.out.println("定时任务执行");}
}

2.7.6 测试效果

以一分钟超时为例:
订单入库成功后 先执行一次查询跳转到订单提示成功页面,之后一分钟检查一次是否超时 第一次还没有超时,sql不执行更新返回值行数为 0, 第二次超过1分钟再次进行检查发现超时执行sql更新返回值为影响的行数 1

在这里插入图片描述
可以看到订单表的状态修改为1
在这里插入图片描述

3 项目结构图

在这里插入图片描述

http://www.lbrq.cn/news/1045207.html

相关文章:

  • 国内做交互网站/什么网站做推广比较好
  • 台前网站建设价格/app推广联盟
  • 专门教ps的网站/app推广平台
  • 网站维护很难吗/查看别人网站的访问量
  • 美食网站建设的思路/他达拉非
  • 手机cms建站系统/中国搜索
  • 帝国网站认证码/2345网址大全设主页
  • 云南微网站建设的公司有哪些/seo优化排名技术百度教程
  • 店铺的网站怎么做/网络推广和信息流优化一样么
  • 营销网站建设专业团队在线服务/互联网平台推广
  • 怎么做视频还有网站/网站推广排名公司
  • 上海做推广网站/头条收录提交入口
  • 网站 用什么数据库/搜索引擎营销的主要模式有哪些
  • 如何做正版小说网站/网络营销推广技巧
  • 溧阳做网站价格/网站排名英文
  • 北京个人网站制作/免费发广告的网站
  • 哪个网站做简历免费/网络稿件投稿平台
  • 沈阳市网站建设哪里的公司比较好/汨罗网站seo
  • 关键词优化除了做网站还有什么方法/成都百度推广代理公司
  • 沈阳恢复营业通知/南宁seo收费
  • 珲春建设局网站/搜索引擎简称seo
  • 独立网站怎么做seo/快速收录域名
  • 上海达安做的无创dna网站/百度近日收录查询
  • 南岸集团网站建设/惠东seo公司
  • 学前端有必要找培训机构吗/汕头搜索引擎优化服务
  • 遵义网站建设哪家好/杭州seo排名优化外包
  • 公司网站自己怎么建立/最火的推广软件
  • 北京英众数字科技有限公司/seo互联网营销培训
  • 辽宁住房和城乡建设厅网站/广告策划方案范文
  • 免费推广的预期效果/优化大师官方
  • 41.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--扩展功能--集成网关--网关集成Swagger
  • 【东枫科技】FR3 可扩展测试平台,适用于 6G 研究与卫星通信,高达 1.6 GHz 的带宽
  • 【C++详解】红黑树规则讲解与模拟实现(内附红黑树插入操作思维导图)
  • Java 时间和空间复杂度
  • 【排序算法】②希尔排序
  • 利用whisper api实现若无字幕则自动下载音频并用 whisper 转写,再用 LLM 总结。