添加购物车

master
DongZeLiang 2024-04-07 11:41:25 +08:00
parent 8a35299496
commit e4eeac9c41
24 changed files with 330 additions and 32 deletions

View File

@ -94,4 +94,11 @@ public interface HashCache <K, HK, HV> extends DecorationKey<K> {
*/
public void remove(K key, HK hashKey);
/**
* redishashKey
* @param key redis
* @param hashKey hash
*/
public boolean hasKey(K key, HK hashKey);
}

View File

@ -21,7 +21,13 @@ public abstract class AtomicSequenceCacheAbs<K> implements AtomicSequenceCache<K
*/
@Override
public Long get (K key) {
return this.redisService.getCacheObject(encode(key));
Long cacheValue = this.redisService.getCacheObject(encode(key));
if (cacheValue == null){
Long data = getData(key);
cacheValue = data == null ? 0L : data;
this.redisService.setCacheObject(encode(key), cacheValue);
}
return cacheValue;
}
/**

View File

@ -164,6 +164,17 @@ public abstract class HashCacheAbs<K, HK, HV> implements HashCache<K, HK, HV> {
redisService.deleteCacheMapValue(encode(key), encodeHashKey(hashKey));
}
/**
* redishashKey
*
* @param key redis
* @param hashKey hash
*/
@Override
public boolean hasKey (K key, HK hashKey) {
return redisService.hashKey(encode(key), encodeHashKey(hashKey));
}
/**
*
* @param dataMap

View File

@ -20,4 +20,9 @@ public class ServiceNameConstants {
* serviceid
*/
public static final String FILE_SERVICE = "muyu-file";
/**
*
*/
public static final String PRODUCT_SERVICE = "muyu-product";
}

View File

@ -232,6 +232,15 @@ public class RedisService {
public <T> List<T> getMultiCacheMapValue (final String key, final Collection<?> hKeys) {
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* redishashKey
* @param key redis
* @param hashKey hash
*/
public boolean hashKey(final String key, final String hashKey){
return this.redisTemplate.opsForHash().hasKey(key, hashKey);
}
/**
* Hash

View File

@ -0,0 +1,3 @@
com.muyu.product.cache.ProjectInfoCache
com.muyu.product.cache.ProjectSkuCache
com.muyu.product.cache.ProjectSkuStockCache

View File

@ -0,0 +1,33 @@
package com.muyu.product.remote;
import com.muyu.common.core.constant.ServiceNameConstants;
import com.muyu.common.core.domain.Result;
import com.muyu.product.domain.ProjectSkuInfo;
import com.muyu.product.remote.factory.RemoteProjectSkuFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @author DongZl
* @description:
* @Date 2024-4-7 10:58
*/
@FeignClient(
contextId = "remoteProjectSkuService",
value = ServiceNameConstants.PRODUCT_SERVICE,
fallbackFactory = RemoteProjectSkuFactory.class,
path = "/sku"
)
public interface RemoteProjectSkuService {
/**
* IDSKUSKU
* @param projectId ID
* @param projectSku SKU
* @return SKU
*/
@GetMapping("/info/{projectId}/{projectSku}")
public Result<ProjectSkuInfo> getInfoByProjectIdAndSku(@PathVariable("projectId") Long projectId,
@PathVariable("projectSku") String projectSku);
}

View File

@ -0,0 +1,23 @@
package com.muyu.product.remote.factory;
import com.muyu.common.core.domain.Result;
import com.muyu.product.domain.ProjectSkuInfo;
import com.muyu.product.remote.RemoteProjectSkuService;
import org.springframework.cloud.openfeign.FallbackFactory;
/**
* @author DongZl
* @description:
* @Date 2024-4-7 10:59
*/
public class RemoteProjectSkuFactory implements FallbackFactory<RemoteProjectSkuService> {
@Override
public RemoteProjectSkuService create (Throwable cause) {
return new RemoteProjectSkuService() {
@Override
public Result<ProjectSkuInfo> getInfoByProjectIdAndSku (Long projectId, String projectSku) {
return Result.error(cause.getMessage());
}
};
}
}

View File

@ -21,10 +21,7 @@ public class ProjectSkuStockDataImpl implements ProjectSkuStockData {
@Override
public Long getData (SkuStockKey key) {
LambdaQueryWrapper<ProjectSkuInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ProjectSkuInfo::getProjectId, key.getProjectId());
queryWrapper.eq(ProjectSkuInfo::getSku, key.getSku());
ProjectSkuInfo projectSkuInfo = projectSkuInfoService.getOne(queryWrapper);
ProjectSkuInfo projectSkuInfo = projectSkuInfoService.getInfoByProjectIdAndSku(key.getProjectId(), key.getSku());
return projectSkuInfo.getStock();
}
}

View File

@ -75,6 +75,18 @@ public class ProjectSkuInfoController extends BaseController {
return Result.success(projectSkuInfoService.getById(id));
}
/**
* IDSKUSKU
* @param projectId ID
* @param projectSku SKU
* @return SKU
*/
@GetMapping("/info/{projectId}/{projectSku}")
public Result<ProjectSkuInfo> getInfoByProjectIdAndSku(@PathVariable("projectId") Long projectId,
@PathVariable("projectSku") String projectSku){
return Result.success(projectSkuInfoService.getInfoByProjectIdAndSku(projectId, projectSku));
}
/**
* SKU
*/

View File

@ -19,4 +19,11 @@ public interface ProjectSkuInfoService extends IService<ProjectSkuInfo> {
*/
public List<ProjectSkuInfo> list(ProjectSkuInfo projectSkuInfo);
/**
* IDSKUSKU
* @param projectId ID
* @param projectSku SKU
* @return SKU
*/
ProjectSkuInfo getInfoByProjectIdAndSku (Long projectId, String projectSku);
}

View File

@ -58,4 +58,20 @@ public class ProjectSkuInfoServiceImpl extends ServiceImpl<ProjectSkuInfoMapper,
return list(queryWrapper);
}
/**
* IDSKUSKU
*
* @param projectId ID
* @param projectSku SKU
*
* @return SKU
*/
@Override
public ProjectSkuInfo getInfoByProjectIdAndSku (Long projectId, String projectSku) {
LambdaQueryWrapper<ProjectSkuInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(ProjectSkuInfo::getProjectId, projectId);
queryWrapper.eq(ProjectSkuInfo::getSku, projectSku);
return this.getOne(queryWrapper);
}
}

View File

@ -82,9 +82,7 @@ public class CartInfo extends BaseEntity {
return CartInfo.builder()
.projectId(cartInfoSaveReq.getProjectId())
.projectSku(cartInfoSaveReq.getProjectSku())
.userId(cartInfoSaveReq.getUserId())
.num(cartInfoSaveReq.getNum())
.isSelected(cartInfoSaveReq.getIsSelected())
.build();
}

View File

@ -23,34 +23,17 @@ public class CartInfoSaveReq extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 主键 */
@ApiModelProperty(name = "主键", value = "主键")
private Long id;
/** 商品 */
@ApiModelProperty(name = "商品", value = "商品", required = true)
private Long projectId;
/** SKU */
@ApiModelProperty(name = "SKU", value = "SKU", required = true)
private String projectSku;
/** 用户 */
@ApiModelProperty(name = "用户", value = "用户", required = true)
private Long userId;
/** 数量 */
@ApiModelProperty(name = "数量", value = "数量", required = true)
private Long num;
/** 是否选中 */
@ApiModelProperty(name = "是否选中", value = "是否选中", required = true)
private String isSelected;
}

View File

@ -92,6 +92,18 @@
<artifactId>muyu-shop-cart-cache</artifactId>
</dependency>
<!-- 商品模块 缓存 依赖 -->
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-product-cache</artifactId>
</dependency>
<!-- 商品模块 远程调用 依赖 -->
<dependency>
<groupId>com.muyu</groupId>
<artifactId>muyu-product-remote</artifactId>
</dependency>
</dependencies>
<build>

View File

@ -0,0 +1,25 @@
package com.muyu.shop.cart.cache.impl;
import com.muyu.product.cache.datasource.ProjectInfoData;
import com.muyu.product.domain.ProjectInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author DongZl
* @description:
* @Date 2024-3-27 03:37
*/
@Service
public class ProjectInfoDataImpl implements ProjectInfoData {
/**
*
* @param key ID
* @return
*/
@Override
public ProjectInfo getData (Long key) {
return new ProjectInfo();
}
}

View File

@ -0,0 +1,43 @@
package com.muyu.shop.cart.cache.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.muyu.product.cache.datasource.ProjectSkuData;
import com.muyu.product.domain.ProjectSkuInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author DongZl
* @description: SKU
* @Date 2024-4-1 11:38
*/
@Service
public class ProjectSkuDataImpl implements ProjectSkuData {
/**
* hash
* @param projectId ID
* @return
*/
@Override
public Map<String, ProjectSkuInfo> getData (Long projectId) {
return new HashMap<>();
}
/**
* hashhash
* @param projectId ID
* @param projectSku SKU
* @return hash
*/
@Override
public ProjectSkuInfo getData (Long projectId, String projectSku) {
return new ProjectSkuInfo();
}
}

View File

@ -0,0 +1,36 @@
package com.muyu.shop.cart.cache.impl;
import com.muyu.common.core.domain.Result;
import com.muyu.product.cache.datasource.ProjectSkuStockData;
import com.muyu.product.cache.key.SkuStockKey;
import com.muyu.product.domain.ProjectSkuInfo;
import com.muyu.product.remote.RemoteProjectSkuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
/**
* @author DongZl
* @description: sku
* @Date 2024-4-2 10:53
*/
@Service
public class ProjectSkuStockDataRemoteImpl implements ProjectSkuStockData {
@Autowired
private RemoteProjectSkuService remoteProjectSkuService;
@Override
public Long getData (SkuStockKey key) {
Result<ProjectSkuInfo> result
= remoteProjectSkuService.getInfoByProjectIdAndSku(key.getProjectId(), key.getSku());
if (Result.isSuccess(result)){
ProjectSkuInfo projectSkuInfo = result.getData();
if (!Objects.isNull(projectSkuInfo)){
return projectSkuInfo.getStock();
}
}
return 0L;
}
}

View File

@ -83,7 +83,7 @@ public class CartInfoController extends BaseController {
@PostMapping
@ApiOperation("新增购物车")
public Result<String> add(@RequestBody CartInfoSaveReq cartInfoSaveReq) {
return toAjax(cartInfoService.save(CartInfo.saveBuild(cartInfoSaveReq)));
return toAjax(cartInfoService.add(CartInfo.saveBuild(cartInfoSaveReq)));
}
/**

View File

@ -19,4 +19,10 @@ public interface CartInfoService extends IService<CartInfo> {
*/
public List<CartInfo> list(CartInfo cartInfo);
/**
*
* @param cartInfo
* @return
*/
boolean add (CartInfo cartInfo);
}

View File

@ -1,9 +1,19 @@
package com.muyu.shop.cart.service.impl;
import java.util.Date;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.muyu.common.core.exception.ServiceException;
import com.muyu.common.core.text.Convert;
import com.muyu.common.core.utils.ObjUtils;
import com.muyu.common.security.utils.SecurityUtils;
import com.muyu.product.cache.ProjectSkuStockCache;
import com.muyu.product.cache.key.SkuStockKey;
import com.muyu.shop.cart.cache.CartCache;
import com.muyu.shop.cart.cache.key.CartHashKey;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.muyu.shop.cart.mapper.CartInfoMapper;
import com.muyu.shop.cart.domain.CartInfo;
@ -21,6 +31,12 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@Service
public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo> implements CartInfoService {
@Autowired
private CartCache cartCache;
@Autowired
private ProjectSkuStockCache projectSkuStockCache;
/**
*
*
@ -52,10 +68,53 @@ public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo>
queryWrapper.eq(CartInfo::getIsSelected, cartInfo.getIsSelected());
}
return list(queryWrapper);
}
/**
*
*
* @param cartInfo
*
* @return
*/
@Override
public boolean add (CartInfo cartInfo) {
Long userId = SecurityUtils.getUserId();
CartHashKey cartHashKey = CartHashKey.builder()
.projectId(cartInfo.getProjectId())
.projectSku(cartInfo.getProjectSku())
.build();
SkuStockKey skuStockKey = SkuStockKey.builder()
.projectId(cartInfo.getProjectId())
.sku(cartInfo.getProjectSku())
.build();
Long skuStock = Convert.toLong(projectSkuStockCache.get(skuStockKey), -1L);
if (cartCache.hasKey(userId, cartHashKey)){
// 取出来修改
CartInfo cartInfoCache = cartCache.get(userId, cartHashKey);
cartInfoCache.setNum( cartInfoCache.getNum() + cartInfo.getNum());
if (skuStock < cartInfoCache.getNum()){
throw new ServiceException("当前库存不足");
}
LambdaUpdateWrapper<CartInfo> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.set(CartInfo::getNum, cartInfoCache.getNum());
updateWrapper.eq(CartInfo::getId, cartInfoCache.getId());
this.update(updateWrapper);
this.cartCache.put(userId, cartHashKey, cartInfoCache);
}else {
// 存进去
if (skuStock < cartInfo.getNum()){
throw new ServiceException("当前库存不足");
}
cartInfo.setIsSelected("Y");
cartInfo.setUserId(userId);
cartInfo.setCreateBy(SecurityUtils.getUsername());
cartInfo.setCreateTime(new Date());
this.save(cartInfo);
this.cartCache.put(userId, cartHashKey, cartInfo);
}
return true;
}
}

View File

@ -42,13 +42,17 @@ public class CartDataImpl implements CartData {
/**
* hashhash
*
* @param key
* @param userId
* @param hashKey hash
*
* @return hash
*/
@Override
public CartInfo getData (Long key, CartHashKey hashKey) {
return null;
public CartInfo getData (Long userId, CartHashKey hashKey) {
LambdaQueryWrapper<CartInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(CartInfo::getUserId, userId);
queryWrapper.eq(CartInfo::getProjectId, hashKey.getProjectId());
queryWrapper.eq(CartInfo::getProjectSku, hashKey.getProjectSku());
return cartInfoService.getOne(queryWrapper);
}
}

View File

@ -4,6 +4,8 @@ server:
# Spring
spring:
main:
allow-circular-references: true
application:
# 应用名称
name: muyu-shop-cart