package orderman import ( "fmt" "math" "time" "git.rosy.net.cn/baseapi" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxcallback/scheduler" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/business/msghub" "git.rosy.net.cn/jx-callback/globals" "github.com/astaxie/beego/orm" ) type tStoreSkuBindAndVendorSkuID struct { VendorSkuID int64 `orm:"column(vendor_sku_id)"` SkuID int `orm:"column(sku_id)"` Weight int Price int } func init() { } func (c *OrderManager) LoadPendingOrders() []*model.GoodsOrder { db := orm.NewOrm() var orders []*model.GoodsOrder tillTime := time.Now().Add(-pendingOrderGapMax) _, err := db.Raw(` SELECT * FROM goods_order WHERE order_created_at >= ? AND status < ? `, tillTime, model.OrderStatusEndBegin).QueryRows(&orders) if err != nil { globals.SugarLogger.Warnf("LoadPendingOrders load pending orders error:%v", err) return nil } for _, order := range orders { utils.CallFuncLogError(func() error { _, err = db.QueryTable("order_sku").Filter("vendor_order_id", order.VendorOrderID).Filter("vendor_id", order.VendorID).All(&order.Skus) return err }, "LoadPendingOrders order:%v", order) } return orders } // msgVendorStatus的意思是事件本身的类型,类似有时收到NewOrder事件去取,订单状态不一定就是New的 // OnOrderAdjust也类似,而OrderStatus要记录的是消息,所以添加这个 func (c *OrderManager) OnOrderNew(order *model.GoodsOrder, msgVendorStatus string) (err error) { if order.ConsigneeMobile2 == "" && !jxutils.IsMobileFake(order.ConsigneeMobile) { order.ConsigneeMobile2 = order.ConsigneeMobile } // todo transaction db := orm.NewOrm() if order.Status == model.OrderStatusUnknown { order.Status = model.OrderStatusNew } status := model.Order2Status(order) if status.Status > model.OrderStatusNew { status.Status = model.OrderStatusNew } status.VendorStatus = msgVendorStatus isDuplicated, err := addOrderOrWaybillStatus(status, db) if err == nil && !isDuplicated { if isDuplicated, err = c.SaveOrder(order, false, db); err == nil && !isDuplicated { err = scheduler.CurrentScheduler.OnOrderNew(order, false) } } return err } // todo 调整单的处理可能还需要再细化一点,当前只是简单的删除重建 func (c *OrderManager) OnOrderAdjust(order *model.GoodsOrder, msgVendorStatus string) (err error) { if order.ConsigneeMobile2 == "" && !jxutils.IsMobileFake(order.ConsigneeMobile) { order.ConsigneeMobile2 = order.ConsigneeMobile } // todo transaction db := orm.NewOrm() if order.Status == model.OrderStatusUnknown { order.Status = model.OrderStatusNew } status := model.Order2Status(order) status.Status = model.OrderStatusAdjust status.VendorStatus = msgVendorStatus isDuplicated, err := addOrderOrWaybillStatus(status, db) if err == nil && !isDuplicated { err = utils.CallFuncLogError(func() error { _, err = db.Raw("DELETE FROM order_sku WHERE vendor_order_id = ? AND vendor_id = ?", order.VendorOrderID, order.VendorID).Exec() return err }, "OnAdjustOrder delete order, orderID:%s", order.VendorOrderID) if err != nil { return err } err = utils.CallFuncLogError(func() error { _, err = db.Raw("DELETE FROM goods_order WHERE vendor_order_id = ? AND vendor_id = ?", order.VendorOrderID, order.VendorID).Exec() return err }, "OnAdjustOrder delete order_sku, orderID:%s", order.VendorOrderID) if err != nil { return err } if isDuplicated, err = c.SaveOrder(order, true, db); err == nil && !isDuplicated { msghub.OnNewOrder(order) // 因为订单调度器需要的是真实状态,所以用order的状态 err = scheduler.CurrentScheduler.OnOrderNew(order, false) err = scheduler.CurrentScheduler.OnOrderStatusChanged(model.Order2Status(order), false) } } return err } func (c *OrderManager) OnOrderStatusChanged(orderStatus *model.OrderStatus) (err error) { isDuplicated, err := c.addOrderStatus(orderStatus, nil) if err == nil && !isDuplicated { err = scheduler.CurrentScheduler.OnOrderStatusChanged(orderStatus, false) } return err } func (c *OrderManager) OnOrderMsg(order *model.GoodsOrder, vendorStatus, remark string) (err error) { _, err = c.addOrderStatus(&model.OrderStatus{ VendorOrderID: order.VendorOrderID, VendorID: order.VendorID, OrderType: model.OrderTypeOrder, RefVendorOrderID: order.VendorOrderID, RefVendorID: order.VendorID, VendorStatus: vendorStatus, Status: model.OrderStatusMsg, StatusTime: time.Now(), Remark: remark, }, nil) return err } func (c *OrderManager) SaveOrder(order *model.GoodsOrder, isAdjust bool, db orm.Ormer) (isDuplicated bool, err error) { globals.SugarLogger.Debugf("SaveOrder orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID) // 忽略查找JX信息错误 c.updateOrderOtherInfo(order, db) order.ID = 0 order.WaybillVendorID = model.VendorIDUnknown order.OrderFinishedAt = utils.DefaultTimeValue // todo hardcode 兼容京东消息错序问题 if true { //order.VendorID == model.VendorIDJD { orderStatus := &model.OrderStatus{} if db.Raw(` SELECT * FROM order_status WHERE order_type = ? AND vendor_order_id = ? AND vendor_id = ? ORDER BY status_time DESC LIMIT 1 `, model.OrderTypeOrder, order.VendorOrderID, order.VendorID).QueryRow(orderStatus) == nil { if orderStatus.Status > order.Status { order.Status = orderStatus.Status order.VendorStatus = orderStatus.VendorStatus order.StatusTime = orderStatus.StatusTime } } } order.OrderCreatedAt = order.StatusTime // globals.SugarLogger.Debugf("saveOrder isAdjust:%t, order:%v", isAdjust, order) db.Begin() defer func() { db.Rollback() }() created, _, err2 := db.ReadOrCreate(order, "VendorOrderID", "VendorID") if err = err2; err == nil { originalOrder := &model.GoodsOrderOriginal{ VendorOrderID: order.VendorOrderID, VendorID: order.VendorID, OrderCreatedAt: order.OrderCreatedAt, OriginalData: order.OriginalData, } if _, _, err = db.ReadOrCreate(originalOrder, "VendorOrderID", "VendorID"); err == nil { if created { sql := `INSERT INTO order_sku(vendor_order_id, vendor_id, count, vendor_sku_id, sku_id, jx_sku_id, sku_name, shop_price, sale_price, weight, sku_type, promotion_type, order_created_at) VALUES` params := []interface{}{} for _, sku := range order.Skus { sql += "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)," // 有时不是通过京西平台建立的SKU,不范围要超过 skuID := 0 if sku.SkuID < math.MaxInt32 { skuID = sku.SkuID } params = append(params, sku.VendorOrderID, sku.VendorID, sku.Count, sku.VendorSkuID, skuID, sku.JxSkuID, sku.SkuName, sku.ShopPrice, sku.SalePrice, sku.Weight, sku.SkuType, sku.PromotionType, order.OrderCreatedAt) } sql = sql[:len(sql)-1] + ";" if _, err = db.Raw(sql, params...).Exec(); err != nil { baseapi.SugarLogger.Warnf("saveOrder insert order:%v, order_sku error:%v", order, err) } else { db.Commit() } } else { isDuplicated = true order.DuplicatedCount++ db.Update(order, "DuplicatedCount") db.Commit() baseapi.SugarLogger.Infof("saveOrder duplicated orderid:%s msg received", order.VendorOrderID) } } } else { globals.SugarLogger.Warnf("saveOrder create order:%v, error:%v", order, err) } return isDuplicated, err } func (c *OrderManager) updateOrderSkuOtherInfo(order *model.GoodsOrder, db orm.Ormer) (err error) { globals.SugarLogger.Debugf("updateOrderSkuOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID) jxStoreID := jxutils.GetShowStoreIDFromOrder(order) opNumStr := "2" if time.Now().Sub(order.OrderCreatedAt) < 48*time.Hour && order.VendorID != model.VendorIDEBAI { opNumStr = "" } if jxStoreID == 0 { if order.VendorID != model.VendorIDEBAI { globals.SugarLogger.Infof("updateOrderSkuOtherInfo [运营%s]订单在京西与平台都找不到京西门店信息orderID:%s, VendorStoreID:%s", opNumStr, order.VendorOrderID, order.VendorStoreID) } return nil } orderSkus := order.Skus vendorSkuIDs := make([]int64, 0) for _, v := range orderSkus { intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0) if intVendorSkuID != 0 { vendorSkuIDs = append(vendorSkuIDs, intVendorSkuID) } } if len(vendorSkuIDs) > 0 { tableName := "t2" if model.MultiStoresVendorMap[order.VendorID] == 1 { tableName = "t1" } fieldPrefix := dao.ConvertDBFieldPrefix(model.VendorNames[order.VendorID]) sql := ` SELECT %s.%s_id vendor_sku_id, t1.id sku_id, t2.price, t1.weight FROM sku t1 LEFT JOIN store_sku_bind t2 ON t1.id = t2.sku_id AND t2.deleted_at = ? AND t2.store_id = ? WHERE t1.deleted_at = ? AND %s.%s_id IN (-1, ` + dao.GenQuestionMarks(len(vendorSkuIDs)) + ")" sql = fmt.Sprintf(sql, tableName, fieldPrefix, tableName, fieldPrefix) var skuInfos []*tStoreSkuBindAndVendorSkuID db2 := dao.WrapDB(db) if err = dao.GetRows(db2, &skuInfos, sql, utils.DefaultTimeValue, jxStoreID, utils.DefaultTimeValue, vendorSkuIDs); err != nil { globals.SugarLogger.Errorf("updateOrderSkuOtherInfo can not get sku info for orderID:%s, error:%v", order.VendorOrderID, err) return err } skumapper := make(map[int64]*tStoreSkuBindAndVendorSkuID) for _, v := range skuInfos { skumapper[v.VendorSkuID] = v } for _, v := range orderSkus { intVendorSkuID := utils.Str2Int64WithDefault(v.VendorSkuID, 0) if intVendorSkuID != 0 && v.VendorSkuID != "-70000" { // todo hard code skuBindInfo := skumapper[intVendorSkuID] if skuBindInfo == nil { globals.SugarLogger.Infof("updateOrderSkuOtherInfo [运营%s]%s订单sku找不到门店价格(或商品映射),orderID:%s, StoreID:%d, VendorSkuID:%s, sku:%v", opNumStr, model.VendorChineseNames[order.VendorID], order.VendorOrderID, jxStoreID, v.VendorSkuID, v) } else { v.JxSkuID = skuBindInfo.SkuID v.ShopPrice = int64(skuBindInfo.Price) v.Weight = skuBindInfo.Weight // 以本地信息中的WEIGHT为准 order.ShopPrice += v.ShopPrice * int64(v.Count) if skuBindInfo.Price == 0 { globals.SugarLogger.Infof("updateOrderSkuOtherInfo [运营%s]%s订单sku门店价格为零(一般原因为没有门店价格信息),orderID:%s, StoreID:%d, SkuID:%d, sku:%v", opNumStr, model.VendorChineseNames[order.VendorID], order.VendorOrderID, jxStoreID, v.JxSkuID, v) } } } } } return nil } func (c *OrderManager) updateOrderOtherInfo(order *model.GoodsOrder, db orm.Ormer) (err error) { globals.SugarLogger.Debugf("updateOrderOtherInfo orderID:%s, VendorStoreID:%s", order.VendorOrderID, order.VendorStoreID) storeMap := &model.StoreMap{ VendorID: order.VendorID, VendorStoreID: order.VendorStoreID, } storeMap.DeletedAt = utils.DefaultTimeValue db2 := dao.WrapDB(db) if err = dao.GetEntity(db2, storeMap, model.FieldVendorID, model.FieldVendorStoreID, model.FieldDeletedAt); err != nil && err != orm.ErrNoRows { globals.SugarLogger.Warnf("updateOrderOtherInfo GetEntity orderID:%s, VendorStoreID:%s, error:%v", order.VendorOrderID, order.VendorStoreID, err) return err } order.JxStoreID = storeMap.StoreID if err = c.updateOrderSkuOtherInfo(order, db); err == nil { if order.Weight == 0 { for _, v := range order.Skus { order.Weight += v.Weight } } } return err } func (c *OrderManager) addOrderStatus(orderStatus *model.OrderStatus, db orm.Ormer) (isDuplicated bool, err error) { if db == nil { db = orm.NewOrm() } isDuplicated, err = addOrderOrWaybillStatus(orderStatus, db) if err == nil && !isDuplicated && (orderStatus.Status > model.OrderStatusUnknown || (orderStatus.Status == model.OrderStatusUnlocked || orderStatus.Status == model.OrderStatusLocked || orderStatus.Status == model.OrderStatusApplyCancel)) { order := &model.GoodsOrder{ VendorOrderID: orderStatus.VendorOrderID, VendorID: orderStatus.VendorID, } if err = db.ReadForUpdate(order, "VendorOrderID", "VendorID"); err == nil { if (orderStatus.Status == model.OrderStatusUnlocked || orderStatus.Status == model.OrderStatusLocked || orderStatus.Status == model.OrderStatusApplyCancel) || (orderStatus.Status > model.OrderStatusUnknown && orderStatus.Status >= order.Status) { // todo 要求status不能回绕 order.VendorStatus = orderStatus.VendorStatus order.StatusTime = orderStatus.StatusTime updateFields := []string{ "VendorStatus", "StatusTime", } if orderStatus.Status > model.OrderStatusUnknown { order.LockStatus = model.OrderStatusUnknown order.Status = orderStatus.Status updateFields = append(updateFields, "Status", "LockStatus") } else { if orderStatus.Status == model.OrderStatusUnlocked { order.LockStatus = model.OrderStatusUnknown updateFields = append(updateFields, "LockStatus") } else if orderStatus.Status == model.OrderStatusLocked || orderStatus.Status == model.OrderStatusApplyCancel { order.LockStatus = orderStatus.Status updateFields = append(updateFields, "LockStatus") } } orderStatus.LockStatus = order.LockStatus if orderStatus.Status >= model.OrderStatusEndBegin { order.OrderFinishedAt = orderStatus.StatusTime updateFields = append(updateFields, "OrderFinishedAt") } utils.CallFuncLogError(func() error { _, err = db.Update(order, updateFields...) return err }, "addOrderStatus update orderID:%s, status:%v", order.VendorOrderID, orderStatus) } else { isDuplicated = true } } else { if err == orm.ErrNoRows { // todo 消息错序 err = nil } else { globals.SugarLogger.Warnf("addOrderStatus orderID:%s read failed with error:%v", order.VendorOrderID, err) } } } return isDuplicated, err } func (c *OrderManager) loadOrder(vendorOrderID, vendorOrderID2 string, vendorID int) (order *model.GoodsOrder, err error) { db := orm.NewOrm() order = &model.GoodsOrder{ VendorOrderID: vendorOrderID, VendorOrderID2: vendorOrderID2, VendorID: vendorID, } keyFields := []string{ model.FieldVendorID, } if vendorOrderID != "" { keyFields = append(keyFields, model.FieldVendorOrderID) } if vendorOrderID2 != "" { keyFields = append(keyFields, model.FieldVendorOrderID2) } if err = db.Read(order, keyFields...); err == nil { vendorOrderID = order.VendorOrderID err = utils.CallFuncLogError(func() error { _, err = db.QueryTable("order_sku").Filter("vendor_order_id", vendorOrderID).Filter("vendor_id", vendorID).All(&order.Skus) return err }, "LoadOrder orderID:%s", vendorOrderID) } if err != nil { if err == orm.ErrNoRows { err = ErrCanNotFindOrder } globals.SugarLogger.Infof("LoadOrder orderID:%s failed with error:%v", vendorOrderID, err) } return order, err } func (c *OrderManager) LoadOrder(vendorOrderID string, vendorID int) (order *model.GoodsOrder, err error) { return c.loadOrder(vendorOrderID, "", vendorID) } func (c *OrderManager) LoadOrder2(vendorOrderID2 string, vendorID int) (order *model.GoodsOrder, err error) { return c.loadOrder("", vendorOrderID2, vendorID) } func (c *OrderManager) LoadOrderFinancial(vendorOrderID string, vendorID int) (order *model.OrderFinancial, err error) { return c.loadOrderFinancial(vendorOrderID, "", vendorID) } func (c *OrderManager) LoadOrderFinancial2(vendorOrderID2 string, vendorID int) (order *model.OrderFinancial, err error) { return c.loadOrderFinancial("", vendorOrderID2, vendorID) } func (c *OrderManager) loadOrderFinancial(vendorOrderID, vendorOrderID2 string, vendorID int) (order *model.OrderFinancial, err error) { db := orm.NewOrm() order = &model.OrderFinancial{ VendorOrderID: vendorOrderID, VendorOrderID2: vendorOrderID2, VendorID: vendorID, } keyFields := []string{ model.FieldVendorID, } if vendorOrderID != "" { keyFields = append(keyFields, model.FieldVendorOrderID) } if vendorOrderID2 != "" { keyFields = append(keyFields, model.FieldVendorOrderID2) } // 这块不知道怎么写了、、、标注一下 if err = db.Read(order, keyFields...); err == nil { vendorOrderID = order.VendorOrderID err = utils.CallFuncLogError(func() error { _, err = db.QueryTable("order_sku_financial").Filter("vendor_order_id", vendorOrderID).Filter("vendor_id", vendorID).All(&order.Skus) return err }, "LoadOrder orderID:%s", vendorOrderID) } if err != nil { if err == orm.ErrNoRows { err = ErrCanNotFindOrder } globals.SugarLogger.Infof("LoadOrderFinancial orderID:%s failed with error:%v", vendorOrderID, err) } return order, err } func (c *OrderManager) UpdateOrderStatusAndFlag(order *model.GoodsOrder) (err error) { db := orm.NewOrm() utils.CallFuncLogError(func() error { _, err = db.Update(order, "Status", "DeliveryFlag") return err }, "UpdateOrderStatusAndFlag orderID:%s failed with error:%v", order.VendorOrderID, err) return err } //Waybill func (c *OrderManager) UpdateWaybillVendorID(bill *model.Waybill, revertStatus bool) (err error) { globals.SugarLogger.Debugf("UpdateWaybillVendorID bill:%v", bill) db := orm.NewOrm() params := orm.Params{ "vendor_waybill_id": bill.VendorWaybillID, "waybill_vendor_id": bill.WaybillVendorID, } // 如果运单被取消,则要保持在已拣货状态 if revertStatus && bill.WaybillVendorID == model.VendorIDUnknown { params["status"] = model.OrderStatusFinishedPickup } utils.CallFuncLogError(func() error { _, err = db.QueryTable("goods_order").Filter("vendor_order_id", bill.VendorOrderID).Filter("vendor_id", bill.OrderVendorID).Update(params) return err }, "UpdateWaybillVendorID update order, bill:%v", bill) return err }