订单系统和前台系统的交互---预订单--订单--支付--登录?

订单单点登录功能实现

导入shop-sso依赖

开启@EnableDubbo

配置yml文件中的dubbo服务

提供应用信息和zookeeper地址

拦截器和配置拦截器类

因为订单这个系统是没有登录页面的,所以我们拦截器拦截这个订单系统的时候,如果他的ticket为空或者过期那么就跳回到前台系统的登录页面,因为这个是跨系统的,所以我们重定向的时候需要一个完整的路径,那么我们需要拿到前台系统的url,前台系统的url可以在yml文件中配置,通过@Value("${shop.portal.url}")去获取。
在拦截器中我们根据ticket拦截,如果没有登录则去登录,登录成功后重定向到已经登录过后才可以访问的页面。

代码:Important!!!

单点登录失败,从订单系统重定向前台系统

response.sendRedirect(portalUrl+"login?redirectUrl="+request.getRequestURL());

这是一个JavaWeb中的重定向代码。response.sendRedirect() 是重定向到指定的URL,参数portalUrl是重定向的URL基础地址,login是登录页面的URL,redirectUrl是登录成功后要重定向回来的页面的URL。request.getRequestURL()是获取当前请求的URL,这里将其作为参数传递给了重定向URL,登录成功后重回当前页面。其中redirectUrl参数可以是任何需要登录后才能访问的页面,例如用户个人中心、订单页面等等。

具体实现
当用户的ticket在订单系统没有通过验证,被拦截器拦截了,就需要跳转到portal系统的login方法那里,传进去一个redirectUrl参数,存到request域中,跳转到登录页面,登录页面有一个隐藏域来存放这个redirectUrl,如果是重定向来的,会给这个隐藏域赋值,反之不赋值,登录页面填写信息之后点击登录,发送请求,如果redirectUrl中有值,重定向到redirectUrl中的地址,反之,重定向到portal首页。

注意这里需要重新去写这个login方法,不能用portal里面未登录进入登录页面,登录成功的去向的Controller,因为这里虽然是去了portal的登录页面,但登录成功之后是重定向了原先请求的页面,需要去向不同,我们应该重新进行一个编写。

代码
配置拦截器类:


/**
 * MVC配置类
 *
 * @author zhoubin
 * @since 1.0.0
 */
@Configuration
@EnableWebMvc
public class MvcConfig implements WebMvcConfigurer {

	@Autowired
	private OrderLoginInterceptor loginInterceptor;
	@Autowired
	private OrderCommonInterceptor commonInterceptor;

	/**
	 * addInterceptor:添加自定义拦截器
	 * addPathPatterns:添加拦截请求  /**表示拦截所有
	 * excludePathPatterns:不拦截的请求
	 * @param registry
	 */
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(commonInterceptor)
				.addPathPatterns("/**");
		registry.addInterceptor(loginInterceptor)
				.addPathPatterns("/**")
				.excludePathPatterns("/static/**")
				.excludePathPatterns("/login/**")
				.excludePathPatterns("/image/**")
				.excludePathPatterns("/user/login/**")
				.excludePathPatterns("/user/logout/**");
	}

	/**
	 * 放行静态资源
	 * @param registry
	 */
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
	}
}

拦截器三种方法:

@Component
public class OrderLoginInterceptor implements HandlerInterceptor {

	@Reference(interfaceClass = ShopSSOService.class)
	private ShopSSOService ssoService;
	@Autowired
	private RedisTemplate<String,String> redisTemplate;
	@Value("${user.ticket}")
	private String userTicket;
	@Value("${shop.portal.url}")
	private String portalUrl;

	/**
	 * 请求处理的方法之前执行
	 * @param request
	 * @param response
	 * @param handler
	 * @return
	 * @throws Exception
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		//获取用户票据
		String ticket = CookieUtil.getCookieValue(request, "userTicket");
		if (!StringUtils.isEmpty(ticket)){
			//如果票据存在,进行验证
			Admin admin = ssoService.validate(ticket);
			//将用户信息存入session中,用于页面返显
			request.getSession().setAttribute("user",admin);
			//重新设置失效时间
			ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
			valueOperations.set(userTicket+":"+ticket, JsonUtil.object2JsonStr(admin),30, TimeUnit.MINUTES);
			return true;
		}
		//票据不存在或者用户验证失败,重定向至登录页面
		response.sendRedirect(portalUrl+"login?redirectUrl="+request.getRequestURL());
		return false;
	}

	/**
	 * 请求处理的方法之后执行
	 * @param request
	 * @param response
	 * @param handler
	 * @param modelAndView
	 * @throws Exception
	 */
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

	}

	/**
	 * 处理后执行清理工作
	 * @param request
	 * @param response
	 * @param handler
	 * @param ex
	 * @throws Exception
	 */
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
	                            Exception ex) throws Exception {

	}
}

在portal前台页面中编写login方法

 @RequestMapping("login")
    public String login(String redirectUrl, Model model){
        model.addAttribute("redirectUrl",redirectUrl);
        return "login";
    }

前台
!''表示如果获取到的值为空,则将其转换为空字符串。

<input type="hidden" id="redirectUrl" value="${redirectUrl!''}"/>
<script type="text/javascript">
    // 用户登录
    function userLogin() {
        $.ajax({
            url: "${ctx}/user/login",
            type: "POST",
            data: $("#formlogin").serialize(),
            dataType: "JSON",
            success: function (result) {
                if (200 == result.code) {
                    // 如果存在重定向url则重定向至该url
                    if ($("#redirectUrl").val()) {
                        location.href = $("#redirectUrl").val();
                    }else {
                        location.href = "${ctx}/index";
                    }

                } else {
                    layer.msg("用户名或密码错误,请重新输入!");
                }
            },
            error: function () {
                layer.alert("亲,系统正在升级中,请稍后再试!");
            }
        });
    }
</script>

点击去结算跳转到预订单页面,说明为什么需要定义一个拦截器?@Value?

去结算的那个页面是在前台系统的商品列表页面中,点击按钮之后跳转到订单系统的预订单页面。因此我们把订单系统的url放到前台系统的yml中,通过@Value("${shop.order.url}")获取订单系统的完整的url
注意这里的小坑
@Value注解是spring容器中的,我们无法在Controller层也就是springmvc容器中获取key中的value值。

原因是controller注册在spring-mvc.xml代表的SpringMVC的容器中,而service则注册在applicationContext.xml代表的Spring的容器中。如果要使用@Value注解,需要在对应的容器中进行注册。

解决方法:我们可以把我们的url放到我们的一个最大的作用域application,通过拦截器去拿,因为拦截器会使用@Component注解,这样我们就可以将@Value注解注册到spring容器中了。然后将url放到application域中。
但是我们这个拦截器会不拦截请求,但是每一个请求他都会将url存入到我们的application域中,会造成一个频繁的写操作,因此我们要进行判断,如果这个url已经存过了,那么就不需要再存了。

配置拦截器类

@Autowired
	private PortalCommonInterceptor commonInterceptor;

	/**
	 * addInterceptor:添加自定义拦截器
	 * addPathPatterns:添加拦截请求  /**表示拦截所有
	 * excludePathPatterns:不拦截的请求
	 * @param registry
	 */
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(commonInterceptor)
				.addPathPatterns("/**");
		registry.addInterceptor(loginInterceptor)
				.addPathPatterns("/cart/**")
				.excludePathPatterns("/static/**")
				.excludePathPatterns("/login/**")
				.excludePathPatterns("/user/login/**")
				.excludePathPatterns("/user/logout/**");
	}

实现HanlerInterceptor接口

@Component
public class PortalCommonInterceptor implements HandlerInterceptor {


	@Value("${shop.order.url}")
	private String shopOrderUrl;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		//获取application对象
		ServletContext servletContext = request.getSession().getServletContext();
		String orderUrl = (String) servletContext.getAttribute("orderUrl");
		if (StringUtils.isEmpty(orderUrl)){
			servletContext.setAttribute("orderUrl",shopOrderUrl);
		}
		return true;
	}
}

接下来就是实现从前台系统跳转到订单系统了。
此时portal的全局域内有orderUrl,我们点击去结算按钮走重定向的Controller,到达订单系统之后我们走订单系统的Controller,去预订单页面。
前台系统的Controller

    /**
     * 跳转订单系统
     * @return
     */
    @RequestMapping("/toPreOrder")
    public String toPreOrder(HttpServletRequest request){
        //获取ServletContext上下文中的值
        String orderUrl = (String) request.getSession().getServletContext().getAttribute("orderUrl");
        //重定向
        return "redirect:"+orderUrl+"order/preOrder";
    }

订单系统的Controller

/**
	 * 跳转到预订单页面
	 *
	 * @return
	 */
	@RequestMapping("/preOrder")
	public String preOrder(Model model, HttpServletRequest request) {
		Admin admin = (Admin) request.getSession().getAttribute("user");
		model.addAttribute("cartResult", cartService.getCartList(admin));
		return "order/preOrder";
	}

到达预订单系统应当展示购物车列表和总金额

**怎么获取?**购物车列表和总金额可以调用rpc服务中的CartService获取。要调用这个方法必须要获取用户信息,查看哪个用户的购物车信息。因为在拦截器里面登录的用户信息存放在session中,因此我们从session中获取用户信息。

@RequestMapping("/preOrder")
	public String preOrder(Model model, HttpServletRequest request) {
		Admin admin = (Admin) request.getSession().getAttribute("user");
		model.addAttribute("cartResult", cartService.getCartList(admin));
		return "order/preOrder";
	}

公共的页面展示出购物车的数量,调用rpc的CatService

@Controller
@RequestMapping("/cart")
public class CartController {

	@Reference(interfaceClass = CartService.class)
	private CartService cartService;


	/**
	 * 获取购物车数量
	 * @return
	 */
	@RequestMapping("/getCartNum")
	@ResponseBody
	public Integer getCartNum(HttpServletRequest request){
		Admin admin = (Admin) request.getSession().getAttribute("user");
		return cartService.getCartNum(admin);
	}

}

提交订单

点击提交按钮,前端传过来的有CartResult(购物车列表,总金额),
点击提交订单之后,有三件事情需要完成
1、跳转到订单页面
2、删除购物车中的数据
3、将提交的数据变道数据库里面去,为了后续的查看全部支部订单。
返回的是否成功,因为需要渲染页面,把订单和总金额传入前台。

生成订单

要把订单存入到数据库中首先要先生成订单。
通过mybatis-plus进行生成。
有关订单的数据库有t_order,t_order_goods,生成pojo,mapper,xml文件。
new一个order,属性赋值,然后插入到数据库中,如果插入成功,则获取CarResult中List的每一个元素,让他转换为OrderGoods,设置订单商品中的订单号为订单编码,与Order关联起来,然后将OrderGoods放到一个List集合中,插入数据库中,如果插入成功,并未出现异常,则返回成功,否则返回失败,因为Controller需要一个订单编号,所以我们可以将返回的成功的信息里面放入我们的订单编号,在Controller就可以进行存入了。
在这里生成的订单的状态信息会用一个枚举类来进行定义。

注意这里的订单编号是唯一的,我们用的是redis的自增key

@Override
	public BaseResult orderSave(Admin admin, CartResult cartResult) {
		//创建order对象
		Order order = new Order();
		//订单编号 shop_年月日时分秒_自增key
		String orderSn = "shop_"+ DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now())+"_"+getIncrement(redisOrderIncrement);
		order.setOrderSn(orderSn);
		//用户id
		order.setUserId(admin.getAdminId().intValue());
		//订单状态(未确认)
		order.setOrderStatus(OrderStatus.no_confirm.getStatus());
		//发货状态(未发货)
		order.setShippingStatus(SendStatus.no_pay.getStatus());
		//支付状态(未支付)
		order.setPayStatus(PayStatus.no_pay.getStatus());
		//商品总价
		order.setGoodsPrice(cartResult.getTotalPrice());
		//应付金额
		order.setOrderAmount(cartResult.getTotalPrice());
		//订单总价
		order.setTotalAmount(cartResult.getTotalPrice());
		//订单时间
		Long addTime = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
		order.setAddTime(addTime.intValue());
		int result = orderMapper.insertSelective(order);
		//存储成功
		if (result>0){
			List<OrderGoods> orderGoodsList  = new ArrayList<>();
			for (CartVo cartVo : cartResult.getCartList()) {
				//创建orderGoods对象
				OrderGoods orderGoods = new OrderGoods();
				//订单id
				orderGoods.setOrderId(order.getOrderId());
				//商品id
				orderGoods.setGoodsId(cartVo.getGoodsId());
				//商品名称
				orderGoods.setGoodsName(cartVo.getGoodsName());
				//商品价格
				orderGoods.setGoodsPrice(cartVo.getMarketPrice());
				//商品数量
				orderGoods.setGoodsNum(cartVo.getGoodsNum().shortValue());
				//订单方式
				orderGoods.setPromType(PromTypeStatus.normal.getStatus());
				//发货状态
				orderGoods.setIsSend(SendStatus.no_pay.getStatus());
				//添加到订单商品对象列表
				orderGoodsList.add(orderGoods);
			}
			//批量插入
			result = orderGoodsMapper.insertOrderGoodsBatch(orderGoodsList);
			if (result>0){
				BaseResult baseResult = BaseResult.success();
				baseResult.setMessage(orderSn);
				return baseResult;
			}
		}
		return BaseResult.error();
	}

/**
	 * redis自增key
	 * @param key
	 * @return
	 */
	private Long getIncrement(String key){
		RedisAtomicLong entityIdCounter = new RedisAtomicLong(key,redisTemplate.getConnectionFactory());
		return entityIdCounter.getAndIncrement();
	}
}

跳转到提交页面

1、生成订单
2、清空购物车
3、跳转页面

	@RequestMapping("/submitOrder")
	public String submitOrder(Model model, HttpServletRequest request) {
		Admin admin = (Admin) request.getSession().getAttribute("user");
		CartResult cartResult = cartService.getCartList(admin);
		//1.存入订单信息
		BaseResult baseResult = orderService.orderSave(admin, cartResult);
		//2.清除购物车信息
		cartService.clearCart(admin);
		//总价
		model.addAttribute("totalPrice", cartResult.getTotalPrice());
		//订单编号
		model.addAttribute("orderSn", baseResult.getMessage());
		//3.页面跳转
		return "/order/submitOrder";
	}

订单系统和前台系统的交互

订单系统可以在搜索框中进行搜索,搜索跳转到前台系统。

搜索进入前台页面

@Controller
@RequestMapping("search")
public class SearchController {

	/**
	 * 跳转搜索页面,传过去一个searchStr
	 * @param request
	 * @param searchStr
	 * @param model
	 * @return
	 */
	@RequestMapping("index")
	public String index(HttpServletRequest request, String searchStr, Model model){
		try {
			//对输入的内容进行编码,防止中文乱码
			searchStr = URLEncoder.encode(searchStr,"UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "redirect:"+request.getSession().getServletContext().getAttribute("portalUrl")+"search/index?searchStr="+searchStr;
	}

}

在订单系统点击登出:
重定向,从应用域中获取数据request.getSession().getServletContext().getAttribute()

	@RequestMapping("/index")
	public String index(HttpServletRequest request, String searchStr, Model model){
		try {
			//对输入的内容进行编码,防止中文乱码
			searchStr = URLEncoder.encode(searchStr,"UTF-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return "redirect:"+request.getSession().getServletContext().getAttribute("portalUrl")+"search/index?searchStr="+searchStr;
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值