将 onemall 老代码,统一到归档目录,后续不断迁移移除

This commit is contained in:
YunaiV
2022-06-16 09:06:44 +08:00
parent 64c478a45b
commit 71930d492e
1095 changed files with 0 additions and 16 deletions

View File

@@ -0,0 +1,19 @@
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);
}
}

View File

@@ -0,0 +1,42 @@
package cn.iocoder.mall.shopweb.client.pay;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.payservice.rpc.transaction.PayTransactionFeign;
import cn.iocoder.mall.payservice.rpc.transaction.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Objects;
@Service
public class PayTransactionClient {
@Autowired
private PayTransactionFeign payTransactionFeign;
public PayTransactionRespDTO getPayTransaction(Integer userId, String appId, String orderId) {
CommonResult<PayTransactionRespDTO> getPayTransactionResult = payTransactionFeign.getPayTransaction(new PayTransactionGetReqDTO()
.setAppId(appId).setOrderId(orderId));
getPayTransactionResult.checkError();
if (getPayTransactionResult.getData() == null) {
return null;
}
// 如果用户编号不匹配,则返回 null
return Objects.equals(getPayTransactionResult.getData().getUserId(), userId) ?
getPayTransactionResult.getData() : null;
}
public PayTransactionSubmitRespDTO submitPayTransaction(PayTransactionSubmitReqDTO submitReqDTO) {
CommonResult<PayTransactionSubmitRespDTO> submitPayTransactionResult = payTransactionFeign.submitPayTransaction(submitReqDTO);
submitPayTransactionResult.checkError();
return submitPayTransactionResult.getData();
}
public void updatePayTransactionSuccess(Integer payChannel, String params) {
CommonResult<Boolean> updatePayTransactionSuccessResult = payTransactionFeign.updatePayTransactionSuccess(
new PayTransactionSuccessReqDTO().setPayChannel(payChannel).setParams(params));
updatePayTransactionSuccessResult.checkError();
}
}

View File

@@ -0,0 +1,38 @@
package cn.iocoder.mall.shopweb.client.trade;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.tradeservice.rpc.order.TradeOrderFeign;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Arrays;
@Service
public class TradeOrderClient {
@Autowired
private TradeOrderFeign tradeOrderFeign;
public Integer createTradeOrder(TradeOrderCreateReqDTO createReqDTO) {
CommonResult<Integer> createTradeOrderResult = tradeOrderFeign.createTradeOrder(createReqDTO);
createTradeOrderResult.checkError();
return createTradeOrderResult.getData();
}
public PageResult<TradeOrderRespDTO> pageTradeOrder(TradeOrderPageReqDTO pageReqDTO) {
CommonResult<PageResult<TradeOrderRespDTO>> pageTradeOrderResult = tradeOrderFeign.pageTradeOrder(pageReqDTO);
pageTradeOrderResult.checkError();
return pageTradeOrderResult.getData();
}
public TradeOrderRespDTO getTradeOrder(Integer tradeOrderId, String... fields) {
CommonResult<TradeOrderRespDTO> getTradeOrderResult = tradeOrderFeign.getTradeOrder(tradeOrderId, Arrays.asList(fields));
getTradeOrderResult.checkError();
return getTradeOrderResult.getData();
}
}

View File

@@ -0,0 +1,14 @@
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;
}

View File

@@ -0,0 +1,81 @@
package cn.iocoder.mall.shopweb.controller.pay;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.payservice.enums.PayChannelEnum;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionRespVO;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionSubmitReqVO;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionSubmitRespVO;
import cn.iocoder.mall.shopweb.service.pay.PayTransactionService;
import cn.iocoder.security.annotations.RequiresAuthenticate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api("支付交易 API")
@RestController
@RequestMapping("/pay/transaction")
@Validated
@Slf4j
public class PayTransactionController {
@Autowired
private PayTransactionService payTransactionService;
// TODO 芋艿:这个 API 定义可能不太太合适,应该改成支付交易单号
@GetMapping("/get")
@ApiOperation("获得支付交易")
@ApiImplicitParams({
@ApiImplicitParam(name = "appId", required = true, value = "应用编号", example = "POd4RC6a"),
@ApiImplicitParam(name = "orderId", required = true, value = "订单号", example = "1024"),
})
@RequiresAuthenticate
public CommonResult<PayTransactionRespVO> getPayTransaction(@RequestParam("appId") String appId,
@RequestParam("orderId") String orderId) {
return success(payTransactionService.getPayTransaction(UserSecurityContextHolder.getUserId(), appId, orderId));
}
// TODO 芋艿:这个 API 定义可能不太太合适,应该改成支付交易单号
@PostMapping("/submit")
@ApiOperation("提交支付交易")
@RequiresAuthenticate
public CommonResult<PayTransactionSubmitRespVO> submitPayTransaction(HttpServletRequest request,
PayTransactionSubmitReqVO submitReqVO) {
return success(payTransactionService.submitPayTransaction(submitReqVO, HttpUtil.getIp(request)));
}
@PostMapping(value = "pingxx_pay_success", consumes = MediaType.APPLICATION_JSON_VALUE)
@ApiOperation("Pingxx 支付成功回调")
// @GetMapping(value = "pingxx_pay_success")
public String updatePayTransactionSuccess(HttpServletRequest request) throws IOException {
log.info("[pingxxPaySuccess][被回调]");
// 读取 webhook
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = request.getReader()) {
String line;
while ((line = reader.readLine()) != null) {
sb.append(line);
}
}
// JSONObject bodyObj = JSON.parseObject(sb.toString());
// bodyObj.put("webhookId", bodyObj.remove("id"));
// String body = bodyObj.toString();
payTransactionService.updatePayTransactionSuccess(PayChannelEnum.PINGXX.getId(), sb.toString());
return "success";
}
}

View File

@@ -0,0 +1,39 @@
package cn.iocoder.mall.shopweb.controller.pay.vo.transaction;
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 PayTransactionRespVO {
@ApiModelProperty(value = "交易编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "应用编号", required = true, example = "POd4RC6a")
private String appId;
@ApiModelProperty(value = "订单号不能为空", required = true, example = "1024")
private String orderId;
@ApiModelProperty(value = "商品名", required = true, example = "芋道源码")
private String orderSubject;
@ApiModelProperty(value = "订单商品描述", required = true, example = "绵啾啾的")
private String orderDescription;
@ApiModelProperty(value = "支付金额,单位:分。", required = true, example = "10")
private Integer price;
@ApiModelProperty(value = "订单状态", required = true, example = "1", notes = "参见 PayTransactionStatusEnum 枚举")
private Integer status;
@ApiModelProperty(value = "交易过期时间", required = true)
private Date expireTime;
}

View File

@@ -0,0 +1,31 @@
package cn.iocoder.mall.shopweb.controller.pay.vo.transaction;
import cn.iocoder.common.framework.validator.InEnum;
import cn.iocoder.mall.payservice.enums.PayChannelEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
@ApiModel("支付交易提交 Request VO")
@Data
@Accessors(chain = true)
public class PayTransactionSubmitReqVO {
@ApiModelProperty(value = "应用编号", required = true, example = "POd4RC6a")
@NotEmpty(message = "应用编号不能为空")
private String appId;
@ApiModelProperty(value = "订单号", required = true, example = "1024")
@NotEmpty(message = "订单号不能为空")
private String orderId;
@ApiModelProperty(value = "支付渠道", required = true, example = "1", notes = "参见 PayChannelEnum 枚举")
@InEnum(value = PayChannelEnum.class, message = "支付渠道必须是 {value}")
@NotNull(message = "支付渠道")
private Integer payChannel;
}

View File

@@ -0,0 +1,19 @@
package cn.iocoder.mall.shopweb.controller.pay.vo.transaction;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("支付交易提交 Response VO")
@Data
@Accessors(chain = true)
public class PayTransactionSubmitRespVO {
@ApiModelProperty(value = "支付交易拓展单编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "调用三方平台的响应结果", required = true)
private String invokeResponse;
}

View File

@@ -0,0 +1,4 @@
### /user-address/get-default 成功
GET {{shop-api-base-url}}/product-category/list?pid=0
###

View File

@@ -0,0 +1,36 @@
package cn.iocoder.mall.shopweb.controller.product;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.shopweb.controller.product.vo.category.ProductCategoryRespVO;
import cn.iocoder.mall.shopweb.service.product.ProductCategoryManager;
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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api(tags = "商品分类 API")
@RestController
@RequestMapping("/product-category")
@Validated
public class ProductCategoryController {
@Autowired
private ProductCategoryManager productCategoryManager;
@GetMapping("/list")
@ApiOperation("获得商品分类的列表")
@ApiImplicitParam(name = "pid", value = "父分类编号", required = true, example = "1024")
public CommonResult<List<ProductCategoryRespVO>> listProductCategories(@RequestParam("pid") Integer pid) {
return success(productCategoryManager.listProductCategories(pid));
}
}

View File

@@ -0,0 +1,5 @@
### /product-spu/page 计算商品 SKU 价格
GET http://127.0.0.1:18084/shop-api/product-sku/cal-price?id=33
Content-Type: application/x-www-form-urlencoded
###

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.shopweb.controller.product;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.product.vo.sku.ProductSkuCalcPriceRespVO;
import cn.iocoder.mall.shopweb.service.product.ProductSkuManager;
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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Api(tags = "商品 SKU API")
@RestController
@RequestMapping("/product-sku")
@Validated
public class ProductSkuController {
@Autowired
private ProductSkuManager productSkuManager;
@GetMapping("/cal-price")
@ApiOperation("计算商品 SKU 价格")
@ApiImplicitParam(name = "id", required = true, value = "商品 SKU 编号", example = "1024")
public CommonResult<ProductSkuCalcPriceRespVO> calcProductSkuPrice(@RequestParam("id") Integer id) {
return CommonResult.success(productSkuManager.calcProductSkuPrice(UserSecurityContextHolder.getUserId(), id));
}
}

View File

@@ -0,0 +1,13 @@
### /product-spu/page 成功(全部)
GET http://127.0.0.1:18084/shop-api/product-spu/page?pageNo=1&pageSize=10&keyword=骚气
Content-Type: application/x-www-form-urlencoded
### /product-spu/search-condition 成功
GET http://127.0.0.1:18084/shop-api/product-spu/search-condition?keyword=骚气
Content-Type: application/x-www-form-urlencoded
### /product-spu/get-detail 成功
GET http://127.0.0.1:18084/shop-api/product-spu/get-detail?id=63
Content-Type: application/x-www-form-urlencoded
###

View File

@@ -0,0 +1,52 @@
package cn.iocoder.mall.shopweb.controller.product;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuDetailRespVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuPageReqVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuRespVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuSearchConditionRespVO;
import cn.iocoder.mall.shopweb.service.product.ProductSpuManager;
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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api(tags = "商品 SPU API")
@RestController
@RequestMapping("/product-spu")
@Validated
public class ProductSpuController {
@Autowired
private ProductSpuManager productSpuManager;
@GetMapping("/page")
@ApiOperation("获得商品 SPU 的分页")
public CommonResult<PageResult<ProductSpuRespVO>> pageProductSpu(ProductSpuPageReqVO pageReqVO) {
return success(productSpuManager.pageProductSpu(pageReqVO));
}
@GetMapping("/search-condition")
@ApiOperation("获得商品的搜索条件")
@ApiImplicitParam(name = "keyword", value = "关键字", example = "芋艿")
public CommonResult<ProductSpuSearchConditionRespVO> getProductSpuSearchCondition(
@RequestParam(value = "keyword", required = false) String keyword) {
return success(productSpuManager.getProductSpuSearchCondition(keyword));
}
@GetMapping("/get-detail")
@ApiOperation("获得商品 SPU 的明细,包括 SKU 等等信息")
@ApiImplicitParam(name = "id", required = true, value = "商品 SPU 编号", example = "1024")
public CommonResult<ProductSpuDetailRespVO> getProductSpuDetail(@RequestParam("id") Integer id) {
return success(productSpuManager.getProductSpuDetail(id));
}
}

View File

@@ -0,0 +1,22 @@
package cn.iocoder.mall.shopweb.controller.product.vo.attr;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel(value = "商品规格 KEY + VALUE 对的 Response VO")
@Data
@Accessors(chain = true)
public class ProductAttrKeyValueRespVO {
@ApiModelProperty(value = "规格 KEY 编号", required = true, example = "1")
private Integer attrKeyId;
@ApiModelProperty(value = "规格 KEY 名字", required = true, example = "颜色")
private String attrKeyName;
@ApiModelProperty(value = "规格 VALUE 值", required = true, example = "10")
private Integer attrValueId;
@ApiModelProperty(value = "规格 VALUE 名字", required = true, example = "红色")
private String attrValueName;
}

View File

@@ -0,0 +1,18 @@
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;
}

View File

@@ -0,0 +1,61 @@
package cn.iocoder.mall.shopweb.controller.product.vo.product;
import cn.iocoder.mall.shopweb.controller.product.vo.attr.ProductAttrKeyValueRespVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.List;
@ApiModel(value = "商品 SPU 详细 Response VO", description = "包括 SKU 信息 VO")
@Data
@Accessors(chain = true)
public class ProductSpuDetailRespVO {
@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 = "http://www.iocoder.cn/xxx.jpg", notes = "多个之间,使用逗号分隔")
private List<String> picUrls;
// ========== SKU =========
/**
* SKU 数组
*/
private List<Sku> skus;
@ApiModel("商品 SKU 详细 Response VO")
@Data
@Accessors(chain = true)
public static class Sku implements Serializable {
@ApiModelProperty(value = "sku 编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "SPU 编号", required = true, example = "1")
private Integer spuId;
@ApiModelProperty(value = "图片地址", required = true, example = "http://www.iocoder.cn")
private String picURL;
/**
* 规格值数组
*/
private List<ProductAttrKeyValueRespVO> attrs;
@ApiModelProperty(value = "价格,单位:分", required = true, example = "100")
private Integer price;
@ApiModelProperty(value = "库存数量", required = true, example = "100")
private Integer quantity;
}
}

View File

@@ -0,0 +1,25 @@
package cn.iocoder.mall.shopweb.controller.product.vo.product;
import cn.iocoder.common.framework.vo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
@ApiModel("商品 SPU 分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
public class ProductSpuPageReqVO extends PageParam {
@ApiModelProperty(value = "分类编号", example = "1")
private Integer cid;
@ApiModelProperty(value = "关键字", example = "芋艿")
private String keyword;
@ApiModelProperty(value = "排序字段", example = "buyPrice", notes = "参见 SearchProductPageQuerySortFieldEnum 枚举")
private String sortField;
@ApiModelProperty(value = "排序顺序", example = "1")
private String sortOrder;
}

View File

@@ -0,0 +1,61 @@
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;
}

View File

@@ -0,0 +1,32 @@
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 ProductSpuSearchConditionRespVO {
@ApiModel("商品分类信息")
@Data
@Accessors(chain = true)
public static class Category {
@ApiModelProperty(value = "分类编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "分类名称", required = true, example = "手机")
private String name;
}
/**
* 商品分类数组
*/
private List<Category> categories;
}

View File

@@ -0,0 +1,35 @@
package cn.iocoder.mall.shopweb.controller.product.vo.sku;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("计算商品 SKU 价格结果 VO")
@Data
@Accessors(chain = true)
// TODO 芋艿swagger 注解
public class ProductSkuCalcPriceRespVO {
/**
* 原价格,单位:分。
*/
private Integer originalPrice;
/**
* 最终价格,单位:分。
*/
private Integer buyPrice;
/**
* 满减送促销活动
*
* TODO 芋艿,后续改成 VO
*/
private PromotionActivityRespDTO fullPrivilege;
/**
* 限时折扣促销活动
*
* TODO 芋艿,后续改成 VO
*/
private PromotionActivityRespDTO timeLimitedDiscount;
}

View File

@@ -0,0 +1,33 @@
package cn.iocoder.mall.shopweb.controller.promotion;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.shopweb.controller.promotion.vo.brand.BannerRespVO;
import cn.iocoder.mall.shopweb.service.promotion.BannerManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
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;
@RestController
@RequestMapping("/promotion/banner")
@Api(tags = "Banner API")
@Validated
public class BannerController {
@Autowired
private BannerManager bannerManager;
@GetMapping("/list")
@ApiOperation("获得所有 Banner 列表")
public CommonResult<List<BannerRespVO>> listBanners() {
return success(bannerManager.listBanners());
}
}

View File

@@ -0,0 +1,23 @@
### /promotion/coupon-card/page 优惠劵分页(未使用)
GET {{shop-api-base-url}}/promotion/coupon-card/page?pageNo=1&pageSize=10&status=1
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /promotion/coupon-card/page 优惠劵分页(已使用)
GET {{shop-api-base-url}}/promotion/coupon-card/page?pageNo=1&pageSize=10&status=2
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /promotion/coupon-card/page 优惠劵分页(已过期)
GET {{shop-api-base-url}}/promotion/coupon-card/page?pageNo=1&pageSize=10&status=3
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /promotion/coupon-card/create 用户领取优惠劵(成功)
POST {{shop-api-base-url}}/promotion/coupon-card/create
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
couponTemplateId=1
###

View File

@@ -0,0 +1,43 @@
package cn.iocoder.mall.shopweb.controller.promotion;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardPageReqVO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardRespVO;
import cn.iocoder.mall.shopweb.service.promotion.CouponCardManager;
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.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@RestController
@RequestMapping("/promotion/coupon-card")
@Api(tags = "优惠劵 API")
@Validated
public class CouponCardController {
@Autowired
private CouponCardManager couponCardManager;
@GetMapping("/page")
@ApiOperation("获得优惠劵分页")
@RequiresAuthenticate
public CommonResult<PageResult<CouponCardRespVO>> pageCouponCard(CouponCardPageReqVO pageVO) {
return success(couponCardManager.pageCouponCard(UserSecurityContextHolder.getUserId(),pageVO));
}
@PostMapping("/create")
@ApiOperation("用户领取优惠劵")
@ApiImplicitParam(name = "couponTemplateId", value = "优惠劵模板编号", required = true, example = "1024")
@RequiresAuthenticate
public CommonResult<Integer> createCouponCard(@RequestParam("couponTemplateId") Integer couponTemplateId) {
return success(couponCardManager.createCouponCard(UserSecurityContextHolder.getUserId(), couponTemplateId));
}
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.mall.shopweb.controller.promotion;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.template.CouponTemplateRespVO;
import cn.iocoder.mall.shopweb.service.promotion.CouponTemplateManager;
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.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@RestController
@RequestMapping("/promotion/coupon-template")
@Api(tags = "优惠劵(码)模板 API")
@Validated
public class CouponTemplateController {
@Autowired
private CouponTemplateManager couponTemplateManager;
@GetMapping("/get")
@ApiOperation(value = "优惠劵(码)模板信息")
@ApiImplicitParam(name = "id", value = "优惠劵(码)模板编号", required = true, example = "10")
public CommonResult<CouponTemplateRespVO> getCouponTemplate(@RequestParam("id") Integer id) {
return success(couponTemplateManager.getCouponTemplate(id));
}
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.mall.shopweb.controller.promotion;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuRespVO;
import cn.iocoder.mall.shopweb.service.promotion.ProductRecommendManager;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collection;
import java.util.Map;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@RestController
@RequestMapping("/promotion/product-recommend")
@Api(tags = "商品推荐 API")
@Validated
public class ProductRecommendController {
@Autowired
private ProductRecommendManager productRecommendManager;
@GetMapping("/list")
@ApiOperation("获得所有商品推荐列表")
public CommonResult<Map<Integer, Collection<ProductSpuRespVO>>> list() {
return success(productRecommendManager.listProductRecommends());
}
}

View File

@@ -0,0 +1,18 @@
package cn.iocoder.mall.shopweb.controller.promotion.vo.brand;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
@ApiModel("Banner Response VO")
@Data
@Accessors(chain = true)
public class BannerRespVO {
@ApiModelProperty(value = "跳转链接", required = true, example = "http://www.baidu.com")
private String url;
@ApiModelProperty(value = "图片链接", required = true, example = "http://www.iocoder.cn/01.jpg")
private String picUrl;
}

View File

@@ -0,0 +1,17 @@
package cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card;
import cn.iocoder.common.framework.vo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ApiModel("优惠劵分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class CouponCardPageReqVO extends PageParam {
@ApiModelProperty(value = "优惠码状态", required = true, example = "1", notes = "对应 CouponCardStatusEnum 枚举")
private Integer status;
}

View File

@@ -0,0 +1,51 @@
package cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card;
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 CouponCardRespVO {
// ========== 基本信息 BEGIN ==========
@ApiModelProperty(value = "优惠劵编号", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "模板编号,自增唯一", required = true, example = "1")
private Integer templateId;
@ApiModelProperty(value = "优惠劵名", required = true, example = "大保剑")
private String title;
@ApiModelProperty(value = "优惠码状态", required = true, example = "参见 CouponCardStatusEnum 枚举")
private Integer status;
// ========== 基本信息 END ==========
// ========== 使用规则 BEGIN ==========
@ApiModelProperty(value = "是否设置满多少金额可用,单位:分", required = true)
private Integer priceAvailable;
@ApiModelProperty(value = "固定日期-生效开始时间", required = true)
private Date validStartTime;
@ApiModelProperty(value = "固定日期-生效结束时间", required = true)
private Date validEndTime;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
@ApiModelProperty(value = "优惠类型", required = true, example = "参见 CouponTemplatePreferentialTypeEnum 枚举")
private Integer preferentialType;
@ApiModelProperty(value = "折扣百分比")
private Integer percentOff;
@ApiModelProperty(value = "优惠金额,单位:分")
private Integer priceOff;
@ApiModelProperty(value = "折扣上限")
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 使用情况 BEGIN ==========
// ========== 使用情况 END ==========
}

View File

@@ -0,0 +1,80 @@
package cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.template;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.Date;
import java.util.List;
@ApiModel("优惠劵(码)模板 VO")
@Data
@Accessors(chain = true)
public class CouponTemplateRespVO {
// ========== 基本信息 BEGIN ==========
@ApiModelProperty(value = "模板编号,自增唯一", required = true, example = "1")
private Integer id;
@ApiModelProperty(value = "标题", required = true, example = "优惠劵牛逼")
private String title;
@ApiModelProperty(value = "使用说明", required = true, example = "我只是描述")
private String description;
@ApiModelProperty(value = "优惠劵类型", required = true, example = "1", notes = "参见 CouponTemplateTypeEnum 枚举")
private Integer type;
/**
* 码类型
*
* 1-一卡一码UNIQUE
* 2-通用码GENERAL
*
* 【优惠码独有】 @see CouponCodeDO
*/
private Integer codeType;
@ApiModelProperty(value = "优惠码状态", required = true, example = "1", notes = "参见 CouponTemplateStatusEnum 枚举")
private Integer status;
@ApiModelProperty(value = "每人限领个数", example = "1", notes = "null - 则表示不限制")
private Integer quota;
@ApiModelProperty(value = "发放总量", example = "100")
private Integer total;
// ========== 领取规则 END ==========
// ========== 使用规则 BEGIN ==========
@ApiModelProperty(value = "是否设置满多少金额可用,单位:分", required = true, example = "0", notes = "0-不限制大于0-多少金额可用")
private Integer priceAvailable;
@ApiModelProperty(value = "可用范围的类型", required = true, example = "10", notes = "参见 RangeTypeEnum 枚举")
private Integer rangeType;
@ApiModelProperty(value = "指定商品 / 分类列表,使用逗号分隔商品编号", example = "1,3,5")
private List<Integer> rangeValues;
@ApiModelProperty(value = "生效日期类型", example = "1", notes = "参见 CouponTemplateDateTypeEnum 枚举")
private Integer dateType;
@ApiModelProperty(value = "固定日期-生效开始时间", notes = "当 dateType 为固定日期时,非空")
private Date validStartTime;
@ApiModelProperty(value = "固定日期-生效结束时间", notes = "当 dateType 为固定日期时,非空")
private Date validEndTime;
@ApiModelProperty(value = "领取日期-开始天数", example = "0", notes = "例如0-当天1-次天")
private Integer fixedStartTerm;
@ApiModelProperty(value = "领取日期-结束天数", example = "1", notes = "当 dateType 为领取日期时,非空")
private Integer fixedEndTerm;
// ========== 使用规则 END ==========
// ========== 使用效果 BEGIN ==========
@ApiModelProperty(value = "优惠类型", required = true, example = "1", notes = "参见 PreferentialTypeEnum 枚举")
private Integer preferentialType;
@ApiModelProperty(value = "折扣百分比", example = "80", notes = "当 preferentialType 为现金券时,非空")
private Integer percentOff;
@ApiModelProperty(value = "优惠金额,单位:分", example = "100", notes = "当 preferentialType 为折扣卷时,非空")
private Integer priceOff;
@ApiModelProperty(value = "折扣上限", example = "100", notes = "当 preferentialType 为折扣卷时,非空")
private Integer discountPriceLimit;
// ========== 使用效果 END ==========
// ========== 统计信息 BEGIN ==========
@ApiModelProperty(value = "领取优惠券的次数", required = true)
private Integer statFetchNum;
// ========== 统计信息 END ==========
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@@ -0,0 +1,83 @@
package cn.iocoder.mall.shopweb.controller.trade;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.trade.vo.cart.CartDetailVO;
import cn.iocoder.mall.shopweb.service.trade.CartManager;
import cn.iocoder.security.annotations.RequiresAuthenticate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
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 java.util.Set;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api(tags = "购物车 API")
@RestController
@RequestMapping("/cart")
@Validated
public class CartController {
@Autowired
private CartManager cartManager;
@PostMapping("add")
@ApiOperation("添加商品到购物车")
@ApiImplicitParams({
@ApiImplicitParam(name = "skuId", value = "商品 SKU 编号", required = true, example = "1"),
@ApiImplicitParam(name = "quantity", value = "增加数量", required = true, example = "1024")
})
@RequiresAuthenticate
public CommonResult<Boolean> addCartItem(@RequestParam("skuId") Integer skuId,
@RequestParam("quantity") Integer quantity) {
cartManager.addCartItem(UserSecurityContextHolder.getUserId(), skuId, quantity);
return success(true);
}
@GetMapping("sum-quantity")
@ApiOperation("查询用户在购物车中的商品数量")
@RequiresAuthenticate
public CommonResult<Integer> sumCartItemQuantity() {
return success(cartManager.sumCartItemQuantity(UserSecurityContextHolder.getUserId()));
}
@GetMapping("/get-detail")
@ApiOperation("查询用户的购物车的商品列表")
@RequiresAuthenticate
public CommonResult<CartDetailVO> getCartDetail() {
return success(cartManager.getCartDetail(UserSecurityContextHolder.getUserId()));
}
@PostMapping("update-quantity")
@ApiOperation("更新购物车商品数量")
@ApiImplicitParams({
@ApiImplicitParam(name = "skuId", value = "商品 SKU 编号", required = true, example = "1"),
@ApiImplicitParam(name = "quantity", value = "增加数量", required = true, example = "1024")
})
@RequiresAuthenticate
public CommonResult<Boolean> updateCartItemQuantity(@RequestParam("skuId") Integer skuId,
@RequestParam("quantity") Integer quantity) {
cartManager.updateCartItemQuantity(UserSecurityContextHolder.getUserId(), skuId, quantity);
return success(true);
}
@PostMapping("update-selected")
@ApiOperation("更新购物车商品是否选中")
@ApiImplicitParams({
@ApiImplicitParam(name = "skuIds", value = "商品 SKU 编号数组", required = true, example = "1,3"),
@ApiImplicitParam(name = "selected", value = "是否选中", required = true, example = "true")
})
@RequiresAuthenticate
public CommonResult<Boolean> updateCartItemSelected(@RequestParam("skuIds") Set<Integer> skuIds,
@RequestParam("selected") Boolean selected) {
cartManager.updateCartItemSelected(UserSecurityContextHolder.getUserId(), skuIds, selected);
// 获得目前购物车明细
return success(true);
}
}

View File

@@ -0,0 +1,31 @@
### /trade-order/confirm-create-order-info 基于商品,确认创建订单
GET {{shop-api-base-url}}/trade-order/confirm-create-order-info?skuId=33&quantity=1
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /trade-order/confirm-create-order-info-from-cart 基于购物车,确认创建订单
GET {{shop-api-base-url}}/trade-order/confirm-create-order-info-from-cart
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {{user-access-token}}
### /trade-order/confirm-create-order-info-from-cart 基于商品,创建订单
POST {{shop-api-base-url}}/trade-order/create
Content-Type: application/json
Authorization: Bearer {{user-access-token}}
{
"userAddressId": 19,
"remark": "我是备注",
"orderItems": [
{
"skuId": 3,
"quantity": 1
}
]
}
### /trade-order/page 获得订单交易分页
GET {{shop-api-base-url}}/trade-order/page?status=1&pageNo=1&pageSize=10
Content-Type: application/x-www-form-urlencoded
###

View File

@@ -0,0 +1,84 @@
package cn.iocoder.mall.shopweb.controller.trade;
import cn.iocoder.common.framework.util.HttpUtil;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.security.user.core.context.UserSecurityContextHolder;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.*;
import cn.iocoder.mall.shopweb.service.trade.TradeOrderService;
import cn.iocoder.security.annotations.RequiresAuthenticate;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
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.servlet.http.HttpServletRequest;
import static cn.iocoder.common.framework.vo.CommonResult.success;
@Api(tags = "交易订单 API")
@RestController
@RequestMapping("/trade-order")
@Validated
public class TradeOrderController {
@Autowired
private TradeOrderService tradeOrderService;
@GetMapping("confirm-create-order-info")
@ApiOperation("基于商品,确认创建订单")
@ApiImplicitParams({
@ApiImplicitParam(name = "skuId", required = true, value = "商品 SKU 编号", example = "1024"),
@ApiImplicitParam(name = "quantity", required = true, value = "购买数量", example = "2"),
@ApiImplicitParam(name = "couponCardId", value = "优惠劵编号", example = "1"),
})
@RequiresAuthenticate
public CommonResult<TradeOrderConfirmCreateInfoRespVO> getTradeOrderConfirmCreateInfo(
@RequestParam("skuId") Integer skuId,
@RequestParam("quantity") Integer quantity,
@RequestParam(value = "couponCardId", required = false) Integer couponCardId) {
return success(tradeOrderService.getOrderConfirmCreateInfo(UserSecurityContextHolder.getUserId(), skuId, quantity, couponCardId));
}
@GetMapping("confirm-create-order-info-from-cart")
@ApiOperation("基于购物车,确认创建订单")
@ApiImplicitParam(name = "couponCardId", value = "优惠劵编号", example = "1")
@RequiresAuthenticate
public CommonResult<TradeOrderConfirmCreateInfoRespVO> getTradeOrderConfirmCreateInfoFromCart(
@RequestParam(value = "couponCardId", required = false) Integer couponCardId) {
return success(tradeOrderService.getOrderConfirmCreateInfoFromCart(UserSecurityContextHolder.getUserId(), couponCardId));
}
@PostMapping("create")
@ApiOperation("基于商品,创建订单")
@RequiresAuthenticate
public CommonResult<Integer> createTradeOrder(@RequestBody TradeOrderCreateReqVO createReqVO,
HttpServletRequest servletRequest) {
return success(tradeOrderService.createTradeOrder(UserSecurityContextHolder.getUserId(),
HttpUtil.getIp(servletRequest), createReqVO));
}
@GetMapping("create-from-cart")
@ApiOperation("基于购物车,创建订单")
@RequiresAuthenticate
public CommonResult<Integer> createTradeOrder(TradeOrderCreateFromCartReqVO createReqVO) {
return null;
}
@GetMapping("/get")
@ApiOperation("获得交易订单")
@ApiImplicitParam(name = "tradeOrderId", value = "交易订单编号", required = true)
public CommonResult<TradeOrderRespVO> getTradeOrder(@RequestParam("tradeOrderId") Integer tradeOrderId) {
return success(tradeOrderService.getTradeOrder(tradeOrderId));
}
@GetMapping("/page")
@ApiOperation("获得订单交易分页")
public CommonResult<PageResult<TradeOrderRespVO>> pageTradeOrder(TradeOrderPageReqVO pageVO) {
return success(tradeOrderService.pageTradeOrder(UserSecurityContextHolder.getUserId(), pageVO));
}
}

View File

@@ -0,0 +1,213 @@
package cn.iocoder.mall.shopweb.controller.trade.vo.cart;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.attr.ProductAttrKeyValueRespVO;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel(value = "用户的购物车明细 Response VO") // TODO 芋艿swagger 文档完善
@Data
@Accessors(chain = true)
public class CartDetailVO {
/**
* 商品分组数组
*/
private List<ItemGroup> itemGroups;
/**
* 费用
*/
private Fee fee;
/**
* 商品分组
*
* 多个商品,参加同一个活动,从而形成分组。
*/
@Data
@Accessors(chain = true)
public static class ItemGroup {
/**
* 优惠活动
*/
private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
/**
* 促销减少的金额
*
* 1. 若未参与促销活动,或不满足促销条件,返回 null
* 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。
*/
private Integer activityDiscountTotal;
/**
* 商品数组
*/
private List<Sku> items;
}
@Data
@Accessors(chain = true)
public static class Sku {
// SKU 自带信息
/**
* sku 编号
*/
private Integer id;
/**
* SPU 信息
*/
private Spu spu;
/**
* 图片地址
*/
private String picURL;
/**
* 规格值数组
*/
private List<ProductAttrKeyValueRespVO> attrs; // TODO 后面改下
/**
* 价格,单位:分
*/
private Integer price;
/**
* 库存数量
*/
private Integer quantity;
// 非 SKU 自带信息
/**
* 购买数量
*/
private Integer buyQuantity;
/**
* 是否选中
*/
private Boolean selected;
/**
* 优惠活动
*/
private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
/**
* 原始单价,单位:分。
*/
private Integer originPrice;
/**
* 购买单价,单位:分
*/
private Integer buyPrice;
/**
* 最终价格,单位:分。
*/
private Integer presentPrice;
/**
* 购买总金额,单位:分
*
* 用途类似 {@link #presentTotal}
*/
private Integer buyTotal;
/**
* 优惠总金额,单位:分。
*/
private Integer discountTotal;
/**
* 最终总金额,单位:分。
*
* 注意presentPrice * quantity 不一定等于 presentTotal 。
* 因为,存在无法整除的情况。
* 举个例子presentPrice = 8.33 quantity = 3 的情况presentTotal 有可能是 24.99 ,也可能是 25 。
* 所以,需要存储一个该字段。
*/
private Integer presentTotal;
}
@Data
@Accessors(chain = true)
public static class Spu {
/**
* SPU 编号
*/
private Integer id;
// ========== 基本信息 =========
/**
* SPU 名字
*/
private String name;
/**
* 分类编号
*/
private Integer cid;
/**
* 商品主图地址
*
* 数组,以逗号分隔
*
* 建议尺寸800*800像素你可以拖拽图片调整顺序最多上传15张
*/
private List<String> picUrls;
}
/**
* 费用(合计)
*/
@Data
@Accessors(chain = true)
public static class Fee {
/**
* 购买总价
*/
private Integer buyTotal;
/**
* 优惠总价
*
* 注意,满多少元包邮,不算在优惠中。
*/
private Integer discountTotal;
/**
* 邮费
*/
private Integer postageTotal;
/**
* 最终价格
*
* 计算公式 = 总价 - 优惠总价 + 邮费
*/
private Integer presentTotal;
public Fee() {
}
public Fee(Integer buyTotal, Integer discountTotal, Integer postageTotal, Integer presentTotal) {
this.buyTotal = buyTotal;
this.discountTotal = discountTotal;
this.postageTotal = postageTotal;
this.presentTotal = presentTotal;
}
}
/**
* 邮费信息 TODO 芋艿,未完成
*/
@Data
@Accessors(chain = true)
public static class Postage {
/**
* 需要满足多少钱,可以包邮。单位:分
*/
private Integer threshold;
}
}

View File

@@ -0,0 +1,216 @@
package cn.iocoder.mall.shopweb.controller.trade.vo.order;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableRespDTO;
import cn.iocoder.mall.shopweb.controller.trade.vo.cart.CartDetailVO;
import cn.iocoder.mall.shopweb.controller.product.vo.attr.ProductAttrKeyValueRespVO;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@ApiModel(value = "订单确认创建信息 Response VO") // TODO 芋艿swagger 文档完善
@Data
@Accessors(chain = true) // TODO 芋艿:和 CartDetailVO、ProductSkuCalcPriceRespVO 有点重复,后续要优化下;
public class TradeOrderConfirmCreateInfoRespVO {
/**
* 商品分组数组
*/
private List<ItemGroup> itemGroups;
/**
* 费用
*/
private Fee fee;
/**
* 优惠劵列表 TODO 芋艿,后续改改
*/
private List<CouponCardAvailableRespDTO> couponCards;
/**
* 商品分组
*
* 多个商品,参加同一个活动,从而形成分组。
*/
@Data
@Accessors(chain = true)
public static class ItemGroup {
/**
* 优惠活动
*/
private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
/**
* 促销减少的金额
*
* 1. 若未参与促销活动,或不满足促销条件,返回 null
* 2. 该金额,已经分摊到每个 Item 的 discountTotal ,需要注意。
*/
private Integer activityDiscountTotal;
/**
* 商品数组
*/
private List<CartDetailVO.Sku> items;
}
@Data
@Accessors(chain = true)
public static class Sku {
// SKU 自带信息
/**
* sku 编号
*/
private Integer id;
/**
* SPU 信息
*/
private Spu spu;
/**
* 图片地址
*/
private String picURL;
/**
* 规格值数组
*/
private List<ProductAttrKeyValueRespVO> attrs; // TODO 后面改下
/**
* 价格,单位:分
*/
private Integer price;
/**
* 库存数量
*/
private Integer quantity;
// 非 SKU 自带信息
/**
* 购买数量
*/
private Integer buyQuantity;
/**
* 优惠活动
*/
private PromotionActivityRespDTO activity; // TODO 芋艿,偷懒
/**
* 原始单价,单位:分。
*/
private Integer originPrice;
/**
* 购买单价,单位:分
*/
private Integer buyPrice;
/**
* 最终价格,单位:分。
*/
private Integer presentPrice;
/**
* 购买总金额,单位:分
*
* 用途类似 {@link #presentTotal}
*/
private Integer buyTotal;
/**
* 优惠总金额,单位:分。
*/
private Integer discountTotal;
/**
* 最终总金额,单位:分。
*
* 注意presentPrice * quantity 不一定等于 presentTotal 。
* 因为,存在无法整除的情况。
* 举个例子presentPrice = 8.33 quantity = 3 的情况presentTotal 有可能是 24.99 ,也可能是 25 。
* 所以,需要存储一个该字段。
*/
private Integer presentTotal;
}
@Data
@Accessors(chain = true)
public static class Spu {
/**
* SPU 编号
*/
private Integer id;
// ========== 基本信息 =========
/**
* SPU 名字
*/
private String name;
/**
* 分类编号
*/
private Integer cid;
/**
* 商品主图地址
*
* 数组,以逗号分隔
*
* 建议尺寸800*800像素你可以拖拽图片调整顺序最多上传15张
*/
private List<String> picUrls;
}
/**
* 费用(合计)
*/
@Data
@Accessors(chain = true)
public static class Fee {
/**
* 购买总价
*/
private Integer buyTotal;
/**
* 优惠总价
*
* 注意,满多少元包邮,不算在优惠中。
*/
private Integer discountTotal;
/**
* 邮费
*/
private Integer postageTotal;
/**
* 最终价格
*
* 计算公式 = 总价 - 优惠总价 + 邮费
*/
private Integer presentTotal;
public Fee() {
}
public Fee(Integer buyTotal, Integer discountTotal, Integer postageTotal, Integer presentTotal) {
this.buyTotal = buyTotal;
this.discountTotal = discountTotal;
this.postageTotal = postageTotal;
this.presentTotal = presentTotal;
}
}
/**
* 邮费信息 TODO 芋艿,未完成
*/
@Data
@Accessors(chain = true)
public static class Postage {
/**
* 需要满足多少钱,可以包邮。单位:分
*/
private Integer threshold;
}
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.shopweb.controller.trade.vo.order;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotNull;
@ApiModel(value = "创建交易订单 VO基于购物车")
@Data
@Accessors(chain = true)
public class TradeOrderCreateFromCartReqVO {
@ApiModelProperty(name = "收件地址编号", required = true, example = "1")
@NotNull(message = "用户地址不能为空")
private Integer userAddressId;
@ApiModelProperty(name = "优惠劵编号", example = "1024")
private Integer couponCardId;
@ApiModelProperty(name = "备注", example = "1024")
private String remark;
}

View File

@@ -0,0 +1,45 @@
package cn.iocoder.mall.shopweb.controller.trade.vo.order;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.List;
@ApiModel(value = "创建交易订单 VO基于商品")
@Data
@Accessors(chain = true)
public class TradeOrderCreateReqVO {
@ApiModelProperty(name = "收件地址编号", required = true, example = "1")
@NotNull(message = "收件地址不能为空")
private Integer userAddressId;
@ApiModelProperty(name = "优惠劵编号", example = "1024")
private Integer couponCardId;
@ApiModelProperty(name = "备注", example = "1024")
private String remark;
/**
* 订单商品项列表
*/
@NotNull(message = "必须选择购买的商品")
private List<OrderItem> orderItems;
@ApiModel(value = "订单商品项")
@Data
@Accessors(chain = true)
public static class OrderItem {
@ApiModelProperty(name = "商品 SKU 编号", required = true, example = "111")
@NotNull(message = "商品 SKU 编号不能为空")
private Integer skuId;
@ApiModelProperty(name = "商品 SKU 购买数量", required = true, example = "1024")
@NotNull(message = "商品 SKU 购买数量不能为空")
@Min(value = 1, message = "商品 SKU 购买数量必须大于 0")
private Integer quantity;
}
}

View File

@@ -0,0 +1,53 @@
package cn.iocoder.mall.shopweb.controller.trade.vo.order;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
@ApiModel("交易订单项 Response VO")
@Data
public class TradeOrderItemRespVO {
@ApiModelProperty(value = "id自增长", required = true)
private Integer id;
@ApiModelProperty(value = "订单编号", required = true)
private Integer orderId;
@ApiModelProperty(value = "订单项状态", required = true)
private Integer status;
@ApiModelProperty(value = "商品 SKU 编号", required = true)
private Integer skuId;
@ApiModelProperty(value = "商品 SPU 编号", required = true)
private Integer spuId;
@ApiModelProperty(value = "商品名字", required = true)
private String skuName;
@ApiModelProperty(value = "图片名字", required = true)
private String skuImage;
@ApiModelProperty(value = "商品数量", required = true)
private Integer quantity;
@ApiModelProperty(value = "原始单价,单位:分", required = true)
private Integer originPrice;
@ApiModelProperty(value = "购买单价,单位:分", required = true)
private Integer buyPrice;
@ApiModelProperty(value = "最终价格,单位:分", required = true)
private Integer presentPrice;
@ApiModelProperty(value = "购买总金额,单位:分", required = true)
private Integer buyTotal;
@ApiModelProperty(value = "优惠总金额,单位:分", required = true)
private Integer discountTotal;
@ApiModelProperty(value = "最终总金额,单位:分", required = true)
private Integer presentTotal;
@ApiModelProperty(value = "退款总金额,单位:分", required = true)
private Integer refundTotal;
@ApiModelProperty(value = "物流id")
private Integer logisticsId;
@ApiModelProperty(value = "售后状态", required = true)
private Integer afterSaleStatus;
@ApiModelProperty(value = "售后订单编号")
private Integer afterSaleOrderId;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
}

View File

@@ -0,0 +1,17 @@
package cn.iocoder.mall.shopweb.controller.trade.vo.order;
import cn.iocoder.common.framework.vo.PageParam;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@ApiModel("交易订单分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class TradeOrderPageReqVO extends PageParam {
@ApiModelProperty(value = "订单状态", example = "1", notes = "参见 TradeOrderStatusEnum 枚举")
private Integer orderStatus;
}

View File

@@ -0,0 +1,70 @@
package cn.iocoder.mall.shopweb.controller.trade.vo.order;
import lombok.*;
import io.swagger.annotations.*;
import java.util.*;
@ApiModel("订单交易 Response VO")
@Data
public class TradeOrderRespVO {
@ApiModelProperty(value = "订单编号", required = true)
private Integer id;
@ApiModelProperty(value = "用户编号", required = true)
private Integer userId;
@ApiModelProperty(value = "订单单号", required = true)
private String orderNo;
@ApiModelProperty(value = "订单状态", required = true)
private Integer orderStatus;
@ApiModelProperty(value = "备注")
private String remark;
@ApiModelProperty(value = "订单结束时间")
private Date endTime;
@ApiModelProperty(value = "订单金额(总金额),单位:分", required = true)
private Integer buyPrice;
@ApiModelProperty(value = "优惠总金额,单位:分", required = true)
private Integer discountPrice;
@ApiModelProperty(value = "物流金额,单位:分", required = true)
private Integer logisticsPrice;
@ApiModelProperty(value = "最终金额,单位:分", required = true)
private Integer presentPrice;
@ApiModelProperty(value = "支付金额,单位:分", required = true)
private Integer payPrice;
@ApiModelProperty(value = "退款金额,单位:分", required = true)
private Integer refundPrice;
@ApiModelProperty(value = "付款时间")
private Date payTime;
@ApiModelProperty(value = "支付订单编号")
private Integer payTransactionId;
@ApiModelProperty(value = "支付渠道")
private Integer payChannel;
@ApiModelProperty(value = "配送类型", required = true)
private Integer deliveryType;
@ApiModelProperty(value = "发货时间")
private Date deliveryTime;
@ApiModelProperty(value = "收货时间")
private Date receiveTime;
@ApiModelProperty(value = "收件人名称", required = true)
private String receiverName;
@ApiModelProperty(value = "手机号", required = true)
private String receiverMobile;
@ApiModelProperty(value = "地区编码", required = true)
private Integer receiverAreaCode;
@ApiModelProperty(value = "收件详细地址", required = true)
private String receiverDetailAddress;
@ApiModelProperty(value = "售后状态", required = true)
private Integer afterSaleStatus;
@ApiModelProperty(value = "优惠劵编号")
private Integer couponCardId;
@ApiModelProperty(value = "创建时间", required = true)
private Date createTime;
/**
* 订单项数组
*
* // TODO 芋艿,后续考虑怎么优化下,目前是内嵌了别的 dto
*/
private List<TradeOrderItemRespVO> orderItems;
}

View File

@@ -0,0 +1,19 @@
### /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
###

View File

@@ -0,0 +1,56 @@
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)));
}
}

View File

@@ -0,0 +1,6 @@
### /user-address/get-default 成功
GET {{shop-api-base-url}}/user-address/get-default
Authorization: Bearer {{user-access-token}}
dubbo-tag: {{dubboTag}}
###

View File

@@ -0,0 +1,80 @@
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()));
}
}

View File

@@ -0,0 +1,50 @@
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);
}
}

View File

@@ -0,0 +1,33 @@
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;
}

View File

@@ -0,0 +1,31 @@
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;
}

View File

@@ -0,0 +1,36 @@
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;
}

View File

@@ -0,0 +1,22 @@
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;
}

View File

@@ -0,0 +1,30 @@
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;
}

View File

@@ -0,0 +1,23 @@
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;
}

View File

@@ -0,0 +1,22 @@
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;
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.shopweb.convert.pay;
import cn.iocoder.mall.payservice.rpc.transaction.dto.PayTransactionRespDTO;
import cn.iocoder.mall.payservice.rpc.transaction.dto.PayTransactionSubmitReqDTO;
import cn.iocoder.mall.payservice.rpc.transaction.dto.PayTransactionSubmitRespDTO;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionRespVO;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionSubmitReqVO;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionSubmitRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface PayTransactionConvert {
PayTransactionConvert INSTANCE = Mappers.getMapper(PayTransactionConvert.class);
PayTransactionSubmitReqDTO convert(PayTransactionSubmitReqVO bean);
PayTransactionSubmitRespVO convert(PayTransactionSubmitRespDTO bean);
PayTransactionRespVO convert(PayTransactionRespDTO bean);
}

View File

@@ -0,0 +1,17 @@
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);
}

View File

@@ -0,0 +1,20 @@
package cn.iocoder.mall.shopweb.convert.product;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.sku.ProductSkuCalcPriceRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ProductSkuConvert {
ProductSkuConvert INSTANCE = Mappers.getMapper(ProductSkuConvert.class);
default ProductSkuCalcPriceRespVO convert(PriceProductCalcRespDTO.Item item,
PromotionActivityRespDTO fullPrivilege, PromotionActivityRespDTO timeLimitedDiscount) {
return new ProductSkuCalcPriceRespVO().setOriginalPrice(item.getOriginPrice()).setBuyPrice(item.getBuyPrice())
.setFullPrivilege(fullPrivilege).setTimeLimitedDiscount(timeLimitedDiscount);
}
}

View File

@@ -0,0 +1,41 @@
package cn.iocoder.mall.shopweb.convert.product;
import cn.iocoder.common.framework.util.StringUtils;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.common.framework.vo.SortingField;
import cn.iocoder.mall.productservice.rpc.category.dto.ProductCategoryRespDTO;
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuDetailRespDTO;
import cn.iocoder.mall.searchservice.rpc.product.dto.SearchProductPageReqDTO;
import cn.iocoder.mall.searchservice.rpc.product.dto.SearchProductRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuDetailRespVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuPageReqVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuRespVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuSearchConditionRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.Collections;
import java.util.List;
@Mapper
public interface ProductSpuConvert {
ProductSpuConvert INSTANCE = Mappers.getMapper(ProductSpuConvert.class);
default SearchProductPageReqDTO convert(ProductSpuPageReqVO bean) {
SearchProductPageReqDTO reqDTO = new SearchProductPageReqDTO()
.setCid(bean.getCid()).setKeyword(bean.getKeyword());
reqDTO.setPageNo(bean.getPageNo()).setPageSize(bean.getPageSize());
if (StringUtils.hasText(bean.getSortField()) && StringUtils.hasText(bean.getSortOrder())) {
reqDTO.setSorts(Collections.singletonList(new SortingField(bean.getSortField(), bean.getSortOrder())));
}
return reqDTO;
}
PageResult<ProductSpuRespVO> convertPage(PageResult<SearchProductRespDTO> page);
List<ProductSpuSearchConditionRespVO.Category> convertList(List<ProductCategoryRespDTO> list);
ProductSpuDetailRespVO convert(ProductSpuDetailRespDTO bean);
}

View File

@@ -0,0 +1,17 @@
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);
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.shopweb.convert.promotion;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardPageReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardPageReqVO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
* 优惠劵 Convert
*/
@Mapper
public interface CouponCardConvert {
CouponCardConvert INSTANCE = Mappers.getMapper(CouponCardConvert.class);
PageResult<CouponCardRespVO> convertPage(PageResult<CouponCardRespDTO> page);
CouponCardPageReqDTO convert(CouponCardPageReqVO bean);
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.mall.shopweb.convert.promotion;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponTemplateRespDTO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.template.CouponTemplateRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CouponTemplateConvert {
CouponTemplateConvert INSTANCE = Mappers.getMapper(CouponTemplateConvert.class);
CouponTemplateRespVO convert(CouponTemplateRespDTO bean);
}

View File

@@ -0,0 +1,15 @@
package cn.iocoder.mall.shopweb.convert.promotion;
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuRespVO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
@Mapper
public interface ProductRecommendConvert {
ProductRecommendConvert INSTANCE = Mappers.getMapper(ProductRecommendConvert.class);
ProductSpuRespVO convert(ProductSpuRespDTO bean);
}

View File

@@ -0,0 +1,21 @@
package cn.iocoder.mall.shopweb.convert.trade;
import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuRespDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.trade.vo.cart.CartDetailVO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface CartConvert {
CartConvert INSTANCE = Mappers.getMapper(CartConvert.class);
CartDetailVO.Fee convert(PriceProductCalcRespDTO.Fee bean);
@Mapping(source = "sku.id", target = "id")
CartDetailVO.Sku convert(PriceProductCalcRespDTO.Item item, ProductSkuRespDTO sku, PromotionActivityRespDTO activity);
}

View File

@@ -0,0 +1,48 @@
package cn.iocoder.mall.shopweb.convert.trade;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.TradeOrderConfirmCreateInfoRespVO;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.TradeOrderCreateReqVO;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.TradeOrderPageReqVO;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.TradeOrderRespVO;
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 org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Mapper
public interface TradeOrderConvert {
TradeOrderConvert INSTANCE = Mappers.getMapper(TradeOrderConvert.class);
TradeOrderConfirmCreateInfoRespVO.Fee convert(PriceProductCalcRespDTO.Fee bean);
default List<CouponCardAvailableListReqDTO.Item> convertList(List<PriceProductCalcRespDTO.ItemGroup> itemGroups) {
List<CouponCardAvailableListReqDTO.Item> items = new ArrayList<>();
itemGroups.forEach(itemGroup -> items.addAll(itemGroup.getItems().stream().map(
item -> new CouponCardAvailableListReqDTO.Item()
.setSpuId(item.getSpuId())
.setSkuId(item.getSkuId())
.setCid(item.getCid())
.setPrice(item.getBuyPrice())
.setQuantity(item.getBuyQuantity()))
.collect(Collectors.toList())));
return items;
}
TradeOrderCreateReqDTO convert(TradeOrderCreateReqVO bean);
TradeOrderPageReqDTO convert(TradeOrderPageReqVO bean);
PageResult<TradeOrderRespVO> convertPage(PageResult<TradeOrderRespDTO> page);
TradeOrderRespVO convert(TradeOrderRespDTO bean);
}

View File

@@ -0,0 +1,25 @@
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);
}

View File

@@ -0,0 +1,27 @@
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);
}

View File

@@ -0,0 +1,15 @@
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);
}

View File

@@ -0,0 +1,32 @@
package cn.iocoder.mall.shopweb.service.pay;
import cn.iocoder.mall.payservice.rpc.transaction.dto.PayTransactionSubmitRespDTO;
import cn.iocoder.mall.shopweb.client.pay.PayTransactionClient;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionRespVO;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionSubmitReqVO;
import cn.iocoder.mall.shopweb.controller.pay.vo.transaction.PayTransactionSubmitRespVO;
import cn.iocoder.mall.shopweb.convert.pay.PayTransactionConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PayTransactionService {
@Autowired
private PayTransactionClient payTransactionClient;
public PayTransactionSubmitRespVO submitPayTransaction(PayTransactionSubmitReqVO submitReqVO, String ip) {
PayTransactionSubmitRespDTO submitPayTransaction = payTransactionClient.submitPayTransaction(
PayTransactionConvert.INSTANCE.convert(submitReqVO).setCreateIp(ip));
return PayTransactionConvert.INSTANCE.convert(submitPayTransaction);
}
public PayTransactionRespVO getPayTransaction(Integer userId, String appId, String orderId) {
return PayTransactionConvert.INSTANCE.convert(payTransactionClient.getPayTransaction(userId, appId, orderId));
}
public void updatePayTransactionSuccess(Integer payChannel, String params) {
payTransactionClient.updatePayTransactionSuccess(payChannel, params);
}
}

View File

@@ -0,0 +1,34 @@
package cn.iocoder.mall.shopweb.service.product;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.productservice.rpc.category.ProductCategoryFeign;
import cn.iocoder.mall.productservice.rpc.category.dto.ProductCategoryListQueryReqDTO;
import cn.iocoder.mall.productservice.rpc.category.dto.ProductCategoryRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.category.ProductCategoryRespVO;
import cn.iocoder.mall.shopweb.convert.product.ProductCategoryConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
/**
* Product 分类 Manager
*/
@Service
@Validated
public class ProductCategoryManager {
@Autowired
private ProductCategoryFeign productCategoryFeign;
public List<ProductCategoryRespVO> listProductCategories(Integer pid) {
CommonResult<List<ProductCategoryRespDTO>> listProductCategoriesResult = productCategoryFeign.listProductCategories(
new ProductCategoryListQueryReqDTO().setPid(pid).setStatus(CommonStatusEnum.ENABLE.getValue()));
listProductCategoriesResult.checkError();
return ProductCategoryConvert.INSTANCE.convertList(listProductCategoriesResult.getData());
}
}

View File

@@ -0,0 +1,60 @@
package cn.iocoder.mall.shopweb.service.product;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.promotion.api.rpc.activity.PromotionActivityFeign;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.PriceFeign;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.sku.ProductSkuCalcPriceRespVO;
import cn.iocoder.mall.shopweb.convert.product.ProductSkuConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collections;
import java.util.List;
/**
* 商品 SKU Manager
*/
@Service
@Validated
public class ProductSkuManager {
@Autowired
private PriceFeign priceFeign;
@Autowired
private PromotionActivityFeign promotionActivityFeign;
/**
* 计算商品 SKU 价格
*
* @param userId 用户编号
* @param skuId 商品 SKU 编号
* @return SKU 价格明细
*/
public ProductSkuCalcPriceRespVO calcProductSkuPrice(Integer userId, Integer skuId) {
CommonResult<PriceProductCalcRespDTO> calcProductPriceResult = priceFeign.calcProductPrice(new PriceProductCalcReqDTO().setUserId(userId)
.setItems(Collections.singletonList(new PriceProductCalcReqDTO.Item(skuId, 1, true))));
calcProductPriceResult.checkError();
// 拼接结果
PriceProductCalcRespDTO.ItemGroup itemGroup = calcProductPriceResult.getData().getItemGroups().get(0);
// 1. 加载 满减送 促销活动
PromotionActivityRespDTO fullPrivilege = itemGroup.getActivityId() != null ? this.getPromotionActivity(itemGroup.getActivityId()) : null;
// 2. 加载 限时折扣 促销活动
PriceProductCalcRespDTO.Item item = itemGroup.getItems().get(0);
PromotionActivityRespDTO timeLimitedDiscount = item.getActivityId() != null ? this.getPromotionActivity(item.getActivityId()) : null;
// 3. 最终组装
return ProductSkuConvert.INSTANCE.convert(item, fullPrivilege, timeLimitedDiscount);
}
private PromotionActivityRespDTO getPromotionActivity(Integer activityId) {
CommonResult<List<PromotionActivityRespDTO>> listPromotionActivitiesResult = promotionActivityFeign.listPromotionActivities(
new PromotionActivityListReqDTO().setActiveIds(Collections.singleton(activityId)));
listPromotionActivitiesResult.checkError();
return listPromotionActivitiesResult.getData().get(0);
}
}

View File

@@ -0,0 +1,77 @@
package cn.iocoder.mall.shopweb.service.product;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.productservice.enums.spu.ProductSpuDetailFieldEnum;
import cn.iocoder.mall.productservice.rpc.category.ProductCategoryFeign;
import cn.iocoder.mall.productservice.rpc.category.dto.ProductCategoryRespDTO;
import cn.iocoder.mall.productservice.rpc.spu.ProductSpuFeign;
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuDetailRespDTO;
import cn.iocoder.mall.searchservice.enums.product.SearchProductConditionFieldEnum;
import cn.iocoder.mall.searchservice.rpc.product.SearchProductFeign;
import cn.iocoder.mall.searchservice.rpc.product.dto.SearchProductConditionReqDTO;
import cn.iocoder.mall.searchservice.rpc.product.dto.SearchProductConditionRespDTO;
import cn.iocoder.mall.searchservice.rpc.product.dto.SearchProductRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuDetailRespVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuPageReqVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuRespVO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuSearchConditionRespVO;
import cn.iocoder.mall.shopweb.convert.product.ProductSpuConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 商品 SPU Manager
*/
@Service
@Validated
public class ProductSpuManager {
@Autowired
private SearchProductFeign searchProductFeign;
@Autowired
private ProductCategoryFeign productCategoryFeign;
@Autowired
private ProductSpuFeign productSpuFeign;
public PageResult<ProductSpuRespVO> pageProductSpu(ProductSpuPageReqVO pageReqVO) {
CommonResult<PageResult<SearchProductRespDTO>> pageResult =
searchProductFeign.pageSearchProduct(ProductSpuConvert.INSTANCE.convert(pageReqVO));
pageResult.checkError();
return ProductSpuConvert.INSTANCE.convertPage(pageResult.getData());
}
public ProductSpuSearchConditionRespVO getProductSpuSearchCondition(String keyword) {
// 获得搜索条件
CommonResult<SearchProductConditionRespDTO> getSearchProductConditionResult =
searchProductFeign.getSearchProductCondition(new SearchProductConditionReqDTO().setKeyword(keyword)
.setFields(Collections.singletonList(SearchProductConditionFieldEnum.CATEGORY.getField())));
getSearchProductConditionResult.checkError();
// 拼接结果
ProductSpuSearchConditionRespVO conditionRespVO = new ProductSpuSearchConditionRespVO();
if (CollectionUtils.isEmpty(getSearchProductConditionResult.getData().getCids())) {
conditionRespVO.setCategories(Collections.emptyList());
} else {
CommonResult<List<ProductCategoryRespDTO>> listProductCategoriesResult =
productCategoryFeign.listProductCategoriesByIds(getSearchProductConditionResult.getData().getCids());
listProductCategoriesResult.checkError();
conditionRespVO.setCategories(ProductSpuConvert.INSTANCE.convertList(listProductCategoriesResult.getData()));
}
return conditionRespVO;
}
public ProductSpuDetailRespVO getProductSpuDetail(Integer id) {
CommonResult<ProductSpuDetailRespDTO> getProductSpuDetailResult = productSpuFeign.getProductSpuDetail(id,
Arrays.asList(ProductSpuDetailFieldEnum.SKU.getField(), ProductSpuDetailFieldEnum.ATTR.getField()));
getProductSpuDetailResult.checkError();
return ProductSpuConvert.INSTANCE.convert(getProductSpuDetailResult.getData());
}
}

View File

@@ -0,0 +1,35 @@
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());
}
}

View File

@@ -0,0 +1,50 @@
package cn.iocoder.mall.shopweb.service.promotion;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.CouponCardFeign;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardCreateReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardRespDTO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardPageReqVO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.card.CouponCardRespVO;
import cn.iocoder.mall.shopweb.convert.promotion.CouponCardConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 优惠劵 Manager
*/
@Service
public class CouponCardManager {
@Autowired
private CouponCardFeign couponCardFeign;
/**
* 获得优惠劵分页
*
* @param userId 用户编号
* @param pageVO 优惠劵分页查询
* @return 优惠劵分页结果
*/
public PageResult<CouponCardRespVO> pageCouponCard(Integer userId, CouponCardPageReqVO pageVO) {
CommonResult<PageResult<CouponCardRespDTO>> pageCouponCardResult = couponCardFeign.pageCouponCard(
CouponCardConvert.INSTANCE.convert(pageVO).setUserId(userId));
pageCouponCardResult.checkError();
return CouponCardConvert.INSTANCE.convertPage(pageCouponCardResult.getData());
}
/**
* 用户领取优惠劵
*
* @param userId 用户编号
* @param couponTemplateId 优惠劵模板编号
* @return 优惠劵编号
*/
public Integer createCouponCard(Integer userId, Integer couponTemplateId) {
CommonResult<Integer> createCouponCardResult = couponCardFeign.createCouponCard(
new CouponCardCreateReqDTO().setUserId(userId).setCouponTemplateId(couponTemplateId));
createCouponCardResult.checkError();
return createCouponCardResult.getData();
}
}

View File

@@ -0,0 +1,23 @@
package cn.iocoder.mall.shopweb.service.promotion;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.promotion.api.rpc.coupon.CouponTemplateFeign;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.template.CouponTemplateRespDTO;
import cn.iocoder.mall.shopweb.controller.promotion.vo.coupon.template.CouponTemplateRespVO;
import cn.iocoder.mall.shopweb.convert.promotion.CouponTemplateConvert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* 优惠劵(码)模板 Manager
*/
@Service
public class CouponTemplateManager {
@Autowired
private CouponTemplateFeign couponTemplateFeign;
public CouponTemplateRespVO getCouponTemplate(Integer id) {
CommonResult<CouponTemplateRespDTO> getCouponTemplateResult = couponTemplateFeign.getCouponTemplate(id);
getCouponTemplateResult.checkError();
return CouponTemplateConvert.INSTANCE.convert(getCouponTemplateResult.getData());
}
}

View File

@@ -0,0 +1,57 @@
package cn.iocoder.mall.shopweb.service.promotion;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.productservice.rpc.spu.ProductSpuFeign;
import cn.iocoder.mall.productservice.rpc.spu.dto.ProductSpuRespDTO;
import cn.iocoder.mall.promotion.api.rpc.recommend.ProductRecommendFeign;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.ProductRecommendListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.recommend.dto.ProductRecommendRespDTO;
import cn.iocoder.mall.shopweb.controller.product.vo.product.ProductSpuRespVO;
import cn.iocoder.mall.shopweb.convert.promotion.ProductRecommendConvert;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
/**
* 商品推荐 Manager
*/
@Service
@Validated
public class ProductRecommendManager {
@Autowired
private ProductRecommendFeign productRecommendFeign;
@Autowired
private ProductSpuFeign productSpuFeign;
public Map<Integer, Collection<ProductSpuRespVO>> listProductRecommends() {
// 查询商品推荐列表
CommonResult<List<ProductRecommendRespDTO>> listProductRecommendsResult = productRecommendFeign.listProductRecommends(
new ProductRecommendListReqDTO().setStatus(CommonStatusEnum.ENABLE.getValue()));
listProductRecommendsResult.checkError();
listProductRecommendsResult.getData().sort(Comparator.comparing(ProductRecommendRespDTO::getSort)); // 排序,按照 sort 升序
// 获得商品集合
Map<Integer, ProductSpuRespDTO> spuMap = this.getProductSkuMap(listProductRecommendsResult.getData());
// 组合结果,返回
Multimap<Integer, ProductSpuRespVO> result = HashMultimap.create();
listProductRecommendsResult.getData().forEach(productRecommendBO -> result.put(productRecommendBO.getType(),
ProductRecommendConvert.INSTANCE.convert(spuMap.get(productRecommendBO.getProductSpuId()))));
return result.asMap();
}
private Map<Integer, ProductSpuRespDTO> getProductSkuMap(List<ProductRecommendRespDTO> productRecommends) {
CommonResult<List<ProductSpuRespDTO>> listProductSpusResult = productSpuFeign.listProductSpus(
CollectionUtils.convertSet(productRecommends, ProductRecommendRespDTO::getProductSpuId));
listProductSpusResult.checkError();
return CollectionUtils.convertMap(listProductSpusResult.getData(), ProductSpuRespDTO::getId);
}
}

View File

@@ -0,0 +1,170 @@
package cn.iocoder.mall.shopweb.service.trade;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.mall.productservice.enums.sku.ProductSkuDetailFieldEnum;
import cn.iocoder.mall.productservice.rpc.sku.ProductSkuFeign;
import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuListQueryReqDTO;
import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuRespDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.PromotionActivityFeign;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.PriceFeign;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.controller.trade.vo.cart.CartDetailVO;
import cn.iocoder.mall.shopweb.convert.trade.CartConvert;
import cn.iocoder.mall.tradeservice.rpc.cart.CartFeign;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
* 购物车 Manager
*/
@Service
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();
}
/**
* 查询用户的购物车的商品列表
*
* @return 商品列表
*/
public CartDetailVO getCartDetail(Integer userId) {
// 获得购物车的商品
CommonResult<List<CartItemRespDTO>> listCartItemsResult = cartFeign.listCartItems(new CartItemListReqDTO().setUserId(userId));
listCartItemsResult.checkError();
// 购物车为空时,构造空的 UsersOrderConfirmCreateVO 返回
if (CollectionUtils.isEmpty(listCartItemsResult.getData())) {
CartDetailVO result = new CartDetailVO();
result.setItemGroups(Collections.emptyList());
result.setFee(new CartDetailVO.Fee(0, 0, 0, 0));
return result;
}
// 计算商品价格
CommonResult<PriceProductCalcRespDTO> calcProductPriceResult = priceFeign.calcProductPrice(new PriceProductCalcReqDTO().setUserId(userId)
.setItems(listCartItemsResult.getData().stream()
.map(cartItem -> new PriceProductCalcReqDTO.Item(cartItem.getSkuId(), cartItem.getQuantity(), cartItem.getSelected()))
.collect(Collectors.toList())));
calcProductPriceResult.checkError();
// 获得促销活动信息
Map<Integer, PromotionActivityRespDTO> promotionActivityMap = this.getPromotionActivityMap(calcProductPriceResult.getData());
// 获得商品 SKU 信息
Map<Integer, ProductSkuRespDTO> productSkuMap = this.getProductSkuMap(listCartItemsResult.getData());
// 拼接结果
CartDetailVO cartDetailVO = new CartDetailVO();
cartDetailVO.setFee(CartConvert.INSTANCE.convert(calcProductPriceResult.getData().getFee()));
cartDetailVO.setItemGroups(new ArrayList<>());
for (PriceProductCalcRespDTO.ItemGroup itemGroupDTO : calcProductPriceResult.getData().getItemGroups()) {
CartDetailVO.ItemGroup itemGroupVO = new CartDetailVO.ItemGroup();
cartDetailVO.getItemGroups().add(itemGroupVO);
// 活动信息
if (itemGroupDTO.getActivityId() != null) {
itemGroupVO.setActivity(promotionActivityMap.get(itemGroupDTO.getActivityId()))
.setActivityDiscountTotal(itemGroupDTO.getActivityDiscountTotal());
}
// 商品 SKU 信息
itemGroupVO.setItems(new ArrayList<>());
itemGroupDTO.getItems().forEach(item -> itemGroupVO.getItems().add(CartConvert.INSTANCE.convert(item,
productSkuMap.get(item.getSkuId()), promotionActivityMap.get(item.getActivityId()))));
}
return cartDetailVO;
}
private Map<Integer, PromotionActivityRespDTO> getPromotionActivityMap(PriceProductCalcRespDTO calcRespDTO) {
// 获得所有促销活动编号
Set<Integer> activeIds = new HashSet<>();
calcRespDTO.getItemGroups().forEach(itemGroup -> {
if (itemGroup.getActivityId() != null) {
activeIds.add(itemGroup.getActivityId());
}
itemGroup.getItems().forEach(item -> {
if (item.getActivityId() != null) {
activeIds.add(item.getActivityId());
}
});
});
if (CollectionUtils.isEmpty(activeIds)) {
return Collections.emptyMap();
}
// 查询促销活动列表
CommonResult<List<PromotionActivityRespDTO>> listPromotionActivitiesResult =
promotionActivityFeign.listPromotionActivities(new PromotionActivityListReqDTO().setActiveIds(activeIds));
listPromotionActivitiesResult.checkError();
return CollectionUtils.convertMap(listPromotionActivitiesResult.getData(), PromotionActivityRespDTO::getId);
}
private Map<Integer, ProductSkuRespDTO> getProductSkuMap(List<CartItemRespDTO> itemRespDTOs) {
CommonResult<List<ProductSkuRespDTO>> listProductSkusResult = productSkuFeign.listProductSkus(new ProductSkuListQueryReqDTO()
.setProductSkuIds(CollectionUtils.convertSet(itemRespDTOs, CartItemRespDTO::getSkuId))
.setFields(Arrays.asList(ProductSkuDetailFieldEnum.SPU.getField(), ProductSkuDetailFieldEnum.ATTR.getField())));
listProductSkusResult.checkError();
return CollectionUtils.convertMap(listProductSkusResult.getData(), ProductSkuRespDTO::getId);
}
}

View File

@@ -0,0 +1,203 @@
package cn.iocoder.mall.shopweb.service.trade;
import cn.iocoder.common.framework.enums.CommonStatusEnum;
import cn.iocoder.common.framework.exception.util.ServiceExceptionUtil;
import cn.iocoder.common.framework.util.CollectionUtils;
import cn.iocoder.common.framework.vo.CommonResult;
import cn.iocoder.common.framework.vo.PageResult;
import cn.iocoder.mall.productservice.enums.sku.ProductSkuDetailFieldEnum;
import cn.iocoder.mall.productservice.rpc.sku.ProductSkuFeign;
import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuListQueryReqDTO;
import cn.iocoder.mall.productservice.rpc.sku.dto.ProductSkuRespDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.PromotionActivityFeign;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.activity.dto.PromotionActivityRespDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.CouponCardFeign;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableListReqDTO;
import cn.iocoder.mall.promotion.api.rpc.coupon.dto.card.CouponCardAvailableRespDTO;
import cn.iocoder.mall.promotion.api.rpc.price.PriceFeign;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcReqDTO;
import cn.iocoder.mall.promotion.api.rpc.price.dto.PriceProductCalcRespDTO;
import cn.iocoder.mall.shopweb.client.trade.TradeOrderClient;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.TradeOrderConfirmCreateInfoRespVO;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.TradeOrderCreateReqVO;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.TradeOrderPageReqVO;
import cn.iocoder.mall.shopweb.controller.trade.vo.order.TradeOrderRespVO;
import cn.iocoder.mall.shopweb.convert.trade.CartConvert;
import cn.iocoder.mall.shopweb.convert.trade.TradeOrderConvert;
import cn.iocoder.mall.tradeservice.enums.order.TradeOrderDetailFieldEnum;
import cn.iocoder.mall.tradeservice.rpc.cart.CartFeign;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.CartItemListReqDTO;
import cn.iocoder.mall.tradeservice.rpc.cart.dto.CartItemRespDTO;
import cn.iocoder.mall.tradeservice.rpc.order.dto.TradeOrderPageReqDTO;
import cn.iocoder.mall.tradeservice.rpc.order.dto.TradeOrderRespDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.mall.shopweb.enums.ShopWebErrorCodeConstants.ORDER_PRODUCT_SKU_NOT_EXISTS;
import static cn.iocoder.mall.shopweb.enums.ShopWebErrorCodeConstants.ORDER_PRODUCT_SKU_QUANTITY_NOT_ENOUGH;
/**
* 交易订单 Service
*/
@Service
@Validated
public class TradeOrderService {
@Autowired
private PriceFeign priceFeign;
@Autowired
private PromotionActivityFeign promotionActivityFeign;
@Autowired
private CartFeign cartFeign;
@Autowired
private CouponCardFeign couponCardFeign;
@Autowired
private ProductSkuFeign productSkuFeign;
@Autowired
private TradeOrderClient tradeOrderClient;
public TradeOrderConfirmCreateInfoRespVO getOrderConfirmCreateInfo(Integer userId, Integer skuId, Integer quantity, Integer couponCardId) {
Map<Integer, Integer> skuMap = new HashMap<>();
skuMap.put(skuId, quantity);
return this.getOrderConfirmCreateInfo0(userId, skuMap, couponCardId);
}
public TradeOrderConfirmCreateInfoRespVO getOrderConfirmCreateInfoFromCart(Integer userId, Integer couponCardId) {
// 获得购物车的商品
CommonResult<List<CartItemRespDTO>> listCartItemsResult = cartFeign.listCartItems(
new CartItemListReqDTO().setUserId(userId).setSelected(true));
listCartItemsResult.checkError();
// 购物车为空时,构造空的 OrderConfirmCreateInfoRespVO 返回
if (CollectionUtils.isEmpty(listCartItemsResult.getData())) {
TradeOrderConfirmCreateInfoRespVO result = new TradeOrderConfirmCreateInfoRespVO();
result.setItemGroups(Collections.emptyList());
result.setFee(new TradeOrderConfirmCreateInfoRespVO.Fee(0, 0, 0, 0));
return result;
}
// 计算商品价格
Map<Integer, Integer> skuMap = CollectionUtils.convertMap(listCartItemsResult.getData(),
CartItemRespDTO::getSkuId, CartItemRespDTO::getQuantity);
return this.getOrderConfirmCreateInfo0(userId, skuMap, couponCardId);
}
private TradeOrderConfirmCreateInfoRespVO getOrderConfirmCreateInfo0(Integer userId, Map<Integer, Integer> skuMap, Integer couponCardId) {
// 校验商品都存在,并且库存足够
this.checkProductSkus(skuMap);
// 获得商品 SKU 信息
Map<Integer, ProductSkuRespDTO> productSkuMap = this.checkProductSkus(skuMap);
// 计算商品价格
CommonResult<PriceProductCalcRespDTO> calcProductPriceResult = priceFeign.calcProductPrice(new PriceProductCalcReqDTO()
.setUserId(userId).setCouponCardId(couponCardId)
.setItems(skuMap.entrySet().stream().map(entry -> new PriceProductCalcReqDTO.Item(entry.getKey(), entry.getValue(), true))
.collect(Collectors.toList())));
calcProductPriceResult.checkError();
// 获得促销活动信息
Map<Integer, PromotionActivityRespDTO> promotionActivityMap = this.getPromotionActivityMap(calcProductPriceResult.getData());
// 拼接结果
TradeOrderConfirmCreateInfoRespVO createInfoRespVO = new TradeOrderConfirmCreateInfoRespVO();
createInfoRespVO.setFee(TradeOrderConvert.INSTANCE.convert(calcProductPriceResult.getData().getFee()));
createInfoRespVO.setItemGroups(new ArrayList<>(calcProductPriceResult.getData().getItemGroups().size()));
for (PriceProductCalcRespDTO.ItemGroup itemGroupDTO : calcProductPriceResult.getData().getItemGroups()) {
TradeOrderConfirmCreateInfoRespVO.ItemGroup itemGroupVO = new TradeOrderConfirmCreateInfoRespVO.ItemGroup();
createInfoRespVO.getItemGroups().add(itemGroupVO);
// 活动信息
if (itemGroupDTO.getActivityId() != null) {
itemGroupVO.setActivity(promotionActivityMap.get(itemGroupDTO.getActivityId()))
.setActivityDiscountTotal(itemGroupDTO.getActivityDiscountTotal());
}
// 商品 SKU 信息
itemGroupVO.setItems(new ArrayList<>());
itemGroupDTO.getItems().forEach(item -> itemGroupVO.getItems().add(CartConvert.INSTANCE.convert(item,
productSkuMap.get(item.getSkuId()), promotionActivityMap.get(item.getActivityId()))));
}
// 查询可用优惠劵信息
CommonResult<List<CouponCardAvailableRespDTO>> listAvailableCouponCardsResult = couponCardFeign.listAvailableCouponCards(
new CouponCardAvailableListReqDTO().setUserId(userId)
.setItems(TradeOrderConvert.INSTANCE.convertList(calcProductPriceResult.getData().getItemGroups())));
listAvailableCouponCardsResult.checkError();
createInfoRespVO.setCouponCards(listAvailableCouponCardsResult.getData());
return createInfoRespVO;
}
private Map<Integer, ProductSkuRespDTO> checkProductSkus(Map<Integer, Integer> skuMap) {
// 获得商品 SKU 列表
CommonResult<List<ProductSkuRespDTO>> listProductSkusResult = productSkuFeign.listProductSkus(new ProductSkuListQueryReqDTO()
.setProductSkuIds(skuMap.keySet())
.setFields(Arrays.asList(ProductSkuDetailFieldEnum.SPU.getField(), ProductSkuDetailFieldEnum.ATTR.getField())));
listProductSkusResult.checkError();
Map<Integer, ProductSkuRespDTO> productSkuMap = CollectionUtils.convertMap(listProductSkusResult.getData(), ProductSkuRespDTO::getId);
// 校验商品 SKU 是否合法
for (Map.Entry<Integer, Integer> entry : skuMap.entrySet()) {
ProductSkuRespDTO productSku = productSkuMap.get(entry.getKey());
if (productSku == null || !CommonStatusEnum.ENABLE.getValue().equals(productSku.getStatus())) {
throw ServiceExceptionUtil.exception(ORDER_PRODUCT_SKU_NOT_EXISTS);
}
if (productSku.getQuantity() < entry.getValue()) {
throw ServiceExceptionUtil.exception(ORDER_PRODUCT_SKU_QUANTITY_NOT_ENOUGH);
}
}
return productSkuMap;
}
private Map<Integer, PromotionActivityRespDTO> getPromotionActivityMap(PriceProductCalcRespDTO calcRespDTO) {
// 获得所有促销活动编号
Set<Integer> activeIds = new HashSet<>();
calcRespDTO.getItemGroups().forEach(itemGroup -> {
if (itemGroup.getActivityId() != null) {
activeIds.add(itemGroup.getActivityId());
}
itemGroup.getItems().forEach(item -> {
if (item.getActivityId() != null) {
activeIds.add(item.getActivityId());
}
});
});
if (CollectionUtils.isEmpty(activeIds)) {
return Collections.emptyMap();
}
// 查询促销活动列表
CommonResult<List<PromotionActivityRespDTO>> listPromotionActivitiesResult =
promotionActivityFeign.listPromotionActivities(new PromotionActivityListReqDTO().setActiveIds(activeIds));
listPromotionActivitiesResult.checkError();
return CollectionUtils.convertMap(listPromotionActivitiesResult.getData(), PromotionActivityRespDTO::getId);
}
public Integer createTradeOrder(Integer userId, String ip, TradeOrderCreateReqVO createReqVO) {
return tradeOrderClient.createTradeOrder(TradeOrderConvert.INSTANCE.convert(createReqVO)
.setUserId(userId).setIp(ip));
}
/**
* 获得交易订单
*
* @param tradeOrderId 交易订单编号
* @return 交易订单
*/
public TradeOrderRespVO getTradeOrder(Integer tradeOrderId) {
return TradeOrderConvert.INSTANCE.convert(tradeOrderClient.getTradeOrder(tradeOrderId,
TradeOrderDetailFieldEnum.ITEM.getField()));
}
/**
* 获得交易订单分页
*
* @param pageVO 订单交易分页查询
* @return 订单交易分页结果
*/
public PageResult<TradeOrderRespVO> pageTradeOrder(Integer userId, TradeOrderPageReqVO pageVO) {
PageResult<TradeOrderRespDTO> pageTradeOrderResult = tradeOrderClient.pageTradeOrder(
TradeOrderConvert.INSTANCE.convert(pageVO).setUserId(userId)
.setFields(Collections.singleton(TradeOrderDetailFieldEnum.ITEM.getField()))
.setSorts(Collections.singletonList(TradeOrderPageReqDTO.ID_DESC)));
return TradeOrderConvert.INSTANCE.convertPage(pageTradeOrderResult);
}
}

View File

@@ -0,0 +1,60 @@
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());
}
}

View File

@@ -0,0 +1,129 @@
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);
}
}
}

View File

@@ -0,0 +1,33 @@
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();
}
}

View File

@@ -0,0 +1,15 @@
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 服务注册中心的地址

View File

@@ -0,0 +1,18 @@
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 服务注册中心的地址

View File

@@ -0,0 +1,75 @@
# 服务器的配置项
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: '*' # 暴露所有监控端点