Files
juwan-backend/app/order/api/internal/logic/order/helpers.go
T
zetaloop dd3cd24b70 Merge branch 'main-merge-base-a'
# Conflicts:
#	deploy/dev/docker-compose.yml
#	deploy/dev/envoy.yaml
#	desc/api/dispute.api
#	desc/api/review.api
#	desc/api/search.api
2026-04-25 05:42:42 +08:00

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
}