【电商项目】---购物车
程序员文章站
2024-03-25 21:53:28
...
前言
电商系统购物车
购物车存储形式-Cookie
- 无须登录、无须查库、保存在浏览器端
- 优点: 性能好、访问快、没有和数据库交互
- 缺点:
换电脑购物车数据会丢失
电脑被其他人登录,隐私安全
购物车存储形式-Session
- 用户登录后,购物车数据放入用户会话
- 优点: session是基于服务器内存的,初期性能较好,访问快
- 缺点:
session基于内存,用户量庞大影响服务器性能
只能存在与当前会话,不使用集群与分布式系统
购物车存储形式-数据库
用户登录后,购物车数据存入数据库
- 优点:
数据持久化,可在任何地点任何时间访问 - 缺点:
频繁读写数据库,造成数据库压力
购物车存储形式-Redis
用户登录后,购物车数据存入Redis缓存
- 优点:
数据持久化,可在任何地点任何时间访问
频繁读写只基于缓存,不会造成数据库压力
适用于集群与分布式系统,可扩展性强
未登录与已登录购物车业务代码
@Api(value = "购物车接口controller", tags = {"购物车接口相关的api"})
@RequestMapping("shopcart")
@RestController
public class ShopcatController {
@ApiOperation(value = "添加商品到购物车", notes = "添加商品到购物车", httpMethod = "POST")
@PostMapping("/add")
public ZCWJSONResult add(
@RequestParam String userId,
@RequestBody ShopcartBO shopcartBO,
HttpServletRequest request,
HttpServletResponse response
) {
if (StringUtils.isBlank(userId)) {
return ZCWJSONResult.errorMsg("");
}
System.out.println(shopcartBO);
// TODO 前端用户在登录的情况下,添加商品到购物车,会同时在后端同步购物车到redis缓存
return ZCWJSONResult.ok();
}
@ApiOperation(value = "从购物车中删除商品", notes = "从购物车中删除商品", httpMethod = "POST")
@PostMapping("/del")
public ZCWJSONResult del(
@RequestParam String userId,
@RequestParam String itemSpecId,
HttpServletRequest request,
HttpServletResponse response
) {
if (StringUtils.isBlank(userId) || StringUtils.isBlank(itemSpecId)) {
return ZCWJSONResult.errorMsg("参数不能为空");
}
// TODO 用户在页面删除购物车中的商品数据,如果此时用户已经登录,则需要同步删除后端购物车中的商品
return ZCWJSONResult.ok();
}
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
/**
*
* @Title: CookieUtils.java
* @Package com.zcw.utils
* @Description: Cookie 工具类
* Copyright: Copyright (c)
* Company: https://zhaocunwei.blog.csdn.net/
*
* @author zhaocunwei
* @version V1.0
*/
public final class CookieUtils {
final static Logger logger = LoggerFactory.getLogger(CookieUtils.class);
/**
*
* @Description: 得到Cookie的值, 不编码
* @param request
* @param cookieName
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName) {
return getCookieValue(request, cookieName, false);
}
/**
*
* @Description: 得到Cookie的值
* @param request
* @param cookieName
* @param isDecoder
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, boolean isDecoder) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
if (isDecoder) {
retValue = URLDecoder.decode(cookieList[i].getValue(), "UTF-8");
} else {
retValue = cookieList[i].getValue();
}
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
*
* @Description: 得到Cookie的值
* @param request
* @param cookieName
* @param encodeString
* @return
*/
public static String getCookieValue(HttpServletRequest request, String cookieName, String encodeString) {
Cookie[] cookieList = request.getCookies();
if (cookieList == null || cookieName == null) {
return null;
}
String retValue = null;
try {
for (int i = 0; i < cookieList.length; i++) {
if (cookieList[i].getName().equals(cookieName)) {
retValue = URLDecoder.decode(cookieList[i].getValue(), encodeString);
break;
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return retValue;
}
/**
*
* @Description: 设置Cookie的值 不设置生效时间默认浏览器关闭即失效,也不编码
* @param request
* @param response
* @param cookieName
* @param cookieValue
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue) {
setCookie(request, response, cookieName, cookieValue, -1);
}
/**
*
* @Description: 设置Cookie的值 在指定时间内生效,但不编码
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage) {
setCookie(request, response, cookieName, cookieValue, cookieMaxage, false);
}
/**
*
* @Description: 设置Cookie的值 不设置生效时间,但编码
* 在服务器被创建,返回给客户端,并且保存客户端
* 如果设置了SETMAXAGE(int seconds),会把cookie保存在客户端的硬盘中
* 如果没有设置,会默认把cookie保存在浏览器的内存中
* 一旦设置setPath():只能通过设置的路径才能获取到当前的cookie信息
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param isEncode
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, boolean isEncode) {
setCookie(request, response, cookieName, cookieValue, -1, isEncode);
}
/**
*
* @Description: 设置Cookie的值 在指定时间内生效, 编码参数
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
* @param isEncode
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, boolean isEncode) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, isEncode);
}
/**
*
* @Description: 设置Cookie的值 在指定时间内生效, 编码参数(指定编码)
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage
* @param encodeString
*/
public static void setCookie(HttpServletRequest request, HttpServletResponse response, String cookieName,
String cookieValue, int cookieMaxage, String encodeString) {
doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
}
/**
*
* @Description: 删除Cookie带cookie域名
* @param request
* @param response
* @param cookieName
*/
public static void deleteCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName) {
doSetCookie(request, response, cookieName, null, -1, false);
// doSetCookie(request, response, cookieName, "", -1, false);
}
/**
*
* @Description: 设置Cookie的值,并使其在指定时间内生效
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage cookie生效的最大秒数
* @param isEncode
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName, String cookieValue, int cookieMaxage, boolean isEncode) {
try {
if (cookieValue == null) {
cookieValue = "";
} else if (isEncode) {
cookieValue = URLEncoder.encode(cookieValue, "utf-8");
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
logger.info("========== domainName: {} ==========", domainName);
if (!"localhost".equals(domainName)) {
cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* @Description: 设置Cookie的值,并使其在指定时间内生效
* @param request
* @param response
* @param cookieName
* @param cookieValue
* @param cookieMaxage cookie生效的最大秒数
* @param encodeString
*/
private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response,
String cookieName, String cookieValue, int cookieMaxage, String encodeString) {
try {
if (cookieValue == null) {
cookieValue = "";
} else {
cookieValue = URLEncoder.encode(cookieValue, encodeString);
}
Cookie cookie = new Cookie(cookieName, cookieValue);
if (cookieMaxage > 0)
cookie.setMaxAge(cookieMaxage);
if (null != request) {// 设置域名的cookie
String domainName = getDomainName(request);
logger.info("========== domainName: {} ==========", domainName);
if (!"localhost".equals(domainName)) {
cookie.setDomain(domainName);
}
}
cookie.setPath("/");
response.addCookie(cookie);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* @Description: 得到cookie的域名
* @return
*/
private static final String getDomainName(HttpServletRequest request) {
String domainName = null;
String serverName = request.getRequestURL().toString();
if (serverName == null || serverName.equals("")) {
domainName = "";
} else {
serverName = serverName.toLowerCase();
serverName = serverName.substring(7);
final int end = serverName.indexOf("/");
serverName = serverName.substring(0, end);
if (serverName.indexOf(":") > 0) {
String[] ary = serverName.split("\\:");
serverName = ary[0];
}
final String[] domains = serverName.split("\\.");
int len = domains.length;
if (len > 3 && !isIp(serverName)) {
// www.xxx.com.cn
domainName = "." + domains[len - 3] + "." + domains[len - 2] + "." + domains[len - 1];
} else if (len <= 3 && len > 1) {
// xxx.com or xxx.cn
domainName = "." + domains[len - 2] + "." + domains[len - 1];
} else {
domainName = serverName;
}
}
return domainName;
}
public static String trimSpaces(String IP){//去掉IP字符串前后所有的空格
while(IP.startsWith(" ")){
IP= IP.substring(1,IP.length()).trim();
}
while(IP.endsWith(" ")){
IP= IP.substring(0,IP.length()-1).trim();
}
return IP;
}
public static boolean isIp(String IP){//判断是否是一个IP
boolean b = false;
IP = trimSpaces(IP);
if(IP.matches("\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}")){
String s[] = IP.split("\\.");
if(Integer.parseInt(s[0])<255)
if(Integer.parseInt(s[1])<255)
if(Integer.parseInt(s[2])<255)
if(Integer.parseInt(s[3])<255)
b = true;
}
return b;
}
}
渲染(刷新)购物车
- 号只是演示,在实际开发中,不要使用* ,需要什么字段,就查询哪些字段
SELECT
*
FROM
items_spec t_items_spec
LEFT JOIN items t_items ON t_items.id = t_items_spec.item_id
LEFT JOIN items_img t_items_img ON t_items_img.item_id = t_items.id
WHERE
t_items_img.is_main = 1
AND t_items_spec.id IN (
'1',
'3',
'5')
- 复杂SQL编写
<select id="searchItems" parameterType="Map" resultType="com.zcw.pojo.vo.SearchItemsVO">
SELECT
i.id as itemId,
i.item_name as itemName,
i.sell_counts as sellCounts,
ii.url as imgUrl,
tempSpec.price_discount as price
FROM
items i
LEFT JOIN
items_img ii
on
i.id = ii.item_id
LEFT JOIN
(SELECT item_id,MIN(price_discount) as price_discount from items_spec GROUP BY item_id) tempSpec
on
i.id = tempSpec.item_id
WHERE
ii.is_main = 1
<if test=" paramsMap.keywords != null and paramsMap.keywords != '' ">
AND i.item_name like '%${paramsMap.keywords}%'
</if>
order by
<choose>
<when test=" paramsMap.sort == "c" ">
i.sell_counts desc
</when>
<when test=" paramsMap.sort == "p" ">
tempSpec.price_discount asc
</when>
<otherwise>
i.item_name asc
</otherwise>
</choose>
</select>
<!-- k: 默认,代表默认排序,根据name-->
<!-- c: 根据销量排序-->
<!-- p: 根据价格排序-->
<select id="searchItemsByThirdCat" parameterType="Map" resultType="com.zcw.pojo.vo.SearchItemsVO">
SELECT
i.id as itemId,
i.item_name as itemName,
i.sell_counts as sellCounts,
ii.url as imgUrl,
tempSpec.price_discount as price
FROM
items i
LEFT JOIN
items_img ii
on
i.id = ii.item_id
LEFT JOIN
(SELECT item_id,MIN(price_discount) as price_discount from items_spec GROUP BY item_id) tempSpec
on
i.id = tempSpec.item_id
WHERE
ii.is_main = 1
and
i.cat_id = #{paramsMap.catId}
order by
<choose>
<when test=" paramsMap.sort == "c" ">
i.sell_counts desc
</when>
<when test=" paramsMap.sort == "p" ">
tempSpec.price_discount asc
</when>
<otherwise>
i.item_name asc
</otherwise>
</choose>
</select>
<select id="queryItemsBySpecIds" parameterType="List" resultType="com.zcw.pojo.vo.ShopcartVO">
SELECT
t_items.id as itemId,
t_items.item_name as itemName,
t_items_img.url as itemImgUrl,
t_items_spec.id as specId,
t_items_spec.`name` as specName,
t_items_spec.price_discount as priceDiscount,
t_items_spec.price_normal as priceNormal
FROM
items_spec t_items_spec
LEFT JOIN
items t_items
ON
t_items.id = t_items_spec.item_id
LEFT JOIN
items_img t_items_img
on
t_items_img.item_id = t_items.id
WHERE
t_items_img.is_main = 1
AND
t_items_spec.id IN
<foreach collection="paramsList" index="index" item="specId" open="(" separator="," close=")">
#{specId}
</foreach>
</select>
- 数组直接转换集合
String ids[] = specIds.split(",");
List<String> specIdsList = new ArrayList<>();
Collections.addAll(specIdsList, ids);
上一篇: Python实现卡尔曼滤波器