diff --git a/shop-api/Dockerfile b/shop-api/Dockerfile
new file mode 100644
index 0000000..5fdea24
--- /dev/null
+++ b/shop-api/Dockerfile
@@ -0,0 +1,14 @@
+FROM openjdk:17.0.2
+
+
+RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
+
+RUN mkdir -p /opt/projects/mall4j
+
+WORKDIR /opt/projects/mall4j
+
+EXPOSE 8086
+
+ADD ./yami-shop-api/target/yami-shop-api-0.0.1-SNAPSHOT.jar ./
+
+CMD java -jar -Xms1024m -Xmx1024m -Xmn256m -Xss256k -XX:SurvivorRatio=8 -Dspring.profiles.active=docker yami-shop-api-0.0.1-SNAPSHOT.jar
diff --git a/shop-api/pom.xml b/shop-api/pom.xml
new file mode 100644
index 0000000..d797674
--- /dev/null
+++ b/shop-api/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+ yami-shop
+ com.yami.shop
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ yami-shop-api
+ jar
+
+
+
+ com.yami.shop
+ yami-shop-service
+ ${yami.shop.version}
+
+
+ com.yami.shop
+ yami-shop-security-api
+ ${yami.shop.version}
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring-boot.version}
+
+
+
+ repackage
+
+
+
+
+
+
+
diff --git a/shop-api/src/main/java/com/yami/shop/api/ApiApplication.java b/shop-api/src/main/java/com/yami/shop/api/ApiApplication.java
new file mode 100644
index 0000000..5c5f18b
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/ApiApplication.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api;
+
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+import org.springframework.context.annotation.ComponentScan;
+
+/**
+ * @author lgh
+ */
+@SpringBootApplication
+@ComponentScan(basePackages = {"com.yami.shop"})
+public class ApiApplication extends SpringBootServletInitializer{
+
+ public static void main(String[] args) {
+ SpringApplication.run(ApiApplication.class, args);
+ }
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
+ return builder.sources(ApiApplication.class);
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/config/ApiBeanConfig.java b/shop-api/src/main/java/com/yami/shop/api/config/ApiBeanConfig.java
new file mode 100644
index 0000000..485feb0
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/config/ApiBeanConfig.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.config;
+
+import cn.hutool.core.lang.Snowflake;
+import lombok.AllArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author lanhai
+ */
+@Configuration
+@AllArgsConstructor
+public class ApiBeanConfig {
+
+ private final ApiConfig apiConfig;
+
+ @Bean
+ public Snowflake snowflake() {
+ return new Snowflake(apiConfig.getWorkerId(), apiConfig.getDatacenterId());
+ }
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/config/ApiConfig.java b/shop-api/src/main/java/com/yami/shop/api/config/ApiConfig.java
new file mode 100644
index 0000000..03469db
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/config/ApiConfig.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.stereotype.Component;
+
+
+/**
+ * 商城配置文件
+ * @author lgh
+ */
+@Data
+@Component
+@PropertySource("classpath:api.properties")
+@ConfigurationProperties(prefix = "api")
+public class ApiConfig {
+
+ /**
+ * 数据中心ID
+ */
+ private Integer datacenterId;
+
+ /**
+ * 终端ID
+ */
+ private Integer workerId;
+
+ /**
+ * 域名
+ */
+ private String domainName;
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/config/SwaggerConfiguration.java b/shop-api/src/main/java/com/yami/shop/api/config/SwaggerConfiguration.java
new file mode 100644
index 0000000..e2048f3
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/config/SwaggerConfiguration.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.config;
+
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.info.License;
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Swagger文档,只有在测试环境才会使用
+ * @author LGH
+ */
+@Configuration
+public class SwaggerConfiguration {
+
+ @Bean
+ public GroupedOpenApi createRestApi() {
+ return GroupedOpenApi.builder()
+ .group("接口文档")
+ .packagesToScan("com.yami.shop.api").build();
+ }
+
+
+ @Bean
+ public OpenAPI springShopOpenApi() {
+ return new OpenAPI()
+ .info(new Info().title("Mall4j接口文档")
+ .description("Mall4j接口文档,openapi3.0 接口,用于前端对接")
+ .version("v0.0.1")
+ .license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com")));
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/AddrController.java b/shop-api/src/main/java/com/yami/shop/api/controller/AddrController.java
new file mode 100644
index 0000000..739cd7e
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/AddrController.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.yami.shop.bean.app.dto.UserAddrDto;
+import com.yami.shop.bean.app.param.AddrParam;
+import com.yami.shop.bean.model.UserAddr;
+import com.yami.shop.common.exception.YamiShopBindException;
+import com.yami.shop.common.response.ServerResponseEntity;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.UserAddrService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import cn.hutool.core.bean.BeanUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.validation.Valid;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/p/address")
+@Tag(name = "地址接口")
+@AllArgsConstructor
+public class AddrController {
+
+ @Autowired
+ private UserAddrService userAddrService;
+
+ /**
+ * 选择订单配送地址
+ */
+ @GetMapping("/list")
+ @Operation(summary = "用户地址列表" , description = "获取用户的所有地址信息")
+ public ServerResponseEntity> dvyList() {
+ String userId = SecurityUtils.getUser().getUserId();
+ List userAddrs = userAddrService.list(new LambdaQueryWrapper().eq(UserAddr::getUserId, userId).orderByDesc(UserAddr::getCommonAddr).orderByDesc(UserAddr::getUpdateTime));
+ return ServerResponseEntity.success(BeanUtil.copyToList(userAddrs, UserAddrDto.class));
+ }
+
+ @PostMapping("/addAddr")
+ @Operation(summary = "新增用户地址" , description = "新增用户地址")
+ public ServerResponseEntity addAddr(@Valid @RequestBody AddrParam addrParam) {
+ String userId = SecurityUtils.getUser().getUserId();
+
+ if (addrParam.getAddrId() != null && addrParam.getAddrId() != 0) {
+ return ServerResponseEntity.showFailMsg("该地址已存在");
+ }
+ long addrCount = userAddrService.count(new LambdaQueryWrapper().eq(UserAddr::getUserId, userId));
+ UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class);
+
+ if (addrCount == 0) {
+ userAddr.setCommonAddr(1);
+ } else {
+ userAddr.setCommonAddr(0);
+ }
+ userAddr.setUserId(userId);
+ userAddr.setStatus(1);
+ userAddr.setCreateTime(new Date());
+ userAddr.setUpdateTime(new Date());
+ userAddrService.save(userAddr);
+ if (userAddr.getCommonAddr() == 1) {
+ // 清除默认地址缓存
+ userAddrService.removeUserAddrByUserId(0L, userId);
+ }
+ return ServerResponseEntity.success("添加地址成功");
+ }
+
+ /**
+ * 修改订单配送地址
+ */
+ @PutMapping("/updateAddr")
+ @Operation(summary = "修改订单用户地址" , description = "修改用户地址")
+ public ServerResponseEntity updateAddr(@Valid @RequestBody AddrParam addrParam) {
+ String userId = SecurityUtils.getUser().getUserId();
+
+ UserAddr dbUserAddr = userAddrService.getUserAddrByUserId(addrParam.getAddrId(), userId);
+ if (dbUserAddr == null) {
+ return ServerResponseEntity.showFailMsg("该地址已被删除");
+ }
+
+ UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class);
+ userAddr.setUserId(userId);
+ userAddr.setUpdateTime(new Date());
+ userAddrService.updateById(userAddr);
+ // 清除当前地址缓存
+ userAddrService.removeUserAddrByUserId(addrParam.getAddrId(), userId);
+ // 清除默认地址缓存
+ userAddrService.removeUserAddrByUserId(0L, userId);
+ return ServerResponseEntity.success("修改地址成功");
+ }
+
+ /**
+ * 删除订单配送地址
+ */
+ @DeleteMapping("/deleteAddr/{addrId}")
+ @Operation(summary = "删除订单用户地址" , description = "根据地址id,删除用户地址")
+ @Parameter(name = "addrId", description = "地址ID" , required = true)
+ public ServerResponseEntity deleteDvy(@PathVariable("addrId") Long addrId) {
+ String userId = SecurityUtils.getUser().getUserId();
+ UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId);
+ if (userAddr == null) {
+ return ServerResponseEntity.showFailMsg("该地址已被删除");
+ }
+ if (userAddr.getCommonAddr() == 1) {
+ return ServerResponseEntity.showFailMsg("默认地址无法删除");
+ }
+ userAddrService.removeById(addrId);
+ userAddrService.removeUserAddrByUserId(addrId, userId);
+ return ServerResponseEntity.success("删除地址成功");
+ }
+
+ /**
+ * 设置默认地址
+ */
+ @PutMapping("/defaultAddr/{addrId}")
+ @Operation(summary = "设置默认地址" , description = "根据地址id,设置默认地址")
+ public ServerResponseEntity defaultAddr(@PathVariable("addrId") Long addrId) {
+ String userId = SecurityUtils.getUser().getUserId();
+
+ userAddrService.updateDefaultUserAddr(addrId, userId);
+
+ userAddrService.removeUserAddrByUserId(0L, userId);
+ userAddrService.removeUserAddrByUserId(addrId, userId);
+ return ServerResponseEntity.success("修改地址成功");
+ }
+
+ /**
+ * 获取地址信息订单配送地址
+ */
+ @GetMapping("/addrInfo/{addrId}")
+ @Operation(summary = "获取地址信息" , description = "根据地址id,获取地址信息")
+ @Parameter(name = "addrId", description = "地址ID" , required = true)
+ public ServerResponseEntity addrInfo(@PathVariable("addrId") Long addrId) {
+ String userId = SecurityUtils.getUser().getUserId();
+ UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId);
+ if (userAddr == null) {
+ throw new YamiShopBindException("该地址已被删除");
+ }
+ return ServerResponseEntity.success(BeanUtil.copyProperties(userAddr, UserAddrDto.class));
+ }
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/AreaController.java b/shop-api/src/main/java/com/yami/shop/api/controller/AreaController.java
new file mode 100644
index 0000000..b2c1d1b
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/AreaController.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import java.util.List;
+
+import com.yami.shop.service.AreaService;
+import org.springframework.beans.factory.annotation.Autowired;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.yami.shop.bean.model.Area;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+/**
+ *
+ * @author lgh on 2018/10/26.
+ */
+@RestController
+@RequestMapping("/p/area")
+@Tag(name = "省市区接口")
+public class AreaController {
+
+ @Autowired
+ private AreaService areaService;
+
+ /**
+ * 分页获取
+ */
+ @GetMapping("/listByPid")
+ @Operation(summary = "获取省市区信息" , description = "根据省市区的pid获取地址信息")
+ @Parameter(name = "pid", description = "省市区的pid(pid为0获取所有省份)" , required = true)
+ public ServerResponseEntity> listByPid(Long pid){
+ List list = areaService.listByPid(pid);
+ return ServerResponseEntity.success(list);
+ }
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/CategoryController.java b/shop-api/src/main/java/com/yami/shop/api/controller/CategoryController.java
new file mode 100644
index 0000000..939a315
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/CategoryController.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import java.util.List;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import com.yami.shop.common.response.ServerResponseEntity;
+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 com.yami.shop.bean.app.dto.CategoryDto;
+import com.yami.shop.bean.model.Category;
+import com.yami.shop.service.CategoryService;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+import cn.hutool.core.bean.BeanUtil;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/category")
+@Tag(name = "分类接口")
+public class CategoryController {
+
+ @Autowired
+ private CategoryService categoryService;
+
+
+ /**
+ * 分类信息列表接口
+ */
+ @GetMapping("/categoryInfo")
+ @Operation(summary = "分类信息列表" , description = "获取所有的产品分类信息,顶级分类的parentId为0,默认为顶级分类")
+ @Parameter(name = "parentId", description = "分类ID", required = false)
+ public ServerResponseEntity> categoryInfo(@RequestParam(value = "parentId", defaultValue = "0") Long parentId) {
+ List categories = categoryService.listByParentId(parentId);
+ List categoryDtos = BeanUtil.copyToList(categories, CategoryDto.class);
+ return ServerResponseEntity.success(categoryDtos);
+ }
+
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/DeliveryController.java b/shop-api/src/main/java/com/yami/shop/api/controller/DeliveryController.java
new file mode 100644
index 0000000..64b9060
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/DeliveryController.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.yami.shop.service.OrderService;
+import org.springframework.beans.factory.annotation.Autowired;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import com.yami.shop.bean.app.dto.DeliveryDto;
+import com.yami.shop.bean.model.Delivery;
+import com.yami.shop.bean.model.Order;
+import com.yami.shop.common.util.Json;
+import com.yami.shop.service.DeliveryService;
+
+import cn.hutool.http.HttpUtil;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/delivery")
+@Tag(name = "查看物流接口")
+public class DeliveryController {
+
+ @Autowired
+ private DeliveryService deliveryService;
+ @Autowired
+ private OrderService orderService;
+
+ /**
+ * 查看物流接口
+ */
+ @GetMapping("/check")
+ @Operation(summary = "查看物流" , description = "根据订单号查看物流")
+ @Parameter(name = "orderNumber", description = "订单号" , required = true)
+ public ServerResponseEntity checkDelivery(String orderNumber) {
+
+ Order order = orderService.getOrderByOrderNumber(orderNumber);
+ Delivery delivery = deliveryService.getById(order.getDvyId());
+ String url = delivery.getQueryUrl().replace("{dvyFlowId}", order.getDvyFlowId());
+ String deliveryJson = HttpUtil.get(url);
+
+ DeliveryDto deliveryDto = Json.parseObject(deliveryJson, DeliveryDto.class);
+ deliveryDto.setDvyFlowId(order.getDvyFlowId());
+ deliveryDto.setCompanyHomeUrl(delivery.getCompanyHomeUrl());
+ deliveryDto.setCompanyName(delivery.getDvyName());
+ return ServerResponseEntity.success(deliveryDto);
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/IndexImgController.java b/shop-api/src/main/java/com/yami/shop/api/controller/IndexImgController.java
new file mode 100644
index 0000000..487a093
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/IndexImgController.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.yami.shop.bean.app.dto.IndexImgDto;
+import com.yami.shop.bean.model.IndexImg;
+import com.yami.shop.service.IndexImgService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import cn.hutool.core.bean.BeanUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@Tag(name = "首页轮播图接口")
+public class IndexImgController {
+
+ @Autowired
+ private IndexImgService indexImgService;
+
+ /**
+ * 首页轮播图接口
+ */
+ @GetMapping("/indexImgs")
+ @Operation(summary = "首页轮播图" , description = "获取首页轮播图列表信息")
+ public ServerResponseEntity> indexImgs() {
+ List indexImgList = indexImgService.listIndexImg();
+ List indexImgDtos = BeanUtil.copyToList(indexImgList, IndexImgDto.class);
+ return ServerResponseEntity.success(indexImgDtos);
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/MyOrderController.java b/shop-api/src/main/java/com/yami/shop/api/controller/MyOrderController.java
new file mode 100644
index 0000000..711bfc1
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/MyOrderController.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yami.shop.bean.app.dto.*;
+import com.yami.shop.bean.enums.OrderStatus;
+import com.yami.shop.bean.model.Order;
+import com.yami.shop.bean.model.OrderItem;
+import com.yami.shop.bean.model.ShopDetail;
+import com.yami.shop.bean.model.UserAddrOrder;
+import com.yami.shop.common.exception.YamiShopBindException;
+import com.yami.shop.common.response.ServerResponseEntity;
+import com.yami.shop.common.util.Arith;
+import com.yami.shop.common.util.PageParam;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.*;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/p/myOrder")
+@Tag(name = "我的订单接口")
+@AllArgsConstructor
+public class MyOrderController {
+
+ private final OrderService orderService;
+
+
+ private final UserAddrOrderService userAddrOrderService;
+
+ private final ProductService productService;
+
+ private final SkuService skuService;
+
+ private final MyOrderService myOrderService;
+
+ private final ShopDetailService shopDetailService;
+
+ private final OrderItemService orderItemService;
+
+
+ /**
+ * 订单详情信息接口
+ */
+ @GetMapping("/orderDetail")
+ @Operation(summary = "订单详情信息", description = "根据订单号获取订单详情信息")
+ @Parameter(name = "orderNumber", description = "订单号", required = true)
+ public ServerResponseEntity orderDetail(@RequestParam(value = "orderNumber") String orderNumber) {
+
+ String userId = SecurityUtils.getUser().getUserId();
+ OrderShopDto orderShopDto = new OrderShopDto();
+
+ Order order = orderService.getOrderByOrderNumber(orderNumber);
+
+ if (order == null) {
+ throw new RuntimeException("该订单不存在");
+ }
+ if (!Objects.equals(order.getUserId(), userId)) {
+ throw new RuntimeException("你没有权限获取该订单信息");
+ }
+
+ ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(order.getShopId());
+ UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId());
+ UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddrOrder, UserAddrDto.class);
+ List orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
+ List orderItemList = BeanUtil.copyToList(orderItems, OrderItemDto.class);
+
+ orderShopDto.setShopId(shopDetail.getShopId());
+ orderShopDto.setShopName(shopDetail.getShopName());
+ orderShopDto.setActualTotal(order.getActualTotal());
+ orderShopDto.setUserAddrDto(userAddrDto);
+ orderShopDto.setOrderItemDtos(orderItemList);
+ orderShopDto.setTransfee(order.getFreightAmount());
+ orderShopDto.setReduceAmount(order.getReduceAmount());
+ orderShopDto.setCreateTime(order.getCreateTime());
+ orderShopDto.setRemarks(order.getRemarks());
+ orderShopDto.setStatus(order.getStatus());
+
+ double total = 0.0;
+ Integer totalNum = 0;
+ for (OrderItemDto orderItem : orderShopDto.getOrderItemDtos()) {
+ total = Arith.add(total, orderItem.getProductTotalAmount());
+ totalNum += orderItem.getProdCount();
+ }
+ orderShopDto.setTotal(total);
+ orderShopDto.setTotalNum(totalNum);
+
+ return ServerResponseEntity.success(orderShopDto);
+ }
+
+
+ /**
+ * 订单列表接口
+ */
+ @GetMapping("/myOrder")
+ @Operation(summary = "订单列表信息", description = "根据订单状态获取订单列表信息,状态为0时获取所有订单")
+ @Parameters({
+ @Parameter(name = "status", description = "订单状态 1:待付款 2:待发货 3:待收货 4:待评价 5:成功 6:失败")
+ })
+ public ServerResponseEntity> myOrder(@RequestParam(value = "status") Integer status, PageParam page) {
+ String userId = SecurityUtils.getUser().getUserId();
+ IPage myOrderDtoIpage = myOrderService.pageMyOrderByUserIdAndStatus(page, userId, status);
+ return ServerResponseEntity.success(myOrderDtoIpage);
+ }
+
+ /**
+ * 取消订单
+ */
+ @PutMapping("/cancel/{orderNumber}")
+ @Operation(summary = "根据订单号取消订单", description = "根据订单号取消订单")
+ @Parameter(name = "orderNumber", description = "订单号", required = true)
+ public ServerResponseEntity cancel(@PathVariable("orderNumber") String orderNumber) {
+ String userId = SecurityUtils.getUser().getUserId();
+ Order order = orderService.getOrderByOrderNumber(orderNumber);
+ if (!Objects.equals(order.getUserId(), userId)) {
+ throw new YamiShopBindException("你没有权限获取该订单信息");
+ }
+ if (!Objects.equals(order.getStatus(), OrderStatus.UNPAY.value())) {
+ throw new YamiShopBindException("订单已支付,无法取消订单");
+ }
+ List orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
+ order.setOrderItems(orderItems);
+ // 取消订单
+ orderService.cancelOrders(Collections.singletonList(order));
+
+ // 清除缓存
+ for (OrderItem orderItem : orderItems) {
+ productService.removeProductCacheByProdId(orderItem.getProdId());
+ skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
+ }
+ return ServerResponseEntity.success();
+ }
+
+
+ /**
+ * 确认收货
+ */
+ @PutMapping("/receipt/{orderNumber}")
+ @Operation(summary = "根据订单号确认收货", description = "根据订单号确认收货")
+ public ServerResponseEntity receipt(@PathVariable("orderNumber") String orderNumber) {
+ String userId = SecurityUtils.getUser().getUserId();
+ Order order = orderService.getOrderByOrderNumber(orderNumber);
+ if (!Objects.equals(order.getUserId(), userId)) {
+ throw new YamiShopBindException("你没有权限获取该订单信息");
+ }
+ if (!Objects.equals(order.getStatus(), OrderStatus.CONSIGNMENT.value())) {
+ throw new YamiShopBindException("订单未发货,无法确认收货");
+ }
+ List orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
+ order.setOrderItems(orderItems);
+ // 确认收货
+ orderService.confirmOrder(Collections.singletonList(order));
+
+ for (OrderItem orderItem : orderItems) {
+ productService.removeProductCacheByProdId(orderItem.getProdId());
+ skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
+ }
+ return ServerResponseEntity.success();
+ }
+
+ /**
+ * 删除订单
+ */
+ @DeleteMapping("/{orderNumber}")
+ @Operation(summary = "根据订单号删除订单", description = "根据订单号删除订单")
+ @Parameter(name = "orderNumber", description = "订单号", required = true)
+ public ServerResponseEntity delete(@PathVariable("orderNumber") String orderNumber) {
+ String userId = SecurityUtils.getUser().getUserId();
+
+ Order order = orderService.getOrderByOrderNumber(orderNumber);
+ if (order == null) {
+ throw new YamiShopBindException("该订单不存在");
+ }
+ if (!Objects.equals(order.getUserId(), userId)) {
+ throw new YamiShopBindException("你没有权限获取该订单信息");
+ }
+ if (!Objects.equals(order.getStatus(), OrderStatus.SUCCESS.value()) && !Objects.equals(order.getStatus(), OrderStatus.CLOSE.value())) {
+ throw new YamiShopBindException("订单未完成或未关闭,无法删除订单");
+ }
+
+ // 删除订单
+ orderService.deleteOrders(Collections.singletonList(order));
+
+ return ServerResponseEntity.success("删除成功");
+ }
+
+ /**
+ * 获取我的订单订单数量
+ */
+ @GetMapping("/orderCount")
+ @Operation(summary = "获取我的订单订单数量", description = "获取我的订单订单数量")
+ public ServerResponseEntity getOrderCount() {
+ String userId = SecurityUtils.getUser().getUserId();
+ OrderCountData orderCountMap = orderService.getOrderCount(userId);
+ return ServerResponseEntity.success(orderCountMap);
+ }
+
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/NoticeController.java b/shop-api/src/main/java/com/yami/shop/api/controller/NoticeController.java
new file mode 100644
index 0000000..918ee35
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/NoticeController.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import cn.hutool.core.bean.BeanUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yami.shop.bean.app.dto.NoticeDto;
+import com.yami.shop.bean.model.Notice;
+import com.yami.shop.common.response.ServerResponseEntity;
+import com.yami.shop.common.util.PageParam;
+import com.yami.shop.service.NoticeService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/shop/notice")
+@Tag(name = "公告管理接口")
+@AllArgsConstructor
+public class NoticeController {
+
+ private NoticeService noticeService;
+
+
+
+ /**
+ * 置顶公告列表接口
+ */
+ @GetMapping("/topNoticeList")
+ @Operation(summary = "置顶公告列表信息" , description = "获取所有置顶公告列表信息")
+ public ServerResponseEntity> getTopNoticeList() {
+ List noticeList = noticeService.listNotice();
+ List noticeDtoList = BeanUtil.copyToList(noticeList, NoticeDto.class);
+ return ServerResponseEntity.success(noticeDtoList);
+ }
+
+ /**
+ * 获取公告详情
+ */
+ @GetMapping("/info/{id}")
+ @Operation(summary = "公告详情" , description = "获取公告id公告详情")
+ public ServerResponseEntity getNoticeById(@PathVariable("id") Long id) {
+ Notice notice = noticeService.getNoticeById(id);
+ NoticeDto noticeDto = BeanUtil.copyProperties(notice, NoticeDto.class);
+ return ServerResponseEntity.success(noticeDto);
+ }
+
+ /**
+ * 公告列表
+ */
+ @GetMapping("/noticeList")
+ @Operation(summary = "公告列表信息" , description = "获取所有公告列表信息")
+ @Parameters({
+ })
+ public ServerResponseEntity> pageNotice(PageParam page) {
+
+ return ServerResponseEntity.success(noticeService.pageNotice(page));
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/OrderController.java b/shop-api/src/main/java/com/yami/shop/api/controller/OrderController.java
new file mode 100644
index 0000000..bf20799
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/OrderController.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.yami.shop.bean.app.dto.*;
+import com.yami.shop.bean.app.param.OrderParam;
+import com.yami.shop.bean.app.param.OrderShopParam;
+import com.yami.shop.bean.app.param.SubmitOrderParam;
+import com.yami.shop.bean.event.ConfirmOrderEvent;
+import com.yami.shop.bean.model.Order;
+import com.yami.shop.bean.model.UserAddr;
+import com.yami.shop.common.exception.YamiShopBindException;
+import com.yami.shop.common.util.Arith;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.*;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import cn.hutool.core.bean.BeanUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import jakarta.validation.Valid;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/p/order")
+@Tag(name = "订单接口")
+public class OrderController {
+
+ @Autowired
+ private OrderService orderService;
+ @Autowired
+ private SkuService skuService;
+ @Autowired
+ private ProductService productService;
+ @Autowired
+ private UserAddrService userAddrService;
+ @Autowired
+ private BasketService basketService;
+ @Autowired
+ private ApplicationContext applicationContext;
+
+
+ /**
+ * 生成订单
+ */
+ @PostMapping("/confirm")
+ @Operation(summary = "结算,生成订单信息" , description = "传入下单所需要的参数进行下单")
+ public ServerResponseEntity confirm(@Valid @RequestBody OrderParam orderParam) {
+ String userId = SecurityUtils.getUser().getUserId();
+
+ // 订单的地址信息
+ UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId);
+ UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddr, UserAddrDto.class);
+
+
+ // 组装获取用户提交的购物车商品项
+ List shopCartItems = basketService.getShopCartItemsByOrderItems(orderParam.getBasketIds(),orderParam.getOrderItem(),userId);
+
+ if (CollectionUtil.isEmpty(shopCartItems)) {
+ throw new YamiShopBindException("请选择您需要的商品加入购物车");
+ }
+
+ // 根据店铺组装购车中的商品信息,返回每个店铺中的购物车商品信息
+ List shopCarts = basketService.getShopCarts(shopCartItems);
+
+ // 将要返回给前端的完整的订单信息
+ ShopCartOrderMergerDto shopCartOrderMergerDto = new ShopCartOrderMergerDto();
+
+ shopCartOrderMergerDto.setUserAddr(userAddrDto);
+
+ // 所有店铺的订单信息
+ List shopCartOrders = new ArrayList<>();
+
+ double actualTotal = 0.0;
+ double total = 0.0;
+ int totalCount = 0;
+ double orderReduce = 0.0;
+ for (ShopCartDto shopCart : shopCarts) {
+
+ // 每个店铺的订单信息
+ ShopCartOrderDto shopCartOrder = new ShopCartOrderDto();
+ shopCartOrder.setShopId(shopCart.getShopId());
+ shopCartOrder.setShopName(shopCart.getShopName());
+
+
+ List shopCartItemDiscounts = shopCart.getShopCartItemDiscounts();
+
+ // 店铺中的所有商品项信息
+ List shopAllShopCartItems = new ArrayList<>();
+ for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
+ List discountShopCartItems = shopCartItemDiscount.getShopCartItems();
+ shopAllShopCartItems.addAll(discountShopCartItems);
+ }
+
+ shopCartOrder.setShopCartItemDiscounts(shopCartItemDiscounts);
+
+ applicationContext.publishEvent(new ConfirmOrderEvent(shopCartOrder,orderParam,shopAllShopCartItems));
+
+ actualTotal = Arith.add(actualTotal,shopCartOrder.getActualTotal());
+ total = Arith.add(total,shopCartOrder.getTotal());
+ totalCount = totalCount + shopCartOrder.getTotalCount();
+ orderReduce = Arith.add(orderReduce,shopCartOrder.getShopReduce());
+ shopCartOrders.add(shopCartOrder);
+
+
+ }
+
+ shopCartOrderMergerDto.setActualTotal(actualTotal);
+ shopCartOrderMergerDto.setTotal(total);
+ shopCartOrderMergerDto.setTotalCount(totalCount);
+ shopCartOrderMergerDto.setShopCartOrders(shopCartOrders);
+ shopCartOrderMergerDto.setOrderReduce(orderReduce);
+
+ shopCartOrderMergerDto = orderService.putConfirmOrderCache(userId, shopCartOrderMergerDto);
+
+ return ServerResponseEntity.success(shopCartOrderMergerDto);
+ }
+
+ /**
+ * 购物车/立即购买 提交订单,根据店铺拆单
+ */
+ @PostMapping("/submit")
+ @Operation(summary = "提交订单,返回支付流水号" , description = "根据传入的参数判断是否为购物车提交订单,同时对购物车进行删除,用户开始进行支付")
+ public ServerResponseEntity submitOrders(@Valid @RequestBody SubmitOrderParam submitOrderParam) {
+ String userId = SecurityUtils.getUser().getUserId();
+ ShopCartOrderMergerDto mergerOrder = orderService.getConfirmOrderCache(userId);
+ if (mergerOrder == null) {
+ throw new YamiShopBindException("订单已过期,请重新下单");
+ }
+
+ List orderShopParams = submitOrderParam.getOrderShopParam();
+
+ List shopCartOrders = mergerOrder.getShopCartOrders();
+ // 设置备注
+ if (CollectionUtil.isNotEmpty(orderShopParams)) {
+ for (ShopCartOrderDto shopCartOrder : shopCartOrders) {
+ for (OrderShopParam orderShopParam : orderShopParams) {
+ if (Objects.equals(shopCartOrder.getShopId(), orderShopParam.getShopId())) {
+ shopCartOrder.setRemarks(orderShopParam.getRemarks());
+ }
+ }
+ }
+ }
+
+ List orders = orderService.submit(userId,mergerOrder);
+
+
+
+ StringBuilder orderNumbers = new StringBuilder();
+ for (Order order : orders) {
+ orderNumbers.append(order.getOrderNumber()).append(",");
+ }
+ orderNumbers.deleteCharAt(orderNumbers.length() - 1);
+
+ boolean isShopCartOrder = false;
+ // 移除缓存
+ for (ShopCartOrderDto shopCartOrder : shopCartOrders) {
+ for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartOrder.getShopCartItemDiscounts()) {
+ for (ShopCartItemDto shopCartItem : shopCartItemDiscount.getShopCartItems()) {
+ Long basketId = shopCartItem.getBasketId();
+ if (basketId != null && basketId != 0) {
+ isShopCartOrder = true;
+ }
+ skuService.removeSkuCacheBySkuId(shopCartItem.getSkuId(),shopCartItem.getProdId());
+ productService.removeProductCacheByProdId(shopCartItem.getProdId());
+ }
+ }
+ }
+ // 购物车提交订单时(即有购物车ID时)
+ if (isShopCartOrder) {
+ basketService.removeShopCartItemsCacheByUserId(userId);
+ }
+ orderService.removeConfirmOrderCache(userId);
+ return ServerResponseEntity.success(new OrderNumbersDto(orderNumbers.toString()));
+ }
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/PayController.java b/shop-api/src/main/java/com/yami/shop/api/controller/PayController.java
new file mode 100644
index 0000000..fd12a1f
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/PayController.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.yami.shop.bean.app.param.PayParam;
+import com.yami.shop.bean.pay.PayInfoDto;
+import com.yami.shop.security.api.model.YamiUser;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.PayService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.AllArgsConstructor;
+import lombok.SneakyThrows;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/p/order")
+@Tag(name = "订单接口")
+@AllArgsConstructor
+public class PayController {
+
+ private final PayService payService;
+
+ /**
+ * 支付接口
+ */
+ @PostMapping("/pay")
+ @Operation(summary = "根据订单号进行支付" , description = "根据订单号进行支付")
+ public ServerResponseEntity pay(@RequestBody PayParam payParam) {
+ YamiUser user = SecurityUtils.getUser();
+ String userId = user.getUserId();
+
+
+ PayInfoDto payInfo = payService.pay(userId, payParam);
+ payService.paySuccess(payInfo.getPayNo(), "");
+ return ServerResponseEntity.success();
+ }
+
+ /**
+ * 普通支付接口
+ */
+ @PostMapping("/normalPay")
+ @Operation(summary = "根据订单号进行支付" , description = "根据订单号进行支付")
+ public ServerResponseEntity normalPay(@RequestBody PayParam payParam) {
+
+ YamiUser user = SecurityUtils.getUser();
+ String userId = user.getUserId();
+ PayInfoDto pay = payService.pay(userId, payParam);
+
+ // 根据内部订单号更新order settlement
+ payService.paySuccess(pay.getPayNo(), "");
+
+ return ServerResponseEntity.success(true);
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/PayNoticeController.java b/shop-api/src/main/java/com/yami/shop/api/controller/PayNoticeController.java
new file mode 100644
index 0000000..4cfe7c8
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/PayNoticeController.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import lombok.AllArgsConstructor;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import io.swagger.v3.oas.annotations.Hidden;
+
+/**
+ * @author lanhai
+ */
+@Hidden
+@RestController
+@RequestMapping("/notice/pay")
+@AllArgsConstructor
+public class PayNoticeController {
+//模拟支付不需要回调
+// /**
+// * 小程序支付
+// */
+// private final WxPayService wxMiniPayService;
+//
+// private final PayService payService;
+//
+//
+// @RequestMapping("/order")
+// public ServerResponseEntity submit(@RequestBody String xmlData) throws WxPayException {
+// WxPayOrderNotifyResult parseOrderNotifyResult = wxMiniPayService.parseOrderNotifyResult(xmlData);
+//
+// String payNo = parseOrderNotifyResult.getOutTradeNo();
+// String bizPayNo = parseOrderNotifyResult.getTransactionId();
+//
+// // 根据内部订单号更新order settlement
+// payService.paySuccess(payNo, bizPayNo);
+//
+//
+// return ServerResponseEntity.success();
+// }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/ProdCommController.java b/shop-api/src/main/java/com/yami/shop/api/controller/ProdCommController.java
new file mode 100644
index 0000000..bcd9b44
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/ProdCommController.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yami.shop.bean.app.dto.ProdCommDataDto;
+import com.yami.shop.bean.app.dto.ProdCommDto;
+import com.yami.shop.bean.app.param.ProdCommParam;
+import com.yami.shop.bean.model.ProdComm;
+import com.yami.shop.common.util.PageParam;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.ProdCommService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.AllArgsConstructor;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/prodComm")
+@Tag(name = "评论接口")
+@AllArgsConstructor
+public class ProdCommController {
+
+ private final ProdCommService prodCommService;
+
+
+ @GetMapping("/prodCommData")
+ @Operation(summary = "返回商品评论数据(好评率 好评数量 中评数 差评数)" , description = "根据商品id获取")
+ public ServerResponseEntity getProdCommData(Long prodId) {
+ return ServerResponseEntity.success(prodCommService.getProdCommDataByProdId(prodId));
+ }
+
+ @GetMapping("/prodCommPageByUser")
+ @Operation(summary = "根据用户返回评论分页数据" , description = "传入页码")
+ public ServerResponseEntity> getProdCommPage(PageParam page) {
+ return ServerResponseEntity.success(prodCommService.getProdCommDtoPageByUserId(page, SecurityUtils.getUser().getUserId()));
+ }
+
+ @GetMapping("/prodCommPageByProd")
+ @Operation(summary = "根据商品返回评论分页数据" , description = "传入商品id和页码")
+ @Parameters({
+ @Parameter(name = "prodId", description = "商品id" , required = true),
+ @Parameter(name = "evaluate", description = "-1或null 全部,0好评 1中评 2差评 3有图" , required = true),
+ })
+ public ServerResponseEntity> getProdCommPageByProdId(PageParam page, Long prodId, Integer evaluate) {
+ return ServerResponseEntity.success(prodCommService.getProdCommDtoPageByProdId(page, prodId, evaluate));
+ }
+
+ @PostMapping
+ @Operation(summary = "添加评论")
+ public ServerResponseEntity saveProdCommPage(ProdCommParam prodCommParam) {
+ ProdComm prodComm = new ProdComm();
+ prodComm.setProdId(prodCommParam.getProdId());
+ prodComm.setOrderItemId(prodCommParam.getOrderItemId());
+ prodComm.setUserId(SecurityUtils.getUser().getUserId());
+ prodComm.setScore(prodCommParam.getScore());
+ prodComm.setContent(prodCommParam.getContent());
+ prodComm.setPics(prodCommParam.getPics());
+ prodComm.setIsAnonymous(prodCommParam.getIsAnonymous());
+ prodComm.setRecTime(new Date());
+ prodComm.setStatus(0);
+ prodComm.setEvaluate(prodCommParam.getEvaluate());
+ prodCommService.save(prodComm);
+ return ServerResponseEntity.success();
+ }
+
+ @DeleteMapping
+ @Operation(summary = "删除评论" , description = "根据id删除")
+ public ServerResponseEntity deleteProdComm(Long prodCommId) {
+ prodCommService.removeById(prodCommId);
+ return ServerResponseEntity.success();
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/ProdController.java b/shop-api/src/main/java/com/yami/shop/api/controller/ProdController.java
new file mode 100644
index 0000000..6c61fc0
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/ProdController.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yami.shop.bean.app.dto.ProductDto;
+import com.yami.shop.bean.app.dto.TagProductDto;
+import com.yami.shop.bean.model.Product;
+import com.yami.shop.bean.model.Sku;
+import com.yami.shop.bean.model.Transport;
+import com.yami.shop.common.response.ServerResponseEntity;
+import com.yami.shop.common.util.Json;
+import com.yami.shop.common.util.PageParam;
+import com.yami.shop.service.ProductService;
+import com.yami.shop.service.SkuService;
+import com.yami.shop.service.TransportService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import cn.hutool.core.bean.BeanUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+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 java.util.stream.Collectors;
+
+/**
+ * @author lgh on 2018/11/26.
+ */
+@RestController
+@RequestMapping("/prod")
+@Tag(name = "商品接口")
+public class ProdController {
+
+ @Autowired
+ private ProductService prodService;
+
+
+ @Autowired
+ private SkuService skuService;
+
+ @Autowired
+ private TransportService transportService;
+
+
+ @GetMapping("/pageProd")
+ @Operation(summary = "通过分类id商品列表信息" , description = "根据分类ID获取该分类下所有的商品列表信息")
+ @Parameters({
+ @Parameter(name = "categoryId", description = "分类ID" , required = true),
+ })
+ public ServerResponseEntity> prodList(
+ @RequestParam(value = "categoryId") Long categoryId,PageParam page) {
+ IPage productPage = prodService.pageByCategoryId(page, categoryId);
+ return ServerResponseEntity.success(productPage);
+ }
+
+ @GetMapping("/prodInfo")
+ @Operation(summary = "商品详情信息" , description = "根据商品ID(prodId)获取商品信息")
+ @Parameter(name = "prodId", description = "商品ID" , required = true)
+ public ServerResponseEntity prodInfo(Long prodId) {
+
+ Product product = prodService.getProductByProdId(prodId);
+ if (product == null) {
+ return ServerResponseEntity.success();
+ }
+
+ List skuList = skuService.listByProdId(prodId);
+ // 启用的sku列表
+ List useSkuList = skuList.stream().filter(sku -> sku.getStatus() == 1).collect(Collectors.toList());
+ product.setSkuList(useSkuList);
+ ProductDto productDto = BeanUtil.copyProperties(product, ProductDto.class);
+
+
+ // 商品的配送方式
+ Product.DeliveryModeVO deliveryModeVO = Json.parseObject(product.getDeliveryMode(), Product.DeliveryModeVO.class);
+ // 有店铺配送的方式, 且存在运费模板,才返回运费模板的信息,供前端查阅
+ if (deliveryModeVO.getHasShopDelivery() && product.getDeliveryTemplateId() != null) {
+ Transport transportAndAllItems = transportService.getTransportAndAllItems(product.getDeliveryTemplateId());
+ productDto.setTransport(transportAndAllItems);
+ }
+
+ return ServerResponseEntity.success(productDto);
+ }
+
+ @GetMapping("/lastedProdPage")
+ @Operation(summary = "新品推荐" , description = "获取新品推荐商品列表")
+ @Parameters({
+ })
+ public ServerResponseEntity> lastedProdPage(PageParam page) {
+ IPage productPage = prodService.pageByPutAwayTime(page);
+ return ServerResponseEntity.success(productPage);
+ }
+
+ @GetMapping("/prodListByTagId")
+ @Operation(summary = "通过分组标签获取商品列表" , description = "通过分组标签id(tagId)获取商品列表")
+ @Parameters({
+ @Parameter(name = "tagId", description = "当前页,默认为1" , required = true),
+ })
+ public ServerResponseEntity> prodListByTagId(
+ @RequestParam(value = "tagId") Long tagId,PageParam page) {
+ IPage productPage = prodService.pageByTagId(page, tagId);
+ return ServerResponseEntity.success(productPage);
+ }
+
+ @GetMapping("/moreBuyProdList")
+ @Operation(summary = "每日疯抢" , description = "获取销量最多的商品列表")
+ @Parameters({})
+ public ServerResponseEntity> moreBuyProdList(PageParam page) {
+ IPage productPage = prodService.moreBuyProdList(page);
+ return ServerResponseEntity.success(productPage);
+ }
+
+ @GetMapping("/tagProdList")
+ @Operation(summary = "首页所有标签商品接口" , description = "获取首页所有标签商品接口")
+ public ServerResponseEntity> getTagProdList() {
+ List productDtoList = prodService.tagProdList();
+ return ServerResponseEntity.success(productDtoList);
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/ProdTagController.java b/shop-api/src/main/java/com/yami/shop/api/controller/ProdTagController.java
new file mode 100644
index 0000000..d1a1e65
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/ProdTagController.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.yami.shop.bean.app.dto.ProdTagDto;
+import com.yami.shop.bean.model.ProdTag;
+import com.yami.shop.service.ProdTagService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import cn.hutool.core.bean.BeanUtil;
+import com.yami.shop.common.response.ServerResponseEntity;
+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;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/prod/tag")
+@Tag(name = "商品分组标签接口")
+@AllArgsConstructor
+public class ProdTagController {
+
+ private ProdTagService prodTagService;
+
+
+
+ /**
+ * 商品分组标签列表接口
+ */
+ @GetMapping("/prodTagList")
+ @Operation(summary = "商品分组标签列表" , description = "获取所有的商品分组列表")
+ public ServerResponseEntity> getProdTagList() {
+ List prodTagList = prodTagService.listProdTag();
+ List prodTagDtoList = BeanUtil.copyToList(prodTagList, ProdTagDto.class);
+ return ServerResponseEntity.success(prodTagDtoList);
+ }
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/SearchController.java b/shop-api/src/main/java/com/yami/shop/api/controller/SearchController.java
new file mode 100644
index 0000000..900bf31
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/SearchController.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yami.shop.common.util.PageParam;
+import com.yami.shop.bean.dto.HotSearchDto;
+import com.yami.shop.bean.dto.SearchProdDto;
+import com.yami.shop.service.HotSearchService;
+import com.yami.shop.service.ProductService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.AllArgsConstructor;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/search")
+@Tag(name = "搜索接口")
+@AllArgsConstructor
+public class SearchController {
+
+ private final HotSearchService hotSearchService;
+
+ private final ProductService productService;
+
+ @GetMapping("/hotSearchByShopId")
+ @Operation(summary = "查看店铺热搜" , description = "根据店铺id,热搜数量获取热搜")
+ @Parameters({
+ @Parameter(name = "shopId", description = "店铺id" , required = true),
+ @Parameter(name = "number", description = "取数" , required = true),
+ @Parameter(name = "sort", description = "是否按照顺序(0 否 1是)"),
+ })
+ public ServerResponseEntity> hotSearchByShopId(Long shopId,Integer number,Integer sort) {
+ List list = hotSearchService.getHotSearchDtoByShopId(shopId);
+
+ return getListResponseEntity(number, sort, list);
+ }
+
+ @GetMapping("/hotSearch")
+ @Operation(summary = "查看全局热搜" , description = "根据店铺id,热搜数量获取热搜")
+ @Parameters({
+ @Parameter(name = "number", description = "取数" , required = true),
+ @Parameter(name = "sort", description = "是否按照顺序(0 否 1是)", required = false ),
+ })
+ public ServerResponseEntity> hotSearch(Integer number,Integer sort) {
+ List list = hotSearchService.getHotSearchDtoByShopId(0L);
+ return getListResponseEntity(number, sort, list);
+ }
+
+ private ServerResponseEntity> getListResponseEntity(Integer number, Integer sort, List list) {
+ if(sort == null || sort == 0){
+ Collections.shuffle(list);
+ }
+ if(!CollectionUtil.isNotEmpty(list) || list.size()< number){
+ return ServerResponseEntity.success(list);
+ }
+ return ServerResponseEntity.success(list.subList(0, number));
+ }
+
+ @GetMapping("/searchProdPage")
+ @Operation(summary = "分页排序搜索商品" , description = "根据商品名搜索")
+ @Parameters({
+ @Parameter(name = "prodName", description = "商品名" , required = true),
+ @Parameter(name = "sort", description = "排序(0 默认排序 1销量排序 2价格排序)"),
+ @Parameter(name = "orderBy", description = "排序(0升序 1降序)"),
+ @Parameter(name = "shopId", description = "店铺id" , required = true),
+ })
+ public ServerResponseEntity> searchProdPage(PageParam page, String prodName, Integer sort, Integer orderBy, Long shopId) {
+
+ return ServerResponseEntity.success(productService.getSearchProdDtoPageByProdName(page,prodName,sort,orderBy));
+ }
+
+
+
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/ShopCartController.java b/shop-api/src/main/java/com/yami/shop/api/controller/ShopCartController.java
new file mode 100644
index 0000000..f77c367
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/ShopCartController.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.map.MapUtil;
+import com.google.common.collect.Lists;
+import com.yami.shop.bean.app.dto.*;
+import com.yami.shop.bean.app.param.ChangeShopCartParam;
+import com.yami.shop.bean.app.param.ShopCartParam;
+import com.yami.shop.bean.event.ShopCartEvent;
+import com.yami.shop.bean.model.Basket;
+import com.yami.shop.bean.model.Product;
+import com.yami.shop.bean.model.Sku;
+import com.yami.shop.common.util.Arith;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.BasketService;
+import com.yami.shop.service.ProductService;
+import com.yami.shop.service.SkuService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.AllArgsConstructor;
+import org.springframework.context.ApplicationContext;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.validation.Valid;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/p/shopCart")
+@Tag(name = "购物车接口")
+@AllArgsConstructor
+public class ShopCartController {
+
+ private final BasketService basketService;
+
+ private final ProductService productService;
+
+ private final SkuService skuService;
+
+ private final ApplicationContext applicationContext;
+
+ /**
+ * 获取用户购物车信息
+ *
+ * @param basketIdShopCartParamMap 购物车参数对象列表
+ * @return
+ */
+ @PostMapping("/info")
+ @Operation(summary = "获取用户购物车信息" , description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key")
+ public ServerResponseEntity> info(@RequestBody Map basketIdShopCartParamMap) {
+ String userId = SecurityUtils.getUser().getUserId();
+
+ // 更新购物车信息,
+ if (MapUtil.isNotEmpty(basketIdShopCartParamMap)) {
+ basketService.updateBasketByShopCartParam(userId, basketIdShopCartParamMap);
+ }
+
+ // 拿到购物车的所有item
+ List shopCartItems = basketService.getShopCartItems(userId);
+ return ServerResponseEntity.success(basketService.getShopCarts(shopCartItems));
+
+ }
+
+ @DeleteMapping("/deleteItem")
+ @Operation(summary = "删除用户购物车物品" , description = "通过购物车id删除用户购物车物品")
+ public ServerResponseEntity deleteItem(@RequestBody List basketIds) {
+ String userId = SecurityUtils.getUser().getUserId();
+ basketService.deleteShopCartItemsByBasketIds(userId, basketIds);
+ return ServerResponseEntity.success();
+ }
+
+ @DeleteMapping("/deleteAll")
+ @Operation(summary = "清空用户购物车所有物品" , description = "清空用户购物车所有物品")
+ public ServerResponseEntity deleteAll() {
+ String userId = SecurityUtils.getUser().getUserId();
+ basketService.deleteAllShopCartItems(userId);
+ return ServerResponseEntity.success("删除成功");
+ }
+
+ @PostMapping("/changeItem")
+ @Operation(summary = "添加、修改用户购物车物品", description = "通过商品id(prodId)、skuId、店铺Id(shopId),添加/修改用户购物车商品,并传入改变的商品个数(count)," +
+ "当count为正值时,增加商品数量,当count为负值时,将减去商品的数量,当最终count值小于0时,会将商品从购物车里面删除")
+ public ServerResponseEntity addItem(@Valid @RequestBody ChangeShopCartParam param) {
+
+ if (param.getCount() == 0) {
+ return ServerResponseEntity.showFailMsg("输入更改数量");
+ }
+
+ String userId = SecurityUtils.getUser().getUserId();
+ List shopCartItems = basketService.getShopCartItems(userId);
+ Product prodParam = productService.getProductByProdId(param.getProdId());
+ Sku skuParam = skuService.getSkuBySkuId(param.getSkuId());
+
+ // 当商品状态不正常时,不能添加到购物车
+ if (prodParam.getStatus() != 1 || skuParam.getStatus() != 1) {
+ return ServerResponseEntity.showFailMsg("当前商品已下架");
+ }
+ for (ShopCartItemDto shopCartItemDto : shopCartItems) {
+ if (Objects.equals(param.getSkuId(), shopCartItemDto.getSkuId())) {
+ Basket basket = new Basket();
+ basket.setUserId(userId);
+ basket.setBasketCount(param.getCount() + shopCartItemDto.getProdCount());
+ basket.setBasketId(shopCartItemDto.getBasketId());
+
+ // 防止购物车变成负数
+ if (basket.getBasketCount() <= 0) {
+ basketService.deleteShopCartItemsByBasketIds(userId, Collections.singletonList(basket.getBasketId()));
+ return ServerResponseEntity.success();
+ }
+
+ // 当sku实际库存不足时,不能添加到购物车
+ if (skuParam.getStocks() < basket.getBasketCount() && shopCartItemDto.getProdCount() > 0) {
+ return ServerResponseEntity.showFailMsg("库存不足");
+ }
+ basketService.updateShopCartItem(basket);
+ return ServerResponseEntity.success();
+ }
+ }
+
+ // 防止购物车已被删除的情况下,添加了负数的商品
+ if (param.getCount() < 0) {
+ return ServerResponseEntity.showFailMsg("商品已从购物车移除");
+ }
+ // 当sku实际库存不足时,不能添加到购物车
+ if (skuParam.getStocks() < param.getCount()) {
+ return ServerResponseEntity.showFailMsg("库存不足");
+ }
+ // 所有都正常时
+ basketService.addShopCartItem(param,userId);
+ return ServerResponseEntity.success("添加成功");
+ }
+
+ @GetMapping("/prodCount")
+ @Operation(summary = "获取购物车商品数量" , description = "获取所有购物车商品数量")
+ public ServerResponseEntity prodCount() {
+ String userId = SecurityUtils.getUser().getUserId();
+ List shopCartItems = basketService.getShopCartItems(userId);
+ if (CollectionUtil.isEmpty(shopCartItems)) {
+ return ServerResponseEntity.success(0);
+ }
+ Integer totalCount = shopCartItems.stream().map(ShopCartItemDto::getProdCount).reduce(0, Integer::sum);
+ return ServerResponseEntity.success(totalCount);
+ }
+
+ @GetMapping("/expiryProdList")
+ @Operation(summary = "获取购物车失效商品信息" , description = "获取购物车失效商品列表")
+ public ServerResponseEntity> expiryProdList() {
+ String userId = SecurityUtils.getUser().getUserId();
+ List shopCartItems = basketService.getShopCartExpiryItems(userId);
+ //根据店铺ID划分item
+ Map> shopCartItemDtoMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getShopId));
+
+ // 返回一个店铺对应的所有信息
+ List shopcartExpiryitems = Lists.newArrayList();
+
+ for (Long key : shopCartItemDtoMap.keySet()) {
+ ShopCartExpiryItemDto shopCartExpiryItemDto = new ShopCartExpiryItemDto();
+ shopCartExpiryItemDto.setShopId(key);
+ List shopCartItemDtos = Lists.newArrayList();
+ for (ShopCartItemDto tempShopCartItemDto : shopCartItemDtoMap.get(key)) {
+ shopCartExpiryItemDto.setShopName(tempShopCartItemDto.getShopName());
+ shopCartItemDtos.add(tempShopCartItemDto);
+ }
+ shopCartExpiryItemDto.setShopCartItemDtoList(shopCartItemDtos);
+ shopcartExpiryitems.add(shopCartExpiryItemDto);
+ }
+
+ return ServerResponseEntity.success(shopcartExpiryitems);
+ }
+
+ @DeleteMapping("/cleanExpiryProdList")
+ @Operation(summary = "清空用户失效商品" , description = "清空用户失效商品")
+ public ServerResponseEntity cleanExpiryProdList() {
+ String userId = SecurityUtils.getUser().getUserId();
+ basketService.cleanExpiryProdList(userId);
+ return ServerResponseEntity.success();
+ }
+
+ @PostMapping("/totalPay")
+ @Operation(summary = "获取选中购物项总计、选中的商品数量" , description = "获取选中购物项总计、选中的商品数量,参数为购物车id数组")
+ public ServerResponseEntity getTotalPay(@RequestBody List basketIds) {
+
+ // 拿到购物车的所有item
+ List dbShopCartItems = basketService.getShopCartItems(SecurityUtils.getUser().getUserId());
+
+ List chooseShopCartItems = dbShopCartItems
+ .stream()
+ .filter(shopCartItemDto -> {
+ for (Long basketId : basketIds) {
+ if (Objects.equals(basketId,shopCartItemDto.getBasketId())) {
+ return true;
+ }
+ }
+ return false;
+ })
+ .toList();
+
+ // 根据店铺ID划分item
+ Map> shopCartMap = chooseShopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getShopId));
+
+ double total = 0.0;
+ int count = 0;
+ double reduce = 0.0;
+ for (Long shopId : shopCartMap.keySet()) {
+ //获取店铺的所有商品项
+ List shopCartItemDtoList = shopCartMap.get(shopId);
+ // 构建每个店铺的购物车信息
+ ShopCartDto shopCart = new ShopCartDto();
+ shopCart.setShopId(shopId);
+
+ applicationContext.publishEvent(new ShopCartEvent(shopCart, shopCartItemDtoList));
+
+ List shopCartItemDiscounts = shopCart.getShopCartItemDiscounts();
+
+ for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
+ List shopCartItems = shopCartItemDiscount.getShopCartItems();
+
+ for (ShopCartItemDto shopCartItem : shopCartItems) {
+ count = shopCartItem.getProdCount() + count;
+ total = Arith.add(shopCartItem.getProductTotalAmount(), total);
+ }
+ }
+ }
+ ShopCartAmountDto shopCartAmountDto = new ShopCartAmountDto();
+ shopCartAmountDto.setCount(count);
+ shopCartAmountDto.setTotalMoney(total);
+ shopCartAmountDto.setSubtractMoney(reduce);
+ shopCartAmountDto.setFinalMoney(Arith.sub(shopCartAmountDto.getTotalMoney(), shopCartAmountDto.getSubtractMoney()));
+
+ return ServerResponseEntity.success(shopCartAmountDto);
+ }
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/SkuController.java b/shop-api/src/main/java/com/yami/shop/api/controller/SkuController.java
new file mode 100644
index 0000000..217c29e
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/SkuController.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.yami.shop.bean.app.dto.SkuDto;
+import com.yami.shop.bean.model.Sku;
+import com.yami.shop.service.SkuService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.AllArgsConstructor;
+import cn.hutool.core.bean.BeanUtil;
+import com.yami.shop.common.response.ServerResponseEntity;
+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;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/sku")
+@Tag(name = "sku规格接口")
+@AllArgsConstructor
+public class SkuController {
+
+ private final SkuService skuService;
+
+
+
+ @GetMapping("/getSkuList")
+ @Operation(summary = "通过prodId获取商品全部规格列表" , description = "通过prodId获取商品全部规格列表")
+ @Parameter(name = "prodId", description = "商品id" )
+ public ServerResponseEntity> getSkuListByProdId(Long prodId) {
+ List skus = skuService.list(new LambdaQueryWrapper()
+ .eq(Sku::getStatus, 1)
+ .eq(Sku::getIsDelete, 0)
+ .eq(Sku::getProdId, prodId)
+ );
+ List skuDtoList = BeanUtil.copyToList(skus, SkuDto.class);
+ return ServerResponseEntity.success(skuDtoList);
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/SmsController.java b/shop-api/src/main/java/com/yami/shop/api/controller/SmsController.java
new file mode 100644
index 0000000..9c04760
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/SmsController.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.google.common.collect.Maps;
+import com.yami.shop.bean.app.param.SendSmsParam;
+import com.yami.shop.bean.enums.SmsType;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.SmsLogService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import org.springframework.beans.factory.annotation.Autowired;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/p/sms")
+@Tag(name = "发送验证码接口")
+public class SmsController {
+
+ @Autowired
+ private SmsLogService smsLogService;
+ /**
+ * 发送验证码接口
+ */
+ @PostMapping("/send")
+ @Operation(summary = "发送验证码" , description = "用户的发送验证码")
+ public ServerResponseEntity audit(@RequestBody SendSmsParam sendSmsParam) {
+ String userId = SecurityUtils.getUser().getUserId();
+ smsLogService.sendSms(SmsType.VALID, userId, sendSmsParam.getMobile(),Maps.newHashMap());
+
+ return ServerResponseEntity.success();
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/UserCollectionController.java b/shop-api/src/main/java/com/yami/shop/api/controller/UserCollectionController.java
new file mode 100644
index 0000000..9022d2d
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/UserCollectionController.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.yami.shop.bean.app.dto.ProductDto;
+import com.yami.shop.bean.app.dto.UserCollectionDto;
+import com.yami.shop.bean.model.Product;
+import com.yami.shop.bean.model.UserCollection;
+import com.yami.shop.common.exception.YamiShopBindException;
+import com.yami.shop.common.util.PageParam;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.ProductService;
+import com.yami.shop.service.UserCollectionService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.AllArgsConstructor;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Date;
+import java.util.Objects;
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/p/user/collection")
+@Tag(name = "收藏接口")
+@AllArgsConstructor
+public class UserCollectionController {
+
+ private final UserCollectionService userCollectionService;
+
+ private final ProductService productService;
+
+ @GetMapping("/page")
+ @Operation(summary = "分页返回收藏数据" , description = "根据用户id获取")
+ public ServerResponseEntity> getUserCollectionDtoPageByUserId(PageParam page) {
+ return ServerResponseEntity.success(userCollectionService.getUserCollectionDtoPageByUserId(page, SecurityUtils.getUser().getUserId()));
+ }
+
+ @GetMapping("isCollection")
+ @Operation(summary = "根据商品id获取该商品是否在收藏夹中" , description = "传入收藏商品id")
+ public ServerResponseEntity isCollection(Long prodId) {
+ if (productService.count(new LambdaQueryWrapper()
+ .eq(Product::getProdId, prodId)) < 1) {
+ throw new YamiShopBindException("该商品不存在");
+ }
+ return ServerResponseEntity.success(userCollectionService.count(new LambdaQueryWrapper()
+ .eq(UserCollection::getProdId, prodId)
+ .eq(UserCollection::getUserId, SecurityUtils.getUser().getUserId())) > 0);
+ }
+
+ @PostMapping("/addOrCancel")
+ @Operation(summary = "添加/取消收藏" , description = "传入收藏商品id,如果商品未收藏则收藏商品,已收藏则取消收藏")
+ @Parameter(name = "prodId", description = "商品id" , required = true)
+ public ServerResponseEntity addOrCancel(@RequestBody Long prodId) {
+ if (Objects.isNull(productService.getProductByProdId(prodId))) {
+ throw new YamiShopBindException("该商品不存在");
+ }
+ String userId = SecurityUtils.getUser().getUserId();
+ if (userCollectionService.count(new LambdaQueryWrapper()
+ .eq(UserCollection::getProdId, prodId)
+ .eq(UserCollection::getUserId, userId)) > 0) {
+ userCollectionService.remove(new LambdaQueryWrapper()
+ .eq(UserCollection::getProdId, prodId)
+ .eq(UserCollection::getUserId, userId));
+ } else {
+ UserCollection userCollection = new UserCollection();
+ userCollection.setCreateTime(new Date());
+ userCollection.setUserId(userId);
+ userCollection.setProdId(prodId);
+ userCollectionService.save(userCollection);
+ }
+ return ServerResponseEntity.success();
+ }
+
+ /**
+ * 查询用户收藏商品数量
+ */
+ @GetMapping("count")
+ @Operation(summary = "查询用户收藏商品数量" , description = "查询用户收藏商品数量")
+ public ServerResponseEntity findUserCollectionCount() {
+ String userId = SecurityUtils.getUser().getUserId();
+ return ServerResponseEntity.success(userCollectionService.count(new LambdaQueryWrapper().eq(UserCollection::getUserId, userId)));
+ }
+
+ @GetMapping("/prods")
+ @Operation(summary = "获取用户收藏商品列表" , description = "获取用户收藏商品列表")
+ public ServerResponseEntity> collectionProds(PageParam page) {
+ String userId = SecurityUtils.getUser().getUserId();
+ IPage productDtoPage = productService.collectionProds(page, userId);
+ return ServerResponseEntity.success(productDtoPage);
+ }
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/UserController.java b/shop-api/src/main/java/com/yami/shop/api/controller/UserController.java
new file mode 100644
index 0000000..1d8843f
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/UserController.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.controller;
+
+import com.yami.shop.bean.app.dto.UserDto;
+import com.yami.shop.bean.app.param.UserInfoParam;
+import com.yami.shop.bean.model.User;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.UserService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import cn.hutool.core.bean.BeanUtil;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.web.bind.annotation.*;
+/**
+ * @author lanhai
+ */
+@RestController
+@RequestMapping("/p/user")
+@Tag(name = "用户接口")
+@AllArgsConstructor
+public class UserController {
+
+ private final UserService userService;
+
+
+ /**
+ * 查看用户接口
+ */
+ @GetMapping("/userInfo")
+ @Operation(summary = "查看用户信息" , description = "根据用户ID(userId)获取用户信息")
+ public ServerResponseEntity userInfo() {
+ String userId = SecurityUtils.getUser().getUserId();
+ User user = userService.getById(userId);
+ UserDto userDto = BeanUtil.copyProperties(user, UserDto.class);
+ return ServerResponseEntity.success(userDto);
+ }
+
+ @PutMapping("/setUserInfo")
+ @Operation(summary = "设置用户信息" , description = "设置用户信息")
+ public ServerResponseEntity setUserInfo(@RequestBody UserInfoParam userInfoParam) {
+ String userId = SecurityUtils.getUser().getUserId();
+ User user = new User();
+ user.setUserId(userId);
+ user.setPic(userInfoParam.getAvatarUrl());
+ user.setNickName(userInfoParam.getNickName());
+ userService.updateById(user);
+ return ServerResponseEntity.success();
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/controller/UserRegisterController.java b/shop-api/src/main/java/com/yami/shop/api/controller/UserRegisterController.java
new file mode 100644
index 0000000..0241868
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/controller/UserRegisterController.java
@@ -0,0 +1,101 @@
+package com.yami.shop.api.controller;
+
+
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.yami.shop.bean.model.User;
+import com.yami.shop.bean.param.UserRegisterParam;
+import com.yami.shop.common.exception.YamiShopBindException;
+import com.yami.shop.security.common.bo.UserInfoInTokenBO;
+import com.yami.shop.security.common.enums.SysTypeEnum;
+import com.yami.shop.security.common.manager.PasswordManager;
+import com.yami.shop.security.common.manager.TokenStore;
+import com.yami.shop.security.common.vo.TokenInfoVO;
+import com.yami.shop.service.UserService;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import io.swagger.v3.oas.annotations.Operation;
+import lombok.AllArgsConstructor;
+import com.yami.shop.common.response.ServerResponseEntity;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.web.bind.annotation.*;
+
+import jakarta.validation.Valid;
+import java.util.Date;
+
+/**
+ * 用户信息
+ *
+ * @author SJL
+ */
+@RestController
+@RequestMapping("/user")
+@Tag(name = "用户注册相关接口")
+@AllArgsConstructor
+public class UserRegisterController {
+
+ private final UserService userService;
+
+ private final PasswordEncoder passwordEncoder;
+
+ private final TokenStore tokenStore;
+
+ private final PasswordManager passwordManager;
+
+ @PostMapping("/register")
+ @Operation(summary = "注册" , description = "用户注册或绑定手机号接口")
+ public ServerResponseEntity register(@Valid @RequestBody UserRegisterParam userRegisterParam) {
+ if (StrUtil.isBlank(userRegisterParam.getNickName())) {
+ userRegisterParam.setNickName(userRegisterParam.getUserName());
+ }
+ // 正在进行申请注册
+ if (userService.count(new LambdaQueryWrapper().eq(User::getNickName, userRegisterParam.getNickName())) > 0) {
+ // 该用户名已注册,无法重新注册
+ throw new YamiShopBindException("该用户名已注册,无法重新注册");
+ }
+ Date now = new Date();
+ User user = new User();
+ user.setModifyTime(now);
+ user.setUserRegtime(now);
+ user.setStatus(1);
+ user.setNickName(userRegisterParam.getNickName());
+ user.setUserMail(userRegisterParam.getUserMail());
+ String decryptPassword = passwordManager.decryptPassword(userRegisterParam.getPassWord());
+ user.setLoginPassword(passwordEncoder.encode(decryptPassword));
+ String userId = IdUtil.simpleUUID();
+ user.setUserId(userId);
+ userService.save(user);
+ // 2. 登录
+ UserInfoInTokenBO userInfoInTokenBO = new UserInfoInTokenBO();
+ userInfoInTokenBO.setUserId(user.getUserId());
+ userInfoInTokenBO.setSysType(SysTypeEnum.ORDINARY.value());
+ userInfoInTokenBO.setIsAdmin(0);
+ userInfoInTokenBO.setEnabled(true);
+ return ServerResponseEntity.success(tokenStore.storeAndGetVo(userInfoInTokenBO));
+ }
+
+
+ @PutMapping("/updatePwd")
+ @Operation(summary = "修改密码" , description = "修改密码")
+ public ServerResponseEntity updatePwd(@Valid @RequestBody UserRegisterParam userPwdUpdateParam) {
+ User user = userService.getOne(new LambdaQueryWrapper().eq(User::getNickName, userPwdUpdateParam.getNickName()));
+ if (user == null) {
+ // 无法获取用户信息
+ throw new YamiShopBindException("无法获取用户信息");
+ }
+ String decryptPassword = passwordManager.decryptPassword(userPwdUpdateParam.getPassWord());
+ if (StrUtil.isBlank(decryptPassword)) {
+ // 新密码不能为空
+ throw new YamiShopBindException("新密码不能为空");
+ }
+ String password = passwordEncoder.encode(decryptPassword);
+ if (StrUtil.equals(password, user.getLoginPassword())) {
+ // 新密码不能与原密码相同
+ throw new YamiShopBindException("新密码不能与原密码相同");
+ }
+ user.setModifyTime(new Date());
+ user.setLoginPassword(password);
+ userService.updateById(user);
+ return ServerResponseEntity.success();
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/listener/ConfirmOrderListener.java b/shop-api/src/main/java/com/yami/shop/api/listener/ConfirmOrderListener.java
new file mode 100644
index 0000000..c7f11c7
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/listener/ConfirmOrderListener.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.listener;
+
+import com.yami.shop.bean.app.dto.ShopCartItemDto;
+import com.yami.shop.bean.app.dto.ShopCartOrderDto;
+import com.yami.shop.bean.app.param.OrderParam;
+import com.yami.shop.bean.event.ConfirmOrderEvent;
+import com.yami.shop.bean.model.Product;
+import com.yami.shop.bean.model.Sku;
+import com.yami.shop.bean.model.UserAddr;
+import com.yami.shop.bean.order.ConfirmOrderOrder;
+import com.yami.shop.common.exception.YamiShopBindException;
+import com.yami.shop.common.util.Arith;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.ProductService;
+import com.yami.shop.service.SkuService;
+import com.yami.shop.service.TransportManagerService;
+import com.yami.shop.service.UserAddrService;
+import lombok.AllArgsConstructor;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+/**
+ * 确认订单信息时的默认操作
+ * @author LGH
+ */
+@Component("defaultConfirmOrderListener")
+@AllArgsConstructor
+public class ConfirmOrderListener {
+
+ private final UserAddrService userAddrService;
+
+ private final TransportManagerService transportManagerService;
+
+ private final ProductService productService;
+
+ private final SkuService skuService;
+
+ /**
+ * 计算订单金额
+ */
+ @EventListener(ConfirmOrderEvent.class)
+ @Order(ConfirmOrderOrder.DEFAULT)
+ public void defaultConfirmOrderEvent(ConfirmOrderEvent event) {
+
+
+ ShopCartOrderDto shopCartOrderDto = event.getShopCartOrderDto();
+
+ OrderParam orderParam = event.getOrderParam();
+
+ String userId = SecurityUtils.getUser().getUserId();
+
+ // 订单的地址信息
+ UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId);
+
+ double total = 0.0;
+
+ int totalCount = 0;
+
+ double transfee = 0.0;
+
+ for (ShopCartItemDto shopCartItem : event.getShopCartItems()) {
+ // 获取商品信息
+ Product product = productService.getProductByProdId(shopCartItem.getProdId());
+ // 获取sku信息
+ Sku sku = skuService.getSkuBySkuId(shopCartItem.getSkuId());
+ if (product == null || sku == null) {
+ throw new YamiShopBindException("购物车包含无法识别的商品");
+ }
+ if (product.getStatus() != 1 || sku.getStatus() != 1) {
+ throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架");
+ }
+
+ totalCount = shopCartItem.getProdCount() + totalCount;
+ total = Arith.add(shopCartItem.getProductTotalAmount(), total);
+ // 用户地址如果为空,则表示该用户从未设置过任何地址相关信息
+ if (userAddr != null) {
+ // 每个产品的运费相加
+ transfee = Arith.add(transfee, transportManagerService.calculateTransfee(shopCartItem, userAddr));
+ }
+
+ shopCartItem.setActualTotal(shopCartItem.getProductTotalAmount());
+ shopCartOrderDto.setActualTotal(Arith.add(total, transfee));
+ shopCartOrderDto.setTotal(total);
+ shopCartOrderDto.setTotalCount(totalCount);
+ shopCartOrderDto.setTransfee(transfee);
+ }
+ }
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/listener/ShopCartListener.java b/shop-api/src/main/java/com/yami/shop/api/listener/ShopCartListener.java
new file mode 100644
index 0000000..4c4087e
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/listener/ShopCartListener.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.listener;
+
+import com.google.common.collect.Lists;
+import com.yami.shop.bean.app.dto.ShopCartDto;
+import com.yami.shop.bean.app.dto.ShopCartItemDiscountDto;
+import com.yami.shop.bean.app.dto.ShopCartItemDto;
+import com.yami.shop.bean.event.ShopCartEvent;
+import com.yami.shop.bean.order.ShopCartEventOrder;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 默认的购物车链进行组装时的操作
+ * @author LGH
+ */
+@Component("defaultShopCartListener")
+public class ShopCartListener {
+
+ /**
+ * 将店铺下的所有商品归属到该店铺的购物车当中
+ * @param event#getShopCart() 购物车
+ * @param event#shopCartItemDtoList 该购物车的商品
+ * @return 是否继续组装
+ */
+ @EventListener(ShopCartEvent.class)
+ @Order(ShopCartEventOrder.DEFAULT)
+ public void defaultShopCartEvent(ShopCartEvent event) {
+ ShopCartDto shopCart = event.getShopCartDto();
+ List shopCartItemDtoList = event.getShopCartItemDtoList();
+ // 对数据进行组装
+ List shopCartItemDiscountDtoList = Lists.newArrayList();
+ ShopCartItemDiscountDto shopCartItemDiscountDto = new ShopCartItemDiscountDto();
+
+ shopCartItemDiscountDto.setShopCartItems(shopCartItemDtoList);
+ shopCartItemDiscountDtoList.add(shopCartItemDiscountDto);
+
+ shopCart.setShopCartItemDiscounts(shopCartItemDiscountDtoList);
+ }
+
+}
diff --git a/shop-api/src/main/java/com/yami/shop/api/listener/SubmitOrderListener.java b/shop-api/src/main/java/com/yami/shop/api/listener/SubmitOrderListener.java
new file mode 100644
index 0000000..2cb0f8d
--- /dev/null
+++ b/shop-api/src/main/java/com/yami/shop/api/listener/SubmitOrderListener.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
+ *
+ * https://www.mall4j.com/
+ *
+ * 未经允许,不可做商业用途!
+ *
+ * 版权所有,侵权必究!
+ */
+
+package com.yami.shop.api.listener;
+
+import cn.hutool.core.lang.Snowflake;
+import cn.hutool.core.util.StrUtil;
+import com.yami.shop.bean.app.dto.ShopCartItemDiscountDto;
+import com.yami.shop.bean.app.dto.ShopCartItemDto;
+import com.yami.shop.bean.app.dto.ShopCartOrderDto;
+import com.yami.shop.bean.app.dto.ShopCartOrderMergerDto;
+import com.yami.shop.bean.enums.OrderStatus;
+import com.yami.shop.bean.event.SubmitOrderEvent;
+import com.yami.shop.bean.model.*;
+import com.yami.shop.bean.order.SubmitOrderOrder;
+import com.yami.shop.common.constants.Constant;
+import com.yami.shop.common.exception.YamiShopBindException;
+import com.yami.shop.common.util.Arith;
+import com.yami.shop.dao.*;
+import com.yami.shop.security.api.util.SecurityUtils;
+import com.yami.shop.service.ProductService;
+import com.yami.shop.service.SkuService;
+import com.yami.shop.service.UserAddrOrderService;
+import lombok.AllArgsConstructor;
+import cn.hutool.core.bean.BeanUtil;
+import org.springframework.context.event.EventListener;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * 确认订单信息时的默认操作
+ *
+ * @author LGH
+ */
+@Component("defaultSubmitOrderListener")
+@AllArgsConstructor
+public class SubmitOrderListener {
+
+
+
+
+ private final UserAddrOrderService userAddrOrderService;
+
+ private final ProductService productService;
+
+ private final SkuService skuService;
+
+ private final Snowflake snowflake;
+
+ private final OrderItemMapper orderItemMapper;
+
+ private final SkuMapper skuMapper;
+
+ private final ProductMapper productMapper;
+
+ private final OrderMapper orderMapper;
+
+ private final OrderSettlementMapper orderSettlementMapper;
+
+ private final BasketMapper basketMapper;
+
+ /**
+ * 计算订单金额
+ */
+ @EventListener(SubmitOrderEvent.class)
+ @Order(SubmitOrderOrder.DEFAULT)
+ public void defaultSubmitOrderListener(SubmitOrderEvent event) {
+ Date now = new Date();
+ String userId = SecurityUtils.getUser().getUserId();
+
+ ShopCartOrderMergerDto mergerOrder = event.getMergerOrder();
+
+ // 订单商品参数
+ List shopCartOrders = mergerOrder.getShopCartOrders();
+
+ List basketIds = new ArrayList<>();
+ // 商品skuId为key 需要更新的sku为value的map
+ Map skuStocksMap = new HashMap<>(16);
+ // 商品productId为key 需要更新的product为value的map
+ Map prodStocksMap = new HashMap<>(16);
+
+ // 把订单地址保存到数据库
+ UserAddrOrder userAddrOrder = BeanUtil.copyProperties(mergerOrder.getUserAddr(), UserAddrOrder.class);
+ if (userAddrOrder == null) {
+ throw new YamiShopBindException("请填写收货地址");
+ }
+ userAddrOrder.setUserId(userId);
+ userAddrOrder.setCreateTime(now);
+ userAddrOrderService.save(userAddrOrder);
+
+ // 订单地址id
+ Long addrOrderId = userAddrOrder.getAddrOrderId();
+
+
+ // 每个店铺生成一个订单
+ for (ShopCartOrderDto shopCartOrderDto : shopCartOrders) {
+ createOrder(event, now, userId, basketIds, skuStocksMap, prodStocksMap, addrOrderId, shopCartOrderDto);
+
+ }
+
+ // 删除购物车的商品信息
+ if (!basketIds.isEmpty()) {
+ basketMapper.deleteShopCartItemsByBasketIds(userId, basketIds);
+
+ }
+
+
+ // 更新sku库存
+ skuStocksMap.forEach((key, sku) -> {
+
+ if (skuMapper.updateStocks(sku) == 0) {
+ skuService.removeSkuCacheBySkuId(key, sku.getProdId());
+ throw new YamiShopBindException("商品:[" + sku.getProdName() + "]库存不足");
+ }
+ });
+
+ // 更新商品库存
+ prodStocksMap.forEach((prodId, prod) -> {
+
+ if (productMapper.updateStocks(prod) == 0) {
+ productService.removeProductCacheByProdId(prodId);
+ throw new YamiShopBindException("商品:[" + prod.getProdName() + "]库存不足");
+ }
+ });
+
+ }
+
+ private void createOrder(SubmitOrderEvent event, Date now, String userId, List basketIds, Map skuStocksMap, Map prodStocksMap, Long addrOrderId, ShopCartOrderDto shopCartOrderDto) {
+ // 使用雪花算法生成的订单号
+ String orderNumber = String.valueOf(snowflake.nextId());
+ shopCartOrderDto.setOrderNumber(orderNumber);
+
+ Long shopId = shopCartOrderDto.getShopId();
+
+ // 订单商品名称
+ StringBuilder orderProdName = new StringBuilder(100);
+
+ List orderItems = new ArrayList<>();
+
+ List shopCartItemDiscounts = shopCartOrderDto.getShopCartItemDiscounts();
+ for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
+ List shopCartItems = shopCartItemDiscount.getShopCartItems();
+ for (ShopCartItemDto shopCartItem : shopCartItems) {
+ Sku sku = checkAndGetSku(shopCartItem.getSkuId(), shopCartItem, skuStocksMap);
+ Product product = checkAndGetProd(shopCartItem.getProdId(), shopCartItem, prodStocksMap);
+
+ OrderItem orderItem = getOrderItem(now, userId, orderNumber, shopId, orderProdName, shopCartItem, sku, product);
+
+ orderItems.add(orderItem);
+
+ if (shopCartItem.getBasketId() != null && shopCartItem.getBasketId() != 0) {
+ basketIds.add(shopCartItem.getBasketId());
+ }
+ }
+
+ }
+
+
+ orderProdName.subSequence(0, Math.min(orderProdName.length() - 1, 100));
+ if (orderProdName.lastIndexOf(Constant.COMMA) == orderProdName.length() - 1) {
+ orderProdName.deleteCharAt(orderProdName.length() - 1);
+ }
+
+
+ // 订单信息
+ com.yami.shop.bean.model.Order order = getOrder(now, userId, addrOrderId, shopCartOrderDto, orderNumber, shopId, orderProdName, orderItems);
+ event.getOrders().add(order);
+ // 插入订单结算表
+ OrderSettlement orderSettlement = new OrderSettlement();
+ orderSettlement.setUserId(userId);
+ orderSettlement.setIsClearing(0);
+ orderSettlement.setCreateTime(now);
+ orderSettlement.setOrderNumber(orderNumber);
+ orderSettlement.setPayAmount(order.getActualTotal());
+ orderSettlement.setPayStatus(0);
+ orderSettlement.setVersion(0);
+ orderSettlementMapper.insert(orderSettlement);
+ }
+
+ private com.yami.shop.bean.model.Order getOrder(Date now, String userId, Long addrOrderId, ShopCartOrderDto shopCartOrderDto, String orderNumber, Long shopId, StringBuilder orderProdName, List orderItems) {
+ com.yami.shop.bean.model.Order order = new com.yami.shop.bean.model.Order();
+
+ order.setShopId(shopId);
+ order.setOrderNumber(orderNumber);
+ // 订单商品名称
+ order.setProdName(orderProdName.toString());
+ // 用户id
+ order.setUserId(userId);
+ // 商品总额
+ order.setTotal(shopCartOrderDto.getTotal());
+ // 实际总额
+ order.setActualTotal(shopCartOrderDto.getActualTotal());
+ order.setStatus(OrderStatus.UNPAY.value());
+ order.setUpdateTime(now);
+ order.setCreateTime(now);
+ order.setIsPayed(0);
+ order.setDeleteStatus(0);
+ order.setProductNums(shopCartOrderDto.getTotalCount());
+ order.setAddrOrderId(addrOrderId);
+ order.setReduceAmount(Arith.sub(Arith.add(shopCartOrderDto.getTotal(), shopCartOrderDto.getTransfee()), shopCartOrderDto.getActualTotal()));
+ order.setFreightAmount(shopCartOrderDto.getTransfee());
+ order.setRemarks(shopCartOrderDto.getRemarks());
+
+ order.setOrderItems(orderItems);
+ return order;
+ }
+
+ private OrderItem getOrderItem(Date now, String userId, String orderNumber, Long shopId, StringBuilder orderProdName, ShopCartItemDto shopCartItem, Sku sku, Product product) {
+ OrderItem orderItem = new OrderItem();
+ orderItem.setShopId(shopId);
+ orderItem.setOrderNumber(orderNumber);
+ orderItem.setProdId(sku.getProdId());
+ orderItem.setSkuId(sku.getSkuId());
+ orderItem.setSkuName(sku.getSkuName());
+ orderItem.setProdCount(shopCartItem.getProdCount());
+ orderItem.setProdName(sku.getProdName());
+ orderItem.setPic(StrUtil.isBlank(sku.getPic()) ? product.getPic() : sku.getPic());
+ orderItem.setPrice(shopCartItem.getPrice());
+ orderItem.setUserId(userId);
+ orderItem.setProductTotalAmount(shopCartItem.getProductTotalAmount());
+ orderItem.setRecTime(now);
+ orderItem.setCommSts(0);
+ orderItem.setBasketDate(shopCartItem.getBasketDate());
+ orderProdName.append(orderItem.getProdName()).append(",");
+ //推广员卡号
+ orderItem.setDistributionCardNo(shopCartItem.getDistributionCardNo());
+ return orderItem;
+ }
+
+ @SuppressWarnings({"Duplicates"})
+ private Product checkAndGetProd(Long prodId, ShopCartItemDto shopCartItem, Map prodStocksMap) {
+ Product product = productService.getProductByProdId(prodId);
+ if (product == null) {
+ throw new YamiShopBindException("购物车包含无法识别的商品");
+ }
+
+ if (product.getStatus() != 1) {
+ throw new YamiShopBindException("商品[" + product.getProdName() + "]已下架");
+ }
+
+ // 商品需要改变的库存
+ Product mapProduct = prodStocksMap.get(prodId);
+
+ if (mapProduct == null) {
+ mapProduct = new Product();
+ mapProduct.setTotalStocks(0);
+ mapProduct.setProdId(prodId);
+ mapProduct.setProdName(product.getProdName());
+
+ }
+
+ if (product.getTotalStocks() != -1) {
+ mapProduct.setTotalStocks(mapProduct.getTotalStocks() + shopCartItem.getProdCount());
+ prodStocksMap.put(product.getProdId(), mapProduct);
+ }
+
+ // -1为无限库存
+ if (product.getTotalStocks() != -1 && mapProduct.getTotalStocks() > product.getTotalStocks()) {
+ throw new YamiShopBindException("商品:[" + product.getProdName() + "]库存不足");
+ }
+
+ return product;
+ }
+
+ @SuppressWarnings({"Duplicates"})
+ private Sku checkAndGetSku(Long skuId, ShopCartItemDto shopCartItem, Map skuStocksMap) {
+ // 获取sku信息
+ Sku sku = skuService.getSkuBySkuId(skuId);
+ if (sku == null) {
+ throw new YamiShopBindException("购物车包含无法识别的商品");
+ }
+
+ if (sku.getStatus() != 1) {
+ throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架");
+ }
+ // -1为无限库存
+ if (sku.getStocks() != -1 && shopCartItem.getProdCount() > sku.getStocks()) {
+ throw new YamiShopBindException("商品:[" + sku.getProdName() + "]库存不足");
+ }
+
+ if (sku.getStocks() != -1) {
+ Sku mapSku = new Sku();
+ mapSku.setProdId(sku.getProdId());
+ // 这里的库存是改变的库存
+ mapSku.setStocks(shopCartItem.getProdCount());
+ mapSku.setSkuId(sku.getSkuId());
+ mapSku.setProdName(sku.getProdName());
+ skuStocksMap.put(sku.getSkuId(), mapSku);
+ }
+ return sku;
+ }
+
+
+}
diff --git a/shop-api/src/main/resources/api.properties b/shop-api/src/main/resources/api.properties
new file mode 100644
index 0000000..82620f9
--- /dev/null
+++ b/shop-api/src/main/resources/api.properties
@@ -0,0 +1,3 @@
+api.datacenterId=1
+api.workerId=1
+api.domainName=http://xxx.com
\ No newline at end of file
diff --git a/shop-api/src/main/resources/application-dev.yml b/shop-api/src/main/resources/application-dev.yml
new file mode 100644
index 0000000..e014318
--- /dev/null
+++ b/shop-api/src/main/resources/application-dev.yml
@@ -0,0 +1,22 @@
+server:
+ port: 8086
+spring:
+ datasource:
+ url: jdbc:mysql://127.0.0.1:3306/yami_shops?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
+ username: root
+ password: root
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ type: com.zaxxer.hikari.HikariDataSource
+ hikari:
+ minimum-idle: 0
+ maximum-pool-size: 20
+ idle-timeout: 10000
+ connection-test-query: select 1
+ data:
+ redis:
+ host: 127.0.0.1
+ port: 6379
+ database: 0
+logging:
+ config: classpath:logback/logback-dev.xml
+
diff --git a/shop-api/src/main/resources/application-docker.yml b/shop-api/src/main/resources/application-docker.yml
new file mode 100644
index 0000000..2ccd9fe
--- /dev/null
+++ b/shop-api/src/main/resources/application-docker.yml
@@ -0,0 +1,21 @@
+server:
+ port: 8086
+
+spring:
+ datasource:
+ url: jdbc:mysql://${MYSQL_HOST:mall4j-mysql}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:yami_shops}?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
+ username: ${MYSQL_USERNAME:root}
+ password: ${MYSQL_PASSWORD:root}
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ type: com.zaxxer.hikari.HikariDataSource
+ hikari:
+ minimum-idle: 0
+ maximum-pool-size: 20
+ connection-test-query: select 1
+ data:
+ redis:
+ host: ${REDIS_HOST:mall4j-redis}
+ port: ${REDIS_PORT:6379}
+ database: ${REDIS_DATABASE:0}
+logging:
+ config: classpath:logback/logback-prod.xml
diff --git a/shop-api/src/main/resources/application-prod.yml b/shop-api/src/main/resources/application-prod.yml
new file mode 100644
index 0000000..d25012f
--- /dev/null
+++ b/shop-api/src/main/resources/application-prod.yml
@@ -0,0 +1,21 @@
+server:
+ port: 8112
+
+spring:
+ datasource:
+ url: jdbc:mysql://127.0.0.1:3306/yami_shops?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
+ username: root
+ password: root
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ type: com.zaxxer.hikari.HikariDataSource
+ hikari:
+ minimum-idle: 0
+ maximum-pool-size: 20
+ connection-test-query: select 1
+ data:
+ redis:
+ host: 127.0.0.1
+ port: 6379
+ database: ${REDIS_DATABASE:0}
+logging:
+ config: classpath:logback/logback-prod.xml
diff --git a/shop-api/src/main/resources/application.yml b/shop-api/src/main/resources/application.yml
new file mode 100644
index 0000000..da39db5
--- /dev/null
+++ b/shop-api/src/main/resources/application.yml
@@ -0,0 +1,42 @@
+spring:
+ # 环境 dev|test|prod
+ profiles:
+ active: dev
+ #文件上传设置
+ servlet:
+ multipart:
+ max-file-size: 100MB
+ max-request-size: 100MB
+ enabled: true
+ jackson:
+ date-format: yyyy-MM-dd HH:mm:ss
+ time-zone: GMT+8
+# mybaits-plus配置
+mybatis-plus:
+ # MyBatis Mapper所对应的XML文件位置
+ mapper-locations: classpath*:/mapper/*Mapper.xml
+ global-config:
+ # 关闭MP3.0自带的banner
+ banner: false
+ db-config:
+ # 主键类型 0:数据库ID自增 1.未定义 2.用户输入 3 id_worker 4.uuid 5.id_worker字符串表示
+ id-type: AUTO
+ #字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
+ field-strategy: NOT_NULL
+ # 默认数据库表下划线命名
+ table-underline: true
+
+management:
+ server:
+ add-application-context-header: false
+sa-token:
+ # token名称 (同时也是cookie名称)
+ token-name: Authorization
+ # 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
+ is-concurrent: true
+ # 在多人登录同一账号时,是否共用一个token(不共用,避免登出时导致其他用户也登出)
+ is-share: false
+ # token风格(默认可取值:uuid、simple-uuid、random-32、random-64、random-128、tik)
+ token-style: uuid
+ # 是否输出操作日志
+ is-log: false
diff --git a/shop-api/src/main/resources/banner.txt b/shop-api/src/main/resources/banner.txt
new file mode 100644
index 0000000..9fdec0d
--- /dev/null
+++ b/shop-api/src/main/resources/banner.txt
@@ -0,0 +1,11 @@
+ .----------------. .----------------. .----------------. .----------------. .----------------. .----------------.
+| .--------------. || .--------------. || .--------------. || .--------------. || .--------------. || .--------------. |
+| | ____ ____ | || | __ | || | _____ | || | _____ | || | _ _ | || | _____ | |
+| ||_ \ / _|| || | / \ | || | |_ _| | || | |_ _| | || | | | | | | || | |_ _| | |
+| | | \/ | | || | / /\ \ | || | | | | || | | | | || | | |__| |_ | || | | | | |
+| | | |\ /| | | || | / ____ \ | || | | | _ | || | | | _ | || | |____ _| | || | _ | | | |
+| | _| |_\/_| |_ | || | _/ / \ \_ | || | _| |__/ | | || | _| |__/ | | || | _| |_ | || | | |_' | | |
+| ||_____||_____|| || ||____| |____|| || | |________| | || | |________| | || | |_____| | || | `.___.' | |
+| | | || | | || | | || | | || | | || | | |
+| '--------------' || '--------------' || '--------------' || '--------------' || '--------------' || '--------------' |
+ '----------------' '----------------' '----------------' '----------------' '----------------' '----------------'
diff --git a/shop-api/src/main/resources/logback/logback-dev.xml b/shop-api/src/main/resources/logback/logback-dev.xml
new file mode 100644
index 0000000..37ddf47
--- /dev/null
+++ b/shop-api/src/main/resources/logback/logback-dev.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shop-api/src/main/resources/logback/logback-prod.xml b/shop-api/src/main/resources/logback/logback-prod.xml
new file mode 100644
index 0000000..e4809c9
--- /dev/null
+++ b/shop-api/src/main/resources/logback/logback-prod.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+ true
+
+ ${FILE_LOG_PATTERN}
+ UTF-8
+
+ ${PROJECT_PATH}/log/api.log
+
+
+ ${logging.level}
+
+
+ ${PROJECT_PATH}/log/api/%d{yyyy-MM}/api-%d{yyyy-MM-dd}-%i.log.gz
+ ${LOG_FILE_MAX_SIZE}
+ ${LOG_FILE_MAX_HISTORY}
+
+
+
+
+
+
+
+
+
+
+
+
+
+