- 后端:引入分布式事务框架 seata 框架。

- 后端:目前订单提交时,如果发生异常,会回滚优惠劵的使用。
This commit is contained in:
YunaiV 2019-05-12 18:47:45 +08:00
parent 36018c84da
commit 4888dfaf2a
20 changed files with 275 additions and 146 deletions

View File

@ -173,6 +173,7 @@
createOrder({ createOrder({
orderItems, orderItems,
userAddressId, userAddressId,
couponCardId,
remark, remark,
}).then(result => { }).then(result => {
if (result) { if (result) {

View File

@ -74,11 +74,13 @@
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId> <artifactId>spring-boot-starter-actuator</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId> <!-- <dependency>-->
<artifactId>spring-boot-devtools</artifactId> <!-- <groupId>org.springframework.boot</groupId>-->
<optional>true</optional> <!-- <artifactId>spring-boot-devtools</artifactId>-->
</dependency> <!-- <optional>true</optional>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>de.codecentric</groupId> <groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId> <artifactId>spring-boot-admin-starter-client</artifactId>

View File

@ -17,6 +17,8 @@ import cn.iocoder.mall.order.application.convert.CartConvert;
import cn.iocoder.mall.order.application.convert.OrderConvertAPP; import cn.iocoder.mall.order.application.convert.OrderConvertAPP;
import cn.iocoder.mall.order.application.po.user.OrderCreatePO; import cn.iocoder.mall.order.application.po.user.OrderCreatePO;
import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO; import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO;
import cn.iocoder.mall.promotion.api.CouponService;
import cn.iocoder.mall.promotion.api.bo.CouponCardAvailableBO;
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder; import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
@ -29,6 +31,8 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cn.iocoder.common.framework.vo.CommonResult.success;
/** /**
* 订单API(users) * 订单API(users)
* *
@ -46,6 +50,8 @@ public class OrderController {
private CartService cartService; private CartService cartService;
@Reference(validation = "true", version = "${dubbo.consumer.DataDictService.version}") @Reference(validation = "true", version = "${dubbo.consumer.DataDictService.version}")
private DataDictService dataDictService; private DataDictService dataDictService;
@Reference(validation = "true", version = "${dubbo.consumer.CouponService.version}")
private CouponService couponService;
@GetMapping("order_page") @GetMapping("order_page")
@ApiOperation("订单分页") @ApiOperation("订单分页")
@ -97,14 +103,18 @@ public class OrderController {
public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam("skuId") Integer skuId, public CommonResult<UsersOrderConfirmCreateVO> getConfirmCreateOrder(@RequestParam("skuId") Integer skuId,
@RequestParam("quantity") Integer quantity, @RequestParam("quantity") Integer quantity,
@RequestParam(value = "couponCardId", required = false) Integer couponCardId) { @RequestParam(value = "couponCardId", required = false) Integer couponCardId) {
Integer userId = UserSecurityContextHolder.getContext().getUserId();
// 创建 CalcOrderPriceDTO 对象并执行价格计算 // 创建 CalcOrderPriceDTO 对象并执行价格计算
CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO() CalcOrderPriceDTO calcOrderPriceDTO = new CalcOrderPriceDTO()
.setUserId(UserSecurityContextHolder.getContext().getUserId()) .setUserId(userId)
.setItems(Collections.singletonList(new CalcOrderPriceDTO.Item(skuId, quantity, true))) .setItems(Collections.singletonList(new CalcOrderPriceDTO.Item(skuId, quantity, true)))
.setCouponCardId(couponCardId); .setCouponCardId(couponCardId);
CalcOrderPriceBO calcOrderPrice = cartService.calcOrderPrice(calcOrderPriceDTO); CalcOrderPriceBO calcOrderPrice = cartService.calcOrderPrice(calcOrderPriceDTO);
// 获得优惠劵
List<CouponCardAvailableBO> couponCards = couponService.getCouponCardList(userId,
CartConvert.INSTANCE.convertList(calcOrderPrice.getItemGroups()));
// 执行数据拼装 // 执行数据拼装
return CommonResult.success(CartConvert.INSTANCE.convert(calcOrderPrice)); return success(CartConvert.INSTANCE.convert(calcOrderPrice).setCouponCards(couponCards));
} }
@PostMapping("confirm_receiving") @PostMapping("confirm_receiving")

View File

@ -30,7 +30,6 @@
<groupId>cn.iocoder.mall</groupId> <groupId>cn.iocoder.mall</groupId>
<artifactId>pay-service-api</artifactId> <artifactId>pay-service-api</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.iocoder.mall</groupId> <groupId>cn.iocoder.mall</groupId>
@ -41,13 +40,11 @@
<groupId>cn.iocoder.mall</groupId> <groupId>cn.iocoder.mall</groupId>
<artifactId>system-service-api</artifactId> <artifactId>system-service-api</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.iocoder.mall</groupId> <groupId>cn.iocoder.mall</groupId>
<artifactId>user-service-api</artifactId> <artifactId>user-service-api</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.iocoder.mall</groupId> <groupId>cn.iocoder.mall</groupId>
@ -72,10 +69,17 @@
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId> <!-- <dependency>-->
<artifactId>spring-boot-starter-jdbc</artifactId> <!-- <groupId>org.springframework.boot</groupId>-->
</dependency> <!-- <artifactId>spring-boot-starter-jdbc</artifactId>-->
<!--&lt;!&ndash; <exclusions>&ndash;&gt;-->
<!--&lt;!&ndash; <exclusion>&ndash;&gt;-->
<!--&lt;!&ndash; <artifactId>HikariCP</artifactId>&ndash;&gt;-->
<!--&lt;!&ndash; <groupId>com.zaxxer</groupId>&ndash;&gt;-->
<!--&lt;!&ndash; </exclusion>&ndash;&gt;-->
<!--&lt;!&ndash; </exclusions>&ndash;&gt;-->
<!-- </dependency>-->
<!-- spring cloud and dubbo --> <!-- spring cloud and dubbo -->
<dependency> <dependency>
@ -94,21 +98,25 @@
<groupId>org.apache.dubbo</groupId> <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId> <artifactId>dubbo-spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.baomidou</groupId> <groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency> <dependency>
<groupId>org.apache.rocketmq</groupId> <groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId> <artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.0.1</version> </dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring</artifactId>
</dependency>
<!--dependency for Apache Dubbo-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-dubbo</artifactId>
</dependency> </dependency>
<!-- test --> <!-- test -->

View File

@ -1,9 +1,20 @@
package cn.iocoder.mall.order.biz.config; package cn.iocoder.mall.order.biz.config;
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import io.seata.spring.annotation.GlobalTransactionScanner;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration @Configuration
@MapperScan("cn.iocoder.mall.order.biz.dao") // 扫描对应的 Mapper 接口 @MapperScan("cn.iocoder.mall.order.biz.dao") // 扫描对应的 Mapper 接口
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理为什么使用 proxyTargetClass 参数参见 https://blog.csdn.net/huang_550/article/details/76492600 @EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理为什么使用 proxyTargetClass 参数参见 https://blog.csdn.net/huang_550/article/details/76492600
@ -11,4 +22,54 @@ public class DatabaseConfiguration {
// 数据源使用 HikariCP // 数据源使用 HikariCP
@Value("${spring.application.name}")
private String applicationId;
@Autowired
private DataSourceProperties dataSourceProperties;
@Bean
// @Primary
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(dataSourceProperties.getUrl());
druidDataSource.setUsername(dataSourceProperties.getUsername());
druidDataSource.setPassword(dataSourceProperties.getPassword());
druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
druidDataSource.setInitialSize(0);
druidDataSource.setMaxActive(180);
druidDataSource.setMaxWait(60000);
druidDataSource.setMinIdle(0);
druidDataSource.setValidationQuery("Select 1 from DUAL");
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(1800);
druidDataSource.setLogAbandoned(true);
return druidDataSource;
}
@ConfigurationProperties(prefix = "spring.datasource")
@Primary
@Bean("dataSource")
// @Bean
public DataSource dataSource(DruidDataSource dataSource) {
return new DataSourceProxy(dataSource);
}
/**
* 注册一个StatViewServlet
*
* @return global transaction scanner
*/
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
return new GlobalTransactionScanner(applicationId, "my_test_tx_group");
// TODO 芋艿txServiceGroup 后续要编辑下
}
} }

View File

@ -0,0 +1,9 @@
package cn.iocoder.mall.order.biz.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SeataConfiguration {
}

View File

@ -2,6 +2,7 @@ package cn.iocoder.mall.order.biz.dao;
import cn.iocoder.mall.order.api.dto.OrderQueryDTO; import cn.iocoder.mall.order.api.dto.OrderQueryDTO;
import cn.iocoder.mall.order.biz.dataobject.OrderDO; import cn.iocoder.mall.order.biz.dataobject.OrderDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@ -14,14 +15,7 @@ import java.util.List;
* @time 2019-03-16 15:09 * @time 2019-03-16 15:09
*/ */
@Repository @Repository
public interface OrderMapper { public interface OrderMapper extends BaseMapper<OrderDO> {
/**
* 插入数据
*
* @param orderDO
*/
void insert(OrderDO orderDO);
/** /**
* 更新 - 根据 id 更新 * 更新 - 根据 id 更新

View File

@ -1,6 +1,7 @@
package cn.iocoder.mall.order.biz.dataobject; package cn.iocoder.mall.order.biz.dataobject;
import cn.iocoder.common.framework.dataobject.DeletableDO; import cn.iocoder.common.framework.dataobject.DeletableDO;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
@ -14,6 +15,7 @@ import java.util.Date;
*/ */
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
@TableName(value = "orders")
public class OrderDO extends DeletableDO { public class OrderDO extends DeletableDO {
/** /**

View File

@ -21,6 +21,7 @@ import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO;
import cn.iocoder.mall.promotion.api.CouponService; import cn.iocoder.mall.promotion.api.CouponService;
import cn.iocoder.mall.user.api.UserAddressService; import cn.iocoder.mall.user.api.UserAddressService;
import cn.iocoder.mall.user.api.bo.UserAddressBO; import cn.iocoder.mall.user.api.bo.UserAddressBO;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.dubbo.config.annotation.Reference; import org.apache.dubbo.config.annotation.Reference;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -210,6 +211,7 @@ public class OrderServiceImpl implements OrderService {
} }
@Override @Override
@GlobalTransactional
@Transactional // TODO 芋艿先不考虑分布式事务的问题 @Transactional // TODO 芋艿先不考虑分布式事务的问题
public CommonResult<OrderCreateBO> createOrder(OrderCreateDTO orderCreateDTO) { public CommonResult<OrderCreateBO> createOrder(OrderCreateDTO orderCreateDTO) {
Integer userId = orderCreateDTO.getUserId(); Integer userId = orderCreateDTO.getUserId();
@ -284,8 +286,8 @@ public class OrderServiceImpl implements OrderService {
.setHasReturnExchange(OrderHasReturnExchangeEnum.NO.getValue()) .setHasReturnExchange(OrderHasReturnExchangeEnum.NO.getValue())
.setRemark(Optional.ofNullable(orderCreateDTO.getRemark()).orElse("")); .setRemark(Optional.ofNullable(orderCreateDTO.getRemark()).orElse(""));
orderDO.setDeleted(DeletedStatusEnum.DELETED_NO.getValue()); orderDO.setDeleted(DeletedStatusEnum.DELETED_NO.getValue());
orderDO.setCreateTime(new Date()); // orderDO.setCreateTime(new Date());
orderDO.setUpdateTime(null); // orderDO.setUpdateTime(null);
orderMapper.insert(orderDO); orderMapper.insert(orderDO);
// 收件人信息 // 收件人信息
@ -321,6 +323,10 @@ public class OrderServiceImpl implements OrderService {
// 一次性插入 // 一次性插入
orderItemMapper.insert(orderItemDOList); orderItemMapper.insert(orderItemDOList);
if (true) {
throw new RuntimeException("测试 seata 事务回滚");
}
// 创建预订单 // 创建预订单
createPayTransaction(orderDO, orderItemDOList, orderCreateDTO.getIp()); createPayTransaction(orderDO, orderItemDOList, orderCreateDTO.getIp());

View File

@ -38,6 +38,7 @@ dubbo:
CartService: CartService:
version: 1.0.0 version: 1.0.0
consumer: consumer:
timeout: 120000 # 设置长一点,方便调试代码
ProductSpuService: ProductSpuService:
version: 1.0.0 version: 1.0.0
PromotionActivityService: PromotionActivityService:

View File

@ -9,26 +9,6 @@
status, remark, create_time, update_time, `deleted` status, remark, create_time, update_time, `deleted`
</sql> </sql>
<!--
插入数据
-->
<insert id="insert" parameterType="OrderDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
INSERT INTO `order` (
user_id, order_no, buy_price, discount_price, logistics_price, present_price, pay_amount,
payment_time, delivery_time, receiver_time, closing_time,
has_return_exchange,
status, remark, create_time, update_time, `deleted`
) VALUES (
#{userId}, #{orderNo}, #{buyPrice}, #{discountPrice}, #{logisticsPrice}, #{presentPrice}, #{payAmount},
#{paymentTime}, #{deliveryTime}, #{receiverTime}, #{closingTime},
#{hasReturnExchange},
#{status}, #{remark}, #{createTime}, #{updateTime}, #{deleted}
)
</insert>
<!--
更新 - 可更新的字段
-->
<sql id="updateFieldSql" > <sql id="updateFieldSql" >
<set> <set>
<if test="orderNo != null"> <if test="orderNo != null">
@ -86,9 +66,6 @@
</set> </set>
</sql> </sql>
<!--
更新 - 根据 id 更新
-->
<update id="updateById" parameterType="OrderDO"> <update id="updateById" parameterType="OrderDO">
UPDATE `order` UPDATE `order`
<include refid="updateFieldSql" /> <include refid="updateFieldSql" />
@ -112,9 +89,6 @@
AND status = #{status} AND status = #{status}
</update> </update>
<!--
查询 - 根据id 查询
-->
<select id="selectById" resultType="cn.iocoder.mall.order.biz.dataobject.OrderDO"> <select id="selectById" resultType="cn.iocoder.mall.order.biz.dataobject.OrderDO">
SELECT SELECT
<include refid="FIELDS" /> <include refid="FIELDS" />
@ -122,9 +96,6 @@
WHERE id = #{id} WHERE id = #{id}
</select> </select>
<!--
查询条件 注意:条件顺序,避免不能使用索引
-->
<sql id="selectWhere"> <sql id="selectWhere">
<if test="status != null"> <if test="status != null">
AND `status` = #{status} AND `status` = #{status}
@ -147,26 +118,22 @@
</if> </if>
</sql> </sql>
<!--
查询 - 后台分页page Count
-->
<select id="selectPageCount" resultType="java.lang.Integer"> <select id="selectPageCount" resultType="java.lang.Integer">
SELECT SELECT
COUNT(*) COUNT(*)
FROM `order` FROM `order`
WHERE 1 = 1 <!-- TODO 芋艿 不要 1=1 ,会有问题,使用 where 标签 --> <where>
<include refid="selectWhere" /> <include refid="selectWhere" />
</where>
</select> </select>
<!--
查询 - 后台分页page
-->
<select id="selectPage" resultType="cn.iocoder.mall.order.biz.dataobject.OrderDO"> <select id="selectPage" resultType="cn.iocoder.mall.order.biz.dataobject.OrderDO">
SELECT SELECT
<include refid="FIELDS" /> <include refid="FIELDS" />
FROM `order` FROM `order`
WHERE 1 = 1 <!-- TODO 芋艿 不要 1=1 ,会有问题,使用 where 标签 --> <where>
<include refid="selectWhere" /> <include refid="selectWhere" />
</where>
LIMIT ${pageNo * pageSize}, ${pageSize} LIMIT ${pageNo * pageSize}, ${pageSize}
</select> </select>

View File

@ -16,7 +16,7 @@
`type`, create_time, update_time `type`, create_time, update_time
) VALUES ( ) VALUES (
#{orderId}, #{areaNo}, #{name}, #{mobile}, #{address}, #{orderId}, #{areaNo}, #{name}, #{mobile}, #{address},
#{type}, #{createTime}, #{updateTime} #{type}, #{createTime,jdbcType=TIMESTAMP} , #{updateTime}
) )
</insert> </insert>

View File

@ -30,7 +30,7 @@
<springboot.version>2.1.3.RELEASE</springboot.version> <springboot.version>2.1.3.RELEASE</springboot.version>
<!-- <com.alibab.dubbo.version>2.6.5</com.alibab.dubbo.version>--> <!-- <com.alibab.dubbo.version>2.6.5</com.alibab.dubbo.version>-->
<dubbo.version>2.7.1</dubbo.version> <dubbo.version>2.7.1</dubbo.version>
<mysql-connector-java.version>5.1.47</mysql-connector-java.version> <mysql-connector-java.version>5.1.46</mysql-connector-java.version>
<!-- <dubbo-spring-boot-starter.version>0.2.1.RELEASE</dubbo-spring-boot-starter.version>--> <!-- <dubbo-spring-boot-starter.version>0.2.1.RELEASE</dubbo-spring-boot-starter.version>-->
<org.mapstruct.version>1.3.0.Final</org.mapstruct.version> <org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
<curator.version>2.13.0</curator.version> <curator.version>2.13.0</curator.version>

View File

@ -22,7 +22,6 @@
<artifactId>mall-spring-boot</artifactId> <artifactId>mall-spring-boot</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>cn.iocoder.mall</groupId> <groupId>cn.iocoder.mall</groupId>
<artifactId>product-service-api</artifactId> <artifactId>product-service-api</artifactId>

View File

@ -1,36 +0,0 @@
package cn.iocoder.mall.promotion.application.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
@Configuration
@EnableSwagger2 // TODO 生产环境时禁用掉
public class SwaggerConfiguration {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.iocoder.mall.promotion.application.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("营销子系统")
.description("营销子系统")
.termsOfServiceUrl("http://www.iocoder.cn")
.version("1.0.0")
.build();
}
}

View File

@ -28,6 +28,35 @@
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-jdbc</artifactId>-->
<!-- <exclusions>-->
<!-- <exclusion>-->
<!-- <artifactId>logback-classic</artifactId>-->
<!-- <groupId>ch.qos.logback</groupId>-->
<!-- </exclusion>-->
<!-- <exclusion>-->
<!-- <artifactId>spring-boot-starter-logging</artifactId>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- </exclusion>-->
<!-- </exclusions>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>org.apache.dubbo</groupId> <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo</artifactId> <artifactId>dubbo</artifactId>
@ -46,50 +75,53 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>com.baomidou</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<exclusions> <exclusions>
<exclusion> <exclusion>
<artifactId>logback-classic</artifactId>
<groupId>ch.qos.logback</groupId>
</exclusion>
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.apache.rocketmq</groupId> <groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId> <artifactId>rocketmq-spring-boot-starter</artifactId>
</dependency> </dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring</artifactId>
</dependency>
<!--dependency for Apache Dubbo-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-dubbo</artifactId>
</dependency>
<!-- test --> <!-- test -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework</groupId>-->
<!-- <artifactId>spring-tx</artifactId>-->
<!-- <version>5.0.12.RELEASE</version>-->
<!-- <scope>compile</scope>-->
<!-- </dependency>-->
</dependencies> </dependencies>

View File

@ -1,14 +1,78 @@
package cn.iocoder.mall.promotion.biz.config; package cn.iocoder.mall.promotion.biz.config;
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import io.seata.spring.annotation.GlobalTransactionScanner;
import org.mybatis.spring.annotation.MapperScan; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration @Configuration
@MapperScan("cn.iocoder.mall.promotion.biz.dao") // 扫描对应的 Mapper 接口 @MapperScan("cn.iocoder.mall.promotion.biz.dao") // 扫描对应的 Mapper 接口
@EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理为什么使用 proxyTargetClass 参数参见 https://blog.csdn.net/huang_550/article/details/76492600 @EnableTransactionManagement(proxyTargetClass = true) // 启动事务管理为什么使用 proxyTargetClass 参数参见 https://blog.csdn.net/huang_550/article/details/76492600
@EnableConfigurationProperties(DataSourceProperties.class)
public class DatabaseConfiguration { public class DatabaseConfiguration {
// 数据源使用 HikariCP // 数据源使用 HikariCP
@Value("${spring.application.name}")
private String applicationId;
@Autowired
private DataSourceProperties dataSourceProperties;
// @Bean // TODO 芋艿加了就一直报错后面在找原因
// @Primary
public DruidDataSource druidDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl(dataSourceProperties.getUrl());
druidDataSource.setUsername(dataSourceProperties.getUsername());
druidDataSource.setPassword(dataSourceProperties.getPassword());
druidDataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
druidDataSource.setInitialSize(0);
druidDataSource.setMaxActive(180);
druidDataSource.setMaxWait(60000);
druidDataSource.setMinIdle(0);
druidDataSource.setValidationQuery("Select 1 from DUAL");
druidDataSource.setTestOnBorrow(false);
druidDataSource.setTestOnReturn(false);
druidDataSource.setTestWhileIdle(true);
druidDataSource.setTimeBetweenEvictionRunsMillis(60000);
druidDataSource.setMinEvictableIdleTimeMillis(25200000);
druidDataSource.setRemoveAbandoned(true);
druidDataSource.setRemoveAbandonedTimeout(1800);
druidDataSource.setLogAbandoned(true);
return druidDataSource;
}
@ConfigurationProperties(prefix = "spring.datasource")
@Primary
@Bean("dataSource")
// @Bean
public DataSource dataSource() {
DruidDataSource dataSource = druidDataSource();
return new DataSourceProxy(dataSource);
}
/**
* 注册一个StatViewServlet
*
* @return global transaction scanner
*/
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
return new GlobalTransactionScanner(applicationId, "my_test_tx_group");
// TODO 芋艿txServiceGroup 后续要编辑下
}
} }

View File

@ -242,14 +242,14 @@ public class CouponServiceImpl implements CouponService {
if (!userId.equals(card.getUserId())) { if (!userId.equals(card.getUserId())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_ERROR_USER.getCode()); throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_ERROR_USER.getCode());
} }
if (CouponCardStatusEnum.UNUSED.getValue().equals(card.getStatus())) { if (!CouponCardStatusEnum.UNUSED.getValue().equals(card.getStatus())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode()); throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode());
} }
if (DateUtil.isBetween(card.getValidStartTime(), card.getValidEndTime())) { // 为避免定时器没跑实际优惠劵已经过期 if (DateUtil.isBetween(card.getValidStartTime(), card.getValidEndTime())) { // 为避免定时器没跑实际优惠劵已经过期
throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode()); throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode());
} }
// 更新优惠劵已使用 // 更新优惠劵已使用
int updateCount = couponCardMapper.updateByIdAndStatus(card.getId(), CouponCardStatusEnum.USED.getValue(), int updateCount = couponCardMapper.updateByIdAndStatus(card.getId(), CouponCardStatusEnum.UNUSED.getValue(),
new CouponCardDO().setStatus(CouponCardStatusEnum.USED.getValue()).setUsedTime(new Date())); new CouponCardDO().setStatus(CouponCardStatusEnum.USED.getValue()).setUsedTime(new Date()));
if (updateCount == 0) { if (updateCount == 0) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode()); throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_UNUSED.getCode());
@ -267,12 +267,12 @@ public class CouponServiceImpl implements CouponService {
if (!userId.equals(card.getUserId())) { if (!userId.equals(card.getUserId())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_ERROR_USER.getCode()); throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_ERROR_USER.getCode());
} }
if (CouponCardStatusEnum.USED.getValue().equals(card.getStatus())) { if (!CouponCardStatusEnum.USED.getValue().equals(card.getStatus())) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_USED.getCode()); throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_USED.getCode());
} }
// 更新优惠劵已使用 // 更新优惠劵已使用
int updateCount = couponCardMapper.updateByIdAndStatus(card.getId(), CouponCardStatusEnum.UNUSED.getValue(), int updateCount = couponCardMapper.updateByIdAndStatus(card.getId(), CouponCardStatusEnum.USED.getValue(),
new CouponCardDO().setStatus(CouponCardStatusEnum.USED.getValue())); // TODO 芋艿usedTime 未设置空后面处理 new CouponCardDO().setStatus(CouponCardStatusEnum.UNUSED.getValue())); // TODO 芋艿usedTime 未设置空后面处理
if (updateCount == 0) { if (updateCount == 0) {
throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_USED.getCode()); throw ServiceExceptionUtil.exception(PromotionErrorCodeEnum.COUPON_CARD_STATUS_NOT_USED.getCode());
} }

View File

@ -7,9 +7,18 @@ spring:
password: ${MALL_MYSQL_PASSWORD} password: ${MALL_MYSQL_PASSWORD}
# mybatis # mybatis
mybatis: #mybatis:
config-location: classpath:mybatis-config.xml # config-location: classpath:mybatis-config.xml
mapper-locations: classpath:mapper/*.xml # mapper-locations: classpath:mapper/*.xml
# type-aliases-package: cn.iocoder.mall.promotion.biz.dataobject
# mybatis-plus
mybatis-plus:
configuration:
map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
global-config:
db-config:
id-type: auto
mapper-locations: classpath*:mapper/*.xml
type-aliases-package: cn.iocoder.mall.promotion.biz.dataobject type-aliases-package: cn.iocoder.mall.promotion.biz.dataobject
# dubbo # dubbo