这是indexloc提供的服务,不要输入任何密码
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions saas-modules/temporal/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,43 @@
<version>${temporal.version}</version>
</dependency>

<dependency>
<groupId>io.temporal</groupId>
<artifactId>temporal-spring-boot-starter</artifactId>
<version>${temporal.version}</version>
</dependency>

<dependency>
<groupId>io.temporal</groupId>
<artifactId>temporal-testing</artifactId>
<version>${temporal.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>


</dependencies>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.baeldung.temporal.workflows.sboot.order;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OrderApplication {

public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.baeldung.temporal.workflows.sboot.order.activities;

import com.baeldung.temporal.workflows.sboot.order.domain.*;
import io.temporal.activity.ActivityInterface;

@ActivityInterface
public interface OrderActivities {

void reserveOrderItems(Order order);
void cancelReservedItems(Order order);
void returnOrderItems(Order order);
void dispatchOrderItems(Order order);

PaymentAuthorization createPaymentRequest(Order order, BillingInfo billingInfo);
RefundRequest createRefundRequest(PaymentAuthorization payment);

Shipping createShipping(Order order);
Shipping updateShipping(Shipping shipping, ShippingStatus status);


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package com.baeldung.temporal.workflows.sboot.order.activities;

import com.baeldung.temporal.workflows.sboot.order.domain.*;
import com.baeldung.temporal.workflows.sboot.order.services.InventoryService;
import io.temporal.spring.boot.ActivityImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.time.Clock;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

@Service
@ActivityImpl(taskQueues = "ORDERS")
public class OrderActivitiesImpl implements OrderActivities{
private static final Logger log = LoggerFactory.getLogger(OrderActivitiesImpl.class);

private final Clock clock;
private final InventoryService inventoryService;

public OrderActivitiesImpl(Clock clock, InventoryService inventoryService) {
this.clock = clock;
this.inventoryService = inventoryService;
}

@Override
public void reserveOrderItems(Order order) {
log.info("[isVirtual={}] reserveOrderItems: order={}", Thread.currentThread().isVirtual(),order);
for (OrderItem item : order.items()) {
inventoryService.reserveInventory(item.sku(), item.quantity());
}
}

@Override
public void cancelReservedItems(Order order) {
log.info("[isVirtual={}]cancelReservedItems: order={}", Thread.currentThread().isVirtual(),order);
for (OrderItem item : order.items()) {
inventoryService.cancelInventoryReservation(item.sku(), item.quantity());
}
}

@Override
public void returnOrderItems(Order order) {
log.info("returnOrderItems: order={}", order);
for (OrderItem item : order.items()) {
inventoryService.addInventory(item.sku(), item.quantity());
}
}

@Override
public void dispatchOrderItems(Order order) {
log.info("deliverOrderItems: order={}", order);
for(OrderItem item : order.items()) {
inventoryService.addInventory(item.sku(), -item.quantity());
}
}

@Override
public PaymentAuthorization createPaymentRequest(Order order, BillingInfo billingInfo) {
log.info("createPaymentRequest: order={}, billingInfo={}", order, billingInfo);
return new PaymentAuthorization(
billingInfo,
PaymentStatus.PENDING,
order.orderId().toString(),
UUID.randomUUID().toString(),
null,
null);
}

@Override
public RefundRequest createRefundRequest(PaymentAuthorization payment) {
log.info("createRefundRequest: payment={}", payment);
return new RefundRequest(payment);
}

@Override
public Shipping createShipping(Order order) {
var provider = selectProvider(order);
return new Shipping(
order,
provider,
ShippingStatus.CREATED,
List.of(new ShippingEvent(
clock.instant(),
ShippingStatus.CREATED,
"Shipping created")));
}

private ShippingProvider selectProvider(Order order) {

int totalItems = order.items().stream()
.map(OrderItem::quantity)
.reduce(0, Integer::sum);

if ( totalItems < 5) {
return ShippingProvider.DHL;
}
else if ( totalItems < 10) {
return ShippingProvider.FedEx;
}
else {
return ShippingProvider.UPS;
}

}

@Override
public Shipping updateShipping(Shipping shipping, ShippingStatus status) {
return shipping.toStatus(status, clock.instant(), "Shipping status update");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.baeldung.temporal.workflows.sboot.order.activities;

public interface OrderActivitiesStub extends OrderActivities {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.baeldung.temporal.workflows.sboot.order.adapter.rest;

import com.baeldung.temporal.workflows.sboot.order.domain.*;
import com.baeldung.temporal.workflows.sboot.order.services.OrderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;

import java.time.Instant;

@RestController
@RequestMapping("/order")
public class OrderApi {

private static final Logger log = LoggerFactory.getLogger(OrderApi.class);

private final OrderService orderService;

public OrderApi(OrderService orderService) {
this.orderService = orderService;
}


@PostMapping
public ResponseEntity<OrderCreationResponse> createOrder(@RequestBody OrderSpec orderSpec) {
var execution = orderService.createOrderWorkflow(orderSpec);
var location = UriComponentsBuilder.fromUriString("/order/{orderExecutionId}").build(execution);

return ResponseEntity.created(location).body(new OrderCreationResponse(execution));

}

@GetMapping("/{orderExecutionId}")
public ResponseEntity<Order> getOrder(@PathVariable("orderExecutionId") String orderExecutionId) {
var wf = orderService.getWorkflow(orderExecutionId);
return ResponseEntity.ok(wf.getOrder());
}

@GetMapping("/{orderExecutionId}/payment")
public ResponseEntity<PaymentAuthorization> getPayment(@PathVariable("orderExecutionId") String orderExecutionId) {
var wf = orderService.getWorkflow(orderExecutionId);
var payment = wf.getPayment();
if (payment == null) {
return ResponseEntity.noContent().build();
}
else {
return ResponseEntity.ok(wf.getPayment());
}
}


@GetMapping("/{orderExecutionId}/shipping")
public ResponseEntity<Shipping> getOrderShipping(@PathVariable("orderExecutionId") String orderExecutionId) {
var wf = orderService.getWorkflow(orderExecutionId);
return ResponseEntity.ok(wf.getShipping());
}

@PutMapping("/{orderExecutionId}/paymentStatus")
public ResponseEntity<Void> updatePaymentStatus(@PathVariable("orderExecutionId") String orderExecutionId, @RequestBody PaymentStatusUpdateInfo info) {
var wf = orderService.getWorkflow(orderExecutionId);
log.info("updatePaymentStatus: info={}", info.status());
switch (info.status()) {
case APPROVED -> wf.paymentAuthorized(info.transactionId(), info.authorizationId());
case DECLINED -> wf.paymentDeclined(info.transactionId(), info.cause());
default -> throw new IllegalArgumentException("Unsupported payment status: " + info.status());
};

return ResponseEntity.accepted().build();
}

@PutMapping("/{orderExecutionId}/shippingStatus")
public ResponseEntity<Void> updateShippingStatus(@PathVariable("orderExecutionId") String orderExecutionId, @RequestBody ShippingStatusUpdateInfo info) {
var wf = orderService.getWorkflow(orderExecutionId);
log.info("updateShippingStatus: info={}", info.status());
switch (info.status()) {
case RETURNED -> wf.packageReturned(info.statusTime());
case SHIPPED -> wf.packagePickup(info.statusTime());
case DELIVERED -> wf.packageDelivered(info.statusTime());
default-> log.info("shipping status update: new status={}", info.status());
}
return ResponseEntity.accepted().build();
}

public record OrderCreationResponse(
String orderExecutionId
) {};

public record PaymentStatusUpdateInfo(
PaymentStatus status,
String authorizationId,
String transactionId,
String cause
){};

public record ShippingStatusUpdateInfo(
ShippingStatus status,
Instant statusTime
) {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.baeldung.temporal.workflows.sboot.order.config;

import io.temporal.spring.boot.autoconfigure.ServiceStubsAutoConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Clock;

@Configuration
@AutoConfigureBefore(ServiceStubsAutoConfiguration.class)
public class OrderWorkflowConfiguration {

private static Logger log = LoggerFactory.getLogger(OrderWorkflowConfiguration.class);

@Bean
@ConditionalOnMissingBean(Clock.class)
Clock standardClock() {
return Clock.systemDefaultZone();
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.baeldung.temporal.workflows.sboot.order.domain;

import java.math.BigDecimal;

public record BillingInfo(
String cardToken,
BigDecimal amount,
String currency
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.baeldung.temporal.workflows.sboot.order.domain;

public record Cart(

) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.baeldung.temporal.workflows.sboot.order.domain;

import java.time.LocalDate;
import java.util.UUID;

public record Customer(
UUID uuid,
String name,
LocalDate birthDate
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.baeldung.temporal.workflows.sboot.order.domain;

import java.util.List;
import java.util.UUID;

public record Order(UUID orderId, List<OrderItem> items) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.baeldung.temporal.workflows.sboot.order.domain;

public record OrderItem(
String sku,
int quantity
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.baeldung.temporal.workflows.sboot.order.domain;

public record OrderSpec(
Order order,
BillingInfo billingInfo,
ShippingInfo shippingInfo,
Customer customer) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.baeldung.temporal.workflows.sboot.order.domain;

public record PaymentAuthorization(
BillingInfo info,
PaymentStatus status,
String orderId,
String transactionId,
String authorizationId,
String cause) {
}
Loading