Compare commits
No commits in common. '79b41707059a5b5fbcbeddaa0fdca67f68c84831' and 'fa8e64a4ce62328994443ac7fd3b6325f9e6ed8d' have entirely different histories.
79b4170705
...
fa8e64a4ce
@ -1,65 +0,0 @@
|
|||||||
## transaction log store, only used in seata-server
|
|
||||||
store {
|
|
||||||
## store mode: file、db、redis
|
|
||||||
mode = "db"
|
|
||||||
|
|
||||||
## file store property
|
|
||||||
file {
|
|
||||||
## store location dir
|
|
||||||
dir = "sessionStore"
|
|
||||||
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
|
|
||||||
maxBranchSessionSize = 16384
|
|
||||||
# globe session size , if exceeded throws exceptions
|
|
||||||
maxGlobalSessionSize = 512
|
|
||||||
# file buffer size , if exceeded allocate new buffer
|
|
||||||
fileWriteBufferCacheSize = 16384
|
|
||||||
# when recover batch read size
|
|
||||||
sessionReloadReadSize = 100
|
|
||||||
# async, sync
|
|
||||||
flushDiskMode = async
|
|
||||||
}
|
|
||||||
|
|
||||||
## database store property
|
|
||||||
db {
|
|
||||||
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
|
|
||||||
datasource = "druid"
|
|
||||||
## mysql/oracle/postgresql/h2/oceanbase etc.
|
|
||||||
dbType = "mysql"
|
|
||||||
driverClassName = "com.mysql.jdbc.Driver"
|
|
||||||
url = "jdbc:mysql://127.0.0.1:3306/seata?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false"
|
|
||||||
user = "root"
|
|
||||||
password = "root"
|
|
||||||
minConn = 5
|
|
||||||
maxConn = 100
|
|
||||||
globalTable = "global_table"
|
|
||||||
branchTable = "branch_table"
|
|
||||||
lockTable = "lock_table"
|
|
||||||
queryLimit = 100
|
|
||||||
maxWait = 5000
|
|
||||||
}
|
|
||||||
|
|
||||||
## redis store property
|
|
||||||
redis {
|
|
||||||
host = "127.0.0.1"
|
|
||||||
port = "6379"
|
|
||||||
password = ""
|
|
||||||
database = "0"
|
|
||||||
minConn = 1
|
|
||||||
maxConn = 10
|
|
||||||
maxTotal = 100
|
|
||||||
queryLimit = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
service {
|
|
||||||
vgroupMapping.dev-protocol = "default"
|
|
||||||
default.grouplist = "127.0.0.1:8091"
|
|
||||||
}
|
|
||||||
client {
|
|
||||||
async.commit.buffer.limit = 10000
|
|
||||||
lock {
|
|
||||||
retry.internal = 10
|
|
||||||
retry.times = 30
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
registry {
|
|
||||||
# file、nacos、eureka、redis、zk、consul
|
|
||||||
type = "file"
|
|
||||||
|
|
||||||
file {
|
|
||||||
name = "file.conf"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
config {
|
|
||||||
type = "file"
|
|
||||||
|
|
||||||
file {
|
|
||||||
name = "file.conf"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
## transaction log store, only used in seata-server
|
|
||||||
store {
|
|
||||||
## store mode: file、db、redis
|
|
||||||
mode = "db"
|
|
||||||
|
|
||||||
## file store property
|
|
||||||
file {
|
|
||||||
## store location dir
|
|
||||||
dir = "sessionStore"
|
|
||||||
# branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions
|
|
||||||
maxBranchSessionSize = 16384
|
|
||||||
# globe session size , if exceeded throws exceptions
|
|
||||||
maxGlobalSessionSize = 512
|
|
||||||
# file buffer size , if exceeded allocate new buffer
|
|
||||||
fileWriteBufferCacheSize = 16384
|
|
||||||
# when recover batch read size
|
|
||||||
sessionReloadReadSize = 100
|
|
||||||
# async, sync
|
|
||||||
flushDiskMode = async
|
|
||||||
}
|
|
||||||
|
|
||||||
## database store property
|
|
||||||
db {
|
|
||||||
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari) etc.
|
|
||||||
datasource = "druid"
|
|
||||||
## mysql/oracle/postgresql/h2/oceanbase etc.
|
|
||||||
dbType = "mysql"
|
|
||||||
driverClassName = "com.mysql.jdbc.Driver"
|
|
||||||
url = "jdbc:mysql://127.0.0.1:3306/seata?autoReconnect=true&useUnicode=true&characterEncoding=utf8&useSSL=false"
|
|
||||||
user = "root"
|
|
||||||
password = "root"
|
|
||||||
minConn = 5
|
|
||||||
maxConn = 100
|
|
||||||
globalTable = "global_table"
|
|
||||||
branchTable = "branch_table"
|
|
||||||
lockTable = "lock_table"
|
|
||||||
queryLimit = 100
|
|
||||||
maxWait = 5000
|
|
||||||
}
|
|
||||||
|
|
||||||
## redis store property
|
|
||||||
redis {
|
|
||||||
host = "127.0.0.1"
|
|
||||||
port = "6379"
|
|
||||||
password = ""
|
|
||||||
database = "0"
|
|
||||||
minConn = 1
|
|
||||||
maxConn = 10
|
|
||||||
maxTotal = 100
|
|
||||||
queryLimit = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
service {
|
|
||||||
vgroupMapping.dev-protocol = "default"
|
|
||||||
default.grouplist = "127.0.0.1:8091"
|
|
||||||
}
|
|
||||||
client {
|
|
||||||
async.commit.buffer.limit = 10000
|
|
||||||
lock {
|
|
||||||
retry.internal = 10
|
|
||||||
retry.times = 30
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
registry {
|
|
||||||
# file、nacos、eureka、redis、zk、consul
|
|
||||||
type = "file"
|
|
||||||
|
|
||||||
file {
|
|
||||||
name = "file.conf"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
config {
|
|
||||||
type = "file"
|
|
||||||
|
|
||||||
file {
|
|
||||||
name = "file.conf"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
# 订单微服务
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
@ -1,52 +0,0 @@
|
|||||||
package org.example.controller;
|
|
||||||
|
|
||||||
import io.swagger.annotations.Api;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.example.common.TableId;
|
|
||||||
import org.example.order.OrderInfo;
|
|
||||||
import org.example.service.IOrderService;
|
|
||||||
import org.example.vo.PageSimpleOrderDetail;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>订单服务对外 HTTP 接口</h1>
|
|
||||||
* */
|
|
||||||
@Api(tags = "订单服务")
|
|
||||||
@Slf4j
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/order")
|
|
||||||
public class OrderController {
|
|
||||||
|
|
||||||
private final IOrderService orderService;
|
|
||||||
|
|
||||||
public OrderController(IOrderService orderService) {
|
|
||||||
this.orderService = orderService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation(
|
|
||||||
value = "创建",
|
|
||||||
notes = "购买(分布式事务): 创建订单 -> 扣减库存 -> 扣减余额 -> 发送物流消息",
|
|
||||||
httpMethod = "POST"
|
|
||||||
)
|
|
||||||
@PostMapping("/create-order")
|
|
||||||
public TableId createOrder(@RequestBody OrderInfo orderInfo) {
|
|
||||||
return orderService.createOrder(orderInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation(
|
|
||||||
value = "订单信息",
|
|
||||||
notes = "获取当前用户的订单信息: 带有分页",
|
|
||||||
httpMethod = "GET"
|
|
||||||
)
|
|
||||||
@GetMapping("/order-detail")
|
|
||||||
public PageSimpleOrderDetail getSimpleOrderDetailByPage(
|
|
||||||
@RequestParam(required = false, defaultValue = "1") int page) {
|
|
||||||
return orderService.getSimpleOrderDetailByPage(page);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package org.example.dao;
|
|
||||||
|
|
||||||
import org.example.entity.EcommerceOrder;
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.data.repository.PagingAndSortingRepository;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>EcommerceOrder Dao 接口定义</h1>
|
|
||||||
* 支持分页
|
|
||||||
* */
|
|
||||||
public interface EcommerceOrderDao extends PagingAndSortingRepository<EcommerceOrder, Long> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>根据 userId 查询分页订单</h2>
|
|
||||||
* select * from t_dev_protocol_cloud_order where user_id = ?
|
|
||||||
* order by ... desc/asc limit x offset y
|
|
||||||
* */
|
|
||||||
Page<EcommerceOrder> findAllByUserId(Long userId, Pageable pageable);
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package org.example.entity;
|
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import org.springframework.data.annotation.CreatedDate;
|
|
||||||
import org.springframework.data.annotation.LastModifiedDate;
|
|
||||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
|
||||||
|
|
||||||
import javax.persistence.Column;
|
|
||||||
import javax.persistence.Entity;
|
|
||||||
import javax.persistence.EntityListeners;
|
|
||||||
import javax.persistence.GeneratedValue;
|
|
||||||
import javax.persistence.GenerationType;
|
|
||||||
import javax.persistence.Id;
|
|
||||||
import javax.persistence.Table;
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>用户订单表实体类定义</h1>
|
|
||||||
* */
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
@Entity
|
|
||||||
@EntityListeners(AuditingEntityListener.class)
|
|
||||||
@Table(name = "t_dev_protocol_cloud_order")
|
|
||||||
public class EcommerceOrder {
|
|
||||||
|
|
||||||
/** 自增主键 */
|
|
||||||
@Id
|
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
|
||||||
@Column(name = "id", nullable = false)
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/** 用户 id */
|
|
||||||
@Column(name = "user_id", nullable = false)
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/** 用户地址 id */
|
|
||||||
@Column(name = "address_id", nullable = false)
|
|
||||||
private Long addressId;
|
|
||||||
|
|
||||||
/** 订单详情(json 存储) */
|
|
||||||
@Column(name = "order_detail", nullable = false)
|
|
||||||
private String orderDetail;
|
|
||||||
|
|
||||||
/** 创建时间 */
|
|
||||||
@CreatedDate
|
|
||||||
@Column(name = "create_time", nullable = false)
|
|
||||||
private Date createTime;
|
|
||||||
|
|
||||||
/** 更新时间 */
|
|
||||||
@LastModifiedDate
|
|
||||||
@Column(name = "update_time", nullable = false)
|
|
||||||
private Date updateTime;
|
|
||||||
|
|
||||||
public EcommerceOrder(Long userId, Long addressId, String orderDetail) {
|
|
||||||
|
|
||||||
this.userId = userId;
|
|
||||||
this.addressId = addressId;
|
|
||||||
this.orderDetail = orderDetail;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
package org.example.feign;
|
|
||||||
|
|
||||||
import org.example.account.AddressInfo;
|
|
||||||
import org.example.common.TableId;
|
|
||||||
import org.example.feign.hystrix.AddressClientHystrix;
|
|
||||||
import org.example.vo.CommonResponse;
|
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>用户账户服务 Feign 接口(安全的)</h1>
|
|
||||||
* */
|
|
||||||
@FeignClient(
|
|
||||||
contextId = "AddressClient",
|
|
||||||
value = "dev-protocol-springcloud-project-account-service", // 调用账号微服务
|
|
||||||
fallback = AddressClientHystrix.class // 兜底策略
|
|
||||||
)
|
|
||||||
public interface AddressClient {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>根据 id 查询地址信息</h2>
|
|
||||||
* */
|
|
||||||
@RequestMapping(
|
|
||||||
value = "/dev-protocol-springcloud-project-account-service/address/address-info-by-table-id",
|
|
||||||
method = RequestMethod.POST
|
|
||||||
)
|
|
||||||
CommonResponse<AddressInfo> getAddressInfoByTablesId(@RequestBody TableId tableId);
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package org.example.feign;
|
|
||||||
|
|
||||||
import feign.RequestInterceptor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
|
||||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.Enumeration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>Feign 调用时, 把 Header 也传递到服务提供方</h1>
|
|
||||||
* */
|
|
||||||
@Slf4j
|
|
||||||
@Configuration
|
|
||||||
public class FeignConfig {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>给 Feign 配置请求拦截器</h2>
|
|
||||||
* RequestInterceptor 是我们提供给 open-feign 的请求拦截器, 把 Header 信息传递
|
|
||||||
* */
|
|
||||||
@Bean
|
|
||||||
public RequestInterceptor headerInterceptor() {
|
|
||||||
|
|
||||||
return template -> {
|
|
||||||
// 获取请求的信息
|
|
||||||
ServletRequestAttributes attributes =
|
|
||||||
(ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
|
|
||||||
if (null != attributes) {
|
|
||||||
HttpServletRequest request = attributes.getRequest();
|
|
||||||
Enumeration<String> headerNames = request.getHeaderNames();
|
|
||||||
if (null != headerNames) {
|
|
||||||
while (headerNames.hasMoreElements()) {
|
|
||||||
String name = headerNames.nextElement();
|
|
||||||
String values = request.getHeader(name);
|
|
||||||
// 不能把当前请求的 content-length 传递到下游的服务提供方, 这明显是不对的
|
|
||||||
// 请求可能一直返回不了, 或者是请求响应数据被截断
|
|
||||||
if (!name.equalsIgnoreCase("content-length")) {
|
|
||||||
// 这里的 template 就是 RestTemplate
|
|
||||||
template.header(name, values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package org.example.feign;
|
|
||||||
|
|
||||||
import org.example.account.BalanceInfo;
|
|
||||||
import org.example.vo.CommonResponse;
|
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>用户账户服务 Feign 接口</h1>
|
|
||||||
* 无兜底策略
|
|
||||||
* */
|
|
||||||
@FeignClient(
|
|
||||||
contextId = "NotSecuredBalanceClient",
|
|
||||||
value = "dev-protocol-springcloud-project-account-service" // 调用账号微服务
|
|
||||||
)
|
|
||||||
public interface NotSecuredBalanceClient {
|
|
||||||
|
|
||||||
@RequestMapping(
|
|
||||||
value = "/dev-protocol-springcloud-project-account-service/balance/deduct-balance",
|
|
||||||
method = RequestMethod.PUT
|
|
||||||
)
|
|
||||||
CommonResponse<BalanceInfo> deductBalance(@RequestBody BalanceInfo balanceInfo);
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
package org.example.feign;
|
|
||||||
|
|
||||||
import org.example.common.TableId;
|
|
||||||
import org.example.goods.DeductGoodsInventory;
|
|
||||||
import org.example.goods.SimpleGoodsInfo;
|
|
||||||
import org.example.vo.CommonResponse;
|
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>不安全的商品服务 Feign 接口</h1>
|
|
||||||
* */
|
|
||||||
@FeignClient(
|
|
||||||
contextId = "NotSecuredGoodsClient",
|
|
||||||
value = "dev-protocol-springcloud-project-goods-service" // 调用商品微服务接口
|
|
||||||
)
|
|
||||||
public interface NotSecuredGoodsClient {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>根据 ids 查询简单的商品信息</h2>
|
|
||||||
* */
|
|
||||||
@RequestMapping(
|
|
||||||
value = "/dev-protocol-springcloud-project-goods-service/goods/deduct-goods-inventory",
|
|
||||||
method = RequestMethod.PUT
|
|
||||||
)
|
|
||||||
CommonResponse<Boolean> deductGoodsInventory(
|
|
||||||
@RequestBody List<DeductGoodsInventory> deductGoodsInventories);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>根据 ids 查询简单的商品信息</h2>
|
|
||||||
* */
|
|
||||||
@RequestMapping(
|
|
||||||
value = "/dev-protocol-springcloud-project-goods-service/goods/simple-goods-info",
|
|
||||||
method = RequestMethod.POST
|
|
||||||
)
|
|
||||||
CommonResponse<List<SimpleGoodsInfo>> getSimpleGoodsInfoByTableId(
|
|
||||||
@RequestBody TableId tableId);
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package org.example.feign;
|
|
||||||
|
|
||||||
import org.example.common.TableId;
|
|
||||||
import org.example.feign.hystrix.GoodsClientHystrix;
|
|
||||||
import org.example.goods.SimpleGoodsInfo;
|
|
||||||
import org.example.vo.CommonResponse;
|
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>商品服务 Feign 接口(安全的)</h1>
|
|
||||||
* */
|
|
||||||
@FeignClient(
|
|
||||||
contextId = "SecuredGoodsClient",
|
|
||||||
value = "dev-protocol-springcloud-project-goods-service",
|
|
||||||
fallback = GoodsClientHystrix.class
|
|
||||||
)
|
|
||||||
public interface SecuredGoodsClient {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>根据 ids 查询简单的商品信息</h2>
|
|
||||||
* */
|
|
||||||
@RequestMapping(
|
|
||||||
value = "/dev-protocol-springcloud-project-goods-service/goods/simple-goods-info",
|
|
||||||
method = RequestMethod.POST
|
|
||||||
)
|
|
||||||
CommonResponse<List<SimpleGoodsInfo>> getSimpleGoodsInfoByTableId(
|
|
||||||
@RequestBody TableId tableId);
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
package org.example.feign.hystrix;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.example.account.AddressInfo;
|
|
||||||
import org.example.common.TableId;
|
|
||||||
import org.example.feign.AddressClient;
|
|
||||||
import org.example.vo.CommonResponse;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>账户服务熔断降级兜底策略</h1>
|
|
||||||
* */
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
public class AddressClientHystrix implements AddressClient {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommonResponse<AddressInfo> getAddressInfoByTablesId(TableId tableId) {
|
|
||||||
|
|
||||||
log.error("[account client feign request error in order service] get address info" +
|
|
||||||
"error: [{}]", JSON.toJSONString(tableId));
|
|
||||||
return new CommonResponse<>(
|
|
||||||
-1,
|
|
||||||
"[account client feign request error in order service]",
|
|
||||||
new AddressInfo(-1L, Collections.emptyList()) // 返回用户Id为-1, 兜底返回一个空的List
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
package org.example.feign.hystrix;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.example.common.TableId;
|
|
||||||
import org.example.feign.SecuredGoodsClient;
|
|
||||||
import org.example.goods.SimpleGoodsInfo;
|
|
||||||
import org.example.vo.CommonResponse;
|
|
||||||
import org.springframework.stereotype.Component;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>商品服务熔断降级兜底</h1>
|
|
||||||
* */
|
|
||||||
@Slf4j
|
|
||||||
@Component
|
|
||||||
public class GoodsClientHystrix implements SecuredGoodsClient {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CommonResponse<List<SimpleGoodsInfo>> getSimpleGoodsInfoByTableId(TableId tableId) {
|
|
||||||
|
|
||||||
log.error("[goods client feign request error in order service] get simple goods" +
|
|
||||||
"error: [{}]", JSON.toJSONString(tableId));
|
|
||||||
return new CommonResponse<>(
|
|
||||||
-1,
|
|
||||||
"[goods client feign request error in order service]",
|
|
||||||
Collections.emptyList() // 兜底返回一个空的List
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
package org.example.service;
|
|
||||||
|
|
||||||
|
|
||||||
import org.example.common.TableId;
|
|
||||||
import org.example.order.OrderInfo;
|
|
||||||
import org.example.vo.PageSimpleOrderDetail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>订单相关服务接口定义</h1>
|
|
||||||
* */
|
|
||||||
public interface IOrderService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>下单(分布式事务): 创建订单 -> 扣减库存 -> 扣减余额 -> 创建物流信息(Stream + Kafka)</h2>
|
|
||||||
* */
|
|
||||||
TableId createOrder(OrderInfo orderInfo);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>获取当前用户的订单信息: 带有分页</h2>
|
|
||||||
* */
|
|
||||||
PageSimpleOrderDetail getSimpleOrderDetailByPage(int page);
|
|
||||||
}
|
|
@ -1,286 +0,0 @@
|
|||||||
package org.example.service.impl;
|
|
||||||
|
|
||||||
import com.alibaba.fastjson.JSON;
|
|
||||||
import io.seata.spring.annotation.GlobalTransactional;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.apache.commons.collections4.CollectionUtils;
|
|
||||||
import org.example.account.AddressInfo;
|
|
||||||
import org.example.account.BalanceInfo;
|
|
||||||
import org.example.common.TableId;
|
|
||||||
import org.example.dao.EcommerceOrderDao;
|
|
||||||
import org.example.entity.EcommerceOrder;
|
|
||||||
import org.example.feign.AddressClient;
|
|
||||||
import org.example.feign.NotSecuredBalanceClient;
|
|
||||||
import org.example.feign.NotSecuredGoodsClient;
|
|
||||||
import org.example.feign.SecuredGoodsClient;
|
|
||||||
import org.example.filter.AccessContext;
|
|
||||||
import org.example.goods.DeductGoodsInventory;
|
|
||||||
import org.example.goods.SimpleGoodsInfo;
|
|
||||||
import org.example.order.LogisticsMessage;
|
|
||||||
import org.example.order.OrderInfo;
|
|
||||||
import org.example.service.IOrderService;
|
|
||||||
import org.example.source.LogisticsSource;
|
|
||||||
import org.example.vo.PageSimpleOrderDetail;
|
|
||||||
import org.springframework.cloud.stream.annotation.EnableBinding;
|
|
||||||
import org.springframework.data.domain.Page;
|
|
||||||
import org.springframework.data.domain.PageRequest;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.data.domain.Sort;
|
|
||||||
import org.springframework.messaging.support.MessageBuilder;
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Function;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>订单相关服务接口实现</h1>
|
|
||||||
* */
|
|
||||||
@Slf4j
|
|
||||||
@Service
|
|
||||||
@EnableBinding(LogisticsSource.class)
|
|
||||||
public class OrderServiceImpl implements IOrderService {
|
|
||||||
|
|
||||||
/** 表的 dao 接口 */
|
|
||||||
private final EcommerceOrderDao orderDao;
|
|
||||||
|
|
||||||
/** Feign 客户端 */
|
|
||||||
private final AddressClient addressClient;
|
|
||||||
private final SecuredGoodsClient securedGoodsClient;
|
|
||||||
private final NotSecuredGoodsClient notSecuredGoodsClient;
|
|
||||||
private final NotSecuredBalanceClient notSecuredBalanceClient;
|
|
||||||
|
|
||||||
/** SpringCloud Stream 的发射器 */
|
|
||||||
private final LogisticsSource logisticsSource;
|
|
||||||
|
|
||||||
public OrderServiceImpl(EcommerceOrderDao orderDao,
|
|
||||||
// fixme 这里如果报红的话, 可以不用理会, 去 Idea 工具中进行配置忽略 设置-> 编辑器(Editor) -> 检查(Inspections) ->
|
|
||||||
// Spring | Spring Core | 代码 | Spring Bean 组件中不正确的自动装配(Autowiring for bean class)
|
|
||||||
AddressClient addressClient,
|
|
||||||
SecuredGoodsClient securedGoodsClient,
|
|
||||||
NotSecuredGoodsClient notSecuredGoodsClient,
|
|
||||||
NotSecuredBalanceClient notSecuredBalanceClient,
|
|
||||||
LogisticsSource logisticsSource) {
|
|
||||||
this.orderDao = orderDao;
|
|
||||||
this.addressClient = addressClient;
|
|
||||||
this.securedGoodsClient = securedGoodsClient;
|
|
||||||
this.notSecuredGoodsClient = notSecuredGoodsClient;
|
|
||||||
this.notSecuredBalanceClient = notSecuredBalanceClient;
|
|
||||||
this.logisticsSource = logisticsSource;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>创建订单: 这里会涉及到分布式事务</h2>
|
|
||||||
* 创建订单会涉及到多个步骤和校验, 当不满足情况时直接抛出异常;
|
|
||||||
* 1. 校验请求对象是否合法
|
|
||||||
* 2. 创建订单 - 本地事务
|
|
||||||
* 3. 扣减商品库存 - 本地事务
|
|
||||||
* 4. 扣减用户余额 - 本地事务
|
|
||||||
* 5. 发送订单物流消息 SpringCloud Stream + Kafka
|
|
||||||
* */
|
|
||||||
@Override
|
|
||||||
@GlobalTransactional(rollbackFor = Exception.class) // 分布式事务注解
|
|
||||||
public TableId createOrder(OrderInfo orderInfo) {
|
|
||||||
|
|
||||||
// 获取地址信息
|
|
||||||
AddressInfo addressInfo = addressClient.getAddressInfoByTablesId(
|
|
||||||
new TableId(Collections.singletonList(
|
|
||||||
new TableId.Id(orderInfo.getUserAddress())))).getData();
|
|
||||||
|
|
||||||
// 1. 校验请求对象是否合法(商品信息不需要校验, 扣减库存会做校验)
|
|
||||||
if (CollectionUtils.isEmpty(addressInfo.getAddressItems())) {
|
|
||||||
throw new RuntimeException("user address is not exist: "
|
|
||||||
+ orderInfo.getUserAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 创建订单
|
|
||||||
EcommerceOrder newOrder = orderDao.save(
|
|
||||||
new EcommerceOrder(
|
|
||||||
AccessContext.getLoginUserInfo().getId(),
|
|
||||||
orderInfo.getUserAddress(),
|
|
||||||
JSON.toJSONString(orderInfo.getOrderItems())
|
|
||||||
)
|
|
||||||
);
|
|
||||||
log.info("create order success: [{}], [{}]",
|
|
||||||
AccessContext.getLoginUserInfo().getId(), newOrder.getId());
|
|
||||||
|
|
||||||
// 3. 扣减商品库存
|
|
||||||
if (
|
|
||||||
!notSecuredGoodsClient.deductGoodsInventory(
|
|
||||||
orderInfo.getOrderItems()
|
|
||||||
.stream()
|
|
||||||
.map(OrderInfo.OrderItem::toDeductGoodsInventory)
|
|
||||||
.collect(Collectors.toList())
|
|
||||||
).getData()
|
|
||||||
) {
|
|
||||||
throw new RuntimeException("deduct goods inventory failure");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 扣减用户账户余额
|
|
||||||
// 4.1 获取商品信息, 计算总价格
|
|
||||||
List<SimpleGoodsInfo> goodsInfos = notSecuredGoodsClient.getSimpleGoodsInfoByTableId(
|
|
||||||
new TableId(
|
|
||||||
orderInfo.getOrderItems()
|
|
||||||
.stream()
|
|
||||||
.map(o -> new TableId.Id(o.getGoodsId()))
|
|
||||||
.collect(Collectors.toList())
|
|
||||||
)
|
|
||||||
).getData();
|
|
||||||
|
|
||||||
Map<Long, SimpleGoodsInfo> goodsId2GoodsInfo = goodsInfos.stream()
|
|
||||||
.collect(Collectors.toMap(SimpleGoodsInfo::getId, Function.identity()));
|
|
||||||
long balance = 0;
|
|
||||||
for (OrderInfo.OrderItem orderItem : orderInfo.getOrderItems()) {
|
|
||||||
balance += goodsId2GoodsInfo.get(orderItem.getGoodsId()).getPrice()
|
|
||||||
* orderItem.getCount();
|
|
||||||
}
|
|
||||||
assert balance > 0;
|
|
||||||
|
|
||||||
// 4.2 填写总价格, 扣减账户余额
|
|
||||||
BalanceInfo balanceInfo = notSecuredBalanceClient.deductBalance(
|
|
||||||
new BalanceInfo(AccessContext.getLoginUserInfo().getId(), balance)
|
|
||||||
).getData();
|
|
||||||
if (null == balanceInfo) {
|
|
||||||
throw new RuntimeException("deduct user balance failure");
|
|
||||||
}
|
|
||||||
log.info("deduct user balance: [{}], [{}]", newOrder.getId(),
|
|
||||||
JSON.toJSONString(balanceInfo));
|
|
||||||
|
|
||||||
// 5. 发送订单物流消息 SpringCloud Stream + Kafka
|
|
||||||
LogisticsMessage logisticsMessage = new LogisticsMessage(
|
|
||||||
AccessContext.getLoginUserInfo().getId(),
|
|
||||||
newOrder.getId(),
|
|
||||||
orderInfo.getUserAddress(),
|
|
||||||
null // 没有备注信息
|
|
||||||
);
|
|
||||||
if (!logisticsSource.logisticsOutput().send(
|
|
||||||
MessageBuilder.withPayload(JSON.toJSONString(logisticsMessage)).build()
|
|
||||||
)) {
|
|
||||||
throw new RuntimeException("send logistics message failure");
|
|
||||||
}
|
|
||||||
log.info("send create order message to kafka with stream: [{}]",
|
|
||||||
JSON.toJSONString(logisticsMessage));
|
|
||||||
|
|
||||||
// 返回订单 id
|
|
||||||
return new TableId(Collections.singletonList(new TableId.Id(newOrder.getId())));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public PageSimpleOrderDetail getSimpleOrderDetailByPage(int page) {
|
|
||||||
|
|
||||||
if (page <= 0) {
|
|
||||||
page = 1; // 默认是第一页
|
|
||||||
}
|
|
||||||
|
|
||||||
// 这里分页的规则是: 1页10条数据, 按照 id 倒序排列
|
|
||||||
Pageable pageable = PageRequest.of(page - 1, 10,
|
|
||||||
Sort.by("id").descending());
|
|
||||||
Page<EcommerceOrder> orderPage = orderDao.findAllByUserId(
|
|
||||||
AccessContext.getLoginUserInfo().getId(), pageable
|
|
||||||
);
|
|
||||||
List<EcommerceOrder> orders = orderPage.getContent();
|
|
||||||
|
|
||||||
// 如果是空, 直接返回空数组
|
|
||||||
if (CollectionUtils.isEmpty(orders)) {
|
|
||||||
return new PageSimpleOrderDetail(Collections.emptyList(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取当前订单中所有的 goodsId, 这个 set 不可能为空或者是 null, 否则, 代码一定有 bug
|
|
||||||
Set<Long> goodsIdsInOrders = new HashSet<>();
|
|
||||||
orders.forEach(o -> {
|
|
||||||
List<DeductGoodsInventory> goodsAndCount = JSON.parseArray(
|
|
||||||
o.getOrderDetail(), DeductGoodsInventory.class
|
|
||||||
);
|
|
||||||
goodsIdsInOrders.addAll(goodsAndCount.stream()
|
|
||||||
.map(DeductGoodsInventory::getGoodsId)
|
|
||||||
.collect(Collectors.toSet()));
|
|
||||||
});
|
|
||||||
|
|
||||||
assert CollectionUtils.isNotEmpty(goodsIdsInOrders);
|
|
||||||
|
|
||||||
// 是否还有更多页: 总页数是否大于当前给定的页
|
|
||||||
boolean hasMore = orderPage.getTotalPages() > page;
|
|
||||||
|
|
||||||
// 获取商品信息
|
|
||||||
List<SimpleGoodsInfo> goodsInfos = securedGoodsClient.getSimpleGoodsInfoByTableId(
|
|
||||||
new TableId(goodsIdsInOrders.stream()
|
|
||||||
.map(TableId.Id::new).collect(Collectors.toList()))
|
|
||||||
).getData();
|
|
||||||
|
|
||||||
// 获取地址信息
|
|
||||||
AddressInfo addressInfo = addressClient.getAddressInfoByTablesId(
|
|
||||||
new TableId(orders.stream()
|
|
||||||
.map(o -> new TableId.Id(o.getAddressId()))
|
|
||||||
.distinct().collect(Collectors.toList()))
|
|
||||||
).getData();
|
|
||||||
|
|
||||||
// 组装订单中的商品, 地址信息 -> 订单信息
|
|
||||||
return new PageSimpleOrderDetail(
|
|
||||||
assembleSimpleOrderDetail(orders, goodsInfos, addressInfo),
|
|
||||||
hasMore
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>组装订单详情</h2>
|
|
||||||
* */
|
|
||||||
private List<PageSimpleOrderDetail.SingleOrderItem> assembleSimpleOrderDetail(
|
|
||||||
List<EcommerceOrder> orders, List<SimpleGoodsInfo> goodsInfos,
|
|
||||||
AddressInfo addressInfo
|
|
||||||
) {
|
|
||||||
// goodsId -> SimpleGoodsInfo
|
|
||||||
Map<Long, SimpleGoodsInfo> id2GoodsInfo = goodsInfos.stream()
|
|
||||||
.collect(Collectors.toMap(SimpleGoodsInfo::getId, Function.identity()));
|
|
||||||
// addressId -> AddressInfo.AddressItem
|
|
||||||
Map<Long, AddressInfo.AddressItem> id2AddressItem = addressInfo.getAddressItems()
|
|
||||||
.stream().collect(
|
|
||||||
Collectors.toMap(AddressInfo.AddressItem::getId, Function.identity())
|
|
||||||
);
|
|
||||||
|
|
||||||
List<PageSimpleOrderDetail.SingleOrderItem> result = new ArrayList<>(orders.size());
|
|
||||||
orders.forEach(o -> {
|
|
||||||
|
|
||||||
PageSimpleOrderDetail.SingleOrderItem orderItem =
|
|
||||||
new PageSimpleOrderDetail.SingleOrderItem();
|
|
||||||
orderItem.setId(o.getId());
|
|
||||||
orderItem.setUserAddress(id2AddressItem.getOrDefault(o.getAddressId(),
|
|
||||||
new AddressInfo.AddressItem(-1L)).toUserAddress());
|
|
||||||
orderItem.setGoodsItems(buildOrderGoodsItem(o, id2GoodsInfo));
|
|
||||||
|
|
||||||
result.add(orderItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>构造订单中的商品信息</h2>
|
|
||||||
* */
|
|
||||||
private List<PageSimpleOrderDetail.SingleOrderGoodsItem> buildOrderGoodsItem(
|
|
||||||
EcommerceOrder order, Map<Long, SimpleGoodsInfo> id2GoodsInfo
|
|
||||||
) {
|
|
||||||
|
|
||||||
List<PageSimpleOrderDetail.SingleOrderGoodsItem> goodsItems = new ArrayList<>();
|
|
||||||
List<DeductGoodsInventory> goodsAndCount = JSON.parseArray(
|
|
||||||
order.getOrderDetail(), DeductGoodsInventory.class
|
|
||||||
);
|
|
||||||
|
|
||||||
goodsAndCount.forEach(gc -> {
|
|
||||||
|
|
||||||
PageSimpleOrderDetail.SingleOrderGoodsItem goodsItem =
|
|
||||||
new PageSimpleOrderDetail.SingleOrderGoodsItem();
|
|
||||||
goodsItem.setCount(gc.getCount());
|
|
||||||
goodsItem.setSimpleGoodsInfo(id2GoodsInfo.getOrDefault(gc.getGoodsId(),
|
|
||||||
new SimpleGoodsInfo(-1L)));
|
|
||||||
|
|
||||||
goodsItems.add(goodsItem);
|
|
||||||
});
|
|
||||||
|
|
||||||
return goodsItems;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
package org.example.source;
|
|
||||||
|
|
||||||
import org.springframework.cloud.stream.annotation.Output;
|
|
||||||
import org.springframework.messaging.MessageChannel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>自定义物流消息通信信道(Source)</h1>
|
|
||||||
* */
|
|
||||||
public interface LogisticsSource {
|
|
||||||
|
|
||||||
/** 输出信道名称 */
|
|
||||||
String OUTPUT = "logisticsOutput";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>物流 Source -> logisticsOutput</h2>
|
|
||||||
* 通信信道的名称是 logisticsOutput, 对应到 yml 文件里的配置
|
|
||||||
* */
|
|
||||||
@Output(LogisticsSource.OUTPUT)
|
|
||||||
MessageChannel logisticsOutput();
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
package org.example.vo;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import org.example.account.UserAddress;
|
|
||||||
import org.example.goods.SimpleGoodsInfo;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h1>订单详情</h1> - 分页, 仅用于订单服务
|
|
||||||
* */
|
|
||||||
@ApiModel(description = "分页订单详情对象")
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class PageSimpleOrderDetail {
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "订单详情")
|
|
||||||
private List<SingleOrderItem> orderItems;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "是否有更多的订单(分页)")
|
|
||||||
private Boolean hasMore;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>单个订单信息</h2>
|
|
||||||
* */
|
|
||||||
@ApiModel(description = "单个订单信息对象")
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public static class SingleOrderItem {
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "订单表主键 id")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "用户地址信息")
|
|
||||||
private UserAddress userAddress;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "订单商品信息")
|
|
||||||
private List<SingleOrderGoodsItem> goodsItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiModel(description = "单个订单中的单项商品信息")
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public static class SingleOrderGoodsItem {
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "简单商品信息")
|
|
||||||
private SimpleGoodsInfo simpleGoodsInfo;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "商品个数")
|
|
||||||
private Integer count;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
package org.example.conf;
|
|
||||||
|
|
||||||
//import com.alibaba.druid.support.http.StatViewServlet;
|
|
||||||
//import com.alibaba.druid.support.http.WebStatFilter;
|
|
||||||
import com.alibaba.druid.pool.DruidDataSource;
|
|
||||||
import com.alibaba.fastjson2.JSON;
|
|
||||||
//import com.zaxxer.hikari.HikariDataSource;
|
|
||||||
import io.seata.rm.datasource.DataSourceProxy;
|
|
||||||
import com.zaxxer.hikari.HikariDataSource;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
|
||||||
import org.springframework.boot.web.servlet.ServletRegistrationBean;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.annotation.Primary;
|
|
||||||
|
|
||||||
import javax.annotation.Resource;
|
|
||||||
import javax.sql.DataSource;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author q
|
|
||||||
* @createTime 2022/12/28/ 16:13:00
|
|
||||||
* @Description Seata 所需要的数据源代理配置类
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@Configuration
|
|
||||||
public class DataSourceProxyAutoConfiguration {
|
|
||||||
|
|
||||||
@Resource
|
|
||||||
private DataSourceProperties dataSourceProperties;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 配置数据源代理, 用于 Seata 全局事务回滚
|
|
||||||
* before image + after image -> undo_log
|
|
||||||
*/
|
|
||||||
@Primary
|
|
||||||
@Bean("dataSource")
|
|
||||||
public DataSource dataSource() {
|
|
||||||
|
|
||||||
HikariDataSource dataSource = new HikariDataSource();
|
|
||||||
log.info("dataSource properties:[{}]", JSON.toJSONString(
|
|
||||||
dataSourceProperties
|
|
||||||
));
|
|
||||||
dataSource.setJdbcUrl(dataSourceProperties.getUrl());
|
|
||||||
dataSource.setUsername(dataSourceProperties.getUsername());
|
|
||||||
dataSource.setPassword(dataSourceProperties.getPassword());
|
|
||||||
dataSource.setDriverClassName(dataSourceProperties.getDriverClassName());
|
|
||||||
return new DataSourceProxy(dataSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// @ConfigurationProperties(prefix = "spring.datasource")
|
|
||||||
// @Bean
|
|
||||||
// public DataSource dataSource(){
|
|
||||||
// return new DruidDataSource();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Bean
|
|
||||||
// @ConfigurationProperties(prefix = "spring.datasource")
|
|
||||||
// public DataSource druidDataSource(){
|
|
||||||
// return new DruidDataSource();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Bean
|
|
||||||
// public DataSourceProxy dataSourceProxy(DataSource dataSource) {
|
|
||||||
// return new DataSourceProxy(dataSource);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Description: 后台监控
|
|
||||||
* @Author: J.Flying
|
|
||||||
* @Date: 2020/10/20
|
|
||||||
*/
|
|
||||||
// @Bean
|
|
||||||
// public ServletRegistrationBean registrationBean(){
|
|
||||||
// ServletRegistrationBean<StatViewServlet> bean = new ServletRegistrationBean<>(new StatViewServlet(),"/druid/*");
|
|
||||||
// Map<String, String> initParameters=new HashMap<>();
|
|
||||||
// // 登录名
|
|
||||||
// initParameters.put("loginUsername","admin");
|
|
||||||
// initParameters.put("loginPassword","1234");
|
|
||||||
//
|
|
||||||
// //准许访问
|
|
||||||
// initParameters.put("allow","");
|
|
||||||
//
|
|
||||||
// bean.setInitParameters(initParameters);
|
|
||||||
// return bean;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Bean
|
|
||||||
// public FilterRegistrationBean druidStatFilter() {
|
|
||||||
// FilterRegistrationBean filterRegistrationBean =
|
|
||||||
// new FilterRegistrationBean(new WebStatFilter());
|
|
||||||
// //添加过滤规则
|
|
||||||
// filterRegistrationBean.addUrlPatterns("/*");
|
|
||||||
// //添加需要忽略的格式信息
|
|
||||||
// filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif," +
|
|
||||||
// "*.jpg,*.png, *.css,*.ico,/druid/*");
|
|
||||||
// return filterRegistrationBean;
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package org.example.order;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author q
|
|
||||||
* @createTime 2022/12/29/ 17:23:00
|
|
||||||
* @Description 创建订单时发送的物流消息
|
|
||||||
*/
|
|
||||||
@ApiModel(description = "Stream 物流消息对象")
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class LogisticsMessage {
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "用户表主键 id")
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "订单表主键 id")
|
|
||||||
private Long orderId;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "用户地址表主键 id")
|
|
||||||
private Long addressId;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "备注信息(json 存储)")
|
|
||||||
private String extraInfo;
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
package org.example.order;
|
|
||||||
|
|
||||||
import io.swagger.annotations.ApiModel;
|
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
import lombok.Setter;
|
|
||||||
import org.example.goods.DeductGoodsInventory;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author danny
|
|
||||||
* @createTime 2022/12/29/ 17:21:00
|
|
||||||
* @Description 订单信息
|
|
||||||
*/
|
|
||||||
@ApiModel(description = "用户发起购买订单")
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class OrderInfo {
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "用户地址表主键 id")
|
|
||||||
private Long userAddress;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "订单中的商品信息")
|
|
||||||
private List<OrderItem> orderItems;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 订单中的商品信息
|
|
||||||
*/
|
|
||||||
@ApiModel(description = "订单中的单项商品信息")
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public static class OrderItem {
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "商品表主键 id")
|
|
||||||
private Long goodsId;
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "购买商品个数")
|
|
||||||
private Integer count;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 扣减库存
|
|
||||||
*/
|
|
||||||
public DeductGoodsInventory toDeductGoodsInventory() {
|
|
||||||
return new DeductGoodsInventory(this.goodsId, this.count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue