package localjx import ( "crypto/md5" "fmt" "math" "time" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/jx-callback/globals" ) const ( OrderCreateTypePre = 0 // 预创建 OrderCreateTypeNormal = 1 // 正常创建 PayWaitingTime = 10 * time.Minute // 等待支付的最长时间 ) type JxSkuInfo struct { SkuID int `json:"skuID"` Count int `json:"count"` Price int64 `json:"price,omitempty"` // 原价 SalePrice int64 `json:"salePrice,omitempty"` // 售卖价 Name string `json:"name"` } type JxOrderInfo struct { BuyerComment string StoreID int `json:"storeID"` Skus []*JxSkuInfo `json:"skus"` ExpectedDeliveredTime *time.Time `orm:"type(datetime)" json:"expectedDeliveredTime"` // 预期送达时间 TotalPrice int64 `json:"totalPrice"` // 单位为分 订单总价 FreightPrice int64 `json:"freightPrice"` // 单位为分 订单配送费 OrderPrice int64 `json:"orderPrice"` // 单位为分 订单商品价格 ActualPayPrice int64 `json:"actualPayPrice"` // 单位为分 顾客实际支付 OrderID int64 `json:"orderID"` StoreName string `json:"storeName"` } var ( orderNoBeginTimestamp int64 ) func init() { orderNoBeginTimestamp = utils.Str2Time("2010-01-01 00:00:00").Unix() } func CreateOrder(ctx *jxcontext.Context, jxOrder *JxOrderInfo, addressID int64, createType int) (outJxOrder *JxOrderInfo, err error) { outJxOrder, deliveryAddress, err := generateOrder(ctx, jxOrder, addressID) if err != nil { return nil, err } if createType != OrderCreateTypePre { if outJxOrder.TotalPrice != jxOrder.TotalPrice { return nil, fmt.Errorf("商品或配送信息发生改变,请重新下单") } outJxOrder.OrderID = GenOrderNo(ctx) order, err2 := jxOrder2GoodsOrder(ctx, outJxOrder, deliveryAddress) if err = err2; err == nil { partner.CurOrderManager.OnOrderNew(order, model.Order2Status(order)) } } return outJxOrder, err } func Pay4Order(ctx *jxcontext.Context, orderID int64, payType int, vendorPayType string) (orderPay *model.OrderPay, err error) { order, err := partner.CurOrderManager.LoadOrder(utils.Int64ToStr(orderID), model.VendorIDJX) if err == nil { switch payType { case model.PayTypeWX: orderPay, err = pay4OrderByWX(ctx, order, vendorPayType) dao.WrapAddIDCULDEntity(orderPay, ctx.GetUserName()) err = dao.CreateEntity(dao.GetDB(), orderPay) default: err = fmt.Errorf("支付方式:%d当前不支持", payType) } } return orderPay, err } func OnPayFinished(orderPay *model.OrderPay) (err error) { order, err := partner.CurOrderManager.LoadOrder(orderPay.VendorOrderID, orderPay.VendorID) if err == nil { db := dao.GetDB() dao.UpdateEntity(db, orderPay) order.Status = model.OrderStatusNew order.StatusTime = *orderPay.PayFinishedAt err = partner.CurOrderManager.OnOrderNew(order, model.Order2Status(order)) } return err } func GenOrderNo(ctx *jxcontext.Context) (orderNo int64) { const prefix = 88 const randPartNum = 1000 orderNo = time.Now().Unix() - orderNoBeginTimestamp // fmt.Println(orderNo) orderNo = orderNo * randPartNum md5Bytes := md5.Sum([]byte(utils.GetUUID())) randPart := 0 for k, v := range md5Bytes { randPart += int(v) << ((k % 3) * 8) } orderNo += int64(randPart % randPartNum) orderNo += int64(math.Pow10(int(math.Log10(float64(orderNo)))+1)) * prefix return orderNo } func GenRefundID(order *model.GoodsOrder) (refundID int64) { const suffix = 100000 orderID := utils.Str2Int64(order.VendorOrderID) * suffix orderID += int64(time.Now().Sub(order.OrderFinishedAt) / time.Minute) return refundID } func formalizeSkus(skus []*JxSkuInfo) (outSkus []*JxSkuInfo) { skuMap := make(map[int]int) for _, v := range skus { skuMap[v.SkuID] += v.Count } for skuID, skuCount := range skuMap { outSkus = append(outSkus, &JxSkuInfo{ SkuID: skuID, Count: skuCount, }) } return outSkus } func isTimeInOpTime(openTime1, closeTime1, openTime2, closeTime2 int16, time2Check time.Time) bool { timeStrList := []string{ jxutils.OperationTime2StrWithSecond(openTime1), jxutils.OperationTime2StrWithSecond(closeTime1), } if openTime1 > 0 { timeStrList = append(timeStrList, jxutils.OperationTime2StrWithSecond(openTime2), jxutils.OperationTime2StrWithSecond(closeTime2), ) } checkTimeStr := utils.Time2TimeStr(time2Check) for i := 0; i < len(timeStrList); i += 2 { if checkTimeStr >= timeStrList[i] && checkTimeStr <= timeStrList[i+1] { return true } } return false } func generateOrder(ctx *jxcontext.Context, jxOrder *JxOrderInfo, addressID int64) (outJxOrder *JxOrderInfo, deliveryAddress *dao.UserDeliveryAddressEx, err error) { db := dao.GetDB() if jxOrder.StoreID == 0 { return nil, nil, fmt.Errorf("没有指定门店信息") } // 配送范围检查 storeDetail, err := dao.GetStoreDetail(db, jxOrder.StoreID, model.VendorIDJX) if err != nil { return nil, nil, err } addressList, _, err := dao.QueryUserDeliveryAddress(db, addressID, []string{ctx.GetUserID()}, 0, 0) if err != nil { return nil, nil, err } if len(addressList) == 0 { return nil, nil, fmt.Errorf("地址ID不正确") } deliveryAddress = addressList[0] if distance := jxutils.Point2StoreDistance(deliveryAddress.Lng, deliveryAddress.Lat, storeDetail.Lng, storeDetail.Lat, storeDetail.DeliveryRangeType, storeDetail.DeliveryRange); distance == 0 { return nil, nil, fmt.Errorf("送货地址:%s不在门店%s的配送范围", deliveryAddress.DetailAddress, storeDetail.Name) } // 营业状态及时间检查 if storeDetail.Status == model.StoreStatusDisabled { return nil, nil, fmt.Errorf("门店:%s状态是:%s", storeDetail.Name, model.StoreStatusName[storeDetail.Status]) } checkTime := time.Now() if jxOrder.ExpectedDeliveredTime == nil { if storeDetail.Status != model.StoreStatusOpened { return nil, nil, fmt.Errorf("门店:%s不是营业状态,状态是:%s", storeDetail.Name, model.StoreStatusName[storeDetail.Status]) } } else { checkTime = *jxOrder.ExpectedDeliveredTime if utils.Time2Date(time.Now()).Sub(utils.Time2Date(checkTime)) > 24*time.Hour { return nil, nil, fmt.Errorf("预订单只能预定当天或第二天") } } if !isTimeInOpTime(storeDetail.OpenTime1, storeDetail.CloseTime1, storeDetail.OpenTime2, storeDetail.CloseTime2, checkTime) { return nil, nil, fmt.Errorf("门店:%s不在营业时间范围", storeDetail.Name) } skus := formalizeSkus(jxOrder.Skus) if len(skus) == 0 { return nil, nil, fmt.Errorf("商品列表为空") } var skuIDs []int for _, v := range skus { skuIDs = append(skuIDs, v.SkuID) } storeSkuList, err := dao.GetStoresSkusInfo(db, []int{jxOrder.StoreID}, skuIDs) if err != nil { return nil, nil, err } storeSkuMap := make(map[int]*model.StoreSkuBind) for _, v := range storeSkuList { storeSkuMap[v.SkuID] = v } skuList, err := dao.GetSkus(db, skuIDs, nil, nil, nil) if err != nil { return nil, nil, err } skuMap := make(map[int]*model.SkuAndName) for _, v := range skuList { skuMap[v.ID] = v } outJxOrder2 := *jxOrder outJxOrder2.Skus = nil outJxOrder2.OrderPrice = 0 outJxOrder = &outJxOrder2 outJxOrder.StoreName = storeDetail.Name for _, v := range skus { if storeSkuBind := storeSkuMap[v.SkuID]; storeSkuBind != nil { if sku := skuMap[v.SkuID]; sku != nil { jxSku := &JxSkuInfo{ SkuID: v.SkuID, Count: v.Count, Price: int64(storeSkuBind.JxPrice), SalePrice: int64(storeSkuBind.JxPrice), // todo 考虑活动价 Name: jxutils.ComposeSkuName(sku.Prefix, sku.Name, sku.Comment, sku.Unit, sku.SpecQuality, sku.SpecUnit, 0), } outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(v.Count) * jxSku.SalePrice } } } outJxOrder.TotalPrice = outJxOrder.OrderPrice + outJxOrder.FreightPrice outJxOrder.ActualPayPrice = outJxOrder.TotalPrice return outJxOrder, deliveryAddress, err } func jxOrder2GoodsOrder(ctx *jxcontext.Context, jxOrder *JxOrderInfo, deliveryAddress *dao.UserDeliveryAddressEx) (order *model.GoodsOrder, err error) { order = &model.GoodsOrder{ VendorOrderID: utils.Int64ToStr(jxOrder.OrderID), VendorID: model.VendorIDJX, VendorStoreID: utils.Int2Str(jxOrder.StoreID), StoreID: jxOrder.StoreID, StoreName: jxOrder.StoreName, UserID: ctx.GetUserID(), ConsigneeName: deliveryAddress.ConsigneeName, ConsigneeMobile: deliveryAddress.ConsigneeMobile, ConsigneeMobile2: deliveryAddress.ConsigneeMobile, ConsigneeAddress: deliveryAddress.DetailAddress, CoordinateType: model.CoordinateTypeMars, ConsigneeLng: jxutils.StandardCoordinate2Int(deliveryAddress.Lng), ConsigneeLat: jxutils.StandardCoordinate2Int(deliveryAddress.Lat), Status: model.OrderStatusUnknown, VendorStatus: "realnew", OrderSeq: 0, BuyerComment: jxOrder.BuyerComment, DeliveryType: model.OrderDeliveryTypeStoreSelf, StatusTime: time.Now(), } order.OrderCreatedAt = order.StatusTime order.VendorUserID = order.UserID if jxOrder.ExpectedDeliveredTime != nil { order.ExpectedDeliveredTime = *jxOrder.ExpectedDeliveredTime order.BusinessType = model.BusinessTypeDingshida } else { order.ExpectedDeliveredTime = utils.DefaultTimeValue order.BusinessType = model.BusinessTypeImmediate } for _, sku := range jxOrder.Skus { order.Skus = append(order.Skus, &model.OrderSku{ Count: sku.Count, VendorSkuID: utils.Int2Str(sku.SkuID), SkuID: sku.SkuID, SkuName: sku.Name, VendorPrice: sku.Price, SalePrice: sku.SalePrice, }) order.TotalShopMoney += int64(sku.Count) * sku.SalePrice } order.TotalShopMoney += jxOrder.FreightPrice order.ActualPayPrice = order.TotalShopMoney return order, err } func AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) { var status int if isAcceptIt { status = model.OrderStatusAccepted } else { status = model.OrderStatusCanceled } return changeOrderStatus(order.VendorOrderID, status, "") } func PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) { return changeOrderStatus(order.VendorOrderID, model.OrderStatusFinishedPickup, "") } func SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) { return changeOrderStatus(order.VendorOrderID, model.OrderStatusDelivering, "") } func SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) { return changeOrderStatus(order.VendorOrderID, model.OrderStatusFinished, "") } func CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) { if order.Status < model.OrderStatusDelivering { orderPay := &model.OrderPay{ VendorOrderID: order.VendorOrderID, VendorID: jxutils.GetPossibleVendorIDFromVendorOrderID(order.VendorOrderID), Status: model.PayStatusYes, } orderPay.DeletedAt = utils.DefaultTimeValue db := dao.GetDB() if err = dao.GetEntity(db, orderPay, "VendorOrderID", "VendorID", "Status", "DeletedAt"); err == nil || dao.IsNoRowsError(err) { if err == nil { refundID := utils.Int64ToStr(GenRefundID(order)) orderPayRefund, err2 := refundOrderByWX(ctx, orderPay, refundID) if err = err2; err == nil { dao.WrapAddIDCULDEntity(orderPayRefund, ctx.GetUserName()) err = dao.CreateEntity(dao.GetDB(), orderPay) } } else { if order.Status >= model.OrderStatusNew { globals.SugarLogger.Warnf("订单:%s找不到有效的支付方式", order.VendorOrderID) } } if err == nil { err = changeOrderStatus(order.VendorOrderID, model.OrderStatusCanceled, reason) } } } else { err = fmt.Errorf("当前订单状态:%s不允许取消", model.OrderStatusName[order.Status]) } return err } func changeOrderStatus(vendorOrderID string, status int, remark string) (err error) { orderStatus := &model.OrderStatus{ VendorOrderID: vendorOrderID, VendorID: model.VendorIDJX, OrderType: model.OrderTypeOrder, RefVendorOrderID: vendorOrderID, RefVendorID: model.VendorIDJX, VendorStatus: utils.Int2Str(status), Status: status, StatusTime: time.Now(), Remark: remark, } return partner.CurOrderManager.OnOrderStatusChanged(orderStatus) }