1. 今日大纲

  1. 实现购物车
  2. 基于Mysql实现读写分离
  1. 购物车

    1. 需求描述

  1. 用户可以在登录状态下将商品添加到购物车
  2. 用户可以在未登录状态下将商品添加到购物车
  3. 用户可以使用购物车一起结算下单
  4. 用户可以查询自己的购物车
  5. 用户可以在购物车中可以修改购买商品的数量。
  6. 用户可以在购物车中删除商品。

开发模式:敏捷开发

2个核心:

  1. 用户故事
  2. 周期迭代
  1. 业务流程

  1. 搭建购物车系统(taotao-cart)

    1. 创建工程

  1. 导入依赖

<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.parent</groupId>

<artifactId>taotao-parent</artifactId>

<version>0.0.1-SNAPSHOT</version>

</parent>

<groupId>com.taotao.cart</groupId>

<artifactId>taotao-cart</artifactId>

<version>1.0.0-SNAPSHOT</version>

<packaging>war</packaging>

<dependencies>

<dependency>

<groupId>com.taotao.common</groupId>

<artifactId>taotao-common</artifactId>

<version>1.0.0-SNAPSHOT</version>

</dependency>

<!-- 单元测试 -->

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<scope>test</scope>

</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>

<!-- Mybatis -->

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

</dependency>

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis-spring</artifactId>

</dependency>

<!-- 分页助手 -->

<dependency>

<groupId>com.github.pagehelper</groupId>

<artifactId>pagehelper</artifactId>

</dependency>

<dependency>

<groupId>com.github.jsqlparser</groupId>

<artifactId>jsqlparser</artifactId>

</dependency>

<!-- 通用Mapper -->

<dependency>

<groupId>com.github.abel533</groupId>

<artifactId>mapper</artifactId>

</dependency>

<!-- MySql -->

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

</dependency>

<dependency>

<groupId>org.slf4j</groupId>

<artifactId>slf4j-log4j12</artifactId>

</dependency>

<!-- Jackson Json处理工具包 -->

<dependency>

<groupId>com.fasterxml.jackson.core</groupId>

<artifactId>jackson-databind</artifactId>

</dependency>

<!-- 连接池 -->

<dependency>

<groupId>com.jolbox</groupId>

<artifactId>bonecp-spring</artifactId>

<version>0.8.0.RELEASE</version>

</dependency>

<!-- httpclient -->

<dependency>

<groupId>org.apache.httpcomponents</groupId>

<artifactId>httpclient</artifactId>

</dependency>

<!-- JSP相关 -->

<dependency>

<groupId>jstl</groupId>

<artifactId>jstl</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>

<!-- Apache工具组件 -->

<dependency>

<groupId>org.apache.commons</groupId>

<artifactId>commons-lang3</artifactId>

</dependency>

<dependency>

<groupId>org.apache.commons</groupId>

<artifactId>commons-io</artifactId>

</dependency>

</dependencies>

<build>

<plugins>

<!-- 配置Tomcat插件 -->

<plugin>

<groupId>org.apache.tomcat.maven</groupId>

<artifactId>tomcat7-maven-plugin</artifactId>

<configuration>

<port>8086</port>

<path>/</path>

</configuration>

</plugin>

</plugins>

</build>

</project>

  1. Web.xml

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns="http://java.sun.com/xml/ns/javaee"

xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

id="WebApp_ID" version="2.5">

<display-name>taotao-cart</display-name>

<context-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:spring/applicationContext*.xml</param-value>

</context-param>

<!--Spring的ApplicationContext 载入 -->

<listener>

<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

<!-- 编码过滤器,以UTF8编码 -->

<filter>

<filter-name>encodingFilter</filter-name>

<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>

<init-param>

<param-name>encoding</param-name>

<param-value>UTF8</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>encodingFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

<!-- 配置SpringMVC框架入口 -->

<servlet>

<servlet-name>taotao-cart</servlet-name>

<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

<init-param>

<param-name>contextConfigLocation</param-name>

<param-value>classpath:spring/taotao-cart-servlet.xml</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

<servlet-name>taotao-cart</servlet-name>

<url-pattern>*.html</url-pattern>

</servlet-mapping>

<welcome-file-list>

<welcome-file>index.jsp</welcome-file>

</welcome-file-list>

</web-app>

  1. 其他配置文件

  1. 配置nginx和hosts

Hosts:

  1. 导入页面

  1. 表结构

使用联合索引,一定要注意索引字段的顺序。

  1. 加入商品到购物车的地址

http://cart.taotao.com/cart/{itemId}.html

  1. 通过拦截器判断用户是否登录

    1. 编写拦截器

  1. 配置拦截器

  1. 商品加入购物车

    1. Controller

  1. Service

加入商品到购物车:

public void addItemToCart(Long itemId) {

// 判断该商品在购物车中是否存在

User user = UserThreadLocal.get();

Cart record = new Cart();

record.setItemId(itemId);

record.setUserId(user.getId());

Cart cart = this.cartMapper.selectOne(record);

if (null == cart) {

// 购物车中不存在该商品

cart = new Cart();

cart.setItemId(itemId);

cart.setUserId(user.getId());

cart.setCreated(new Date());

cart.setUpdated(cart.getCreated());

Item item = this.itemService.queryItemById(itemId);

cart.setItemImage(item.getImages()[0]);

cart.setItemPrice(item.getPrice());

cart.setItemTitle(item.getTitle());

// 将Cart保存到数据库

this.cartMapper.insert(cart);

} else {

// 该商品已经存在购物车中

cart.setUpdated(new Date());

this.cartMapper.updateByPrimaryKey(cart);

}

}

  1. 查询购物车列表

    1. Controller

  1. Service

按照加入购物车时间倒序排序。

  1. 测试

  1. 计算总价

计算总价:

显示总价:

效果:

  1. 修改购买数量

    1. JS

      1. 限制用户输入

解释:http://www.cnblogs.com/PeunZhang/archive/2012/08/16/2642084.html

使用:

  1. 后台实现

    1. Controller

  1. Service

  1. 格式化价格

格式化压缩的js:

  1. 删除购物车中商品

    1. Controller

  1. Service

  1. 分页插件

  1. 未登录状态下的购物车

    1. 以什么样数据格式保存购物车数据到cookie?

可以使用json数据格式。

[

{

itemId:1001

itemTitle:"小米手机手机中的战斗机,欧耶"

……

}

]

  1. Service实现

package com.taotao.cart.service;

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.Date;

import java.util.List;

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.stereotype.Service;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.taotao.cart.bean.Item;

import com.taotao.cart.pojo.Cart;

import com.taotao.common.utils.CookieUtils;

@Service

public
class CartCookieService {

private
static
final String COOKIE_NAME = "TT_CART";

private
static
final Integer COOKIE_TIME = 60 * 60 * 24 * 30 * 12;

private
static
final ObjectMapper MAPPER = new ObjectMapper();

@Autowired

private ItemService itemService;

public
void addItemToCart(Long itemId, HttpServletRequest request, HttpServletResponse response) {

// 判断该商品在购物车中是否存在,如果存在数量相加,如果不存在,直接添加

List<Cart> carts = this.queryCartList(request);

// 判断该商品是否存在

Cart cart = null;

for (Cart c : carts) {

if (c.getItemId().longValue() == itemId.longValue()) {

cart = c;

break;

}

}

if (null == cart) {

// 不存在

cart = new Cart();

cart.setItemId(itemId);

cart.setCreated(new Date());

cart.setUpdated(cart.getCreated());

cart.setNum(1); // TODO未完成

Item item = this.itemService.queryItemById(itemId);

String[] images = StringUtils.split(item.getImage(), ',');

if (null != images && images.length > 0) {

cart.setItemImage(images[0]);

}

cart.setItemPrice(item.getPrice());

cart.setItemTitle(item.getTitle());

carts.add(cart);

} else {

// 存在

cart.setNum(cart.getNum() + 1); // TODO未完成

cart.setUpdated(new Date());

}

try {

// 将购物车数据写入到cookie中

CookieUtils.setCookie(request, response, COOKIE_NAME, MAPPER.writeValueAsString(carts),

COOKIE_TIME, true);

} catch (Exception e) {

e.printStackTrace();

}

}

public List<Cart> queryCartList(HttpServletRequest request) {

String jsonData = CookieUtils.getCookieValue(request, COOKIE_NAME, true);

List<Cart> carts = null;

if (StringUtils.isNotEmpty(jsonData)) {

// 反序列化为List集合对象

try {

carts = MAPPER.readValue(jsonData,

MAPPER.getTypeFactory().constructCollectionType(List.class, Cart.class));

} catch (Exception e) {

e.printStackTrace();

carts = new ArrayList<Cart>();

}

} else {

carts = new ArrayList<Cart>();

}

// 按照创建时间倒序排序

Collections.sort(carts, new Comparator<Cart>() {

@Override

public
int compare(Cart o1, Cart o2) {

return (int) (o2.getCreated().getTime() - o1.getCreated().getTime());

}

});

return
carts;

}

public
void updateNum(Long itemId, Integer num, HttpServletRequest request, HttpServletResponse response) {

List<Cart> carts = this.queryCartList(request);

// 判断该商品是否存在

Cart cart = null;

for (Cart c : carts) {

if (c.getItemId().longValue() == itemId.longValue()) {

cart = c;

break;

}

}

if (cart == null) {

return;

}

cart.setNum(num);

cart.setUpdated(new Date());

try {

// 将购物车数据写入到cookie中

CookieUtils.setCookie(request, response, COOKIE_NAME, MAPPER.writeValueAsString(carts),

COOKIE_TIME, true);

} catch (Exception e) {

e.printStackTrace();

}

}

public
void delete(Long itemId, HttpServletRequest request, HttpServletResponse response) {

List<Cart> carts = this.queryCartList(request);

// 判断该商品是否存在

for (Cart c : carts) {

if (c.getItemId().longValue() == itemId.longValue()) {

carts.remove(c);

break;

}

}

try {

// 将购物车数据写入到cookie中

CookieUtils.setCookie(request, response, COOKIE_NAME, MAPPER.writeValueAsString(carts),

COOKIE_TIME, true);

} catch (Exception e) {

e.printStackTrace();

}

}

}

  1. 基于购物车实现下单功能

    1. 分析

  1. 购物车页面中跳转到前端的订单确认页

  1. 前端系统的中实现

通过购物车系统提供的接口查询数据:

购物车系统中开发接口,根据用户id查询购物车列表:

订单确认页:

关键点:form表单

下单成功:

数据:

  1. 购物车的优化改进

    1. 未登录状态的数据存储的优化

      1. 现在有什么问题?

现在,将未登录状态下的购物车数据保存在cookie中,存在问题,因为,浏览器对cookie的大小是有限制的,意味着,我们不可能一直向cookie中存数据,限制了用户的使用。

  1. 如何改造、优化?

未登录状态下的购物车数据已经不能保存在客户端(cookie),只能保存在服务端:

  1. 数据库
    1. 数据库的读写压力比较大
    2. 容易产生垃圾数据,需要定期去清理它
  2. 文件中
    1. 还不如保存在数据库
    2. IO(读写)问题
  3. Redis
    1. 可行的
    2. 内存存储,读写速度快
    3. 有生存时间,可以自动清理垃圾数据
    4. 但是,要考虑到一个问题,容量问题?
      1. Redis的集群
      2. 一种:分片式集群
      3. 一种:Redis3.0的新功能,集群
  4. 其他
    1. 。。。。
    1. 使用Redis的什么数据结构?

方案一:

将原有的cookie中字符串数据,按照字符串的数据结构保存到Redis中。

该方案存在的问题:

对于每一个Cart对象的操作,都需要经历,字符串 è List<Cart> è 字符串,假如,购物车中1000个商品,问题就更为突出了。

方案二:

使用Redis的hash结构。

结构: key field value

实现:

  1. key : 为每一个人都生成一个唯一的key,可以使用md5
  2. field :商品id
  3. value: Cart对象序列化之后的json字符串

显然,我们采用方案二。

  1. 扩展RedisService

/**

* 保存数据到hash结构中(指定生存时间)

*

* @param key

* @param field

* @param value

* @param seconds

* @return

*/

public Long hset(final String key, final String field, final String value, final Integer seconds) {

return
this.execute(new Function<Long, ShardedJedis>() {

@Override

public Long callback(ShardedJedis e) {

Long _long = e.hset(key, field, value);

e.expire(key, seconds);

return
_long;

}

});

}

/**

* 保存数据到hash结构中

*

* @param key

* @param field

* @param value

* @return

*/

public Long hset(final String key, final String field, final String value) {

return
this.execute(new Function<Long, ShardedJedis>() {

@Override

public Long callback(ShardedJedis e) {

return
e.hset(key, field, value);

}

});

}

/**

* 获取hash中的数据

*

* @param key

* @param
field

* @return

*/

public String hget(final String key, final String field) {

return
this.execute(new Function<String, ShardedJedis>() {

@Override

public String callback(ShardedJedis e) {

return
e.hget(key, field);

}

});

}

/**

* 获取hash结构中所有的数据

*

* @param key

* @return

*/

public Map<String, String> hgetAll(final String key) {

return
this.execute(new Function<Map<String, String>, ShardedJedis>() {

@Override

public Map<String, String> callback(ShardedJedis e) {

return
e.hgetAll(key);

}

});

}

/**

* 删除hash中的数据

*

* @param key

* @param field

* @return

*/

public Long hdel(final String key, final String field) {

return
this.execute(new Function<Long, ShardedJedis>() {

@Override

public Long callback(ShardedJedis e) {

return
e.hdel(key, field);

}

});

}

  1. 实现RedisService

import java.util.ArrayList;

import java.util.Collections;

import java.util.Comparator;

import java.util.Date;

import java.util.List;

import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;

import org.apache.commons.lang3.RandomUtils;

import org.apache.commons.lang3.StringUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import com.fasterxml.jackson.databind.ObjectMapper;

import com.taotao.cart.bean.Item;

import com.taotao.cart.pojo.Cart;

import com.taotao.common.service.RedisService;

import com.taotao.common.utils.CookieUtils;

@Service

public
class CartRedisService {

private
static
final String COOKIE_NAME = "TT_CART";

private
static
final Integer TIME = 60 * 60 * 24 * 30;

@Autowired

private ItemService itemService;

@Autowired

private RedisService redisService;

private
static
final ObjectMapper MAPPER = new ObjectMapper();

public
void addItemToCart(Long itemId, HttpServletRequest request, HttpServletResponse response) {

String key = CookieUtils.getCookieValue(request, COOKIE_NAME);

if (StringUtils.isEmpty(key)) {

// 生存一个key

key = DigestUtils.md5Hex("" + System.currentTimeMillis() + itemId

+ RandomUtils.nextInt(1000, 9999));

// 写入到cookie中

CookieUtils.setCookie(request, response, COOKIE_NAME, key, TIME);

}

// 判断该商品在购物车中是否存在,如果存在数量相加,如果不存在,直接添加

String redisKey = "CART_" + key;

String cartStr = this.redisService.hget(redisKey, String.valueOf(itemId));

Cart cart = null;

if (StringUtils.isNotEmpty(cartStr)) {

try {

cart = MAPPER.readValue(cartStr, Cart.class);

} catch (Exception e) {

e.printStackTrace();

}

}

if (null == cart) {

// 不存在

cart = new Cart();

cart.setItemId(itemId);

cart.setCreated(new Date());

cart.setUpdated(cart.getCreated());

cart.setNum(1); // TODO未完成

Item item = this.itemService.queryItemById(itemId);

String[] images = StringUtils.split(item.getImage(), ',');

if (null != images && images.length > 0) {

cart.setItemImage(images[0]);

}

cart.setItemPrice(item.getPrice());

cart.setItemTitle(item.getTitle());

} else {

// 存在

cart.setNum(cart.getNum() + 1); // TODO未完成

cart.setUpdated(new Date());

}

try {

this.redisService.hset(redisKey, String.valueOf(itemId), MAPPER.writeValueAsString(cart), TIME);

} catch (Exception e) {

e.printStackTrace();

}

}

public List<Cart> queryCartList(HttpServletRequest request, HttpServletResponse response) {

String key = CookieUtils.getCookieValue(request, COOKIE_NAME);

String redisKey = "CART_" + key;

Map<String, String> map = this.redisService.hgetAll(redisKey);

if (null == map || map.isEmpty()) {

return
new ArrayList<Cart>();

}

List<Cart> carts = new ArrayList<Cart>();

for (String k : map.keySet()) {

String json = map.get(k);

Cart cart;

try {

cart = MAPPER.readValue(json, Cart.class);

carts.add(cart);

} catch (Exception e) {

// 如果有异常,就忽略该数据

e.printStackTrace();

}

}

// 按照创建时间倒序排序

Collections.sort(carts, new Comparator<Cart>() {

@Override

public
int compare(Cart o1, Cart o2) {

return (int) (o2.getCreated().getTime() - o1.getCreated().getTime());

}

});

// 刷新数据的生存时间

this.redisService.expire(redisKey, TIME);

CookieUtils.setCookie(request, response, COOKIE_NAME, key, TIME);

return
carts;

}

public
void updateNum(Long itemId, Integer num, HttpServletRequest request) {

String key = CookieUtils.getCookieValue(request, COOKIE_NAME);

String redisKey = "CART_" + key;

String cartStr = this.redisService.hget(redisKey, String.valueOf(itemId));

if (StringUtils.isEmpty(cartStr)) {

return;

}

Cart cart = null;

if (StringUtils.isNotEmpty(cartStr)) {

try {

cart = MAPPER.readValue(cartStr, Cart.class);

} catch (Exception e) {

e.printStackTrace();

}

}

if (null != cart) {

cart.setNum(num);

cart.setUpdated(new Date());

try {

this.redisService.hset(redisKey, String.valueOf(itemId), MAPPER.writeValueAsString(cart),

TIME);

} catch (Exception e) {

e.printStackTrace();

}

}

}

public
void delete(Long itemId, HttpServletRequest request) {

String key = CookieUtils.getCookieValue(request, COOKIE_NAME);

String redisKey = "CART_" + key;

this.redisService.hdel(redisKey, String.valueOf(itemId));

}

}

  1. 登录成功后,合并未登录和登录状态下的数据

    1. 合并的动作在哪个系统中完成?

  1. 实现分析

  1. 用户在登录成功时,需要发送消息到交换机
    1. 发送消息时,携带cookie中的购物车key的值
  2. 购物车系统创建队列,绑定到该交换机
  3. 购物车系统接收到消息之后,读取未登录状态下的购物车数据,和数据库中的数据进行合并,清空未登录状态下的数据
    1. Sso系统中发送登录成功的消息

  4. 导入依赖
  5. RabbitMQ的配置文件
  6. 在UserService中,登录成功后发送消息
    1. 购物车系统中接收消息

  7. 导入依赖
  8. 配置文件
  9. 处理消息

@Service

public
class
CartRedisService {

private
static
final String COOKIE_NAME = "TT_CART";

private
static
final Integer TIME = 60 * 60 * 24 * 30;

@Autowired

private ItemService itemService;

@Autowired

private RedisService redisService;

private
static
final ObjectMapper MAPPER = new ObjectMapper();

public
void addItemToCart(Long itemId, HttpServletRequest request, HttpServletResponse response) {

String key = CookieUtils.getCookieValue(request, COOKIE_NAME);

if (StringUtils.isEmpty(key)) {

// 生存一个key

key = DigestUtils.md5Hex("" + System.currentTimeMillis() + itemId

+ RandomUtils.nextInt(1000, 9999));

// 写入到cookie中

CookieUtils.setCookie(request, response, COOKIE_NAME, key, TIME);

}

// 判断该商品在购物车中是否存在,如果存在数量相加,如果不存在,直接添加

String redisKey = getRedisKey(key);

String cartStr = this.redisService.hget(redisKey, String.valueOf(itemId));

Cart cart = null;

if (StringUtils.isNotEmpty(cartStr)) {

try {

cart = MAPPER.readValue(cartStr, Cart.class);

} catch (Exception e) {

e.printStackTrace();

}

}

if (null == cart) {

// 不存在

cart = new Cart();

cart.setItemId(itemId);

cart.setCreated(new Date());

cart.setUpdated(cart.getCreated());

cart.setNum(1); // TODO未完成

Item item = this.itemService.queryItemById(itemId);

String[] images = StringUtils.split(item.getImage(), ',');

if (null != images && images.length > 0) {

cart.setItemImage(images[0]);

}

cart.setItemPrice(item.getPrice());

cart.setItemTitle(item.getTitle());

} else {

// 存在

cart.setNum(cart.getNum() + 1); // TODO未完成

cart.setUpdated(new Date());

}

try {

this.redisService.hset(redisKey, String.valueOf(itemId), MAPPER.writeValueAsString(cart), TIME);

} catch (Exception e) {

e.printStackTrace();

}

}

public List<Cart> queryCartList(String cookieCart) {

String redisKey = getRedisKey(cookieCart);

Map<String, String> map = this.redisService.hgetAll(redisKey);

if (null == map || map.isEmpty()) {

return
new ArrayList<Cart>();

}

List<Cart> carts = new ArrayList<Cart>();

for (String k : map.keySet()) {

String json = map.get(k);

Cart cart;

try {

cart = MAPPER.readValue(json, Cart.class);

carts.add(cart);

} catch (Exception e) {

// 如果有异常,就忽略该数据

e.printStackTrace();

}

}

// 刷新数据的生存时间

this.redisService.expire(redisKey, TIME);

return
carts;

}

public List<Cart> queryCartList(HttpServletRequest request, HttpServletResponse response) {

String key = CookieUtils.getCookieValue(request, COOKIE_NAME);

List<Cart> carts = this.queryCartList(key);

// 按照创建时间倒序排序

Collections.sort(carts, new Comparator<Cart>() {

@Override

public
int compare(Cart o1, Cart o2) {

return (int) (o2.getCreated().getTime() - o1.getCreated().getTime());

}

});

CookieUtils.setCookie(request, response, COOKIE_NAME, key, TIME);

return
carts;

}

public
void updateNum(Long itemId, Integer num, HttpServletRequest request) {

String key = CookieUtils.getCookieValue(request, COOKIE_NAME);

String redisKey = getRedisKey(key);

String cartStr = this.redisService.hget(redisKey, String.valueOf(itemId));

if (StringUtils.isEmpty(cartStr)) {

return;

}

Cart cart = null;

if (StringUtils.isNotEmpty(cartStr)) {

try {

cart = MAPPER.readValue(cartStr, Cart.class);

} catch (Exception e) {

e.printStackTrace();

}

}

if (null != cart) {

cart.setNum(num);

cart.setUpdated(new Date());

try {

this.redisService.hset(redisKey, String.valueOf(itemId), MAPPER.writeValueAsString(cart),

TIME);

} catch (Exception e) {

e.printStackTrace();

}

}

}

private String getRedisKey(String cookieKey){

return
"CART_" + cookieKey;

}

public
void delete(Long itemId, HttpServletRequest request) {

String key = CookieUtils.getCookieValue(request, COOKIE_NAME);

String redisKey = getRedisKey(key);

this.redisService.hdel(redisKey, String.valueOf(itemId));

}

public
void clear(String cookieCart) {

String redisKey = getRedisKey(cookieCart);

this.redisService.del(redisKey);

}

}

  1. 下单成功后,删除成功下单的商品

    1. 分析

订单系统中,下单成功后发送消息。

  1. 订单系统发消息
  2. 购物车系统监听消息
  3. 处理消息
    1. 实现

  4. 导入Spring-rabbit依赖
  5. 编写配置文件
  6. 发送消息
  7. 处理消息(taotao-cart)

    1. 读写分离

《使用Spring实现读写分离(MySQL实现主从复制).docx》

  1. 主从设置失败

如何定位问题?

查看3381的日志:

问题:

如何设置Mysql的UUID?

问题解决:

最新文章

  1. Bootstrap3 Grid system原理及应用
  2. Mysql 第一天
  3. 开源GIS软件初探
  4. 最近在 OS-10.9下配置opencv, cgal, latex, qt, pillow
  5. SQL Server排序规则
  6. 怎样实现excel隔行隔列变色效果的方法
  7. [Android Tips] 5. INSTALL_PARSE_FAILED_MANIFEST_MALFORMED on Android-2.1
  8. android: open failed: EACCES (Permission denied)
  9. VIM配置相关记录
  10. javascript的三个组成部分
  11. C语言 可变参数
  12. Android 带checkbox的listView 实现多选,全选,反选,删除
  13. android 图片画画板
  14. 核心运营报表无线端数据,pv,uv相关数据,从9月1号开始就没了,为什么?
  15. LINUX专题之操作系统字符集
  16. 汇编指令-str存储指令(4)
  17. ORA-01578和ORA-26040--NOLOGGING操作引起的坏块-错误解释和解决方案(文档ID 1623284.1)
  18. python基础—迭代器、生成器
  19. mysql把一个查询结果当作一个子集来查询
  20. Django 信号、中间件、i18n 专题

热门文章

  1. 获取Exception的详细信息
  2. 【微信开发】微信公众平台接入及绑定提示“请求URL超时”的解决办法
  3. ViewCompat.animate(view) floatEval.evaluate() argbEval.evaluate()
  4. ListView控制消息
  5. 【01背包】HDU 2602 Bone Collector (模板题)
  6. find指令
  7. springMVC实现文件上传下载
  8. Bucket Sort - leetcode [桶排序]
  9. centos 6.5 安装mysql 5.6错误
  10. 面试题-Java基础-布局管理器