提交 239c38cf authored 作者: YunaiV's avatar YunaiV

清理已迁移的 cart 代码

上级 a347d2a1
package cn.iocoder.mall.managementweb.convert.promotion;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.managementweb.controller.promotion.brand.vo.BannerCreateReqVO;
import cn.iocoder.mall.managementweb.controller.promotion.brand.vo.BannerPageReqVO;
import cn.iocoder.mall.managementweb.controller.promotion.brand.vo.BannerRespVO;
import cn.iocoder.mall.managementweb.controller.promotion.brand.vo.BannerUpdateReqVO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerCreateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerPageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerRespDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerUpdateReqDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface BannerConvert {
BannerConvert INSTANCE = Mappers.getMapper(BannerConvert.class);
BannerCreateReqDTO convert(BannerCreateReqVO bean);
BannerUpdateReqDTO convert(BannerUpdateReqVO bean);
BannerPageReqDTO convert(BannerPageReqVO bean);
PageResult<BannerRespVO> convertPage(PageResult<BannerRespDTO> page);
}
package cn.iocoder.mall.managementweb.manager.promotion.brand;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.managementweb.controller.promotion.brand.vo.BannerCreateReqVO;
import cn.iocoder.mall.managementweb.controller.promotion.brand.vo.BannerPageReqVO;
import cn.iocoder.mall.managementweb.controller.promotion.brand.vo.BannerRespVO;
import cn.iocoder.mall.managementweb.controller.promotion.brand.vo.BannerUpdateReqVO;
import cn.iocoder.mall.managementweb.convert.promotion.BannerConvert;
import cn.iocoder.mall.promotion.api.rpc.banner.BannerFeign;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerRespDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Banner Manager
*/
@Service
public class BannerManager {
@Autowired
private BannerFeign bannerFeign;
/**
* 创建 Banner
*
* @param createVO 创建 Banner VO
* @return Banner
*/
public Integer createBanner(BannerCreateReqVO createVO) {
CommonResult<Integer> createBannerResult = bannerFeign.createBanner(BannerConvert.INSTANCE.convert(createVO));
createBannerResult.checkError();
return createBannerResult.getData();
}
/**
* 更新 Banner
*
* @param updateVO 更新 Banner VO
*/
public void updateBanner(BannerUpdateReqVO updateVO) {
CommonResult<Boolean> updateBannerResult = bannerFeign.updateBanner(BannerConvert.INSTANCE.convert(updateVO));
updateBannerResult.checkError();
}
/**
* 删除 Banner
*
* @param bannerId Banner 编号
*/
public void deleteBanner(Integer bannerId) {
CommonResult<Boolean> deleteBannerResult = bannerFeign.deleteBanner(bannerId);
deleteBannerResult.checkError();
}
/**
* 获得 Banner 分页
*
* @param pageVO Banner 分页查询
* @return Banner 分页结果
*/
public PageResult<BannerRespVO> pageBanner(BannerPageReqVO pageVO) {
CommonResult<PageResult<BannerRespDTO>> pageBannerResult = bannerFeign.pageBanner(BannerConvert.INSTANCE.convert(pageVO));
pageBannerResult.checkError();
return BannerConvert.INSTANCE.convertPage(pageBannerResult.getData());
}
}
## 订单
TODO
**退货**
1. 商家未发货,退货原因
- 拍错/勿拍/多拍
- 缺货
2. 商家已发货,退货原因
- 七天无理由
- 质量问题
- 不想要了
- 其他
\ No newline at end of file
<?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.cart.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="selectByUserIdAndStatusAndSelected" resultType="CartItemDO">
SELECT
<include refid="FIELDS" />
FROM cart_item
WHERE user_id = #{userId}
AND status = #{status}
<if test="selected != null">
AND selected = #{selected}
</if>
-- AND deleted = 0
</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>
<update id="updateListByUserIdAndSkuId">
UPDATE cart_item
<set>
<if test="selected != null">
selected = #{selected},
</if>
<if test="status != null">
status = #{status},
</if>
</set>
WHERE user_id = #{userId}
AND sku_id IN
<foreach item="skuId" collection="skuIds" separator="," open="(" close=")" index="">
#{skuId}
</foreach>
-- AND deleted = 0
</update>
</mapper>
package cn.iocoder.mall.productservice.dal.mysql.dataobject.sku;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
import cn.iocoder.mall.productservice.dal.mysql.dataobject.attr.ProductAttrValueDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 商品 SKU
*/
@TableName("product_sku")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class ProductSkuDO extends DeletableDO {
/**
* sku 编号
*/
private Integer id;
/**
* 商品编号
*/
private Integer spuId;
// TODO 店铺编号
/**
* 状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 图片地址
*/
private String picUrl;
/**
* 规格值({@link ProductAttrValueDO#getId()})数组
*
* 数组,以逗号分隔
*/
private String attrs;
/**
* 价格,单位:分
*/
private Integer price;
/**
* 库存数量
*/
private Integer quantity;
// /**
// * 商品在付款减库存的状态下,该Sku上未付款的订单数量
// */
// private Integer withHoldQuantity;
// /**
// * 销量
// */
// private Integer soldNum;
}
package cn.iocoder.mall.productservice.dal.mysql.dataobject.spu;
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 商品 SPU
*
* TODO 芋艿,后面增加商品普通参数。例如说,正面材料,背面材料,屏幕尺寸。
*/
@TableName("product_spu")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class ProductSpuDO extends DeletableDO {
/**
* SPU 编号
*/
private Integer id;
// TODO 店铺编号 先不考虑,因为第一个版本,不做 B2B2C
// ========== 基本信息 =========
/**
* SPU 名字
*/
private String name;
/**
* 卖点
*/
private String sellPoint;
/**
* 描述
*/
private String description;
/**
* 分类编号
*/
private Integer cid;
/**
* 商品主图地址
*
* 数组,以逗号分隔
*
* 建议尺寸:800*800像素,你可以拖拽图片调整顺序,最多上传15张
*/
private String picUrls;
// TODO 运费信息
// ========== 其他信息 =========
/**
* 是否上架商品(是否可见)。
*
* true 为已上架
* false 为已下架
*/
private Boolean visible;
/**
* 排序字段
*/
private Integer sort;
// ========== Sku 相关字段 =========
/**
* 价格
*
* 目前的计算方式是,以 Sku 最小价格为准
*/
private Integer price;
/**
* 库存数量
*
* 目前的计算方式是,以 Sku 库存累加为准
*/
private Integer quantity;
}
...@@ -17,8 +17,6 @@ public interface ProductSkuMapper extends BaseMapper<ProductSkuDO> { ...@@ -17,8 +17,6 @@ public interface ProductSkuMapper extends BaseMapper<ProductSkuDO> {
.eq("status", status)); .eq("status", status));
} }
void insertList(@Param("productSkuDOs") List<ProductSkuDO> productSkuDOs);
default List<ProductSkuDO> selectList(ProductSkuListQueryBO queryBO) { default List<ProductSkuDO> selectList(ProductSkuListQueryBO queryBO) {
return selectList(new QueryWrapperX<ProductSkuDO>().eqIfPresent("id", queryBO.getProductSkuId()) return selectList(new QueryWrapperX<ProductSkuDO>().eqIfPresent("id", queryBO.getProductSkuId())
.inIfPresent("id", queryBO.getProductSkuIds()) .inIfPresent("id", queryBO.getProductSkuIds())
......
...@@ -32,17 +32,6 @@ public class ProductSkuManager { ...@@ -32,17 +32,6 @@ public class ProductSkuManager {
@Autowired @Autowired
private ProductAttrService productAttrService; private ProductAttrService productAttrService;
/**
* 获得商品 SKU
*
* @param productSkuId 商品 SKU编号
* @return 商品 SKU
*/
public ProductSkuRespDTO getProductSku(Integer productSkuId) {
ProductSkuBO productSkuBO = productSkuService.getProductSku(productSkuId);
return ProductSkuConvert.INSTANCE.convert(productSkuBO);
}
/** /**
* 获得商品 SKU 列表 * 获得商品 SKU 列表
* *
......
...@@ -51,17 +51,6 @@ public class ProductSpuManager { ...@@ -51,17 +51,6 @@ public class ProductSpuManager {
@Autowired @Autowired
private ProductMQProducer productMQProducer; private ProductMQProducer productMQProducer;
/**
* 获得商品 SPU
*
* @param productSpuId 商品 SPU编号
* @return 商品 SPU
*/
public ProductSpuRespDTO getProductSpu(Integer productSpuId) {
ProductSpuBO productSpuBO = productSpuService.getProductSpu(productSpuId);
return ProductSpuConvert.INSTANCE.convert(productSpuBO);
}
/** /**
* 获得商品 SPU列表 * 获得商品 SPU列表
* *
...@@ -84,25 +73,6 @@ public class ProductSpuManager { ...@@ -84,25 +73,6 @@ public class ProductSpuManager {
return ProductSpuConvert.INSTANCE.convertPage(pageResultBO); return ProductSpuConvert.INSTANCE.convertPage(pageResultBO);
} }
/**
* 添加或修改商品 SPU 时,校验商品分类是否合法
*
* @param cid 商品分类编号
* @return 商品分类
*/
private ProductCategoryBO checkProductCategory(Integer cid) {
ProductCategoryBO categoryBO = productCategoryService.getProductCategory(cid);
if (categoryBO == null) {
// 不存在
throw ServiceExceptionUtil.exception(PRODUCT_CATEGORY_NOT_EXISTS);
}
if (ProductCategoryIdEnum.ROOT.getId().equals(categoryBO.getPid())) {
// 商品只能添加到二级分类下
throw ServiceExceptionUtil.exception(PRODUCT_SPU_CATEGORY_MUST_BE_LEVEL2);
}
return categoryBO;
}
/** /**
* 顺序获得商品 SPU 编号数组 * 顺序获得商品 SPU 编号数组
* *
......
...@@ -22,15 +22,6 @@ public class ProductSkuService { ...@@ -22,15 +22,6 @@ public class ProductSkuService {
@Autowired @Autowired
private ProductSkuMapper productSkuMapper; private ProductSkuMapper productSkuMapper;
public void createProductSkus(Integer spuId, List<ProductSkuCreateOrUpdateBO> createSkuBOs) {
List<ProductSkuDO> skus = ProductSkuConvert.INSTANCE.convertList(createSkuBOs);
skus.forEach(sku -> {
sku.setStatus(CommonStatusEnum.ENABLE.getValue());
sku.setSpuId(spuId);
});
productSkuMapper.insertList(skus);
}
/** /**
* 获得 sku 数组中,指定规格的 sku * 获得 sku 数组中,指定规格的 sku
* *
...@@ -54,17 +45,6 @@ public class ProductSkuService { ...@@ -54,17 +45,6 @@ public class ProductSkuService {
return null; return null;
} }
/**
* 获得商品 SKU
*
* @param productSkuId 商品 SKU 编号
* @return 商品 SKU
*/
public ProductSkuBO getProductSku(Integer productSkuId) {
ProductSkuDO productSkuDO = productSkuMapper.selectById(productSkuId);
return ProductSkuConvert.INSTANCE.convert(productSkuDO);
}
/** /**
* 获得商品 SKU 列表 * 获得商品 SKU 列表
* *
......
package cn.iocoder.mall.productservice.service.sku.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
/**
* 商品 SKU BO
*/
@Data
@Accessors(chain = true)
public class ProductSkuBO {
/**
* sku 编号
*/
private Integer id;
/**
* 商品编号
*/
private Integer spuId;
/**
* 状态
*
* 1-正常
* 2-禁用
*/
private Integer status;
/**
* 图片地址
*/
private String picUrl;
/**
* 规格值编号数组
*/
private List<Integer> attrValueIds;
/**
* 价格,单位:分
*/
private Integer price;
/**
* 库存数量
*/
private Integer quantity;
/**
* 创建时间
*/
private Date createTime;
/**
* 最后更新时间
*/
private Date updateTime;
/**
* 是否删除
*/
private Integer deleted;
}
package cn.iocoder.mall.productservice.service.spu.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
/**
* 商品 SPU 信息 BO
*/
@Data
@Accessors(chain = true)
public class ProductSpuBO {
/**
* 商品 SPU 编号
*/
private Integer id;
/**
* SPU 名字
*/
private String name;
/**
* 卖点
*/
private String sellPoint;
/**
* 描述
*/
private String description;
/**
* 分类编号
*/
private Integer cid;
/**
* 商品主图地址
*/
private List<String> picUrls;
/**
* 是否上架商品
*/
private Boolean visible;
/**
* 排序字段
*/
private Integer sort;
/**
* 价格
*/
private Integer price;
/**
* 库存数量
*/
private Integer quantity;
/**
* 创建时间
*/
private Date createTime;
}
package cn.iocoder.mall.productservice.service.spu.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 商品 SPU 创建 BO
*/
@Data
@Accessors(chain = true)
public class ProductSpuCreateBO {
/**
* SPU 名字
*/
@NotEmpty(message = "SPU 名字不能为空")
private String name;
/**
* 卖点
*/
@NotEmpty(message = "卖点不能为空")
private String sellPoint;
/**
* 描述
*/
@NotEmpty(message = "描述不能为空")
private String description;
/**
* 分类编号
*/
@NotNull(message = "分类编号不能为空")
private Integer cid;
/**
* 商品主图地址
*/
@NotEmpty(message = "商品主图地址不能为空")
private List<String> picUrls;
/**
* 是否上架商品
*/
@NotNull(message = "是否上架商品不能为空")
private Boolean visible;
/**
* 排序字段
*/
@NotNull(message = "排序字段不能为空")
private Integer sort;
/**
* 价格
*/
@NotNull(message = "价格不能为空")
private Integer price;
/**
* 库存数量
*/
@NotNull(message = "库存数量不能为空")
private Integer quantity;
}
package cn.iocoder.mall.productservice.service.spu.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
* 商品 SPU 更新 BO
*/
@Data
@Accessors(chain = true)
public class ProductSpuUpdateBO {
/**
* 商品 SPU 编号
*/
@NotNull(message = "商品 SPU 编号不能为空")
private Integer id;
/**
* SPU 名字
*/
@NotEmpty(message = "SPU 名字不能为空")
private String name;
/**
* 卖点
*/
@NotEmpty(message = "卖点不能为空")
private String sellPoint;
/**
* 描述
*/
@NotEmpty(message = "描述不能为空")
private String description;
/**
* 分类编号
*/
@NotNull(message = "分类编号不能为空")
private Integer cid;
/**
* 商品主图地址
*/
@NotEmpty(message = "商品主图地址不能为空")
private List<String> picUrls;
/**
* 是否上架商品
*/
@NotNull(message = "是否上架商品不能为空")
private Boolean visible;
/**
* 排序字段
*/
private Integer sort;
/**
* 价格
*/
@NotNull(message = "价格不能为空")
private Integer price;
/**
* 库存数量
*/
@NotNull(message = "库存数量不能为空")
private Integer quantity;
}
package cn.iocoder.mall.promotion.api.rpc.banner;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.List;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@FeignClient("promotion-service")
public interface BannerFeign {
@PostMapping("/promotion/banner/createBanner")
public CommonResult<Integer> createBanner(@RequestBody BannerCreateReqDTO createDTO) ;
@PostMapping("/promotion/banner/updateBanner")
public CommonResult<Boolean> updateBanner(@RequestBody BannerUpdateReqDTO updateDTO) ;
@PostMapping("/promotion/banner/deleteBanner")
public CommonResult<Boolean> deleteBanner(@RequestBody Integer bannerId) ;
@PostMapping("/promotion/banner/listBanners")
public CommonResult<List<BannerRespDTO>> listBanners(@RequestBody BannerListReqDTO listDTO) ;
@PostMapping("/promotion/banner/pageBanner")
public CommonResult<PageResult<BannerRespDTO>> pageBanner(@RequestBody BannerPageReqDTO pageDTO);
}
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Banner 创建 Request DTO
*/
@Data
@Accessors(chain = true)
public class BannerCreateReqDTO implements Serializable {
/**
* 标题
*/
@NotEmpty(message = "标题不能为空")
@Length(min = 2, max = 32, message = "标题长度为 2-32 位")
private String title;
/**
* 跳转链接
*/
@NotEmpty(message = "跳转链接不能为空")
@URL(message = "跳转链接格式不正确")
@Length(max = 255, message = "跳转链接最大长度为 255 位")
private String url;
/**
* 图片链接
*/
@NotEmpty(message = "图片链接不能为空")
@URL(message = "图片链接格式不正确")
@Length(max = 255, message = "图片链接最大长度为 255 位")
private String picUrl;
/**
* 排序
*/
@NotNull(message = "排序不能为空")
private Integer sort;
/**
* 状态
*/
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
/**
* 备注
*/
@Length(max = 255, message = "备注最大长度为 255 位")
private String memo;
}
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* Banner 列表 Request DTO
*/
@Data
@Accessors(chain = true)
public class BannerListReqDTO implements Serializable {
/**
* 状态
*/
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
}
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import cn.iocoder.common.framework.vo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* Banner 分页 Request DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class BannerPageReqDTO extends PageParam {
/**
* 标题,模糊匹配
*/
private String title;
}
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* Banner Response DTO
*/
@Data
@Accessors(chain = true)
public class BannerRespDTO implements Serializable {
/**
* 编号
*/
private Integer id;
/**
* 标题
*/
private String title;
/**
* 跳转链接
*/
private String url;
/**
* 图片链接
*/
private String picUrl;
/**
* 排序
*/
private Integer sort;
/**
* 状态
*/
private Integer status;
/**
* 备注
*/
private String memo;
/**
* 创建时间
*/
private Date createTime;
}
package cn.iocoder.mall.promotion.api.rpc.banner.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* Banner 更新 Request DTO
*/
@Data
@Accessors(chain = true)
public class BannerUpdateReqDTO implements Serializable {
/**
* 编号
*/
@NotNull(message = "编号不能为空")
private Integer id;
/**
* 标题
*/
@NotEmpty(message = "标题不能为空")
@Length(min = 2, max = 32, message = "标题长度为 2-32 位")
private String title;
/**
* 跳转链接
*/
@NotEmpty(message = "跳转链接不能为空")
@URL(message = "跳转链接格式不正确")
@Length(max = 255, message = "跳转链接最大长度为 255 位")
private String url;
/**
* 图片链接
*/
@NotEmpty(message = "图片链接不能为空")
@URL(message = "图片链接格式不正确")
@Length(max = 255, message = "图片链接最大长度为 255 位")
private String picUrl;
/**
* 排序
*/
@NotNull(message = "排序不能为空")
private Integer sort;
/**
* 状态
*/
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
/**
* 备注
*/
@Length(max = 255, message = "备注最大长度为 255 位")
private String memo;
}
package cn.iocoder.mall.promotionservice.controller;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.*;
import cn.iocoder.mall.promotionservice.manager.banner.BannerManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@RestController
@RequestMapping("/promotion/banner")
public class BannerController {
@Autowired
private BannerManager bannerManager;
@PostMapping("createBanner")
public CommonResult<Integer> createBanner(@RequestBody BannerCreateReqDTO createDTO) {
return success(bannerManager.createBanner(createDTO));
}
@PostMapping("updateBanner")
public CommonResult<Boolean> updateBanner(@RequestBody BannerUpdateReqDTO updateDTO) {
bannerManager.updateBanner(updateDTO);
return success(true);
}
@PostMapping("deleteBanner")
public CommonResult<Boolean> deleteBanner(@RequestBody Integer bannerId) {
bannerManager.deleteBanner(bannerId);
return success(true);
}
@PostMapping("listBanners")
public CommonResult<List<BannerRespDTO>> listBanners(@RequestBody BannerListReqDTO listDTO) {
return success(bannerManager.listBanners(listDTO));
}
@PostMapping("pageBanner")
public CommonResult<PageResult<BannerRespDTO>> pageBanner(@RequestBody BannerPageReqDTO pageDTO) {
return success(bannerManager.pageBanner(pageDTO));
}
}
package cn.iocoder.mall.promotionservice.convert.banner;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerCreateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerRespDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerUpdateReqDTO;
import cn.iocoder.mall.promotionservice.dal.mysql.dataobject.banner.BannerDO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface BannerConvert {
BannerConvert INSTANCE = Mappers.getMapper(BannerConvert.class);
BannerDO convert(BannerCreateReqDTO bean);
BannerDO convert(BannerUpdateReqDTO bean);
@Mapping(source = "records", target = "list")
PageResult<BannerRespDTO> convertPage(IPage<BannerDO> page);
List<BannerRespDTO> convertList(List<BannerDO> list);
}
package cn.iocoder.mall.promotionservice.manager.banner;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.*;
import cn.iocoder.mall.promotionservice.service.banner.BannerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.validation.Valid;
import java.util.List;
/**
* Banner Manager
*/
@Service
@Valid
public class BannerManager {
@Autowired
private BannerService bannerService;
public Integer createBanner(BannerCreateReqDTO createDTO) {
return bannerService.createBanner(createDTO);
}
public void updateBanner(BannerUpdateReqDTO updateDTO) {
bannerService.updateBanner(updateDTO);
}
public void deleteBanner(Integer bannerId) {
bannerService.deleteBanner(bannerId);
}
public List<BannerRespDTO> listBanners(BannerListReqDTO listDTO) {
return bannerService.listBanners(listDTO);
}
public PageResult<BannerRespDTO> pageBanner(BannerPageReqDTO pageDTO) {
return bannerService.pageBanner(pageDTO);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>onemall</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shop-web-app</artifactId>
<description>商城,用于用户购物</description>
<dependencyManagement>
<dependencies>
<!-- onemall 基础 bom 文件 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-dependencies</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-swagger</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-security-user</artifactId>
</dependency>
<!-- RPC 相关 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-dubbo</artifactId>
</dependency>
<dependency>
<!-- 用户服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>user-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 商品服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>product-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 搜索服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>search-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 订单服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>trade-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 营销服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>promotion-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 系统服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 支付服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>pay-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Registry 和 Config 相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<!-- 使用 spring-boot-maven-plugin 插件打包 -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package cn.iocoder.mall.shopweb;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"cn.iocoder.mall.productservice.rpc","cn.iocoder.mall.searchservice.rpc",
"cn.iocoder.mall.tradeservice.rpc","cn.iocoder.mall.payservice.rpc","cn.iocoder.mall.promotion.api.rpc",
"cn.iocoder.mall.systemservice.rpc","cn.iocoder.mall.userservice.rpc"})
public class ShopWebApplication {
public static void main(String[] args) {
SpringApplication.run(ShopWebApplication.class, args);
}
}
package cn.iocoder.mall.shopweb.client.user;
import cn.iocoder.mall.userservice.rpc.address.UserAddressFeign;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserAddressClient {
@Autowired
private UserAddressFeign userAddressFeign;
}
package cn.iocoder.mall.shopweb.controller.product.vo.category;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@ApiModel("商品分类 Response VO")
@Data
public class ProductCategoryRespVO {
@ApiModelProperty(value = "分类编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "分类名称", required = true, example = "手机")
private String name;
@ApiModelProperty(value = "分类图片", notes = "一般情况下,只有根分类才有图片", example = "http://www.iocoder.cn/xx.jpg")
private String picUrl;
}
package cn.iocoder.mall.shopweb.controller.product.vo.product;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel("商品 SPU Response VO")
@Data
@Accessors(chain = true)
public class ProductSpuRespVO {
@ApiModelProperty(value = "SPU 编号", required = true, example = "1")
private Integer id;
// ========== 基本信息 =========
@ApiModelProperty(value = "SPU 名字", required = true, example = "芋艿")
private String name;
@ApiModelProperty(value = "卖点", required = true, example = "好吃好玩")
private String sellPoint;
@ApiModelProperty(value = "描述", required = true, example = "我是哈哈哈")
private String description;
@ApiModelProperty(value = "分类编号", required = true, example = "1")
private Integer cid;
@ApiModelProperty(value = "分类名字", required = true, example = "蔬菜")
private String categoryName;
@ApiModelProperty(value = "商品主图地址", required = true, example = "http://www.iocoder.cn/xxx.jpg", notes = "多个之间,使用逗号分隔")
private List<String> picUrls;
// ========== 其他信息 =========
@ApiModelProperty(value = "是否上架商品", required = true, example = "true")
private Boolean visible;
@ApiModelProperty(value = "排序字段", required = true, example = "1024")
private Integer sort;
// ========== Sku 相关字段 =========
@ApiModelProperty(value = "原始价格,单位:分", required = true, example = "233", notes = "该价格为商品的原始价格")
private Integer originalPrice;
@ApiModelProperty(value = "购买价格,单位:分", required = true, example = "233", notes = "该价格为商品经过优惠计算后的价格")
private Integer buyPrice;
@ApiModelProperty(value = "库存数量", required = true, example = "1024")
private Integer quantity;
// ========== 促销活动相关字段 ========= TODO 芋艿:等做到促销在处理
// 目前只促销单体商品促销,目前仅限制折扣。
/**
* 促销活动编号
*/
private Integer promotionActivityId;
/**
* 促销活动标题
*/
private String promotionActivityTitle;
/**
* 促销活动类型
*/
private Integer promotionActivityType;
}
### /passport/login-by-sms 成功
POST {{shop-api-base-url}}/passport/login-by-sms
Content-Type: application/x-www-form-urlencoded
mobile=15601691300&code=9999
### /passport/send-sms-code 成功
POST {{shop-api-base-url}}/passport/send-sms-code
Content-Type: application/x-www-form-urlencoded
mobile=15601691300&scene=1
### /passport/refresh-token
POST {{shop-api-base-url}}/passport/refresh-token
Content-Type: application/x-www-form-urlencoded
refreshToken=77abd74e84e34cfc8aba9625317a14a3
###
package cn.iocoder.mall.shopweb.controller.user;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportAccessTokenRespVO;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportLoginBySmsReqVO;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportSendSmsRespVO;
import cn.iocoder.mall.shopweb.service.user.PassportManager;
import cn.iocoder.security.annotations.RequiresNone;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api(tags = "用户 Passport API")
@RestController
@RequestMapping("/passport")
public class PassportController {
@Autowired
private PassportManager passportManager;
@PostMapping("/login-by-sms")
@ApiOperation("手机验证码登陆")
@RequiresNone
public CommonResult<PassportAccessTokenRespVO> loginBySms(PassportLoginBySmsReqVO loginBySmsDTO,
HttpServletRequest request) {
return success(passportManager.loginBySms(loginBySmsDTO, HttpUtil.getIp(request)));
}
@PostMapping("/send-sms-code")
@ApiOperation("发送手机验证码")
@RequiresNone
public CommonResult<Boolean> sendSmsCode(PassportSendSmsRespVO sendSmsCodeDTO,
HttpServletRequest request) {
passportManager.sendSmsCode(sendSmsCodeDTO, HttpUtil.getIp(request));
// 返回成功
return success(true);
}
@PostMapping("/refresh-token")
@ApiOperation("刷新令牌")
@RequiresNone
public CommonResult<PassportAccessTokenRespVO> refreshToken(@RequestParam("refreshToken") String refreshToken,
HttpServletRequest request) {
return success(passportManager.refreshToken(refreshToken, HttpUtil.getIp(request)));
}
}
### /user-address/get-default 成功
GET {{shop-api-base-url}}/user-address/get-default
Authorization: Bearer {{user-access-token}}
dubbo-tag: {{dubboTag}}
###
package cn.iocoder.mall.shopweb.controller.user;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressCreateReqVO;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressRespVO;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressUpdateReqVO;
import cn.iocoder.mall.shopweb.service.user.UserAddressManager;
import cn.iocoder.security.annotations.RequiresPermissions;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/**
* 用户收件地址 Controller
*/
@RestController
@RequestMapping("/user-address")
@Api(tags = "用户收件地址")
@Validated
public class UserAddressController {
@Autowired
private UserAddressManager userAddressManager;
@PostMapping("/create")
@ApiOperation("创建用户收件地址")
@RequiresPermissions
public CommonResult<Integer> createUserAddress(@Valid UserAddressCreateReqVO createVO) {
return success(userAddressManager.createUserAddress(UserSecurityContextHolder.getUserId(), createVO));
}
@PostMapping("/update")
@ApiOperation("更新用户收件地址")
@RequiresPermissions
public CommonResult<Boolean> updateUserAddress(@Valid UserAddressUpdateReqVO updateVO) {
userAddressManager.updateUserAddress(UserSecurityContextHolder.getUserId(), updateVO);
return success(true);
}
@PostMapping("/delete")
@ApiOperation("删除用户收件地址")
@ApiImplicitParam(name = "userAddressId", value = "用户收件地址编号", required = true)
@RequiresPermissions
public CommonResult<Boolean> deleteUserAddress(@RequestParam("userAddressId") Integer userAddressId) {
userAddressManager.deleteUserAddress(UserSecurityContextHolder.getUserId(), userAddressId);
return success(true);
}
@GetMapping("/get")
@ApiOperation("获得用户收件地址")
@ApiImplicitParam(name = "userAddressId", value = "用户收件地址编号", required = true)
@RequiresPermissions
public CommonResult<UserAddressRespVO> getUserAddress(@RequestParam("userAddressId") Integer userAddressId) {
return success(userAddressManager.getUserAddress(UserSecurityContextHolder.getUserId(), userAddressId));
}
@GetMapping("/get-default")
@ApiOperation("获得默认的用户收件地址")
@RequiresPermissions
public CommonResult<UserAddressRespVO> getDefaultUserAddress() {
return success(userAddressManager.getDefaultUserAddress(UserSecurityContextHolder.getUserId()));
}
@GetMapping("/list")
@ApiOperation("获得用户收件地址列表")
@RequiresPermissions
public CommonResult<List<UserAddressRespVO>> listUserAddresses() {
return success(userAddressManager.listUserAddresses(UserSecurityContextHolder.getUserId()));
}
}
package cn.iocoder.mall.shopweb.controller.user;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.user.vo.user.UserRespVO;
import cn.iocoder.mall.shopweb.service.user.UserManager;
import cn.iocoder.security.annotations.RequiresAuthenticate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api(tags = "用户信息 API")
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserManager userManager;
@ApiOperation(value = "用户信息")
@GetMapping("/info")
@RequiresAuthenticate
public CommonResult<UserRespVO> getUserInfo() {
UserRespVO user = userManager.getUser(UserSecurityContextHolder.getUserId());
return success(user);
}
@PostMapping("/update-avatar")
@RequiresAuthenticate
@ApiOperation(value = "更新头像")
@ApiImplicitParam(name = "avatar", value = "头像", required = true, example = "http://www.iocoder.cn/xxx.png")
public CommonResult<Boolean> updateUserAvatar(@RequestParam("avatar") String avatar) {
userManager.updateUserAvatar(UserSecurityContextHolder.getUserId(), avatar);
return success(true);
}
@PostMapping("/update-nickname")
@RequiresAuthenticate
@ApiOperation(value = "更新昵称")
@ApiImplicitParam(name = "nickname", value = "昵称", required = true, example = "蠢艿艿")
public CommonResult<Boolean> updateUserNickname(@RequestParam("nickname") String nickname) {
userManager.updateUserNickname(UserSecurityContextHolder.getUserId(), nickname);
return success(true);
}
}
package cn.iocoder.mall.shopweb.controller.user.vo.address;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.userservice.enums.address.UserAddressType;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ApiModel("用户收件地址创建 Request VO")
@Data
public class UserAddressCreateReqVO {
@ApiModelProperty(value = "收件人名称", required = true, example = "帅艿艿")
@NotEmpty(message = "收件人名称不能为空")
private String name;
@ApiModelProperty(value = "手机号", required = true, example = "15601691300")
@NotEmpty(message = "手机号不能为空")
private String mobile;
@ApiModelProperty(value = "区域编号", required = true, example = "610632")
@NotNull(message = "地区编码不能为空")
private Integer areaCode;
@ApiModelProperty(value = "收件详细地址", required = true, example = "芋道源码 233 号 666 室")
@NotEmpty(message = "收件详细地址不能为空")
private String detailAddress;
@ApiModelProperty(value = "地址类型", required = true, example = "1", notes = "参见 UserAddressType 枚举类")
@NotNull(message = "地址类型不能为空")
@InEnum(value = UserAddressType.class, message = "地址类型必须是 {value}")
private Integer type;
}
package cn.iocoder.mall.shopweb.controller.user.vo.address;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ApiModel("用户收件地址 Response VO")
@Data
public class UserAddressRespVO {
@ApiModelProperty(value = "收件地址编号", required = true, example = "1024")
private Integer id;
@ApiModelProperty(value = "用户编号", required = true, example = "2048")
private Integer userId;
@ApiModelProperty(value = "收件人名称", required = true, example = "帅艿艿")
private String name;
@ApiModelProperty(value = "手机号", required = true, example = "15601691300")
private String mobile;
@ApiModelProperty(value = "区域编号", required = true, example = "610632")
private Integer areaCode;
@ApiModelProperty(value = "收件详细地址", required = true, example = "芋道源码 233 号 666 室")
private String detailAddress;
@ApiModelProperty(value = "地址类型", required = true, example = "1", notes = "参见 UserAddressType 枚举类")
private Integer type;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}
package cn.iocoder.mall.shopweb.controller.user.vo.address;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.userservice.enums.address.UserAddressType;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ApiModel("用户收件地址更新 Request VO")
@Data
public class UserAddressUpdateReqVO {
@ApiModelProperty(value = "收件地址编号", required = true, example = "1024")
@NotNull(message = "收件地址编号不能为空")
private Integer id;
@ApiModelProperty(value = "收件人名称", required = true, example = "帅艿艿")
@NotEmpty(message = "收件人名称不能为空")
private String name;
@ApiModelProperty(value = "手机号", required = true, example = "15601691300")
@NotEmpty(message = "手机号不能为空")
private String mobile;
@ApiModelProperty(value = "区域编号", required = true, example = "610632")
@NotNull(message = "地区编码不能为空")
private Integer areaCode;
@ApiModelProperty(value = "收件详细地址", required = true, example = "芋道源码 233 号 666 室")
@NotEmpty(message = "收件详细地址不能为空")
private String detailAddress;
@ApiModelProperty(value = "地址类型", required = true, example = "1", notes = "参见 UserAddressType 枚举类")
@NotNull(message = "地址类型不能为空")
@InEnum(value = UserAddressType.class, message = "地址类型必须是 {value}")
private Integer type;
}
package cn.iocoder.mall.shopweb.controller.user.vo.passport;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
@ApiModel("访问令牌信息 Response VO")
@Data
@Accessors(chain = true)
public class PassportAccessTokenRespVO {
@ApiModelProperty(value = "访问令牌", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
private String accessToken;
@ApiModelProperty(value = "刷新令牌", required = true, example = "001e8f49b20e47f7b3a2de774497cd50")
private String refreshToken;
@ApiModelProperty(value = "过期时间", required = true)
private Date expiresTime;
}
package cn.iocoder.mall.shopweb.controller.user.vo.passport;
import cn.iocoder.common.framework.validator.Mobile;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
@ApiModel("用户短信验证码登陆 Request VO")
@Data
@Accessors(chain = true)
public class PassportLoginBySmsReqVO implements Serializable {
@ApiModelProperty(value = "手机号", required = true, example = "15601691300")
@NotEmpty(message = "手机号不能为空")
@Mobile
private String mobile;
@ApiModelProperty(value = "手机验证码", required = true, example = "1024")
@NotEmpty(message = "手机验证码不能为空")
@Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
@Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
private String code;
}
package cn.iocoder.mall.shopweb.controller.user.vo.passport;
import cn.iocoder.common.framework.validator.Mobile;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
@ApiModel("发送手机验证码 Response VO")
@Data
@Accessors(chain = true)
public class PassportSendSmsRespVO {
@ApiModelProperty(value = "手机号", example = "15601691234")
@Mobile
private String mobile;
@ApiModelProperty(value = "发送场景", example = "1", notes = "对应 UserSmsSceneEnum 枚举")
@NotNull(message = "发送场景不能为空")
private Integer scene;
}
package cn.iocoder.mall.shopweb.controller.user.vo.user;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("用户信息 VO")
@Data
@Accessors(chain = true)
public class UserRespVO {
@ApiModelProperty(value = "用户编号", required = true, example = "123")
private Integer id;
@ApiModelProperty(value = "手机号", required = true, example = "15601691300")
private String mobile;
@ApiModelProperty(value = "昵称", required = true, example = "小王")
private String nickname;
@ApiModelProperty(value = "头像", required = true, example = "http://www.iocoder.cn/xxx.jpg")
private String avatar;
}
package cn.iocoder.mall.shopweb.convert.product;
import cn.iocoder.mall.productservice.rpc.category.dto.ProductCategoryRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.category.ProductCategoryRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface ProductCategoryConvert {
ProductCategoryConvert INSTANCE = Mappers.getMapper(ProductCategoryConvert.class);
List<ProductCategoryRespVO> convertList(List<ProductCategoryRespDTO> list);
}
package cn.iocoder.mall.shopweb.convert.promotion;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerRespDTO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.brand.BannerRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface BannerConvert {
BannerConvert INSTANCE = Mappers.getMapper(BannerConvert.class);
List<BannerRespVO> convertList(List<BannerRespDTO> list);
}
package cn.iocoder.mall.shopweb.convert.user;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportAccessTokenRespVO;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportLoginBySmsReqVO;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportSendSmsRespVO;
import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2AccessTokenRespDTO;
import cn.iocoder.mall.userservice.rpc.sms.dto.UserSendSmsCodeReqDTO;
import cn.iocoder.mall.userservice.rpc.sms.dto.UserVerifySmsCodeReqDTO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserCreateReqDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface PassportConvert {
PassportConvert INSTANCE = Mappers.getMapper(PassportConvert.class);
UserVerifySmsCodeReqDTO convert(PassportLoginBySmsReqVO bean);
UserCreateReqDTO convert02(PassportLoginBySmsReqVO bean);
UserSendSmsCodeReqDTO convert(PassportSendSmsRespVO bean);
PassportAccessTokenRespVO convert(OAuth2AccessTokenRespDTO bean);
}
package cn.iocoder.mall.shopweb.convert.user;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressCreateReqVO;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressRespVO;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressUpdateReqVO;
import cn.iocoder.mall.userservice.rpc.address.dto.UserAddressCreateReqDTO;
import cn.iocoder.mall.userservice.rpc.address.dto.UserAddressRespDTO;
import cn.iocoder.mall.userservice.rpc.address.dto.UserAddressUpdateReqDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface UserAddressConvert {
UserAddressConvert INSTANCE = Mappers.getMapper(UserAddressConvert.class);
UserAddressUpdateReqDTO convert(UserAddressUpdateReqVO bean);
UserAddressRespVO convert(UserAddressRespDTO bean);
List<UserAddressRespVO> convertList(List<UserAddressRespDTO> list);
UserAddressCreateReqDTO convert(UserAddressCreateReqVO bean);
}
package cn.iocoder.mall.shopweb.convert.user;
import cn.iocoder.mall.shopweb.controller.user.vo.user.UserRespVO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserRespDTO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface UserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
UserRespVO convert(UserRespDTO bean);
}
package cn.iocoder.mall.shopweb.service.promotion;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.promotion.api.rpc.banner.BannerFeign;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.banner.dto.BannerRespDTO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.brand.BannerRespVO;
import cn.iocoder.mall.shopweb.convert.promotion.BannerConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Comparator;
import java.util.List;
/**
* Banner Manager
*/
@Service
@Validated
public class BannerManager {
@Autowired
private BannerFeign bannerFeign;
public List<BannerRespVO> listBanners() {
// 获取 Banner 列表
CommonResult<List<BannerRespDTO>> listBannersResult = bannerFeign.listBanners(
new BannerListReqDTO().setStatus(CommonStatusEnum.ENABLE.getValue()));
listBannersResult.checkError();
// 排序返回
listBannersResult.getData().sort(Comparator.comparing(BannerRespDTO::getSort));
return BannerConvert.INSTANCE.convertList(listBannersResult.getData());
}
}
...@@ -28,68 +28,6 @@ import java.util.stream.Collectors; ...@@ -28,68 +28,6 @@ import java.util.stream.Collectors;
@Service @Service
public class CartManager { public class CartManager {
@Autowired
private CartFeign cartFeign;
@Autowired
private PriceFeign priceFeign;
@Autowired
private PromotionActivityFeign promotionActivityFeign;
@Autowired
private ProductSkuFeign productSkuFeign;
/**
* 添加商品到购物车
*
* @param userId 用户编号
* @param skuId 商品 SKU 编号
* @param quantity 增加数量
*/
public void addCartItem(Integer userId, Integer skuId, Integer quantity) {
CommonResult<Boolean> addCartItemResult = cartFeign.addCartItem(new CartItemAddReqDTO().setUserId(userId)
.setSkuId(skuId).setQuantity(quantity));
addCartItemResult.checkError();
}
/**
* 查询用户在购物车中的商品数量
*
* @param userId 用户编号
* @return 商品数量
*/
public Integer sumCartItemQuantity(Integer userId) {
CommonResult<Integer> sumCartItemQuantityResult = cartFeign.sumCartItemQuantity(userId);
sumCartItemQuantityResult.checkError();
return sumCartItemQuantityResult.getData();
}
/**
* 更新购物车商品数量
*
* @param userId 用户编号
* @param skuId 商品 SKU 编号
* @param quantity 数量
*/
public void updateCartItemQuantity(Integer userId, Integer skuId, Integer quantity) {
CommonResult<Boolean> updateCartItemQuantityResult = cartFeign.updateCartItemQuantity(new CartItemUpdateQuantityReqDTO()
.setUserId(userId).setSkuId(skuId).setQuantity(quantity));
updateCartItemQuantityResult.checkError();
}
/**
* 更新购物车商品是否选中
*
* @param userId 用户编号
* @param skuIds 商品 SKU 编号数组
* @param selected 是否选中
*/
public void updateCartItemSelected(Integer userId, Set<Integer> skuIds, Boolean selected) {
CommonResult<Boolean> updateCartItemSelectedResult = cartFeign.updateCartItemSelected(new CartItemUpdateSelectedReqDTO()
.setUserId(userId).setSkuIds(skuIds).setSelected(selected));
updateCartItemSelectedResult.checkError();
}
/** /**
* 查询用户的购物车的商品列表 * 查询用户的购物车的商品列表
* *
......
package cn.iocoder.mall.shopweb.service.user;
import cn.iocoder.common.framework.enums.UserTypeEnum;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportAccessTokenRespVO;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportLoginBySmsReqVO;
import cn.iocoder.mall.shopweb.controller.user.vo.passport.PassportSendSmsRespVO;
import cn.iocoder.mall.shopweb.convert.user.PassportConvert;
import cn.iocoder.mall.systemservice.rpc.oauth.OAuthFeign;
import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2AccessTokenRespDTO;
import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2CreateAccessTokenReqDTO;
import cn.iocoder.mall.systemservice.rpc.oauth.dto.OAuth2RefreshAccessTokenReqDTO;
import cn.iocoder.mall.userservice.enums.sms.UserSmsSceneEnum;
import cn.iocoder.mall.userservice.rpc.sms.UserSmsCodeFeign;
import cn.iocoder.mall.userservice.rpc.user.UserFeign;
import cn.iocoder.mall.userservice.rpc.user.dto.UserRespDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PassportManager {
private UserSmsCodeFeign userSmsCodeFeign;
private UserFeign userFeign;
@Autowired
private OAuthFeign oAuthFeign;
public PassportAccessTokenRespVO loginBySms(PassportLoginBySmsReqVO loginBySmsDTO, String ip) {
// 校验验证码
CommonResult<Boolean> verifySmsCodeResult = userSmsCodeFeign.verifySmsCode(
PassportConvert.INSTANCE.convert(loginBySmsDTO).setScene(UserSmsSceneEnum.LOGIN_BY_SMS.getValue()).setIp(ip));
verifySmsCodeResult.checkError();
// 获得用户
CommonResult<UserRespDTO> createUserResult = userFeign.createUserIfAbsent(
PassportConvert.INSTANCE.convert02(loginBySmsDTO).setIp(ip));
createUserResult.checkError();
// 创建访问令牌
CommonResult<OAuth2AccessTokenRespDTO> createAccessTokenResult = oAuthFeign.createAccessToken(
new OAuth2CreateAccessTokenReqDTO().setUserId(createUserResult.getData().getId())
.setUserType(UserTypeEnum.USER.getValue()).setCreateIp(ip));
createAccessTokenResult.checkError();
// 返回
return PassportConvert.INSTANCE.convert(createAccessTokenResult.getData());
}
public void sendSmsCode(PassportSendSmsRespVO sendSmsCodeDTO, String ip) {
CommonResult<Boolean> sendSmsCodeResult = userSmsCodeFeign.sendSmsCode(
PassportConvert.INSTANCE.convert(sendSmsCodeDTO).setIp(ip));
sendSmsCodeResult.checkError();
}
public PassportAccessTokenRespVO refreshToken(String refreshToken, String ip) {
CommonResult<OAuth2AccessTokenRespDTO> refreshAccessTokenResult = oAuthFeign.refreshAccessToken(
new OAuth2RefreshAccessTokenReqDTO().setRefreshToken(refreshToken).setCreateIp(ip));
refreshAccessTokenResult.checkError();
return PassportConvert.INSTANCE.convert(refreshAccessTokenResult.getData());
}
}
package cn.iocoder.mall.shopweb.service.user;
import cn.iocoder.common.framework.exception.GlobalException;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressCreateReqVO;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressRespVO;
import cn.iocoder.mall.shopweb.controller.user.vo.address.UserAddressUpdateReqVO;
import cn.iocoder.mall.shopweb.convert.user.UserAddressConvert;
import cn.iocoder.mall.userservice.enums.address.UserAddressType;
import cn.iocoder.mall.userservice.rpc.address.UserAddressFeign;
import cn.iocoder.mall.userservice.rpc.address.dto.UserAddressRespDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import static cn.iocoder.common.framework.exception.enums.GlobalErrorCodeConstants.FORBIDDEN;
/**
* 用户收件地址 Manager
*/
@Service
public class UserAddressManager {
@Autowired
private UserAddressFeign userAddressFeign;
/**
* 创建用户收件地址
*
* @param userId 用户编号
* @param createVO 创建用户收件地址 VO
* @return 用户收件地址
*/
public Integer createUserAddress(Integer userId, UserAddressCreateReqVO createVO) {
CommonResult<Integer> createUserAddressResult = userAddressFeign.createUserAddress(
UserAddressConvert.INSTANCE.convert(createVO).setUserId(userId));
createUserAddressResult.checkError();
return createUserAddressResult.getData();
}
/**
* 更新用户收件地址
*
* @param userId 用户编号
* @param updateVO 更新用户收件地址 VO
*/
public void updateUserAddress(Integer userId, UserAddressUpdateReqVO updateVO) {
// 校验是否能够操作
check(userId, updateVO.getId());
// 执行更新
CommonResult<Boolean> updateUserAddressResult = userAddressFeign.updateUserAddress(UserAddressConvert.INSTANCE.convert(updateVO)
.setUserId(userId));
updateUserAddressResult.checkError();
}
/**
* 删除用户收件地址
*
* @param userId 用户编号
* @param userAddressId 用户收件地址编号
*/
public void deleteUserAddress(Integer userId, Integer userAddressId) {
// 校验是否能够操作
check(userId, userAddressId);
// 执行删除
CommonResult<Boolean> deleteUserAddressResult = userAddressFeign.deleteUserAddress(userAddressId);
deleteUserAddressResult.checkError();
}
/**
* 获得用户收件地址
*
* @param userId 用户编号
* @param userAddressId 用户收件地址编号
* @return 用户收件地址
*/
public UserAddressRespVO getUserAddress(Integer userId, Integer userAddressId) {
CommonResult<UserAddressRespDTO> getUserAddressResult = userAddressFeign.getUserAddress(userAddressId);
getUserAddressResult.checkError();
// 校验是否能够操作
this.check(userId, userAddressId);
return UserAddressConvert.INSTANCE.convert(getUserAddressResult.getData());
}
/**
* 获得用户收件地址列表
*
* @param userId 用户编号
* @return 用户收件地址列表
*/
public List<UserAddressRespVO> listUserAddresses(Integer userId) {
CommonResult<List<UserAddressRespDTO>> listUserAddressResult = userAddressFeign.listUserAddresses(userId, null);
listUserAddressResult.checkError();
return UserAddressConvert.INSTANCE.convertList(listUserAddressResult.getData());
}
/**
* 获得用户的默认收件地址
*
* @param userId 用户编号
* @return 用户收件地址
*/
public UserAddressRespVO getDefaultUserAddress(Integer userId) {
CommonResult<List<UserAddressRespDTO>> listUserAddressResult = userAddressFeign.listUserAddresses(userId, UserAddressType.DEFAULT.getType());
listUserAddressResult.checkError();
return !CollectionUtils.isEmpty(listUserAddressResult.getData()) ?
UserAddressConvert.INSTANCE.convert(listUserAddressResult.getData().get(0)) : null;
}
/**
* 校验用户收件地址是不是属于该用户
*
* @param userId 用户编号
* @param userAddressId 用户收件地址
*/
private void check(Integer userId, Integer userAddressId) {
CommonResult<UserAddressRespDTO> getUserAddressResult = userAddressFeign.getUserAddress(userAddressId);
getUserAddressResult.checkError();
this.check(userId, getUserAddressResult.getData());
}
private void check(Integer userId, UserAddressRespDTO userAddressRespDTO) {
if (!userAddressRespDTO.getUserId().equals(userId)) {
throw new GlobalException(FORBIDDEN);
}
}
}
package cn.iocoder.mall.shopweb.service.user;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.shopweb.controller.user.vo.user.UserRespVO;
import cn.iocoder.mall.shopweb.convert.user.UserConvert;
import cn.iocoder.mall.userservice.rpc.user.UserFeign;
import cn.iocoder.mall.userservice.rpc.user.dto.UserRespDTO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserUpdateReqDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserManager {
@Autowired
private UserFeign userFeign;
public UserRespVO getUser(Integer id) {
CommonResult<UserRespDTO> userResult = userFeign.getUser(id);
userResult.checkError();
return UserConvert.INSTANCE.convert(userResult.getData());
}
public void updateUserAvatar(Integer userId, String avatar) {
CommonResult<Boolean> updateUserResult = userFeign.updateUser(new UserUpdateReqDTO().setId(userId).setAvatar(avatar));
updateUserResult.checkError();
}
public void updateUserNickname(Integer userId, String nickname) {
CommonResult<Boolean> updateUserResult = userFeign.updateUser(new UserUpdateReqDTO().setId(userId).setNickname(nickname));
updateUserResult.checkError();
}
}
spring:
# Spring Cloud 配置项
cloud:
nacos:
# Spring Cloud Nacos Discovery 配置项
discovery:
server-addr: localhost:8848 # Nacos 服务器地址
namespace: dev # Nacos 命名空间
# Dubbo 配置项
dubbo:
# Dubbo 注册中心
registry:
# address: spring-cloud://localhost:8848 # 指定 Dubbo 服务注册中心的地址
address: nacos://localhost:8848?namespace=dev # 指定 Dubbo 服务注册中心的地址
spring:
# Spring Cloud 配置项
cloud:
nacos:
# Spring Cloud Nacos Discovery 配置项
discovery:
server-addr: localhost:8848 # Nacos 服务器地址
namespace: dev # Nacos 命名空间
# Spring 主应用配置
main:
lazy-initialization: true # 开启延迟加载,保证本地开发的性能
# Dubbo 配置项
dubbo:
# Dubbo 注册中心
registry:
# address: spring-cloud://localhost:8848 # 指定 Dubbo 服务注册中心的地址
address: nacos://localhost:8848?namespace=dev # 指定 Dubbo 服务注册中心的地址
# 服务器的配置项
server:
port: 18084
servlet:
context-path: /shop-api/
spring:
# Application 的配置项
application:
name: shop-web
# Profile 的配置项
profiles:
active: local
# SpringMVC 配置项
mvc:
throw-exception-if-no-handler-found: true # 匹配不到路径时,抛出 NoHandlerFoundException 异常
static-path-pattern: /doc.html # 静态资源的路径
# Dubbo 配置项
dubbo:
# Spring Cloud Alibaba Dubbo 专属配置
cloud:
subscribed-services: 'user-service,system-service' # 设置订阅的应用列表,默认为 * 订阅所有应用
# Dubbo 服务消费者的配置
consumer:
timeout: 10000
validation: true # 开启 Consumer 的参数校验
UserRpc:
version: 1.0.0
OAuth2Rpc:
version: 1.0.0
SystemAccessLogRpc:
version: 1.0.0
SystemExceptionLogRpc:
version: 1.0.0
ProductCategoryRpc:
version: 1.0.0
ProductSpuRpc:
version: 1.0.0
ProductSkuRpc:
version: 1.0.0
SearchProductRpc:
version: 1.0.0
PriceRpc:
version: 1.0.0
PromotionActivityRpc:
version: 1.0.0
CouponCardRpc:
version: 1.0.0
CouponTemplateRpc:
version: 1.0.0
BannerRpc:
version: 1.0.0
ProductRecommendRpc:
version: 1.0.0
TradeOrderRpc:
version: 1.0.0
PayTransactionRpc:
version: 1.0.0
UserSmsCodeRpc:
version: 1.0.0
UserAddressRpc:
version: 1.0.0
# Swagger 配置项
swagger:
title: 商城中心
description: 提供用户商城购物流程中的 API
version: 1.0.0
base-package: cn.iocoder.mall.shopweb.controller
# Actuator 监控配置项
management:
server.port: 38088 # 独立端口,避免被暴露出去
endpoints.web.exposure.include: '*' # 暴露所有监控端点
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>trade-service-project</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>trade-service-api</artifactId>
<dependencies>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
</project>
...@@ -39,11 +39,6 @@ public interface OrderErrorCodeConstants { ...@@ -39,11 +39,6 @@ public interface OrderErrorCodeConstants {
ErrorCode ORDER_RETURN_NOT_EXISTENT = new ErrorCode(1008000401, "退货订单不存在"); ErrorCode ORDER_RETURN_NOT_EXISTENT = new ErrorCode(1008000401, "退货订单不存在");
ErrorCode ORDER_RETURN_REFUND_FAILED = new ErrorCode(1008000402, "退款失败"); ErrorCode ORDER_RETURN_REFUND_FAILED = new ErrorCode(1008000402, "退款失败");
// ========== 购物车 ==========
ErrorCode CARD_ITEM_NOT_FOUND = new ErrorCode(1008003000, "购物车项不存在");
ErrorCode CARD_ITEM_SKU_NOT_FOUND = new ErrorCode(1008003001, "商品不存在");
ErrorCode CARD_ITEM_SKU_QUANTITY_NOT_ENOUGH = new ErrorCode(1008003002, "商品库存不足");
// 工具类服务 1008004000 // 工具类服务 1008004000
ErrorCode DICT_SERVER_INVOKING_FAIL = new ErrorCode(1008004000, "字典服务调用失败!"); ErrorCode DICT_SERVER_INVOKING_FAIL = new ErrorCode(1008004000, "字典服务调用失败!");
ErrorCode ORDER_LOGISTICS_INVOKING_FAIL = new ErrorCode(1008004001, "订单物流调用失败!"); ErrorCode ORDER_LOGISTICS_INVOKING_FAIL = new ErrorCode(1008004001, "订单物流调用失败!");
......
package cn.iocoder.mall.tradeservice.rpc.cart;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.*;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* Title:
* Description:
*
* @author zhuyang
* @version 1.0 2021/10/9
*/
@FeignClient(value = "trade-service")
public interface CartFeign {
/**
* 添加商品到购物车
*
* @param addReqDTO 添加商品信息
* @return 成功
*/
@PostMapping("/trade/cart/addCartItem")
CommonResult<Boolean> addCartItem(@RequestBody CartItemAddReqDTO addReqDTO);
/**
* 更新购物车商品数量
*
* @param updateQuantityReqDTO 更新商品数量 DTO
* @return 成功
*/
@PostMapping("/trade/cart/updateCartItemQuantity")
CommonResult<Boolean> updateCartItemQuantity(@RequestBody CartItemUpdateQuantityReqDTO updateQuantityReqDTO);
/**
* 更新购物车商品是否选中
*
* @param updateSelectedReqDTO 更新商品是否选中 DTO
* @return 成功
*/
@PostMapping("/trade/cart/updateCartItemSelected")
CommonResult<Boolean> updateCartItemSelected(@RequestBody CartItemUpdateSelectedReqDTO updateSelectedReqDTO);
/**
* 删除购物车商品列表
*
* @param deleteListReqDTO 删除商品列表 DTO
* @return 成功
*/
@PostMapping("/trade/cart/deleteCartItems")
CommonResult<Boolean> deleteCartItems(@RequestBody CartItemDeleteListReqDTO deleteListReqDTO);
@GetMapping("/sumCartItemQuantity")
public CommonResult<Integer> sumCartItemQuantity(@RequestParam("userId") Integer userId) ;
@PostMapping("/trade/cart/listCartItems")
public CommonResult<List<CartItemRespDTO>> listCartItems(@RequestBody CartItemListReqDTO listReqDTO) ;
}
package cn.iocoder.mall.tradeservice.rpc.cart.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 购物车添加购物项 Request DTO
*/
@Data
@Accessors(chain = true)
public class CartItemAddReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 商品 SKU 编号
*/
@NotNull(message = "商品 SKU 编号不能为空")
private Integer skuId;
/**
* 数量
*/
@NotNull(message = "数量不能为空")
@Min(message = "数量必须大于 0", value = 1L)
private Integer quantity;
}
package cn.iocoder.mall.tradeservice.rpc.cart.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.List;
/**
* 购物车删除商品列表 Request DTO
*/
@Data
@Accessors(chain = true)
public class CartItemDeleteListReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 商品 SKU 编号列表
*/
@NotNull(message = "商品 SKU 编号列表不能为空")
private List<Integer> skuIds;
}
package cn.iocoder.mall.tradeservice.rpc.cart.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 购物车的商品信息查询 BO
*/
@Data
@Accessors(chain = true)
public class CartItemListReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 是否选中
*/
private Boolean selected;
}
package cn.iocoder.mall.tradeservice.rpc.cart.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
/**
* 购物车的商品信息 Response DTO
*/
@Data
@Accessors(chain = true)
public class CartItemRespDTO implements Serializable {
// ========= 基础字段 BEGIN =========
/**
* 编号,唯一自增。
*/
private Integer id;
/**
* 是否选中
*/
private Boolean selected;
// ========= 基础字段 END =========
// ========= 买家信息 BEGIN =========
/**
* 用户编号
*/
private Integer userId;
// ========= 买家信息 END =========
// ========= 商品信息 BEGIN =========
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 商品 SKU 编号
*/
private Integer skuId;
/**
* 商品购买数量
*/
private Integer quantity;
// ========= 商品信息 END =========
// ========= 优惠信息 BEGIN =========
// /**
// * 商品营销活动编号
// */
// private Integer activityId;
// /**
// * 商品营销活动类型
// */
// private Integer activityType;
// ========= 优惠信息 END =========
}
package cn.iocoder.mall.tradeservice.rpc.cart.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 购物车更新数量 Request DTO
*/
@Data
@Accessors(chain = true)
public class CartItemUpdateQuantityReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 商品 SKU 编号
*/
@NotNull(message = "商品 SKU 编号不能为空")
private Integer skuId;
/**
* 数量
*/
@NotNull(message = "数量不能为空")
@Min(message = "数量必须大于 0", value = 1L)
private Integer quantity;
}
package cn.iocoder.mall.tradeservice.rpc.cart.dto;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Collection;
/**
* 购物车更新是否选中 Request DTO
*/
@Data
@Accessors(chain = true)
public class CartItemUpdateSelectedReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 商品 SKU 编号列表
*/
@NotNull(message = "商品 SKU 编号列表不能为空")
private Collection<Integer> skuIds;
/**
* 是否选中
*/
@NotNull(message = "是否选中不能为空")
private Boolean selected;
}
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>trade-service-project</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>trade-service-app</artifactId>
<dependencies>
<!-- RPC 相关 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-dubbo</artifactId>
</dependency>
<dependency>
<!-- 订单服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>trade-service-api</artifactId>
</dependency>
<dependency>
<!-- 系统服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 用户服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>user-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 商品服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>product-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 支付服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>pay-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<!-- 营销服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>promotion-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <!-- 需要开启 Web 容器,因为 Actuator 需要使用到 -->
</dependency>
<!-- MQ 相关 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-rocketmq</artifactId>
</dependency>
<!-- Registry 和 Config 相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.6</version>
</dependency>
<!-- Test 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<!-- 使用 spring-boot-maven-plugin 插件打包 -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package cn.iocoder.mall.tradeservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients(basePackages = {"cn.iocoder.mall.productservice.rpc","cn.iocoder.mall.promotion.api.rpc","cn.iocoder.mall.userservice.rpc"})
public class TradeServiceApplication {
public static void main(String[] args) {
SpringApplication.run(TradeServiceApplication.class, args);
}
}
package cn.iocoder.mall.tradeservice.client.user;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.userservice.rpc.address.UserAddressFeign;
import cn.iocoder.mall.userservice.rpc.address.dto.UserAddressRespDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
@Service
public class UserAddressClient {
@Autowired
private UserAddressFeign userAddressFeign;
public UserAddressRespDTO getUserAddress(Integer userAddressId, Integer userId) {
CommonResult<UserAddressRespDTO> getUserAddressResult = userAddressFeign.getUserAddress(userAddressId);
getUserAddressResult.checkError();
if (getUserAddressResult.getData() == null) {
return null;
}
// 如果用户编号不匹配,则返回 null
return Objects.equals(getUserAddressResult.getData().getUserId(), userId) ?
getUserAddressResult.getData() : null;
}
}
package cn.iocoder.mall.tradeservice.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
* Spring Aop 配置类
*/
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
public class AopConfiguration {
}
package cn.iocoder.mall.tradeservice.config;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@Configuration
@MapperScan("cn.iocoder.mall.tradeservice.dal.mysql.mapper") // 扫描对应的 Mapper 接口
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理。
public class DatabaseConfiguration {
// 数据库连接池 Druid
@Bean
public ISqlInjector sqlInjector() {
return new DefaultSqlInjector(); // MyBatis Plus 逻辑删除
}
@Bean
public PaginationInterceptor paginationInterceptor() {
return new PaginationInterceptor(); // MyBatis Plus 分页插件
}
}
package cn.iocoder.mall.tradeservice.controller;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.*;
import cn.iocoder.mall.tradeservice.service.cart.CartManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@RestController
@RequestMapping("/trade/cart")
public class CartController {
@Autowired
private CartManager cartManager;
/**
* 添加商品到购物车
*
* @param addReqDTO 添加商品信息
* @return 成功
*/
@PostMapping("addCartItem")
CommonResult<Boolean> addCartItem(@RequestBody CartItemAddReqDTO addReqDTO){
cartManager.addCartItem(addReqDTO);
return success(true);
}
/**
* 更新购物车商品数量
*
* @param updateQuantityReqDTO 更新商品数量 DTO
* @return 成功
*/
@PostMapping("updateCartItemQuantity")
CommonResult<Boolean> updateCartItemQuantity(@RequestBody CartItemUpdateQuantityReqDTO updateQuantityReqDTO){
cartManager.updateCartItemSelected(updateQuantityReqDTO);
return success(true);
}
/**
* 更新购物车商品是否选中
*
* @param updateSelectedReqDTO 更新商品是否选中 DTO
* @return 成功
*/
@PostMapping("updateCartItemSelected")
CommonResult<Boolean> updateCartItemSelected(@RequestBody CartItemUpdateSelectedReqDTO updateSelectedReqDTO){
cartManager.updateCartItemSelected(updateSelectedReqDTO);
return success(true);
}
/**
* 删除购物车商品列表
*
* @param deleteListReqDTO 删除商品列表 DTO
* @return 成功
*/
@PostMapping("deleteCartItems")
CommonResult<Boolean> deleteCartItems(@RequestBody CartItemDeleteListReqDTO deleteListReqDTO){
cartManager.deleteCartItems(deleteListReqDTO);
return success(true);
}
@GetMapping("/sumCartItemQuantity")
public CommonResult<Integer> sumCartItemQuantity(@RequestParam("userId") Integer userId) {
return success(cartManager.sumCartItemQuantity(userId));
}
@PostMapping("/listCartItems")
public CommonResult<List<CartItemRespDTO>> listCartItems(@RequestBody CartItemListReqDTO listReqDTO) {
return success(cartManager.listCartItems(listReqDTO));
}
}
package cn.iocoder.mall.tradeservice.convert.cart;
import cn.iocoder.mall.tradeservice.dal.mysql.dataobject.cart.CartItemDO;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.CartItemAddReqDTO;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.CartItemListReqDTO;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.CartItemRespDTO;
import cn.iocoder.mall.tradeservice.service.cart.bo.CartItemAddBO;
import cn.iocoder.mall.tradeservice.service.cart.bo.CartItemBO;
import cn.iocoder.mall.tradeservice.service.cart.bo.CartItemListQueryBO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface CartConvert {
CartConvert INSTANCE = Mappers.getMapper(CartConvert.class);
CartItemDO convert(CartItemAddBO bean);
CartItemAddBO convert(CartItemAddReqDTO bean);
List<CartItemBO> convertList(List<CartItemDO> list);
CartItemListQueryBO convert(CartItemListReqDTO bean);
List<CartItemRespDTO> convertList02(List<CartItemBO> list);
}
package cn.iocoder.mall.tradeservice.convert.order;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.tradeservice.dal.mysql.dataobject.order.TradeOrderDO;
import cn.iocoder.mall.tradeservice.dal.mysql.dataobject.order.TradeOrderItemDO;
import cn.iocoder.mall.tradeservice.rpc.order.dto.TradeOrderItemRespDTO;
import cn.iocoder.mall.tradeservice.rpc.order.dto.TradeOrderRespDTO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface TradeOrderConvert {
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
TradeOrderRespDTO convert(TradeOrderDO bean);
@Mapping(source = "records", target = "list")
PageResult<TradeOrderRespDTO> convertPage(IPage<TradeOrderDO> page);
List<TradeOrderItemRespDTO> convertList(List<TradeOrderItemDO> list);
TradeOrderItemRespDTO convert(TradeOrderItemDO bean);
}
package cn.iocoder.mall.tradeservice.dal.mysql.dataobject.cart;
import cn.iocoder.mall.mybatis.core.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 购物车的商品信息 DO
*/
@TableName("cart_item")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class CartItemDO extends DeletableDO {
// ========= 基础字段 BEGIN =========
/**
* 编号,唯一自增。
*/
private Integer id;
/**
* 是否选中
*/
private Boolean selected;
// ========= 基础字段 END =========
// ========= 买家信息 BEGIN =========
/**
* 用户编号
*/
private Integer userId;
// ========= 买家信息 END =========
// ========= 商品信息 BEGIN =========
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 商品 SKU 编号
*/
private Integer skuId;
/**
* 商品购买数量
*/
private Integer quantity;
// TODO 冗余字段
// ========= 商品信息 END =========
// ========= 优惠信息 BEGIN =========
// /**
// * 商品营销活动编号
// */
// private Integer activityId;
// /**
// * 商品营销活动类型
// */
// private Integer activityType;
// ========= 优惠信息 END =========
}
package cn.iocoder.mall.tradeservice.dal.mysql.mapper.cart;
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
import cn.iocoder.mall.tradeservice.dal.mysql.dataobject.cart.CartItemDO;
import cn.iocoder.mall.tradeservice.service.cart.bo.CartItemListQueryBO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@Repository
public interface CartItemMapper extends BaseMapper<CartItemDO> {
default CartItemDO selectByUserIdAndSkuId(Integer userId, Integer skuId) {
return selectOne(new QueryWrapper<CartItemDO>().eq("user_id", userId)
.eq("sku_id", skuId));
}
default List<CartItemDO> selectListByUserIdAndSkuIds(Integer userId, Collection<Integer> skuIds) {
return selectList(new QueryWrapperX<CartItemDO>().eq("user_id", userId)
.inIfPresent("sku_id", skuIds));
}
default void updateByIds(@Param("ids") Set<Integer> ids, @Param("updateObject") CartItemDO updateObject) {
// TODO 芋艿:batch update ,在 mybatis plus 做拓展,这里先临时处理
ids.forEach(id -> updateById(updateObject.setId(id)));
}
default Integer selectSumQuantityByUserId(Integer userId) {
// SQL sum 查询
List<Map<String, Object>> result = selectMaps(new QueryWrapper<CartItemDO>()
.select("SUM(quantity) AS sumQuantity")
.eq("user_id", userId));
// 获得数量
return ((BigDecimal) result.get(0).get("sumQuantity")).intValue();
}
default List<CartItemDO> selectList(CartItemListQueryBO queryBO) {
return selectList(new QueryWrapperX<CartItemDO>().eq("user_id", queryBO.getUserId())
.eqIfPresent("selected", queryBO.getSelected()));
}
}
package cn.iocoder.mall.tradeservice.service.cart;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.productservice.rpc.sku.ProductSkuFeign;
import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuRespDTO;
import cn.iocoder.mall.tradeservice.convert.cart.CartConvert;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.*;
import cn.iocoder.mall.tradeservice.service.cart.bo.CartItemBO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import static cn.iocoder.mall.tradeservice.enums.OrderErrorCodeConstants.CARD_ITEM_SKU_NOT_FOUND;
/**
* 购物车 Manager
*/
@Service
public class CartManager {
@Autowired
private ProductSkuFeign productSkuFeign;
@Autowired
private CartService cartService;
/**
* 添加商品到购物车
*
* @param addReqDTO 添加商品信息
*/
public void addCartItem(CartItemAddReqDTO addReqDTO) {
// 校验商品 SKU 是否合法
ProductSkuRespDTO skuDTO = this.checkProductSku(addReqDTO.getSkuId());
// 添加购物车项
cartService.addCartItem(CartConvert.INSTANCE.convert(addReqDTO).setSpuId(skuDTO.getSpuId()), skuDTO.getQuantity());
}
/**
* 更新购物车商品数量
*
* @param updateQuantityReqDTO 更新商品数量
*/
public void updateCartItemSelected(CartItemUpdateQuantityReqDTO updateQuantityReqDTO) {
// 校验商品 SKU 是否合法
ProductSkuRespDTO skuDTO = this.checkProductSku(updateQuantityReqDTO.getSkuId());
// 更新购物车商品数量
cartService.updateCartItemQuantity(updateQuantityReqDTO.getUserId(), updateQuantityReqDTO.getSkuId(),
updateQuantityReqDTO.getQuantity(), skuDTO.getQuantity());
}
/**
* 更新购物车商品是否选中
*
* @param updateSelectedReqDTO 更新商品是否选中 DTO
*/
public void updateCartItemSelected(CartItemUpdateSelectedReqDTO updateSelectedReqDTO) {
cartService.updateCartItemSelected(updateSelectedReqDTO.getUserId(),
updateSelectedReqDTO.getSkuIds(), updateSelectedReqDTO.getSelected());
}
/**
* 删除购物车商品列表
*
* @param deleteListReqDTO 删除商品列表 DTO
*/
public void deleteCartItems(CartItemDeleteListReqDTO deleteListReqDTO) {
cartService.deleteCartItems(deleteListReqDTO.getUserId(),
deleteListReqDTO.getSkuIds());
}
/**
* 查询用户在购物车中的商品数量
*
* @param userId 用户编号
* @return 商品数量
*/
public Integer sumCartItemQuantity(Integer userId) {
return cartService.sumCartItemQuantity(userId);
}
/**
* 查询用户在购物车种的商品列表
*
* @param listReqDTO 查询条件 DTO
* @return 购物车中商品列表信息
*/
public List<CartItemRespDTO> listCartItems(CartItemListReqDTO listReqDTO) {
List<CartItemBO> cartItemBOs = cartService.listCartItems(CartConvert.INSTANCE.convert(listReqDTO));
return CartConvert.INSTANCE.convertList02(cartItemBOs);
}
/**
* 校验商品 SKU 是否合法
* 1. 是否存在
* 2. 是否下架
*
* @param skuId 商品 SKU 编号
* @return 商品 SKU 信息
*/
private ProductSkuRespDTO checkProductSku(Integer skuId) {
CommonResult<ProductSkuRespDTO> getProductSkuResult = productSkuFeign.getProductSku(skuId);
getProductSkuResult.checkError();
ProductSkuRespDTO skuDTO = getProductSkuResult.getData();
if (skuDTO == null || CommonStatusEnum.DISABLE.getValue().equals(skuDTO.getStatus())) {
throw ServiceExceptionUtil.exception(CARD_ITEM_SKU_NOT_FOUND);
}
return skuDTO;
}
}
package cn.iocoder.mall.tradeservice.service.cart;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.mall.tradeservice.convert.cart.CartConvert;
import cn.iocoder.mall.tradeservice.dal.mysql.dataobject.cart.CartItemDO;
import cn.iocoder.mall.tradeservice.dal.mysql.mapper.cart.CartItemMapper;
import cn.iocoder.mall.tradeservice.service.cart.bo.CartItemAddBO;
import cn.iocoder.mall.tradeservice.service.cart.bo.CartItemBO;
import cn.iocoder.mall.tradeservice.service.cart.bo.CartItemListQueryBO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.validation.Valid;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.mall.tradeservice.enums.OrderErrorCodeConstants.CARD_ITEM_NOT_FOUND;
import static cn.iocoder.mall.tradeservice.enums.OrderErrorCodeConstants.CARD_ITEM_SKU_QUANTITY_NOT_ENOUGH;
/**
* 购物车 Service
*/
@Service
@Validated
public class CartService {
@Autowired
private CartItemMapper cartItemMapper;
/**
* 添加商品到购物车
*
* @param addBO 添加商品信息
* @param skuQuantity 商品 SKU 的库存,主要用于库存校验
*/
public void addCartItem(@Valid CartItemAddBO addBO, Integer skuQuantity) {
// 查询 CartItemDO
CartItemDO itemDO = cartItemMapper.selectByUserIdAndSkuId(addBO.getUserId(), addBO.getSkuId());
// 存在,则进行数量更新
if (itemDO != null) {
if (addBO.getQuantity() + itemDO.getQuantity() > skuQuantity) {
// 校验库存
throw ServiceExceptionUtil.exception(CARD_ITEM_SKU_QUANTITY_NOT_ENOUGH);
}
cartItemMapper.updateById(new CartItemDO().setId(itemDO.getId()).setSelected(true)
.setQuantity(addBO.getQuantity() + itemDO.getQuantity()));
return;
}
// 不存在,则进行插入
if (addBO.getQuantity() > skuQuantity) {
// 校验库存
throw ServiceExceptionUtil.exception(CARD_ITEM_SKU_QUANTITY_NOT_ENOUGH);
}
cartItemMapper.insert(CartConvert.INSTANCE.convert(addBO).setSelected(true));
}
/**
* 更新购物车商品数量
*
* @param userId 用户编号
* @param skuId 商品 SKU 编号
* @param quantity 数量
* @param skuQuantity 商品 SKU 的库存,主要用于库存校验
*/
public void updateCartItemQuantity(Integer userId, Integer skuId, Integer quantity, Integer skuQuantity) {
if (quantity > skuQuantity) {
// 校验库存
throw ServiceExceptionUtil.exception(CARD_ITEM_SKU_QUANTITY_NOT_ENOUGH);
}
// 查询 CartItemDO
CartItemDO itemDO = cartItemMapper.selectByUserIdAndSkuId(userId, skuId);
if (itemDO == null) {
throw ServiceExceptionUtil.exception(CARD_ITEM_NOT_FOUND);
}
// 更新数量
cartItemMapper.updateById(new CartItemDO().setId(itemDO.getId()).setQuantity(quantity));
}
/**
* 更新购物车商品是否选中
*
* @param userId 用户编号
* @param skuIds 商品 SKU 编号数组
* @param selected 是否选中
*/
public void updateCartItemSelected(Integer userId, Collection<Integer> skuIds, Boolean selected) {
// 查询 CartItemDO 列表
List<CartItemDO> itemDOs = cartItemMapper.selectListByUserIdAndSkuIds(userId, skuIds);
if (skuIds.size() != itemDOs.size()) {
throw ServiceExceptionUtil.exception(CARD_ITEM_NOT_FOUND);
}
// 更新选中
cartItemMapper.updateByIds(CollectionUtils.convertSet(itemDOs, CartItemDO::getId),
new CartItemDO().setSelected(selected));
}
/**
* 购物车删除商品
*
* @param userId 用户编号
* @param skuIds 商品 SKU 编号的数组
*/
public void deleteCartItems(Integer userId, List<Integer> skuIds) {
// 查询 CartItemDO 列表
List<CartItemDO> itemDOs = cartItemMapper.selectListByUserIdAndSkuIds(userId, skuIds);
if (CollectionUtils.isEmpty(itemDOs)) {
return;
}
// 批量标记删除
cartItemMapper.deleteBatchIds(CollectionUtils.convertSet(itemDOs, CartItemDO::getId));
}
/**
* 查询用户在购物车中的商品数量
*
* @param userId 用户编号
* @return 商品数量
*/
public Integer sumCartItemQuantity(Integer userId) {
return cartItemMapper.selectSumQuantityByUserId(userId);
}
/**
* 查询用户在购物车种的商品列表
*
* @param queryBO 查询条件 BO
* @return 购物车中商品列表信息
*/
public List<CartItemBO> listCartItems(CartItemListQueryBO queryBO) {
List<CartItemDO> cartItemDOs = cartItemMapper.selectList(queryBO);
return CartConvert.INSTANCE.convertList(cartItemDOs);
}
}
package cn.iocoder.mall.tradeservice.service.cart.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
/**
* 购物车添加购物项 Request DTO
*/
@Data
@Accessors(chain = true)
public class CartItemAddBO {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 商品 SPU 编号
*/
@NotNull(message = "商品 SPU 编号不能为空")
private Integer spuId;
/**
* 商品 SKU 编号
*/
@NotNull(message = "商品 SKU 编号不能为空")
private Integer skuId;
/**
* 数量
*/
@NotNull(message = "数量不能为空")
@Min(message = "数量必须大于 0", value = 1L)
private Integer quantity;
}
package cn.iocoder.mall.tradeservice.service.cart.bo;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 购物车的商品信息 BO
*/
@Data
@Accessors(chain = true)
public class CartItemBO {
// ========= 基础字段 BEGIN =========
/**
* 编号,唯一自增。
*/
private Integer id;
/**
* 是否选中
*/
private Boolean selected;
// ========= 基础字段 END =========
// ========= 买家信息 BEGIN =========
/**
* 用户编号
*/
private Integer userId;
// ========= 买家信息 END =========
// ========= 商品信息 BEGIN =========
/**
* 商品 SPU 编号
*/
private Integer spuId;
/**
* 商品 SKU 编号
*/
private Integer skuId;
/**
* 商品购买数量
*/
private Integer quantity;
// ========= 商品信息 END =========
// ========= 优惠信息 BEGIN =========
// /**
// * 商品营销活动编号
// */
// private Integer activityId;
// /**
// * 商品营销活动类型
// */
// private Integer activityType;
// ========= 优惠信息 END =========
}
package cn.iocoder.mall.tradeservice.service.cart.bo;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
/**
* 购物车的商品信息查询 BO
*/
@Data
@Accessors(chain = true)
public class CartItemListQueryBO {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer userId;
/**
* 是否选中
*/
private Boolean selected;
}
package cn.iocoder.mall.tradeservice.service.order;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.tradeservice.rpc.order.dto.TradeOrderCreateReqDTO;
import cn.iocoder.mall.tradeservice.rpc.order.dto.TradeOrderPageReqDTO;
import cn.iocoder.mall.tradeservice.rpc.order.dto.TradeOrderRespDTO;
import java.util.Collection;
/**
* 交易订单 Service 接口
*/
public interface TradeOrderService {
/**
* 创建交易订单
*
* @param createReqDTO 订单信息
* @return 订单编号
*/
Integer createTradeOrder(TradeOrderCreateReqDTO createReqDTO);
/**
* 获得订单交易
*
* @param tradeOrderId 订单交易编号
* @param fields 额外返回字段,可见 {@link cn.iocoder.mall.tradeservice.enums.order.TradeOrderDetailFieldEnum}
* @return 订单交易
*/
TradeOrderRespDTO getTradeOrder(Integer tradeOrderId, Collection<String> fields);
/**
* 获得订单交易分页
*
* @param pageReqDTO 订单交易分页查询
* @return 订单交易分页结果
*/
PageResult<TradeOrderRespDTO> pageTradeOrder(TradeOrderPageReqDTO pageReqDTO);
/**
* 更新交易订单支付成功
*
* @param tradeOrderId 交易订单编号
* @param payAmount 支付金额
*/
void updateTradeOrderPaySuccess(Integer tradeOrderId, Integer payAmount);
}
spring:
# 数据源配置项
datasource:
url: jdbc:mysql://localhost:3306/mall_trade?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
driver-class-name: com.mysql.jdbc.Driver
username: root
password: zhuyang
# Spring Cloud 配置项
cloud:
nacos:
# Spring Cloud Nacos Discovery 配置项
discovery:
server-addr: localhost:8848 # Nacos 服务器地址
namespace: dev # Nacos 命名空间
# Dubbo 配置项
dubbo:
# Dubbo 注册中心
registry:
# address: spring-cloud://localhost:8848 # 指定 Dubbo 服务注册中心的地址
address: nacos://localhost:8848?namespace=dev # 指定 Dubbo 服务注册中心的地址
spring:
# 数据源配置项
datasource:
url: jdbc:mysql://localhost:3306/mall_trade?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=CTT
driver-class-name: com.mysql.jdbc.Driver
username: root
password: zhuyang
# Spring Cloud 配置项
cloud:
nacos:
# Spring Cloud Nacos Discovery 配置项
discovery:
server-addr: localhost:8848 # Nacos 服务器地址
namespace: dev # Nacos 命名空间
# # Spring 主应用配置
# main:
# lazy-initialization: true # 开启延迟加载,保证本地开发的性能
# Dubbo 配置项
dubbo:
# Dubbo 注册中心
registry:
# address: spring-cloud://localhost:8848 # 指定 Dubbo 服务注册中心的地址
address: nacos://localhost:8848?namespace=dev # 指定 Dubbo 服务注册中心的地址
# Dubbo 服务提供者的配置
provider:
tag: ${DUBBO_TAG} # Dubbo 路由分组
# MyBatis Plus 配置
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 本地开发环境下,多打印 SQL 到控制台
spring:
# Application 的配置项
application:
name: trade-service
# Profile 的配置项
profiles:
active: local
# MyBatis Plus 配置项
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
id-type: auto
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: cn.iocoder.mall.tradeservice.dal.mysql.dataobject
# Dubbo 配置项
dubbo:
# Spring Cloud Alibaba Dubbo 专属配置
cloud:
subscribed-services: '' # 设置订阅的应用列表,默认为 * 订阅所有应用
# Dubbo 提供者的协议
protocol:
name: dubbo
port: -1
# Dubbo 提供服务的扫描基础包
scan:
base-packages: cn.iocoder.mall.tradeservice.rpc
# Dubbo 服务提供者的配置
provider:
filter: -exception
validation: true # 开启 Provider 参数校验
version: 1.0.0 # 服务的版本号
# Dubbo 服务消费者的配置
consumer:
ErrorCodeRpc:
version: 1.0.0
ProductSkuRpc:
version: 1.0.0
UserAddressRpc:
version: 1.0.0
PriceRpc:
version: 1.0.0
CouponCardRpc:
version: 1.0.0
PayTransactionRpc:
version: 1.0.0
# RocketMQ 配置项
rocketmq:
name-server: localhost:9876
producer:
group: ${spring.application.name}-producer-group
# Actuator 监控配置项
management:
server.port: 38084 # 独立端口,避免被暴露出去
endpoints.web.exposure.include: '*' # 暴露所有监控端点
server.port: ${management.server.port} # 设置使用 Actuator 的服务器端口,因为 RPC 服务不需要 Web 端口
# Mall 配置项
mall:
# 错误码配置项对应 ErrorCodeProperties 配置类
error-code:
group: ${spring.application.name}
constants-class: cn.iocoder.mall.tradeservice.enums.OrderErrorCodeConstants
# 业务配置
biz:
pay-app-id: POd4RC6a
pay-expire-time: 120
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>onemall</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-service-project</artifactId>
<packaging>pom</packaging>
<description>用户服务</description>
<modules>
<module>user-service-api</module>
<module>user-service-app</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- onemall 基础 bom 文件 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-dependencies</artifactId>
<version>1.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- 自身项目 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>user-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>user-service-project</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-service-api</artifactId>
<dependencies>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>common-framework</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-core</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
</project>
package cn.iocoder.mall.userservice.enums;
import cn.iocoder.common.framework.exception.ErrorCode;
/**
* 错误码枚举类
*
* user-service 服务,使用 1-002-000-000 段
*/
public interface UserErrorCodeConstants {
// ========== 用户地址 ==========
ErrorCode USER_ADDRESS_NOT_FOUND = new ErrorCode(1001004000, "用户地址不存在!");
ErrorCode USER_GET_ADDRESS_NOT_EXISTS = new ErrorCode(1001004001, "获取的地址不存在!");
}
package cn.iocoder.mall.userservice.rpc.user;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.userservice.rpc.user.dto.UserCreateReqDTO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserPageReqDTO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserRespDTO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserUpdateReqDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
/**
* 用户短信验证码 Rpc 接口
*/
@FeignClient("user-service")
public interface UserFeign {
@GetMapping("/user/manage/getUser")
public CommonResult<UserRespDTO> getUser(@RequestParam("id") Integer id) ;
@GetMapping("/user/manage/listUsers")
public CommonResult<List<UserRespDTO>> listUsers(@RequestParam("userIds") List<Integer> userIds) ;
@PostMapping("/user/manage/createUserIfAbsent")
public CommonResult<UserRespDTO> createUserIfAbsent(@RequestBody UserCreateReqDTO createDTO);
@PostMapping("/user/manage/updateUser")
public CommonResult<Boolean> updateUser(@RequestBody UserUpdateReqDTO updateDTO);
@PostMapping("/user/manage/pageUser")
public CommonResult<PageResult<UserRespDTO>> pageUser(@RequestBody UserPageReqDTO pageDTO);
}
package cn.iocoder.mall.userservice.rpc.user.dto;
import cn.iocoder.common.framework.validator.Mobile;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 用户创建 Request DTO
*/
@Data
@Accessors(chain = true)
public class UserCreateReqDTO implements Serializable {
/**
* 手机号
*/
@NotNull(message = "手机号不能为空")
@Mobile(message = "手机格式不正确")
private String mobile;
/**
* 密码
*
* 允许为空,自动生成
*/
private String password;
/**
* IP
*/
@NotNull(message = "IP 不能为空")
private String ip;
}
package cn.iocoder.mall.userservice.rpc.user.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.common.framework.vo.PageParam;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 用户分页 Request DTO
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class UserPageReqDTO extends PageParam {
/**
* 昵称,模糊匹配
*/
private String nickname;
/**
* 状态
*/
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
}
package cn.iocoder.mall.userservice.rpc.user.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;
/**
* 用户信息 Response DTO
*/
@Data
@Accessors(chain = true)
public class UserRespDTO implements Serializable {
/**
* 用户编号
*/
private Integer id;
/**
* 手机号
*/
private String mobile;
/**
* 昵称
*/
private String nickname;
/**
* 头像
*/
private String avatar;
/**
* 用户状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 注册时间
*/
private Date createTime;
}
package cn.iocoder.mall.userservice.rpc.user.dto;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.common.framework.validator.Mobile;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
/**
* 用户更新 Request DTO
*/
@Data
@Accessors(chain = true)
public class UserUpdateReqDTO implements Serializable {
/**
* 用户编号
*/
@NotNull(message = "用户编号不能为空")
private Integer id;
/**
* 昵称
*/
private String nickname;
/**
* 头像
*/
private String avatar;
/**
* 状态
*/
@InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}")
private Integer status;
/**
* 手机号
*/
@Mobile
private String mobile;
/**
* 加密后的密码
*/
private String password;
}
<?xml version="1.0" encoding="UTF-8"?>
<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">
<parent>
<artifactId>user-service-project</artifactId>
<groupId>cn.iocoder.mall</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>user-service-app</artifactId>
<dependencies>
<!-- RPC 相关 -->
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-dubbo</artifactId>
</dependency>
<dependency>
<!-- 系统服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId>
</dependency>
<dependency>
<!-- 用户服务 -->
<groupId>cn.iocoder.mall</groupId>
<artifactId>user-service-api</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId> <!-- 需要开启 Web 容器,因为 Actuator 需要使用到 -->
</dependency>
<!-- Registry 和 Config 相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.mall</groupId>
<artifactId>mall-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<!-- 使用 spring-boot-maven-plugin 插件打包 -->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package cn.iocoder.mall.userservice;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients(basePackages = {"cn.iocoder.mall.systemservice.rpc"})
@EnableDiscoveryClient
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}
package cn.iocoder.mall.userservice.controller;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.userservice.manager.user.UserManager;
import cn.iocoder.mall.userservice.rpc.user.dto.UserCreateReqDTO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserPageReqDTO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserRespDTO;
import cn.iocoder.mall.userservice.rpc.user.dto.UserUpdateReqDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@RestController
@RequestMapping("/user/manage")
public class UserManageController {
@Autowired
private UserManager userManager;
@GetMapping("getUser")
public CommonResult<UserRespDTO> getUser(@RequestParam("id") Integer id) {
return success(userManager.getUser(id));
}
@GetMapping("listUsers")
public CommonResult<List<UserRespDTO>> listUsers(@RequestParam("userIds") List<Integer> userIds) {
return success(userManager.listUsers(userIds));
}
@PostMapping("createUserIfAbsent")
public CommonResult<UserRespDTO> createUserIfAbsent(@RequestBody UserCreateReqDTO createDTO) {
return success(userManager.createUserIfAbsent(createDTO));
}
@PostMapping("updateUser")
public CommonResult<Boolean> updateUser(@RequestBody UserUpdateReqDTO updateDTO) {
userManager.updateUser(updateDTO);
return success(true);
}
@PostMapping("pageUser")
public CommonResult<PageResult<UserRespDTO>> pageUser(@RequestBody UserPageReqDTO pageDTO) {
return success(userManager.pageUser(pageDTO));
}
}
\ No newline at end of file
package cn.iocoder.mall.userservice.dal.mysql.dataobject.sms;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import cn.iocoder.mall.userservice.enums.sms.UserSmsSceneEnum;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 手机验证码 DO
*
* idx_mobile 索引:基于 {@link #mobile} 字段
*/
@TableName("user_sms_code")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class UserSmsCodeDO extends BaseDO {
/**
* 编号
*/
private Integer id;
/**
* 手机号
*/
private String mobile;
/**
* 验证码
*/
private String code;
/**
* 发送场景
*
* 外键 {@link UserSmsSceneEnum}
*/
private Integer scene;
/**
* 创建 IP
*/
private String createIp;
/**
* 今日发送的第几条
*/
private Integer todayIndex;
/**
* 是否使用
*/
private Boolean used;
/**
* 使用时间
*/
private Date usedTime;
/**
* 使用 IP
*/
private String usedIp;
}
package cn.iocoder.mall.userservice.dal.mysql.dataobject.user;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.mall.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 用户实体
*
* uk_mobile 索引:基于 {@link #mobile} 字段
*/
@TableName(value = "users")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class UserDO extends BaseDO {
/**
* 用户编号
*/
private Integer id;
/**
* 昵称
*/
private String nickname;
/**
* 头像
*/
private String avatar;
/**
* 用户状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 手机
*/
private String mobile;
/**
* 经过加密的密码串
*/
private String password;
/**
* {@link #password} 的盐
*/
private String passwordSalt;
/**
* 注册 IP
*/
private String createIp;
}
package cn.iocoder.mall.userservice.dal.mysql.dataobject.user;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
/**
* 用户三方开放平台授权,例如:QQ / 微博 / 微信等等。
*
* TODO 优化点:需要在改改
*/
@Data
@Accessors(chain = true)
public class UserThirdAuthDO {
/**
* 用户编号
*
* 外键 {@link UserDO#uid}
*/
private Long uid;
// ========== 授权相关字段
/**
* 用户的唯一标识
*/
private String openid;
/**
* 开放平台
*
* @see cn.iocoder.mall.user.api.constant.ThirdPlatformConstant
*/
private Integer platform;
/**
* 访问令牌
*/
private Date accessToken;
/**
* 过期时间
*/
private Date expireTime;
/**
* 刷新令牌
*/
private Date refreshToken;
/**
* 授权范围。一般情况下,使用逗号分隔
*/
private String scopes;
// ========== 基础信息
/**
* 用户昵称
*/
private String nickname;
/**
* 性别
*
* TODO 芋艿,找地方统一枚举。0-未知,1-男,2-女
*/
private Integer gender;
// TODO https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140842
// TODO 芋艿,其他字段,国家/省份/城市/地区等
// TODO 芋艿,头像
// TODO 芋艿,微信独有 unionid
/**
* 统一存储基础信息,使用 JSON 格式化,避免未有效解析的情况。
*/
private String extras;
}
package cn.iocoder.mall.userservice.dal.mysql.mapper.address;
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
import cn.iocoder.mall.userservice.dal.mysql.dataobject.address.UserAddressDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
import javax.annotation.Nullable;
import java.util.List;
@Repository
public interface UserAddressMapper extends BaseMapper<UserAddressDO> {
default List<UserAddressDO> selectListByUserIdAndType(Integer userId, @Nullable Integer type) {
return selectList(new QueryWrapperX<UserAddressDO>().eq("user_id", userId)
.eqIfPresent("type", type));
}
}
package cn.iocoder.mall.userservice.dal.mysql.mapper.sms;
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
import cn.iocoder.mall.userservice.dal.mysql.dataobject.sms.UserSmsCodeDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.springframework.stereotype.Repository;
@Repository
public interface UserSmsCodeMapper extends BaseMapper<UserSmsCodeDO> {
/**
* 获得手机号的最后一个手机验证码
*
* @param mobile 手机号
* @param scene 发送场景,选填
* @return 手机验证码
*/
default UserSmsCodeDO selectLastByMobile(String mobile, Integer scene) {
QueryWrapperX<UserSmsCodeDO> query = new QueryWrapperX<UserSmsCodeDO>()
.eq("mobile", mobile)
.eqIfPresent("scene", scene)
.orderByDesc("id")
.last("limit 1");
return selectOne(query);
}
}
package cn.iocoder.mall.userservice.dal.mysql.mapper.user;
import cn.iocoder.mall.mybatis.core.query.QueryWrapperX;
import cn.iocoder.mall.userservice.dal.mysql.dataobject.user.UserDO;
import cn.iocoder.mall.userservice.service.user.bo.UserPageBO;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.springframework.stereotype.Repository;
@Repository
public interface UserMapper extends BaseMapper<UserDO> {
default UserDO selectByMobile(String mobile) {
return selectOne(new QueryWrapper<UserDO>()
.eq("mobile", mobile)
);
}
default IPage<UserDO> selectPage(UserPageBO pageBO) {
return selectPage(new Page<>(pageBO.getPageNo(), pageBO.getPageSize()),
new QueryWrapperX<UserDO>().likeIfPresent("nickname", pageBO.getNickname())
.eqIfPresent("status", pageBO.getStatus()));
}
}
package cn.iocoder.mall.userservice.manager.sms;
import cn.iocoder.mall.userservice.rpc.sms.dto.UserSendSmsCodeReqDTO;
import cn.iocoder.mall.userservice.rpc.sms.dto.UserVerifySmsCodeReqDTO;
import cn.iocoder.mall.userservice.service.sms.UserSmsCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserSmsCodeManager {
@Autowired
private UserSmsCodeService userSmsCodeService;
public void sendSmsCode(UserSendSmsCodeReqDTO sendSmsCodeDTO) {
// 生成短信验证码
String smsCode = userSmsCodeService.createSmsCode(sendSmsCodeDTO.getMobile(),
sendSmsCodeDTO.getScene(), sendSmsCodeDTO.getIp());
// TODO 调用发送验证码
}
public void verifySmsCode(UserVerifySmsCodeReqDTO verifySmsCodeDTO) {
userSmsCodeService.verifySmsCode(verifySmsCodeDTO.getMobile(), verifySmsCodeDTO.getCode(),
verifySmsCodeDTO.getScene(), verifySmsCodeDTO.getIp());
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论