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?

问题解决:

淘淘商城_day09_课堂笔记的更多相关文章

  1. 淘淘商城_day11_课堂笔记

    今日大纲 发布前的准备 实施发布 一部分是由我来发布 一部分是由你们来发布 讲解分布式部署架构 测试 功能测试 压力测试 项目实战的准备以及分组 分组 抽取功能 讲解所需要开发的功能 项目部署上线流程 ...

  2. 淘淘商城_day01_课堂笔记

    今日大纲 聊聊电商行业 电商行业发展 11.11 2015双11: 2016年: 预测:2017年的双11交易额将达到:1400亿 电商行业技术特点 淘淘商城简介 淘淘商城的前身 电商行业的概念 B2 ...

  3. 淘淘商城_day04_课堂笔记

    今日大纲 实现首页的大广告位功能 实现内容管理系统 首页的大广告 什么是大广告 JS效果: 点击下面的序号选择查询哪个广告 自动切换 点击图片查询具体的页面 以上是由前端团队来开发. 数据结构 说明: ...

  4. 淘淘商城_day02_课堂笔记

    今日大纲 学习Nginx的使用 实现商品的管理 新增商品 查询商品列表 编辑商品 删除商品 上架和下架商品 学习nginx 开发阶段中的环境 开发环境:自己的电脑 测试环境:提供给测试人员使用的环境 ...

  5. 淘淘商城_day10_课堂笔记

    今日大纲 Dubbo入门学习 使用dubbo优化单点登录系统 系统间服务调用方式 浏览器直接访问 浏览器发起请求,通过ajax或jsonp方式请求: Httpclient方式 系统与系统之间通过Htt ...

  6. 淘淘商城_day08_课堂笔记

    今日大纲 问题,如何实现商品数据的同步? 学习MQ(消息队列) 搭建RabbitMQ的环境 学习RabbitMQ的队列 学习Spring-Rabbit 使用RabbitMQ完成商品数据的同步 如何实现 ...

  7. 淘淘商城_day07_课堂笔记

    今日大纲 讲解订单系统 基于订单系统完成下单功能的开发 使用Solr完成商品的搜索功能 订单系统 说明:订单系统只是做讲解,不做开发. 导入taotao-order 表结构 订单表: 订单商品表: 疑 ...

  8. 淘淘商城_day05_课堂笔记

    今日大纲 学习Redis 使用Redis完成项目中缓存需求 实现商品详情页功能 缓存的需求 大广告位数据无需每次查询后台系统的接口,可以在前台系统添加缓存,提高访问首页的速度. 商品类目的数据也可以缓 ...

  9. 淘淘商城_day06_课堂笔记

    今日大纲 实现单点登录系统 基于单点登录系统实现,用户的注册和登录 商品数据同步问题 问题 后台系统中将商品修改,前台系统没有进行数据的同步,导致前端系统不能够实时显示最新的数据. 解决 后台系统中商 ...

随机推荐

  1. charles支持https抓包

    前言 最近发现访问项目的网页偶尔会被插入广告,很有可能是运营商劫持流量插入进去的,我在家里使用的长城宽带打开非加密的网页,时不时会弹个广告窗,这个也算是中国特色了.因此计划项目上线https,抓包分析 ...

  2. (三)Harbor使用OpenLDAP认证登陆

    接上一篇<安装Harbor>,安装好之后,接下来我们使用OpenLDAP来进行Harbor  web界面的登陆验证及权限分配! OpenLDAP: 使用OpenLDAP的都知道,这是一个集 ...

  3. jquery新版本不支持toggle()的解决方法

    toggle() 方法用于绑定两个或多个事件处理器函数,以响应被选元素的轮流的 click 事件. 在1.9以后官方废除了这个方法: 解决如下 在需要调用的js文件下引用 $.fn.toggle = ...

  4. 用ES6语法和方式写gulp

    安装依赖模块 npm i -g gulp npm i gulp babel-core babel-preset-es2015 --save-dev 在创建文件 .babelrc(文件名) : (文件内 ...

  5. fpga之显示字符串

    //必须在有效区域下显示颜色才有颜色 显示字符可以在设定一个有效区域内显示 另加两个wire 求出新的x,ymodule vga_fpga( clk,rst_n, vga_b,vga_g,vga_r, ...

  6. DevExpress的SpinEdit控件无法输入数字的问题

    今天在发布程序后突然发现了这个问题,刚开始很莫名其妙的,因为在调试时从来没碰到过.然后经过测试发现,这个问题的原因和输入法有很大关系: 当你的输入法是中文状态时,是无法向框中输入数字的,此时只能点击上 ...

  7. openstack私有云布署实践【18 修改实例DHCP服务的DNS IP】

    某天,由于Linux服务器默认没有DNS缓存功能,每次服务器每访问一个http域名链接时,都会触发一次DNS域名解析查询,降低了调用API接口的时延,所以我司后续启用的内网的dnsmasq DNS服务 ...

  8. [Q]关于无法进入主界面问题解答

    打图精灵适用于AutoCAD2007或更高版,低于2007版无法使用. 若在安装打图精灵前AutoCAD已经打开,则需要将AutoCAD重新打开,然后使用“QPlot”命令. 若重新打开仍然调不出界面 ...

  9. Zeppelin0.5.6使用hive解释器

    此zeppelin为官方0.5.6版,可能还在孵化阶段,可能出现一些bug吧. 配置 cp zeppelin-env.sh.template zeppelin-env.sh vi zeppelin-e ...

  10. ubuntu搭建git服务器

    1.准备两台ubuntu虚拟机,其中一个作为server,另一个作为client.检查是否安装ssh,如果没有安装:sudo apt-get install openssh-server, 然后在se ...