package localjx import ( "crypto/md5" "fmt" "math" "regexp" "strings" "time" "git.rosy.net.cn/baseapi/platformapi/jdshopapi" "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" "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/jx-callback/business/jxutils/netprinter" "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" ) const ( OrderCreateTypePre = 0 // 预创建 OrderCreateTypeNormal = 1 // 正常创建 PayWaitingTime = 10 * time.Minute // 等待支付的最长时间 DingShiDaMinTime = 1 * time.Hour specialStoreID = 100274 specialFreightPrice = 1500 wxAppID = "wx4b5930c13f8b1170" autoCancelOrderReason = "支付超时,系统自动取消!" cancelMatterOrderReason = "失败重发!" splitMatterOrderMinWeight = 4500 //物料订单分包最少要4.5kg jxwxfMatterEclpID = "EMG4418113943423" //京西五香粉物料编码 ) 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"` GroupSign bool `json:"groupSign"` } type JxSkuInfo2 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"` GroupSign bool `json:"groupSign"` } 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"` EarningType int `json:"earningType"` OrderType int `json:"orderType"` } 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: "六", 0: "日", } dayList = []string{"今天", "明天", "后天"} bagMap = map[int]int{ 6039382: 100, 6039383: 200, 6039384: 200, 6039387: 200, 6039390: 200, } regexpCnameAndCmobile = regexp.MustCompile(`配送员,(.*),手机号,(.*)`) regexpCnameAndCmobile2 = regexp.MustCompile(`(快递员:(.*),联系电话:(.*))`) bagSkuMap = map[int]int{ //京西物料袋子skuid 6039382: 6039382, 6039383: 6039383, 6039384: 6039384, 6039387: 6039387, 6039390: 6039390, } ) 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 GetMyAfsOrders(ctx *jxcontext.Context, vendorOrderID, afsOrderID, userID, fromTime, toTime string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) { afsOrderList, totalCount, err := dao.GetAfsOrdersByPage(dao.GetDB(), vendorOrderID, afsOrderID, userID, utils.Str2Time(fromTime), utils.Str2Time(toTime), offset, pageSize) pagedInfo = &model.PagedInfo{ TotalCount: totalCount, Data: afsOrderList, } 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 } //fromStoreID 为0 表示非物料订单(京西商城订单等) //fromStoreID 为 门店ID ,表示是物料订单,fromStoreID表示是哪个门店申请的物料,或者进货方门店 //fromStoreID 为-1 表示也是物料订单,但是不是门店申请,是个人申请的 //fromStoreID 在后面 generateOrder中有用 func CreateOrder(ctx *jxcontext.Context, jxOrder *JxOrderInfo, addressID int64, createType int, fromStoreID int, IsDeliverySelf bool) (outJxOrder *JxOrderInfo, err error) { outJxOrder, deliveryAddress, err := generateOrder(ctx, jxOrder, addressID, fromStoreID, "", IsDeliverySelf) if err != nil { return nil, err } if fromStoreID != 0 { checkMatterDeliveryAddress(deliveryAddress) } if createType != OrderCreateTypePre { if outJxOrder.TotalPrice != jxOrder.TotalPrice { return nil, fmt.Errorf("商品或配送信息发生改变,请重新下单") } outJxOrder.OrderID = GenOrderNo(ctx) order, err2 := jxOrder2GoodsOrder(ctx, outJxOrder, deliveryAddress, "", IsDeliverySelf) if err = err2; err == nil { order.AddressID = addressID 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 && orderPay != 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, }) } } 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, }) } } } if len(timeInfo.TimeList) > 0 { deliverTimerList = append(deliverTimerList, timeInfo) } beginDate = beginDate.Add(24 * time.Hour) } if len(deliverTimerList) > 0 { if deliverTimerList[0].TimeList[0].UnixTime != 0 { deliverTimerList[0].TimeList[0].ViewTime = "营业即送" } } 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) //如果是物料的订单,直接到拣货完成,配送中的状态 // if order.OrderType != model.OrderTypeNormal { if order.FromStoreID != 0 { netprinter.PrintOrderByOrder(jxcontext.AdminCtx, order) PickupGoods(order, false, "jxadmin") } // } } 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 GenAfsOrderNo(ctx *jxcontext.Context) (orderNo int64) { const prefix = 80 const randPartNum = 100 orderNo = time.Now().Unix() - orderNoBeginTimestamp 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, userID string, IsDeliverySelf bool) (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 } var addressList []*dao.UserDeliveryAddressEx if userID == "" { addressList, _, err = dao.QueryUserDeliveryAddress(db, addressID, nil, 0, 0) } else { addressList, _, err = dao.QueryUserDeliveryAddress(db, addressID, []string{userID}, 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.PayPercentage < 50 { jxOrder.EarningType = model.EarningTypePoints } else { jxOrder.EarningType = model.EarningTypeQuote } // 营业状态及时间检查 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("预订单只能预定当天或第二天") } } 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 { if v2.StoreSkuStatus != model.SkuStatusNormal { return nil, nil, fmt.Errorf("此商品已下架,请联系管理员!skuID:[%v]", v2.SkuID) } 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 jxOrder.OrderType == model.OrderTypeMatter { if v.EclpID == "" { return nil, nil, fmt.Errorf("此商品物料编码为空,请联系管理员!skuID:[%v]", v.ID) } } skuMap[v.ID] = v } var ( result *orderman.OrderCount sum int //申请物料的店的最近销量,以下会根据销量计算具体袋子的价格 flag = false //新店袋子拆分当个参数 ) if jxOrder.OrderType == model.OrderTypeMatter && fromStoreID != -1 { result, _ = orderman.GetMatterStoreOrderCount(nil, fromStoreID) sum = result.Count } if jxOrder.Weight == 0 { for _, v := range jxOrder.Skus { v.Weight = storeSkuMap[v.SkuID].Weight jxOrder.Weight += v.Weight * v.Count } } for _, v := range skus { if storeSkuBind := storeSkuMap[v.SkuID]; storeSkuBind != nil { if jxOrder.OrderType == model.OrderTypeMatter { result2, _ := api.JdEclpAPI.QueryStock(storeSkuBind.EclpID) if len(result2) > 0 { if result2[0].UsableNum < v.Count { return nil, nil, fmt.Errorf("此商品库存不足无法购买,请联系管理员!skuID:[%v]", v.SkuID) } } } 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 fromStoreID != -1 { //活动商品要拆分,一分钱的单独列一个(count为1),正常价格的列在一起(count叠加) 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 { if jxOrder.OrderType != model.OrderTypeMatter || (jxOrder.OrderType == model.OrderTypeMatter && fromStoreID == -1) { outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice } else { //以下else为物料订单袋子金额和数量处理 if !result.Flag { //只要flag是false就按原价申请,是true再按订单量 outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice } else { if result.Count == 0 { //这个条件被认为是新店,袋子限量 if bagMap[jxSku.SkuID] != 0 { //如果他买了袋子,第一个袋子算1分钱,其余按原价(包括所有袋子) if !flag { salePirce := jxSku.SalePrice count := jxSku.Count jxSku.SalePrice = 1 jxSku.Count = 1 outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice if count > 1 { jxSku2 := *jxSku jxSku2.SalePrice = salePirce jxSku2.Count = count - 1 outJxOrder.Skus = append(outJxOrder.Skus, &jxSku2) outJxOrder.OrderPrice += int64(jxSku2.Count) * jxSku2.SalePrice outJxOrder.Weight += jxSku2.Count * jxSku2.Weight } flag = true } else { outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice } } else { outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice } } else { //袋子总数小于等于销量就是1分钱,只要大于销量其他就按原价 //这个袋子规格是一份100个 if jxSku.SkuID == 6039382 { if bagMap[jxSku.SkuID] != 0 { if sum > 0 { if bagMap[jxSku.SkuID]*jxSku.Count <= sum+100 { jxSku.SalePrice = 1 outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(1 * jxSku.Count) } else { jxSku2 := *jxSku jxSku2.SalePrice = jxSku.SalePrice jxSku2.Count = int(int64(jxSku.Count) - utils.Float64TwoInt64(math.Ceil(utils.Int2Float64(sum)/100))) outJxOrder.Weight += jxSku2.Count * jxSku2.Weight jxSku.SalePrice = 1 jxSku.Count = int(utils.Float64TwoInt64(math.Ceil(utils.Int2Float64(sum) / 100))) outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.Skus = append(outJxOrder.Skus, &jxSku2) outJxOrder.OrderPrice += jxSku.SalePrice * int64(jxSku.Count) outJxOrder.OrderPrice += jxSku2.SalePrice * int64(jxSku2.Count) } } else { outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice } sum = sum - bagMap[jxSku.SkuID]*jxSku.Count } } else if jxSku.SkuID == 6039383 || jxSku.SkuID == 6039384 || jxSku.SkuID == 6039387 || jxSku.SkuID == 6039390 { //这些袋子是一份200个 if bagMap[jxSku.SkuID] != 0 { if sum > 0 { if bagMap[jxSku.SkuID]*jxSku.Count <= sum+200 { jxSku.SalePrice = 1 outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(1 * jxSku.Count) } else { jxSku2 := *jxSku jxSku2.SalePrice = jxSku.SalePrice jxSku2.Count = int(int64(jxSku.Count) - utils.Float64TwoInt64(math.Ceil(utils.Int2Float64(sum)/200))) outJxOrder.Weight += jxSku2.Count * jxSku2.Weight jxSku.SalePrice = 1 jxSku.Count = int(utils.Float64TwoInt64(math.Ceil(utils.Int2Float64(sum) / 200))) outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.Skus = append(outJxOrder.Skus, &jxSku2) outJxOrder.OrderPrice += jxSku.SalePrice * int64(jxSku.Count) outJxOrder.OrderPrice += jxSku2.SalePrice * int64(jxSku2.Count) } } else { outJxOrder.Skus = append(outJxOrder.Skus, jxSku) outJxOrder.OrderPrice += int64(jxSku.Count) * jxSku.SalePrice } sum = sum - bagMap[jxSku.SkuID]*jxSku.Count } } else { 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) //TODO 2020-08-06 配送费固定5元 outJxOrder.FreightPrice = 500 // } } 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) } outJxOrder.FromStoreID = fromStoreID if jxOrder.OrderType == model.OrderTypeMatter { //TODO 修改配送费规则,2020-04-28 //3kg 5元,每多1kg加2元 //配送费要按分包规则计算 if outJxOrder.Weight <= 3000 { outJxOrder.FreightPrice = 500 } else if outJxOrder.Weight > 3000 && outJxOrder.Weight <= splitMatterOrderMinWeight { outJxOrder.FreightPrice = utils.Float64TwoInt64(500 + math.Ceil((utils.Int2Float64(outJxOrder.Weight)-3000)/1000)*200) } else { _, freightPrice, _ := tryToSplitMatterOrder(jxOrder) outJxOrder.FreightPrice = freightPrice } } //要求配送人姓名填门店名 if fromStoreID != -1 { deliveryAddress.ConsigneeName = storeDetail2.Name } } else { if outJxOrder.FreightPrice > specialFreightPrice { outJxOrder.FreightPrice = specialFreightPrice } if outJxOrder.OrderPrice >= int64(storeDetail.DeliveryFeeDeductionSill) { outJxOrder.FreightPrice -= int64(storeDetail.DeliveryFeeDeductionFee) if outJxOrder.FreightPrice < 0 || IsDeliverySelf { 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 matterSkusLimited(skus []*JxSkuInfo, storeID int) (err error) { result, err := orderman.GetMatterStoreOrderCount(nil, storeID) sum := 0 if result.Count != 0 { for _, sku := range skus { if sku.SkuID == 6039382 { sum1 := 0 if bagMap[sku.SkuID] != 0 { sum1 += bagMap[sku.SkuID] * sku.Count sum += sum1 } if utils.Int2Float64(sum1/100) >= math.Ceil(utils.Int2Float64(result.Count)/100) { return fmt.Errorf("订单100个一份背心袋订购数量过多,请按照实际销量购买!,大概销量:[%v],购买数量:[%v]", result.Count, sum1) } } if sku.SkuID == 6039383 || sku.SkuID == 6039384 || sku.SkuID == 6039387 || sku.SkuID == 6039390 { sum2 := 0 if bagMap[sku.SkuID] != 0 { sum2 += bagMap[sku.SkuID] * sku.Count sum += sum2 } if utils.Int2Float64(sum2/200) >= math.Ceil(utils.Int2Float64(result.Count)/200) { return fmt.Errorf("订单200个一份背心袋订购数量过多,请按照实际销量购买!,大概销量:[%v],购买数量:[%v]", result.Count, sum2) } } } if sum-result.Count > 100 { return fmt.Errorf("订单背心袋订购数量过多,请按照实际销量购买!,大概销量:[%v],购买数量:[%v]", result.Count, sum) } } return err } func jxOrder2GoodsOrder(ctx *jxcontext.Context, jxOrder *JxOrderInfo, deliveryAddress *dao.UserDeliveryAddressEx, userID string, IsDeliverySelf bool) (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(), EarningType: jxOrder.EarningType, OrderType: jxOrder.OrderType, } if userID == "" { order.UserID = ctx.GetUserID() } else { order.UserID = userID } 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 order.TotalShopMoney = utils.Float64TwoInt64(float64(order.TotalShopMoney) * jdshopapi.JdsPayPercentage) if jxOrder.FromStoreID != 0 { order.FromStoreID = jxOrder.FromStoreID order.DeliveryFlag = model.OrderDeliveryFlagMaskScheduleDisabled order.Flag = 1 if jxOrder.OrderType == model.OrderTypeMatter { order.WaybillVendorID = model.VendorIDJDWL order.ConsigneeAddress = deliveryAddress.Address } } //如果是自提单就设置 if IsDeliverySelf { order.DeliveryType = model.OrderDeliveryTypeSelfTake } 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) { err = changeOrderStatus(order.VendorOrderID, model.OrderStatusDelivering, "") if order.OrderType == model.OrderTypeMatter { var ( db = dao.GetDB() ) goods, err := dao.QueryOrders(db, order.VendorOrderID, -1, []int{model.VendorIDJX}, -1, utils.ZeroTimeValue, utils.ZeroTimeValue) if err != nil || len(goods) == 0 { return err } order.WaybillVendorID = model.VendorIDJDWL dao.UpdateEntity(db, order, "WaybillVendorID") orderSkus := goods[0].Skus if order.Weight <= splitMatterOrderMinWeight { //2020-04-26, 从5kg 改为小于4.5kg var ( goodsNos []string prices []string quantities []string countSum int ) for _, v := range orderSkus { 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)) countSum += v.Count } //总订单不足3kg && 商品数量不足3个 && 五香粉有库存,要送五香粉,补足3个 stockResult, err := api.JdEclpAPI.QueryStock(jxwxfMatterEclpID) if err == nil && len(stockResult) > 0 && stockResult[0].UsableNum > 0 { if order.Weight < 3000 && countSum < 3 { //要判断他本身买没买五香粉EMG4418113943423 var index = 9999 for k, v := range goodsNos { if v == jxwxfMatterEclpID { index = k } } //说明他买了五香粉 if index != 9999 { quantities[index] = utils.Int2Str(utils.Str2Int(quantities[index]) + 3 - countSum) } else { goodsNos = append(goodsNos, jxwxfMatterEclpID) prices = append(prices, "0") quantities = append(quantities, utils.Int2Str(3-countSum)) } } } 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") waybill := &model.Waybill{ VendorOrderID: order.VendorOrderID, OrderVendorID: model.VendorIDJX, VendorWaybillID: order.EclpOutID, WaybillVendorID: model.VendorIDJDWL, Status: model.WaybillStatusDelivering, WaybillCreatedAt: time.Now(), StatusTime: time.Now(), WaybillFinishedAt: utils.DefaultTimeValue, DeliveryFlag: model.OrderDeliveryFlagMaskScheduleDisabled, } dao.CreateEntity(db, waybill) } else { //如果重量超过5kg则需要进行拆单分包,商品分包规则。最后一个包不超过5kg,其他包不超过3kg outOrders, _, _ := tryToSplitMatterOrder(buildJxOrderInfo(order, orderSkus)) //以下为仿照CreateOrder,改了一些参数 for k, v := range outOrders { outJxOrder, deliveryAddress, err := generateOrder(jxcontext.AdminCtx, v, order.AddressID, order.FromStoreID, order.UserID, false) if err != nil { return err } //分包后的子订单ID默认是后面加两位(目前的规则),要改的话要注意取消订单那的判断 outJxOrder.OrderID = utils.Str2Int64(order.VendorOrderID)*100 + int64(k+1) checkMatterDeliveryAddress(deliveryAddress) order2, err2 := jxOrder2GoodsOrder(jxcontext.AdminCtx, outJxOrder, deliveryAddress, order.UserID, false) if err = err2; err == nil { order2.AddressID = order.AddressID order2.Status = model.OrderStatusDelivering err = partner.CurOrderManager.OnOrderNew(order2, model.Order2Status(order2)) orderSolutionForWuLiao(order2) } } //刷新一下库存 for _, v := range goods[0].Skus { cms.RefreshMatterStock(jxcontext.AdminCtx, v.SkuID) } } for _, v := range order.Skus { if bagSkuMap[v.SkuID] != 0 { stores, _ := dao.GetStoreList(db, []int{order.FromStoreID}, nil, nil, nil, "") if len(stores) > 0 { store := stores[0] store.IsBoughtMatter = model.YES dao.UpdateEntity(db, store, "IsBoughtMatter") } } } } 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)) } else { errList.AddErr(err) } } else if orderPay.PayType == model.PayTypeTL { orderPayRefund, err = RefundOrderByTL(ctx, orderPay, refundID, orderPay.TotalFee, reason) if err != nil { errList.AddErr(err) } } if err == nil { MarkArrears(db, order, orderPay) err2 := CancelMatterOrder(db, order, reason) errList.AddErr(err2) } } 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() if len(payList) == 0 { err = CancelMatterOrder(db, order, "") } } else { err = fmt.Errorf("当前订单状态:%s不允许取消", model.OrderStatusName[order.Status]) } return err } func CancelMatterOrder(db *dao.DaoDB, order *model.GoodsOrder, reason string) (err error) { if order.OrderType == model.OrderTypeMatter { if order.EclpOutID != "" { //表示是京西的物料订单的子订单(拆分后的订单) if len(order.VendorOrderID) == 16 && order.VendorID == model.VendorIDJX { return fmt.Errorf("不允许取消该子订单,请取消主订单进行退货退款!主订单ID :[%v]", order.VendorOrderID[:len(order.VendorOrderID)-2]) } _, err = api.JdEclpAPI.CancelOrder(order.EclpOutID) } else { goodsList, err := dao.GetMatterChildOrders(db, order.VendorOrderID) if err == nil && len(goodsList) > 0 { for _, v := range goodsList { _, err = api.JdEclpAPI.CancelOrder(v.EclpOutID) changeOrderStatus(v.VendorOrderID, model.OrderStatusCanceled, reason) } } } stores, _ := dao.GetStoreList(db, []int{order.FromStoreID}, nil, nil, nil, "") if len(stores) > 0 { //如果这周还买过其他物料,则不刷新是否购买标志 var ( orderPays []*model.OrderPay day int ) sql := ` SELECT b.* FROM goods_order a JOIN order_pay b ON a.vendor_order_id = b.vendor_order_id WHERE IF(a.store_id = 0, a.jx_store_id, a.store_id) = 666666 AND a.from_store_id = ? AND a.status >= ? AND a.status <> ? AND b.status = ? AND b.pay_finished_at <= NOW() AND b.pay_finished_at >= ? ` weekInt := int(time.Now().Weekday()) if weekInt == 0 { day = 7 } else { day = weekInt } lastTime := utils.Str2Time(time.Now().AddDate(0, 0, -(day-1)).Format("2006-01-02") + " 01:00:00") sqlParams := []interface{}{order.FromStoreID, model.OrderStatusDelivering, model.OrderStatusCanceled, model.PayStatusYes, lastTime} err = dao.GetRows(db, &orderPays, sql, sqlParams) if len(orderPays) == 0 { store := stores[0] store.IsBoughtMatter = model.NO dao.UpdateEntity(db, store, "IsBoughtMatter") } } } 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 := changeOrderStatus(v.VendorOrderID, model.OrderStatusCanceled, 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 GetDiscountActHoursList() (str []string) { for k := 1; k < 3; k++ { for i := 0; i < 10; i++ { for j := 0; j < 6; j++ { if k == 1 && i == 0 && j == 0 { continue } if k == 2 && i > 2 { break } if k == 2 && i == 2 && j > 0 { break } str = append(str, utils.Int2Str(k)+utils.Int2Str(i)+":"+utils.Int2Str(j)+"0"+":00") } } } return str } func RefreshAllMatterOrderStatus(ctx *jxcontext.Context) (err error) { var ( db = dao.GetDB() goodsList []*model.GoodsOrder realTime time.Time ) realTime = time.Now().AddDate(0, 0, -7) //有分包的主订单 sql := ` SELECT * FROM goods_order WHERE store_id = 666666 AND order_created_at >= ? AND status < ? AND vendor_id = ? AND LENGTH(vendor_order_id) = 14 ` sqlParams := []interface{}{realTime, model.OrderStatusEndBegin, model.VendorIDJX} err = dao.GetRows(db, &goodsList, sql, sqlParams) for _, v := range goodsList { if v.EclpOutID == "" { var ( goodsList2 []*model.GoodsOrder cancelCount int deliveringCount int ) sql2 := "SELECT * FROM goods_order WHERE vendor_order_id LIKE ? OR vendor_order_id LIKE ? AND vendor_id = ? AND eclp_out_id <> '' AND LENGTH(vendor_order_id) = 16" sqlParams2 := []interface{}{v.VendorOrderID + "0%", v.VendorOrderID + "1%", model.VendorIDJX} err = dao.GetRows(db, &goodsList2, sql2, sqlParams2) for _, vv := range goodsList2 { if vv.Status < model.OrderStatusFinished { queryOrderStatus, _ := api.JdEclpAPI.QueryOrderStatus(vv.EclpOutID) 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) } }() vv.Status = model.OrderStatusFinished dao.UpdateEntity(db, vv, "Status") waybills, err := dao.GetWaybills(db, vv.VendorOrderID) if err == nil && len(waybills) > 0 { waybills[0].Status = model.WaybillStatusDelivered dao.UpdateEntity(db, waybills[0], "Status") } dao.Commit(db) changeOrderStatus(vv.VendorOrderID, model.OrderStatusFinished, "") getTrackMessagePlusByOrderResult, _ := api.JdEclpAPI.GetTrackMessagePlusByOrder(vv.VendorOrderID) updateJdWayBillInfo(db, vv, getTrackMessagePlusByOrderResult) } else { deliveringCount++ } } } else if vv.Status == model.OrderStatusCanceled { cancelCount++ } } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if deliveringCount == 0 { v.Status = model.OrderStatusFinished } else { if cancelCount == len(goodsList2) { v.Status = model.OrderStatusCanceled } } if v.Status < model.OrderStatusDelivering { v.Status = model.OrderStatusCanceled } dao.UpdateEntity(db, v, "Status") dao.Commit(db) changeOrderStatus(v.VendorOrderID, v.Status, "") } else { queryOrderStatus, _ := api.JdEclpAPI.QueryOrderStatus(v.EclpOutID) getTrackMessagePlusByOrderResult, _ := api.JdEclpAPI.GetTrackMessagePlusByOrder(v.VendorOrderID) updateMatterOrderStatus(db, v, queryOrderStatus) updateJdWayBillInfo(db, v, getTrackMessagePlusByOrderResult) } } return err } func GetMatterOrderStatus(ctx *jxcontext.Context, vendorOrderID string) (result []*MatterOrderStatus, err error) { var ( db = dao.GetDB() ) order, err := dao.GetSimpleOrder(db, vendorOrderID) 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) if err != nil { return nil, err } 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 { updateMatterOrderStatus(db, order, queryOrderStatus) } if len(getTrackMessagePlusByOrderResult.ResultData) > 0 { updateJdWayBillInfo(db, order, getTrackMessagePlusByOrderResult) } return result, err } func updateMatterOrderStatus(db *dao.DaoDB, order *model.GoodsOrder, queryOrderStatus *jdeclpapi.QueryOrderStatusResult) { code := queryOrderStatus.OrderStatusList[len(queryOrderStatus.OrderStatusList)-1].SoStatusCode if code == jdeclpapi.SoStatusCode10034 || code == jdeclpapi.SoStatusCode10038 { dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() order.Status = model.OrderStatusFinished dao.UpdateEntity(db, order, "Status") waybills, err := dao.GetWaybills(db, order.VendorOrderID) if err == nil && len(waybills) > 0 { waybills[0].Status = model.WaybillStatusDelivered dao.UpdateEntity(db, waybills[0], "Status") } dao.Commit(db) changeOrderStatus(order.VendorOrderID, model.OrderStatusFinished, "") } } func updateJdWayBillInfo(db *dao.DaoDB, order *model.GoodsOrder, getTrackMessagePlusByOrderResult *jdeclpapi.GetTrackMessagePlusByOrderResult) (err error) { var ( waybillCode string cName string cMobile string waybills []*model.Waybill ) if len(getTrackMessagePlusByOrderResult.ResultData) > 0 { waybillCode = getTrackMessagePlusByOrderResult.ResultData[0].WaybillCode } else { return err } for _, vv := range getTrackMessagePlusByOrderResult.ResultData { if vv.OpeTitle == "配送员收货" { result := regexpCnameAndCmobile.FindAllStringSubmatch(vv.OpeRemark, -1) if len(result) > 0 { cName = result[0][1] cMobile = result[0][2] } else { result2 := regexpCnameAndCmobile2.FindAllStringSubmatch(vv.OpeRemark, -1) if len(result2) > 0 { cName = result2[0][1] cMobile = result2[0][2] } } break } } waybills, err = dao.GetWaybills(db, order.VendorOrderID) if len(waybills) > 0 { waybills[0].VendorWaybillID = waybillCode waybills[0].CourierName = cName waybills[0].CourierMobile = cMobile order.VendorWaybillID = waybillCode order.WaybillVendorID = model.VendorIDJDWL dao.UpdateEntity(db, order, "VendorWaybillID", "WaybillVendorID") dao.UpdateEntity(db, waybills[0], "VendorWaybillID", "CourierName", "CourierMobile") } return err } func tryToSplitMatterOrder(jxOrder *JxOrderInfo) (outOrders []*JxOrderInfo, freightPrice int64, err error) { var ( skus = jxOrder.Skus weightList []*JxSkuInfo2 ) //我的思路为把所有商品依次按重量销量从大到小排列,然后第一个包尽量分出3kg,后面分出4.5kg //但是我把JxSkuInfo放进list的时候,想根据一个参数GroupSign判断是否这个商品已经分出去了 //但是在list中同一个商品通过以下方式放进去的地址是一样的,我改了一个商品的GroupSign,所有的都变了,所以只有分出一个JxSkuInfo2去弄。。 //要做优化的话更好 for _, v := range skus { for i := 0; i < v.Count; i++ { var sku2 = &JxSkuInfo2{} sku2.Count = v.Count sku2.Name = v.Name sku2.Price = v.Price sku2.SalePrice = v.SalePrice sku2.Weight = v.Weight sku2.SkuID = v.SkuID weightList = append(weightList, sku2) } } for i := 0; i < len(weightList)-1; i++ { for j := 0; j < len(weightList)-i-1; j++ { if weightList[j].Weight < weightList[j+1].Weight { tmp := weightList[j] weightList[j] = weightList[j+1] weightList[j+1] = tmp } } } weight := jxOrder.Weight for { //每拿出一组就删掉list里的值 outOrders = append(outOrders, loop2(weightList, jxOrder.StoreID, &weight)) for i := 0; i < len(weightList); { if weightList[i].GroupSign { var weightList3 []*JxSkuInfo2 weightList3 = append(weightList[:i], weightList[i+1:]...) weightList = weightList3 } else { i++ } } if len(weightList) == 0 { break } } for _, v := range outOrders { if v.Weight <= 3000 { freightPrice += 500 } else if v.Weight > 3000 && v.Weight <= splitMatterOrderMinWeight { freightPrice += utils.Float64TwoInt64(500 + math.Ceil((utils.Int2Float64(v.Weight)-3000)/1000)*200) } } return outOrders, freightPrice, err } func jxOrderChange(sku2 *JxSkuInfo2) *JxSkuInfo { sku := &JxSkuInfo{} sku.Count = 1 sku.Name = sku2.Name sku.Price = sku2.Price sku.SalePrice = sku2.SalePrice sku.SkuID = sku2.SkuID sku.Weight = sku2.Weight return sku } //TODO 修改为4.5kg 2020-04-2? //商品分包规则2。最后一个包不超过5kg,其他包不超过3kg // 举例1:5.1kg,分为 2.5kg,2.6kg,确保每个包不超过3kg,最后一个包不超过5kg // 举例2:4kg,分为4kg // 举例3:10kg,分为 3kg,3kg,4kg // 举例4:8.1kg,分为 3kg,3kg,2.1kg ; 不能分为3kg,5.1kg //sum3表示3kg的计算 func loop2(weightList []*JxSkuInfo2, storeID int, weight *int) (outOrder *JxOrderInfo) { outOrder = &JxOrderInfo{} outOrder.StoreID = storeID sum3 := 0 if *weight <= splitMatterOrderMinWeight { for i := 0; i < len(weightList); i++ { buildOutOrderSkus(weightList[i], outOrder) } } else { for i := 0; i < len(weightList); i++ { if weightList[i].Weight+sum3 <= 3000 { sum3 += weightList[i].Weight *weight -= weightList[i].Weight buildOutOrderSkus(weightList[i], outOrder) } else { if sum3 >= 3000 { break } continue } } } return outOrder } func buildOutOrderSkus(weightp *JxSkuInfo2, outOrder *JxOrderInfo) { weightp.GroupSign = true outOrder.Weight += weightp.Weight if len(outOrder.Skus) > 0 { var flag = false for _, v := range outOrder.Skus { if v.SkuID == weightp.SkuID { v.Count++ flag = true } } if !flag { outOrder.Skus = append(outOrder.Skus, jxOrderChange(weightp)) } } else { outOrder.Skus = append(outOrder.Skus, jxOrderChange(weightp)) } } func checkMatterDeliveryAddress(deliveryAddress *dao.UserDeliveryAddressEx) { var ( db = dao.GetDB() ) if !strings.Contains(deliveryAddress.Address, "区") { deliveryAddress.Address = deliveryAddress.DistrictName + deliveryAddress.Address } if !strings.Contains(deliveryAddress.Address, "市") { deliveryAddress.Address = deliveryAddress.CityName + deliveryAddress.Address } if !strings.Contains(deliveryAddress.Address, "省") { if place1, err := dao.GetPlaceByCode(db, deliveryAddress.CityCode); err == nil { if place2, err2 := dao.GetPlaceByCode(db, place1.ParentCode); err2 == nil { deliveryAddress.Address = place2.Name + deliveryAddress.Address } } } } func SendFailedMatterOrder(ctx *jxcontext.Context, vendorOrderID string) (err error) { var ( db = dao.GetDB() ) order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, model.VendorIDJX) if err != nil { return err } if order == nil || order.StoreID != model.MatterStoreID || order.FromStoreID == 0 { return fmt.Errorf("只允许物料店重发物料订单调用此接口!") } // queryOrderStatus, err := api.JdEclpAPI.QueryOrderStatus(order.EclpOutID) // if len(queryOrderStatus.OrderStatusList) > 0 { // code := queryOrderStatus.OrderStatusList[len(queryOrderStatus.OrderStatusList)-1].SoStatusCode // if code == jdeclpapi.SoStatusCode10022 || code == jdeclpapi.SoStatusCode10038 { //表示该订单在京东物流为暂停或已经逆向发货完成 if len(order.VendorOrderID) == 14 && order.EclpOutID != "" { //这是不分包的订单 _, err = createMatterOrder(buildJxOrderInfo(order, order.Skus), order, int64(01)) err = CancelMatterOrder(db, order, cancelMatterOrderReason) changeOrderStatus(order.VendorOrderID, model.OrderStatusCanceled, cancelMatterOrderReason) for _, v := range order.Skus { cms.RefreshMatterStock(jxcontext.AdminCtx, v.SkuID) } } else if len(order.VendorOrderID) == 14 && order.EclpOutID == "" { //这是分包的主订单 goodsList, err := dao.GetMatterChildOrders(db, order.VendorOrderID) if err != nil { return err } if len(goodsList) > 0 { for _, v := range goodsList { cOrder, err := partner.CurOrderManager.LoadOrder(v.VendorOrderID, model.VendorIDJX) if err != nil { return err } suffix := utils.Str2Int64(cOrder.VendorOrderID[len(cOrder.VendorOrderID)-2:]) + int64(len(goodsList)) _, err = createMatterOrder(buildJxOrderInfo(cOrder, cOrder.Skus), cOrder, suffix) for _, v := range cOrder.Skus { cms.RefreshMatterStock(jxcontext.AdminCtx, v.SkuID) } } } err = CancelMatterOrder(db, order, cancelMatterOrderReason) changeOrderStatus(order.VendorOrderID, model.OrderStatusCanceled, cancelMatterOrderReason) } else if len(order.VendorOrderID) == 16 && order.EclpOutID != "" { // 这是分包的子订单 return fmt.Errorf("请重发主订单!主订单号:[%v]", order.VendorOrderID[len(order.VendorOrderID)-2:]) } // } else { // return fmt.Errorf("只允许物流订单为暂停或逆向完成才能调用此接口!") // } // } return err } func createMatterOrder(jxOrder *JxOrderInfo, order *model.GoodsOrder, newOrderIDSuffix int64) (order2 *model.GoodsOrder, err error) { outJxOrder, deliveryAddress, err := generateOrder(jxcontext.AdminCtx, jxOrder, order.AddressID, order.FromStoreID, order.UserID, false) if err != nil { return nil, err } outJxOrder.OrderID = utils.Str2Int64(order.VendorOrderID)*100 + newOrderIDSuffix checkMatterDeliveryAddress(deliveryAddress) order2, err2 := jxOrder2GoodsOrder(jxcontext.AdminCtx, outJxOrder, deliveryAddress, order.UserID, false) if err = err2; err == nil { order2.AddressID = order.AddressID order2.Status = model.OrderStatusDelivering err = partner.CurOrderManager.OnOrderNew(order2, model.Order2Status(order2)) err = orderSolutionForWuLiao(order2) } return order2, err } func buildJxOrderInfo(order *model.GoodsOrder, orderSkus []*model.OrderSku) (jxOrder *JxOrderInfo) { jxOrder = &JxOrderInfo{} jxOrder.StoreID = order.StoreID weight := 0 var skus []*JxSkuInfo for _, v := range orderSkus { weight += v.Weight * v.Count sku := &JxSkuInfo{} sku.SkuID = v.SkuID sku.SalePrice = v.SalePrice sku.Name = v.SkuName sku.Weight = v.Weight sku.Count = v.Count skus = append(skus, sku) } jxOrder.Skus = skus jxOrder.Weight = weight jxOrder.OrderType = model.OrderTypeMatter return jxOrder } func GetSupplySupportStoreSkus(ctx *jxcontext.Context, fromDate, toDate string, fromStoreID, storeID int, percentage string) (orderSkus []*model.OrderSku, err error) { var ( db = dao.GetDB() ) orderSkus, err = dao.GetSupplySupportStoreSkus(db, utils.Str2Time(fromDate), utils.Str2Time(toDate), fromStoreID, storeID, utils.Str2Float64(percentage)) return orderSkus, err }