后端 + 前端:添加到购物车

This commit is contained in:
YunaiV 2019-04-11 19:49:58 +08:00
parent 4300ce141d
commit b807a63966
22 changed files with 541 additions and 177 deletions

View File

@ -45,3 +45,24 @@ export function createOrder(params) {
}); });
} }
// Cart
export function addCart(skuId, quantity) {
return request({
url: '/order-api/users/cart/add',
method: 'post',
params: {
skuId,
quantity,
}
});
}
export function countCart() {
return request({
url: '/order-api/users/cart/count',
method: 'get',
params: {
}
});
}

View File

@ -8,8 +8,7 @@
style="background:#fff" style="background:#fff"
> >
<template slot="thumb"> <template slot="thumb">
<!-- TODO 芋艿 暂时去掉等会就恢复 --> <img :src="product.picUrls[0]"/>
<!-- <img :src="product.picUrls[0]"/>-->
<!-- TODO 芋艿 暂时去掉 --> <!-- TODO 芋艿 暂时去掉 -->
<!-- <p v-if="product.imageTag!=null&&product.imageTag!=''" class="image_tag">{{product.imageTag}}</p>--> <!-- <p v-if="product.imageTag!=null&&product.imageTag!=''" class="image_tag">{{product.imageTag}}</p>-->
</template> </template>

View File

@ -208,6 +208,7 @@ service.interceptors.response.use(
// this.$router.push({ path: '/login' }) // this.$router.push({ path: '/login' })
// TODO 跳转到登陆页.不是很优雅 // TODO 跳转到登陆页.不是很优雅
location.replace('/#login'); location.replace('/#login');
location.reload();
} }
} }
}); });

View File

@ -58,7 +58,8 @@ const routes = [
component: () => import('../page/user/info/detail'), component: () => import('../page/user/info/detail'),
name: 'user', name: 'user',
meta: { meta: {
title: '个人信息' title: '个人信息',
requireAuth: true,
} }
}, },
{ {
@ -178,7 +179,8 @@ const routes = [
path: '/order', path: '/order',
component: () => import('../page/shipping/order'), component: () => import('../page/shipping/order'),
meta: { meta: {
title: '确认订单' title: '确认订单',
requireAuth: true,
} }
}, },
{ {

View File

@ -6,74 +6,36 @@
<van-checkbox v-model="checkedAll" >全选</van-checkbox> <van-checkbox v-model="checkedAll" >全选</van-checkbox>
</template> </template>
</van-cell> </van-cell>
<van-checkbox-group class="card-goods" v-model="checkedGoods"> <van-checkbox-group class="card-goods" v-model="checkedGoods">
<div class="promotion-group"> <div class="promotion-group">
<div v-for="(item,index) in goods" <div v-for="(item,index) in goods" :key="index" class="card-goods__item">
:key="index" class="card-goods__item">
<van-checkbox :name="item.id"></van-checkbox> <van-checkbox :name="item.id"></van-checkbox>
<product-card :product='item' :iscard='true' > <product-card :product='item' :iscard='false' >
<template slot> <!-- <template slot>-->
<van-cell value="修改" > <!-- <van-cell value="修改" >-->
<template slot="title"> <!-- <template slot="title">-->
<van-tag type="danger">促销</van-tag> <!-- <van-tag type="danger">促销</van-tag>-->
<span class="van-cell-text" >满60元减5元</span> <!-- <span class="van-cell-text" >满60元减5元</span>-->
</template> <!-- </template>-->
</van-cell> <!-- </van-cell>-->
</template> <!-- </template>-->
</product-card> </product-card>
</div> </div>
</div>
<div class="promotion-group">
<van-cell is-link class="head">
<template slot="title">
<van-checkbox v-model="checkedAll" >京东自营</van-checkbox>
</template>
</van-cell>
<div v-for="(item,index) in goods"
:key="index+10" class="card-goods__item">
<van-checkbox :name="item.id"></van-checkbox>
<product-card :product='item' :iscard='true' >
<template slot>
<van-cell value="修改" >
<template slot="title">
<van-tag type="danger">促销</van-tag>
<span class="van-cell-text" >满60元减5元</span>
</template>
</van-cell>
</template>
</product-card>
</div> </div>
<van-cell value="去凑单" is-link class="promotion">
<template slot="title"> <!-- <div class="promotion-group">-->
<p><van-tag type="danger">满减</van-tag>605</p>
</template> <!-- <van-cell is-link class="head">-->
</van-cell> <!-- <template slot="title">-->
<div v-for="(item,index) in goods" <!-- <van-checkbox v-model="checkedAll" >京东自营</van-checkbox>-->
:key="index+20" class="card-goods__item"> <!-- </template>-->
<van-checkbox :name="item.id"></van-checkbox> <!-- </van-cell>-->
<!-- </div>-->
<product-card :product='item' :iscard='true' >
<template slot>
<van-cell value="修改" >
<template slot="title">
<van-tag type="danger">促销</van-tag>
<span class="van-cell-text" >满60元减5元</span>
</template>
</van-cell>
</template>
</product-card>
</div>
</div>
</van-checkbox-group> </van-checkbox-group>
<div style="height:50px;"></div> <div style="height:50px;"></div>
<van-submit-bar <van-submit-bar
:price="totalPrice" :price="totalPrice"
@ -103,7 +65,7 @@ export default {
desc: '3.18kg/件', desc: '3.18kg/件',
price: '200.00', price: '200.00',
quantity: 1, quantity: 1,
imageURL: 'https://img.yzcdn.cn/public_files/2017/10/24/2f9a36046449dafb8608e99990b3c205.jpeg', picUrls: ['https://img.yzcdn.cn/public_files/2017/10/24/2f9a36046449dafb8608e99990b3c205.jpeg'],
imageTag:'比加入时降5元', imageTag:'比加入时降5元',
}, { }, {
id: '2', id: '2',
@ -111,7 +73,7 @@ export default {
desc: '约600g', desc: '约600g',
price: '690.00', price: '690.00',
quantity: 1, quantity: 1,
imageURL: 'https://img.yzcdn.cn/public_files/2017/10/24/f6aabd6ac5521195e01e8e89ee9fc63f.jpeg', picUrls: ['https://img.yzcdn.cn/public_files/2017/10/24/f6aabd6ac5521195e01e8e89ee9fc63f.jpeg'],
gift: [ gift: [
{ {
title: "星巴克Starbucks星冰乐小熊吊饰星巴克Starbucks星冰乐小熊吊饰", title: "星巴克Starbucks星冰乐小熊吊饰星巴克Starbucks星冰乐小熊吊饰",
@ -128,7 +90,7 @@ export default {
desc: '约680g/3个', desc: '约680g/3个',
price: '2680.00', price: '2680.00',
quantity: 1, quantity: 1,
imageURL: 'https://img.yzcdn.cn/public_files/2017/10/24/320454216bbe9e25c7651e1fa51b31fd.jpeg' picUrls: ['https://img.yzcdn.cn/public_files/2017/10/24/320454216bbe9e25c7651e1fa51b31fd.jpeg']
}] }]
}; };
}, },
@ -143,7 +105,7 @@ export default {
}, },
methods: { methods: {
onSubmit() { onSubmit() {
this.$router.push('/order') this.$router.push('/order')
} }
} }
@ -201,5 +163,5 @@ export default {
} }
} }
</style> </style>

View File

@ -74,7 +74,7 @@
<van-goods-action-mini-btn icon="like-o" @click="sorry"> <van-goods-action-mini-btn icon="like-o" @click="sorry">
收藏 收藏
</van-goods-action-mini-btn> </van-goods-action-mini-btn>
<van-goods-action-mini-btn icon="cart" @click="onClickCart"> <van-goods-action-mini-btn icon="cart" :info="cartCount > 0 ? cartCount : undefined" @click="onClickCart">
购物车 购物车
</van-goods-action-mini-btn> </van-goods-action-mini-btn>
<van-goods-action-big-btn @click="showSku"> <van-goods-action-big-btn @click="showSku">
@ -167,6 +167,8 @@
<script> <script>
// import skuData from '../../data/sku'; // import skuData from '../../data/sku';
import {getProductSpuInfo} from '../../api/product'; import {getProductSpuInfo} from '../../api/product';
import {addCart, countCart} from '../../api/order';
import { Dialog } from 'vant';
export default { export default {
components: {}, components: {},
@ -190,26 +192,12 @@
}, },
attrValueMap: new Map(), // attrValueMap: new Map(), //
// show:false, showBase: false, // sku
// showTag:false,
// goods: {
// title: ' Lifeline Care DHAD3 30 2',
// subtitle:'QDHA400D3齿DPA',
// price: 2680,
// market_price:9999,
// express: '',
// remain: 19,
// thumb: [
// 'https://img.yzcdn.cn/public_files/2017/10/24/e5a5a02309a41f9f5def56684808d9ae.jpeg',
// 'https://img.yzcdn.cn/public_files/2017/10/24/1791ba14088f9c2be8c610d0a6cc0f93.jpeg'
// ],
// info:'<p style="text-align:center;"><img src="https://haitao.nosdn2.127.net/ac19460151ee4d95a6657202bcfc653c1531470912089jjjq8ml410763.jpg" ></p><p style="text-align:center;"><img src="https://haitao.nos.netease.com/2a91cfad22404e5498d347672b1440301531470912182jjjq8mnq10764.jpg" ></p><p style="text-align:center;"><img src="https://haitao.nos.netease.com/caddd5a213de4c1cb1347c267e8275731531470912412jjjq8mu410765.jpg" ></p>',
// },
showBase: false, // sku
closeOnClickOverlay: true, // closeOnClickOverlay: true, //
hideStock: true, // hideStock: true, //
cartCount: 0,
}; };
}, },
methods: { methods: {
@ -231,9 +219,9 @@
this.initialSku.quantity = value; this.initialSku.quantity = value;
}, },
skuSelected({skuValue, selectedSku, selectedSkuComb}) { // sku skuSelected({skuValue, selectedSku, selectedSkuComb}) { // sku
console.log(skuValue); // console.log(skuValue);
console.log(selectedSku); // console.log(selectedSku);
console.log(selectedSkuComb); // console.log(selectedSkuComb);
this.initialSku = { this.initialSku = {
...selectedSkuComb, ...selectedSkuComb,
quantity: 1, quantity: 1,
@ -256,25 +244,36 @@
this.showTag = true; this.showTag = true;
}, },
onBuyClicked(data) { onBuyClicked(data) {
const { selectedNum, goodsId } = data; const { selectedNum } = data;
console.log(data); // debugger;
// console.log(data);
// this.$toast(JSON.stringify(data)); // this.$toast(JSON.stringify(data));
this.$router.push({ this.$router.push({
path:'/order', path:'/order',
query:{ query:{
skuId: goodsId, skuId: data.selectedSkuComb.id,
quantity: selectedNum, quantity: selectedNum,
} }
}); });
}, },
onAddCartClicked(data) { onAddCartClicked(data) {
this.$toast(JSON.stringify(data)); const { selectedNum } = data;
// debugger;
addCart(data.selectedSkuComb.id,selectedNum).then(data => {
//
this.cartCount = data;
//
Dialog.alert({
title: '系统提示',
message: '添加购物车成功',
});
});
}, },
}, },
mounted() { mounted() {
//
let id = this.$route.params.id; // let id = this.$route.params.id; //
let response = getProductSpuInfo(id); getProductSpuInfo(id).then(data => {
response.then(data => {
// spu // spu
this.spu = data; this.spu = data;
// vanSku // vanSku
@ -337,6 +336,10 @@
this.initialSku = vanSku.list[0]; this.initialSku = vanSku.list[0];
this.initialSku.quantity = 1; this.initialSku.quantity = 1;
}); });
//
countCart().then(data => {
this.cartCount = data;
})
} }
}; };
</script> </script>

View File

@ -16,12 +16,22 @@
<div>{{addressData.address}}</div> <div>{{addressData.address}}</div>
</template> </template>
</van-cell> </van-cell>
<div style="height:15px;"></div> <div style="height:15px;"></div>
<div class="card" v-for="(product,i) in products" :key="i">
<product-card :product='product'/>
<!-- <div class="card" v-for="(product,i) in products" :key="i">-->
<!-- <product-card :product='product'/>-->
<!-- </div>-->
<div v-for="(itemGroup, i) in itemGroups" >
<div class="card" v-for="(item, j) in itemGroup.items" :key="j">
<product-card :product='convertProduct(item)'/>
</div>
<div style="height:15px;"></div>
</div> </div>
<div style="height:15px;"></div> <div style="height:15px;"></div>
<van-cell-group> <van-cell-group>
<van-field <van-field
@ -61,7 +71,6 @@
import {createOrder, getConfirmCreateOrder} from '../../api/order'; import {createOrder, getConfirmCreateOrder} from '../../api/order';
import {GetDefaultAddress} from '../../api/user'; import {GetDefaultAddress} from '../../api/user';
import orderStore from '../../store/order' import orderStore from '../../store/order'
import eventBus from '../eventBus';
export default { export default {
data() { data() {
@ -141,8 +150,17 @@
remark, remark,
}) })
}, },
convertProduct() { convertProduct(item) {
// debugger;
return {
...item.spu,
quantity: item.buyQuantity,
price: item.price,
sku: {
...item,
spu: undefined,
}
};
} }
}, },
mounted: function() { mounted: function() {

View File

@ -3,14 +3,109 @@ package cn.iocoder.mall.order.application.controller.users;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.order.api.CartService; import cn.iocoder.mall.order.api.CartService;
import cn.iocoder.mall.order.api.OrderService; import cn.iocoder.mall.order.api.OrderService;
import cn.iocoder.mall.order.api.bo.CartItemBO;
import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO;
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
import com.alibaba.dubbo.config.annotation.Reference;
import org.apache.ibatis.annotations.Param;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
@RestController
@RequestMapping("users/cart")
public class UsersCartController { public class UsersCartController {
// TODO 注入 @Reference(validation = "true")
private CartService cartService; private CartService cartService;
// TODO 注入 @Reference(validation = "true")
private OrderService orderService; private OrderService orderService;
@PostMapping("add")
public CommonResult<Integer> add(@Param("skuId") Integer skuId,
@Param("quantity") Integer quantity) {
// 添加到购物车
CommonResult<Boolean> addResult = cartService.add(UserSecurityContextHolder.getContext().getUserId(),
skuId, quantity);
// 添加失败则直接返回错误
if (addResult.isError()) {
return CommonResult.error(addResult);
}
// 获得目前购物车商品总数量
return cartService.count(UserSecurityContextHolder.getContext().getUserId());
}
@PostMapping("update_quantity")
public CommonResult<Integer> updateQuantity(@Param("skuId") Integer skuId,
@Param("quantity") Integer quantity) {
// 添加到购物车
CommonResult<Boolean> updateQuantityResult = cartService.updateQuantity(UserSecurityContextHolder.getContext().getUserId(),
skuId, quantity);
// 添加失败则直接返回错误
if (updateQuantityResult.isError()) {
return CommonResult.error(updateQuantityResult);
}
// 获得目前购物车商品总数量
// TODO 芋艿需要改成价格计算
return cartService.count(UserSecurityContextHolder.getContext().getUserId());
}
@PostMapping("update_selected")
public CommonResult<Integer> updateSelected(@Param("skuId") Integer skuId,
@Param("selected") Boolean selected) {
// 添加到购物车
CommonResult<Boolean> updateSelectedResult = cartService.updateSelected(UserSecurityContextHolder.getContext().getUserId(),
skuId, selected);
// 添加失败则直接返回错误
if (updateSelectedResult.isError()) {
return CommonResult.error(updateSelectedResult);
}
// 获得目前购物车商品总数量
// TODO 芋艿需要改成价格计算
return cartService.count(UserSecurityContextHolder.getContext().getUserId());
}
@GetMapping("count")
public CommonResult<Integer> count() {
return cartService.count(UserSecurityContextHolder.getContext().getUserId());
}
@GetMapping("/list")
public CommonResult<UsersOrderConfirmCreateVO> list() {
// 获得购物车中所有的
List<CartItemBO> cartItems = cartService.list(UserSecurityContextHolder.getContext().getUserId(), null);
// 购物车为空时构造空的 UsersOrderConfirmCreateVO 返回
if (cartItems.isEmpty()) {
UsersOrderConfirmCreateVO result = new UsersOrderConfirmCreateVO();
result.setItemGroups(Collections.emptyList());
result.setFee(new UsersOrderConfirmCreateVO.Fee(0, 0, 0, 0));
return CommonResult.success(result);
}
// 购物车非空时获得具体结果
return null;
}
@GetMapping("/confirm_create_order")
public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder() {
// 获得购物车中选中的
List<CartItemBO> cartItems = cartService.list(UserSecurityContextHolder.getContext().getUserId(), true);
// 购物车为空时构造空的 UsersOrderConfirmCreateVO 返回
if (cartItems.isEmpty()) {
UsersOrderConfirmCreateVO result = new UsersOrderConfirmCreateVO();
result.setItemGroups(Collections.emptyList());
result.setFee(new UsersOrderConfirmCreateVO.Fee(0, 0, 0, 0));
return CommonResult.success(result);
}
// 购物车非空时获得具体结果
// return CommonResult.success(CartConvert.INSTANCE.convert(calcOrderPriceResult.getData()));
return null;
}
public CommonResult<Object> confirmOrder() { public CommonResult<Object> confirmOrder() {
// 查询购物车列表选中的 // 查询购物车列表选中的
// cartService.list(userId, true); // cartService.list(userId, true);

View File

@ -133,6 +133,15 @@ public class UsersOrderConfirmCreateVO {
*/ */
private Integer presentTotal; private Integer presentTotal;
public Fee() {
}
public Fee(Integer originalTotal, Integer discountTotal, Integer postageTotal, Integer presentTotal) {
this.originalTotal = originalTotal;
this.discountTotal = discountTotal;
this.postageTotal = postageTotal;
this.presentTotal = presentTotal;
}
} }
/** /**

View File

@ -39,9 +39,10 @@ public interface CartService {
* *
* @param userId 用户编号 * @param userId 用户编号
* @param skuId 商品 SKU 编号 * @param skuId 商品 SKU 编号
* @param selected 是否选中
* @return 是否成功 * @return 是否成功
*/ */
CommonResult<Boolean> updateSelected(Integer userId, Integer skuId); CommonResult<Boolean> updateSelected(Integer userId, Integer skuId, Boolean selected);
/** /**
* 购物车删除商品 * 购物车删除商品
@ -67,7 +68,7 @@ public interface CartService {
* @param userId 用户编号 * @param userId 用户编号
* @return 商品数量 * @return 商品数量
*/ */
CommonResult<Integer> count(Integer userId, String nobody, Integer shopId); CommonResult<Integer> count(Integer userId);
/** /**
* 显示买家购物车中的商品列表并根据 selected 进行过滤 * 显示买家购物车中的商品列表并根据 selected 进行过滤

View File

@ -0,0 +1,46 @@
package cn.iocoder.mall.order.api.constant;
import java.util.Arrays;
public enum CartItemStatusEnum {
ENABLE(1, "正常"),
DELETE_BY_MANUAL(2, "主动删除"),
DELETE_BY_(3, "下单删除"),
;
public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CartItemStatusEnum::getValue).toArray();
/**
* 状态值
*/
private Integer value;
/**
* 状态名
*/
private String name;
CartItemStatusEnum(Integer value, String name) {
this.value = value;
this.name = name;
}
public Integer getValue() {
return value;
}
public CartItemStatusEnum setValue(Integer value) {
this.value = value;
return this;
}
public String getName() {
return name;
}
public CartItemStatusEnum setName(String name) {
this.name = name;
return this;
}
}

View File

@ -27,6 +27,12 @@ public enum OrderErrorCodeEnum {
ORDER_ITEM_ONLY_ONE(1008000004, "订单Item只有一个!"), ORDER_ITEM_ONLY_ONE(1008000004, "订单Item只有一个!"),
ORDER_ITEM_SOME_NOT_EXISTS(-1, "有不存在的商品"), // TODO 芋艿 后面改下错误码 ORDER_ITEM_SOME_NOT_EXISTS(-1, "有不存在的商品"), // TODO 芋艿 后面改下错误码
// ========== 购物车 ==========
CARD_ITEM_NOT_FOUND(1008003000, "购物车项不存在"),
CARD_ITEM_SKU_NOT_FOUND(1008003001, "商品不存在"),
CARD_ITEM_SKU_QUANTITY_NOT_ENOUGH(1008003002, "商品库存不足"),
; ;
private final int code; private final int code;

View File

@ -0,0 +1,39 @@
package cn.iocoder.mall.order.biz.dao;
import cn.iocoder.mall.order.biz.dataobject.CartItemDO;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.List;
@Repository
public interface CartMapper {
CartItemDO selectById(@Param("id") Integer id);
List<CartItemDO> selectByIds(@Param("ids") Collection<Integer> ids);
CartItemDO selectByUserIdAndSkuIdAndStatus(@Param("userId") Integer userId,
@Param("skuId") Integer skuId,
@Param("status") Integer status);
Integer selectQuantitySumByUserIdAndStatus(@Param("userId") Integer userId,
@Param("status") Integer status);
// List<CartItemDO> selectListByStatus(@Param("status") Integer status);
//
// List<CartItemDO> selectListByTitleLike(@Param("title") String title,
// @Param("offset") Integer offset,
// @Param("limit") Integer limit);
// Integer selectCountByTitleLike(@Param("title") String title);
void insert(CartItemDO cartItemDO);
int update(CartItemDO cartItemDO);
int updateQuantity(@Param("id") Integer id,
@Param("quantityIncr") Integer quantityIncr);
}

View File

@ -13,4 +13,5 @@ import org.springframework.stereotype.Repository;
public interface OrderCancelMapper { public interface OrderCancelMapper {
int insert(OrderCancelDO orderCancelDO); int insert(OrderCancelDO orderCancelDO);
} }

View File

@ -76,7 +76,7 @@ public class CartItemDO extends BaseDO {
/** /**
* 订单编号 * 订单编号
*/ */
private String orderId; private Integer orderId;
/** /**
* 订单创建时间 * 订单创建时间
*/ */

View File

@ -1,57 +0,0 @@
package cn.iocoder.mall.order.biz.mock;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.product.api.ProductSpuService;
import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO;
import cn.iocoder.mall.product.api.bo.ProductSpuBO;
import cn.iocoder.mall.product.api.bo.ProductSpuDetailBO;
import cn.iocoder.mall.product.api.bo.ProductSpuPageBO;
import cn.iocoder.mall.product.api.dto.ProductSpuAddDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuPageDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuUpdateDTO;
import java.util.Collection;
import java.util.List;
/**
* @author Sin
* @time 2019-03-24 15:24
*/
public class ProductSpuServiceMock implements ProductSpuService {
@Override
public CommonResult<ProductSpuDetailBO> getProductSpuDetail(Integer id) {
return null;
}
@Override
public CommonResult<ProductSpuDetailBO> addProductSpu(Integer adminId, ProductSpuAddDTO productSpuAddDTO) {
return null;
}
@Override
public CommonResult<Boolean> updateProductSpu(Integer adminId, ProductSpuUpdateDTO productSpuUpdateDTO) {
return null;
}
@Override
public CommonResult<Boolean> updateProductSpuSort(Integer adminId, Integer spuId, Integer sort) {
return null;
}
@Override
public CommonResult<ProductSpuPageBO> getProductSpuPage(ProductSpuPageDTO productSpuPageDTO) {
return null;
}
@Override
public CommonResult<List<ProductSpuBO>> getProductSpuList(Collection<Integer> ids) {
return null;
}
@Override
public CommonResult<List<ProductSkuDetailBO>> getProductSkuDetailList(Collection<Integer> ids) {
return null;
}
}

View File

@ -1,5 +1,6 @@
package cn.iocoder.mall.order.biz.service; package cn.iocoder.mall.order.biz.service;
import cn.iocoder.common.framework.constant.CommonStatusEnum;
import cn.iocoder.common.framework.util.ServiceExceptionUtil; import cn.iocoder.common.framework.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult; import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.order.api.CartService; import cn.iocoder.mall.order.api.CartService;
@ -7,12 +8,17 @@ import cn.iocoder.mall.order.api.bo.CalcOrderPriceBO;
import cn.iocoder.mall.order.api.bo.CartBO; import cn.iocoder.mall.order.api.bo.CartBO;
import cn.iocoder.mall.order.api.bo.CartItemBO; import cn.iocoder.mall.order.api.bo.CartItemBO;
import cn.iocoder.mall.order.api.bo.OrderCreateBO; import cn.iocoder.mall.order.api.bo.OrderCreateBO;
import cn.iocoder.mall.order.api.constant.CartItemStatusEnum;
import cn.iocoder.mall.order.api.constant.OrderErrorCodeEnum; import cn.iocoder.mall.order.api.constant.OrderErrorCodeEnum;
import cn.iocoder.mall.order.api.dto.CalcOrderPriceDTO; import cn.iocoder.mall.order.api.dto.CalcOrderPriceDTO;
import cn.iocoder.mall.order.biz.convert.CartConvert; import cn.iocoder.mall.order.biz.convert.CartConvert;
import cn.iocoder.mall.order.biz.dao.CartMapper;
import cn.iocoder.mall.order.biz.dataobject.CartItemDO;
import cn.iocoder.mall.product.api.ProductSpuService; import cn.iocoder.mall.product.api.ProductSpuService;
import cn.iocoder.mall.product.api.bo.ProductSkuBO;
import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO; import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO;
import com.alibaba.dubbo.config.annotation.Reference; import com.alibaba.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.*; import java.util.*;
@ -28,19 +34,97 @@ public class CartServiceImpl implements CartService {
@Reference(validation = "true") @Reference(validation = "true")
private ProductSpuService productSpuService; private ProductSpuService productSpuService;
@Autowired
private CartMapper cartMapper;
@Override @Override
@SuppressWarnings("Duplicates")
public CommonResult<Boolean> add(Integer userId, Integer skuId, Integer quantity) { public CommonResult<Boolean> add(Integer userId, Integer skuId, Integer quantity) {
return null; // 查询 SKU 是否合法
CommonResult<ProductSkuBO> skuResult = productSpuService.getProductSku(skuId);
if (skuResult.isError()) {
return CommonResult.error(skuResult);
}
ProductSkuBO sku = skuResult.getData();
if (sku == null
|| CommonStatusEnum.DISABLE.getValue().equals(sku.getStatus())) { // sku 被禁用
return ServiceExceptionUtil.error(OrderErrorCodeEnum.CARD_ITEM_SKU_NOT_FOUND.getCode());
}
// TODO 芋艿后续基于商品是否上下架进一步完善
// 查询 CartItemDO
CartItemDO item = cartMapper.selectByUserIdAndSkuIdAndStatus(userId, skuId, CartItemStatusEnum.ENABLE.getValue());
// 存在则进行数量更新
if (item != null) {
return updateQuantity0(item, sku, quantity);
}
// 不存在则进行插入
return add0(userId, sku, quantity);
}
private CommonResult<Boolean> add0(Integer userId, ProductSkuBO sku, Integer quantity) {
// 校验库存
if (quantity > sku.getQuantity()) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.CARD_ITEM_SKU_NOT_FOUND.getCode());
}
// 创建 CartItemDO 对象并进行保存
CartItemDO item = new CartItemDO()
// 基础字段
.setStatus(CartItemStatusEnum.ENABLE.getValue()).setSelected(true)
// 买家信息
.setUserId(userId)
// 商品信息
.setSpuId(sku.getSpuId()).setSkuId(sku.getId()).setQuantity(quantity);
item.setCreateTime(new Date());
cartMapper.insert(item);
// 返回成功
return CommonResult.success(true);
} }
@Override @Override
@SuppressWarnings("Duplicates")
public CommonResult<Boolean> updateQuantity(Integer userId, Integer skuId, Integer quantity) { public CommonResult<Boolean> updateQuantity(Integer userId, Integer skuId, Integer quantity) {
return null; // 查询 SKU 是否合法
CommonResult<ProductSkuBO> skuResult = productSpuService.getProductSku(skuId);
if (skuResult.isError()) {
return CommonResult.error(skuResult);
}
ProductSkuBO sku = skuResult.getData();
if (sku == null
|| CommonStatusEnum.DISABLE.getValue().equals(sku.getStatus())) { // sku 被禁用
return ServiceExceptionUtil.error(OrderErrorCodeEnum.CARD_ITEM_SKU_NOT_FOUND.getCode());
}
// 查询 CartItemDO
CartItemDO item = cartMapper.selectByUserIdAndSkuIdAndStatus(userId, skuId, CartItemStatusEnum.ENABLE.getValue());
if (item == null) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.CARD_ITEM_NOT_FOUND.getCode());
}
// TODO 芋艿后续基于商品是否上下架进一步完善
return updateQuantity0(item, sku, quantity);
}
private CommonResult<Boolean> updateQuantity0(CartItemDO item, ProductSkuBO sku, Integer quantity) {
// 校验库存
if (item.getQuantity() + quantity > sku.getQuantity()) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.CARD_ITEM_SKU_NOT_FOUND.getCode());
}
// 更新 CartItemDO
cartMapper.updateQuantity(item.getId(), quantity);
// 返回成功
return CommonResult.success(true);
} }
@Override @Override
public CommonResult<Boolean> updateSelected(Integer userId, Integer skuId) { public CommonResult<Boolean> updateSelected(Integer userId, Integer skuId, Boolean selected) {
return null; // 查询 CartItemDO
CartItemDO item = cartMapper.selectByUserIdAndSkuIdAndStatus(userId, skuId, CartItemStatusEnum.ENABLE.getValue());
if (item == null) {
return ServiceExceptionUtil.error(OrderErrorCodeEnum.CARD_ITEM_NOT_FOUND.getCode());
}
// 更新 CartItemDO
CartItemDO updateCartItem = new CartItemDO().setId(item.getId()).setSelected(selected);
cartMapper.update(updateCartItem);
// 返回成功
return CommonResult.success(true);
} }
@Override @Override
@ -54,8 +138,8 @@ public class CartServiceImpl implements CartService {
} }
@Override @Override
public CommonResult<Integer> count(Integer userId, String nobody, Integer shopId) { public CommonResult<Integer> count(Integer userId) {
return null; return CommonResult.success(cartMapper.selectQuantitySumByUserIdAndStatus(userId, CartItemStatusEnum.ENABLE.getValue()));
} }
@Override @Override

View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.iocoder.mall.order.biz.dao.CartMapper">
<sql id="FIELDS">
id, status, delete_time, selected, user_id,
spu_id, sku_id, quantity, order_id, order_create_time,
create_time
</sql>
<select id="selectById" parameterType="Integer" resultType="CartItemDO">
SELECT
<include refid="FIELDS" />
FROM cart_item
WHERE id = #{id}
-- AND deleted = 0
</select>
<select id="selectByIds" resultType="CartItemDO">
SELECT
<include refid="FIELDS" />
FROM cart_item
WHERE id IN
<foreach item="id" collection="ids" separator="," open="(" close=")" index="">
#{id}
</foreach>
-- AND deleted = 0
</select>
<select id="selectByUserIdAndSkuIdAndStatus" resultType="CartItemDO">
SELECT
<include refid="FIELDS" />
FROM cart_item
WHERE user_id = #{userId}
AND sku_id = #{skuId}
AND status = #{status}
-- AND deleted = 0
LIMIT 1
</select>
<select id="selectQuantitySumByUserIdAndStatus" resultType="Integer">
SELECT
SUM(quantity)
FROM cart_item
WHERE user_id = #{userId}
AND status = #{status}
-- AND deleted = 0
</select>
<insert id="insert" parameterType="CartItemDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
INSERT INTO cart_item (
status, delete_time, selected, user_id,
spu_id, sku_id, quantity, order_id, order_create_time,
create_time
) VALUES (
#{status}, #{deleteTime}, #{selected}, #{userId},
#{spuId}, #{skuId}, #{quantity}, #{orderId}, #{orderCreateTime},
#{createTime}
)
</insert>
<update id="update" parameterType="CartItemDO">
UPDATE cart_item
<set>
<if test="status != null">
status = #{status},
</if>
<if test="deleteTime != null">
delete_time = #{deleteTime},
</if>
<if test="selected != null">
selected = #{selected},
</if>
<if test="quantity != null">
quantity = #{quantity},
</if>
<if test="orderId != null">
order_id = #{orderId},
</if>
<if test="orderCreateTime != null">
order_create_time = #{orderCreateTime},
</if>
<if test="price != null">
price = #{price},
</if>
<if test="quantity != null">
quantity = #{quantity},
</if>
</set>
WHERE id = #{id}
</update>
<update id="updateQuantity" parameterType="CartItemDO">
UPDATE cart_item
SET quantity = quantity + #{quantityIncr}
WHERE id = #{id}
</update>
</mapper>

View File

@ -17,6 +17,8 @@ public interface ProductSpuService {
CommonResult<List<ProductSpuBO>> getProductSpuList(Collection<Integer> ids); CommonResult<List<ProductSpuBO>> getProductSpuList(Collection<Integer> ids);
CommonResult<ProductSkuBO> getProductSku(Integer id);
CommonResult<List<ProductSkuDetailBO>> getProductSkuDetailList(Collection<Integer> ids); CommonResult<List<ProductSkuDetailBO>> getProductSkuDetailList(Collection<Integer> ids);
CommonResult<ProductSpuDetailBO> addProductSpu(Integer adminId, ProductSpuAddDTO productSpuAddDTO); CommonResult<ProductSpuDetailBO> addProductSpu(Integer adminId, ProductSpuAddDTO productSpuAddDTO);

View File

@ -6,15 +6,40 @@ import lombok.experimental.Accessors;
import java.io.Serializable; import java.io.Serializable;
/** /**
* 商品 SKU BOA * 商品 SKU BO
*/ */
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class ProductSkuBO implements Serializable { public class ProductSkuBO implements Serializable {
/** /**
* SKU 编号 * sku 编号
*/ */
private Integer id; private Integer id;
/**
* 商品编号
*/
private Integer spuId;
/**
* 状态
*/
private Integer status;
/**
* 图片地址
*/
private String picURL;
// /**
// * 规格值数组
// */
// // TODO 芋艿这个属性目前未进行设置
// private List<ProductAttrAndValuePairBO> attrs;
/**
* 价格单位
*/
private Integer price;
/**
* 库存数量
*/
private Integer quantity;
} }

View File

@ -1,10 +1,7 @@
package cn.iocoder.mall.product.convert; package cn.iocoder.mall.product.convert;
import cn.iocoder.common.framework.util.StringUtil; import cn.iocoder.common.framework.util.StringUtil;
import cn.iocoder.mall.product.api.bo.ProductAttrAndValuePairBO; import cn.iocoder.mall.product.api.bo.*;
import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO;
import cn.iocoder.mall.product.api.bo.ProductSpuBO;
import cn.iocoder.mall.product.api.bo.ProductSpuDetailBO;
import cn.iocoder.mall.product.api.dto.ProductSkuAddOrUpdateDTO; import cn.iocoder.mall.product.api.dto.ProductSkuAddOrUpdateDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuAddDTO; import cn.iocoder.mall.product.api.dto.ProductSpuAddDTO;
import cn.iocoder.mall.product.api.dto.ProductSpuUpdateDTO; import cn.iocoder.mall.product.api.dto.ProductSpuUpdateDTO;
@ -73,6 +70,11 @@ public interface ProductSpuConvert {
}) })
ProductSkuDetailBO convert3(ProductSkuDO sku); ProductSkuDetailBO convert3(ProductSkuDO sku);
@Mappings({
// @Mapping(source = "attrs", target = "attrs", ignore = true) // TODO 芋艿 后续补充
})
ProductSkuBO convert4(ProductSkuDO sku);
@Mappings({}) // TODO 芋艿后续细看下 mapstruct API 优化这块 @Mappings({}) // TODO 芋艿后续细看下 mapstruct API 优化这块
default ProductSpuDetailBO convert2(ProductSpuDO spu, List<ProductSkuDO> skus, List<ProductAttrAndValuePairBO> productAttrDetailBOs) { default ProductSpuDetailBO convert2(ProductSpuDO spu, List<ProductSkuDO> skus, List<ProductAttrAndValuePairBO> productAttrDetailBOs) {
// 创建并转换 ProductSpuDetailBO 对象 // 创建并转换 ProductSpuDetailBO 对象

View File

@ -213,6 +213,12 @@ public class ProductSpuServiceImpl implements ProductSpuService {
return CommonResult.success(ProductSpuConvert.INSTANCE.convert(spus)); return CommonResult.success(ProductSpuConvert.INSTANCE.convert(spus));
} }
@Override
public CommonResult<ProductSkuBO> getProductSku(Integer id) {
ProductSkuDO sku = productSkuMapper.selectById(id);
return CommonResult.success(ProductSpuConvert.INSTANCE.convert4(sku));
}
@Override @Override
public CommonResult<List<ProductSkuDetailBO>> getProductSkuDetailList(Collection<Integer> ids) { public CommonResult<List<ProductSkuDetailBO>> getProductSkuDetailList(Collection<Integer> ids) {
// 查询 SKU 数组 // 查询 SKU 数组