9.订单系统
[taotao]-订单系统
1.购物车
购物车功能逻辑处理
1.添加购物车不需要用户登录。购物车的数据应该放到cookie
2.当向购物车添加同一款商品时,购物车中商品的数量增加
3.购物车中可以删除商品
4.购物车中可以修改商品数量。商品的总价需要重新计算
5.点击“结算”按钮要求用户登录
【1】添加购物车
功能分析
请求的url:/cart/add/${itemId}.html
1)接收商品id,根据商品id取商品的图片、title、单价,需要包含商品的数量。并且把数据保存到列表中。
2)把商品数据转换成json数据,保存到cookie中。对购物车数据进行转码。
3)展示添加购物车成功页面
service
参数:商品id、商品数量
返回值:TaotaoResult
业务逻辑:
1)接收商品id,从cookie中购物车商品列表
2)从商品列表中查询列表是否存在此商品
如果存在商品的数量加上参数中的商品数量
如果不存在,调用rest服务,根据商品id获得商品数据。
3)把商品数据添加到列表中,把购物车商品列表写入cookie
4)返回TaotaoResult
public class CartItem {
private Long id;
private String title;
private Long price;
private Integer num;
private String image;
}
COOKIE_EXPIRE=432000
// 接口定义
public TaotaoResult addCart(Long itemId, Integer num,
HttpServletRequest request, HttpServletResponse response);
// 接口实现
package com.taotao.portal.service.impl;
@Service
public class CartServiceImpl implements CartService {
@Autowired
private ItemService itemService;
@Value("${COOKIE_EXPIRE}")
private Integer COOKIE_EXPIRE;
@Override
public TaotaoResult addCart(Long itemId, Integer num,
HttpServletRequest request, HttpServletResponse response) {
// 1、接收商品id
// 2、从cookie中购物车商品列表
List<CartItem> itemList = getCartItemList(request);
// 3、从商品列表中查询列表是否存在此商品
boolean haveFlg = false;
for (CartItem cartItem : itemList) {
//如果商品存在数量相加
// 4、如果存在商品的数量加上参数中的商品数量
if (cartItem.getId().longValue() == itemId) {
cartItem.setNum(cartItem.getNum() + num);
haveFlg = true;
break;
}
}
// 5、如果不存在,调用rest服务,根据商品id获得商品数据。
if (!haveFlg) {
TbItem item = itemService.getItemById(itemId);
//转换成CartItem
CartItem cartItem = new CartItem();
cartItem.setId(itemId);
cartItem.setNum(num);
cartItem.setPrice(item.getPrice());
cartItem.setTitle(item.getTitle());
if (StringUtils.isNotBlank(item.getImage())) {
String image = item.getImage();
String[] strings = image.split(",");
cartItem.setImage(strings[0]);
}
//添加到购物车商品列表
// 6、把商品数据添加到列表中
itemList.add(cartItem);
}
// 7、把购物车商品列表写入cookie
CookieUtils.setCookie(request, response, "TT_CART", JsonUtils.objectToJson(itemList), COOKIE_EXPIRE, true);
// 8、返回TaotaoResult
return TaotaoResult.ok();
}
/**
* 取购物车商品列表
* <p>Title: getCartItemList</p>
* <p>Description: </p>
* @param request
* @return
*/
private List<CartItem> getCartItemList(HttpServletRequest request) {
try {
//从cookie中取商品列表
String json = CookieUtils.getCookieValue(request, "TT_CART", true);
//把json转换成java对象
List<CartItem> list = JsonUtils.jsonToList(json, CartItem.class);
return list==null?new ArrayList<CartItem>():list;
} catch (Exception e) {
return new ArrayList<CartItem>();
}
}
}
controller
调用Service添加到购物车,返回jsp页面,添加成功页面
请求url:/cart/add/{itemId}.html?num=1
接收参数:商品id、商品数量
package com.taotao.portal.controller;
@Controller
public class CartController {
@Autowired
private CartService cartService;
@RequestMapping("/cart/add/{itemId}")
public String addCart(@PathVariable Long itemId, Integer num,
HttpServletResponse response, HttpServletRequest request) {
TaotaoResult result = cartService.addCart(itemId, num, request, response);
return "cartSuccess";
}
}
jsp
测试
随意访问一个商品详情,点击添加入购物车,测试
【2】展示购物车商品列表
功能分析
从cookie中取购物车商品列表,在页面展示
请求url:/cart/cart
无请求参数
service
没有参数,直接从cookie中取购物车商品列表返回
返回值:List<CartItem>
controller
接收请求,调用Service取商品列表,传递给jsp
- 请求url:/cart/cart
@RequestMapping("/cart/cart")
public String showCartList(HttpServletRequest request, Model model) {
List<CartItem> list = cartService.getCartItems(request);
//把商品列表传递给jsp
model.addAttribute("cartList", list);
return "cart";
}
jsp
查看购物车对应的jsp文件:cart.jsp
测试
【3】修改购物车商品数量
功能分析
请求url:/cart/update/num/{itemId}/{num}.action
1)把购物车商品列表从cookie中取出来,根据itemId找到对应的商品
2)使用参数中的数量更新数量
3)把购物车商品列表写回cookie。
4)返回json数据
service
接收参数:商品id、商品数量
// 接口定义
public TaotaoResult updateCartItem(long itemId, Integer num, HttpServletRequest request,
HttpServletResponse response);
// 接口实现
@Override
public TaotaoResult updateCartItem(long itemId, Integer num, HttpServletRequest request,
HttpServletResponse response) {
// 从cookie中取购物车商品列表
List<CartItem> itemList = getCartItemList(request);
//根据商品id查询商品
for (CartItem cartItem : itemList) {
if (cartItem.getId() == itemId) {
//更新数量
cartItem.setNum(num);
break;
}
}
//写入cookie
CookieUtils.setCookie(request, response, "TT_CART", JsonUtils.objectToJson(itemList), COOKIE_EXPIRE, true);
return TaotaoResult.ok();
}
controller
从url中取两个参数,调用Service更新商品数量,返回TaotaoResult(json格式的数据)
- 请求url:/cart/update/num/{itemId}/{num}.action
@RequestMapping("/cart/update/num/{itemId}/{num}")
@ResponseBody
public TaotaoResult updateCartItemNum(@PathVariable Long itemId, @PathVariable Integer num,
HttpServletResponse response, HttpServletRequest request) {
TaotaoResult result = cartService.updateCartItem(itemId, num, request, response);
return result;
}
【4】刪除购物车商品
功能分析
需要的参数是商品id,根据商品id删除cookie中的商品列表中对应的商品。重新写入cookie。
返回逻辑视图,购物车页面
service
1)接收商品id
2)从cookie中取购物车商品列表
3)找到对应id的商品,删除商品
4)重新把商品列表写入cookie
5)返回响应数据
参数:商品ID
返回值:TaotaoResult
// 接口定义
public TaotaoResult deleteCartItem(long itemId, HttpServletRequest request, HttpServletResponse response) ;
// 接口实现
@Override
public TaotaoResult deleteCartItem(long itemId, HttpServletRequest request, HttpServletResponse response) {
// 1、接收商品id
// 2、从cookie中取购物车商品列表
List<CartItem> itemList = getCartItemList(request);
// 3、找到对应id的商品
for (CartItem cartItem : itemList) {
if (cartItem.getId() == itemId) {
// 4、删除商品。
itemList.remove(cartItem);
break;
}
}
// 5、再重新把商品列表写入cookie。
CookieUtils.setCookie(request, response, "TT_CART", JsonUtils.objectToJson(itemList), COOKIE_EXPIRE, true);
// 6、返回成功
return TaotaoResult.ok();
}
controller
接收商品id,调用Service删除商品,返回购物车页面
请求url:/cart/delete/
@RequestMapping("/cart/delete/{itemId}")
public String deleteCartItem(@PathVariable Long itemId,
HttpServletResponse response, HttpServletRequest request) {
TaotaoResult result = cartService.deleteCartItem(itemId, request, response);
return "redirect:/cart/cart.html";
}
扩展
1.未登录的情况下,可以把购物车写入cookie。
2.已经登录的情况下,需要把购物车写入redis
3.登录时判断cookie中有购物车商品,应该把cookie中的购物车商品列表转移到redis中。
- Key: 用户id
- Value: 购物车商品列表
4.如果想redis中转移商品时,redis的购物车中已经有商品,此时需要把商品合并。如果是同一款商品数量叠加,如果新商品就合并商品。
2.订单系统实现
功能分析
1)订单确认页面,确认购买的商品、使用优惠券或积分、确认配送地址
2)确认页面有一个订单提交按钮,点击按钮生成订单
3)生成订单需要调用服务。需要发布一个生成订单服务
【1】订单系统创建
参考taotao-rest创建订单系统
pom.xml配置
<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>
<parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<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>
<parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-order</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-manager-mapper</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- Redis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
</dependencies>
<!-- 添加tomcat插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8085</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
框架整合
【2】订单服务发布
功能分析
1)接收json数据,需要使用@RequestBody注解,对应提交的json数据格式创建一个pojo接收。涉及表:
tb_order 订单表
tb_order_item 订单明细表
tb_order_shipping 物流表
可以使用订单相关的三个表对应的pojo封装一个pojo接收提交的json数据
public class OrderInfo extends TbOrder{
private List<TbOrderItem> orderItems;
private TbOrderShipping orderShipping;
public List<TbOrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<TbOrderItem> orderItems) {
this.orderItems = orderItems;
}
public TbOrderShipping getOrderShipping() {
return orderShipping;
}
public void setOrderShipping(TbOrderShipping orderShipping) {
this.orderShipping = orderShipping;
}
}
2)接收json数据后,把数据插入到对应的表中
订单号的生成:要求可读性号,不能太长应该是纯数字的,可有如下方案
a.UUID
b.毫秒
c.使用redis的incr命令(redis是单线程,不会重复)
3)返回TaotaoResult其中包含订单号
dao层
向tb_order 订单表tb_order_item 订单明细表tb_order_shipping 物流表插入数据,可以使用逆向工程
service层
1)插入订单表
接收数据OrderInfo
生成订单号
补全字段
插入订单表
2)插入订单明细
生成订单明细id,使用redis的incr命令生成
补全字段
插入数据
3)插入物流表
补全字段
插入数据
4)返回TaotaoResult包装订单号
// resource.properties
REDIS_ORDER_GEN_KEY=REDIS_ORDER_GEN
ORDER_ID_BEGIN=100544
REDIS_ORDER_DETAIL_GEN_KEY=REDIS_ORDER_DETAIL_GEN
// 接口定义
public TaotaoResult createOrder(OrderInfo orderInfo);
// 接口实现
package com.taotao.order.service.impl;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private TbOrderMapper orderMapper;
@Autowired
private TbOrderItemMapper orderItemMapper;
@Autowired
private TbOrderShippingMapper orderShippingMapper;
@Autowired
private JedisClient jedisClient;
@Value("${REDIS_ORDER_GEN_KEY}")
private String REDIS_ORDER_GEN_KEY;
@Value("${ORDER_ID_BEGIN}")
private String ORDER_ID_BEGIN;
@Value("${REDIS_ORDER_DETAIL_GEN_KEY}")
private String REDIS_ORDER_DETAIL_GEN_KEY;
@Override
public TaotaoResult createOrder(OrderInfo orderInfo) {
// 一、插入订单表
// 1、接收数据OrderInfo
// 2、生成订单号
//取订单号
String id = jedisClient.get(REDIS_ORDER_GEN_KEY);
if (StringUtils.isBlank(id)) {
//如果订单号生成key不存在设置初始值
jedisClient.set(REDIS_ORDER_GEN_KEY, ORDER_ID_BEGIN);
}
Long orderId = jedisClient.incr(REDIS_ORDER_GEN_KEY);
// 3、补全字段
orderInfo.setOrderId(orderId.toString());
//状态:1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭
orderInfo.setStatus(1);
Date date = new Date();
orderInfo.setCreateTime(date);
orderInfo.setUpdateTime(date);
// 4、插入订单表
orderMapper.insert(orderInfo);
// 二、插入订单明细
// 2、补全字段
List<TbOrderItem> orderItems = orderInfo.getOrderItems();
for (TbOrderItem orderItem : orderItems) {
// 1、生成订单明细id,使用redis的incr命令生成。
Long detailId = jedisClient.incr(REDIS_ORDER_DETAIL_GEN_KEY);
orderItem.setId(detailId.toString());
//订单号
orderItem.setOrderId(orderId.toString());
// 3、插入数据
orderItemMapper.insert(orderItem);
}
// 三、插入物流表
TbOrderShipping orderShipping = orderInfo.getOrderShipping();
// 1、补全字段
orderShipping.setOrderId(orderId.toString());
orderShipping.setCreated(date);
orderShipping.setUpdated(date);
// 2、插入数据
orderShippingMapper.insert(orderShipping);
// 返回TaotaoResult包装订单号。
return TaotaoResult.ok(orderId);
}
}
controller层
接收客户端发送的数据,接收json格式的数据使用@RequestBody OrderInfo 来接收数据,调用Service生成订单,返回TaotaoResult
package com.taotao.order.controller;
@Controller
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping(value="/order/create", method=RequestMethod.POST)
@ResponseBody
public TaotaoResult createOrder(@RequestBody OrderInfo orderInfo) {
try {
TaotaoResult result = orderService.createOrder(orderInfo);
return result;
} catch (Exception e) {
e.printStackTrace();
return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
}
}
}
测试
{
"orderId": null,
"payment": "20996.00",
"paymentType": 1,
"postFee": "0",
"status": null,
"createTime": null,
"updateTime": null,
"paymentTime": null,
"consignTime": null,
"endTime": null,
"closeTime": null,
"shippingName": null,
"shippingCode": null,
"userId": 38,
"buyerMessage": "empty",
"buyerNick": "xixi",
"buyerRate": null,
"orderItems": [
{
"id": null,
"itemId": "154684233038024",
"orderId": null,
"num": 3,
"title": "宏碁(Acer)暗影骑士3游戏本",
"price": 549900,
"totalFee": 1649700,
"picPath": "http://192.168.187.128/group1/M00/00/00/wKi7gFwvbO-ASU1GAAAF22vMnHo825.jpg"
},
{
"id": null,
"itemId": "154684383340439",
"orderId": null,
"num": 1,
"title": "宏碁(Acer)蜂鸟Swift3微边框金属轻薄本14英寸笔记本电脑SF314(i5-8250U 8G 128G SSD+1T IPS 指纹)小超银",
"price": 449900,
"totalFee": 449900,
"picPath": "http://192.168.187.128/group1/M00/00/00/wKi7gFwvczeABAmSAAAF22vMnHo859.jpg"
}
],
"orderShipping": {
"orderId": null,
"receiverName": "小白",
"receiverPhone": null,
"receiverMobile": "15891588888",
"receiverState": "北京",
"receiverCity": "北京",
"receiverDistrict": "昌平区",
"receiverAddress": "西三旗 xxxxxxxxx",
"receiverZip": null,
"created": null,
"updated": null
}
}
3.门户网站整合订单系统
【1】显示订单确认页面
功能分析
请求的url:/order/order-cart
1)商品数据从购物车中获得
2)访问此页面要求用户登录
3)需要根据用户id查询常用地址列表(待实现)
service
可使用购物车service
controller
package com.taotao.portal.controller;
@Controller
@RequestMapping("/order")
public class OrderController {
@Autowired
private CartService cartService;
@RequestMapping("/order-cart")
public String showOrderCart(Model model, HttpServletRequest request) {
//取购物车商品列表
List<CartItem> list = cartService.getCartItems(request);
model.addAttribute("cartList", list);
return "order-cart";
}
}
测试
【2】提交订单
功能分析
在订单确认页面点击“提交订单按钮”进行提交订单的处理。把表单的内容提交给taotao-portal的后台,后台调用taotao-order发布的服务生成订单。展示订单提交成功页面,并且展示订单号。
service
接收表单的内容OrderInfo,调用taotao-order的服务,生成订单。提交数据格式是json格式;返回TaotaoResult,其中包含订单号,取出订单号,并返回。
参数:OrderInfo
返回值:String 订单号
-- resource.properties
ORDER_BASE_URL=http://localhost:8085/order
ORDER_CREATE_URL=/order/create
// 接口定义
public String createOrder(OrderInfo orderInfo);
// 接口实现
@Service
public class OrderServiceImpl implements OrderService {
@Value("${ORDER_BASE_URL}")
private String ORDER_BASE_URL;
@Value("${ORDER_CREATE_URL}")
private String ORDER_CREATE_URL;
@Override
public String createOrder(OrderInfo orderInfo) {
//把OrderInfo转换成json
String json = JsonUtils.objectToJson(orderInfo);
//提交订单数据
String jsonResult = HttpClientUtil.doPostJson(ORDER_BASE_URL + ORDER_CREATE_URL, json);
//转换成java对象
TaotaoResult taotaoResult = TaotaoResult.format(jsonResult);
//取订单号
String orderId = taotaoResult.getData().toString();
return orderId;
}
}
controller
接收提交的订单信息,使用OrderInfo接收。调用Service创建订单,跳转到成功页面,并且把订单号传递给页面。
- 请求url:/order/create
@Controller
@RequestMapping("/order")
public class OrderController {
@Autowired
private CartService cartService;
@Autowired
private OrderService orderService;
@RequestMapping("/order-cart")
public String showOrderCart(Model model, HttpServletRequest request) {
//取购物车商品列表
List<CartItem> list = cartService.getCartItems(request);
model.addAttribute("cartList", list);
return "order-cart";
}
@RequestMapping(value="/create", method=RequestMethod.POST)
public String createOrder(OrderInfo orderInfo, Model model, HttpServletRequest request) {
//取用户信息
TbUser user = (TbUser) request.getAttribute("user");
//补全orderIn的属性
orderInfo.setUserId(user.getId());
orderInfo.setBuyerNick(user.getUsername());
//调用服务
String orderId = orderService.createOrder(orderInfo);
//把订单号传递个页面
model.addAttribute("orderId", orderId);
model.addAttribute("payment", orderInfo.getPayment());
DateTime dateTime = new DateTime();
dateTime = dateTime.plusDays(3);
model.addAttribute("date", dateTime.toString("yyyy-MM-dd"));
//返回逻辑视图
return "success";
}
}
测试
问题排查1:Jedis连接问题
查看相应的redis是否启动?
查看服务器防火墙是否关闭?
查看redis服务器访问的ip、端口号是否正确?
Jedis对象使用完后需要释放,否则导致其一直占用而无法获取新的资源
当jedispool中的jedis被取完,而等待时间超过你设置的 MaxWaitMillis 就会抛出Could not get a resource from the pool
连接池中空闲的连接过一阵子就会自动断开,但是连接池还以为连接正常