feat:订单项目结构调整

This commit is contained in:
haoxr 2021-02-28 23:46:25 +08:00
parent ec223bbf1e
commit 0b223cb31e
68 changed files with 3576 additions and 18 deletions

168
mall-oms/oms-boot/pom.xml Normal file
View File

@ -0,0 +1,168 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mall-oms</artifactId>
<groupId>com.youlai</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>oms-boot</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
<artifactId>oms-api</artifactId>
<version>${youlai.version}</version>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
<artifactId>pms-api</artifactId>
<version>${youlai.version}</version>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
<artifactId>common-mybatis</artifactId>
<version>${youlai.version}</version>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
<artifactId>common-web</artifactId>
<version>${youlai.version}</version>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
<artifactId>common-redis</artifactId>
<version>${youlai.version}</version>
</dependency>
<dependency>
<groupId>com.youlai</groupId>
<artifactId>common-rabbitmq</artifactId>
<version>${youlai.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- nacos 依赖-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<!-- 排除依赖 指定版本和服务器端一致 -->
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</exclusion>
<exclusion>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<!--执行mvn package,即执行 mvn clean package docker:build-->
<!-- <execution>
<id>build-image</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
</execution>-->
</executions>
<configuration>
<!-- 镜像名称 -->
<imageName>${project.artifactId}</imageName>
<!-- 指定标签 -->
<imageTags>
<imageTag>latest</imageTag>
</imageTags>
<!-- 基础镜像-->
<baseImage>openjdk:8-jdk-alpine</baseImage>
<!-- 切换到容器工作目录-->
<workdir>/</workdir>
<entryPoint>["java","-jar","${project.build.finalName}.jar"]</entryPoint>
<!-- 指定远程 Docker API地址 -->
<dockerHost>https://b.youlai.store:2376</dockerHost>
<!-- 指定tls证书的目录 -->
<dockerCertPath>C:\cert\docker\b.youlai.store</dockerCertPath>
<!-- 复制 jar包到docker容器指定目录-->
<resources>
<resource>
<targetPath>/</targetPath>
<!-- 用于指定需要复制的根目录,${project.build.directory}表示target目录 -->
<directory>${project.build.directory}</directory>
<!-- 用于指定需要复制的文件,${project.build.finalName}.jar就是打包后的target目录下的jar包名称 -->
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,24 @@
package com.youlai.mall;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
//@EnableConfigurationProperties({ThreadPoolProperties.class})
@MapperScan({"com.youlai.mall.oms.mapper", "com.youlai.mall.oms.dao"})
@EnableRabbit
public class OmsApplication {
public static void main(String[] args) {
SpringApplication.run(OmsApplication.class);
}
}

View File

@ -1,6 +1,6 @@
package com.youlai.common.redis.constant;
package com.youlai.mall.oms.common;
public interface RedisKeyConstants {
public interface RedisConstants {
Long REDIS_KEY_TIME_OUT = 3600 * 24L;

View File

@ -0,0 +1,78 @@
package com.youlai.mall.oms.config;
import com.google.common.collect.Lists;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.annotation.Order;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.OAuthBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
import java.util.ArrayList;
import java.util.List;
/**
* @Author haoxr
* @Date 2021-02-25 15:36
* @Version 1.0.0
*/
@Configuration
@EnableSwagger2WebMvc
@Import(BeanValidatorPluginsConfiguration.class)
public class SwaggerConfiguration {
@Bean
public Docket restApi() {
//schema
List<GrantType> grantTypes=new ArrayList<>();
//密码模式
String passwordTokenUrl="http://localhost:9999/youlai-auth/oauth/token";
ResourceOwnerPasswordCredentialsGrant resourceOwnerPasswordCredentialsGrant=new ResourceOwnerPasswordCredentialsGrant(passwordTokenUrl);
grantTypes.add(resourceOwnerPasswordCredentialsGrant);
OAuth oAuth=new OAuthBuilder().name("oauth2")
.grantTypes(grantTypes).build();
//context
//scope方位
List<AuthorizationScope> scopes=new ArrayList<>();
scopes.add(new AuthorizationScope("read","read resources"));
scopes.add(new AuthorizationScope("write","write resources"));
scopes.add(new AuthorizationScope("reads","read all resources"));
scopes.add(new AuthorizationScope("writes","write all resources"));
SecurityReference securityReference=new SecurityReference("oauth2",scopes.toArray(new AuthorizationScope[]{}));
SecurityContext securityContext=new SecurityContext(Lists.newArrayList(securityReference),PathSelectors.ant("/**"));
//schemas
List<SecurityScheme> securitySchemes=Lists.newArrayList(oAuth);
//securyContext
List<SecurityContext> securityContexts=Lists.newArrayList(securityContext);
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.youlai.mall.oms.controller"))
.paths(PathSelectors.any())
.build()
.securityContexts(securityContexts)
.securitySchemes(securitySchemes)
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("订单中心REST接口")
.description("<div style='font-size:14px;color:red;'>订单提交、秒杀接口</div>")
.termsOfServiceUrl("https://www.youlai.store")
.contact(new Contact("yl", "https://github.com/hxrui", "1490493387@qq.com"))
.license("Open Source")
.licenseUrl("https://www.apache.org/licenses/LICENSE-2.0")
.version("1.0.0")
.build();
}
}

View File

@ -0,0 +1,145 @@
package com.youlai.mall.oms.config.rabbitmq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
/**
* @author huawei
* @desc rabbitmq 业务相关配置类
* 订单相关业务统一使用 order-event-exchange 交换机
* order.delay.queue 队列用于存放需要延时多长时间处理的订单不需要被消费
* order.release.queue 队列用于真正执行处理订单需要被业务消费如果订单还没有付款需要自动关闭订单并释放库存
* <p>
* order.create.order 建立交换机与 order.delay.queue 队列的绑定关系目的是将新创建的订单放入延时队列中
* order.release.order 建立交换机与 order.release.queue 队列的绑定关系目的是将延时队列中到达延时时间的订单执行释放操作
* <p>
* 自动创建相关 交换机队列路由键
* @email huawei_code@163.com
* @date 2021/1/17
*/
@Configuration
@Slf4j
public class OmsRabbitConfig {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 订单死信-延时队列
*
* @return
*/
@Bean
public Queue orderDelayQueue() {
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", OmsRabbitConstants.ORDER_EVENT_EXCHANGE);
args.put("x-dead-letter-routing-key", OmsRabbitConstants.ORDER_RELEASE_ORDER_KEY);
args.put("x-message-ttl", 60000);
Queue queue = new Queue(OmsRabbitConstants.ORDER_DELAY_QUEUE, true, false, false, args);
return queue;
}
/**
* 释放订单队列
* 订单系统从该队列中取出订单order判断是否超时未支付
*
* @return
*/
@Bean
public Queue orderReleaseQueue() {
return new Queue(OmsRabbitConstants.ORDER_RELEASE_QUEUE, true, false, false);
}
/**
* Topic类型 订单交换机
* @return
*/
@Bean
public Exchange orderEventExchange() {
return new TopicExchange(OmsRabbitConstants.ORDER_EVENT_EXCHANGE, true, false);
}
/**
* 建立 order.delay.queue 队列与交换机绑定
* @return
*/
@Bean
public Binding orderCreateOrderBinding() {
//String destination, DestinationType destinationType, String exchange, String routingKey,
// @Nullable Map<String, Object> arguments
return new Binding(OmsRabbitConstants.ORDER_DELAY_QUEUE, Binding.DestinationType.QUEUE, OmsRabbitConstants.ORDER_EVENT_EXCHANGE, OmsRabbitConstants.ORDER_CREATE_ORDER_KEY, null);
}
/**
* 建立 order.release.queue 与交换机绑定
* @return
*/
@Bean
public Binding orderReleaseOrderBinding() {
return new Binding(OmsRabbitConstants.ORDER_RELEASE_QUEUE, Binding.DestinationType.QUEUE, OmsRabbitConstants.ORDER_EVENT_EXCHANGE, OmsRabbitConstants.ORDER_RELEASE_ORDER_KEY, null);
}
/**
* 生产者投递消息后如果Broker收到消息后会给生产者一个ACK生产者通过ACK可以确认这条消息是否正常发送到Broker这种方式是消息可靠性投递的核心
* 步骤1yaml文件中添加配置 spring.rabbitmq.publisher-confirm-type: correlated
* 步骤2编写代码
*/
@PostConstruct
public void setConfirmCallback() {
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
/**
* @param correlationData 发送消息时指定的唯一关联数据消息id
* @param ack 投递结果
* @param cause 失败原因
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
log.info("消息投递到交换机成功:[correlationData={}]", correlationData);
} else {
log.error("消息投递到交换机成功:[correlationData={},原因={}]", correlationData, cause);
}
//TODO 根据ACK状态做对应的消息更新操作
}
});
}
/**
*
* 注意下面两项必须同时配置
* # 开启阶段二(消息从E->Q)的确认回调 Exchange --> Queue returnCallback
* spring.rabbitmq.publisher-returns=true
*
* #为true,则交换机处理消息到路由失败则会返回给生产者
* spring.rabbitmq.template.mandatory=true
*/
@PostConstruct
public void setQueueCallback() {
rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
log.error("路由到队列失败,[消息内容:{},交换机:{},路由件:{},回复码:{},回复文本:{}]",
returnedMessage.getMessage(), returnedMessage.getExchange(),
returnedMessage.getRoutingKey(), returnedMessage.getReplyCode(), returnedMessage.getReplyText());
}
});
}
}

View File

@ -0,0 +1,22 @@
package com.youlai.mall.oms.config.rabbitmq;
/**
* @author huawei
* @desc RabbitMQ 相关常量
* @email huawei_code@163.com
* @date 2021/1/17
*/
public class OmsRabbitConstants {
public static final String ORDER_EVENT_EXCHANGE = "order_event_exchange";
public static final String ORDER_DELAY_QUEUE = "order.delay.queue";
public static final String ORDER_RELEASE_QUEUE = "order.release.queue";
public static final String ORDER_CREATE_ORDER_KEY = "order.create.order";
public static final String ORDER_RELEASE_ORDER_KEY = "order.release.order";
public static final Integer ORDER_RELEASE_TTL = 60000;
}

View File

@ -0,0 +1,42 @@
//package com.youlai.mall.oms.config.thread;
//
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.context.annotation.Bean;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.core.task.AsyncTaskExecutor;
//import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
//
//import java.util.concurrent.ThreadPoolExecutor;
//
///**
// * @author huawei
// * @desc 异步任务线程配置
// * @email huawei_code@163.com
// * @date 2021/1/16
// */
//@Configuration
//@Slf4j
//public class AsyncConfig {
//
// @Bean("asyncTaskExecutor")
// public AsyncTaskExecutor asyncTaskExecutor(ThreadPoolProperties properties) {
// log.info("loading asyncTaskExecutor ...");
// ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// //配置核心线程数
// executor.setCorePoolSize(properties.getCorePoolSize());
// //配置最大线程数
// executor.setMaxPoolSize(properties.getMaxPoolSize());
// //配置队列大小
// executor.setQueueCapacity(properties.getQueueCapacity());
// executor.setKeepAliveSeconds(properties.getKeepAliveSeconds());
// //配置线程池中的线程的名称前缀
// executor.setThreadNamePrefix("async-service-");
//
// // 设置拒绝策略当pool已经达到max size的时候如何处理新任务
// // CALLER_RUNS不在新线程中执行任务而是有调用者所在的线程来执行
// executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// //执行初始化
// executor.initialize();
// return executor;
// }
//}

View File

@ -0,0 +1,23 @@
//package com.youlai.mall.oms.config.thread;
//
//import lombok.Data;
//import org.springframework.boot.context.properties.ConfigurationProperties;
//
///**
// * @author huawei
// * @desc 线程池配置属性类
// * @email huawei_code@163.com
// * @date 2021/1/16
// */
//@Data
//@ConfigurationProperties(prefix = "task.pool")
//public class ThreadPoolProperties {
//
// private Integer corePoolSize;
//
// private Integer maxPoolSize;
//
// private Integer queueCapacity;
//
// private Integer keepAliveSeconds;
//}

View File

@ -0,0 +1,31 @@
package com.youlai.mall.oms.config.token;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author huawei
* @desc Token 幂等性校验接口注解
* 幂等性方案注解 + 拦截器
* 注解@TokenVerify 注解该注解有两个属性generate clean
* generate属性为true是表示请求该方法会生成一个uuid token同时放入redis中和用户请求中用于用户下一次请求校验默认为false
* clean如果属性为true表示请求该方法需要校验token唯一性并且在校验通过后清除redis中的token
* 拦截器TokenVerifyHandler
* 实现原理拦截所有的请求如果该请求被 @TokenVerify 注解标识首先判断该注解属性 generate 是否为true然后校验属性 clean 是否为true
* 最后 WebMvcConfig 配置中配置拦截器
* @email huawei_code@163.com
* @date 2021/1/21
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TokenVerify {
// 是否生成校验token
boolean generate() default false;
// 是否校验token
boolean verify() default false;
}

View File

@ -0,0 +1,45 @@
package com.youlai.mall.oms.config.token;
import com.youlai.mall.oms.service.TokenService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author huawei
* @desc
* @email huawei_code@163.com
* @date 2021/1/21
*/
@Component
@AllArgsConstructor
@Slf4j
public class TokenVerifyHandler extends HandlerInterceptorAdapter {
@Autowired
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
HandlerMethod method = (HandlerMethod) handler;
TokenVerify tokenVerify = method.getMethodAnnotation(TokenVerify.class);
if (tokenVerify != null) {
log.debug("请求:{} 使用 @TokenVerify 注解,被拦截器拦截", request.getRequestURI());
// 校验token判断是否重复提交
tokenService.checkToken(request);
}
return true;
}
}

View File

@ -0,0 +1,130 @@
package com.youlai.mall.oms.controller.admin;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.youlai.common.enums.BusinessTypeEnum;
import com.youlai.common.enums.QueryModeEnum;
import com.youlai.common.redis.component.BusinessNoGenerator;
import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode;
import com.youlai.mall.oms.bo.OrderBO;
import com.youlai.mall.oms.service.IOmsOrderService;
import com.youlai.mall.oms.pojo.OmsOrder;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@Api(tags = "【系统管理】订单服务")
@RestController("")
@RequestMapping("/api.admin/v1/orders")
@Slf4j
@AllArgsConstructor
public class OrderController {
private IOmsOrderService iOmsOrderService;
@ApiOperation(value = "列表分页", httpMethod = "GET")
@ApiImplicitParams({
@ApiImplicitParam(name = "queryMode", value = "查询模式", paramType = "query", dataType = "QueryModeEnum"),
@ApiImplicitParam(name = "page", value = "页码", paramType = "query", dataType = "Long"),
@ApiImplicitParam(name = "limit", value = "每页数量", paramType = "query", dataType = "Long"),
@ApiImplicitParam(name = "orderSn", value = "订单编号", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "status", value = "订单状态", paramType = "query", dataType = "Integer"),
@ApiImplicitParam(name = "startDate", value = "开始日期", paramType = "query", dataType = "String"),
@ApiImplicitParam(name = "endDate", value = "结束日期", paramType = "query", dataType = "String"),
})
@GetMapping
public Result list(
String queryMode,
Integer page,
Integer limit,
String orderSn,
Integer status,
String startDate,
String endDate
) {
QueryModeEnum queryModeEnum = QueryModeEnum.getValue(queryMode);
switch (queryModeEnum) {
case PAGE:
LambdaQueryWrapper<OmsOrder> queryWrapper = new LambdaQueryWrapper<OmsOrder>()
.like(StrUtil.isNotBlank(orderSn), OmsOrder::getOrderSn, orderSn)
.eq(status != null, OmsOrder::getStatus, status)
.apply(StrUtil.isNotBlank(startDate),
"date_format (gmt_crate,'%Y-%m-%d') >= date_format('" + startDate + "','%Y-%m-%d')")
.apply(StrUtil.isNotBlank(endDate),
"date_format (gmt_crate,'%Y-%m-%d') <= date_format('" + endDate + "','%Y-%m-%d')")
.orderByDesc(OmsOrder::getGmtModified)
.orderByDesc(OmsOrder::getGmtCreate);
Page<OmsOrder> result = iOmsOrderService.page(new Page<>(page, limit), queryWrapper);
return Result.success(result.getRecords(), result.getTotal());
default:
return Result.failed(ResultCode.QUERY_MODE_IS_NULL);
}
}
@ApiOperation(value = "订单详情", httpMethod = "GET")
@ApiImplicitParam(name = "id", value = "订单id", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{id}")
public Result detail(@PathVariable Long id) {
OrderBO order = iOmsOrderService.getByOrderId(id);
return Result.success(order);
}
@ApiOperation(value = "订单提交", httpMethod = "POST")
@ApiImplicitParam(name = "orderBO", value = "实体JSON对象", required = true, paramType = "body", dataType = "OrderBO")
@PostMapping
public Result add(@RequestBody OrderBO orderBO) {
boolean status = iOmsOrderService.save(orderBO);
return Result.judge(status);
}
@ApiOperation(value = "修改订单", httpMethod = "PUT")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "订单id", required = true, paramType = "path", dataType = "Long"),
@ApiImplicitParam(name = "order", value = "实体JSON对象", required = true, paramType = "body", dataType = "OmsOrder")
})
@PutMapping(value = "/{id}")
public Result update(
@PathVariable Long id,
@RequestBody OmsOrder order) {
boolean status = iOmsOrderService.updateById(order);
return Result.judge(status);
}
@ApiOperation(value = "订单提交", httpMethod = "POST")
@PostMapping("/submit")
public Result submit(Boolean openTransaction) {
boolean status;
if (openTransaction) {
status = iOmsOrderService.submitWithGlobalTransactional();
} else {
status = iOmsOrderService.submit();
}
return Result.judge(status);
}
@ApiOperation(value = "订单详情", httpMethod = "GET")
@ApiImplicitParam(name = "id", value = "订单id", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{id}/detail")
public Result orderDetail(@PathVariable Long id) {
OmsOrder order = iOmsOrderService.getById(id);
return Result.success(order);
}
private BusinessNoGenerator businessNoGenerator;
@PostMapping("/order_sn")
public Result generateOrderSn() {
String orderSn = businessNoGenerator.generate(BusinessTypeEnum.ORDER);
log.info("订单编号:{}", orderSn);
return Result.success(orderSn);
}
}

View File

@ -0,0 +1,90 @@
package com.youlai.mall.oms.controller.app;
import com.youlai.common.result.Result;
import com.youlai.mall.oms.bo.CartItemBo;
import com.youlai.mall.oms.bo.CartItemCheckBo;
import com.youlai.mall.oms.pojo.vo.CartVo;
import com.youlai.mall.oms.service.CartService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* 购物车
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Api(tags = "购物车接口")
@RestController
@RequestMapping("/api.app/v1/carts")
@Slf4j
@AllArgsConstructor
public class CartController {
private CartService cartService;
@ApiOperation(value = "查询购物车", httpMethod = "GET")
@GetMapping
public Result<CartVo> detail() {
CartVo cart = cartService.detail();
return Result.success(cart);
}
@ApiOperation(value = "添加购物车", httpMethod = "POST")
@ApiImplicitParam(name = "skuId", value = "商品SKU Id", required = true, paramType = "param", dataType = "String")
@PostMapping
public Result<Object> save(@RequestParam("skuId") String skuId) throws ExecutionException, InterruptedException {
cartService.save(skuId);
return Result.success();
}
@ApiOperation(value = "修改购物车商品数量", httpMethod = "PUT")
@ApiImplicitParam(name = "cartItemBo", value = "实体JSON对象", required = true, paramType = "body", dataType = "CartItemBo")
@PutMapping
public Result<Object> update(@Validated @RequestBody CartItemBo cartItemBo) {
cartService.update(cartItemBo);
return Result.success();
}
@ApiOperation(value = "是否选择购物车中商品", httpMethod = "PUT")
@ApiImplicitParam(name = "cartItemChooseBo", value = "实体JSON对象", required = true, paramType = "body", dataType = "CartItemChooseBo")
@PutMapping("/check")
public Result<Object> check(@Validated @RequestBody CartItemCheckBo cartItemCheckBo) {
cartService.check(cartItemCheckBo);
return Result.success();
}
@ApiOperation(value = "全选/全不选择购物车", httpMethod = "PUT")
@ApiImplicitParam(name = "check", value = "全选/全不选", required = true, paramType = "param", dataType = "Long")
@PutMapping("/checkAll")
public Result<Object> checkAll(@RequestParam("check") Integer check) {
cartService.checkAll(check);
return Result.success();
}
@ApiOperation(value = "批量删除购物车", httpMethod = "DELETE")
@ApiImplicitParam(name = "skuIds", value = "商品sku id集合", required = true, paramType = "param", dataType = "List")
@DeleteMapping
public Result<Boolean> delete(@RequestParam("skuIds") List<String> skuIds) {
cartService.deleteBatch(skuIds);
return Result.success();
}
@ApiOperation(value = "清空购物车", httpMethod = "GET")
@GetMapping("/clear")
public Result<Boolean> clear() {
cartService.clear();
return Result.success();
}
}

View File

@ -0,0 +1,138 @@
package com.youlai.mall.oms.controller.app;
import com.youlai.common.result.Result;
import com.youlai.mall.oms.pojo.entity.OrderEntity;
import com.youlai.mall.oms.pojo.vo.OrderConfirmVO;
import com.youlai.mall.oms.pojo.vo.OrderListVO;
import com.youlai.mall.oms.pojo.vo.OrderSubmitResultVO;
import com.youlai.mall.oms.pojo.vo.OrderSubmitVO;
import com.youlai.mall.oms.service.OrderService;
import io.seata.spring.annotation.GlobalTransactional;
import io.swagger.annotations.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* 订单详情表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Api(tags = "APP订单详情接口")
@RestController
@RequestMapping("/api.app/v1/orders")
@Slf4j
@AllArgsConstructor
public class OrderController {
private OrderService orderService;
/**
* 订单确认信息生成订单
* 如果入参传了skuId则以当前skuId为准商品数量默认为1
* 如果没有传则从购物车中获取数据
* 如果购物车中没有数据则返回为空
*
* @return
*/
@ApiOperation(value = "订单确认信息", httpMethod = "POST")
@ApiImplicitParams({
@ApiImplicitParam(name = "skuId", value = "商品ID", required = true, paramType = "param", dataType = "String"),
@ApiImplicitParam(name = "number", value = "商品数量", required = true, defaultValue = "1", paramType = "param", dataType = "Integer")
})
@PostMapping("/confirm")
public Result<OrderConfirmVO> confirm(@RequestParam(value = "skuId", required = false) String skuId,
@RequestParam(value = "number", defaultValue = "1") Integer number) {
return Result.success(orderService.confirm(skuId, number));
}
@ApiOperation(value = "提交订单", httpMethod = "POST")
@ApiImplicitParam(name = "submit", value = "提交订单参数", required = true, paramType = "body", dataType = "OrderSubmitVO")
@PostMapping("/submit")
public Result<OrderSubmitResultVO> submit(@Valid @RequestBody OrderSubmitVO submit) throws ExecutionException, InterruptedException {
log.info("提交订单:{}", submit);
return Result.success(orderService.submit(submit));
}
/**
* 根据订单状态查询订单列表
* 步骤
* 1入参 status 表示订单状态
* 2status = 0 表示查询所有订单
* 3已删除订单无法查询
*/
@ApiOperation("订单列表查询")
@GetMapping("/list")
public Result<List<OrderListVO>> list(@ApiParam(name = "status",value = "订单状态",required = true,defaultValue = "0")
@RequestParam(value = "status",required = true,defaultValue = "0") Integer status) {
List<OrderListVO> orderList = orderService.list(status);
return Result.success(orderList);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
@GlobalTransactional
//@RequiresPermissions("oms:order:info")
public Result<OrderEntity> info(@PathVariable("id") Long id) {
OrderEntity order = orderService.getById(id);
return Result.success(order);
}
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("oms:order:save")
public Result<Object> save(@RequestBody OrderEntity order) {
orderService.save(order);
return Result.success();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("oms:order:update")
public Result<Object> update(@RequestBody OrderEntity order) {
orderService.updateById(order);
return Result.success();
}
/**
* 取消订单
*/
@PutMapping("/cancel")
@ApiImplicitParam(name = "id", value = "订单ID", required = true, paramType = "param", dataType = "String")
public Result<Object> cancelOrder(@RequestParam("id") String id) {
orderService.cancelOrder(id);
return Result.success();
}
/**
* 删除
*/
@DeleteMapping
@ApiImplicitParam(name = "id", value = "订单ID", required = true, paramType = "param", dataType = "String")
public Result<Object> delete(@RequestParam("id") String id) {
orderService.deleteOrder(id);
return Result.success();
}
}

View File

@ -0,0 +1,84 @@
package com.youlai.mall.oms.controller.app;
import com.youlai.common.result.Result;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderDeliveryEntity;
import com.youlai.mall.oms.service.OrderDeliveryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Map;
/**
* 订单物流记录表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@RestController
@RequestMapping("oms/orderdelivery")
public class OrderDeliveryController {
@Autowired
private OrderDeliveryService orderDeliveryService;
/**
* 列表
*/
@RequestMapping("/list")
//@RequiresPermissions("oms:orderdelivery:list")
public Result<PageUtils> list(@RequestParam Map<String, Object> params){
PageUtils page = orderDeliveryService.queryPage(params);
return Result.success(page);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
//@RequiresPermissions("oms:orderdelivery:info")
public Result<OrderDeliveryEntity> info(@PathVariable("id") Long id){
OrderDeliveryEntity orderDelivery = orderDeliveryService.getById(id);
return Result.success(orderDelivery);
}
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("oms:orderdelivery:save")
public Result<Object> save(@RequestBody OrderDeliveryEntity orderDelivery){
orderDeliveryService.save(orderDelivery);
return Result.success();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("oms:orderdelivery:update")
public Result<Object> update(@RequestBody OrderDeliveryEntity orderDelivery){
orderDeliveryService.updateById(orderDelivery);
return Result.success();
}
/**
* 删除
*/
@RequestMapping("/delete")
//@RequiresPermissions("oms:orderdelivery:delete")
public Result<Object> delete(@RequestBody Long[] ids){
orderDeliveryService.removeByIds(Arrays.asList(ids));
return Result.success();
}
}

View File

@ -0,0 +1,84 @@
package com.youlai.mall.oms.controller.app;
import com.youlai.common.result.Result;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderGoodsEntity;
import com.youlai.mall.oms.service.OrderGoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Map;
/**
* 订单商品信息表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@RestController
@RequestMapping("oms/ordergoods")
public class OrderGoodsController {
@Autowired
private OrderGoodsService orderGoodsService;
/**
* 列表
*/
@RequestMapping("/list")
//@RequiresPermissions("oms:ordergoods:list")
public Result<PageUtils> list(@RequestParam Map<String, Object> params){
PageUtils page = orderGoodsService.queryPage(params);
return Result.success(page);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
//@RequiresPermissions("oms:ordergoods:info")
public Result<OrderGoodsEntity> info(@PathVariable("id") Long id){
OrderGoodsEntity orderGoods = orderGoodsService.getById(id);
return Result.success(orderGoods);
}
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("oms:ordergoods:save")
public Result<Object> save(@RequestBody OrderGoodsEntity orderGoods){
orderGoodsService.save(orderGoods);
return Result.success();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("oms:ordergoods:update")
public Result<Object> update(@RequestBody OrderGoodsEntity orderGoods){
orderGoodsService.updateById(orderGoods);
return Result.success();
}
/**
* 删除
*/
@RequestMapping("/delete")
//@RequiresPermissions("oms:ordergoods:delete")
public Result<Object> delete(@RequestBody Long[] ids){
orderGoodsService.removeByIds(Arrays.asList(ids));
return Result.success();
}
}

View File

@ -0,0 +1,84 @@
package com.youlai.mall.oms.controller.app;
import com.youlai.common.result.Result;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderLogsEntity;
import com.youlai.mall.oms.service.OrderLogsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Map;
/**
* 订单操作历史记录
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@RestController
@RequestMapping("oms/order/logs")
public class OrderLogsController {
@Autowired
private OrderLogsService orderLogsService;
/**
* 列表
*/
@RequestMapping("/list")
//@RequiresPermissions("oms:orderlogs:list")
public Result<PageUtils> list(@RequestParam Map<String, Object> params){
PageUtils page = orderLogsService.queryPage(params);
return Result.success(page);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
//@RequiresPermissions("oms:orderlogs:info")
public Result<OrderLogsEntity> info(@PathVariable("id") Long id){
OrderLogsEntity orderLogs = orderLogsService.getById(id);
return Result.success(orderLogs);
}
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("oms:orderlogs:save")
public Result<Object> save(@RequestBody OrderLogsEntity orderLogs){
orderLogsService.save(orderLogs);
return Result.success();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("oms:orderlogs:update")
public Result<Object> update(@RequestBody OrderLogsEntity orderLogs){
orderLogsService.updateById(orderLogs);
return Result.success();
}
/**
* 删除
*/
@RequestMapping("/delete")
//@RequiresPermissions("oms:orderlogs:delete")
public Result<Object> delete(@RequestBody Long[] ids){
orderLogsService.removeByIds(Arrays.asList(ids));
return Result.success();
}
}

View File

@ -0,0 +1,122 @@
package com.youlai.mall.oms.controller.app;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.common.result.Result;
import com.youlai.common.utils.EnumUtils;
import com.youlai.mall.oms.enums.OrderPayTypeEnum;
import com.youlai.mall.oms.pojo.entity.OrderPayEntity;
import com.youlai.mall.oms.pojo.form.OrderPayForm;
import com.youlai.mall.oms.pojo.vo.PayInfoVO;
import com.youlai.mall.oms.service.OrderPayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Map;
/**
* 订单支付服务
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Api(tags = "订单支付服务")
@RestController
@RequestMapping("/api.app/v1/order/pay")
@Slf4j
public class OrderPayController {
@Autowired
private OrderPayService orderPayService;
/**
* 订单支付
* 1根据支付类型选择正确支付方式1微信支付2支付宝支付3余额支付
* 2根据订单id查询订单价格进行支付在整个支付的过程中进行事务控制保证整个操作的原子性
* 3将支付结果记录日志并返回给前端
*
* @param orderPayForm 订单支付表单
* @return
*/
@ApiOperation("订单支付")
@PostMapping
public Result doPay(@Validated @RequestBody OrderPayForm orderPayForm) {
OrderPayTypeEnum payTypeEnum = EnumUtils.getByCode(orderPayForm.getPayType(), OrderPayTypeEnum.class);
if (payTypeEnum == null) {
return Result.failed("请选择正确的支付方式");
}
log.info("订单支付orderId={},支付方式={}", orderPayForm.getOrderId(), payTypeEnum.desc);
if (payTypeEnum == OrderPayTypeEnum.BALANCE) {
orderPayService.balancePay(orderPayForm.getOrderId());
}
return Result.success();
}
@ApiOperation(value = "获取订单支付详情")
@GetMapping("/info")
public Result<PayInfoVO> info(@ApiParam(name = "orderId", value = "订单ID", required = true, defaultValue = "1") @RequestParam("orderId") String orderId) {
return Result.success(orderPayService.info(orderId));
}
/**
* 列表
*/
@RequestMapping("/list")
public Result<PageUtils> list(@RequestParam Map<String, Object> params) {
PageUtils page = orderPayService.queryPage(params);
return Result.success(page);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
//@RequiresPermissions("oms:orderpay:info")
public Result<OrderPayEntity> info(@PathVariable("id") Long id) {
OrderPayEntity orderPay = orderPayService.getById(id);
return Result.success(orderPay);
}
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("oms:orderpay:save")
public Result<Object> save(@RequestBody OrderPayEntity orderPay) {
orderPayService.save(orderPay);
return Result.success();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("oms:orderpay:update")
public Result<Object> update(@RequestBody OrderPayEntity orderPay) {
orderPayService.updateById(orderPay);
return Result.success();
}
/**
* 删除
*/
@RequestMapping("/delete")
//@RequiresPermissions("oms:orderpay:delete")
public Result<Object> delete(@RequestBody Long[] ids) {
orderPayService.removeByIds(Arrays.asList(ids));
return Result.success();
}
}

View File

@ -0,0 +1,84 @@
package com.youlai.mall.oms.controller.app;
import com.youlai.common.result.Result;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderSettingEntity;
import com.youlai.mall.oms.service.OrderSettingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Map;
/**
* 订单配置信息
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@RestController
@RequestMapping("oms/ordersetting")
public class OrderSettingController {
@Autowired
private OrderSettingService orderSettingService;
/**
* 列表
*/
@RequestMapping("/list")
//@RequiresPermissions("oms:ordersetting:list")
public Result<PageUtils> list(@RequestParam Map<String, Object> params){
PageUtils page = orderSettingService.queryPage(params);
return Result.success(page);
}
/**
* 信息
*/
@RequestMapping("/info/{id}")
//@RequiresPermissions("oms:ordersetting:info")
public Result<OrderSettingEntity> info(@PathVariable("id") Long id){
OrderSettingEntity orderSetting = orderSettingService.getById(id);
return Result.success(orderSetting);
}
/**
* 保存
*/
@RequestMapping("/save")
//@RequiresPermissions("oms:ordersetting:save")
public Result<Object> save(@RequestBody OrderSettingEntity orderSetting){
orderSettingService.save(orderSetting);
return Result.success();
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("oms:ordersetting:update")
public Result<Object> update(@RequestBody OrderSettingEntity orderSetting){
orderSettingService.updateById(orderSetting);
return Result.success();
}
/**
* 删除
*/
@RequestMapping("/delete")
//@RequiresPermissions("oms:ordersetting:delete")
public Result<Object> delete(@RequestBody Long[] ids){
orderSettingService.removeByIds(Arrays.asList(ids));
return Result.success();
}
}

View File

@ -0,0 +1,17 @@
package com.youlai.mall.oms.dao;
import com.youlai.mall.oms.pojo.entity.OrderEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单详情表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Mapper
public interface OrderDao extends BaseMapper<OrderEntity> {
}

View File

@ -0,0 +1,17 @@
package com.youlai.mall.oms.dao;
import com.youlai.mall.oms.pojo.entity.OrderDeliveryEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单物流记录表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Mapper
public interface OrderDeliveryDao extends BaseMapper<OrderDeliveryEntity> {
}

View File

@ -0,0 +1,17 @@
package com.youlai.mall.oms.dao;
import com.youlai.mall.oms.pojo.entity.OrderGoodsEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单商品信息表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Mapper
public interface OrderGoodsDao extends BaseMapper<OrderGoodsEntity> {
}

View File

@ -0,0 +1,17 @@
package com.youlai.mall.oms.dao;
import com.youlai.mall.oms.pojo.entity.OrderLogsEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单操作历史记录
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Mapper
public interface OrderLogsDao extends BaseMapper<OrderLogsEntity> {
}

View File

@ -0,0 +1,17 @@
package com.youlai.mall.oms.dao;
import com.youlai.mall.oms.pojo.entity.OrderPayEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 支付信息表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Mapper
public interface OrderPayDao extends BaseMapper<OrderPayEntity> {
}

View File

@ -0,0 +1,17 @@
package com.youlai.mall.oms.dao;
import com.youlai.mall.oms.pojo.entity.OrderSettingEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* 订单配置信息
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
@Mapper
public interface OrderSettingDao extends BaseMapper<OrderSettingEntity> {
}

View File

@ -0,0 +1,10 @@
package com.youlai.mall.oms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.youlai.mall.oms.pojo.OmsOrderItem;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface OmsOrderItemMapper extends BaseMapper<OmsOrderItem> {
}

View File

@ -0,0 +1,10 @@
package com.youlai.mall.oms.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.youlai.mall.oms.pojo.OmsOrder;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface OmsOrderMapper extends BaseMapper<OmsOrder> {
}

View File

@ -0,0 +1,68 @@
package com.youlai.mall.oms.service;
import com.youlai.mall.oms.bo.CartItemBo;
import com.youlai.mall.oms.bo.CartItemCheckBo;
import com.youlai.mall.oms.pojo.vo.CartVo;
import java.util.List;
import java.util.concurrent.ExecutionException;
/**
* 购物车业务接口
*/
public interface CartService {
/**
* 添加商品到购物车
* @param skuId 商品模型
*/
void save(String skuId) throws ExecutionException, InterruptedException;
/**
* 修改购物车商品数量
* @param cartItemBo
*/
void update(CartItemBo cartItemBo);
/**
* 修改购物车中商品是否选中状态
* @param cartItemCheckBo
*/
void check(CartItemCheckBo cartItemCheckBo);
/**
* 全选/全不选购物车
* @param check
*/
void checkAll(Integer check);
/**
* 删除购物车中的商品
* @param skuId 商品id
*/
void delete(Long skuId);
/**
* 批量删除购物车中的商品
* @param skuIds 商品id集合
*/
void deleteBatch(List<String> skuIds);
/**
* 查询购物车详情
* @return
*/
CartVo detail();
/**
* 清空购物车
*/
void clear();
/**
* 清空购物车中已选择商品
*/
void cleanSelected();
}

View File

@ -0,0 +1,8 @@
package com.youlai.mall.oms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.mall.oms.pojo.OmsOrderItem;
public interface IOmsOrderItemService extends IService<OmsOrderItem> {
}

View File

@ -0,0 +1,16 @@
package com.youlai.mall.oms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.mall.oms.bo.OrderBO;
import com.youlai.mall.oms.pojo.OmsOrder;
public interface IOmsOrderService extends IService<OmsOrder> {
boolean save(OrderBO orderBO);
OrderBO getByOrderId(Long id);
boolean submit();
boolean submitWithGlobalTransactional();
}

View File

@ -0,0 +1,20 @@
package com.youlai.mall.oms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderDeliveryEntity;
import java.util.Map;
/**
* 订单物流记录表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
public interface OrderDeliveryService extends IService<OrderDeliveryEntity> {
PageUtils queryPage(Map<String, Object> params);
}

View File

@ -0,0 +1,26 @@
package com.youlai.mall.oms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderGoodsEntity;
import java.util.List;
import java.util.Map;
/**
* 订单商品信息表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
public interface OrderGoodsService extends IService<OrderGoodsEntity> {
PageUtils queryPage(Map<String, Object> params);
List<OrderGoodsEntity> getByOrderId(Long orderId);
Map<Long,List<OrderGoodsEntity>> getByOrderIds(List<Long> orderIds);
}

View File

@ -0,0 +1,37 @@
package com.youlai.mall.oms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderLogsEntity;
import java.util.Map;
/**
* 订单操作历史记录
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
public interface OrderLogsService extends IService<OrderLogsEntity> {
PageUtils queryPage(Map<String, Object> params);
/**
* 添加订单操作日志记录
* @param orderId 订单id
* @param orderStatus 订单状态
* @param user 操作人员
* @param detail 描述信息
*/
void addOrderLogs(Long orderId, Integer orderStatus, String user, String detail);
/**
* 添加订单操作日志记录
* @param orderId 订单id
* @param orderStatus 订单状态
* @param detail 描述
*/
void addOrderLogs(Long orderId, Integer orderStatus, String detail);
}

View File

@ -0,0 +1,34 @@
package com.youlai.mall.oms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderPayEntity;
import com.youlai.mall.oms.pojo.vo.PayInfoVO;
import java.util.Map;
/**
* 支付信息表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
public interface OrderPayService extends IService<OrderPayEntity> {
PageUtils queryPage(Map<String, Object> params);
/**
* 获取订单支付详情
* @param orderId 订单id
* @return
*/
PayInfoVO info(String orderId);
/**
* 订单支付
* @param orderId 订单ID
*/
void balancePay(String orderId);
}

View File

@ -0,0 +1,22 @@
package com.youlai.mall.oms.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
/**
* @author huawei
* @desc Order RabbitMQ 业务类
* @email huawei_code@163.com
* @date 2021/2/4
*/
public interface OrderRabbitService {
/**
* 订单超时释放
*
* @param orderSn
* @param message
* @param channel
*/
void releaseOrder(String orderSn, Message message, Channel channel);
}

View File

@ -0,0 +1,89 @@
package com.youlai.mall.oms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderEntity;
import com.youlai.mall.oms.pojo.vo.OrderConfirmVO;
import com.youlai.mall.oms.pojo.vo.OrderListVO;
import com.youlai.mall.oms.pojo.vo.OrderSubmitResultVO;
import com.youlai.mall.oms.pojo.vo.OrderSubmitVO;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
/**
* 订单详情表
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
public interface OrderService extends IService<OrderEntity> {
PageUtils queryPage(Map<String, Object> params);
/**
* 订单确认页信息
*
* @param skuId 商品id非必填参数
* @param number 商品数量
* @return
*/
OrderConfirmVO confirm(String skuId, Integer number);
/**
* 提交订单
*
* @param submit 订单提交参数
*/
OrderSubmitResultVO submit(OrderSubmitVO submit) throws ExecutionException, InterruptedException;
/**
* 根据订单号查询订单详情
*
* @param orderSn 订单号
* @return
*/
OrderEntity getByOrderSn(String orderSn);
/**
* 系统关闭订单
*
* @param orderSn 订单号
*/
boolean closeOrderBySystem(String orderSn);
/**
* 取消订单接口
*
* @param id 订单id
* @return 是否取消成功
*/
boolean cancelOrder(String id);
/**
* 删除订单
*
* @param id 订单id
* @return 是否删除成功
*/
boolean deleteOrder(String id);
/**
* 订单列表查询
*
* @param status 订单状态
* @return 订单列表
*/
List<OrderListVO> list(Integer status);
/**
* 根据订单id获取订单信息
*
* @param id 订单id
* @return 订单信息
*/
OrderEntity getByOrderId(String id);
}

View File

@ -0,0 +1,20 @@
package com.youlai.mall.oms.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.pojo.entity.OrderSettingEntity;
import java.util.Map;
/**
* 订单配置信息
*
* @author huawei
* @email huawei_code@163.com
* @date 2020-12-30 22:31:10
*/
public interface OrderSettingService extends IService<OrderSettingEntity> {
PageUtils queryPage(Map<String, Object> params);
}

View File

@ -0,0 +1,25 @@
package com.youlai.mall.oms.service;
import javax.servlet.http.HttpServletRequest;
/**
* @author huawei
* @desc
* @email huawei_code@163.com
* @date 2021/1/21
*/
public interface TokenService {
/**
* 为当前用户请求生成Token
*
* @return token
*/
String generateToken();
/**
* 校验 Token 是否正确
* @param request 当前请求
*/
void checkToken(HttpServletRequest request);
}

View File

@ -0,0 +1,175 @@
package com.youlai.mall.oms.service.impl;
import com.youlai.common.result.Result;
import com.youlai.common.web.util.RequestUtils;
import com.youlai.mall.pms.api.ProductFeignService;
import com.youlai.mall.oms.bo.CartItemBo;
import com.youlai.mall.oms.bo.CartItemCheckBo;
import com.youlai.mall.pms.pojo.dto.SkuDTO;
import com.youlai.mall.oms.pojo.vo.CartItemVo;
import com.youlai.mall.oms.pojo.vo.CartVo;
import com.youlai.mall.oms.service.CartService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import static com.youlai.common.redis.constant.RedisKeyConstants.MALL_CART_KEY;
/**
* 购物车模块搭建步骤
* 1用户登录后才能操作购物车
* *********添加购物车*************
* 1获取当前登录用户
* 2添加商品到购物车
* 2.1如果购物车中已经有该商品的相关数据
* 2.1.1修改购物车商品数量
* 2.2如果购物车中没有该商品新增该商品
* 2.2.1调用商品服务查询商品sku详情
* 2.2.2封装CartItem实体类
* *********修改购物车*************
* *********删除购物车*************
* *********查询购物车*************
*/
@Service
@Slf4j
@AllArgsConstructor
public class CartServiceImpl implements CartService {
private RedisTemplate redisTemplate;
private ProductFeignService productFeignService;
@Override
public void save(String skuId) throws ExecutionException, InterruptedException {
log.info("添加商品到购物车form:{}", skuId);
BoundHashOperations cartOps = getCartOps();
if (cartOps.get(skuId) != null) {
CartItemVo cartItem = (CartItemVo) cartOps.get(skuId);
Integer number = cartItem.getNumber() + 1;
cartItem.setNumber(number);
cartOps.put(skuId, cartItem);
return;
}
CartItemVo cartItem = new CartItemVo();
// 添加新商品到购物车
CompletableFuture<Void> skuInfoFuture = CompletableFuture.runAsync(() -> {
//1远程查询商品详情
Result<SkuDTO> skuInfo = productFeignService.getSkuById(Long.parseLong(skuId));
SkuDTO data = skuInfo.getData();
cartItem.setSkuId(Long.parseLong(skuId));
cartItem.setChecked(true);
cartItem.setSkuName(data.getName());
cartItem.setSkuImg(data.getPicUrl());
cartItem.setNumber(1);
cartItem.setPrice(data.getPrice());
cartItem.setStock(data.getStock());
});
//2远程查询商品属性
//3远程查询库存
CompletableFuture<Void> allOf = CompletableFuture.allOf(skuInfoFuture);
allOf.get();
cartOps.put(skuId, cartItem);
}
@Override
public void update(CartItemBo cartItemBo) {
log.info("修改购物车商品数量form:{}", cartItemBo);
BoundHashOperations cartOps = getCartOps();
CartItemVo cartItem = (CartItemVo) cartOps.get(cartItemBo.getSkuId().toString());
if (cartItem == null) {
return;
}
cartItem.setNumber(cartItemBo.getNumber());
cartOps.put(cartItemBo.getSkuId().toString(), cartItem);
}
@Override
public void check(CartItemCheckBo cartItemCheckBo) {
log.info("修改购物车商品选中状态form:{}", cartItemCheckBo);
BoundHashOperations cartOps = getCartOps();
CartItemVo cartItem = (CartItemVo) cartOps.get(cartItemCheckBo.getSkuId().toString());
if (cartItem == null) {
return;
}
cartItem.setChecked(cartItemCheckBo.getCheck() == 1);
cartOps.put(cartItemCheckBo.getSkuId().toString(), cartItem);
}
@Override
public void checkAll(Integer check) {
log.info("全选/全不选购物车商品状态check:{}", check);
BoundHashOperations cartOps = getCartOps();
for (Object value : cartOps.values()) {
CartItemVo cartItem = (CartItemVo) value;
cartItem.setChecked(check == 1);
cartOps.put(cartItem.getSkuId().toString(), cartItem);
}
}
@Override
public void delete(Long skuId) {
log.info("删除购物车商品id:{}", skuId);
BoundHashOperations cartOps = getCartOps();
cartOps.delete(skuId);
}
@Override
public void deleteBatch(List<String> skuIds) {
log.info("批量删除购物车商品id集合{}", skuIds);
BoundHashOperations cartOps = getCartOps();
for (String skuId : skuIds) {
cartOps.delete(skuId);
}
}
@Override
public CartVo detail() {
log.info("查询购物车详情");
BoundHashOperations cartOps = getCartOps();
List<CartItemVo> items = cartOps.values();
CartVo cartVo = new CartVo();
cartVo.setItems(items);
return cartVo;
}
@Override
public void clear() {
log.info("清空购物车");
Long userId = RequestUtils.getUserId();
String cartKey = MALL_CART_KEY + userId;
redisTemplate.delete(cartKey);
}
@Override
public void cleanSelected() {
log.info("清空购物车中已选择商品");
BoundHashOperations cartOps = getCartOps();
for (Object value : cartOps.values()) {
CartItemVo cartItem = (CartItemVo) value;
if (cartItem.isChecked()) {
log.info("清空购物车中商品商品id{} | 名称:{}", cartItem.getSkuId(), cartItem.getSkuName());
cartOps.delete(cartItem.getSkuId().toString());
}
}
}
private BoundHashOperations getCartOps() {
Long userId = RequestUtils.getUserId();
String cartKey = MALL_CART_KEY + userId;
BoundHashOperations operations = redisTemplate.boundHashOps(cartKey);
return operations;
}
}

View File

@ -0,0 +1,11 @@
package com.youlai.mall.oms.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.mall.oms.mapper.OmsOrderItemMapper;
import com.youlai.mall.oms.pojo.OmsOrderItem;
import com.youlai.mall.oms.service.IOmsOrderItemService;
import org.springframework.stereotype.Service;
@Service
public class OmsOrderItemServiceImpl extends ServiceImpl<OmsOrderItemMapper, OmsOrderItem> implements IOmsOrderItemService {
}

View File

@ -0,0 +1,119 @@
package com.youlai.mall.oms.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.result.Result;
import com.youlai.common.web.exception.BizException;
import com.youlai.mall.oms.bo.OrderBO;
import com.youlai.mall.oms.mapper.OmsOrderMapper;
import com.youlai.mall.oms.pojo.OmsOrder;
import com.youlai.mall.oms.pojo.OmsOrderItem;
import com.youlai.mall.oms.service.IOmsOrderItemService;
import com.youlai.mall.oms.service.IOmsOrderService;
import com.youlai.mall.pms.api.ProductFeignService;
import com.youlai.mall.ums.api.MemberFeignService;
import com.youlai.mall.ums.pojo.dto.MemberDTO;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Service
@AllArgsConstructor
@Slf4j
public class OmsOrderServiceImpl extends ServiceImpl<OmsOrderMapper, OmsOrder> implements IOmsOrderService {
private IOmsOrderItemService iOmsOrderItemService;
private MemberFeignService memberFeignService;
/**
* 提交订单
*
* @param orderBO
* @return
*/
@Override
public boolean save(OrderBO orderBO) {
// 订单
OmsOrder order = orderBO.getOrder();
String orderSn = IdUtil.createSnowflake(1, 1).nextIdStr();
order.setOrderSn(orderSn);
this.save(order);
// 订单明细
List<OmsOrderItem> orderItems = orderBO.getOrderItems();
if (CollectionUtil.isEmpty(orderItems)) {
throw new BizException("订单明细不能为空");
}
orderItems.forEach(item -> {
item.setOrderId(order.getId());
});
iOmsOrderItemService.saveBatch(orderItems);
return true;
}
@Override
public OrderBO getByOrderId(Long orderId) {
OrderBO orderBO = new OrderBO();
// 订单
OmsOrder order = this.getById(orderId);
if (order == null) {
throw new BizException("订单不存在");
}
// 订单明细
List<OmsOrderItem> orderItems = iOmsOrderItemService.list(
new LambdaQueryWrapper<OmsOrderItem>().eq(OmsOrderItem::getOrderId, orderId)
);
orderItems = Optional.ofNullable(orderItems).orElse(new ArrayList<>());
// 会员明细
Result<MemberDTO> result = memberFeignService.getUserById(order.getUserId());
MemberDTO member = result.getData();
orderBO.setOrder(order).setOrderItems(orderItems).setMember(member);
return orderBO;
}
private ProductFeignService productFeignService;
@Override
public boolean submit() {
log.info("扣减库存----begin");
productFeignService.updateStock(1l, -1);
log.info("扣减库存----end");
log.info("增加积分----begin");
memberFeignService.updatePoint(1l, 10);
log.info("增加积分----end");
log.info("修改订单状态----begin");
boolean result = this.update(new LambdaUpdateWrapper<OmsOrder>().eq(OmsOrder::getId, 1l).set(OmsOrder::getStatus, 901));
log.info("修改订单状态----end");
return result;
}
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public boolean submitWithGlobalTransactional() {
log.info("扣减库存----begin");
productFeignService.updateStock(1l, -1);
log.info("扣减库存----end");
log.info("增加积分----begin");
memberFeignService.updatePoint(1l, 10);
log.info("增加积分----end");
log.info("修改订单状态----begin");
boolean result = this.update(new LambdaUpdateWrapper<OmsOrder>().eq(OmsOrder::getId, 1l).set(OmsOrder::getStatus, 901));
log.info("修改订单状态----end");
return result;
}
}

View File

@ -0,0 +1,29 @@
package com.youlai.mall.oms.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.base.Query;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.dao.OrderDeliveryDao;
import com.youlai.mall.oms.pojo.entity.OrderDeliveryEntity;
import com.youlai.mall.oms.service.OrderDeliveryService;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("orderDeliveryService")
public class OrderDeliveryServiceImpl extends ServiceImpl<OrderDeliveryDao, OrderDeliveryEntity> implements OrderDeliveryService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<OrderDeliveryEntity> page = this.page(
new Query<OrderDeliveryEntity>().getPage(params),
new QueryWrapper<OrderDeliveryEntity>()
);
return new PageUtils(page);
}
}

View File

@ -0,0 +1,56 @@
package com.youlai.mall.oms.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.base.Query;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.dao.OrderGoodsDao;
import com.youlai.mall.oms.pojo.entity.OrderGoodsEntity;
import com.youlai.mall.oms.service.OrderGoodsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service("orderGoodsService")
@Slf4j
public class OrderGoodsServiceImpl extends ServiceImpl<OrderGoodsDao, OrderGoodsEntity> implements OrderGoodsService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<OrderGoodsEntity> page = this.page(
new Query<OrderGoodsEntity>().getPage(params),
new QueryWrapper<OrderGoodsEntity>()
);
return new PageUtils(page);
}
@Override
public List<OrderGoodsEntity> getByOrderId(Long orderId) {
log.info("根据订单id查询订单商品列表orderId={}", orderId);
QueryWrapper<OrderGoodsEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.ge("order_id", orderId);
return baseMapper.selectList(queryWrapper);
}
@Override
public Map<Long, List<OrderGoodsEntity>> getByOrderIds(List<Long> orderIds) {
QueryWrapper<OrderGoodsEntity> orderGoodsQuery = new QueryWrapper<>();
orderGoodsQuery.in("order_id", orderIds).orderByDesc("order_id", "id");
List<OrderGoodsEntity> orderGoods = this.list(orderGoodsQuery);
if (orderGoods == null || orderGoods.size() == 0) {
log.info("根据订单ID列表查询商品为空orderIds={}", orderIds);
return new HashMap<>(8);
}
Map<Long, List<OrderGoodsEntity>> orderGoodsMap = orderGoods.stream()
.collect(Collectors.groupingBy(OrderGoodsEntity::getOrderId));
return orderGoodsMap;
}
}

View File

@ -0,0 +1,53 @@
package com.youlai.mall.oms.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.base.Query;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.common.web.util.RequestUtils;
import com.youlai.mall.oms.dao.OrderLogsDao;
import com.youlai.mall.oms.pojo.entity.OrderLogsEntity;
import com.youlai.mall.oms.service.OrderLogsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("orderLogsService")
@Slf4j
public class OrderLogsServiceImpl extends ServiceImpl<OrderLogsDao, OrderLogsEntity> implements OrderLogsService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<OrderLogsEntity> page = this.page(
new Query<OrderLogsEntity>().getPage(params),
new QueryWrapper<OrderLogsEntity>()
);
return new PageUtils(page);
}
@Override
public void addOrderLogs(Long orderId, Integer orderStatus, String user, String detail) {
try {
log.info("添加订单操作日志orderId={}detail={}", orderId, detail);
OrderLogsEntity orderLogs = new OrderLogsEntity();
orderLogs.setDetail(detail);
orderLogs.setOrderId(orderId);
orderLogs.setOrderStatus(orderStatus);
orderLogs.setUser(user);
baseMapper.insert(orderLogs);
} catch (Exception e) {
log.error("添加订单操作日志失败orderId={}", orderId, e.getMessage());
}
}
@Override
public void addOrderLogs(Long orderId, Integer orderStatus, String detail) {
Long userId = RequestUtils.getUserId();
addOrderLogs(orderId, orderStatus, userId.toString(), detail);
}
}

View File

@ -0,0 +1,126 @@
package com.youlai.mall.oms.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.base.Query;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode;
import com.youlai.common.utils.EnumUtils;
import com.youlai.common.web.exception.BizException;
import com.youlai.common.web.util.RequestUtils;
import com.youlai.mall.oms.dao.OrderPayDao;
import com.youlai.mall.oms.enums.OrderPayTypeEnum;
import com.youlai.mall.oms.enums.OrderStatusEnum;
import com.youlai.mall.oms.pojo.entity.OrderEntity;
import com.youlai.mall.oms.pojo.entity.OrderPayEntity;
import com.youlai.mall.oms.pojo.vo.PayInfoVO;
import com.youlai.mall.oms.service.OrderLogsService;
import com.youlai.mall.oms.service.OrderPayService;
import com.youlai.mall.oms.service.OrderService;
import com.youlai.mall.ums.api.MemberFeignService;
import com.youlai.mall.ums.pojo.dto.MemberDTO;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.Map;
@Slf4j
@AllArgsConstructor
@Service("orderPayService")
public class OrderPayServiceImpl extends ServiceImpl<OrderPayDao, OrderPayEntity> implements OrderPayService {
private OrderService orderService;
private OrderLogsService orderLogsService;
private MemberFeignService memberFeignService;
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<OrderPayEntity> page = this.page(
new Query<OrderPayEntity>().getPage(params),
new QueryWrapper<OrderPayEntity>()
);
return new PageUtils(page);
}
@Override
public PayInfoVO info(String orderId) {
Long userId = RequestUtils.getUserId();
PayInfoVO payInfoVO = new PayInfoVO();
// 1获取订单应支付金额
OrderEntity orderEntity = orderService.getByOrderId(orderId);
payInfoVO.setPayPrice(orderEntity.getPayAmount());
// 2获取会员余额
try {
Result<MemberDTO> memberInfo = memberFeignService.getUserById(RequestUtils.getUserId());
if (memberInfo != null && memberInfo.getCode().equals(ResultCode.SUCCESS.getCode())) {
MemberDTO data = memberInfo.getData();
if (data != null) {
payInfoVO.setBalance(data.getBalance());
} else {
log.error("获取会员信息失败,userId={}", userId);
}
}
} catch (Exception e) {
log.error("获取会员余额失败,userId={}", userId, e);
}
return payInfoVO;
}
@Override
@GlobalTransactional(rollbackFor = Exception.class)
public void balancePay(String orderId) {
// 1查询订单详情判断订单状态是否是待支付状态
log.info("订单进入支付流程orderId{}", orderId);
OrderEntity order = orderService.getByOrderId(orderId);
OrderStatusEnum orderStatusEnum = EnumUtils.getByCode(order.getStatus(), OrderStatusEnum.class);
if (orderStatusEnum != OrderStatusEnum.NEED_PAY) {
log.error("订单状态异常无法支付orderStatus={}", orderStatusEnum.desc);
throw new BizException("订单" + orderStatusEnum.desc);
}
// 2查询用户信息判断用户余额是否足够
Long userId = RequestUtils.getUserId();
Result<MemberDTO> memberInfo = memberFeignService.getUserById(userId);
MemberDTO memberInfoData = memberInfo.getData();
if (memberInfo == null || !memberInfo.getCode().equals(ResultCode.SUCCESS.getCode()) || memberInfoData == null) {
log.error("会员信息异常,无法支付");
throw new BizException("会员信息异常,无法支付");
}
if (memberInfoData.getBalance() < order.getPayAmount()) {
log.error("会员余额不足,无法支付,请先充值");
throw new BizException("会员余额不足,无法支付,请先充值");
}
// 3更新用户余额
memberFeignService.updateBalance(userId, order.getPayAmount());
// 4更新订单状态添加订单支付记录添加订单操作记录
order.setStatus(OrderStatusEnum.IS_PAY.code);
order.setPayTime(new Date());
order.setPayType(OrderPayTypeEnum.BALANCE.code);
orderService.updateById(order);
this.save(createOrderPay(order, OrderPayTypeEnum.BALANCE.code));
orderLogsService.addOrderLogs(order.getId(), OrderStatusEnum.IS_PAY.code, userId.toString(), "支付订单");
}
private OrderPayEntity createOrderPay(OrderEntity order, Integer payType) {
OrderPayEntity payEntity = new OrderPayEntity();
payEntity.setOrderId(order.getId());
payEntity.setPayAmount(order.getPayAmount());
payEntity.setPayTime(new Date());
payEntity.setPayType(payType);
return payEntity;
}
}

View File

@ -0,0 +1,86 @@
package com.youlai.mall.oms.service.impl;
import cn.hutool.core.util.StrUtil;
import com.rabbitmq.client.Channel;
import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode;
import com.youlai.common.web.exception.BizException;
import com.youlai.mall.oms.config.rabbitmq.OmsRabbitConstants;
import com.youlai.mall.oms.enums.OrderStatusEnum;
import com.youlai.mall.oms.pojo.entity.OrderEntity;
import com.youlai.mall.oms.pojo.entity.OrderGoodsEntity;
import com.youlai.mall.oms.service.OrderGoodsService;
import com.youlai.mall.oms.service.OrderRabbitService;
import com.youlai.mall.oms.service.OrderService;
import com.youlai.mall.pms.api.ProductFeignService;
import com.youlai.mall.pms.pojo.vo.SkuStockVO;
import com.youlai.mall.pms.pojo.vo.WareSkuStockVO;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RabbitListener(queues = OmsRabbitConstants.ORDER_RELEASE_QUEUE)
@AllArgsConstructor
@Slf4j
public class OrderRabbitServiceImpl implements OrderRabbitService {
private OrderService orderService;
private OrderGoodsService orderGoodsService;
private ProductFeignService productFeignService;
/**
* 接收超时订单消息
* 订单已支付 - 确认接收消息
* 订单未支付 - 关闭订单释放库存
* 出现异常消费消息失败将数据重新放入队列等待下次消费
*
* @param orderSn
*/
@Override
@RabbitHandler
@GlobalTransactional(rollbackFor = Exception.class)
public void releaseOrder(String orderSn, Message message, Channel channel) {
long msgTag = message.getMessageProperties().getDeliveryTag();
log.info("获取到消息msgTag={}message={}body={}", msgTag, message.toString(), orderSn);
try {
OrderEntity order = orderService.getByOrderSn(orderSn);
if (order.getStatus().equals(OrderStatusEnum.NEED_PAY.code)) {
if (orderService.closeOrderBySystem(orderSn)){
releaseStock(order.getId());
}
}
channel.basicAck(msgTag, false);
} catch (Exception e) {
log.error("关闭订单失败orderSn={}", orderSn);
throw new BizException("关闭订单失败orderSn=" + orderSn);
}
}
private void releaseStock(Long orderId) {
List<OrderGoodsEntity> orderGoods = orderGoodsService.getByOrderId(orderId);
List<SkuStockVO> items = orderGoods.stream().map(good -> {
SkuStockVO itemVO = new SkuStockVO();
itemVO.setSkuId(good.getSkuId());
itemVO.setNumber(good.getSkuQuantity());
return itemVO;
}).collect(Collectors.toList());
WareSkuStockVO wareSkuStock = new WareSkuStockVO();
wareSkuStock.setItems(items);
Result result = productFeignService.releaseStock(wareSkuStock);
if (result == null || !StrUtil.equals(result.getCode(), ResultCode.SUCCESS.getCode())) {
log.error("释放库存异常,商品列表={}", items);
throw new BizException("关闭订单失败,释放库存错误");
}
}
}

View File

@ -0,0 +1,451 @@
package com.youlai.mall.oms.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.base.Query;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.common.result.Result;
import com.youlai.common.result.ResultCode;
import com.youlai.common.utils.EnumUtils;
import com.youlai.common.web.exception.BizException;
import com.youlai.common.web.util.BeanMapperUtils;
import com.youlai.common.web.util.RequestUtils;
import com.youlai.mall.oms.config.rabbitmq.OmsRabbitConstants;
import com.youlai.mall.oms.dao.OrderDao;
import com.youlai.mall.oms.dao.OrderDeliveryDao;
import com.youlai.mall.oms.dao.OrderGoodsDao;
import com.youlai.mall.oms.enums.OrderStatusEnum;
import com.youlai.mall.oms.enums.OrderTypeEnum;
import com.youlai.mall.oms.pojo.entity.OrderDeliveryEntity;
import com.youlai.mall.oms.pojo.entity.OrderEntity;
import com.youlai.mall.oms.pojo.entity.OrderGoodsEntity;
import com.youlai.mall.oms.pojo.vo.*;
import com.youlai.mall.oms.service.CartService;
import com.youlai.mall.oms.service.OrderGoodsService;
import com.youlai.mall.oms.service.OrderLogsService;
import com.youlai.mall.oms.service.OrderService;
import com.youlai.mall.pms.api.ProductFeignService;
import com.youlai.mall.pms.pojo.vo.SkuInfoVO;
import com.youlai.mall.pms.pojo.vo.SkuStockVO;
import com.youlai.mall.pms.pojo.vo.WareSkuStockVO;
import com.youlai.mall.ums.api.MemberFeignService;
import com.youlai.mall.ums.pojo.dto.UmsAddressDTO;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Function;
import java.util.stream.Collectors;
@AllArgsConstructor
@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {
private static final ThreadLocal<OrderSubmitVO> threadOrderSubmit = new ThreadLocal<>();
private CartService cartService;
private ProductFeignService productFeignService;
private MemberFeignService memberFeignService;
private AsyncTaskExecutor executor;
private OrderDao orderDao;
private OrderGoodsDao orderGoodsDao;
private OrderDeliveryDao orderDeliveryDao;
private OrderLogsService orderLogsService;
private OrderGoodsService orderGoodsService;
private RabbitTemplate rabbitTemplate;
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<OrderEntity> page = this.page(
new Query<OrderEntity>().getPage(params),
new QueryWrapper<OrderEntity>()
);
return new PageUtils(page);
}
@Override
public OrderConfirmVO confirm(String skuId, Integer number) {
List<OrderItemVO> items = getOrderItemFromCart(skuId, number);
if (CollectionUtil.isEmpty(items)) {
log.info("订单商品列表为空,无法创建订单确认信息");
return new OrderConfirmVO();
}
// feign调用商品接口获取商品信息
Map<Long, SkuInfoVO> skuMap = new HashMap<>(items.size());
List<String> skuIds = items.stream().map(item -> item.getSkuId().toString()).collect(Collectors.toList());
List<SkuInfoVO> skuInfos = productFeignService.infos(skuIds).getData();
if (!CollectionUtil.isEmpty(skuInfos)) {
skuMap = skuInfos.stream().collect(Collectors.toMap(SkuInfoVO::getSkuId, Function.identity()));
}
for (OrderItemVO item : items) {
SkuInfoVO info = skuMap.get(item.getSkuId());
if (info != null) {
item.setPrice(info.getSkuPrice());
item.setSkuImg(info.getSkuPic());
item.setSkuName(info.getSkuName());
}
}
OrderConfirmVO confirm = new OrderConfirmVO();
confirm.setItems(items);
return confirm;
}
@Override
@GlobalTransactional
public OrderSubmitResultVO submit(OrderSubmitVO submit) throws ExecutionException, InterruptedException {
log.info("开始创建订单:{}", submit);
threadOrderSubmit.set(submit);
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
OrderVO orderVO = new OrderVO();
CompletableFuture<Void> orderFuture = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(attributes);
threadOrderSubmit.set(submit);
OrderEntity order = createOrder();
orderVO.setOrderEntity(order);
}, executor);
CompletableFuture<Void> orderGoodsFuture = CompletableFuture.runAsync(() -> {
// 生成订单商品信息
RequestContextHolder.setRequestAttributes(attributes);
threadOrderSubmit.set(submit);
List<OrderGoodsEntity> orderGoods = createOrderGoods();
orderVO.setOrderGoods(orderGoods);
}, executor);
CompletableFuture<Void> orderDeliveryFuture = CompletableFuture.runAsync(() -> {
threadOrderSubmit.set(submit);
RequestContextHolder.setRequestAttributes(attributes);
// 生成订单地址信息
OrderDeliveryEntity orderDelivery = createOrderDelivery();
if (orderDelivery == null) {
throw new BizException("提交订单失败,无法获取用户地址信息");
}
orderVO.setOrderDeliveryEntity(orderDelivery);
}, executor);
CompletableFuture<Void> future = CompletableFuture.allOf(orderFuture, orderGoodsFuture, orderDeliveryFuture);
future.get();
// 订单验价
computePrice(orderVO.getOrderEntity(), orderVO.getOrderGoods());
// 扣减库存
lockStock(orderVO.getOrderGoods());
// 保存订单
this.baseMapper.insert(orderVO.getOrderEntity());
Long orderId = orderVO.getOrderEntity().getId();
// 保存订单商品
for (OrderGoodsEntity orderGood : orderVO.getOrderGoods()) {
orderGood.setOrderId(orderId);
orderGoodsDao.insert(orderGood);
}
// 保存订单发货信息
orderVO.getOrderDeliveryEntity().setOrderId(orderId);
orderDeliveryDao.insert(orderVO.getOrderDeliveryEntity());
// 保存订单日志
// 清空购物车
if (ObjectUtil.isNull(submit.getSkuId())) {
cartService.cleanSelected();
}
// 将订单放入定时队列中超时未支付系统自动关单是否库存
rabbitTemplate.convertAndSend(OmsRabbitConstants.ORDER_EVENT_EXCHANGE,
OmsRabbitConstants.ORDER_CREATE_ORDER_KEY, orderVO.getOrderEntity().getOrderSn());
orderLogsService.addOrderLogs(orderVO.getOrderEntity().getId(), orderVO.getOrderEntity().getStatus(), RequestUtils.getUserId().toString(), "创建订单");
OrderSubmitResultVO result = new OrderSubmitResultVO();
result.setId(orderId);
result.setOrderSn(orderVO.getOrderEntity().getOrderSn());
return result;
}
@Override
public OrderEntity getByOrderSn(String orderSn) {
log.info("根据订单号查询订单详情orderSn={}", orderSn);
QueryWrapper<OrderEntity> query = new QueryWrapper<>();
query.eq("order_sn", orderSn);
return baseMapper.selectOne(query);
}
@Override
public boolean closeOrderBySystem(String orderSn) {
log.info("订单超时未支付系统自动关闭orderSn={}", orderSn);
OrderEntity order = getByOrderSn(orderSn);
if (!order.getStatus().equals(OrderStatusEnum.NEED_PAY.code)) {
log.info("订单状态异常系统无法自动关闭orderSn={}orderStatus={}", orderSn, order.getStatus());
return false;
}
order.setStatus(OrderStatusEnum.SYS_CANCEL.code);
baseMapper.updateById(order);
// 添加订单操作日志
orderLogsService.addOrderLogs(order.getId(), order.getStatus(),
"系统操作", OrderStatusEnum.SYS_CANCEL.desc);
return true;
}
@Override
public boolean cancelOrder(String id) {
log.info("会员取消订单orderId={}", id);
OrderEntity order = getByOrderId(id);
if (!order.getStatus().equals(OrderStatusEnum.NEED_PAY.code)) {
log.info("订单状态异常会员无法取消orderId={}orderStatus={}", id, order.getStatus());
return false;
}
order.setStatus(OrderStatusEnum.USER_CANCEL.code);
baseMapper.updateById(order);
// 添加订单操作日志
orderLogsService.addOrderLogs(order.getId(), order.getStatus(), OrderStatusEnum.USER_CANCEL.desc);
return true;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean deleteOrder(String id) {
// 查询订单校验订单状态
OrderEntity order = this.getByOrderId(id);
if (!order.getStatus().equals(OrderStatusEnum.SYS_CANCEL.code) &&
order.getStatus().equals(OrderStatusEnum.USER_CANCEL.code)) {
throw new BizException(StrUtil.format("订单无法删除,订单状态【{}】", Objects.requireNonNull(EnumUtils.getByCode(order.getStatus(), OrderStatusEnum.class)).desc));
}
orderDao.deleteById(id);
orderLogsService.addOrderLogs(order.getId(), order.getStatus(), "会员删除订单");
return true;
}
@Override
public List<OrderListVO> list(Integer status) {
log.info("订单列表查询status={}", status);
QueryWrapper<OrderEntity> orderQuery = new QueryWrapper<>();
if (status != 0) {
orderQuery.eq("status", status);
}
orderQuery.orderByDesc("id");
List<OrderEntity> orderList = this.list(orderQuery);
if (orderList == null || orderList.size() <= 0) {
log.info("订单列表查询结果为空status={}", status);
return null;
}
List<Long> orderIds = orderList.stream().map(orderEntity -> orderEntity.getId()).collect(Collectors.toList());
Map<Long, List<OrderGoodsEntity>> orderGoodsMap = orderGoodsService.getByOrderIds(orderIds);
List<OrderListVO> result = orderList.stream().map(orderEntity -> {
OrderListVO orderListVO = BeanMapperUtils.map(orderEntity, OrderListVO.class);
orderListVO.setStatusDesc(EnumUtils.getByCode(orderListVO.getStatus(), OrderStatusEnum.class).desc);
List<OrderGoodsEntity> orderGoodsEntities = orderGoodsMap.get(orderListVO.getId());
if (orderGoodsEntities != null && orderGoodsEntities.size() > 0) {
List<OrderListVO.GoodsListBean> goodsListBeans = orderGoodsEntities.stream().map(orderGoodsEntity -> BeanMapperUtils.map(orderGoodsEntity, OrderListVO.GoodsListBean.class)).collect(Collectors.toList());
orderListVO.setGoodsList(goodsListBeans);
}
return orderListVO;
}).collect(Collectors.toList());
return result;
}
@Override
public OrderEntity getByOrderId(String id) {
Long userId = RequestUtils.getUserId();
QueryWrapper<OrderEntity> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("member_id", userId).eq("id", id);
OrderEntity orderEntity = this.getOne(queryWrapper);
if (orderEntity == null) {
throw new BizException("订单不存在订单id非法");
}
return orderEntity;
}
private void lockStock(List<OrderGoodsEntity> orderGoods) {
List<SkuStockVO> items = orderGoods.stream().map(good -> {
SkuStockVO itemVO = new SkuStockVO();
itemVO.setSkuId(good.getSkuId());
itemVO.setNumber(good.getSkuQuantity());
return itemVO;
}).collect(Collectors.toList());
WareSkuStockVO wareSkuStock = new WareSkuStockVO();
wareSkuStock.setItems(items);
Result result = productFeignService.lockStock(wareSkuStock);
if (result == null || !StrUtil.equals(result.getCode(), ResultCode.SUCCESS.getCode())) {
log.error("锁定库存异常,商品列表={}", items);
throw new BizException("下单失败,锁定库存错误");
}
}
/**
* 计算订单商品价格
*
* @param order
* @param orderGoods
* @return
*/
private Long computePrice(OrderEntity order, List<OrderGoodsEntity> orderGoods) {
log.info("计算订单价格order:{},orderGoods:{}", order, orderGoods);
if (order == null || CollectionUtil.isEmpty(orderGoods)) {
throw new BizException("订单或订单商品列表为空,订单创建失败");
}
Long totalAmount = orderGoods.stream().mapToLong(OrderGoodsEntity::getSkuTotalPrice).sum();
int totalQuantity = orderGoods.stream().mapToInt(OrderGoodsEntity::getSkuQuantity).sum();
Long payAmount = totalAmount;
if (order.getCouponAmount() != null) {
payAmount -= order.getCouponAmount();
}
if (order.getFreightAmount() != null) {
payAmount -= order.getFreightAmount();
}
order.setTotalAmount(totalAmount);
order.setTotalQuantity(totalQuantity);
order.setPayAmount(payAmount);
OrderSubmitVO submit = threadOrderSubmit.get();
if (!StrUtil.equals(submit.getPayAmount().toString(), payAmount.toString())) {
throw new BizException("订单价格变化,请重新提交");
}
return payAmount;
}
/**
* 创建订单商品集合
*
* @return
*/
private List<OrderGoodsEntity> createOrderGoods() {
OrderSubmitVO submit = threadOrderSubmit.get();
log.info("创建订单商品实体类submit:{}", submit);
List<OrderGoodsEntity> orderGoods = null;
if (ObjectUtil.isNull(submit.getSkuId())) {
CartVo detail = cartService.detail();
log.info("从购物车中获取已选择商品信息:{}", detail);
orderGoods = detail.getItems().stream().map(item -> {
OrderGoodsEntity good = new OrderGoodsEntity();
good.setSkuId(item.getSkuId());
good.setSkuQuantity(item.getNumber());
return good;
}).collect(Collectors.toList());
} else {
log.info("会员选择商品直接下单,商品id:{}", submit.getSkuId());
orderGoods = new ArrayList<>();
OrderGoodsEntity good = new OrderGoodsEntity();
good.setSkuId(Long.valueOf(submit.getSkuId()));
good.setSkuQuantity(submit.getSkuNumber());
orderGoods.add(good);
}
List<String> skuIds = orderGoods.stream().map(vo -> vo.getSkuId().toString()).collect(Collectors.toList());
Result<List<SkuInfoVO>> response = productFeignService.infos(skuIds);
List<SkuInfoVO> skuInfos = response.getData();
if (skuInfos == null) {
return null;
}
Map<Long, SkuInfoVO> skuMap = skuInfos.stream().collect(Collectors.toMap(SkuInfoVO::getSkuId, Function.identity(), (o1, o2) -> o2));
for (OrderGoodsEntity good : orderGoods) {
SkuInfoVO skuInfo = skuMap.get(good.getSkuId());
if (skuInfo == null) {
throw new BizException("订单商品库存为空");
}
BeanMapperUtils.copy(skuInfo, good);
Long goodTotalPrice = good.getSkuPrice() * good.getSkuQuantity();
good.setSkuTotalPrice(goodTotalPrice);
}
return orderGoods;
}
/**
* 生成订单数据
*
* @return
*/
private OrderEntity createOrder() {
OrderSubmitVO submit = threadOrderSubmit.get();
log.info("创建订单实体类submit:{}", submit);
OrderEntity order = new OrderEntity();
order.setOrderSn(IdWorker.getTimeId());
order.setRemark(submit.getRemark());
order.setStatus(OrderStatusEnum.NEED_PAY.code);
order.setSourceType(OrderTypeEnum.APP.code);
order.setMemberId(RequestUtils.getUserId());
return order;
}
/**
* 获取订单地址信息
*
* @return
*/
private OrderDeliveryEntity createOrderDelivery() {
String addressId = threadOrderSubmit.get().getAddressId();
log.info("获取订单地址信息addressId{}", addressId);
try {
Result<UmsAddressDTO> response = memberFeignService.getAddressById(addressId);
UmsAddressDTO addressInfo = response.getData();
if (addressInfo != null) {
OrderDeliveryEntity delivery = new OrderDeliveryEntity();
delivery.setReceiverProvince(addressInfo.getProvince());
delivery.setReceiverCity(addressInfo.getCity());
delivery.setReceiverRegion(addressInfo.getArea());
delivery.setReceiverDetailAddress(addressInfo.getAddress());
delivery.setReceiverName(addressInfo.getName());
delivery.setReceiverPostCode(addressInfo.getZipCode());
delivery.setReceiverPhone(addressInfo.getMobile());
return delivery;
}
} catch (Exception e) {
log.error("获取订单地址信息失败addressId{}", addressId, e);
}
return null;
}
/**
* 从购物车中获取订单商品列表
*
* @return
*/
private List<OrderItemVO> getOrderItemFromCart(String skuId, Integer number) {
if (!StrUtil.isEmpty(skuId)) {
log.info("请求携带商品id{},数量:{},订单类型为用户直接购买", skuId, number);
OrderItemVO itemVO = OrderItemVO.builder().skuId(Long.parseLong(skuId)).number(number).build();
return Arrays.asList(itemVO);
}
CartVo cartVo = cartService.detail();
List<OrderItemVO> items = cartVo.getItems().stream().filter(CartItemVo::isChecked).map(cart -> OrderItemVO.builder().skuId(cart.getSkuId()).number(cart.getNumber()).build()).collect(Collectors.toList());
return items;
}
}

View File

@ -0,0 +1,29 @@
package com.youlai.mall.oms.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.base.Query;
import com.youlai.common.mybatis.utils.PageUtils;
import com.youlai.mall.oms.dao.OrderSettingDao;
import com.youlai.mall.oms.pojo.entity.OrderSettingEntity;
import com.youlai.mall.oms.service.OrderSettingService;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service("orderSettingService")
public class OrderSettingServiceImpl extends ServiceImpl<OrderSettingDao, OrderSettingEntity> implements OrderSettingService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<OrderSettingEntity> page = this.page(
new Query<OrderSettingEntity>().getPage(params),
new QueryWrapper<OrderSettingEntity>()
);
return new PageUtils(page);
}
}

View File

@ -0,0 +1,73 @@
package com.youlai.mall.oms.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.youlai.common.web.exception.BizException;
import com.youlai.common.web.util.RequestUtils;
import com.youlai.mall.oms.service.TokenService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import static com.youlai.common.redis.constant.RedisKeyConstants.REDIS_KEY_TIME_OUT;
import static com.youlai.common.redis.constant.RedisKeyConstants.TOKEN_VERIFY;
/**
* @author huawei
* @desc Token 服务
* @email huawei_code@163.com
* @date 2021/1/21
*/
@Service
@AllArgsConstructor
@Slf4j
public class TokenServiceImpl implements TokenService {
private static final String TOKEN = "token";
private RedisTemplate redisTemplate;
@Override
public String generateToken() {
StringBuffer sb = new StringBuffer();
String userId = RequestUtils.getUserId().toString();
//生成UUID
String token = IdUtil.randomUUID();
//前缀 + 用户id + UUID组成 token
String key = sb.append(TOKEN_VERIFY).append(userId).append(':').append(token).toString();
//将token写入redis并设置过期时间
redisTemplate.opsForValue().set(key, key, REDIS_KEY_TIME_OUT);
//redis写入成功则设置token成功
return token;
}
@Override
public void checkToken(HttpServletRequest request) {
String token = request.getHeader(TOKEN);
if (StringUtils.isBlank(token)) {
token = request.getParameter(TOKEN);
if (StringUtils.isBlank(token)) {
throw new BizException("请勿重复提交");
}
}
StringBuilder sb = new StringBuilder();
String userId = RequestUtils.getUserId().toString();
String key = sb.append(TOKEN_VERIFY).append(userId).append(':').append(token).toString();
// 2校验token是否存在
if (!StrUtil.equals(redisTemplate.opsForValue().get(key).toString(), key)) {
log.debug("请求:{} 使用 token{} 校验幂等性,缓存中不存在", request.getRequestURI(), token);
throw new BizException("请勿重复提交");
}
// 3校验token是否删除成功
if (!redisTemplate.delete(key)) {
log.debug("请求:{} 使用 token{} 校验幂等性,删除缓存数据失败", request.getRequestURI(), token);
throw new BizException("请勿重复提交");
}
}
}

View File

@ -0,0 +1,23 @@
${AnsiColor.BRIGHT_RED} 升职加薪
钱 ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
多 ⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 身
事 ⠸⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢙⢂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 强
少 ⢸⣱⢄⣀⣀⣀⣀⣀ ⢀⣀⣠⢀⡀⣤⠤⣚⣼⠀
干 ⠀⠒⠿⣊⣇⣧⣾⣟⣝⣝⣿⣿⣜⡵⠵⢼⣊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 健
的 ⠀⠀⠀⠐⠺⠿⣿⠻⢟⣿⢍⢍⢻⣿⣿⣧⡞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 进
爽 ⠀⠀⠀⠀⠀⠀⣿⢠⣿⣿ ⣿⣴⣿⣿⣟⢷⣦⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 步
⠀⠀⠀⠀⠀⢸⢿⡿⢿⣿⣿⣿ ⢫⣿⠏⢤⣿⠩⣝⣷⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⣠⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 快
⠀⠀⠀⠀⠀⣏⡃⣘⣃⣿⠿⢛⣡⣿⠿⢶⣿⣿⣷⣬⣍⣼⡟⢻⣟⢟⠿⣿⠿⢛⣿⠿⣿⠿⢳⣶⣿⡿⠛⠛⠿⣷⣄⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠈⠉⠛⠛⠻⡿⡻⡉⠡⡀⣿⣿⣿⣿⣿⣿⣿⣷⣾⣬⣥⣴⣿⣗⣿⠡⣷⡗⣼⡷⠛⡻⣷⡀⠀⠀⠈⠻⣦⣀⠀⡔⠒⠲⡇
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢿⡷⣷⡷⡿⣿⣿⣿⣿⣿⣿⣿⡍⠛⢛⢍⣽⠏⣼⠻⣜⣻⢟⣵⣷⠿⣿⢜⣧⠀⠀⠀⠀⠀⠉⠉⠑⠒⠊⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⣮⣠⣠⣴⠿⣿⣿⣿⣿⣿⣿⣿⣾⣼⣾⡿⢧⣽⣎⠿⠻⣻⡵⢻⣿⠏⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣸⡟⢹⣗⣽⠿⣿⣿⣿⣿⣿⣿⡿⣆⣌⠐⣰⣿⣄⣛⣿⣻⠛⢍⣼⣋⣵⠣⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⡤⡲⣴⢄⣯⣬⣿⣷⣄⣹⠅⣱⣿⣿⣿⣿⣿⣿⠌⢽⣿⡟⠛⡋⣹⡝⣷⣉⣻⣏⣉⣲⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⢨⡪⡭⢸⣿⠃⠀⠉⠙⠛⠳⢼⣿⣿⣿⣿⢻⢊⣢⣽⣋⣿⣾⠾⣟⣯⣷⣿⡻⢿⣿⣿⣿⣿⣿⣦⡀⢠⣀⣤⠀⠀⠀⡀⠀⠀
⠀⠛⣠⣃⣬⣿⣁⠀⣿⣗⣤⠀⡀⠀⠀⣜⢿⣿⠏⠉⠋⠉⠉⠉⠉⠀⠀⠀⠙⠿⣿⣿⣧⠀⠈⠙⠛⠿⣿⣯⣼⠀⡃⣣⣧⣤⣾⣇⠀⠀
⢀⣀⣴⡿⣺⡏⢻⣆⡈⠓⠻⠃⡐⠀⠀⣷⣿⠋⠀⠀⠀⠀⠀⣼⣵⠇⡀⠀⠀⠀⣸⣿⡭⠃⠀⠀⠀⠀⠈⠻⣿⡄⠀⠿⣯⣖⡽⠃⠀⠀
⢨⣵⣭⣽⡗⡟⢿⣶⣿⡄⠀⠀⠀⠀⢠⣿⡇⠀⠀⠀⠙⡒⣥⣄⡘⠍⠀⢀⣠⣼⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⢹⣷⡀⠠⢵⠸⣤⣀⠀⠀
⠀⠉⢈⣋⣦⣴⡄⢉⣰⣲⡂⠀⠀⢰⣭⡽⠂⠀⠀⠀⠈⠀⠐⠻⢽⠿⡳⠶⡾⠛⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣯⣵⠣⣟⣆⡃⠧⣾⡅⠀
⠀⠀⠙⠻⠿⠛⠉⠑⠋⠉⠀⠀⠪⣀⣉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠚⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠠⣱⣀⡁⠀⠀⠃
${AnsiColor.BRIGHT_YELLOW}Spring Boot 版本:${spring-boot.version}
${AnsiColor.BRIGHT_YELLOW}项目名称:${spring.application.name} | 端口号:${server.port}

View File

@ -0,0 +1,41 @@
server:
port: 8603
spring:
application:
name: mall-oms
cloud:
nacos:
discovery:
server-addr: http://localhost:8848
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yaml
# 全局参数设置
ribbon:
ReadTimeout: 10000
ConnectTimeout: 10000
SocketTimeout: 10000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
# 分布式事务配置
seata:
tx-service-group: mall_tx_group
enable-auto-data-source-proxy: true
registry:
type: nacos
nacos:
server-addr: c.youlai.store:8848
namespace: seata_namespace_id
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: c.youlai.store:8848
namespace: seata_namespace_id
group: SEATA_GROUP

View File

@ -0,0 +1,51 @@
server:
port: 8603
spring:
application:
name: mall-oms
cloud:
nacos:
discovery:
server-addr: http://c.youlai.store:8848
namespace: prod_namespace_id
config:
server-addr: ${spring.cloud.nacos.discovery.server-addr}
file-extension: yaml
namespace: prod_namespace_id
# 分布式事务配置
seata:
tx-service-group: mall_tx_group
enable-auto-data-source-proxy: true
registry:
type: nacos
nacos:
server-addr: c.youlai.store:8848
namespace: seata_namespace_id
group: SEATA_GROUP
config:
type: nacos
nacos:
server-addr: c.youlai.store:8848
namespace: seata_namespace_id
group: SEATA_GROUP
# 全局参数设置
ribbon:
ReadTimeout: 10000
ConnectTimeout: 10000
SocketTimeout: 10000
MaxAutoRetries: 0
MaxAutoRetriesNextServer: 1
# 站长支付API配置
zzf:
AppKey: c35332f4d84646f7
AppSecret: 1c93baadf7544f5faa5b5c6e02ef4307
CreateOrderURL: https://admin.zhanzhangfu.com/order/createOrder
FindOrderURL: https://admin.zhanzhangfu.com/order/onlinePayFindResult

View File

@ -0,0 +1,3 @@
spring:
profiles:
active: dev

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.youlai.mall.oms.dao.OrderDao">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="com.youlai.mall.oms.pojo.entity.OrderEntity" id="orderMap">
<result property="id" column="id"/>
<result property="orderSn" column="order_sn"/>
<result property="totalAmount" column="total_amount"/>
<result property="totalQuantity" column="total_quantity"/>
<result property="sourceType" column="source_type"/>
<result property="status" column="status"/>
<result property="remark" column="remark"/>
<result property="memberId" column="member_id"/>
<result property="couponId" column="coupon_id"/>
<result property="couponAmount" column="coupon_amount"/>
<result property="freightAmount" column="freight_amount"/>
<result property="payAmount" column="pay_amount"/>
<result property="payTime" column="pay_time"/>
<result property="payType" column="pay_type"/>
<result property="deliveryTime" column="delivery_time"/>
<result property="receiveTime" column="receive_time"/>
<result property="commentTime" column="comment_time"/>
<result property="deleted" column="deleted"/>
<result property="gmtCreate" column="gmt_create"/>
<result property="gmtModified" column="gmt_modified"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.youlai.mall.oms.dao.OrderDeliveryDao">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="com.youlai.mall.oms.pojo.entity.OrderDeliveryEntity" id="orderDeliveryMap">
<result property="id" column="id"/>
<result property="orderId" column="order_id"/>
<result property="deliveryCompany" column="delivery_company"/>
<result property="deliverySn" column="delivery_sn"/>
<result property="receiverName" column="receiver_name"/>
<result property="receiverPhone" column="receiver_phone"/>
<result property="receiverPostCode" column="receiver_post_code"/>
<result property="receiverProvince" column="receiver_province"/>
<result property="receiverCity" column="receiver_city"/>
<result property="receiverRegion" column="receiver_region"/>
<result property="receiverDetailAddress" column="receiver_detail_address"/>
<result property="remark" column="remark"/>
<result property="deliveryStatus" column="delivery_status"/>
<result property="deliveryTime" column="delivery_time"/>
<result property="receiveTime" column="receive_time"/>
<result property="deleted" column="deleted"/>
<result property="gmtCreate" column="gmt_create"/>
<result property="gmtModified" column="gmt_modified"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.youlai.mall.oms.dao.OrderGoodsDao">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="com.youlai.mall.oms.pojo.entity.OrderGoodsEntity" id="orderGoodsMap">
<result property="id" column="id"/>
<result property="orderId" column="order_id"/>
<result property="skuId" column="sku_id"/>
<result property="skuCode" column="sku_code"/>
<result property="skuName" column="sku_name"/>
<result property="skuPic" column="sku_pic"/>
<result property="skuPrice" column="sku_price"/>
<result property="skuQuantity" column="sku_quantity"/>
<result property="skuTotalPrice" column="sku_total_price"/>
<result property="spuId" column="spu_id"/>
<result property="spuName" column="spu_name"/>
<result property="spuPic" column="spu_pic"/>
<result property="brandId" column="brand_id"/>
<result property="brandName" column="brand_name"/>
<result property="categoryId" column="category_id"/>
<result property="categoryName" column="category_name"/>
<result property="deleted" column="deleted"/>
<result property="gmtCreate" column="gmt_create"/>
<result property="gmtModified" column="gmt_modified"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.youlai.mall.oms.dao.OrderLogsDao">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="com.youlai.mall.oms.pojo.entity.OrderLogsEntity" id="orderLogsMap">
<result property="id" column="id"/>
<result property="orderId" column="order_id"/>
<result property="user" column="user"/>
<result property="detail" column="detail"/>
<result property="orderStatus" column="order_status"/>
<result property="remark" column="remark"/>
<result property="deleted" column="deleted"/>
<result property="gmtCreate" column="gmt_create"/>
<result property="gmtModified" column="gmt_modified"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.youlai.mall.oms.dao.OrderPayDao">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="com.youlai.mall.oms.pojo.entity.OrderPayEntity" id="orderPayMap">
<result property="id" column="id"/>
<result property="orderId" column="order_id"/>
<result property="paySn" column="pay_sn"/>
<result property="payAmount" column="pay_amount"/>
<result property="payTime" column="pay_time"/>
<result property="payType" column="pay_type"/>
<result property="payStatus" column="pay_status"/>
<result property="confirmTime" column="confirm_time"/>
<result property="callbackContent" column="callback_content"/>
<result property="callbackTime" column="callback_time"/>
<result property="paySubject" column="pay_subject"/>
<result property="deleted" column="deleted"/>
<result property="gmtCreate" column="gmt_create"/>
<result property="gmtModified" column="gmt_modified"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.youlai.mall.oms.dao.OrderSettingDao">
<!-- 可根据自己的需求,是否要使用 -->
<resultMap type="com.youlai.mall.oms.pojo.entity.OrderSettingEntity" id="orderSettingMap">
<result property="id" column="id"/>
<result property="flashOrderOvertime" column="flash_order_overtime"/>
<result property="normalOrderOvertime" column="normal_order_overtime"/>
<result property="confirmOvertime" column="confirm_overtime"/>
<result property="finishOvertime" column="finish_overtime"/>
<result property="commentOvertime" column="comment_overtime"/>
<result property="memberLevel" column="member_level"/>
<result property="deleted" column="deleted"/>
<result property="gmtCreate" column="gmt_create"/>
<result property="gmtModified" column="gmt_modified"/>
</resultMap>
</mapper>

View File

@ -0,0 +1,78 @@
package com.youlai.mall.oms.controller;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.youlai.common.result.ResultCode;
import com.youlai.mall.oms.controller.admin.OrderController;
import com.youlai.mall.oms.pojo.OmsOrder;
import com.youlai.mall.oms.service.IOmsOrderService;
import com.youlai.mall.pms.api.ProductFeignService;
import com.youlai.mall.ums.api.MemberFeignService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpMethod;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
@AutoConfigureMockMvc
@SpringBootTest
@Slf4j
public class OrderControllerTest {
@Autowired
public MockMvc mockMvc;
@Autowired
public OrderController orderController;
/**
* 提交订单
*
* @throws Exception
*/
@Test
public void saveOrder() throws Exception {
String goods = "{\"order\":{\"userId\":0,\"status\":10,\"source\":0,\"consignee\":\"str\",\"mobile\":\"str\",\"postcode\":\"str\",\"address\":\"str\",\"couponId\":0,\"skuPrice\":0,\"freightPrice\":0,\"couponPrice\":0,\"orderPrice\":0,\"integrationPrice\":0,\"payPrice\":0,\"payId\":\"str\",\"payType\":0,\"payTime\":1606379283562,\"shipSn\":\"str\",\"shipChannel\":\"str\"},\n" +
" \"orderItems\":[{\"spuId\":0,\"spuName\":\"str\",\"skuId\":\"0\",\"skuBarCode\":\"str\",\"skuSpecifications\":\"str\",\"skuPrice\":0,\"skuQuantity\":0,\"pic\":\"str\"}]}";
MvcResult result = mockMvc.perform(MockMvcRequestBuilders.request(HttpMethod.POST, "/orders")
.contentType("application/json")
.content(goods))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value(ResultCode.SUCCESS.getCode()))
.andDo(print())
.andReturn();
log.info(result.getResponse().getContentAsString());
}
@Autowired
private ProductFeignService productFeignService;
@Autowired
private MemberFeignService memberFeignService;
@Autowired
private IOmsOrderService iOmsOrderService;
@Test
@GlobalTransactional(rollbackFor = Exception.class)
public void submitOrder() {
// 扣减库存
productFeignService.updateStock(151l, -1);
// 增加积分
memberFeignService.updatePoint(1l, 10);
// 修改订单状态
iOmsOrderService.update(new LambdaUpdateWrapper<OmsOrder>().eq(OmsOrder::getId, 1l).set(OmsOrder::getStatus, 901));
}
}

View File

@ -14,7 +14,7 @@
<packaging>pom</packaging>
<modules>
<module>oms-api</module>
<module>oms-biz</module>
<module>oms-boot</module>
</modules>
<properties>

View File

@ -0,0 +1,11 @@
package com.youlai.mall.pms.common;
/**
* @author haoxr
* @date 2021-02-28 20:10
*/
public interface RedisConstants {
String PRODUCT_INVENTORY_PREFIX = "product:inventory:";
}

View File

@ -0,0 +1,46 @@
package com.youlai.mall.pms.controller.app;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.youlai.common.result.Result;
import com.youlai.mall.pms.pojo.domain.PmsSku;
import com.youlai.mall.pms.pojo.dto.SkuDTO;
import com.youlai.mall.pms.pojo.vo.SkuInfoVO;
import com.youlai.mall.pms.pojo.vo.WareSkuStockVO;
import com.youlai.mall.pms.service.IPmsSkuService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "【移动端】商品库存")
@RestController("AppInventoryController")
@RequestMapping("/api.app/v1/inventories")
@AllArgsConstructor
public class InventoryController {
private IPmsSkuService iPmsSkuService;
@ApiOperation(value = "商品库存详情", httpMethod = "GET")
@ApiImplicitParam(name = "id", value = "商品SkuId", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{id}")
public Result<SkuDTO> detail(@PathVariable Long id) {
PmsSku sku = iPmsSkuService.getById(id);
SkuDTO skuDTO = new SkuDTO();
BeanUtil.copyProperties(sku, skuDTO);
return Result.success(skuDTO);
}
@ApiImplicitParam(name = "id", value = "商品SkuId", required = true, paramType = "path", dataType = "Long")
@GetMapping("/{skuId}/inventory")
public Result<Integer> getInventoryBySkuId(@PathVariable Long skuId) {
Integer inventory = iPmsSkuService.getInventoryBySkuId(skuId);
return Result.success(inventory);
}
}

View File

@ -18,7 +18,7 @@ import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "【移动端】商品库存")
@RestController("AppInventoryController")
@RestController("AppSkuController")
@RequestMapping("/api.app/v1/sku")
@AllArgsConstructor
public class SkuController {

View File

@ -29,4 +29,12 @@ public interface IPmsSkuService extends IService<PmsSku> {
* @return 释放库存结果
*/
boolean releaseStock(WareSkuStockVO skuStockVO);
/**
* 获取商品库存
* @param skuId
* @return
*/
Integer getInventoryBySkuId(Long skuId);
}

View File

@ -1,14 +1,19 @@
package com.youlai.mall.pms.service.impl;
import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.youlai.common.web.exception.BizException;
import com.youlai.mall.pms.common.RedisConstants;
import com.youlai.mall.pms.mapper.PmsSkuMapper;
import com.youlai.mall.pms.pojo.domain.PmsSku;
import com.youlai.mall.pms.pojo.vo.SkuInfoVO;
import com.youlai.mall.pms.pojo.vo.SkuStockVO;
import com.youlai.mall.pms.pojo.vo.WareSkuStockVO;
import com.youlai.mall.pms.service.IPmsSkuService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -16,8 +21,12 @@ import java.util.List;
@Service
@Slf4j
@AllArgsConstructor
public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> implements IPmsSkuService {
private RedisTemplate redisTemplate;
@Override
public List<SkuInfoVO> getSkuInfoByIds(List<String> skuIds) {
log.info("批量获取商品详情skuIds:{}", skuIds);
@ -50,4 +59,37 @@ public class PmsSkuServiceImpl extends ServiceImpl<PmsSkuMapper, PmsSku> impleme
}
return true;
}
/**
* Cache-Aside pattern 缓存数据库读写模式
* 1. 读取数据先读缓存没有就去读数据库然后将结果写入缓存
* 2. 写入数据先更新数据库再删除缓存
* @param skuId
* @return
*/
@Override
public Integer getInventoryBySkuId(Long skuId) {
Integer inventory = 0;
// ->缓存
Object cacheVal = redisTemplate.opsForValue().get(RedisConstants.PRODUCT_INVENTORY_PREFIX + skuId);
if (cacheVal != null) {
inventory = Convert.toInt(cacheVal);
return inventory;
}
// ->数据库
PmsSku pmsInventory = this.getOne(new LambdaQueryWrapper<PmsSku>()
.eq(PmsSku::getId, skuId)
.select(PmsSku::getStock));
if (pmsInventory != null) {
inventory = pmsInventory.getStock();
// ->缓存
redisTemplate.opsForValue().set(RedisConstants.PRODUCT_INVENTORY_PREFIX + skuId, inventory);
}
return inventory;
}
}

View File

@ -0,0 +1,7 @@
package com.youlai.common.constant;
public interface RedisConstants {
String BUSINESS_NO_PREFIX = "business_no:";
}

View File

@ -4,26 +4,29 @@ import lombok.Getter;
import lombok.Setter;
/**
*
* @author haoxr
* @date 2021-02-17 13:13
*
*/
public enum BusinessTypeEnum {
USER("100", "用户类型编号"),
MEMBER("200", "会员类型编号"),
ORDER("300", "订单类型编号");
USER("user", 100),
MEMBER("member", 200),
ORDER("order", 300);
@Getter
@Setter
private String code;
BusinessTypeEnum(String code, String desc) {
@Getter
@Setter
private Integer value;
BusinessTypeEnum(String code, Integer value) {
this.code = code;
this.value = value;
}
public static BusinessTypeEnum getValue(String code){
public static BusinessTypeEnum getValue(String code) {
for (BusinessTypeEnum value : values()) {
if (value.getCode().equals(code)) {
return value;

View File

@ -1,7 +1,7 @@
package com.youlai.common.redis.component;
import com.youlai.common.constant.RedisConstants;
import com.youlai.common.enums.BusinessTypeEnum;
import com.youlai.common.redis.constant.RedisKeyConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
@ -20,22 +20,22 @@ public class BusinessNoGenerator {
private RedisTemplate redisTemplate;
/**
* @param businessCode 业务类型编号
* @param businessType 业务类型枚举
* @param digit 业务序号位数
* @return
*/
public String generate(String businessCode, Integer digit) {
public String generate(BusinessTypeEnum businessType, Integer digit) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
String date = LocalDateTime.now(ZoneOffset.of("+8")).format(formatter);
String key = RedisKeyConstants.BUSINESS_NO_PREFIX + BusinessTypeEnum.getValue(businessCode).toString().toLowerCase() + ":" + date;
String key = RedisConstants.BUSINESS_NO_PREFIX +businessType.getCode() + ":" + date;
Long increment = redisTemplate.opsForValue().increment(key);
return date + businessCode + String.format("%0" + digit + "d", increment);
return date + businessType.getValue() + String.format("%0" + digit + "d", increment);
}
public String generate(String businessCode) {
public String generate(BusinessTypeEnum businessType) {
Integer defaultDigit = 6;
return generate(businessCode, defaultDigit);
return generate(businessType, defaultDigit);
}
}