package repositories

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"fusenapi/constants"
	"fusenapi/model/gmodel"
	"fusenapi/utils/basic"
	"fusenapi/utils/handlers"
	"fusenapi/utils/order"
	"fusenapi/utils/pay"
	"fusenapi/utils/queue"
	"math"
	"time"

	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/stripe/stripe-go/v75"
	"github.com/zeromicro/go-zero/core/logc"
	"gorm.io/gorm"
)

func NewOrder(gormDB *gorm.DB, awsSession *session.Session, delayQueue *queue.DelayMessage) Order {
	return &defaultOrder{
		MysqlConn:  gormDB,
		DelayQueue: delayQueue,
	}
}

type (
	defaultOrder struct {
		MysqlConn  *gorm.DB
		DelayQueue *queue.DelayMessage
	}
	Order interface {
		// 下单
		Create(ctx context.Context, in *CreateReq) (res *CreateRes, err error)
		// 预支付--定金
		CreatePrePaymentByDeposit(ctx context.Context, in *CreatePrePaymentByDepositReq) (res *CreatePrePaymentByDepositRes, err error)
		// 预支付--定金
		CreatePrePaymentByBalance(ctx context.Context, in *CreatePrePaymentByBalanceReq) (res *CreatePrePaymentByBalanceRes, err error)
		// 列表
		List(ctx context.Context, in *ListReq) (res *ListRes, err error)
		// 详情
		Detail(ctx context.Context, in *DetailReq) (res *DetailRes, err error)
		// 支付成功
		PaymentSuccessful(ctx context.Context, in *PaymentSuccessfulReq) (res *PaymentSuccessfulRes, err error)
		// 关闭
		Close(ctx context.Context, in *CloseReq) (res *CloseRes, err error)
		// 删除
		Delete(ctx context.Context, in *DeleteReq) (res *DeleteRes, err error)

		// 支付超时订单自动关闭
		CloseList(ctx context.Context, in *CloseListReq) (res *CloseListRes, err error)
	}

	PayInfo struct {
		PayMethod     string             `json:"pay_method"`     // 支付方式
		PaymentMethod string             `json:"payment_method"` // 交易方式
		PayTime       time.Time          `json:"pay_time"`       // 支付时间
		Status        gmodel.PayStatus   `json:"status"`         // 当前状态
		StatusLink    []gmodel.PayStatus `json:"status_link"`    // 状态链路
		TradeNo       string             `json:"trade_no"`       // 支付交易号
	}

	OrderAddress struct {
		Address string `json:"address"` // 详细地址
		Mobile  string `json:"mobile"`  // 手机
		Name    string `json:"name"`    // 姓名
	}
	OrderPay struct {
		ClientSecret string                 `json:"client_secret"` // 支付方--秘钥
		Country      string                 `json:"country"`       // 国家
		Currency     string                 `json:"currency"`      // 货币
		Metadata     map[string]interface{} `json:"metadata"`      // 额外参数
		Method       string                 `json:"method"`        // 支付方--途径
		OrderSn      string                 `json:"order_sn"`      // 订单编码
		PayStage     int64                  `json:"pay_stage"`     // 支付阶段
		RedirectURL  *string                `json:"redirect_url"`  // 支付方--重定向地址
		Total        OrderPayTotal          `json:"total"`         // 支付参数
	}
	// 支付参数
	OrderPayTotal struct {
		Amount int64  `json:"amount"` // 金额
		Label  string `json:"label"`  // 标签
	}

	/* 删除订单 */
	DeleteReq struct {
		UserId  int64  `json:"user_id"`
		OrderSn string `json:"order_sn"`
	}
	DeleteRes struct {
		ErrorCode basic.StatusResponse
		OrderSn   string
	}
	/* 删除订单 */

	/*  支付超时订单自动关闭 */
	CloseListReq struct {
		Type int64 // type:1=关闭
	}
	CloseListRes struct{}
	/*  支付超时订单自动关闭 */

	/* 关闭 */
	CloseReq struct {
		Type    int64 // type:1=添加购物车
		OrderSn string
		UserId  int64 `json:"user_id"`
	}
	CloseRes struct {
		ErrorCode basic.StatusResponse
	}
	/* 关闭 */

	/* 支付成功 */
	PaymentSuccessfulReq struct {
		EventId       string
		PaymentMethod string
		Charge        *stripe.Charge
	}
	PaymentSuccessfulRes struct{}
	/* 支付成功 */

	/* 预支付--定金 */
	CreatePrePaymentByDepositReq struct {
		StripeKey       string        `json:"stripe_key"`
		Currency        string        `json:"currency"`
		Country         string        `json:"country"`
		UserId          int64         `json:"user_id"`
		OrderSn         string        `json:"order_sn"`
		DeliveryMethod  int64         `json:"delivery_method"`
		DeliveryAddress *OrderAddress `json:"delivery_address"`
	}
	CreatePrePaymentByDepositRes struct {
		ErrorCode   basic.StatusResponse
		OrderDetail gmodel.OrderDetail
		OrderPay    OrderPay
	}
	/* 预支付--定金 */

	/* 预支付--尾款 */
	CreatePrePaymentByBalanceReq struct {
		StripeKey string `json:"stripe_key"`
		Currency  string `json:"currency"`
		Country   string `json:"country"`
		UserId    int64  `json:"user_id"`
		OrderSn   string `json:"order_sn"`
	}
	CreatePrePaymentByBalanceRes struct {
		ErrorCode   basic.StatusResponse
		OrderDetail gmodel.OrderDetail
		OrderPay    OrderPay
	}
	/* 预支付--尾款 */

	/* 下单 */
	CreateReq struct {
		ExpectedDeliveryTime time.Time     `json:"expected_delivery_time"` // 预计到货时间
		ExchangeRate         int64         `json:"exchange_rate"`          // 换算汇率(厘)
		CurrentCurrency      string        `json:"current_currency"`       // 当前货币
		OriginalCurrency     string        `json:"original_currency"`      // 原始货币
		UserId               int64         `json:"user_id"`
		CartIds              []int64       `json:"cart_ids"`
		DeliveryMethod       int64         `json:"delivery_method"`
		DeliveryAddress      *OrderAddress `json:"delivery_address"` // 收货地址
	}
	CreateRes struct {
		ErrorCode basic.StatusResponse
		OrderSn   string
	}
	/* 下单 */

	/* 详情 */
	DetailReq struct {
		UserId  int64  `json:"user_id"`
		OrderSn string `json:"order_sn"`
	}
	DetailRes struct {
		ErrorCode           basic.StatusResponse
		OrderDetail         gmodel.OrderDetail
		OrderDetailOriginal OrderDetailOriginal
	}
	OrderDetailOriginal struct {
		Status                  *gmodel.OrderStatus
		OrderAmount             *gmodel.OrderAmount
		OrderAddress            *gmodel.OrderAddress
		OrderProduct            []gmodel.OrderProductInter
		ShoppingCartSnapshot    []gmodel.FsShoppingCart
		ShoppingProductSnapshot []gmodel.RelaFsProduct
		StatusLink              []gmodel.OrderStatus
		PayStatusLink           []gmodel.PayStatus
	}
	/* 详情 */

	/* 列表 */
	ListReq struct {
		UserId         int64  `json:"user_id"`
		DeliveryMethod int64  `json:"delivery_method"`
		OrderCycle     string `json:"order_cycle"`
		CurrentPage    int64  `json:"current_page"`
		PerPage        int64  `json:"per_page"`
	}
	ListRes struct {
		OrderDetailList []gmodel.OrderDetail
		Meta            interface{}
	}
	/* 列表 */
)

// 订单删除
func (d *defaultOrder) Delete(ctx context.Context, in *DeleteReq) (res *DeleteRes, err error) {
	var errorCode basic.StatusResponse
	err = d.MysqlConn.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var orderInfo gmodel.FsOrder
		model := tx
		if in.UserId != 0 {
			model = model.Where("user_id = ?", in.UserId)
		}
		if in.OrderSn != "" {
			model = model.Where("order_sn = ?", in.OrderSn)
		}
		result := model.Take(&orderInfo)
		if result.Error != nil {
			if errors.Is(result.Error, gorm.ErrRecordNotFound) {
				errorCode = *basic.CodeErrOrderCreatePrePaymentInfoNoFound
			} else {
				errorCode = *basic.CodeServiceErr
			}
			logc.Errorf(ctx, "order delete failed, err: %v", err)
			return result.Error
		}
		ress, err := d.OrderDetailHandler(ctx, &orderInfo, 0)
		if err != nil {
			logc.Errorf(ctx, "order delete failed DetailOrderDetailHandler,OrderSn:%s, err: %v", in.OrderSn, err)
			return err
		}

		if !(*orderInfo.Status == int64(constants.ORDER_STATUS_CLOSE) || (*orderInfo.DeliveryMethod == int64(constants.DELIVERYMETHODDIRECTMAIL) && *orderInfo.Status == int64(constants.ORDER_STATUS_DIRECTMAIL_ARRIVED))) {
			errorCode = *basic.CodeErrOrderDeleteStatusIllegality
			err = errors.New("order delete failed, status is illegality")
			logc.Errorf(ctx, "order delete failed, err: %v", err)
			return err
		}

		// 状态链路
		var ntime = time.Now().UTC()
		var statusCode = constants.ORDER_STATUS_DELETE
		var statusLink = append(ress.OrderDetailOriginal.StatusLink, gmodel.OrderStatus{
			Ctime:       &ntime,
			Utime:       &ntime,
			StatusCode:  statusCode,
			StatusTitle: constants.OrderStatusMessage[statusCode],
		})
		statusLinkByte, err := json.Marshal(statusLink)
		if err != nil {
			logc.Errorf(ctx, "order delete failed Marshal statusLinkByte,OrderSn:%s, err: %v", in.OrderSn, err)
			return err
		}

		var table = gmodel.NewAllModels(tx).FsOrder.TableName()
		var resUpdate *gorm.DB
		var resUpdateSql string = fmt.Sprintf("UPDATE %s SET `utime` = '%s',`is_del` = 1", table, ntime)
		resUpdate = tx.Exec(fmt.Sprintf("%s ,`status_link`= JSON_MERGE_PATCH(`status_link`,?) WHERE `id` = %d", resUpdateSql, orderInfo.Id), statusLinkByte)

		err = resUpdate.Error
		if err != nil {
			logc.Errorf(ctx, "order delete failed Update FsOrder,OrderSn:%s, err: %v", in.OrderSn, err)
			return err
		}
		return nil
	})
	if err != nil {
		logc.Errorf(ctx, "order delete failed, err: %v", err)
		if errorCode.Code == 0 {
			errorCode.Code = basic.CodeApiErr.Code
			errorCode.Message = basic.CodeApiErr.Message
		}
		return &DeleteRes{
			ErrorCode: errorCode,
		}, err
	}

	return &DeleteRes{
		ErrorCode: errorCode,
		OrderSn:   in.OrderSn,
	}, err
}

// 订单关闭-支付超时
func (d *defaultOrder) CloseList(ctx context.Context, in *CloseListReq) (res *CloseListRes, err error) {
	var orderList []gmodel.FsOrder
	result := d.MysqlConn.Model(&gmodel.FsOrder{}).
		Where("is_del = ?", 0).
		Where("status = ?", int64(constants.ORDER_STATUS_UNPAIDDEPOSIT)).
		Where("pay_status = ?", int64(constants.ORDER_PAY_STATUS_UNPAIDDEPOSIT)).
		Find(&orderList)
	if result.Error != nil {
		logc.Errorf(ctx, "order CloseList failed, err: %v", err)
		return nil, result.Error
	}
	for _, orderInfo := range orderList {
		var ntime = time.Now().UTC()
		var cptime = orderInfo.Ctime.Add(time.Minute * 30)
		var dd = ntime.Unix() - cptime.Unix()
		if in.Type == 1 {
			fmt.Println("未支付超时时间: dd--", dd)
			if dd >= 0 {
				orderSn := *orderInfo.OrderSn
				fmt.Println("即时任务: OrderSn--", orderSn)
				ctx := context.Background()

				logc.Infof(ctx, "order CloseList, orderSn: %s", orderSn)
				d.Close(ctx, &CloseReq{
					OrderSn: orderSn,
					Type:    1,
				})
			} else {
				ddd := math.Abs(float64(dd))
				// 延时任务
				time.AfterFunc(time.Second*time.Duration(ddd), func() {
					orderSn := *orderInfo.OrderSn
					fmt.Println("延时任务: OrderSn--", orderSn)
					ctx := context.Background()

					logc.Infof(ctx, "order CloseList, orderSn: %s", orderSn)
					d.Close(ctx, &CloseReq{
						OrderSn: orderSn,
						Type:    1,
					})
				})
			}
		}
	}
	return nil, nil
}

// 关闭
func (d *defaultOrder) Close(ctx context.Context, in *CloseReq) (res *CloseRes, err error) {
	var errorCode basic.StatusResponse
	err = d.MysqlConn.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var orderInfo gmodel.FsOrder
		model := tx.Where("status = ?", int64(constants.ORDER_STATUS_UNPAIDDEPOSIT)).Where("pay_status = ?", int(constants.ORDER_PAY_STATUS_UNPAIDDEPOSIT))
		if in.UserId != 0 {
			model = model.Where("user_id = ?", in.UserId)
		}
		if in.OrderSn != "" {
			model = model.Where("order_sn = ?", in.OrderSn)
		}
		result := model.Take(&orderInfo)
		if result.Error != nil {
			if errors.Is(result.Error, gorm.ErrRecordNotFound) {
				errorCode = *basic.CodeErrOrderCreatePrePaymentInfoNoFound
			} else {
				errorCode = *basic.CodeServiceErr
			}
			logc.Errorf(ctx, "order close failed, err: %v", err)
			return result.Error
		}
		ress, err := d.OrderDetailHandler(ctx, &orderInfo, 0)
		if err != nil {
			logc.Errorf(ctx, "order close failed DetailOrderDetailHandler,OrderSn:%s, err: %v", in.OrderSn, err)
			return err
		}
		// 更新状态、状态链路
		var ntime = time.Now().UTC()
		var statusCode = constants.ORDER_STATUS_CLOSE
		var statusLink = order.UpdateOrderStatusLink(ress.OrderDetailOriginal.StatusLink, gmodel.OrderStatus{
			Ctime:       &ntime,
			Utime:       &ntime,
			StatusCode:  statusCode,
			StatusTitle: constants.OrderStatusMessage[statusCode],
		})
		statusLinkByte, err := json.Marshal(statusLink)
		if err != nil {
			logc.Errorf(ctx, "order close failed Marshal statusLinkByte,OrderSn:%s, err: %v", in.OrderSn, err)
			return err
		}

		var table = gmodel.NewAllModels(tx).FsOrder.TableName()
		var resUpdate *gorm.DB
		var resUpdateSql string = fmt.Sprintf("UPDATE %s SET `status` = %d , `utime` = '%s'", table, statusCode, ntime)
		resUpdate = tx.Exec(fmt.Sprintf("%s ,`status_link`= JSON_MERGE_PATCH(`status_link`,?) WHERE `id` = %d", resUpdateSql, orderInfo.Id), statusLinkByte)

		err = resUpdate.Error
		if err != nil {
			logc.Errorf(ctx, "order close failed Update FsOrder,OrderSn:%s, err: %v", in.OrderSn, err)
			return err
		}

		// 新增购物车
		if in.Type == 1 {
			var users []gmodel.FsShoppingCart
			for _, shoppingCart := range ress.OrderDetailOriginal.ShoppingCartSnapshot {
				users = append(users, gmodel.FsShoppingCart{
					UserId:             shoppingCart.UserId,
					ProductId:          shoppingCart.ProductId,
					TemplateId:         shoppingCart.TemplateId,
					ModelId:            shoppingCart.ModelId,
					SizeId:             shoppingCart.SizeId,
					LightId:            shoppingCart.FittingId,
					FittingId:          shoppingCart.FittingId,
					PurchaseQuantity:   shoppingCart.PurchaseQuantity,
					Snapshot:           shoppingCart.Snapshot,
					IsSelected:         shoppingCart.IsSelected,
					IsHighlyCustomized: shoppingCart.IsHighlyCustomized,
					Ctime:              &ntime,
					Utime:              &ntime,
				})
			}
			resCreate := tx.Create(&users)
			err = resCreate.Error
			if err != nil {
				logc.Errorf(ctx, "order close failed Create FsShoppingCart,OrderSn:%s, err: %v", in.OrderSn, err)
				return err
			}
		}
		return nil
	})
	if err != nil {
		logc.Errorf(ctx, "order close failed, err: %v", err)
		if errorCode.Code == 0 {
			errorCode.Code = basic.CodeApiErr.Code
			errorCode.Message = basic.CodeApiErr.Message
		}
		return &CloseRes{
			ErrorCode: errorCode,
		}, err
	}

	return &CloseRes{
		ErrorCode: errorCode,
	}, err
}

// 支付成功
func (d *defaultOrder) PaymentSuccessful(ctx context.Context, in *PaymentSuccessfulReq) (res *PaymentSuccessfulRes, err error) {
	var orderSn string
	var payStage string
	var ok bool
	var card string
	var brand string
	var country string
	var currency string
	var tradeSn string
	var payAmount int64
	var payTitle string
	var payTime time.Time
	var paymentMethod string

	var payMethod int64 = 1
	if in.PaymentMethod == "stripe" {
		payMethod = 1
	}
	if in.PaymentMethod == "paypal" {
		payMethod = 2
	}

	if in.Charge != nil {
		charge := in.Charge
		orderSn, ok = charge.Metadata["order_sn"]
		if !ok || orderSn == "" {
			err = errors.New("order_sn is empty")
			logc.Errorf(ctx, "PaymentSuccessful failed param, eventId:%s,err:%v", in.EventId, err)
			return &PaymentSuccessfulRes{}, err
		}
		payStage, ok = charge.Metadata["pay_stage"]
		if !ok || payStage == "" {
			err = errors.New("pay_stage is empty")
			logc.Errorf(ctx, "PaymentSuccessful failed param, eventId:%s,err:%v", in.EventId, err)
			return &PaymentSuccessfulRes{}, err
		}
		country, ok = charge.Metadata["country"]
		if !ok || country == "" {
			err = errors.New("country is empty")
			logc.Errorf(ctx, "PaymentSuccessful failed param, eventId:%s,err:%v", in.EventId, err)
			return &PaymentSuccessfulRes{}, err
		}
		if charge.PaymentMethodDetails != nil {
			if charge.PaymentMethodDetails.Card != nil {
				if charge.PaymentMethodDetails.Card.Last4 != "" {
					card = charge.PaymentMethodDetails.Card.Last4
				}
				if charge.PaymentMethodDetails.Card.Brand != "" {
					brand = string(charge.PaymentMethodDetails.Card.Brand)
				}
			}
			if charge.PaymentMethodDetails.Type != "" {
				paymentMethod = string(charge.PaymentMethodDetails.Type)
			}
		}
		if charge.Currency != "" {
			currency = string(charge.Currency)
		}
		tradeSn = charge.ID
		payAmount = charge.Amount
		payTitle = charge.Description
		payTime = time.Unix(charge.Created, 0).UTC()
	}
	err = d.MysqlConn.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var orderInfo gmodel.FsOrder
		result := tx.Where("is_del = ?", 0).Where("order_sn = ?", orderSn).Take(&orderInfo)
		err = result.Error
		if err != nil {
			logc.Errorf(ctx, "PaymentSuccessful failed order Take, eventId:%s,err: %v", in.EventId, err)
			return err
		}
		ress, err := d.OrderDetailHandler(ctx, &orderInfo, 0)
		if err != nil {
			logc.Errorf(ctx, "PaymentSuccessful failed DetailOrderDetailHandler,eventId:%s, err: %v", in.EventId, err)
			return err
		}
		var ntime = time.Now().UTC()
		if (payStage == "deposit" && *orderInfo.PayStatus == int64(constants.ORDER_PAY_STATUS_UNPAIDDEPOSIT)) || (payStage == "remaining_balance" && *orderInfo.PayStatus == int64(constants.ORDER_PAY_STATUS_PAIDDEPOSIT)) {
			var payStatus = int64(constants.PAY_STATUS_PAID)
			var payStageInt int64
			var orderPayStatusCode constants.OrderPayStatusCode
			// 订单状态--当前
			var status gmodel.OrderStatus
			var statusLink []gmodel.OrderStatus
			var payStatusLink []gmodel.PayStatus
			var orderAmount = make(map[string]interface{})
			var payInfo PayInfo
			payInfo.PayMethod = paymentMethod
			payInfo.PaymentMethod = in.PaymentMethod
			payInfo.PayTime = payTime
			payInfo.TradeNo = tradeSn
			// 当前状态
			var statusCode constants.OrderStatusCode
			var statusCodePre constants.OrderStatusCode
			if payStage == "deposit" {
				if *orderInfo.DeliveryMethod == constants.DELIVERYMETHODDIRECTMAIL {
					// 直邮
					statusCode = constants.ORDER_STATUS_DIRECTMAIL_ORDERED
				}
				if *orderInfo.DeliveryMethod == constants.DELIVERYMETHODDSCLOUDSTORE {
					// 云仓
					statusCode = constants.ORDER_STATUS_CLOUDSTORE_ORDERED
				}
				payStageInt = 1
				orderPayStatusCode = constants.ORDER_PAY_STATUS_PAIDDEPOSIT
				status = gmodel.OrderStatus{
					Ctime:       &ntime,
					Utime:       &ntime,
					StatusCode:  statusCode,
					StatusTitle: constants.OrderStatusMessage[statusCode],
				}
				statusLink = order.UpdateOrderStatusLink(ress.OrderDetailOriginal.StatusLink, gmodel.OrderStatus{
					Ctime:       &ntime,
					Utime:       &ntime,
					StatusCode:  statusCode,
					StatusTitle: constants.OrderStatusMessage[statusCode],
				})
				payInfo.Status = gmodel.PayStatus{
					StatusCode:  int64(constants.PAY_STATUS_PAID),
					StatusTitle: constants.PayStatusMessage[constants.PAY_STATUS_PAID],
				}
				payInfo.StatusLink = append(ress.OrderDetailOriginal.OrderAmount.Deposit.StatusLink, payInfo.Status)
				orderAmount["deposit"] = payInfo
			}
			if payStage == "remaining_balance" {
				if *orderInfo.DeliveryMethod == constants.DELIVERYMETHODDIRECTMAIL {
					// 直邮
					statusCodePre = constants.ORDER_STATUS_DIRECTMAIL_ORDERED
					statusCode = constants.ORDER_STATUS_DIRECTMAIL_ORDEREDMAINING
				}
				if *orderInfo.DeliveryMethod == constants.DELIVERYMETHODDSCLOUDSTORE {
					// 云仓
					statusCodePre = constants.ORDER_STATUS_CLOUDSTORE_ORDERED
					statusCode = constants.ORDER_STATUS_CLOUDSTORE_ORDEREDMAINING
				}
				payStageInt = 2
				orderPayStatusCode = constants.ORDER_PAY_STATUS_PAIDDREMAINING

				var statusChildren []*gmodel.OrderStatus
				// 更新订单状态链路--子状态
				for oStatusLinkKey, oStatusLink := range ress.OrderDetailOriginal.StatusLink {
					if oStatusLink.StatusCode == statusCodePre {
						statusChildren = append(oStatusLink.Children, &gmodel.OrderStatus{
							Ctime:       &ntime,
							Utime:       &ntime,
							StatusCode:  statusCode,
							StatusTitle: constants.OrderStatusMessage[statusCode],
						})
						ress.OrderDetailOriginal.StatusLink[oStatusLinkKey].Children = statusChildren
					}
				}
				statusLink = ress.OrderDetailOriginal.StatusLink

				if ress.OrderDetailOriginal.Status.StatusCode == constants.ORDER_STATUS_DIRECTMAIL_ORDERED || ress.OrderDetailOriginal.Status.StatusCode == constants.ORDER_STATUS_CLOUDSTORE_ORDERED {
					status = *ress.OrderDetailOriginal.Status
					status.Children = statusChildren
				}
				payInfo.Status = gmodel.PayStatus{
					StatusCode:  int64(constants.PAY_STATUS_PAID),
					StatusTitle: constants.PayStatusMessage[constants.PAY_STATUS_PAID],
				}

				payInfo.StatusLink = append(ress.OrderDetailOriginal.OrderAmount.RemainingBalance.StatusLink, payInfo.Status)
				orderAmount["remaining_balance"] = payInfo
			}

			payStatusLink = append(ress.OrderDetailOriginal.PayStatusLink, gmodel.PayStatus{
				StatusCode:  int64(orderPayStatusCode),
				StatusTitle: constants.OrderPayStatusMessage[orderPayStatusCode],
			})

			orderAmountByte, err := json.Marshal(orderAmount)
			if err != nil {
				logc.Errorf(ctx, "PaymentSuccessful failed Marshal orderAmount,eventId:%s, err: %v", in.EventId, err)
				return err
			}

			statusLinkByte, err := json.Marshal(statusLink)
			if err != nil {
				logc.Errorf(ctx, "PaymentSuccessful failed Marshal statusLink,eventId:%s, err: %v", in.EventId, err)
				return err
			}

			payStatusLinkByte, err := json.Marshal(payStatusLink)
			if err != nil {
				logc.Errorf(ctx, "PaymentSuccessful failed Marshal payStatusLink,eventId:%s, err: %v", in.EventId, err)
				return err
			}

			// 新增交易信息
			tx.Create(&gmodel.FsOrderTrade{
				UserId:        orderInfo.UserId,
				OrderSn:       &orderSn,
				OrderSource:   orderInfo.OrderSource,
				TradeSn:       &tradeSn,
				PayAmount:     &payAmount,
				PayStatus:     &payStatus,
				PaymentMethod: &payMethod,
				PayStage:      &payStageInt,
				CardSn:        &card,
				CardBrand:     &brand,
				Country:       &country,
				Currency:      &currency,
				Ctime:         &ntime,
				Utime:         &ntime,
				PayTitle:      &payTitle,
			})

			// 更新数据库
			var table = gmodel.NewAllModels(tx).FsOrder.TableName()
			var resUpdate *gorm.DB
			var resUpdateSql string
			if *orderInfo.Status == int64(constants.ORDER_STATUS_UNPAIDDEPOSIT) {
				resUpdateSql = fmt.Sprintf("UPDATE %s SET `status` = %d,`pay_status` = %d , `utime` = '%s'", table, statusCode, orderPayStatusCode, ntime)
			} else {
				resUpdateSql = fmt.Sprintf("UPDATE %s SET `pay_status` = %d , `utime` = '%s'", table, orderPayStatusCode, ntime)
			}
			resUpdate = tx.Exec(fmt.Sprintf("%s ,`status_link`= JSON_MERGE_PATCH(`status_link`,?),`pay_status_link`= JSON_MERGE_PATCH(`pay_status_link`,?),`order_amount`= JSON_MERGE_PATCH(`order_amount`,?) WHERE `id` = %d", resUpdateSql, orderInfo.Id), statusLinkByte, payStatusLinkByte, orderAmountByte)

			err = resUpdate.Error
			if err != nil {
				logc.Errorf(ctx, "PaymentSuccessful failed resUpdate,eventId:%s, err: %v", in.EventId, err)
				return err
			}
		}
		return nil
	})
	return &PaymentSuccessfulRes{}, nil
}

// 预支付--尾款
func (d *defaultOrder) CreatePrePaymentByBalance(ctx context.Context, in *CreatePrePaymentByBalanceReq) (res *CreatePrePaymentByBalanceRes, err error) {
	var errorCode basic.StatusResponse
	var orderInfo gmodel.FsOrder
	model := d.MysqlConn.Where("is_del = ?", 0)
	if in.UserId != 0 {
		model = model.Where("user_id = ?", in.UserId)
	}
	if in.OrderSn != "" {
		model = model.Where("order_sn = ?", in.OrderSn)
	}
	result := model.Take(&orderInfo)
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			errorCode = *basic.CodeErrOrderCreatePrePaymentInfoNoFound
		} else {
			errorCode = *basic.CodeServiceErr
		}
		logc.Errorf(ctx, "create prePayment balance failed, err: %v", err)
		return &CreatePrePaymentByBalanceRes{
			ErrorCode: errorCode,
		}, result.Error
	}
	// 非未支付
	if *orderInfo.PayStatus != int64(constants.ORDER_PAY_STATUS_PAIDDEPOSIT) {
		errorCode = *basic.CodeErrOrderCreatePrePaymentNoUnPaid
		err = errors.New("order balance pay status is not unPaid")
		logc.Errorf(ctx, "create prePayment balance failed, err: %v", err)
		return &CreatePrePaymentByBalanceRes{
			ErrorCode: errorCode,
		}, err
	}

	ress, err := d.OrderDetailHandler(ctx, &orderInfo, 1)
	if err != nil {
		logc.Errorf(ctx, "create prePayment balance failed DetailOrderDetailHandler, err: %v", err)
		errorCode = *basic.CodeServiceErr
		return &CreatePrePaymentByBalanceRes{
			ErrorCode: errorCode,
		}, err
	}
	// 支付初始化
	amount := int64(ress.OrderDetailOriginal.OrderAmount.RemainingBalance.PayAmount.Current.CurrentAmount.(float64) / float64(10))
	payConfig := &pay.Config{}
	payConfig.Stripe.PayType = "intent"
	payConfig.Stripe.Key = in.StripeKey
	var metadata = make(map[string]string, 2)
	metadata["model"] = "product_order"
	metadata["order_sn"] = in.OrderSn
	metadata["pay_stage"] = "remaining_balance"
	metadata["country"] = in.Country
	var generatePrepaymentReq = &pay.GeneratePrepaymentReq{
		Metadata:           metadata,
		ProductName:        "支付尾款后期统一调整",
		Amount:             amount,
		Currency:           "usd",
		Quantity:           1,
		ProductDescription: "支付尾款后期统一调整",
	}
	payDriver := pay.NewPayDriver(1, payConfig)

	prepaymentRes, err := payDriver.GeneratePrepayment(generatePrepaymentReq)
	if err != nil {
		logc.Errorf(ctx, "create prePayment balance failed GeneratePrepayment, err: %v", err)
		errorCode = *basic.CodeServiceErr
		return &CreatePrePaymentByBalanceRes{
			ErrorCode: errorCode,
		}, nil
	}
	return &CreatePrePaymentByBalanceRes{
		OrderDetail: ress.OrderDetail,
		OrderPay: OrderPay{
			ClientSecret: prepaymentRes.ClientSecret,
			Country:      in.Country,
			Currency:     in.Currency,
			Method:       payConfig.Stripe.PayType,
			OrderSn:      in.OrderSn,
			PayStage:     2,
			Total: OrderPayTotal{
				Amount: amount,
				Label:  "支付尾款后期统一调整",
			},
		},
	}, nil
}

// 预支付--定金
func (d *defaultOrder) CreatePrePaymentByDeposit(ctx context.Context, in *CreatePrePaymentByDepositReq) (res *CreatePrePaymentByDepositRes, err error) {
	var errorCode basic.StatusResponse
	var orderInfo gmodel.FsOrder
	model := d.MysqlConn.Where("is_del = ?", 0)
	if in.UserId != 0 {
		model = model.Where("user_id = ?", in.UserId)
	}
	if in.OrderSn != "" {
		model = model.Where("order_sn = ?", in.OrderSn)
	}
	result := model.Take(&orderInfo)
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			errorCode = *basic.CodeErrOrderCreatePrePaymentInfoNoFound
		} else {
			errorCode = *basic.CodeServiceErr
		}
		logc.Errorf(ctx, "create prePayment deposit failed, err: %v", err)
		return &CreatePrePaymentByDepositRes{
			ErrorCode: errorCode,
		}, result.Error
	}

	// 非未支付
	if *orderInfo.PayStatus != int64(constants.ORDER_PAY_STATUS_UNPAIDDEPOSIT) {
		errorCode = *basic.CodeErrOrderCreatePrePaymentNoUnPaid
		err = errors.New("order pay status is not unPaidDeposit")
		logc.Errorf(ctx, "create prePayment deposit failed, err: %v", err)
		return &CreatePrePaymentByDepositRes{
			ErrorCode: errorCode,
		}, err
	}

	// 是否超时支付
	ntime := time.Now().UTC()
	ctime := *orderInfo.Ctime
	ctimeTimeOut := ctime.Add(30 * time.Minute).UTC().Unix()
	ntimeTimeOut := ntime.Unix()

	// 测试超时支付不限制
	if ctimeTimeOut < ntimeTimeOut {
		// if ctimeTimeOut == ntimeTimeOut {
		errorCode = *basic.CodeErrOrderCreatePrePaymentTimeout
		err = errors.New("order pay timeout")
		logc.Errorf(ctx, "create prePayment deposit failed, err: %v", err)
		return &CreatePrePaymentByDepositRes{
			ErrorCode: errorCode,
		}, err
	}

	ress, err := d.OrderDetailHandler(ctx, &orderInfo, 1)
	if err != nil {
		logc.Errorf(ctx, "create prePayment deposit failed DetailOrderDetailHandler, err: %v", err)
		errorCode = *basic.CodeServiceErr
		return &CreatePrePaymentByDepositRes{
			ErrorCode: errorCode,
		}, err
	}

	var orderAddress *gmodel.OrderAddress
	var orderAddressByte []byte
	var statusLinkByte []byte

	if in.DeliveryMethod == constants.DELIVERYMETHODDIRECTMAIL {
		orderAddress = &gmodel.OrderAddress{
			Name:    in.DeliveryAddress.Name,
			Mobile:  in.DeliveryAddress.Mobile,
			Address: in.DeliveryAddress.Address,
		}
		orderAddressByte, err = json.Marshal(orderAddress)
		if err != nil {
			logc.Errorf(ctx, "create prePayment deposit failed orderAddressByte, err: %v", err)
			errorCode = *basic.CodeServiceErr
			return &CreatePrePaymentByDepositRes{
				ErrorCode: errorCode,
			}, err
		}
	}
	// 订单状态--链路
	tPlus60Days := ntime.AddDate(0, 0, 60).UTC()
	var statusLink = order.GenerateOrderStatusLink(in.DeliveryMethod, ntime, tPlus60Days)
	statusLinkByte, err = json.Marshal(statusLink)
	if err != nil {
		logc.Errorf(ctx, "create prePayment deposit failed statusLinkByte, err: %v", err)
		errorCode = *basic.CodeServiceErr
		return &CreatePrePaymentByDepositRes{
			ErrorCode: errorCode,
		}, err
	}

	// 更新数据库
	var table = gmodel.NewAllModels(d.MysqlConn).FsOrder.TableName()
	var resUpdate *gorm.DB
	if in.DeliveryMethod == constants.DELIVERYMETHODDIRECTMAIL {
		resUpdate = d.MysqlConn.Exec(fmt.Sprintf("UPDATE %s SET `delivery_method` = ? , `utime` = ?, `order_address` = JSON_MERGE_PATCH(`order_address`,?), `status_link` = JSON_MERGE_PATCH(`status_link`,?) WHERE `id` = ?", table), in.DeliveryMethod, ntime, orderAddressByte, statusLinkByte, orderInfo.Id)
	} else {
		resUpdate = d.MysqlConn.Exec(fmt.Sprintf("UPDATE %s SET `delivery_method` = ? , `utime` = ? WHERE `id` = ?", table), in.DeliveryMethod, ntime, orderInfo.Id)
	}
	err = resUpdate.Error
	if err != nil {
		logc.Errorf(ctx, "create prePayment deposit failed update Exec, err: %v", err)
		errorCode = *basic.CodeServiceErr
		return &CreatePrePaymentByDepositRes{
			ErrorCode: errorCode,
		}, err
	}

	ress.OrderDetail.OrderInfo.Utime = &ntime
	ress.OrderDetail.OrderInfo.DeliveryMethod = in.DeliveryMethod
	ress.OrderDetail.OrderInfo.StatusLink = statusLink
	ress.OrderDetail.DeliveryAddress = orderAddress

	// 支付初始化
	amount := int64(ress.OrderDetailOriginal.OrderAmount.Deposit.PayAmount.Current.CurrentAmount.(float64) / float64(10))
	payConfig := &pay.Config{}
	payConfig.Stripe.PayType = "intent"
	payConfig.Stripe.Key = in.StripeKey
	var metadata = make(map[string]string, 2)
	metadata["model"] = "product_order"
	metadata["order_sn"] = in.OrderSn
	metadata["pay_stage"] = "deposit"
	metadata["country"] = in.Country
	var generatePrepaymentReq = &pay.GeneratePrepaymentReq{
		Metadata:           metadata,
		ProductName:        "支付首款",
		Amount:             amount,
		Currency:           "usd",
		Quantity:           1,
		ProductDescription: "支付首款",
	}
	payDriver := pay.NewPayDriver(1, payConfig)

	prepaymentRes, err := payDriver.GeneratePrepayment(generatePrepaymentReq)
	if err != nil {
		logc.Errorf(ctx, "create prePayment deposit failed GeneratePrepayment, err: %v", err)
		errorCode = *basic.CodeServiceErr
		return &CreatePrePaymentByDepositRes{
			ErrorCode: errorCode,
		}, nil
	}
	return &CreatePrePaymentByDepositRes{
		OrderDetail: ress.OrderDetail,
		OrderPay: OrderPay{
			ClientSecret: prepaymentRes.ClientSecret,
			Country:      in.Country,
			Currency:     in.Currency,
			Method:       payConfig.Stripe.PayType,
			OrderSn:      in.OrderSn,
			PayStage:     1,
			Total: OrderPayTotal{
				Amount: amount,
				Label:  "",
			},
		},
	}, nil
}

// 列表
func (d *defaultOrder) List(ctx context.Context, in *ListReq) (res *ListRes, err error) {
	var orderList []gmodel.FsOrder
	model := d.MysqlConn.Model(&gmodel.FsOrder{}).Where("is_del = ?", 0)
	model = model.Where("pay_status > ?", 0)
	if in.UserId != 0 {
		model = model.Where("user_id = ?", in.UserId)
	}

	if in.DeliveryMethod != 0 {
		model = model.Where("delivery_method = ?", in.DeliveryMethod)
	}
	if in.OrderCycle != "" {
		// 下单时间
		switch in.OrderCycle {
		case "within_one_month":
			model = model.Where("ctime >?", time.Now().UTC().AddDate(0, -1, 0))
		case "within_three_month":
			model = model.Where("ctime >?", time.Now().UTC().AddDate(0, -3, 0))
		case "within_six_month":
			model = model.Where("ctime >?", time.Now().UTC().AddDate(0, -6, 0))
		case "within_one_year":
			model = model.Where("ctime >?", time.Now().UTC().AddDate(-1, 0, 0))
		}
	}
	var count int64
	resultCount := model.Count(&count)
	if resultCount.Error != nil {
		logc.Errorf(ctx, "order count failed, err: %v", err)
		return nil, resultCount.Error
	}
	var orderDetailList []gmodel.OrderDetail
	if count > 0 {
		m := model.Scopes(handlers.Paginate(&in.CurrentPage, &in.PerPage)).Order("id desc")
		result := m.Find(&orderList)
		if result.Error != nil {
			logc.Errorf(ctx, "order list failed, err: %v", err)
			return nil, result.Error
		}
		for _, order := range orderList {
			ress, err := d.OrderDetailHandler(ctx, &order, 1)
			if err != nil {
				return nil, err
			}
			orderDetailList = append(orderDetailList, ress.OrderDetail)
		}
	}
	if len(orderList) == 0 {
		orderDetailList = make([]gmodel.OrderDetail, 0)
	}

	return &ListRes{
		OrderDetailList: orderDetailList,
		Meta: map[string]int64{
			"total_count":  count,
			"page_count":   count / in.PerPage,
			"current_page": in.CurrentPage,
			"per_page":     in.PerPage,
		},
	}, nil
}

// 详情
func (d *defaultOrder) Detail(ctx context.Context, in *DetailReq) (res *DetailRes, err error) {
	var errorCode basic.StatusResponse
	var orderInfo gmodel.FsOrder
	model := d.MysqlConn.Where("is_del = ?", 0)
	if in.UserId != 0 {
		model = model.Where("user_id = ?", in.UserId)
	}
	if in.OrderSn != "" {
		model = model.Where("order_sn = ?", in.OrderSn)
	}
	result := model.Take(&orderInfo)
	if result.Error != nil {
		if errors.Is(result.Error, gorm.ErrRecordNotFound) {
			errorCode = *basic.CodeErrOrderCreatePrePaymentInfoNoFound
		} else {
			errorCode = *basic.CodeServiceErr
		}
		logc.Errorf(ctx, "order detail failed, err: %v", err)
		return &DetailRes{
			ErrorCode: errorCode,
		}, result.Error
	}

	// 是否超时支付
	// if *order.Status == int64(constants.ORDER_STATUS_UNPAIDDEPOSIT) {
	// 	ctime := *order.Ctime
	// 	ctimeTimeOut := ctime.Add(30 * time.Minute).UTC().Unix()
	// 	ntimeTimeOut := time.Now().UTC().Unix()
	// 	if ctimeTimeOut < ntimeTimeOut {
	// 		errorCode = *basic.CodeErrOrderCreatePrePaymentTimeout
	// 		err = errors.New("order pay timeout")
	// 		logc.Errorf(ctx, "order detail failed, err: %v", err)
	// 		return &DetailRes{
	// 			ErrorCode: errorCode,
	// 		}, err
	// 	}
	// }

	ress, err := d.OrderDetailHandler(ctx, &orderInfo, 1)
	if err != nil {
		logc.Errorf(ctx, "order detail failed, err: %v", err)
		errorCode = *basic.CodeServiceErr
		return &DetailRes{
			ErrorCode: errorCode,
		}, err
	}
	return &DetailRes{
		ErrorCode:   errorCode,
		OrderDetail: ress.OrderDetail,
	}, nil
}

// 下单
func (d *defaultOrder) Create(ctx context.Context, in *CreateReq) (res *CreateRes, err error) {
	var errorCode basic.StatusResponse
	// 订单编号
	var orderSn string = order.GenerateOrderNumber()

	err = d.MysqlConn.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 查询购物车
		var shoppingCartList []*gmodel.RelaFsShoppingCart
		resShoppingCartFind := tx.Table(gmodel.NewFsShoppingCartModel(tx).TableName()).
			Preload("ShoppingCartProduct", func(dbPreload *gorm.DB) *gorm.DB {
				return dbPreload.Table(gmodel.NewFsProductModel(tx).TableName()).Preload("CoverResource")
			}).
			Preload("ShoppingCartProductModel3d").
			Preload("ShoppingCartProductModel3dFitting").
			Where("id IN ?", in.CartIds).
			Where("user_id = ?", in.UserId).
			Find(&shoppingCartList)
		err = resShoppingCartFind.Error
		if err != nil {
			return err
		}
		shoppingCartListLen := len(shoppingCartList)
		if shoppingCartListLen == 0 {
			errorCode = *basic.CodeErrOrderCreatShoppingCartEmpty
			return errors.New(errorCode.Message)
		}
		if shoppingCartListLen != len(in.CartIds) {
			errorCode = *basic.CodeErrOrderCreatShoppingCartNotMatched
			return errors.New(errorCode.Message)
		}

		// 订单商品列表
		var orderProductList []*gmodel.OrderProductInter
		var shoppingProductSnapshotList []*gmodel.RelaFsProduct
		var shoppingCartSnapshotList []*gmodel.FsShoppingCart

		var shippingFee gmodel.AmountInfo
		// 订单税费总价(厘)
		var shippingFeeTotal int64

		var tax = gmodel.AmountInfo{}
		// 订单邮费总价(厘)
		var taxTotal int64

		var discount gmodel.AmountInfo
		// 订单折扣总价(厘)
		var discountTotal int64

		var subtotal gmodel.AmountInfo
		// 订单商品总价(厘)
		var orderProductTotal int64

		var total gmodel.AmountInfo
		// 订单总价(厘)
		var orderTotal int64

		var nowTime = time.Now().UTC()

		// 收货地址
		var orderAddress *gmodel.OrderAddress

		// 支付状态
		var payStatus = constants.ORDER_PAY_STATUS_UNPAIDDEPOSIT
		// 订单状态
		var status = constants.ORDER_STATUS_UNPAIDDEPOSIT

		// 直邮
		if in.DeliveryMethod == constants.DELIVERYMETHODDIRECTMAIL {
			orderAddress = &gmodel.OrderAddress{
				Mobile: in.DeliveryAddress.Mobile,
				Name:   in.DeliveryAddress.Name,
			}
		}
		// 预计交付时间
		var expectedDeliveryTime = &gmodel.ExpectedDelivery{
			Current:  in.ExpectedDeliveryTime,
			Initiate: in.ExpectedDeliveryTime,
		}

		for _, shoppingCart := range shoppingCartList {
			// 购物车快照
			var shoppingCartSnapshot gmodel.CartSnapshot
			if shoppingCart.Snapshot != nil {
				json.Unmarshal([]byte(*shoppingCart.Snapshot), &shoppingCartSnapshot)
			}
			// 商品异常
			if shoppingCart.ShoppingCartProduct == nil || (shoppingCart.ShoppingCartProduct != nil && *shoppingCart.ShoppingCartProduct.IsShelf == 0) {
				errorCode = *basic.CodeErrOrderCreatProductAbsent
				errorCode.Message = "create order failed, product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is absent"
				return errors.New(errorCode.Message)
			}

			var stepPriceJson gmodel.StepPriceJsonStruct
			if shoppingCart.ShoppingCartProductModel3d.StepPrice != nil {
				err = json.Unmarshal(*shoppingCart.ShoppingCartProductModel3d.StepPrice, &stepPriceJson)
				if err != nil {
					return err
				}
			} else {
				errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
				errorCode.Message = "create order failed, step price of product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is failed"
				return errors.New("shoppingCartProductModel3d.StepPrice nil")
			}

			var fittingPrice int64
			if shoppingCart.ShoppingCartProductModel3dFitting != nil {
				fittingPrice = *shoppingCart.ShoppingCartProductModel3dFitting.Price
			}

			/* 计算价格 */
			productTotalPrice, productPrice, err := NewShoppingCart(tx, nil, nil).CaculateStepPrice(*shoppingCart.PurchaseQuantity, stepPriceJson, fittingPrice)
			if err != nil {
				errorCode = *basic.CodeErrOrderCreatProductPriceAbsent
				errorCode.Message = "create order failed, step price of product '" + shoppingCartSnapshot.ProductInfo.ProductName + "'is failed"
				return err
			}
			/* 计算价格 */

			// 订单商品总价(厘)
			orderProductTotal = orderProductTotal + productTotalPrice

			// 订单商品
			var productCoverMetadata map[string]interface{}
			if shoppingCart.ShoppingCartProduct.CoverResource != nil && shoppingCart.ShoppingCartProduct.CoverResource.Metadata != nil {
				json.Unmarshal(*shoppingCart.ShoppingCartProduct.CoverResource.Metadata, &productCoverMetadata)
			}

			productInter := gmodel.OrderProductInter{
				TotalPrice: order.GetAmountInfo(order.GetAmountInfoReq{
					ExchangeRate:     in.ExchangeRate,
					Initiate:         productTotalPrice,
					Current:          productTotalPrice,
					CurrentCurrency:  in.CurrentCurrency,
					OriginalCurrency: in.OriginalCurrency,
				}),
				ItemPrice: order.GetAmountInfo(order.GetAmountInfoReq{
					ExchangeRate:     in.ExchangeRate,
					Initiate:         productPrice,
					Current:          productPrice,
					CurrentCurrency:  in.CurrentCurrency,
					OriginalCurrency: in.OriginalCurrency,
				}),
				PurchaseQuantity: &gmodel.PurchaseQuantity{
					Current:  *shoppingCart.PurchaseQuantity,
					Initiate: *shoppingCart.PurchaseQuantity,
				},
				ProductId:            *shoppingCart.ProductId,
				ProductCover:         *shoppingCart.ShoppingCartProduct.Cover,
				ProductCoverMetadata: productCoverMetadata,
				ProductName:          *shoppingCart.ShoppingCartProduct.Title,

				ProductSn:      *shoppingCart.ShoppingCartProduct.Sn,
				DiyInformation: &shoppingCartSnapshot.UserDiyInformation,
				FittingInfo: &gmodel.OrderProductFittingInfo{
					FittingID:   *shoppingCart.FittingId,
					FittingName: shoppingCartSnapshot.FittingInfo.FittingName,
				},
				SizeInfo: &gmodel.OrderProductSizeInfo{
					SizeID:   *shoppingCart.SizeId,
					Capacity: shoppingCartSnapshot.SizeInfo.Capacity,
					Title: gmodel.OrderProductSizeInfoTitle{
						Inch: shoppingCartSnapshot.SizeInfo.Inch,
						Cm:   shoppingCartSnapshot.SizeInfo.Cm,
					},
				},
				IsHighlyCustomized:   *shoppingCart.IsHighlyCustomized,
				RenderImage:          shoppingCartSnapshot.RenderImage,
				CartId:               shoppingCart.Id,
				ExpectedDeliveryTime: expectedDeliveryTime,
			}
			orderProductList = append(orderProductList, &productInter)
			shoppingProductSnapshotList = append(shoppingProductSnapshotList, shoppingCart.ShoppingCartProduct)
			shoppingCartSnapshotList = append(shoppingCartSnapshotList, &shoppingCart.FsShoppingCart)
		}

		subtotal = order.GetAmountInfo(order.GetAmountInfoReq{
			ExchangeRate:     in.ExchangeRate,
			Initiate:         orderProductTotal,
			Current:          orderProductTotal,
			CurrentCurrency:  in.CurrentCurrency,
			OriginalCurrency: in.OriginalCurrency,
		})

		orderTotal = orderProductTotal + shippingFeeTotal + taxTotal - discountTotal
		total = order.GetAmountInfo(order.GetAmountInfoReq{
			ExchangeRate:     in.ExchangeRate,
			Initiate:         orderTotal,
			Current:          orderTotal,
			CurrentCurrency:  in.CurrentCurrency,
			OriginalCurrency: in.OriginalCurrency,
		})
		// 定金
		var depositInt int64 = orderTotal / 2
		var deposit = gmodel.PayInfo{
			Status: gmodel.PayStatus{
				StatusCode:  int64(constants.PAY_STATUS_UNPAID),
				StatusTitle: constants.PayStatusMessage[constants.PAY_STATUS_UNPAID],
			},
			StatusLink: make([]gmodel.PayStatus, 0),
			PayAmount: order.GetAmountInfo(order.GetAmountInfoReq{
				ExchangeRate:     in.ExchangeRate,
				Initiate:         depositInt,
				Current:          depositInt,
				CurrentCurrency:  in.CurrentCurrency,
				OriginalCurrency: in.OriginalCurrency,
			}),
		}

		// 尾款
		var remainingBalanceInt int64 = orderTotal - depositInt
		var remainingBalance = gmodel.PayInfo{
			Status: gmodel.PayStatus{
				StatusCode:  int64(constants.PAY_STATUS_UNPAID),
				StatusTitle: constants.PayStatusMessage[constants.PAY_STATUS_UNPAID],
			},
			StatusLink: make([]gmodel.PayStatus, 0),
			PayAmount: order.GetAmountInfo(order.GetAmountInfoReq{
				ExchangeRate:     in.ExchangeRate,
				Initiate:         remainingBalanceInt,
				Current:          remainingBalanceInt,
				CurrentCurrency:  in.CurrentCurrency,
				OriginalCurrency: in.OriginalCurrency,
			}),
		}
		var orderAmount = gmodel.OrderAmount{
			Deposit:          deposit,
			RemainingBalance: remainingBalance,
			Discount:         discount,
			ShippingFee:      shippingFee,
			Tax:              tax,
			Subtotal:         subtotal,
			Total:            total,
		}

		// 订单状态--链路
		var statusLink = order.GenerateOrderStatusLink(in.DeliveryMethod, nowTime, in.ExpectedDeliveryTime)

		byteOrderProduct, _ := json.Marshal(orderProductList)
		byteOrderAddress, _ := json.Marshal(orderAddress)
		byteOrderAmount, _ := json.Marshal(orderAmount)
		byteShoppingCartSnapshot, _ := json.Marshal(shoppingCartSnapshotList)
		byteShoppingProductSnapshot, _ := json.Marshal(shoppingProductSnapshotList)
		byteStatusLink, _ := json.Marshal(statusLink)
		byteOrderMetadata, _ := json.Marshal(gmodel.OrderMetadata{
			ExpectedDeliveryTime: expectedDeliveryTime,
		})

		// 创建订单
		resultCreate := tx.Create(&gmodel.FsOrder{
			OrderProduct:            &byteOrderProduct,
			OrderAddress:            &byteOrderAddress,
			OrderAmount:             &byteOrderAmount,
			ShoppingCartSnapshot:    &byteShoppingCartSnapshot,
			ShoppingProductSnapshot: &byteShoppingProductSnapshot,
			StatusLink:              &byteStatusLink,
			UserId:                  &in.UserId,
			DeliveryMethod:          &in.DeliveryMethod,
			OrderSn:                 &orderSn,
			Status:                  (*int64)(&status),
			PayStatus:               (*int64)(&payStatus),
			Ctime:                   &nowTime,
			Metadata:                &byteOrderMetadata,
		})

		if resultCreate.Error != nil {
			return resultCreate.Error
		}
		// 删除购物车
		resultDelete := tx.Delete(&gmodel.FsShoppingCart{}, in.CartIds)
		if resultDelete.Error != nil {
			return resultDelete.Error
		}

		return nil
	})
	if err != nil {
		logc.Errorf(ctx, "order create order failed, err: %v", err)

		if errorCode.Code == 0 {
			errorCode.Code = basic.CodeApiErr.Code
			errorCode.Message = basic.CodeApiErr.Message
		}
		return &CreateRes{
			OrderSn:   orderSn,
			ErrorCode: errorCode,
		}, err
	}
	return &CreateRes{
		OrderSn:   orderSn,
		ErrorCode: errorCode,
	}, nil
}

// 购物车快照处理
func (d *defaultOrder) OrderShoppingCartSnapshotHandler(ctx context.Context, req *string) (res map[string]interface{}, err error) {
	var snapshot map[string]interface{}
	json.Unmarshal([]byte(*req), &snapshot)
	// snapshotFittingInfoData, snapshotFittingInfoEx := snapshot["fitting_info"]
	// var fittingInfoMap map[string]interface{}
	// if snapshotFittingInfoEx {
	// 	var snapshotFittingInfoJson map[string]interface{}
	// 	var fittingName string
	// 	snapshotFittingInfo := snapshotFittingInfoData.(map[string]interface{})
	// 	snapshotFittingInfoJsonData, snapshotFittingInfoJsonEx := snapshotFittingInfo["fitting_json"]
	// 	if snapshotFittingInfoJsonEx {
	// 		json.Unmarshal([]byte(snapshotFittingInfoJsonData.(string)), &snapshotFittingInfoJson)
	// 	}
	// 	fittingNameData, fittingNameEx := snapshotFittingInfo["fitting_name"]
	// 	if fittingNameEx {
	// 		fittingName = fittingNameData.(string)
	// 	}
	// 	fittingInfoMap = make(map[string]interface{}, 2)
	// 	fittingInfoMap["fitting_json"] = snapshotFittingInfoJson
	// 	fittingInfoMap["fitting_name"] = fittingName
	// }
	// snapshot["fitting_info"] = fittingInfoMap

	// snapshotModelInfoData, snapshotModelInfoEx := snapshot["model_info"]
	// var modelInfoMap map[string]interface{}
	// if snapshotModelInfoEx {
	// 	var snapshotModelInfoJson map[string]interface{}
	// 	snapshotModelInfo := snapshotModelInfoData.(map[string]interface{})
	// 	snapshotModelInfoJsonData, snapshotModelInfoJsonEx := snapshotModelInfo["model_json"]
	// 	if snapshotModelInfoJsonEx {
	// 		json.Unmarshal([]byte(snapshotModelInfoJsonData.(string)), &snapshotModelInfoJson)
	// 	}
	// 	modelInfoMap = make(map[string]interface{}, 1)
	// 	modelInfoMap["model_json"] = snapshotModelInfoJson
	// }
	// snapshot["model_info"] = modelInfoMap

	// snapshotTemplateInfoData, snapshotTemplateInfoEx := snapshot["template_info"]
	// var templateInfoMap map[string]interface{}
	// if snapshotTemplateInfoEx {
	// 	var snapshotTemplateInfoJson map[string]interface{}
	// 	var templateTag string
	// 	snapshotTemplateInfo := snapshotTemplateInfoData.(map[string]interface{})
	// 	snapshotTemplateInfoJsonData, snapshotTemplateInfoJsonEx := snapshotTemplateInfo["template_json"]
	// 	if snapshotTemplateInfoJsonEx {
	// 		json.Unmarshal([]byte(snapshotTemplateInfoJsonData.(string)), &snapshotTemplateInfoJson)
	// 	}
	// 	templateTagData, templateTagEx := snapshotTemplateInfo["template_tag"]
	// 	if templateTagEx {
	// 		templateTag = templateTagData.(string)
	// 	}
	// 	templateInfoMap = make(map[string]interface{}, 2)
	// 	templateInfoMap["template_json"] = snapshotTemplateInfoJson
	// 	templateInfoMap["template_tag"] = templateTag
	// }
	// snapshot["template_info"] = templateInfoMap

	// snapshotLightInfoData, snapshotLightInfoEx := snapshot["light_info"]
	// var lightInfoMap map[string]interface{}
	// if snapshotLightInfoEx {
	// 	var snapshotLightInfoJson map[string]interface{}
	// 	var lightTag string
	// 	snapshotLightInfo := snapshotLightInfoData.(map[string]interface{})
	// 	snapshotLightInfoJsonData, snapshotLightInfoJsonEx := snapshotLightInfo["light_json"]
	// 	if snapshotLightInfoJsonEx {
	// 		json.Unmarshal([]byte(snapshotLightInfoJsonData.(string)), &snapshotLightInfoJson)
	// 	}
	// 	lightTagData, lightTagEx := snapshotLightInfo["light_tag"]
	// 	if lightTagEx {
	// 		lightTag = lightTagData.(string)
	// 	}
	// 	lightInfoMap = make(map[string]interface{}, 2)
	// 	lightInfoMap["light_json"] = snapshotLightInfoJson
	// 	lightInfoMap["light_tag"] = lightTag
	// }
	// snapshot["light_info"] = lightInfoMap
	return snapshot, nil
}

// 详情处理
func (d *defaultOrder) OrderDetailHandler(ctx context.Context, orderInfo *gmodel.FsOrder, original int64) (res *DetailRes, err error) {

	var orderAmount gmodel.OrderAmount
	err = json.Unmarshal(*orderInfo.OrderAmount, &orderAmount)
	if err != nil {
		logc.Errorf(ctx, "order detail handler unmarshal OrderAmount failed, err: %v", err)
		return nil, err
	}

	var orderAddress *gmodel.OrderAddress
	if orderInfo.OrderAddress != nil {
		err = json.Unmarshal(*orderInfo.OrderAddress, &orderAddress)
		if err != nil {
			logc.Errorf(ctx, "order detail handler unmarshal OrderAddress failed, err: %v", err)
			return nil, err
		}
	}

	var orderProduct []gmodel.OrderProductInter
	err = json.Unmarshal(*orderInfo.OrderProduct, &orderProduct)
	if err != nil {
		logc.Errorf(ctx, "order detail handler unmarshal OrderProduct failed, err: %v", err)
		return nil, err
	}

	var shoppingCartSnapshot []gmodel.FsShoppingCart
	err = json.Unmarshal(*orderInfo.ShoppingCartSnapshot, &shoppingCartSnapshot)
	if err != nil {
		logc.Errorf(ctx, "order detail handler unmarshal ShoppingCartSnapshot failed, err: %v", err)
		return nil, err
	}

	var shoppingProductSnapshot []gmodel.RelaFsProduct
	err = json.Unmarshal(*orderInfo.ShoppingProductSnapshot, &shoppingProductSnapshot)
	if err != nil {
		logc.Errorf(ctx, "order detail handler unmarshal ShoppingCartSnapshot failed, err: %v", err)
		return nil, err
	}

	var statusLink []gmodel.OrderStatus
	err = json.Unmarshal(*orderInfo.StatusLink, &statusLink)
	if err != nil {
		logc.Errorf(ctx, "order detail handler unmarshal StatusLink failed, err: %v", err)
		return nil, err
	}

	var payStatusLink []gmodel.PayStatus
	if orderInfo.PayStatusLink != nil {
		err = json.Unmarshal(*orderInfo.PayStatusLink, &payStatusLink)
		if err != nil {
			logc.Errorf(ctx, "order detail handler unmarshal PayStatusLink failed, err: %v", err)
			return nil, err
		}
	}

	var orderMetadata gmodel.OrderMetadata
	if orderInfo.Metadata != nil {
		json.Unmarshal(*orderInfo.Metadata, &orderMetadata)
	}
	// 预计到货时间
	var expectedTime = orderMetadata.ExpectedDeliveryTime.Current

	var status = order.GetOrderStatusCurrent(statusLink, constants.OrderStatusCode(*orderInfo.Status))
	status.ExpectedTime = &expectedTime

	var orderDetail gmodel.OrderDetail

	if original == 1 {
		orderDetail.DeliveryAddress = orderAddress
		orderDetail.OrderAmount = orderAmount
		orderDetail.OrderAmount.Deposit.PayAmount = order.GetAmountInfoFormat(&orderAmount.Deposit.PayAmount)
		orderDetail.OrderAmount.RemainingBalance.PayAmount = order.GetAmountInfoFormat(&orderAmount.RemainingBalance.PayAmount)
		orderDetail.OrderAmount.Subtotal = order.GetAmountInfoFormat(&orderAmount.Subtotal)
		orderDetail.OrderAmount.Total = order.GetAmountInfoFormat(&orderAmount.Total)
		orderDetail.PayStatus = constants.OrderPayStatusCode(*orderInfo.PayStatus)
		orderDetail.PayTimeout = time.Duration(orderInfo.Ctime.Add(30*time.Minute).UTC().Unix() - time.Now().UTC().Unix())
		orderDetail.OrderInfo = gmodel.OrderInfo{
			UserId:         *orderInfo.UserId,
			DeliveryMethod: *orderInfo.DeliveryMethod,
			OrderSn:        *orderInfo.OrderSn,
			Ctime:          orderInfo.Ctime,
			Utime:          orderInfo.Utime,
			Status:         status,
			StatusLink:     order.GetOrderStatusLinkUser(*orderInfo.DeliveryMethod, statusLink),
			Metadata:       orderMetadata,
		}

		var orderProductList []gmodel.OrderProduct
		for _, productValue := range orderProduct {
			var selectColorIndex int64
			var shoppingCartSnapshotData gmodel.FsShoppingCartData
			for _, shoppingCartSnapshotValue := range shoppingCartSnapshot {
				if productValue.CartId == shoppingCartSnapshotValue.Id {

					snapshot, err := d.OrderShoppingCartSnapshotHandler(ctx, shoppingCartSnapshotValue.Snapshot)
					if err != nil {
						logc.Errorf(ctx, "OrderDetailHandler OrderShoppingCartSnapshotHandler failed, err: %v", err)
						return nil, err
					}
					shoppingCartSnapshotData.Id = shoppingCartSnapshotValue.Id
					shoppingCartSnapshotData.UserId = shoppingCartSnapshotValue.UserId
					shoppingCartSnapshotData.ProductId = shoppingCartSnapshotValue.ProductId
					shoppingCartSnapshotData.TemplateId = shoppingCartSnapshotValue.TemplateId
					shoppingCartSnapshotData.ModelId = shoppingCartSnapshotValue.ModelId
					shoppingCartSnapshotData.SizeId = shoppingCartSnapshotValue.SizeId
					shoppingCartSnapshotData.LightId = shoppingCartSnapshotValue.LightId
					shoppingCartSnapshotData.FittingId = shoppingCartSnapshotValue.FittingId
					shoppingCartSnapshotData.PurchaseQuantity = shoppingCartSnapshotValue.PurchaseQuantity
					shoppingCartSnapshotData.IsSelected = shoppingCartSnapshotValue.IsSelected
					shoppingCartSnapshotData.IsSelected = shoppingCartSnapshotValue.IsSelected
					shoppingCartSnapshotData.IsHighlyCustomized = shoppingCartSnapshotValue.IsHighlyCustomized
					shoppingCartSnapshotData.Ctime = shoppingCartSnapshotValue.Ctime
					shoppingCartSnapshotData.Utime = shoppingCartSnapshotValue.Utime
					shoppingCartSnapshotData.Snapshot = &snapshot

					selectColorIndexV, selectColorIndexE := snapshot["select_color_index"]
					if selectColorIndexE {
						selectColorIndex = selectColorIndexV.(int64)
					}

				}
			}
			var expectedDeliveryTime time.Time
			if productValue.ExpectedDeliveryTime == nil {
				expectedDeliveryTime = expectedTime
			} else {
				expectedDeliveryTime = productValue.ExpectedDeliveryTime.Current
			}

			orderProductItem := gmodel.OrderProduct{
				TotalPrice: order.GetAmountInfoFormat(&productValue.TotalPrice),
				ItemPrice:  order.GetAmountInfoFormat(&productValue.ItemPrice),

				ProductId:            productValue.ProductId,
				ProductSn:            productValue.ProductSn,
				ProductName:          productValue.ProductName,
				ProductCover:         productValue.ProductCover,
				ProductCoverMetadata: productValue.ProductCoverMetadata,
				ShoppingCartSnapshot: &shoppingCartSnapshotData,
				ExpectedDeliveryTime: &expectedDeliveryTime,
				PurchaseQuantity:     *productValue.PurchaseQuantity,

				DiyInformation:     productValue.DiyInformation,
				SizeInfo:           productValue.SizeInfo,
				FittingInfo:        productValue.FittingInfo,
				IsHighlyCustomized: productValue.IsHighlyCustomized,
				RenderImage:        productValue.RenderImage,
				SelectColorIndex:   selectColorIndex,
			}

			orderProductList = append(orderProductList, orderProductItem)
		}
		orderDetail.OrderProduct = orderProductList
	}

	return &DetailRes{
		OrderDetail: orderDetail,
		OrderDetailOriginal: OrderDetailOriginal{
			Status:                  &status,
			OrderAmount:             &orderAmount,
			OrderAddress:            orderAddress,
			OrderProduct:            orderProduct,
			ShoppingCartSnapshot:    shoppingCartSnapshot,
			ShoppingProductSnapshot: shoppingProductSnapshot,
			StatusLink:              statusLink,
			PayStatusLink:           payStatusLink,
		},
	}, nil
}