taotao订单系统需求分析、注意点、代码
需要注意的地方:
1、下订单功能一定要使用关系型数据库,因为其设计到钱,而noSql数据库相比来说丢失数据的风险更大。
但是查看订单列表、查看订单详情等功能则可以使用redis缓存来提高效率,因为其不涉及到钱的操作,只是展示给客户看。
2、订单系统还要注意,不能重复提交,比如用户下完订单后,如果点浏览器的后退功能,不能让用户再次退回到下单前的页面等。
3、其实还有个功能,是修改订单状态。
刚下单是下单未付款状态,付完款是 未发货状态,然后是发货状态,签收完成状态。
4、订单表的id生成策略
订单表的主键 id 不能用自增长,因为商城系统中订单实际上是会非常多的,数据量超大,后面很大可能要分表或者分库,而自增主键一般只能用在一个表中。
5、创建订单时的,Controller层数据的接收方式,以及测试时用 RESTClient是如何发送json格式数据的
强制用户登录的目的就是要根据用户id查询用户的收货地址,以便拼接用户订单。
前台表单,后台springMVC的Controller接收时要注意,spirngMVC的参数接收方式。
首先,我们后台用一个大对象来接收所有数据。其中 paymentType等是大对象的直接属性。
而orderItems是大对象中的一个属性,这个属性是一个对象集合List,而后台要使用LIst接收参数,前台要写成 1大属性名,2大属性集合中某一个元素的在集合中的角标,3集合中某一个元素的属性即:orderItems[3].itemId 这种格式。
大对象中还有一个属性是一个对象,而前台提交时,要写成4大对象的属性名,5大对象中属性所表示对象的属性 这种格式。
这个用于接收订单的大对象就是我们之前根据订单创建接口文档创建的订单对象。
返回值:返回一个jsp页面
如果Portal项目中出现异常,前台返回友好的错误页面,后台在Controller中要统一捕获,然后通过邮件和短信通知相关开发人员,迅速解决问题。
如果后台订单项目添加订单成功并返回了,但是这时前台项目portal项目报错了,那么就会造成后台数据库中添加进来多条订单,但是前台因为报错,却不知道,所以这时可以,在前台添加订单报错时,捕获,然后用拿到的订单号再调用一个服务去后台删除数据库中刚刚添加的订单。
后端order项目代码:
Controller:
package com.taotao.order.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import com.taotao.common.pojo.TaotaoResult; import com.taotao.common.utils.ExceptionUtil; import com.taotao.order.pojo.Order; import com.taotao.order.service.OrderService; /** * 订单处理的Controller * @author Administrator * */ @Controller public class OrderController { @Autowired private OrderService orderService; @RequestMapping("/create") @ResponseBody public TaotaoResult createOrder(@RequestBody Order order){ try { TaotaoResult result = orderService.createOrder(order, order.getOrderItems(), order.getOrderShipping()); return result; } catch (Exception e) { e.printStackTrace(); return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e)); } } }
service层:
package com.taotao.order.service.impl; import java.util.Date; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.mysql.fabric.xmlrpc.base.Data; import com.taotao.common.pojo.TaotaoResult; import com.taotao.mapper.TbOrderItemMapper; import com.taotao.mapper.TbOrderMapper; import com.taotao.mapper.TbOrderShippingMapper; import com.taotao.order.dao.JedisClient; import com.taotao.order.service.OrderService; import com.taotao.pojo.TbOrder; import com.taotao.pojo.TbOrderItem; import com.taotao.pojo.TbOrderShipping; @Service public class OrderServiceImpl implements OrderService { @Value("${ORDER_GEN_KEY}") //订单号生成key private String ORDER_GEN_KEY; @Value("${ORDER_INIT_ID}") //初始订单号 private String ORDER_INIT_ID; @Value("${ORDER_DETAIL_GEN_KEY}") //订单明细生成key private String ORDER_DETAIL_GEN_KEY; @Autowired private TbOrderMapper orderMapper; @Autowired private TbOrderItemMapper orderItemMapper; @Autowired private TbOrderShippingMapper orderShippingMapper; @Autowired private JedisClient jedisClient; /** * 生成订单 */ @Override public TaotaoResult createOrder(TbOrder order, List<TbOrderItem> itemList, TbOrderShipping orderShipping) { //向订单表中插入数据 //获得订单号 String string = jedisClient.get(ORDER_GEN_KEY); if (StringUtils.isEmpty(string)) { jedisClient.set(ORDER_GEN_KEY, ORDER_INIT_ID); } long orderId = jedisClient.incr(ORDER_GEN_KEY); //补全pojo的属性 order.setOrderId(orderId+""); //状态:1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭' order.setStatus(1); Date date = new Date(); order.setCreateTime(date); order.setUpdateTime(date); //0:未评价 1:已评价 order.setBuyerRate(0); //向订单表中插入数据 orderMapper.insert(order); //插入订单明细 for (TbOrderItem tbOrderItem : itemList) { long orderDetailId = jedisClient.incr(ORDER_DETAIL_GEN_KEY); tbOrderItem.setId(orderDetailId+""); tbOrderItem.setOrderId(orderId+""); //向订单明细插入记录 orderItemMapper.insert(tbOrderItem); } //插入物流表 //补全物流表的属性 orderShipping.setOrderId(orderId+""); orderShipping.setCreated(date); orderShipping.setUpdated(date); orderShippingMapper.insert(orderShipping); //返回订单号 return TaotaoResult.ok(orderId); } }
配置文件:
resource.properties
#定单号生成key
ORDER_GEN_KEY=ORDER_GEN_KEY
#初始订单号
ORDER_INIT_ID=10544
#订单明细生成key
ORDER_DETAIL_GEN_KEY=ORDER_DETAIL_GEN_KEY
前端项目 portal:
Controller:
package com.taotao.portal.controller; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import com.taotao.common.pojo.CartItem; import com.taotao.common.utils.ExceptionUtil; import com.taotao.portal.pojo.Order; import com.taotao.portal.service.CartService; import com.taotao.portal.service.OrderService; /** * 订单 * @author Administrator * order-cart * */ @Controller @RequestMapping("/order") public class OrderController { @Autowired private CartService cartService; @Autowired private OrderService orderService; //打开订单结算页面 @RequestMapping("/order-cart") public String showOrderCart(HttpServletRequest request,HttpServletResponse response,Model model){ List<CartItem> cartItemList = cartService.getCartItemListSync(request, response); model.addAttribute("cartList", cartItemList); return "order-cart"; } @RequestMapping("/create") public String createOrder(Order order,Model model,HttpServletRequest request,HttpServletResponse response){ try {
//从request中取用户信息
TbUser user = (TbUser) request.getAttribute("user");
//在order对象中补全用户信息
order.setUserId(user.getId());
order.setBuyerNick(user.getUsername());
//调用服务 String orderId = orderService.createOrder(order,request, response); model.addAttribute("orderId", orderId); model.addAttribute("payment", order.getPayment()); model.addAttribute("date", new DateTime().plusDays(3).toString("yyyy-MM-dd")); return "success"; } catch (Exception e) { e.printStackTrace(); model.addAttribute("message", "创建订单出错,请稍后再试"); return "error/exception"; } } }
Service:
orderService:
package com.taotao.portal.service.impl; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.taotao.common.pojo.TaotaoResult; import com.taotao.common.utils.HttpClientUtil; import com.taotao.common.utils.JsonUtils; import com.taotao.portal.pojo.Order; import com.taotao.portal.service.CartService; import com.taotao.portal.service.OrderService; @Service public class OrderServiceImpl implements OrderService { @Value("${ORDER_BASE_URL}") private String ORDER_BASE_URL; @Value("${ORDER_CREATE_URL}") private String ORDER_CREATE_URL; @Autowired private CartService cartService; @Override public String createOrder(Order order,HttpServletRequest request,HttpServletResponse response) { String json = HttpClientUtil.doPostJson(ORDER_BASE_URL+ORDER_CREATE_URL,JsonUtils.objectToJson(order)); TaotaoResult taotaoResult = TaotaoResult.format(json); if (taotaoResult.getStatus()==200) { Object orderId = taotaoResult.getData(); //订单提交完毕后,清空购物车(这里为了简单都是购物车整个提交订单,然后整个清空) cartService.delCart(request, response); return orderId.toString(); } return ""; } }
用到的 购物车相关service:
package com.taotao.portal.service.impl; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import com.taotao.common.pojo.CartItem; import com.taotao.common.pojo.TaotaoResult; import com.taotao.common.utils.CookieUtils; import com.taotao.common.utils.HttpClientUtil; import com.taotao.common.utils.JsonUtils; import com.taotao.pojo.TbItem; import com.taotao.pojo.TbUser; import com.taotao.portal.service.CartService; import com.taotao.portal.service.UserService; /** * 购物车service * @author Administrator */ @Service public class CartServiceImpl implements CartService { //rest服务基础url @Value("${REST_BASE_URL}") private String REST_BASE_URL; //获取商品信息的url @Value("${ITEM_INFO_URL}") private String ITEM_INFO_URL; //同步购物车的url @Value("${REST_CART_SYNC_URL}") private String REST_CART_SYNC_URL; @Autowired private UserService userService; /** * 添加购物车商品 * 1、接收controller传递过来的商品id,根据商品id查询商品信息。 2、从cookie中取出购物车信息,转换成商品pojo列表。 3、把商品信息添加到商品列表中。 参数: 1、商品id 2、Request 3、response 返回值: TaoTaoResult */ @Override public TaotaoResult addCartItem(long itemId, int num, HttpServletRequest request,HttpServletResponse response) { //合并购物车 Map<String, Object> map = mergeCart(request); List<CartItem> newList = (List<CartItem>) map.get("list"); //当前id对应的购物车商品 CartItem cartItem = null; //先判断原购物车列表中是否有当前商品 for (CartItem item : newList) { //如果存在 if (item.getId()==itemId) { //增加商品数量 item.setNum(item.getNum()+num); cartItem = item; break; } } //如果原来没有此商品,则新建 if (cartItem==null) { cartItem = new CartItem(); //调用rest服务获取商品信息 String doGetResult = HttpClientUtil.doGet(REST_BASE_URL+ITEM_INFO_URL+itemId); TaotaoResult taoTaoResult = TaotaoResult.formatToPojo(doGetResult, TbItem.class); if (taoTaoResult.getStatus()==200) { //封装成购物车商品对象 TbItem item = (TbItem) taoTaoResult.getData(); cartItem.setId(itemId); cartItem.setImage(item.getImage()==null?"":item.getImage().split(",")[0]); cartItem.setPrice(item.getPrice()); cartItem.setTitle(item.getTitle()); cartItem.setSellPoint(item.getSellPoint()); cartItem.setNum(num); //将商品加入到购物车列表中 newList.add(cartItem); } } //如果登录则向redis发送数据同步购物车 Boolean isLogin = (Boolean) map.get("isLogin"); if (isLogin) { Long userId = (Long) map.get("userId"); syncCart(userId, JsonUtils.objectToJson(newList)); } //将购物车列表写回cookie String listJson = JsonUtils.objectToJson(newList); //最后一个参数 true,对数据进行编码,这样在 cookie中就看不到中文原始数据了 CookieUtils.setCookie(request, response, "TT_CART", listJson,true); return TaotaoResult.ok(); } //合并购物车的方法 private Map<String, Object> mergeCart(HttpServletRequest request) { List<CartItem> cookieCartList = getCartItemList(request); Map<String, Object> map = new HashMap<>(); //是否登录 Boolean isLogin = false; Long userId = null; List<CartItem> newList; //准备合并redis和当前cookie的购物车 //先判断用户是否登录 TbUser currentUser = getCurrentUser(request); if (currentUser!=null) { //如果登录,先获取redis中的购物车 userId = currentUser.getId(); isLogin = true; List<CartItem> redisCart = syncCart(userId, null); if (redisCart!=null&&redisCart.size()>0) { //将两个购物车列表合并 Iterator<CartItem> it = redisCart.iterator(); //遍历redis购物车,对比,如果有和cookie一样的商品,则把数量加到Cookie中,删除自身 while (it.hasNext()) { CartItem redisItem = it.next(); for (CartItem cookieItem : cookieCartList) { if (redisItem.getId().equals(cookieItem.getId())) { System.out.println(redisItem.getId()); System.out.println(cookieItem.getId()); cookieItem.setNum(redisItem.getNum()+cookieItem.getNum()); //从resisList中删除 it.remove(); break; } } } } newList = new ArrayList<CartItem>(); //合并 if (redisCart!=null && redisCart.size()>0) { newList.addAll(redisCart); } if (cookieCartList!=null && cookieCartList.size()>0) { newList.addAll(cookieCartList); } //向redis发送数据同步购物车 syncCart(userId, JsonUtils.objectToJson(newList)); }else{ //用户没有登录时 newList = cookieCartList; } map.put("list", newList); map.put("isLogin", isLogin); map.put("userId", userId); return map; } /** * 从cookie中取商品列表 * @param request * @return */ private List<CartItem> getCartItemList(HttpServletRequest request) { //从cookie中取商品列表 String cartJson = CookieUtils.getCookieValue(request, "TT_CART",true); if (cartJson==null) { return new ArrayList<CartItem>(); } //把json转换为商品列表 try { List<CartItem> list = JsonUtils.jsonToList(cartJson, CartItem.class); return list; } catch (Exception e) { e.printStackTrace(); } return new ArrayList<CartItem>(); } /** * 获取 购物车商品列表供前台展示 */ @Override public List<CartItem> getCartItemList(HttpServletRequest request, HttpServletResponse response) { //展示列表的逻辑: //判断用户是否登录,如果未登录,直接取cookie中的数据 //如果已登录,直接取redis中的数据(不涉及到合并及同步问题,因为这个只是展示,合并只有在 用户添加商品到购物车时、用户修改购物车时才需要) //定义要返回前台的数据 List<CartItem> list = null; //判断用户是否已经登录,如果登录了,再同步 TbUser currentUser = getCurrentUser(request); if (currentUser!=null) { //如果登录则从redis中取数据到前台 list = syncCart(currentUser.getId(),null); }else{ list = getCartItemList(request); } return list; } /** * 同步获取购物车商品列表,用于未登录用户订单结算时,在订单结算页显示订单商品项 * @param request * @param response * @return */ @Override public List<CartItem> getCartItemListSync(HttpServletRequest request, HttpServletResponse response) { //展示列表的逻辑: //判断用户是否登录,如果未登录,直接取cookie中的数据 //如果已登录,直接取redis中的数据(不涉及到合并及同步问题,因为这个只是展示,合并只有在 用户添加商品到购物车时、用户修改购物车时才需要) //定义要返回前台的数据 List<CartItem> list = null; //判断用户是否已经登录,如果登录了,再同步 TbUser currentUser = getCurrentUser(request); if (currentUser!=null) { //如果登录则从redis中取数据到前台 list = syncCart(currentUser.getId(),null); }else{ // list = getCartItemList(request); //合并购物车 Map<String, Object> map = mergeCart(request); list = (List<CartItem>) map.get("list"); } return list; } /** * 直接输入数量,更新购物车中商品的数量(在购物车列表展示页面中使用) */ @Override public TaotaoResult updateCartItmeNum(long itemId, int num, HttpServletRequest request, HttpServletResponse response){ //执行此方法是,一定是已经打开了购物车列表页,也就是一定可以确定了用户是否已经登录 //先进行未登录的正常逻辑 //当前id对应的购物车商品 CartItem cartItem = null; //从cookie中获取购物车列表 List<CartItem> cartList = getCartItemList(request); //先判断原购物车列表中是否有当前商品 for (CartItem item : cartList) { //如果存在 if (item.getId()==itemId) { //修改商品数量 item.setNum(num); cartItem = item; break; } } //判断用户是否已经登录,如果登录了,再同步 TbUser currentUser = getCurrentUser(request); if (currentUser!=null) { //如果登录则向redis发送数据同步购物车 syncCart(currentUser.getId(), JsonUtils.objectToJson(cartList)); } //将购物车列表写回cookie String listJson = JsonUtils.objectToJson(cartList); //最后一个参数 true,对数据进行编码,这样在 cookie中就看不到中文原始数据了 CookieUtils.setCookie(request, response, "TT_CART", listJson,true); return TaotaoResult.ok(); } /** * 删除购物车中商品 * @param itemId * @param request * @param response * @return */ @Override public TaotaoResult delCartItem(long itemId, HttpServletRequest request, HttpServletResponse response){ //执行此方法是,一定是已经打开了购物车列表页,也就是一定可以确定了用户是否已经登录 //当前id对应的购物车商品 CartItem cartItem = null; //从cookie中获取购物车列表 List<CartItem> cartList = getCartItemList(request); Iterator<CartItem> iterator = cartList.iterator(); //遍历 while (iterator.hasNext()) { CartItem item = iterator.next(); //找到对应的商品 if (item.getId()==itemId) { //执行删除动作 iterator.remove(); break; } } //判断用户是否已经登录,如果登录了,再同步 TbUser currentUser = getCurrentUser(request); if (currentUser!=null) { //如果登录则向redis发送数据同步购物车 syncCart(currentUser.getId(), JsonUtils.objectToJson(cartList)); } //将购物车列表写回cookie String listJson = JsonUtils.objectToJson(cartList); //最后一个参数 true,对数据进行编码,这样在 cookie中就看不到中文原始数据了 CookieUtils.setCookie(request, response, "TT_CART", listJson,true); return TaotaoResult.ok(); } /** * 清空购物车 * @param request * @param response * @return */ @Override public TaotaoResult delCart(HttpServletRequest request, HttpServletResponse response){ //执行此方法是,一定是已经打开了购物车列表页,也就是一定可以确定了用户是否已经登录 //当前id对应的购物车商品 CartItem cartItem = null; //从cookie中获取购物车列表 List<CartItem> cartList = getCartItemList(request); Iterator<CartItem> iterator = cartList.iterator(); //遍历 while (iterator.hasNext()) { iterator.remove(); } //判断用户是否已经登录,如果登录了,再同步 TbUser currentUser = getCurrentUser(request); if (currentUser!=null) { //如果登录则向redis发送数据同步购物车 syncCart(currentUser.getId(), "empty"); } //将购物车列表写回cookie String listJson = JsonUtils.objectToJson(cartList); //最后一个参数 true,对数据进行编码,这样在 cookie中就看不到中文原始数据了 CookieUtils.setCookie(request, response, "TT_CART", listJson,true); return TaotaoResult.ok(); } /** * 从redis中获取购物车信息/同步购物车 * @param userId * @param cartList * 当此参数为 “empty”时为清空购物车 * 当此参数为null时,为获取redis中的购物车信息;否则为向redis中同步购物车信息 * @return */ private List<CartItem> syncCart(long userId, String cartList){ //定义返回值 List<CartItem> returnList = new ArrayList<CartItem>(); HashMap<String, String> map = new HashMap<String,String>(); map.put("userId", userId+""); map.put("cartList", cartList); String url = REST_BASE_URL+REST_CART_SYNC_URL; String json = HttpClientUtil.doPost(url, map); if (StringUtils.isNotEmpty(json)) { TaotaoResult taotaoResult = TaotaoResult.formatToList(json, CartItem.class); if (taotaoResult.getStatus()==200) { returnList = (ArrayList<CartItem>) taotaoResult.getData(); } } return returnList; } //获取当前用户信息 public TbUser getCurrentUser(HttpServletRequest request) { //判断用户是否登录 //从cookie中取token String token = CookieUtils.getCookieValue(request, "TT_TOKEN"); //根据token换取用户信息,调用sso系统的接口 ///这里需要写一些业务 逻辑,不要在拦截器中写,单写一个service,只在这里注入并调用 TbUser user = userService.getUserByToken(token); return user; } }
配置文件:
resourc.properties
#服务层属性定义
#服务层基础 url
REST_BASE_URL = http://localhost:8081/rest
#首页大 广告位
REST_INDEX_AD_URL = /content/list/89
#同步购物车的url
REST_CART_SYNC_URL=/cart/syncCart
#搜索服务基础url
SEARCH_BASE_URL=http://localhost:8083/search/query
#商品基础信息url
ITEM_INFO_URL=/item/info/
#商品描述信息url
ITEM_DESC_URL=/item/desc/
#商品参数的url
ITEM_PARAM_URL=/item/param/
#单点登录系统的url
SSO_BASE_URL=http://localhost:8084
#根据token获取用户信息的url
SSO_USER_TOKEN=/user/token/
#单点登录系统登录url
SSO_PAGE_LOGIN=/page/login/
#退出登录
SSO_USER_LOGOUT=/user/logout/
#订单系统基础url
ORDER_BASE_URL=http://localhost:8085/order
#创建订单服务
ORDER_CREATE_URL=/create