dd3cd24b70
# Conflicts: # deploy/dev/docker-compose.yml # deploy/dev/envoy.yaml # desc/api/dispute.api # desc/api/review.api # desc/api/search.api
138 lines
3.2 KiB
Go
138 lines
3.2 KiB
Go
package order
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"time"
|
|
|
|
"juwan-backend/app/order/api/internal/svc"
|
|
"juwan-backend/app/order/api/internal/types"
|
|
"juwan-backend/app/order/rpc/orderservice"
|
|
"juwan-backend/common/utils/contextj"
|
|
)
|
|
|
|
func int64Ptr(v int64) *int64 {
|
|
return &v
|
|
}
|
|
|
|
func stringPtr(v string) *string {
|
|
return &v
|
|
}
|
|
|
|
func formatUnix(ts int64) string {
|
|
if ts <= 0 {
|
|
return ""
|
|
}
|
|
return time.Unix(ts, 0).UTC().Format(time.RFC3339)
|
|
}
|
|
|
|
func toAPIOrder(in *orderservice.Orders) types.Order {
|
|
order := types.Order{}
|
|
if in == nil {
|
|
return order
|
|
}
|
|
|
|
totalPrice, _ := strconv.ParseFloat(in.GetTotalPrice(), 64)
|
|
playerID := strconv.FormatInt(in.GetPlayerId(), 10)
|
|
service := types.PlayerService{}
|
|
_ = json.Unmarshal([]byte(in.GetServiceSnapshot()), &service)
|
|
|
|
order = types.Order{
|
|
Id: in.GetId(),
|
|
ConsumerId: in.GetConsumerId(),
|
|
PlayerId: playerID,
|
|
ShopId: in.GetShopId(),
|
|
Service: service,
|
|
Status: in.GetStatus(),
|
|
TotalPrice: totalPrice,
|
|
Note: in.GetNote(),
|
|
CreatedAt: formatUnix(in.GetCreatedAt()),
|
|
AcceptedAt: formatUnix(in.GetAcceptedAt()),
|
|
CompletedAt: formatUnix(in.GetCompletedAt()),
|
|
}
|
|
|
|
return order
|
|
}
|
|
|
|
// validTransitions defines the allowed order state transitions per the API spec.
|
|
var validTransitions = map[string][]string{
|
|
"pending_payment": {"pending_accept"},
|
|
"pending_accept": {"in_progress", "cancelled"},
|
|
"in_progress": {"pending_close", "disputed"},
|
|
"pending_close": {"pending_review", "disputed"},
|
|
"pending_review": {"completed"},
|
|
"disputed": {"pending_review"},
|
|
}
|
|
|
|
func transitionOrderStatus(ctx context.Context, svcCtx *svc.ServiceContext, orderID int64, toStatus string, setAcceptedAt bool, setClosedAt bool, setCompletedAt bool, setCancelledAt bool) error {
|
|
current, err := svcCtx.OrderRpc.GetOrdersById(ctx, &orderservice.GetOrdersByIdReq{Id: orderID})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if current.GetOrders() == nil {
|
|
return errors.New("order not found")
|
|
}
|
|
|
|
fromStatus := current.Orders.GetStatus()
|
|
allowed, ok := validTransitions[fromStatus]
|
|
if !ok {
|
|
return fmt.Errorf("order status %q does not allow any transition", fromStatus)
|
|
}
|
|
found := false
|
|
for _, s := range allowed {
|
|
if s == toStatus {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
return fmt.Errorf("transition from %q to %q is not allowed", fromStatus, toStatus)
|
|
}
|
|
|
|
now := time.Now().Unix()
|
|
updateReq := &orderservice.UpdateOrdersReq{
|
|
Id: orderID,
|
|
Status: stringPtr(toStatus),
|
|
UpdatedAt: int64Ptr(now),
|
|
}
|
|
if setAcceptedAt {
|
|
updateReq.AcceptedAt = int64Ptr(now)
|
|
}
|
|
if setClosedAt {
|
|
updateReq.ClosedAt = int64Ptr(now)
|
|
}
|
|
if setCompletedAt {
|
|
updateReq.CompletedAt = int64Ptr(now)
|
|
}
|
|
if setCancelledAt {
|
|
updateReq.CancelledAt = int64Ptr(now)
|
|
}
|
|
|
|
if _, err = svcCtx.OrderRpc.UpdateOrders(ctx, updateReq); err != nil {
|
|
return err
|
|
}
|
|
|
|
actorID, _ := contextj.UserIDFrom(ctx)
|
|
actorRole := "system"
|
|
if actorID > 0 {
|
|
actorRole = "user"
|
|
}
|
|
_, err = svcCtx.OrderRpc.AddOrderStateLogs(ctx, &orderservice.AddOrderStateLogsReq{
|
|
OrderId: orderID,
|
|
FromStatus: &fromStatus,
|
|
ToStatus: toStatus,
|
|
Action: "status_transition",
|
|
ActorId: actorID,
|
|
ActorRole: actorRole,
|
|
CreatedAt: &now,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|