fusenapi/service/repositories/order.go
2023-10-11 10:16:45 +08:00

1576 lines
54 KiB
Go

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)
var byteOrderAddress []byte
if orderAddress != nil {
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
}