package localjx import ( "crypto/md5" "fmt" "math" "sort" "strings" "time" "git.rosy.net.cn/jx-callback/business/jxstore/event" "git.rosy.net.cn/jx-callback/globals" "git.rosy.net.cn/baseapi/platformapi/jdeclpapi" "git.rosy.net.cn/baseapi/platformapi/wxpayapi" "git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/business/jxutils/ddmsg" "git.rosy.net.cn/baseapi/platformapi/dingdingapi" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils/errlist" "git.rosy.net.cn/jx-callback/business/jxstore/cms" "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/business/partner/delivery" ) const ( OrderCreateTypePre = 0 // 预创建 OrderCreateTypeNormal = 1 // 正常创建 PayWaitingTime = 10 * time.Minute // 等待支付的最长时间 DingShiDaMinTime = 1 * time.Hour specialStoreID = 100274 specialFreightPrice = 1500 wxAppID = "wx4b5930c13f8b1170" autoCancelOrderReason = "支付超时,系统自动取消!" ) 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"` Weight int `json:"weight"` } type JxSkuInfoList []*JxSkuInfo func (l JxSkuInfoList) Len() int { return len(l) } func (l JxSkuInfoList) Less(i, j int) bool { if l[i].SkuID == l[j].SkuID { return l[i].SalePrice < l[j].SalePrice } return l[i].SkuID < l[j].SkuID } func (l JxSkuInfoList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } type JxOrderInfo struct { BuyerComment string `json:"buyerComment"` StoreID int `json:"storeID"` Skus []*JxSkuInfo `json:"skus"` ExpectedDeliveredTimestamp int64 `json:"expectedDeliveredTimestamp"` // 预期送达时间 TotalPrice int64 `json:"totalPrice"` // 单位为分 订单总价 FreightPrice int64 `json:"freightPrice"` // 单位为分 订单配送费 OrderPrice int64 `json:"orderPrice"` // 单位为分 订单商品价格 ActualPayPrice int64 `json:"actualPayPrice"` // 单位为分 顾客实际支付 OrderID int64 `json:"orderID"` StoreName string `json:"storeName"` Weight int `json:"weight"` FromStoreID int `json:"fromStoreID"` } type DeliveryTimeItem struct { ViewTime string `json:"viewTime"` UnixTime int64 `json:"unixTime"` ViewShippingFee string `json:"viewShippingFee"` } type DeliveryDayTimeInfo struct { Date string `json:"date"` TimeList []*DeliveryTimeItem `json:"timeList"` } type MatterOrderStatus struct { Time time.Time `json:"time"` Status string `json:"status"` Name string `json:"name"` Sign int `sign` } var ( orderNoBeginTimestamp int64 weekdayMap = map[int]string{ 1: "一", 2: "二", 3: "三", 4: "四", 5: "五", 6: "六", 7: "七", } dayList = []string{"今天", "明天", "后天"} ) func init() { orderNoBeginTimestamp = utils.Str2Time("2010-01-01 00:00:00").Unix() } func GetMyOrders(ctx *jxcontext.Context, fromDateStr, toDateStr string, params map[string]interface{}, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) { db := dao.GetDB() params["vendorIDs"] = string(utils.MustMarshal([]int{model.VendorIDJX})) tmpOrderList, totalCount, err := dao.GetOrders(db, nil, false, false, fromDateStr, toDateStr, false, nil, false, ctx.GetUserID(), params, offset, pageSize) if err == nil { pagedInfo = &model.PagedInfo{ TotalCount: totalCount, } if totalCount > 0 { var ids []int64 for _, v := range tmpOrderList { ids = append(ids, v.ID) } orderSkuList, _, err2 := dao.GetOrders(db, ids, true, false, "", "", false, nil, false, "", nil, 0, model.UnlimitedPageSize) if err = err2; err == nil { orderMap := make(map[string]*model.GoodsOrderExt) var orderList []*model.GoodsOrderExt for _, v := range orderSkuList { universalOrderID := jxutils.ComposeUniversalOrderID(v.VendorOrderID, v.VendorID) if orderMap[universalOrderID] == nil { orderMap[universalOrderID] = v orderList = append(orderList, v) } orderMap[universalOrderID].SkuList = append(orderMap[universalOrderID].SkuList, &v.ShortSkuInfo) } pagedInfo.Data = orderList } else { pagedInfo = nil } } } return pagedInfo, err } func GetMyOrderCountInfo(ctx *jxcontext.Context, fromDate, toDate time.Time, statuss []int) (countInfo []*model.GoodsOrderCountInfo, err error) { countInfo, err = dao.GetMyOrderCountInfo(dao.GetDB(), ctx.GetUserID(), fromDate, toDate, statuss) return countInfo, err } func CreateOrder(ctx *jxcontext.Context, jxOrder *JxOrderInfo, addressID int64, createType int, fromStoreID int) (outJxOrder *JxOrderInfo, err error) { outJxOrder, deliveryAddress, err := generateOrder(ctx, jxOrder, addressID, fromStoreID) 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 { order.Status = model.OrderStatusWait4Pay callNewOrder(order) } } return outJxOrder, err } // 买家取消(或申请取消)订单 func BuyerCancelOrder(ctx *jxcontext.Context, orderID int64, reason string) (canceled bool, err error) { order, err := partner.CurOrderManager.LoadOrder(utils.Int64ToStr(orderID), model.VendorIDJX) if err == nil { if order.Status < model.OrderStatusNew { order.Status = model.OrderStatusCanceled order.VendorStatus = utils.Int2Str(model.OrderStatusCanceled) if err = partner.CurOrderManager.UpdateOrderFields(order, []string{model.FieldStatus, "VendorStatus"}); err == nil { canceled = true } } else { err = fmt.Errorf("暂不支持自行取消订单,请联系商家取消") // err = changeOrderStatus(utils.Int64ToStr(orderID), model.OrderStatusApplyCancel, fmt.Sprintf("用户%s主动取消", ctx.GetUserName())) } } return canceled, 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: if orderPay, err = pay4OrderByWX(ctx, order, vendorPayType); err == nil { dao.WrapAddIDCULDEntity(orderPay, ctx.GetUserName()) err = dao.CreateEntity(dao.GetDB(), orderPay) } case model.PayTypeTL: if orderPay, err = pay4OrderByTL(ctx, order, vendorPayType); err == nil { dao.WrapAddIDCULDEntity(orderPay, ctx.GetUserName()) err = dao.CreateEntity(dao.GetDB(), orderPay) } default: err = fmt.Errorf("支付方式:%d当前不支持", payType) } } return orderPay, err } func time2ShortTimeStr(t time.Time) string { return t.Format("15:04") } func GetAvailableDeliverTime(ctx *jxcontext.Context, storeID int) (deliverTimerList []*DeliveryDayTimeInfo, err error) { db := dao.GetDB() storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJX) if err != nil { return nil, err } if storeDetail.Status != model.StoreStatusOpened { return nil, fmt.Errorf("门店:%s不是营业状态,状态是:%s", storeDetail.Name, model.StoreStatusName[storeDetail.Status]) } // now := utils.Str2Time(timeStr) now := time.Now() // beginDate := utils.Time2Date(utils.Str2Time(timeStr)) beginDate := utils.Time2Date(now) minDingShiDaTime := now.Add(DingShiDaMinTime) viewShippingFee := "约6.6元配送费" if storeID == specialStoreID { viewShippingFee = "免费配送" } var isOrder = false if storeDetail.IsOrder == model.YES { isOrder = true beginDate = beginDate.AddDate(0, 0, 1) } for i, dayStr := range dayList { if isOrder { isOrder = false continue } openTime1 := jxutils.JxOperationTime2TimeByDate(storeDetail.OpenTime1, beginDate) closeTime1 := jxutils.JxOperationTime2TimeByDate(storeDetail.CloseTime1, beginDate) openTime2 := jxutils.JxOperationTime2TimeByDate(storeDetail.OpenTime2, beginDate) closeTime2 := jxutils.JxOperationTime2TimeByDate(storeDetail.CloseTime2, beginDate) timeInfo := &DeliveryDayTimeInfo{ Date: fmt.Sprintf("%s(周%s)", dayStr, weekdayMap[int(beginDate.Weekday())]), } if i == 0 { if isTimeInOpTime(storeDetail.OpenTime1, storeDetail.CloseTime1, storeDetail.OpenTime2, storeDetail.CloseTime2, now) { timeInfo.TimeList = append(timeInfo.TimeList, &DeliveryTimeItem{ ViewTime: "立即送出", UnixTime: 0, ViewShippingFee: viewShippingFee, }) } } deliverTimerList = append(deliverTimerList, timeInfo) for j := 0; j < 24*3; j++ { deliveryTime := beginDate.Add(time.Duration(j) * 20 * time.Minute) if deliveryTime.Sub(minDingShiDaTime) >= 0 { if (deliveryTime.Sub(openTime1) >= 0 && deliveryTime.Sub(closeTime1) <= 0) || (storeDetail.OpenTime2 > 0 && deliveryTime.Sub(openTime2) >= 0 && deliveryTime.Sub(closeTime2) <= 0) { timeInfo.TimeList = append(timeInfo.TimeList, &DeliveryTimeItem{ ViewTime: time2ShortTimeStr(deliveryTime), UnixTime: deliveryTime.Unix(), ViewShippingFee: viewShippingFee, }) } } } beginDate = beginDate.Add(24 * time.Hour) } return deliverTimerList, 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) if count, err2 := dao.GetJxOrderCount(db, jxutils.GetSaleStoreIDFromOrder(order), order.VendorOrderID, order.OrderCreatedAt); err2 == nil { order.OrderSeq = count + 1 partner.CurOrderManager.UpdateOrderFields(order, []string{"OrderSeq"}) } order.Status = model.OrderStatusNew order.VendorStatus = utils.Int2Str(model.OrderStatusNew) order.StatusTime = *orderPay.PayFinishedAt err = callNewOrder(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 GenPayOrderID(order *model.GoodsOrder) (payOrderID int64) { return utils.Str2Int64(order.VendorOrderID) } func GenRefundID(order *model.GoodsOrder) (refundID int64) { const suffix = 100000 refundID = utils.Str2Int64(order.VendorOrderID) * suffix refundID += 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, fromStoreID int) (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的配送范围", storeDetail.Name) } // 营业状态及时间检查 if storeDetail.Status != model.StoreStatusOpened { // model.StoreStatusDisabled { return nil, nil, fmt.Errorf("门店:%s状态是:%s", storeDetail.Name, model.StoreStatusName[storeDetail.Status]) } checkTime := time.Now() if jxOrder.ExpectedDeliveredTimestamp == 0 { if storeDetail.Status != model.StoreStatusOpened { return nil, nil, fmt.Errorf("门店:%s不是营业状态,状态是:%s", storeDetail.Name, model.StoreStatusName[storeDetail.Status]) } } else { checkTime = utils.Timestamp2Time(jxOrder.ExpectedDeliveredTimestamp) if checkTime.Sub(time.Now()) < DingShiDaMinTime { return nil, nil, fmt.Errorf("预订单只能在1小时后") } 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) // } outJxOrder2 := *jxOrder outJxOrder2.Skus = nil outJxOrder2.OrderPrice = 0 outJxOrder2.Weight = 0 outJxOrder = &outJxOrder2 outJxOrder.StoreName = storeDetail.Name skus := formalizeSkus(jxOrder.Skus) // 允许空商品列表(一般用于测试配送地址,门店信息是否合适) if len(skus) > 0 { var skuIDs []int for _, v := range skus { skuIDs = append(skuIDs, v.SkuID) } storeSkuInfo, err := cms.GetStoreSkus(ctx, jxOrder.StoreID, skuIDs, true, "", true, false, map[string]interface{}{ "actVendorID": model.VendorIDJX, }, 0, model.UnlimitedPageSize) if err != nil { return nil, nil, err } storeSkuMap := make(map[int]*dao.StoreSkuExt) for _, v1 := range storeSkuInfo.SkuNames { for _, v2 := range v1.Skus { storeSkuMap[v2.SkuID] = v2 } } skuList, err := dao.GetSkus(db, skuIDs, nil, nil, nil, nil) if err != nil { return nil, nil, err } skuMap := make(map[int]*model.SkuAndName) for _, v := range skuList { if fromStoreID != 0 { if v.EclpID == "" { return nil, nil, fmt.Errorf("此商品物料编码为空,请联系管理员!skuID:[%v]", v.ID) } } skuMap[v.ID] = v } for _, v := range skus { if storeSkuBind := storeSkuMap[v.SkuID]; storeSkuBind != nil { if sku := skuMap[v.SkuID]; sku != nil { jxSku := &JxSkuInfo{ SkuID: v.SkuID, Price: int64(storeSkuBind.JxPrice), Count: v.Count, SalePrice: int64(storeSkuBind.JxPrice), // todo 考虑活动价 Weight: sku.Weight, Name: jxutils.ComposeSkuName(sku.Prefix, sku.Name, sku.Comment, sku.Unit, sku.SpecQuality, sku.SpecUnit, 0, sku.ExPrefix, sku.ExPrefixBegin, sku.ExPrefixEnd), } if storeSkuBind.ActPrice != 0 && storeSkuBind.ActPrice < storeSkuBind.JxPrice { jxSku.SalePrice = int64(storeSkuBind.ActPrice) jxSku.Count = 1 outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice outJxOrder.Weight += jxSku.Count * jxSku.Weight if v.Count-1 > 0 { jxSku2 := *jxSku jxSku2.SalePrice = jxSku.Price jxSku2.Count = v.Count - 1 jxSku = &jxSku2 } else { jxSku = nil } } if jxSku != nil { outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice outJxOrder.Weight += jxSku.Count * jxSku.Weight } } } } if fromStoreID == 0 { sort.Sort(JxSkuInfoList(outJxOrder.Skus)) outJxOrder.FreightPrice, _, err = delivery.CalculateDeliveryFee(dao.GetDB(), jxOrder.StoreID, "", jxutils.StandardCoordinate2Int(deliveryAddress.Lng), jxutils.StandardCoordinate2Int(deliveryAddress.Lat), model.CoordinateTypeMars, outJxOrder.Weight, checkTime) } // if jxOrder.StoreID == specialStoreID { // outJxOrder.FreightPrice = 0 // } } else { outJxOrder.FreightPrice = 0 } //表示此订单为物料配送订单 if fromStoreID != 0 { storeDetail2, err2 := dao.GetStoreDetail(db, fromStoreID, model.VendorIDJX) if err = err2; err != nil { return nil, nil, fmt.Errorf("fromStoreID有误,[%v]", fromStoreID) } deliveryAddress.ConsigneeName = storeDetail2.Name outJxOrder.FromStoreID = fromStoreID if outJxOrder.Weight <= 2000 { outJxOrder.FreightPrice = 500 } else { outJxOrder.FreightPrice = utils.Float64TwoInt64(500 + math.Ceil((utils.Int2Float64(outJxOrder.Weight)-2000)/1000)*200) } } else { if outJxOrder.FreightPrice > specialFreightPrice { outJxOrder.FreightPrice = specialFreightPrice } if outJxOrder.OrderPrice >= int64(storeDetail.DeliveryFeeDeductionSill) { outJxOrder.FreightPrice -= int64(storeDetail.DeliveryFeeDeductionFee) if outJxOrder.FreightPrice < 0 { outJxOrder.FreightPrice = 0 } } } if err == nil { outJxOrder.TotalPrice = outJxOrder.OrderPrice + outJxOrder.FreightPrice outJxOrder.ActualPayPrice = outJxOrder.TotalPrice } else { outJxOrder = nil deliveryAddress = nil } 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: fmt.Sprintf("%s%s", deliveryAddress.Address, 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.ExpectedDeliveredTimestamp != 0 { order.ExpectedDeliveredTime = utils.Timestamp2Time(jxOrder.ExpectedDeliveredTimestamp) order.BusinessType = model.BusinessTypeDingshida } else { order.ExpectedDeliveredTime = order.OrderCreatedAt.Add(time.Hour) 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 if jxOrder.FromStoreID != 0 { order.FromStoreID = jxOrder.FromStoreID order.WaybillVendorID = model.VendorIDJDWL } 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 AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) { return err } func PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) { err = changeOrderStatus(order.VendorOrderID, model.OrderStatusFinishedPickup, "") err = orderSolutionForWuLiao(order) return err } func orderSolutionForWuLiao(order *model.GoodsOrder) (err error) { if order.FromStoreID != 0 { var ( goodsNos []string prices []string quantities []string db = dao.GetDB() ) err = changeOrderStatus(order.VendorOrderID, model.OrderStatusDelivering, "") goods, err := dao.QueryOrders(db, order.VendorOrderID, -1, []int{model.VendorIDJX}, -1, utils.ZeroTimeValue, utils.ZeroTimeValue) if err != nil || len(goods) == 0 { return err } for _, v := range goods[0].Skus { skus, err := dao.GetSkus(db, []int{v.SkuID}, nil, nil, nil, nil) if err != nil || len(skus) == 0 { continue } goodsNos = append(goodsNos, skus[0].EclpID) prices = append(prices, "0") quantities = append(quantities, utils.Int2Str(v.Count)) } result, err := api.JdEclpAPI.AddOrder(&jdeclpapi.AddOrderParam{ IsvUUID: order.VendorOrderID, IsvSource: jdeclpapi.IsvSource, ShopNo: jdeclpapi.ShopNo, DepartmentNo: jdeclpapi.DepartmentNo, WarehouseNo: jdeclpapi.WarehouseNo, SalesPlatformOrderNo: order.VendorOrderID, SalePlatformSource: jdeclpapi.SalePlatformSource, ConsigneeName: order.ConsigneeName, ConsigneeMobile: order.ConsigneeMobile, ConsigneeAddress: order.ConsigneeAddress, OrderMark: jdeclpapi.OrderMark, GoodsNo: strings.Join(goodsNos, ","), Price: strings.Join(prices, ","), Quantity: strings.Join(quantities, ","), }) if err != nil { return err } order.EclpOutID = result dao.UpdateEntity(db, order, "EclpOutID") //刷新一下库存 for _, v := range goods[0].Skus { cms.RefreshMatterStock(jxcontext.AdminCtx, v.SkuID) } } return err } 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 true { //order.Status < model.OrderStatusDelivering { errList := errlist.New() db := dao.GetDB() payList, err2 := dao.GetOrderPayList(db, order.VendorOrderID, jxutils.GetPossibleVendorIDFromVendorOrderID(order.VendorOrderID)) if err = err2; err == nil { for _, orderPay := range payList { if orderPay.Status == model.PayStatusYes { // refundID := utils.Int64ToStr(GenRefundID(order)) refundID := order.VendorOrderID var orderPayRefund *model.OrderPayRefund if orderPay.PayType == model.PayTypeWX { orderPayRefund, err = refundOrderByWX(ctx, orderPay, refundID, orderPay.TotalFee, reason) if err == nil { dao.WrapAddIDCULDEntity(orderPayRefund, ctx.GetUserName()) errList.AddErr(dao.CreateEntity(dao.GetDB(), orderPayRefund)) MarkArrears(db, order, orderPay) CancelMatterOrder(db, order) } else { errList.AddErr(err) } } else if orderPay.PayType == model.PayTypeTL { orderPayRefund, err = refundOrderByTL(ctx, orderPay, refundID, orderPay.TotalFee, reason) if err == nil { MarkArrears(db, order, orderPay) CancelMatterOrder(db, order) } else { errList.AddErr(err) } } } else { orderPay.Status = model.PayStatusCanceled _, err2 := dao.UpdateEntity(db, orderPay) errList.AddErr(err2) } } } else if dao.IsNoRowsError(err) { err = nil } else { errList.AddErr(err) } if errList.GetErrListAsOne() == nil { errList.AddErr(changeOrderStatus(order.VendorOrderID, model.OrderStatusCanceled, reason)) } err = errList.GetErrListAsOne() } else { err = fmt.Errorf("当前订单状态:%s不允许取消", model.OrderStatusName[order.Status]) } return err } func CancelMatterOrder(db *dao.DaoDB, order *model.GoodsOrder) (err error) { if order.FromStoreID != 0 { _, err = api.JdEclpAPI.CancelOrder(order.EclpOutID) } return err } func MarkArrears(db *dao.DaoDB, order *model.GoodsOrder, orderPay *model.OrderPay) { //退款后,若此订单下单用户有推广人,则需要将分给推广人的金额记录到该推广人的欠款中 orders, _ := dao.QueryOrders(db, order.VendorOrderID, 0, []int{model.VendorIDJX}, 0, utils.DefaultTimeValue, utils.DefaultTimeValue) if len(orders) > 0 { user, _ := dao.GetUserByID(db, "user_id", orders[0].UserID) if user.ParentMobile != "" { user2, _ := dao.GetUserByID(db, "mobile", user.ParentMobile) user2.Arrears = user2.Arrears + (orderPay.TotalFee * user2.DividePercentage / 100) dao.UpdateEntity(db, user2, "Arrears") if user2.ParentMobile != "" { user3, _ := dao.GetUserByID(db, "mobile", user2.ParentMobile) user3.Arrears = user3.Arrears + ((orderPay.TotalFee - user2.Arrears) * user3.DividePercentage / 100) dao.UpdateEntity(db, user3, "Arrears") } } } } func AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAgree bool, reason string) (err error) { if isAgree { err = CancelOrder(ctx, order, reason) } else { err = changeOrderStatus(order.VendorOrderID, model.OrderStatusVendorRejectCancel, reason) } return err } // todo 消息用异步可能导致丢失,单同步又有重入相关的问题 func callNewOrder(order *model.GoodsOrder) (err error) { jxutils.CallMsgHandlerAsync(func() { err = partner.CurOrderManager.OnOrderNew(order, model.Order2Status(order)) }, jxutils.ComposeUniversalOrderID(order.VendorOrderID, model.VendorIDJX)) 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, } jxutils.CallMsgHandlerAsync(func() { err = partner.CurOrderManager.OnOrderStatusChanged("", orderStatus) }, jxutils.ComposeUniversalOrderID(vendorOrderID, model.VendorIDJX)) return err } func GetOrderPay(ctx *jxcontext.Context, vendorOrderID string) (payList []*model.OrderPay, err error) { db := dao.GetDB() payList, err = dao.GetOrderPayList(db, vendorOrderID, jxutils.GetPossibleVendorIDFromVendorOrderID(vendorOrderID)) return payList, err } func PayForPopluarMan(ctx *jxcontext.Context, vendorOrderID, userID string, price int) (err error) { db := dao.GetDB() user, err := dao.GetUserByID(db, "user_id", userID) if user == nil { return fmt.Errorf("未找到此用户!用户ID:[%v]\n", userID) } auth, err := dao.GetUserBindAuthInfo(db, userID, model.AuthBindTypeAuth, []string{"weixinmini"}, "", "", "wx4b5930c13f8b1170") if len(auth) == 0 { return fmt.Errorf("未找到此用户的微信验证方式!用户ID:[%v]\n", userID) } goods, err := dao.QueryOrders(db, vendorOrderID, 0, []int{model.VendorIDJX}, 0, utils.ZeroTimeValue, utils.ZeroTimeValue) if len(goods) == 0 { return fmt.Errorf("未找到此订单!订单ID:[%v]\n", vendorOrderID) } param := &wxpayapi.TransfersParam{ CheckName: wxpayapi.CheckName, PartnerTradeNo: vendorOrderID, Desc: "每日推广人订单分成分到个人", SpbillCreateIP: ctx.GetRealRemoteIP(), OpenID: auth[0].AuthID, Amount: price, } _, err = api.WxpayAPI.Transfers(param) return err } //自动打款给市场推广人 func AutoPayForPopluarMan(ctx *jxcontext.Context) (err error) { var ( errMsg string errCode string db = dao.GetDB() fromDateStr = time.Now().AddDate(0, 0, -1).Format("2006-1-2") + " 00:00:00" toDateStr = time.Now().AddDate(0, 0, -1).Format("2006-1-2") + " 23:59:59" mapResult = make(map[string]interface{}) ) result, err := dao.GetOrdersForJxPay(db, utils.Str2Time(fromDateStr), utils.Str2Time(toDateStr)) for _, goods := range result { var ( param = &wxpayapi.TransfersParam{ CheckName: wxpayapi.CheckName, Desc: "每日推广人订单分成分到个人", SpbillCreateIP: ctx.GetRealRemoteIP(), } payPrice1 int payPrice2 int ) user, err := dao.GetUserByID(db, "user_id", goods.UserID) if user.ParentMobile == "" { return err } user2, err := dao.GetUserByID(db, "mobile", user.ParentMobile) auths, err := dao.GetUserBindAuthInfo(db, user2.UserID, model.AuthBindTypeAuth, []string{"weixinmini"}, "", "", "wx4b5930c13f8b1170") if err != nil { return err } if len(auths) == 0 { errMsg += fmt.Sprintf("打款失败!未找到此用户的微信验证方式!订单号:[%v],用户ID:[%v]\n", goods.VendorOrderID, user2.UserID) } else { var openID string for _, auth := range auths { if auth.TypeID == wxAppID { openID = auth.AuthID } } payPrice1 = int(goods.ActualPayPrice) * user2.DividePercentage / 100 //表示这个人之前有欠款,意思是取消订单退款时,这个推广人的分成收不回来,算作欠款,打钱的时候要扣除 //表示这个人之前有小于3毛钱的款没有打(微信付款api付款最低3毛),记录下来加到以后的款项中 rPrice := payPrice1 - user2.Arrears + user2.Profit err = updateUserAndTransfers(db, param, user2, openID, rPrice) if err != nil { errMsg += err.Error() } mapResult["打款人1"] = user2.Name mapResult["打款人金额1"] = rPrice mapResult["打款人电话1"] = user2.Mobile mapResult["打款人1userID"] = user2.UserID } if user2.ParentMobile != "" { user3, err := dao.GetUserByID(db, "mobile", user2.ParentMobile) auths, err := dao.GetUserBindAuthInfo(db, user3.UserID, model.AuthBindTypeAuth, []string{"weixinmini"}, "", "", "wx4b5930c13f8b1170") if err != nil { return err } if len(auths) == 0 { errMsg += fmt.Sprintf("打款失败!未找到此用户的微信验证方式!订单号:[%v],用户ID:[%v]\n", goods.VendorOrderID, user3.UserID) } else { var openID string for _, auth := range auths { if auth.TypeID == wxAppID { openID = auth.AuthID } } payPrice2 = (int(goods.ActualPayPrice) - payPrice1) * user3.DividePercentage / 100 rPrice := payPrice2 - user3.Arrears + user3.Profit err = updateUserAndTransfers(db, param, user3, openID, rPrice) if err != nil { errMsg += err.Error() } mapResult["打款人2"] = user3.Name mapResult["打款人金额2"] = rPrice mapResult["打款人电话2"] = user3.Mobile mapResult["打款人2userID"] = user3.UserID } } } user, _ := dao.GetUserByID(dao.GetDB(), "mobile", "18160030913") if user != nil && errMsg != "" { ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "每日打款错误", errMsg) } if err != nil || errMsg != "" { errMsg += err.Error() errCode = model.ErrCodeGeneralFailed } else { errCode = model.ErrCodeSuccess } err = event.AddOperateEvent(ctx, ctx.GetTrackInfo(), cms.BuildDiffData(mapResult), errCode, errMsg, 0, "AutoPayForPopluarMan") globals.SugarLogger.Debugf("每日订单打款:[%v]", cms.BuildDiffData(mapResult)) return err } func updateUserAndTransfers(db *dao.DaoDB, param *wxpayapi.TransfersParam, user *model.User, authID string, rPrice int) (err error) { if rPrice >= 30 { param.OpenID = authID param.Amount = rPrice param.PartnerTradeNo = utils.GetUUID() _, err = api.WxpayAPI.Transfers(param) user.ProfitSum = user.ProfitSum + rPrice user.Profit = 0 user.Arrears = 0 } else if rPrice >= 0 && rPrice < 30 { user.Profit = rPrice user.Arrears = 0 } else { user.Profit = 0 user.Arrears = int(utils.Float64TwoInt64(math.Abs(utils.Int2Float64(rPrice)))) } _, err = dao.UpdateEntity(db, user, "ProfitSum", "Profit", "Arrears") return err } func CancelPayTimeOutOrder(ctx *jxcontext.Context) (err error) { db := dao.GetDB() var orders []*model.GoodsOrder sql := ` SELECT * FROM goods_order WHERE order_created_at >= ? AND order_created_at <= NOW() AND status = ? AND vendor_id = ? ` sqlParams := []interface{}{ time.Now().Add(-time.Minute * 30), model.OrderStatusWait4Pay, model.VendorIDJX, } err = dao.GetRows(db, &orders, sql, sqlParams) for _, v := range orders { if v.OrderCreatedAt.Add(PayWaitingTime).Before(time.Now()) { err2 := CancelOrder(ctx, v, autoCancelOrderReason) err = err2 } } return err } func GetHalfHoursList() (strs []string) { for k := 0; k < 3; k++ { for i := 0; i < 10; i++ { for j := 0; j < 4; j += 3 { if k == 2 && i > 3 { break } strs = append(strs, utils.Int2Str(k)+utils.Int2Str(i)+":"+utils.Int2Str(j)+"0"+":00") } } } return strs } func GetMatterOrderStatus(ctx *jxcontext.Context, vendorOrderID string) (result []*MatterOrderStatus, err error) { var ( db = dao.GetDB() ) order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, model.VendorIDJX) if err != nil { return nil, err } if order.EclpOutID == "" { return nil, err } queryOrderStatus, err := api.JdEclpAPI.QueryOrderStatus(order.EclpOutID) getTrackMessagePlusByOrderResult, err := api.JdEclpAPI.GetTrackMessagePlusByOrder(vendorOrderID) for _, v := range queryOrderStatus.OrderStatusList { matter := &MatterOrderStatus{} matter.Time = utils.Str2Time(v.OperateTime) matter.Status = v.SoStatusName matter.Name = v.SoStatusName matter.Sign = 1 result = append(result, matter) } if len(getTrackMessagePlusByOrderResult.ResultData) > 0 { for _, vv := range getTrackMessagePlusByOrderResult.ResultData { matter := &MatterOrderStatus{} matter.Time = utils.Str2Time(strings.ReplaceAll(vv.OpeTime, "/", "-")) matter.Status = vv.OpeTitle matter.Name = vv.OpeRemark matter.Sign = 2 result = append(result, matter) } } for i := 0; i < len(result)-1; i++ { for j := 0; j < len(result)-i-1; j++ { if result[j].Time.Unix() > result[j+1].Time.Unix() || (result[j].Time.Unix() == result[j+1].Time.Unix() && result[j].Sign > result[j+1].Sign) { tmp := result[j] result[j] = result[j+1] result[j+1] = tmp } } } if len(queryOrderStatus.OrderStatusList) > 0 { if queryOrderStatus.OrderStatusList[len(queryOrderStatus.OrderStatusList)-1].SoStatusCode == jdeclpapi.SoStatusCode10034 { dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() order.Status = model.OrderStatusFinished dao.UpdateEntity(db, order, "Status") dao.Commit(db) } } return result, err }