diff --git a/business/partner/purchase/tao_vegetable/act.go b/business/partner/purchase/tao_vegetable/act.go new file mode 100644 index 000000000..1db6de94c --- /dev/null +++ b/business/partner/purchase/tao_vegetable/act.go @@ -0,0 +1,265 @@ +package mtwm + +import ( + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + "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/putils" + "git.rosy.net.cn/jx-callback/globals" + "git.rosy.net.cn/jx-callback/globals/api" +) + +func jxActType2Mtwm(actType int) int { + if actType == model.ActSkuDirectDown { + actType = mtwmapi.RetailActTypeDirectDown + } else if actType == model.ActSkuSecKill { + actType = mtwmapi.RetailActTypeSecKill + } else { + actType = 0 + } + return actType +} + +func actOrderRules2Mtwm(actOrderRules []*model.ActOrderRule) (actDetails []*mtwmapi.FullDiscountActDetail) { + for _, v := range actOrderRules { + actDetails = append(actDetails, &mtwmapi.FullDiscountActDetail{ + OriginalPrice: jxutils.IntPrice2Standard(v.SalePrice), + ActPrice: jxutils.IntPrice2Standard(v.DeductPrice), + }) + } + return actDetails +} + +func storeSku2ActData(act *model.Act2, actStoreSku []*model.ActStoreSku2, handler func(int8) bool) (actData []*mtwmapi.RetailDiscountActData) { + orderLimit := 1 + if act.LimitCount > 0 { + orderLimit = act.LimitCount + } + for _, v := range actStoreSku { + if handler == nil || handler(v.SyncStatus) { + dayLimit := -1 + if v.Stock > 0 { + dayLimit = v.Stock + } + actData = append(actData, &mtwmapi.RetailDiscountActData{ + AppFoodCode: utils.Int2Str(v.SkuID), + // UserType: 0, + StartTime: act.BeginAt.Unix(), + EndTime: act.EndAt.Unix(), + OrderLimit: orderLimit, + DayLimit: dayLimit, + // Period: "", + // WeeksTime: "", + SettingType: mtwmapi.SettingTypeAsPrice, + ActPrice: jxutils.IntPrice2Standard(v.ActualActPrice), + // DiscountCoefficient: 0, + // Sequence: int(v.ActPrice), // 此字段不允许重复 + // ItemID: utils.Str2Int64WithDefault(v.VendorActID, 0), + }) + } + } + return actData +} + +func storeSku2ActData4Delete(actStoreSku []*model.ActStoreSku2, handler func(int8) bool) (actIDList []string) { + for _, v := range actStoreSku { + if handler == nil || handler(v.SyncStatus) { + if v.VendorActID != "" { + actIDList = append(actIDList, v.VendorActID) + } + } + } + return actIDList +} + +func isCreateOrUpdate(syncStatus int8) bool { + return model.IsSyncStatusNeedCreate(syncStatus) || model.IsSyncStatusNeedUpdate(syncStatus) +} + +func createOneShopAct(act *model.Act2, vendorStoreID string, actStoreSku []*model.ActStoreSku2) (failedList []*partner.StoreSkuInfoWithErr, err error) { + actData := storeSku2ActData(act, actStoreSku, isCreateOrUpdate) + if len(actData) > 0 { + if globals.EnableMtwmStoreWrite { + actResult, faileInfoList, err2 := api.MtwmAPI.RetailDiscountBatchSave2(vendorStoreID, jxActType2Mtwm(act.Type), actData) + err = err2 + // 忽略错误,都放在failedList里 + // if err != nil { + // return nil, err + // } + actStoreSkuMap := make(map[int]*model.ActStoreSku2) + for _, v := range actStoreSku { + actStoreSkuMap[v.SkuID] = v + } + + for _, v := range actResult { + if v2 := actStoreSkuMap[int(utils.Str2Int64WithDefault(v.AppFoodCode, 0))]; v2 != nil { + v2.VendorActID = utils.Int64ToStr(v.ActID) + } + } + for _, v := range faileInfoList { + if v2 := actStoreSkuMap[int(utils.Str2Int64WithDefault(v.AppFoodCode, 0))]; v2 != nil { + failedList = append(failedList, &partner.StoreSkuInfoWithErr{ + StoreSkuInfo: &partner.StoreSkuInfo{ + SkuID: v2.SkuID, + }, + VendoreID: model.VendorIDMTWM, + StoreID: int(utils.Str2Int64WithDefault(vendorStoreID, 0)), + ErrMsg: v.ErrorMsg, + }) + } + } + } else { + for _, v := range actStoreSku { + v.VendorActID = utils.Int64ToStr(jxutils.GenFakeID()) + } + } + } + return failedList, err +} + +func cancelOneShopAct(act *model.Act2, vendorStoreID string, actStoreSku []*model.ActStoreSku2) (failedList []*partner.StoreSkuInfoWithErr, err error) { + if list := storeSku2ActData4Delete(actStoreSku, nil /*model.IsSyncStatusNeedDelete*/); len(list) > 0 { + if globals.EnableMtwmStoreWrite { + failedList2, err2 := api.MtwmAPI.RetailDiscountDelete2(vendorStoreID, jxActType2Mtwm(act.Type), list) + actStoreSkuMap := make(map[string]*model.ActStoreSku2) + for _, v := range actStoreSku { + actStoreSkuMap[v.VendorActID] = v + } + for _, v := range failedList2 { + if !mtwmapi.CanDeleteActErrMsgIgnore(v.ErrorMsg) { + failedList = append(failedList, &partner.StoreSkuInfoWithErr{ + StoreSkuInfo: &partner.StoreSkuInfo{ + SkuID: actStoreSkuMap[utils.Int64ToStr(v.ActID)].SkuID, + }, + StoreID: int(utils.Str2Int64WithDefault(vendorStoreID, 0)), + ErrMsg: v.ErrorMsg, + VendoreID: model.VendorIDMTWM, + }) + } + } + err = err2 + err = nil // 强制不返回错误,使用部分错误 + } + } + return failedList, err +} + +func getActStoreSkuFromTaskResult(taskReslt []interface{}) (list []*model.ActStoreSku2) { + for _, v := range taskReslt { + actStoreSkuList := v.([]*model.ActStoreSku2) + list = append(list, actStoreSkuList...) + } + return list +} + +func createSkuAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actStoreSku []*model.ActStoreSku2) (createdList []*model.ActStoreSku2, err error) { + actStoreSkuListList := partner.SplitActStoreSku2List(actStoreSku) + task := tasksch.NewParallelTask("mtwm createSkuAct", nil, ctx, + func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { + list := batchItemList[0].([]*model.ActStoreSku2) + failedList, err2 := createOneShopAct(act, list[0].VendorStoreID, list) + if err = err2; err2 == nil { + if len(failedList) > 0 { + failedMap := putils.StoreSkuInfoWithErrList2MapBySku(failedList) + list = []*model.ActStoreSku2{} + for _, v := range actStoreSku { + if failedMap[v.SkuID] == nil { + list = append(list, v) + } + } + list = []*model.ActStoreSku2{} + } + retVal = []interface{}{list} + } + return retVal, err + }, actStoreSkuListList) + tasksch.HandleTask(task, parentTask, true).Run() + result, err := task.GetResult(0) + return getActStoreSkuFromTaskResult(result), err +} + +func cancelSkuAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actStoreSku []*model.ActStoreSku2) (canceledList []*model.ActStoreSku2, err error) { + actStoreSkuListList := partner.SplitActStoreSku2List(actStoreSku) + task := tasksch.NewParallelTask("mtwm cancelSkuAct", nil, ctx, + func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { + actStoreSkuList := batchItemList[0].([]*model.ActStoreSku2) + if _, err = cancelOneShopAct(act, actStoreSkuList[0].VendorStoreID, actStoreSkuList); err == nil { + retVal = []interface{}{actStoreSkuList} + } + return retVal, err + }, actStoreSkuListList) + tasksch.HandleTask(task, parentTask, true).Run() + result, err := task.GetResult(0) + return getActStoreSkuFromTaskResult(result), err +} + +func (c *PurchaseHandler) SyncAct(ctx *jxcontext.Context, parentTask tasksch.ITask, act *model.Act2, actOrderRules []*model.ActOrderRule, actStoreSkuList []*model.ActStoreSku2) (err error) { + var actStoreSkuList4Create, actStoreSkuList4Delete []*model.ActStoreSku2 + var updateItems []*dao.KVUpdateItem + + actStoreSkuMap := partner.SplitActStoreSku(actStoreSkuList) + for storeID := range actStoreSkuMap { + for _, actStoreSku := range actStoreSkuMap[storeID] { + if model.IsSyncStatusDelete(actStoreSku.SyncStatus) { + actStoreSkuList4Delete = append(actStoreSkuList4Delete, actStoreSku) + } else if model.IsSyncStatusNew(actStoreSku.SyncStatus) { + actStoreSkuList4Create = append(actStoreSkuList4Create, actStoreSku) + } + } + } + err = func() (err error) { + if model.IsSyncStatusDelete(act.SyncStatus) { + canceledList, err2 := cancelSkuAct(ctx, nil, act, actStoreSkuList) + updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, canceledList, model.SyncFlagModifiedMask)...) + if err = err2; err == nil { + updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagModifiedMask)) + } + } else if model.IsSyncStatusNew(act.SyncStatus) { + createdList, err2 := createSkuAct(ctx, nil, act, actStoreSkuList4Create) + updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, createdList, model.SyncFlagNewMask)...) + if err = err2; err == nil { + updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagNewMask)) + } + } else if model.IsSyncStatusUpdate(act.SyncStatus) { + if len(actStoreSkuList4Create) > 0 { + addedList, err2 := createSkuAct(ctx, nil, act, actStoreSkuList4Create) + updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, addedList, model.SyncFlagNewMask)...) + if err = err2; err != nil { + return err + } + } + if len(actStoreSkuList4Delete) > 0 { + deletedList, err2 := cancelSkuAct(ctx, nil, act, actStoreSkuList4Delete) + updateItems = append(updateItems, partner.ActStoreSku2Update(ctx, deletedList, model.SyncFlagDeletedMask)...) + if err = err2; err != nil { + return err + } + } + if err == nil { + updateItems = append(updateItems, partner.Act2Update(ctx, act, model.SyncFlagModifiedMask)) + } + } + return err + }() + db := dao.GetDB() + _, err2 := dao.BatchUpdateActEntity(db, model.IsSyncStatusDelete(act.SyncStatus), updateItems) + if err == nil { + err = err2 + } + return err +} + +func (c *PurchaseHandler) GetActAmple(ctx *jxcontext.Context, vendorStoreID, vendorOrgCode string) (ample int, err error) { + for _, v := range mtwmapi.ActTypeList { + //1表示进行中 + if actList, err := getAPI(vendorOrgCode, 0, vendorStoreID).GetByAppPoiCodeAndType(vendorOrgCode, 1, v); err == nil { + ample += len(actList) + } + } + return ample, err +} diff --git a/business/partner/purchase/tao_vegetable/callback.go b/business/partner/purchase/tao_vegetable/callback.go new file mode 100644 index 000000000..ff1c42e4b --- /dev/null +++ b/business/partner/purchase/tao_vegetable/callback.go @@ -0,0 +1,117 @@ +package mtwm + +import ( + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/jxutils/netprinter" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" + "git.rosy.net.cn/jx-callback/globals" + "net/http" + "strings" +) + +// 美团回调接口 +func OnCallbackMsg(msg *mtwmapi.CallbackMsg, msgType string) (response *mtwmapi.CallbackResponse) { + // 获取平台门店id + //_, err := dao.GetStoreDetailByVendorStoreID(dao.GetDB(), GetVendorStoreIDFromMsg(msg), model.VendorIDMTWM, GetVendorAppIdFromMsg(msg)) + //if err != nil && strings.Contains(err.Error(), "no row found") { + // forwardOrderToGy(msg, msgType) + // return mtwmapi.SuccessResponse + //} + if CurPurchaseHandler != nil { + if msg.Cmd == mtwmapi.MsgTypeStoreStatusChanged || msg.Cmd == mtwmapi.MsgTypeStoreAuditStatusChanged { + response = CurPurchaseHandler.onStoreStatusChanged(msg) + } else if msg.Cmd == mtwmapi.MsgTypePrivateNumberDowngrade { + response = CurPurchaseHandler.onNumberDowngrade(msg) + } else if msg.Cmd == mtwmapi.MsgTypeStoreBind { + // 门店授权发生变化 17,失去授权 + vendorStoreID := msg.FormData.Get("app_poi_code") + storeDetail, _ := dao.GetStoreDetailByVendorStoreID(dao.GetDB(), vendorStoreID, model.VendorIDMTWM, "") + _, err := netprinter.PrintStoreStatus(jxcontext.AdminCtx, storeDetail, model.VendorIDMTWM, -9) + response = mtwmapi.Err2CallbackResponse(err, "") + } else { + if orderID := GetOrderIDFromMsg(msg); orderID != "" { + jxutils.CallMsgHandler(func() { + switch msg.Cmd { + case mtwmapi.MsgTypeWaybillStatus: + response = CurPurchaseHandler.onWaybillMsg(msg) + default: + response = CurPurchaseHandler.onOrderMsg(msg) + } + }, jxutils.ComposeUniversalOrderID(orderID, model.VendorIDMTWM)) + } + /*if msg.Cmd == mtwmapi.MsgTypeOrderRefund || msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { + utils.CallFuncAsync(func() { + OnFinancialMsg(msg) + }) + } */ + } + } + return response +} + +func GetOrderIDFromMsg(msg *mtwmapi.CallbackMsg) string { + return msg.FormData.Get(mtwmapi.KeyOrderID) +} + +func GetVendorStoreIDFromMsg(msg *mtwmapi.CallbackMsg) string { + return msg.FormData.Get(mtwmapi.KeyAppPoiCode) +} + +func GetVendorAppIdFromMsg(msg *mtwmapi.CallbackMsg) string { + return msg.FormData.Get(mtwmapi.KeyAppID) +} + +func forwardOrderToGy(msg *mtwmapi.CallbackMsg, msgType string) { + cl := http.Client{} + callbackUrl := GetMsgCallBackUrl(msgType, msg.AppID) + request, err := http.NewRequest(http.MethodPost, "http://callback-jxgy.jxc4.com/mtwm/"+callbackUrl, strings.NewReader(msg.FormData.Encode())) + if err != nil { + return + } + request.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") + cl.Do(request) +} + +func GetMsgCallBackUrl(msgType, appId string) string { + interfaceUrl := "" + switch msgType { + case mtwmapi.MsgTypeWaybillStatus: + interfaceUrl = "/waybillStatus" + case mtwmapi.MsgTypeNewOrder: + interfaceUrl = "/newOrder" + case mtwmapi.MsgTypeOrderAccepted: + interfaceUrl = "/orderAccepted" + case mtwmapi.MsgTypeOrderFinished: + interfaceUrl = "/orderFinished" + case mtwmapi.MsgTypeOrderFinancial: + interfaceUrl = "/orderFinancial" + case mtwmapi.MsgTypeOrderCanceled: + interfaceUrl = "/orderCanceled" + case mtwmapi.MsgTypeUserUrgeOrder: + interfaceUrl = "/userUrgeOrder" + case mtwmapi.MsgTypePrivateNumberDowngrade: + interfaceUrl = "/numberDowngrade" + case mtwmapi.MsgTypeOrderModified: + interfaceUrl = "/orderModified" + case mtwmapi.MsgTypeOrderRefund: + interfaceUrl = "/orderRefund" + case mtwmapi.MsgTypeOrderPartialRefund: + interfaceUrl = "/orderPartialRefund" + case mtwmapi.MsgTypeOrderFinishedPickup: + interfaceUrl = "/orderFinishedPickup" + case mtwmapi.MsgTypeStoreStatusChanged: + interfaceUrl = "/storeStatusChanged" + case mtwmapi.MsgTypeStoreAuditStatusChanged: + interfaceUrl = "/storeAuditStatusChanged" + case mtwmapi.MsgTypeSkuDelete: + interfaceUrl = "/skuDelete" + case mtwmapi.MsgTypeStoreBind: + interfaceUrl = "/storeBind" + default: + globals.SugarLogger.Errorf("美团超市[app_id :=%s ,callbackUrl := %s ]回调推送到果园错误,回调地址不正确", appId, msgType) + } + return interfaceUrl +} diff --git a/business/partner/purchase/tao_vegetable/financial.go b/business/partner/purchase/tao_vegetable/financial.go new file mode 100644 index 000000000..918911506 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/financial.go @@ -0,0 +1,245 @@ +package mtwm + +import ( + "net/url" + + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/partner" + "git.rosy.net.cn/jx-callback/globals" +) + +const ( + PublicWelfareDonation = 1 // 美团公益捐款金额现阶段是每单一分钱 +) + +// 存储美团退款订单结账信息 +func OnFinancialMsg(msg *mtwmapi.CallbackMsg) (err error) { + if msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { // 部分退款处理 + orderData := msg.FormData + if orderData.Get("notify_type") == mtwmapi.NotifyTypeSuccess { + err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(CurPurchaseHandler.AfsOrderDetail2Financial(orderData)) + } + } + if msg.Cmd == mtwmapi.MsgTypeOrderRefund { // todo 全额退款处理 + orderData := msg.FormData + if orderData.Get("notify_type") == mtwmapi.NotifyTypeSuccess { + afsOrderID := orderData.Get("order_id") + orderFinancial, err := partner.CurOrderManager.LoadOrderFinancial(afsOrderID, model.VendorIDMTWM) + if err == nil { + err = partner.CurOrderManager.SaveAfsOrderFinancialInfo(CurPurchaseHandler.OrderFinancialDetail2Refund(orderFinancial, orderData)) + } else { + globals.SugarLogger.Warnf("mtwm OnFinancialMsg, afsOrderID:%s is not found from partner.CurOrderManager.LoadOrderFinancial", afsOrderID) + } + } + } + return err +} + +func (p *PurchaseHandler) OrderFinancialDetail2Refund(orderFinancial *model.OrderFinancial, orderData url.Values) (afsOrder *model.AfsOrder) { + afsOrder = &model.AfsOrder{ + VendorID: model.VendorIDMTWM, + AfsOrderID: orderData.Get("refund_id"), + VendorOrderID: orderData.Get("order_id"), + AfsCreatedAt: utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))), + // BoxMoney: orderFinancial.BoxMoney, + // SkuBoxMoney: orderFinancial.SkuBoxMoney, // 美团的餐盒费已经拆到单条SKU里面去了,退款时直接计算用户支付sku金额就好了 + FreightUserMoney: orderFinancial.FreightMoney, + SkuUserMoney: orderFinancial.ActualPayMoney - orderFinancial.FreightMoney, + PmSubsidyMoney: orderFinancial.PmSubsidyMoney, + } + order, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID) + if err == nil { + afsOrder.JxStoreID = order.JxStoreID + afsOrder.VendorStoreID = order.VendorStoreID + afsOrder.StoreID = order.StoreID + } else { + globals.SugarLogger.Warnf("mtwm AfsOrderDetail2Financial, afsOrderID:%s is not found from partner.CurOrderManager.LoadOrder", afsOrder.VendorOrderID) + err = nil + } + for _, sku := range orderFinancial.Skus { + orderSkuFinancial := &model.OrderSkuFinancial{ + VendorID: sku.VendorID, + VendorOrderID: sku.VendorOrderID, + // OrderFinancialID: sku.VendorOrderID, + // ConfirmTime: afsOrder.AfsCreateAt, + VendorStoreID: afsOrder.VendorStoreID, + StoreID: afsOrder.StoreID, + JxStoreID: afsOrder.JxStoreID, + VendorSkuID: sku.VendorSkuID, + SkuID: sku.SkuID, + PromotionType: sku.PromotionType, + Name: sku.Name, + ShopPrice: sku.ShopPrice, + SalePrice: sku.SalePrice, + Count: sku.Count, + UserMoney: sku.UserMoney, + PmSubsidyMoney: sku.PmSubsidyMoney, + IsAfsOrder: 1, + } + afsOrder.Skus = append(afsOrder.Skus, orderSkuFinancial) + } + return afsOrder +} + +func (p *PurchaseHandler) AfsOrderDetail2Financial(orderData url.Values) (afsOrder *model.AfsOrder) { + afsOrder = &model.AfsOrder{ + VendorID: model.VendorIDMTWM, + AfsOrderID: orderData.Get("order_id"), + VendorOrderID: orderData.Get("order_id"), + AfsCreatedAt: utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))), + RefundMoney: jxutils.StandardPrice2Int(utils.Str2Float64(orderData.Get("money"))), + } + // if orderData.Get("timestamp") != "" { + // afsOrder.AfsCreateAt = utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))) + // } else { + // afsOrder.AfsCreateAt = time.Now() + // } + order, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID) + if err == nil { + afsOrder.JxStoreID = order.JxStoreID + } else { + globals.SugarLogger.Warnf("mtwm AfsOrderDetail2Financial, afsOrderID:%s is not found from partner.CurOrderManager.LoadOrder", afsOrder.VendorOrderID) + err = nil + } + food := orderData.Get("food") + var refundDetail []map[string]interface{} + utils.UnmarshalUseNumber([]byte(food), &refundDetail) + for _, xMap := range refundDetail { + orderSkuFinancial := &model.OrderSkuFinancial{ + VendorID: model.VendorIDMTWM, + AfsOrderID: afsOrder.AfsOrderID, + VendorOrderID: afsOrder.VendorOrderID, + // ConfirmTime: afsOrder.AfsCreateAt, + VendorSkuID: utils.Interface2String(xMap["app_food_code"]), + SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(xMap["sku_id"]), 0)), + Name: utils.Interface2String(xMap["food_name"]), + UserMoney: jxutils.StandardPrice2Int(utils.MustInterface2Float64(xMap["refund_price"]))*utils.MustInterface2Int64(xMap["count"]) + jxutils.StandardPrice2Int(utils.MustInterface2Float64(xMap["box_price"]))*int64(utils.MustInterface2Float64(xMap["box_num"])), + IsAfsOrder: 1, + } + afsOrder.Skus = append(afsOrder.Skus, orderSkuFinancial) + afsOrder.SkuUserMoney += orderSkuFinancial.UserMoney + // afsOrder.PmSubsidyMoney += orderSkuFinancial.PmSubsidyMoney // 美团只给了一个扣款金额,很尴尬、、 + } + afsOrder.PmSubsidyMoney += afsOrder.RefundMoney - afsOrder.SkuUserMoney + if len(refundDetail) <= 0 { + globals.SugarLogger.Warnf("mtwm AfsOrderDetail2Financial, orderID:%s have no refund_detail", afsOrder.VendorOrderID) + } + return afsOrder +} + +// 存储美团正向订单结账信息 +func (p *PurchaseHandler) OnOrderDetail(result map[string]interface{}, operation string) (err error) { + err = partner.CurOrderManager.SaveOrderFinancialInfo(p.OrderDetail2Financial(result), operation) + return err +} + +func (p *PurchaseHandler) OrderDetail2Financial(result map[string]interface{}) (orderFinancial *model.OrderFinancial) { + orderFinancial = &model.OrderFinancial{ + VendorID: model.VendorIDMTWM, + VendorOrderID: utils.Int64ToStr(utils.MustInterface2Int64(result["order_id"])), + } + // orderFinancial.DeliveryConfirmTime = utils.Str2TimeWithDefault(utils.Interface2String(result["order_completed_time"]), utils.DefaultTimeValue) + order, err := partner.CurOrderManager.LoadOrder(orderFinancial.VendorOrderID, orderFinancial.VendorID) + jxStoreID := 0 + if err == nil { + jxStoreID = order.JxStoreID + if order.Skus != nil { + for _, x := range order.Skus { + orderFinancial.ShopPriceMoney += x.ShopPrice * int64(x.Count) + } + } + } else { + err = nil + } + if result["package_bag_money"] != nil { + orderFinancial.BoxMoney = utils.MustInterface2Int64(result["package_bag_money"]) + } + detail := result["detail"] + if detail != nil { + var data []map[string]interface{} + utils.UnmarshalUseNumber([]byte(utils.Interface2String(detail)), &data) + for _, x := range data { + orderSkuFinancial := &model.OrderSkuFinancial{ + VendorID: orderFinancial.VendorID, + VendorOrderID: orderFinancial.VendorOrderID, + // OrderFinancialID: orderFinancial.VendorOrderID, + // ConfirmTime: utils.Str2TimeWithDefault(utils.Interface2String(result["ctime"]), utils.DefaultTimeValue), + VendorStoreID: result["app_poi_code"].(string), + StoreID: 0, + JxStoreID: jxStoreID, + VendorSkuID: utils.Interface2String(x["sku_id"]), + SkuID: int(utils.Str2Int64WithDefault(utils.Interface2String(x["sku_id"]), 0)), + Name: utils.Interface2String(x["food_name"]), + SalePrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(x["price"])), + Count: int(utils.MustInterface2Int64(x["quantity"])), + SkuBoxMoney: jxutils.StandardPrice2Int(utils.MustInterface2Float64(x["box_price"])) * jxutils.StandardPrice2Int(utils.MustInterface2Float64(x["box_num"])), + IsAfsOrder: 0, + } + orderFinancial.Skus = append(orderFinancial.Skus, orderSkuFinancial) + orderFinancial.SalePriceMoney += orderSkuFinancial.SalePrice * int64(orderSkuFinancial.Count) + orderFinancial.SkuBoxMoney += orderSkuFinancial.SkuBoxMoney + } + } else { + globals.SugarLogger.Warnf("mtwm OrderDetail2Financial, orderID:%s have no detail", orderFinancial.VendorOrderID) + } + extras := result["extras"] + if extras != nil { + var data []map[string]interface{} + utils.UnmarshalUseNumber([]byte(utils.Interface2String(extras)), &data) + for _, x := range data { + if x["rider_fee"] == nil { + activity := &model.OrderDiscountFinancial{ + VendorID: orderFinancial.VendorID, + VendorOrderID: orderFinancial.VendorOrderID, + // ActivityName: utils.Interface2String(x["remark"]), + // ActivityMoney: jxutils.StandardPrice2Int(utils.MustInterface2Float64(x["reduce_fee"])), + // VendorActivityID: utils.Int64ToStr(utils.MustInterface2Int64(x["act_detail_id"])), + } + if x["act_detail_id"] != nil { // 容错处理 + activity.VendorActivityID = utils.Int64ToStr(utils.MustInterface2Int64(x["act_detail_id"])) + orderFinancial.Discounts = append(orderFinancial.Discounts, activity) + } + // 通过活动Id去取,京西活动补贴 + // orderFinancial.JxSubsidyMoney += + } + } + } + poiReceiveDetail := result["poi_receive_detail"] + if poiReceiveDetail != nil { + var data map[string]interface{} + utils.UnmarshalUseNumber([]byte(utils.Interface2String(poiReceiveDetail)), &data) + orderFinancial.ReceivableFreight = utils.MustInterface2Int64(data["logisticsFee"]) + orderFinancial.FreightMoney = utils.MustInterface2Int64(data["logisticsFee"]) + orderFinancial.ActualPayMoney = utils.MustInterface2Int64(data["onlinePayment"]) + orderFinancial.PmMoney = utils.MustInterface2Int64(data["foodShareFeeChargeByPoi"]) + orderFinancial.ShopMoney = utils.MustInterface2Int64(data["wmPoiReceiveCent"]) + for _, x := range data["actOrderChargeByMt"].([]interface{}) { + orderFinancial.TotalDiscountMoney += utils.MustInterface2Int64(x.(map[string]interface{})["moneyCent"]) + orderFinancial.PmSubsidyMoney += utils.MustInterface2Int64(x.(map[string]interface{})["moneyCent"]) + } + for _, x := range data["actOrderChargeByPoi"].([]interface{}) { + orderFinancial.TotalDiscountMoney += utils.MustInterface2Int64(x.(map[string]interface{})["moneyCent"]) + } + } else { + globals.SugarLogger.Warnf("mtwm OrderDetail2Financial, orderID:%s have no poi_receive_detail", orderFinancial.VendorOrderID) + } + if utils.MustInterface2Int64(result["is_third_shipping"]) == SelfDeliveryCarrierNo { // is_third_shipping int 是否是第三方配送平台配送,0表否,1表是) + orderFinancial.SelfDeliveryDiscountMoney = orderFinancial.ReceivableFreight + orderFinancial.DistanceFreightMoney = 0 + // 通过本地数据库去取是否转美团/达达,并计算运费 + // wayBill, err := partner.CurOrderManager.LoadWaybill(orderFinancial.VendorOrderID, orderFinancial.VendorID) + // if err == nil { + // orderFinancial.JxFreightMoney = wayBill.DesiredFee + // } + } + // // 美团订单单独处理部分,美团正向订单接口推送时总结算金额没有计算公益捐款一分钱,是否在这里直接提前扣除? + // // 3/18之后的订单一直都不显示公益捐款金额,而且结算现在结算到了3/18之后的订单,没有的已经不计算了,先注释 + // // 2019-04-03 10.52 询问赵mf, 此计划是必须参加的,而且是长期的,每单固定扣除一分钱 + orderFinancial.DonationMoney = PublicWelfareDonation + // 不应该对第三方结账金额做更改,就算有异常,也要保留异常,知道问题出在哪里 + // orderFinancial.ShopMoney -= PublicWelfareDonation + return orderFinancial +} diff --git a/business/partner/purchase/tao_vegetable/financial_test.go b/business/partner/purchase/tao_vegetable/financial_test.go new file mode 100644 index 000000000..63e957774 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/financial_test.go @@ -0,0 +1,63 @@ +package mtwm + +import ( + "fmt" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" + "net/url" + "testing" + "time" + + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/partner" +) + +func TestOnFinancialMsg(t *testing.T) { + msg := &mtwmapi.CallbackMsg{ + Cmd: "orderRefund", + FormData: url.Values{}, + } + msg.FormData.Set("timestamp", utils.Int64ToStr(time.Now().Unix())) + msg.FormData.Set("order_id", "33762863167364867") + msg.FormData.Set("notify_type", "agree") + msg.FormData.Set("money", "23.56") + food := []map[string]interface{}{ + map[string]interface{}{ + "app_food_code": "123", + "food_name": "商品1", + "sku_id": "123", + "refund_price": 3.14, + "count": 2, + "box_num": 1, + "box_price": 1, + }, + map[string]interface{}{ + "app_food_code": "124", + "food_name": "商品2", + "sku_id": "124", + "refund_price": 3.15, + "count": 2, + "box_num": 1, + "box_price": 1, + }, + } + msg.FormData.Set("food", string(utils.MustMarshal(food))) + res := CurPurchaseHandler.onAfsOrderMsg(msg) + fmt.Println(res) +} + +func TestOnOrderDetail(t *testing.T) { + result := map[string]interface{}{ + "app_order_code": "", "app_poi_code": "2828472", "avg_send_time": 2410, "backup_recipient_phone": "[\"13164714130_7645\"]", "caution": " 【如遇缺货】: 缺货时电话与我沟通 收餐人隐私号 13049813276_5307,手机号 139****5027", "city_id": 440300, "ctime": 1555036346, "day_seq": 1, "delivery_time": 0, "detail": "[{\"app_food_code\":\"27262\",\"box_num\":0,\"box_price\":0,\"cart_id\":0,\"food_discount\":1,\"food_name\":\"红管鱿鱼约250g/份\",\"food_property\":\"\",\"price\":23.54,\"quantity\":1,\"sku_id\":\"27262\",\"spec\":\"250g\",\"unit\":\"份\"},{\"app_food_code\":\"24987\",\"box_num\":0,\"box_price\":0,\"cart_id\":0,\"food_discount\":1,\"food_name\":\"带皮猪梅花肉约250g/份\",\"food_property\":\"\",\"price\":15.84,\"quantity\":1,\"sku_id\":\"24987\",\"spec\":\"250g\",\"unit\":\"份\"},{\"app_food_code\":\"27179\",\"box_num\":0,\"box_price\":0,\"cart_id\":0,\"food_discount\":1,\"food_name\":\"[畅销]龙骨约250g/份\",\"food_property\":\"\",\"price\":18.59,\"quantity\":1,\"sku_id\":\"27179\",\"spec\":\"250g\",\"unit\":\"份\"}]", "dinners_number": 0, "expect_deliver_time": 0, "extras": "[{\"act_detail_id\":664795195,\"mt_charge\":0,\"poi_charge\":5,\"reduce_fee\":5,\"remark\":\"满46.0元减5.0元\",\"type\":2},{\"act_detail_id\":665051798,\"mt_charge\":0,\"poi_charge\":4,\"reduce_fee\":4,\"remark\":\"减配送费4.0元\",\"type\":25},{\"act_detail_id\":274839715,\"mt_charge\":0.5,\"poi_charge\":0,\"reduce_fee\":0.5,\"remark\":\"用户使用了支付红包减0.5元\",\"type\":9},{\"mt_charge\":0,\"poi_charge\":0,\"reduce_fee\":0,\"remark\":\"送30元商家代金券\",\"type\":100},{}]", "has_invoiced": 0, "invoice_title": "", "is_favorites": false, "is_poi_first_order": true, "is_pre": 0, "is_third_shipping": 0, "latitude": 22.530194, "logistics_code": "1001", "longitude": 114.08372, "order_id": 28284722536001020, "order_send_time": 1555036356, "original_price": 63.97, "package_bag_money": 0, "pay_type": 2, "pick_type": 0, "poi_receive_detail": "{\"actOrderChargeByMt\":[{\"comment\":\"活动款\",\"feeTypeDesc\":\"活动款\",\"feeTypeId\":10019,\"moneyCent\":50}],\"actOrderChargeByPoi\":[{\"comment\":\"满46.0元减5.0元\",\"feeTypeDesc\":\"活动款\",\"feeTypeId\":10019,\"moneyCent\":500},{\"comment\":\"减配送费4.0元\",\"feeTypeDesc\":\"活动款\",\"feeTypeId\":10019,\"moneyCent\":400}],\"foodShareFeeChargeByPoi\":490,\"logisticsFee\":600,\"onlinePayment\":5447,\"wmPoiReceiveCent\":4406}", "recipient_address": "汇港名苑 (南2区1005)@#广东省深圳市福田区滨河大道滨河大道3119号汇港名苑", "recipient_name": "颜(女士)", "recipient_phone": "13049813276_5307", "remark": "", "result": "ok", "shipper_phone": "", "shipping_fee": 6, "shipping_type": 0, "source_id": 3, "status": 2, "taxpayer_id": "", "total": 54.47, "utime": 1555036346, "wm_order_id_view": 28284722536001020, "wm_poi_address": "深圳市福田区南园街道南华社区滨河路2037号下小庙南区70栋101号滨河街市场", "wm_poi_id": 2828472, "wm_poi_name": "京西菜市(华强南店)", "wm_poi_phone": "13724313878", + } + err := new(PurchaseHandler).OnOrderDetail(result, partner.CreatedPeration) + fmt.Println(err) +} + +func TestName(t *testing.T) { + store, err := dao.GetStoreDetailByVendorStoreID(dao.GetDB(), "1", model.VendorIDMTWM, "1") + fmt.Println(store) + fmt.Println(err) + +} diff --git a/business/partner/purchase/tao_vegetable/mtwm.go b/business/partner/purchase/tao_vegetable/mtwm.go new file mode 100644 index 000000000..32d7a4974 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/mtwm.go @@ -0,0 +1,255 @@ +package mtwm + +import ( + "fmt" + "strings" + "sync" + + "git.rosy.net.cn/jx-callback/business/model/dao" + + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/partner" + "git.rosy.net.cn/jx-callback/business/partner/putils" + "git.rosy.net.cn/jx-callback/globals" + "git.rosy.net.cn/jx-callback/globals/api" +) + +var ( + CurPurchaseHandler *PurchaseHandler +) + +type PurchaseHandler struct { + partner.BasePurchasePlatform + putils.DefSingleStorePlatform + + storeIDs []string + locker sync.RWMutex +} + +func init() { + if api.MtwmAPI != nil || api.Mtwm2API != nil { + CurPurchaseHandler = New() + partner.RegisterPurchasePlatform(CurPurchaseHandler) + } +} + +func New() (obj *PurchaseHandler) { + obj = new(PurchaseHandler) + obj.ISingleStoreStoreSkuHandler = obj + return obj +} + +func (c *PurchaseHandler) GetVendorID() int { + return model.VendorIDTaoVegetable +} + +func (p *PurchaseHandler) GetVendorCategories(ctx *jxcontext.Context) (vendorCats []*model.SkuVendorCategory, err error) { + cats, err := api.MtwmAPI.RetailGetSpTagIds() + if err != nil { + return nil, err + } + vendorCatMapList := make([]map[string]*model.SkuVendorCategory, 3) + manID := 10000 + for i := 0; i < 3; i++ { + vendorCatMapList[i] = make(map[string]*model.SkuVendorCategory) + for _, v := range cats { + if v.Level == 3 { + namePathList := strings.Split(strings.Trim(v.NamePath, ","), ",") + if len(namePathList) != 3 { + panic(fmt.Sprintf("%s没有三级结构", v.NamePath)) + } + name := namePathList[i] + if _, ok := vendorCatMapList[i][name]; !ok { + cat := &model.SkuVendorCategory{ + VendorID: model.VendorIDTaoVegetable, + Name: name, //utils.Interface2String(v["name"]), + Level: i + 1, //int(utils.MustInterface2Int64(v["level"])), + } + vendorCats = append(vendorCats, cat) + vendorCatMapList[i][name] = cat + if i == 2 { + cat.IsLeaf = 1 + cat.VendorCategoryID = utils.Int64ToStr(v.ID) + } else { + cat.VendorCategoryID = utils.Int2Str(manID) // 非叶子结点编码没有实际使用 + manID++ + } + if i > 0 { + cat.ParentID = vendorCatMapList[i-1][namePathList[i-1]].VendorCategoryID + } + } + } + } + } + return vendorCats, nil +} + +func rangeMtwm2JX(areaStr string) string { + var area []interface{} + if err := utils.UnmarshalUseNumber([]byte(areaStr), &area); err == nil { + if len(area) > 0 { + coordList := make([]string, len(area)) + for k, v := range area { + vv := v.(map[string]interface{}) + coordList[k] = fmt.Sprintf("%.6f,%.6f", jxutils.IntCoordinate2Standard(int(utils.ForceInterface2Int64(vv["x"]))), jxutils.IntCoordinate2Standard(int(utils.ForceInterface2Int64(vv["y"])))) + } + return strings.Join(coordList, ";") + } + } + return "" +} + +func openTimeMtwm2JX(vendorOpenTime string) (opTimeList []int16) { + timePairs := strings.Split(vendorOpenTime, ",") + for _, v := range timePairs { + times := strings.Split(v, "-") + if len(times) >= 2 { + opTimeList = append(opTimeList, jxutils.StrTime2JxOperationTime(times[0]+":00", 700), jxutils.StrTime2JxOperationTime(times[1]+":00", 2000)) + } + } + return opTimeList +} + +func openTimeJX2Mtwm(opTimeList []int16) string { + timesLen := len(opTimeList) / 2 * 2 + var strPairs []string + for i := 0; i < timesLen; i += 2 { + if opTimeList[i] != 0 { + strPairs = append(strPairs, jxutils.JxOperationTime2StrTime(opTimeList[i])+"-"+jxutils.JxOperationTime2StrTime(opTimeList[i+1])) + } else { + break + } + } + return strings.Join(strPairs, ",") +} + +func bizStatusMtwm2JX(openLevel, online int) int { + if online != mtwmapi.PoiStatusOnline { + return model.StoreStatusDisabled + } else { + if openLevel == mtwmapi.PoiOpenLevelHaveRest { + return model.StoreStatusClosed + } + } + return model.StoreStatusOpened +} + +func bizStatusJX2Mtwm(status int) (openLevel, online int) { + if status == model.StoreStatusDisabled { + return mtwmapi.PoiOpenLevelHaveRest, mtwmapi.PoiStatusOnline //mtwmapi.PoiStatusOffline + } else if status == model.StoreStatusHaveRest || status == model.StoreStatusClosed { + return mtwmapi.PoiOpenLevelHaveRest, mtwmapi.PoiStatusOnline + } + return mtwmapi.PoiOpenLevelNormal, mtwmapi.PoiStatusOnline +} + +func skuStatusJX2Mtwm(status int) int { + if status == model.SkuStatusNormal { + return mtwmapi.SellStatusOnline + } + return mtwmapi.SellStatusOffline +} + +func (p *PurchaseHandler) UploadImg(ctx *jxcontext.Context, vendorOrgCode, imgURL string, imgData []byte, imgName string, imgType int) (imgHint string, err error) { + poiCode4UploadImg := p.getUploadImgPoiCode() + if poiCode4UploadImg == "" { + return "", fmt.Errorf("找不到一个美团门店来上传图片") + } + if globals.EnableMtwmStoreWrite { + if imgType > model.ImgTypeLocal { + if imgData != nil { + imgHint, err = api.MtwmAPI.ImageUpload(poiCode4UploadImg, imgName, imgData) + } else { + imgHint, err = api.MtwmAPI.ImageUploadByURL(poiCode4UploadImg, imgName, imgURL) + } + } + } else { + imgHint = utils.GetUpperUUID() + } + return imgHint, err +} + +func getStoreIDFromList(storeIDs []string) (poiCode string) { + if len(storeIDs) > 0 { + poiCode = storeIDs[0] + } + return poiCode +} + +func (p *PurchaseHandler) getUploadImgPoiCode() (poiCode string) { + var storeIDs []string + p.locker.RLock() + storeIDs = p.storeIDs + p.locker.RUnlock() + if len(storeIDs) > 0 { + return getStoreIDFromList(storeIDs) + } + + p.locker.Lock() + storeIDs = p.storeIDs + if len(storeIDs) > 0 { + p.locker.Unlock() + return getStoreIDFromList(storeIDs) + } + + defer p.locker.Unlock() + storeIDs, err := api.MtwmAPI.PoiGetIDs() + if err == nil { + if len(storeIDs) > 0 { + p.storeIDs = storeIDs + poiCode = getStoreIDFromList(storeIDs) + } else { + // p.storeIDs = []string{""} + } + } + return poiCode +} + +func GetAPI(appOrgCode string, storeID int, vendorStoreID string) (apiObj *mtwmapi.API) { + if appOrgCode == "" { + globals.SugarLogger.Debugf("getAPI appOrgCode is empty") + } + apiObj = partner.CurAPIManager.GetAPI(model.VendorIDTaoVegetable, appOrgCode).(*mtwmapi.API) + if appOrgCode == globals.Mtwm2Code { + var storeDetail *dao.StoreDetail + if storeID != 0 { + storeDetail, _ = dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDTaoVegetable, appOrgCode) + } else if vendorStoreID != "" { + storeDetail, _ = dao.GetStoreDetailByVendorStoreID(dao.GetDB(), vendorStoreID, model.VendorIDTaoVegetable, appOrgCode) + } + if storeDetail != nil { + apiObj.SetToken(storeDetail.MtwmToken) + } + } + return apiObj +} + +func getAPI(appOrgCode string, storeID int, vendorStoreID string) (apiObj *mtwmapi.API) { + if appOrgCode == "" { + globals.SugarLogger.Debugf("getAPI appOrgCode is empty") + } + apiObj = partner.CurAPIManager.GetAPI(model.VendorIDTaoVegetable, appOrgCode).(*mtwmapi.API) + if appOrgCode == globals.Mtwm2Code { + var storeDetail *dao.StoreDetail + if storeID != 0 { + storeDetail, _ = dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDTaoVegetable, appOrgCode) + } else if vendorStoreID != "" { + storeDetail, _ = dao.GetStoreDetailByVendorStoreID(dao.GetDB(), vendorStoreID, model.VendorIDTaoVegetable, appOrgCode) + } + if storeDetail != nil { + apiObj.SetToken(storeDetail.MtwmToken) + } + } + return apiObj +} + +func getAPIWithoutToken(appOrgCode string) (apiObj *mtwmapi.API) { + if appOrgCode == "" { + globals.SugarLogger.Warnf("getAPI appOrgCode is empty") + } + return partner.CurAPIManager.GetAPI(model.VendorIDTaoVegetable, appOrgCode).(*mtwmapi.API) +} diff --git a/business/partner/purchase/tao_vegetable/mtwm_test.go b/business/partner/purchase/tao_vegetable/mtwm_test.go new file mode 100644 index 000000000..bfb73cdfa --- /dev/null +++ b/business/partner/purchase/tao_vegetable/mtwm_test.go @@ -0,0 +1,50 @@ +package mtwm + +import ( + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" + "testing" + "time" + + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + + "git.rosy.net.cn/baseapi/utils" + _ "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" + "git.rosy.net.cn/jx-callback/globals/testinit" +) + +const ( + testShopVendorID = "2523687" + testShopID = 2 +) + +func init() { + testinit.Init() +} + +// 获取平台分类属性(三级分类标签) +func TestGetVendorCategories(t *testing.T) { + result, err := new(PurchaseHandler).GetVendorCategories(jxcontext.AdminCtx) + if err != nil { + t.Fatal(err) + } + for _, v := range result { + data := model.SkuVendorCategory{ + ModelIDCUL: model.ModelIDCUL{ + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + LastOperator: "刘磊", + }, + VendorCategoryID: v.VendorCategoryID, + VendorID: v.VendorID, + Name: v.Name, + IsLeaf: v.IsLeaf, + Level: v.Level, + ParentID: v.ParentID, + } + if err := dao.CreateEntity(dao.GetDB(), &data); err != nil { + t.Log(utils.Format4Output(err, false)) + } + } + t.Log(utils.Format4Output(result, false)) +} diff --git a/business/partner/purchase/tao_vegetable/order.go b/business/partner/purchase/tao_vegetable/order.go new file mode 100644 index 000000000..1843fdae7 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/order.go @@ -0,0 +1,872 @@ +package mtwm + +import ( + "errors" + "fmt" + "math" + "net/url" + "regexp" + "strings" + "time" + + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" + "git.rosy.net.cn/jx-callback/business/partner" + "git.rosy.net.cn/jx-callback/globals" +) + +const ( + FakeMsgType = "fakeMsgType" + + fakeFinishedPickup = "fake_finished_pickup" + fakeUserApplyCancel = "fake_user_apply_cancel" + fakeMerchantAgreeApplyCancel = "fake_merchant_agree_apply_cancel" + fakeRefuseUserApplyCancel = "fake_refuse_user_apply_cancel" + fakeUserUndoApplyCancel = "fake_user_undo_apply_cancel" + fakeOrderAdjustFinished = "fake_order_adjust_finished" + + keyVendorOrgCode = "vendorOrgCode" +) + +const ( + SelfDeliveryCarrierNo = 1 // 美团配送方式:0-美团专送,1-商家自送 +) + +const ( +// pickupOrderDelay = 260 * time.Second +// pickupOrderDelay = 1 * time.Second + +// callDeliveryDelay = 10 * time.Minute +// callDeliveryDelayGap = 30 +) + +var ( + specPat = regexp.MustCompile(`(\d+)(.+)`) +) + +var ( + VendorStatus2StatusMap = map[string]int{ + mtwmapi.OrderStatusUserCommitted: model.OrderStatusUnknown, + mtwmapi.OrderStatusNew: model.OrderStatusNew, + // mtwmapi.OrderStatusReceived: model.OrderStatusAccepted, + // mtwmapi.OrderStatusAccepted: model.OrderStatusFinishedPickup, + mtwmapi.OrderStatusAccepted: model.OrderStatusAccepted, + + mtwmapi.OrderStatusDelivering: model.OrderStatusDelivering, + mtwmapi.OrderStatusDelivered: model.OrderStatusUnknown, // 以mtwmapi.OrderStatusFinished为结束状态,这个当成一个中间状态(且很少看到这个状态) + mtwmapi.OrderStatusFinished: model.OrderStatusFinished, + mtwmapi.OrderStatusCanceled: model.OrderStatusCanceled, + + fakeFinishedPickup: model.OrderStatusFinishedPickup, + fakeOrderAdjustFinished: model.OrderStatusAdjust, + fakeRefuseUserApplyCancel: model.OrderStatusVendorRejectCancel, + fakeUserApplyCancel: model.OrderStatusApplyCancel, + fakeUserUndoApplyCancel: model.OrderStatusUndoApplyCancel, + fakeMerchantAgreeApplyCancel: model.OrderStatusCanceled, + } + + skuActTypeMap = map[int]int{ + mtwmapi.ExtrasPromotionTypeTeJiaCai: 1, + mtwmapi.ExtrasPromotionTypeZheKouCai: 1, + mtwmapi.ExtrasPromotionTypeSecondHalfPrice: 1, + } +) + +func (p *PurchaseHandler) getStatusFromVendorStatus(vendorStatus string) int { + if status, ok := VendorStatus2StatusMap[vendorStatus]; ok { + return status + } + return model.OrderStatusUnknown +} + +func (p *PurchaseHandler) getOrder(vendorOrgCode, vendorOrderID, vendorStoreID string) (order *model.GoodsOrder, orderMap map[string]interface{}, err error) { + result, err := getAPI(vendorOrgCode, 0, vendorStoreID).OrderGetOrderDetail(utils.Str2Int64(vendorOrderID), true) + if err == nil { + result[keyVendorOrgCode] = vendorOrgCode + order = p.Map2Order(result) + } + return order, result, err +} + +func (p *PurchaseHandler) getOrderRider(vendorOrgCode, vendorStoreID string, param map[string]interface{}) (err error) { + return getAPI(vendorOrgCode, 0, vendorStoreID).OrderStatusAndPsInfo(param) +} + +func (p *PurchaseHandler) GetOrderRider(vendorOrgCode, vendorStoreID string, param map[string]interface{}) (err error) { + return p.getOrderRider(vendorOrgCode, vendorStoreID, param) +} + +func (p *PurchaseHandler) GetOrder(vendorOrgCode, vendorOrderID, vendorStoreID string) (order *model.GoodsOrder, err error) { + order, _, err = p.getOrder(vendorOrgCode, vendorOrderID, vendorStoreID) + return order, err +} + +func (p *PurchaseHandler) GetOrderStatus(vendorOrgCode, vendorOrderID string) (status int, err error) { + if order, _ := partner.CurOrderManager.LoadOrder(vendorOrderID, model.VendorIDMTWM); order != nil { + status, err = getAPI(vendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderViewStatus(utils.Str2Int64(vendorOrderID)) + } + if err == nil { + status = p.getStatusFromVendorStatus(utils.Int2Str(status)) + } + return status, err +} + +func (p *PurchaseHandler) Map2Order(orderData map[string]interface{}) (order *model.GoodsOrder) { + result := orderData + vendorOrderID := utils.Int64ToStr(utils.MustInterface2Int64(result["order_id"])) + // 因为美团外卖不能自动设置商家门店号,且只能通过商家门店号来访问门店, + // 为了在后台设置简单一致,把app_poi_code直接当成平台门店号使用(即在后台设置时,平台门店号与商家门店号一样) + // 订单中wm_poi_id实际来平台门店号,app_poi_code为商家门店号,这样一来,这两个就相同了 + + //_修改为, + caution := strings.ReplaceAll(utils.Interface2String(result["caution"]), "_", ",") + + order = &model.GoodsOrder{ + VendorOrderID: vendorOrderID, + // VendorOrderID2: utils.Int64ToStr(utils.MustInterface2Int64(result["wm_order_id_view"])), + VendorID: model.VendorIDMTWM, + VendorStoreID: result["app_poi_code"].(string), + StoreID: 0, + // VendorStoreID: utils.Int64ToStr(utils.MustInterface2Int64(result["wm_poi_id"])), + // StoreID: int(utils.Str2Int64WithDefault(utils.Interface2String(result["app_poi_code"]), 0)), + StoreName: result["wm_poi_name"].(string), + ConsigneeName: result["recipient_name"].(string), + ConsigneeMobile: jxutils.FormalizeMobile(result["recipient_phone"].(string)), + ConsigneeAddress: result["recipient_address"].(string), + CoordinateType: model.CoordinateTypeMars, + //BuyerComment: utils.TrimBlankChar(utils.Interface2String(result["caution"])), + BuyerComment: utils.TrimBlankChar(caution), + ExpectedDeliveredTime: getTimeFromTimestamp(utils.Interface2Int64WithDefault(result["delivery_time"], 0)), + PickDeadline: utils.DefaultTimeValue, + VendorStatus: utils.Int64ToStr(utils.MustInterface2Int64(result["status"])), + OrderSeq: int(utils.MustInterface2Int64(result["day_seq"])), + StatusTime: getTimeFromTimestamp(utils.MustInterface2Int64(result["ctime"])), + OrderCreatedAt: getTimeFromTimestamp(utils.MustInterface2Int64(result["ctime"])), + // OrderFinishedAt: getTimeFromTimestamp(utils.MustInterface2Int64(result["order_completed_time"])), + OriginalData: string(utils.MustMarshal(result)), + ActualPayPrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(result["total"])), + BaseFreightMoney: jxutils.StandardPrice2Int(utils.Interface2Float64WithDefault(result["shipping_fee"], 0)), + + InvoiceTitle: utils.Interface2String(result["invoice_title"]), + InvoiceTaxerID: utils.Interface2String(result["taxpayer_id"]), + InvoiceEmail: jxutils.GetOneEmailFromStr(utils.Interface2String(result["caution"])), + + VendorOrgCode: utils.Interface2String(result[keyVendorOrgCode]), + } + if result["order_completed_time"] != nil { + order.OrderFinishedAt = getTimeFromTimestamp(utils.MustInterface2Int64(result["order_completed_time"])) + } else { + order.OrderFinishedAt = utils.DefaultTimeValue + } + pickType := int(utils.Interface2Int64WithDefault(result["pick_type"], 0)) + if pickType == mtwmapi.OrderPickTypeSelf { + order.DeliveryType = model.OrderDeliveryTypeSelfTake + } else { + logisticsCode := utils.Interface2String(result["logistics_code"]) + if logisticsCode == mtwmapi.PeiSongTypeSelf || logisticsCode == mtwmapi.PeiSongTypeMTZSPT { + order.DeliveryType = model.OrderDeliveryTypeStoreSelf + } else { + order.DeliveryType = model.OrderDeliveryTypePlatform + } + } + openUID := utils.Interface2Int64WithDefault(result["openUid"], 0) + if openUID > 0 { + order.VendorUserID = utils.Int64ToStr(openUID) + } + // 不设置最晚拣货时间,以缺省值为准 + // if utils.IsTimeZero(order.PickDeadline) && !utils.IsTimeZero(order.StatusTime) { + // order.PickDeadline = order.StatusTime.Add(pickupOrderDelay) // 美团外卖要求在5分钟内拣货,不然订单会被取消 + // } + order.Status = p.getStatusFromVendorStatus(order.VendorStatus) + if utils.IsTimeZero(order.ExpectedDeliveredTime) { + order.BusinessType = model.BusinessTypeImmediate + } else { + order.BusinessType = model.BusinessTypeDingshida + } + + originalLng := utils.MustInterface2Float64(result["longitude"]) + originalLat := utils.MustInterface2Float64(result["latitude"]) + order.ConsigneeLng = jxutils.StandardCoordinate2Int(originalLng) + order.ConsigneeLat = jxutils.StandardCoordinate2Int(originalLat) + + var detail []map[string]interface{} + if err := utils.UnmarshalUseNumber([]byte(result["detail"].(string)), &detail); err != nil { + panic(fmt.Sprintf("mtwm Map2Order vendorID:%s failed with error:%v", vendorOrderID, err)) + } + + // 添加需要赠送的东西 + if result["extras"] != nil { + var extraList []*mtwmapi.OrderExtraInfo + if err := utils.UnmarshalUseNumber([]byte(result["extras"].(string)), &extraList); err != nil { + panic(fmt.Sprintf("mtwm Map2Order vendorID:%s failed with error:%v", vendorOrderID, err)) + } + for _, extra := range extraList { + order.DiscountMoney += jxutils.StandardPrice2Int(extra.ReduceFee) + if extra.Type == mtwmapi.ExtrasPromotionTypeTaoCanZeng || extra.Type == mtwmapi.ExtrasPromotionTypeManZeng { + sku := &model.OrderSku{ + VendorOrderID: order.VendorOrderID, + VendorID: model.VendorIDMTWM, + Count: 1, + SkuID: 0, + VendorSkuID: "", + SkuName: extra.Remark, + Weight: 0, + SalePrice: 0, + StoreSubName: utils.Int2Str(extra.Type), + } + order.Skus = append(order.Skus, sku) + } + } + } + + if poiReceiveDetailStr := utils.Interface2String(result["poi_receive_detail"]); poiReceiveDetailStr != "" { + var poiReceiveDetail *mtwmapi.PoiReceiveDetailInfo + utils.UnmarshalUseNumber([]byte(poiReceiveDetailStr), &poiReceiveDetail) + if poiReceiveDetail != nil { + order.TotalShopMoney = poiReceiveDetail.WmPoiReceiveCent + for _, v := range poiReceiveDetail.ActOrderChargeByMt { + order.PmSubsidyMoney += v.MoneyCent + } + } + } + + var skuBenefitDetailMap map[string]*mtwmapi.SkuBenefitDetailInfo + if skuBenefitDetai := utils.Interface2String(result["sku_benefit_detail"]); skuBenefitDetai != "" { + skuBenefitDetailMap = make(map[string]*mtwmapi.SkuBenefitDetailInfo) + var skuBenefitDetailList []*mtwmapi.SkuBenefitDetailInfo + utils.UnmarshalUseNumber([]byte(skuBenefitDetai), &skuBenefitDetailList) + for _, v := range skuBenefitDetailList { + skuBenefitDetailMap[v.SkuID] = v + } + } + ignoreSkuMap := make(map[int]int) + multiSkuMap := make(map[int]int) + for _, product := range detail { + skuName := product["food_name"].(string) + skuID := utils.Interface2String(product["sku_id"]) + sku := &model.OrderSku{ + VendorOrderID: order.VendorOrderID, + VendorID: model.VendorIDMTWM, + Count: int(utils.MustInterface2Float64(product["quantity"])), + SkuID: int(utils.Str2Int64WithDefault(skuID, 0)), + VendorSkuID: skuID, + SkuName: skuName, + Weight: getSkuWeight(product), + VendorPrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])), + SalePrice: jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])), + } + if product["upc"] != nil && product["upc"].(string) != "" { + sku.Upc = product["upc"].(string) + } + _, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(sku.SkuName) + nameWeight := jxutils.FormatSkuWeight(specQuality, specUnit) + if nameWeight == 0 { + skuName += " " + product["spec"].(string) + } + if sku.VendorSkuID == "" { + if !strings.Contains(product["app_food_code"].(string), "mtcode") { + sku.VendorSkuID = product["app_food_code"].(string) + } else { + sku.VendorSkuID = utils.Int64ToStr(utils.Interface2Int64WithDefault(product["mt_sku_id"], 0)) + } + } + if sku.Weight == 0 { + sku.Weight = 222 // 如果名字里找不到缺省给半斤左右的一个特别值 + } + if skuBenefitDetailMap != nil && skuBenefitDetailMap[sku.VendorSkuID] != nil && ignoreSkuMap[sku.SkuID] == 0 /* && sku.Count == 1 */ { + for _, v := range skuBenefitDetailMap[sku.VendorSkuID].WmAppOrderActDetails { + if /*skuActTypeMap[v.Type] == 1 && */ strings.Index(v.Remark, skuName) >= 0 && sku.Count == v.Count { + if sku.SalePrice-jxutils.StandardPrice2Int(v.MtCharge+v.PoiCharge) < 0 { + continue + } else { + ignoreSkuMap[sku.SkuID] = 1 + sku.SalePrice -= jxutils.StandardPrice2Int(v.MtCharge + v.PoiCharge) + } + sku.StoreSubName = utils.Int2Str(v.Type) + } + } + } + if sku.SalePrice < 0 { + sku.SalePrice = jxutils.StandardPrice2Int(utils.MustInterface2Float64(product["price"])) + } + order.Skus = append(order.Skus, sku) + multiSkuMap[sku.SkuID]++ + } + for _, v := range order.Skus { + if multiSkuMap[v.SkuID] > 1 && v.SalePrice == v.VendorPrice { + v.IsVendorAct = model.YES + } + } + + // 包装袋金额设置 + store, _ := dao.GetStoreDetail(dao.GetDB(), order.JxStoreID, order.VendorID, order.VendorOrgCode) + order.PackagePrice = store.PackageSetting + jxutils.RefreshOrderSkuRelated(order) + return order +} + +func getRefundSkuDetailList(msg *mtwmapi.CallbackMsg, order *model.GoodsOrder) (skuList []*mtwmapi.RefundSkuDetail, err error) { + if false { + skuList = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").GetRefundSkuDetailFromMsg(msg) + } else { + refundOrderDetailList, err2 := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").GetOrderRefundDetail(utils.Str2Int64(GetOrderIDFromMsg(msg)), mtwmapi.RefundTypePart) + if err = err2; err == nil { + for _, v := range refundOrderDetailList { + skuList = append(skuList, v.WmAppRetailForOrderPartRefundList...) + } + } + } + return skuList, err +} + +func getSkuWeight(product map[string]interface{}) (weight int) { + if weight = int(utils.Interface2Int64WithDefault(product["weight"], 0)); weight == 0 { + searchResult := specPat.FindStringSubmatch(product["spec"].(string)) + if len(searchResult) == 3 { + weight = jxutils.FormatSkuWeight(float32(utils.Str2Float64WithDefault(searchResult[1], 0)), utils.TrimBlankChar(searchResult[2])) + } + if weight == 0 { + _, _, _, specUnit, _, specQuality := jxutils.SplitSkuName(product["food_name"].(string)) + weight = jxutils.FormatSkuWeight(specQuality, specUnit) + } + } + return weight +} + +func (c *PurchaseHandler) onOrderMsg(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackResponse) { + var err error + if c.isAfsMsg(msg) { + response = c.OnAfsOrderMsg(msg) + return response + } else { + status := c.callbackMsg2Status(msg) + if partner.CurOrderManager.GetStatusDuplicatedCount(status) > 0 { + return nil + } + if msg.Cmd == mtwmapi.MsgTypeNewOrder { + order, orderMap, err2 := c.getOrder(msg.AppID, GetOrderIDFromMsg(msg), GetVendorStoreIDFromMsg(msg)) + if err = err2; err == nil { + err = partner.CurOrderManager.OnOrderNew(order, c.callbackMsg2Status(msg)) + if err == nil { + utils.CallFuncAsync(func() { + if msg.Cmd == mtwmapi.MsgTypeNewOrder { + c.OnOrderDetail(orderMap, partner.CreatedPeration) + } else { + c.OnOrderDetail(orderMap, partner.UpdatedPeration) + } + }) + } + } + } else { + if status != nil { + var order *model.GoodsOrder + if order, err = partner.CurOrderManager.LoadOrder(GetOrderIDFromMsg(msg), model.VendorIDMTWM); err == nil { + // if order, err = c.GetOrder(msg.AppID, GetOrderIDFromMsg(msg)); err == nil { + if status.Status == model.OrderStatusAdjust { + skuList, err2 := getRefundSkuDetailList(msg, order) + if err = err2; err == nil { + var removedSkuList []*model.OrderSku + for _, mtwmSku := range skuList { + order.ActualPayPrice -= jxutils.StandardPrice2Int(mtwmSku.RefundPrice) * int64(mtwmSku.Count) + removedSkuList = append(removedSkuList, &model.OrderSku{ + SkuID: int(utils.Str2Int64WithDefault(mtwmSku.SkuID, 0)), + Count: mtwmSku.Count, + }) + } + order = jxutils.RemoveSkuFromOrder(order, removedSkuList) + jxutils.RefreshOrderSkuRelated(order) + err = partner.CurOrderManager.OnOrderAdjust(order, status) + } + } else { + if status.Status == model.OrderStatusDelivering { + // 美团订单即使时在配送状态时,如果之前没有调用过拣货完成,也会对门店指标生成影响,这里强制再调用拣货完成,且忽略错误 + utils.CallFuncAsync(func() { + if globals.EnableMtwmStoreWrite { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").PreparationMealComplete(utils.Str2Int64(status.VendorOrderID)) + } + }) + } + err = partner.CurOrderManager.OnOrderStatusChanged(msg.AppID, status) + if err == nil && msg.Cmd == mtwmapi.MsgTypeOrderFinished { + utils.CallFuncAsync(func() { + orderMap, err := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderGetOrderDetail(utils.Str2Int64(GetOrderIDFromMsg(msg)), true) + if err == nil && utils.MustInterface2Int64(orderMap["is_third_shipping"]) == SelfDeliveryCarrierNo { + c.OnOrderDetail(orderMap, partner.UpdatedPeration) + } + }) + } + + } + } + } + } + } + return mtwmapi.Err2CallbackResponse(err, "") +} + +func (c *PurchaseHandler) callbackMsg2Status(msg *mtwmapi.CallbackMsg) (orderStatus *model.OrderStatus) { + orderID := GetOrderIDFromMsg(msg) + vendorStatus := msg.Cmd + remark := "" + statusTime := utils.Str2Int64(msg.FormData.Get("timestamp")) + switch msg.Cmd { + case mtwmapi.MsgTypeUserUrgeOrder, mtwmapi.MsgTypeOrderModified, mtwmapi.MsgTypeOrderFinancial: + vendorStatus = msg.Cmd + case mtwmapi.MsgTypeOrderCanceled: + vendorStatus = mtwmapi.OrderStatusCanceled + remark = msg.FormData.Get("reason") + case FakeMsgType, mtwmapi.MsgTypeNewOrder, mtwmapi.MsgTypeOrderAccepted, mtwmapi.MsgTypeOrderFinished: + vendorStatus = msg.FormData.Get("status") + statusTime = utils.Str2Int64(msg.FormData.Get("utime")) + case mtwmapi.MsgTypeOrderRefund, mtwmapi.MsgTypeOrderPartialRefund: // 订单退款,部分退款 + notifyType := msg.FormData.Get("notify_type") + vendorStatus = msg.Cmd + "-" + notifyType + if true { // 已经提前判断了,到这里的都是售中 + remark = msg.FormData.Get("reason") + if msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { // 部分退款 + if notifyType == mtwmapi.NotifyTypePartyApply { + if globals.EnableMtwmStoreWrite { + //if order, _ := partner.CurOrderManager.LoadOrder(orderID, model.VendorIDMTWM); order != nil { + // getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderRefundAgree(utils.Str2Int64(orderID), "自动确认退款") + //} + // goods, err := dao.GetSimpleOrder(dao.GetDB(), orderID) + // if err == nil { + // if goods.Status < model.OrderStatusDelivering { + // } else { + // api.MtwmAPI.OrderRefundReject(utils.Str2Int64(orderID), "商品配送中,请联系门店。") // todo 京东与饿百都没有售前用户提出订单调整的,自动拒绝调整单 + // } + // } + } + } else if notifyType == mtwmapi.NotifyTypeSuccess { + vendorStatus = fakeOrderAdjustFinished + } + } else if msg.Cmd == mtwmapi.MsgTypeOrderRefund { + if notifyType == mtwmapi.NotifyTypeApply { + vendorStatus = fakeUserApplyCancel + } else if notifyType == mtwmapi.NotifyTypeCancelRefund || notifyType == mtwmapi.NotifyTypeCancelRefundComplaint { + vendorStatus = fakeUserUndoApplyCancel + } else if notifyType == mtwmapi.NotifyTypeReject { + vendorStatus = fakeRefuseUserApplyCancel + } else if notifyType == mtwmapi.NotifyTypeSuccess { + vendorStatus = fakeMerchantAgreeApplyCancel // todo 可能导致订单取消消息重复 + } + } + } + default: + globals.SugarLogger.Errorf("mtwm unkonw msg:%s", utils.Format4Output(msg, false)) + } + if vendorStatus != "" { + orderStatus = &model.OrderStatus{ + VendorOrderID: orderID, + VendorID: model.VendorIDMTWM, + OrderType: model.OrderTypeOrder, + RefVendorOrderID: orderID, + RefVendorID: model.VendorIDMTWM, + VendorStatus: vendorStatus, + Status: c.getStatusFromVendorStatus(vendorStatus), + StatusTime: getTimeFromTimestamp(statusTime), + Remark: remark, + } + } + return orderStatus +} + +func (c *PurchaseHandler) postFakeMsg(vendorOrderID, cmd, vendorStatus string) { + msg := &mtwmapi.CallbackMsg{ + Cmd: cmd, + FormData: make(url.Values), + } + timeStr := utils.Int64ToStr(time.Now().Unix()) + msg.FormData.Set(mtwmapi.KeyOrderID, vendorOrderID) + msg.FormData.Set("status", vendorStatus) + msg.FormData.Set("timestamp", timeStr) + msg.FormData.Set("utime", timeStr) + utils.CallFuncAsync(func() { + c.onOrderMsg(msg) + }) +} + +func (c *PurchaseHandler) AcceptOrRefuseOrder(order *model.GoodsOrder, isAcceptIt bool, userName string) (err error) { + if isAcceptIt { + if globals.EnableMtwmStoreWrite { + // err = api.MtwmAPI.OrderReceived(utils.Str2Int64(order.VendorOrderID)) + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderConfirm(utils.Str2Int64(order.VendorOrderID)) + if err != nil { + if utils.IsErrMatch(err, utils.Int2Str(mtwmapi.ErrCodeOpFailed), []string{ + "订单已经确认过了", + }) { + err = nil + } else { + globals.SugarLogger.Warnf("mtwm AcceptOrRefuseOrder orderID:%s failed with err:%v", order.VendorOrderID, err) + } + } + } + // if err == nil { + // c.postFakeMsg(order.VendorOrderID, FakeMsgType, mtwmapi.OrderStatusReceived) + // } + } else { + if globals.EnableMtwmStoreWrite { + err = c.CancelOrder(jxcontext.AdminCtx, order, "bu") + } + } + return err +} + +func (c *PurchaseHandler) PickupGoods(order *model.GoodsOrder, isSelfDelivery bool, userName string) (err error) { + if !isSelfDelivery { + if globals.EnableMtwmStoreWrite { + // err = api.MtwmAPI.OrderConfirm(utils.Str2Int64(order.VendorOrderID)) + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").PreparationMealComplete(utils.Str2Int64(order.VendorOrderID)) + } + } + if err == nil { + c.postFakeMsg(order.VendorOrderID, FakeMsgType, fakeFinishedPickup) + } + return err +} + +func (p *PurchaseHandler) AcceptOrRefuseFailedGetOrder(ctx *jxcontext.Context, order *model.GoodsOrder, isAcceptIt bool) (err error) { + return err +} + +func (p *PurchaseHandler) CallCourier(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 拣货失败后再次招唤平台配送 + return err +} + +func (p *PurchaseHandler) ConfirmReceiveGoods(ctx *jxcontext.Context, order *model.GoodsOrder) (err error) { // 投递失败后确认收到退货 + return err +} + +// 美团预定单不能转商家自送 +func (c *PurchaseHandler) CanSwitch2SelfDeliver(order *model.GoodsOrder) (isCan bool, err error) { + return order.BusinessType != model.BusinessTypeDingshida, nil +} + +func (c *PurchaseHandler) Swtich2SelfDeliver(order *model.GoodsOrder, userName string) (err error) { + if globals.EnableMtwmStoreWrite { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderLogisticsChange2Self(utils.Str2Int64(order.VendorOrderID)) + } + return err +} + +func (c *PurchaseHandler) Swtich2SelfDelivered(order *model.GoodsOrder, userName string) (err error) { + if globals.EnableMtwmStoreWrite { + // 您好,之前的答复已经更正为,调用变更配送状态的接口,会校验门店的配送类型。美团配送的门店即便转自配后因门店配送类型是美团配送所以无法调用接口变更配送状态。可提醒顾客点击确认收货。谢谢 + // 非自配送门店订单调用OrderArrived好像会报错:{"data":"ng","error":{"code":1038,"msg":"只允许商家配送调用该接口"}} + // err = api.MtwmAPI.OrderArrived(utils.Str2Int64(order.VendorOrderID)) + } + return err +} + +func (c *PurchaseHandler) SelfDeliverDelivering(order *model.GoodsOrder, userName string) (err error) { + if globals.EnableMtwmStoreWrite { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderDelivering(utils.Str2Int64(order.VendorOrderID)) + } + return err +} + +func (c *PurchaseHandler) SelfDeliverDelivered(order *model.GoodsOrder, userName string) (err error) { + if globals.EnableMtwmStoreWrite { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderArrived(utils.Str2Int64(order.VendorOrderID)) + } + return err +} + +func getTimeFromTimestamp(timeStamp int64) time.Time { + if timeStamp < 1538103149 { // 立即达订单给的是1(而不是空,0),1538103149不是特殊值,只是一个任意之前的时间,这样写可以处理 + return utils.DefaultTimeValue + } + return utils.Timestamp2Time(timeStamp) +} + +func (c *PurchaseHandler) GetOrderRealMobile(ctx *jxcontext.Context, order *model.GoodsOrder) (mobile string, err error) { + err = errors.New("美团外卖还未实现GetOrderRealMobile") + return mobile, err +} + +func (c *PurchaseHandler) AgreeOrRefuseCancel(ctx *jxcontext.Context, order *model.GoodsOrder, isAgree bool, reason string) (err error) { + if globals.EnableMtwmStoreWrite { + if isAgree { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderRefundAgree(utils.Str2Int64(order.VendorOrderID), reason) + } else { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderRefundReject(utils.Str2Int64(order.VendorOrderID), reason) + } + } + return err +} + +func (c *PurchaseHandler) CancelOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) { + if globals.EnableMtwmStoreWrite { + if err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderCancel(utils.Str2Int64(order.VendorOrderID), reason, mtwmapi.CancelReasonOther); err == nil { + // 调用开放平台接口取消订单,不推送取消订单消息和退款消息。 + c.postFakeMsg(order.VendorOrderID, mtwmapi.MsgTypeOrderCanceled, mtwmapi.OrderStatusCanceled) + } + } + return err +} + +func (c *PurchaseHandler) AdjustOrder(ctx *jxcontext.Context, order *model.GoodsOrder, removedSkuList []*model.OrderSku, reason string) (err error) { + // 美团外卖必须要确认订单后才能调整单 + if order.Status < model.OrderStatusFinishedPickup { + err = c.PickupGoods(order, false, ctx.GetUserName()) + } + if err == nil { + var skuList []*mtwmapi.RefundSku + for _, sku := range removedSkuList { + skuID := utils.Int2Str(jxutils.GetSkuIDFromOrderSku(sku)) + skuList = append(skuList, &mtwmapi.RefundSku{ + AppFoodCode: skuID, + SkuID: skuID, + Count: sku.Count, + }) + } + if globals.EnableMtwmStoreWrite { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").OrderApplyPartRefund(utils.Str2Int64(order.VendorOrderID), reason, skuList) + } + } + return err +} + +func (c *PurchaseHandler) ListOrders(ctx *jxcontext.Context, vendorOrgCode string, parentTask tasksch.ITask, queryDate time.Time, vendorStoreID string) (vendorOrderIDs []string, err error) { + if utils.IsTimeZero(queryDate) { + return nil, fmt.Errorf("queryDate必须指定") + } + queryDate = utils.Time2Date(queryDate) + + var vendorStoreIDs []string + if vendorStoreID == "" { + vendorStoreIDs, err = c.GetAllStoresVendorID(ctx, vendorOrgCode) + if err != nil { + return nil, err + } + } else { + vendorStoreIDs = []string{vendorStoreID} + } + task := tasksch.NewParallelTask("mtwm ListOrders", nil, ctx, + func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { + vendorStoreID := batchItemList[0].(string) + var orderIDs []string + seqStart := 1 + i := 0 + for { + batchSize := int(math.Min(math.Pow(2, float64(i*3)), float64(mtwmapi.MaxGap4GetOrderIdByDaySeq))) + seqEnd := seqStart + batchSize - 1 + var tmpOrderIDs []int64 + if seqStart == seqEnd { + if vendorOderID, err2 := getAPI(vendorOrgCode, 0, vendorStoreID).GetOrderIdByDaySeqSingle(vendorStoreID, queryDate, seqStart); err2 == nil { + tmpOrderIDs = []int64{vendorOderID} + } + } else { + tmpOrderIDs, err = getAPI(vendorOrgCode, 0, vendorStoreID).GetOrderIdByDaySeq(vendorStoreID, queryDate, seqStart, seqEnd) + } + if len(tmpOrderIDs) > 0 { + for _, v := range tmpOrderIDs { + orderIDs = append(orderIDs, utils.Int64ToStr(v)) + } + } + if err != nil || len(tmpOrderIDs) < batchSize { + err = nil + break + } + seqStart = seqEnd + 1 + i++ + } + retVal = orderIDs + return retVal, nil + }, vendorStoreIDs) + tasksch.HandleTask(task, parentTask, true).Run() + orderList, err := task.GetResult(0) + if err == nil && len(orderList) > 0 { + vendorOrderIDs = make([]string, len(orderList)) + for k, v := range orderList { + vendorOrderIDs[k] = v.(string) + } + } + return vendorOrderIDs, err +} + +// func (c *PurchaseHandler) UpdateWaybillTip(ctx *jxcontext.Context, order *model.GoodsOrder, tipFee int64) (err error) { +// if globals.EnableMtwmStoreWrite { +// err = api.MtwmAPI.OrderUpdateTip(utils.Str2Int64(order.VendorOrderID), jxutils.IntPrice2Standard(tipFee)) +// } +// return err +// } + +func (p *PurchaseHandler) GetOrderConsigneeNumber(ctx *jxcontext.Context, storeID int, vendorStoreID string) (numberList []*partner.OrderPhoneNumberInfo, err error) { + offset := 0 + for { + store, _ := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDMTWM, "") + result, err2 := getAPI(store.VendorOrgCode, storeID, "").OrderBatchPullPhoneNumber(vendorStoreID, offset, mtwmapi.MaxBatchPullPhoneNumberLimit) + if err = err2; err == nil { + for _, v := range result { + v2 := &partner.OrderPhoneNumberInfo{ + VendorOrderID: utils.Int64ToStr(v.OrderID), + PhoneNumber: v.RealPhoneNumber, + } + if v2.PhoneNumber == "" { + v2.PhoneNumber = v.RealOrderPhoneNumber + } + numberList = append(numberList, v2) + } + if len(result) <= mtwmapi.MaxBatchPullPhoneNumberLimit { + break + } + offset += mtwmapi.MaxBatchPullPhoneNumberLimit + } else { + break + } + } + return numberList, err +} + +func (p *PurchaseHandler) GetOrderCourierNumber(ctx *jxcontext.Context, storeID int, vendorStoreID string) (numberList []*partner.OrderPhoneNumberInfo, err error) { + offset := 0 + for { + store, _ := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDMTWM, "") + result, err2 := getAPI(store.VendorOrgCode, 0, "").OrderGetRiderInfoPhoneNumber(vendorStoreID, offset, mtwmapi.MaxBatchPullPhoneNumberLimit) + if err = err2; err == nil { + for _, v := range result { + numberList = append(numberList, &partner.OrderPhoneNumberInfo{ + VendorOrderID: utils.Int64ToStr(v.OrderID), + PhoneNumber: v.RiderRealPhoneNumber, + }) + } + if len(result) <= mtwmapi.MaxBatchPullPhoneNumberLimit { + break + } + offset += mtwmapi.MaxBatchPullPhoneNumberLimit + } else { + break + } + } + return numberList, err +} + +func (p *PurchaseHandler) onNumberDowngrade(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackResponse) { + userNumberMap := make(map[string]*partner.OrderPhoneNumberInfo) + courierNumberMap := make(map[string]*partner.OrderPhoneNumberInfo) + orderMap := make(map[string]int) + ctx := jxcontext.AdminCtx + task := tasksch.NewParallelTask("美团外卖平台处理隐私号降级通知", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx, + func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { + step := batchItemList[0].(int) + switch step { + case 0: + userNumberList, err2 := p.GetOrderConsigneeNumber(ctx, 0, "") + if err = err2; err == nil { + for _, v := range userNumberList { + userNumberMap[v.VendorOrderID] = v + orderMap[v.VendorOrderID] = 1 + } + } + case 1: + courierNumberList, err2 := p.GetOrderCourierNumber(ctx, 0, "") + if err = err2; err == nil { + for _, v := range courierNumberList { + courierNumberMap[v.VendorOrderID] = v + orderMap[v.VendorOrderID] = 1 + } + } + case 2: + orderList := jxutils.StringMap2List(orderMap) + if len(orderList) > 0 { + updateTask := tasksch.NewParallelTask("美团外卖平台处理隐私号降级通知/处理订单", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx, + func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { + vendorOrderID := batchItemList[0].(string) + db := dao.GetDB() + if userNumberMap[vendorOrderID] != nil { + _, err = dao.UpdateEntityByKV(db, &model.GoodsOrder{}, map[string]interface{}{ + "ConsigneeMobile": userNumberMap[vendorOrderID].PhoneNumber, + "ConsigneeMobile2": userNumberMap[vendorOrderID].PhoneNumber, + }, map[string]interface{}{ + model.FieldVendorOrderID: vendorOrderID, + model.FieldVendorID: model.VendorIDMTWM, + }) + } + if courierNumberMap[vendorOrderID] != nil { + _, err = dao.UpdateEntityByKV(db, &model.Waybill{}, map[string]interface{}{ + "CourierMobile": courierNumberMap[vendorOrderID].PhoneNumber, + }, map[string]interface{}{ + "VendorWaybillID": vendorOrderID, + "WaybillVendorID": model.VendorIDMTWM, + }) + } + return retVal, err + }, orderList) + tasksch.HandleTask(updateTask, task, true).Run() + _, err = updateTask.GetResult(0) + } + } + return retVal, err + }, []int{0, 1, 2}) + tasksch.HandleTask(task, nil, true).Run() + return response +} + +func (c *PurchaseHandler) GetWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2 string) (tipFee int64, err error) { + orderInfo, err := getAPI(vendorOrgCode, 0, vendorStoreID).GetDistributeOrderDetail(vendorOrderID, vendorStoreID) + if err == nil { + tipFee = jxutils.StandardPrice2Int(orderInfo.TipAmount) + } + return tipFee, err +} + +func (c *PurchaseHandler) UpdateWaybillTip(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorOrderID, vendorWaybillID, vendorWaybillID2, cityCode string, tipFee int64) (err error) { + if globals.EnableMtwmStoreWrite { + err = getAPI(vendorOrgCode, 0, vendorStoreID).OrderModityTips(vendorOrderID, vendorStoreID, jxutils.IntPrice2Standard(tipFee)) + } + return err +} + +func (c *PurchaseHandler) GetSelfTakeCode(ctx *jxcontext.Context, order *model.GoodsOrder) (selfTakeCode string, err error) { + return selfTakeCode, err +} + +func (c *PurchaseHandler) ConfirmSelfTake(ctx *jxcontext.Context, order *model.GoodsOrder, selfTakeCode string) (err error) { + return err +} + +func (c *PurchaseHandler) ComplaintRider(vendorOrderId string, resonID int, resonContent string) (err error) { + return err +} + +// GetCancelDeliveryReason 转自配送时取消非专送混合送门店取消理由 +func (c *PurchaseHandler) GetCancelDeliveryReason(order *model.GoodsOrder) (string, error) { + reason, err := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").GetCancelDeliveryReason(utils.Str2Int64(order.VendorOrderID), order.VendorStoreID) + if err != nil { + return "", err + } + return reason, nil +} + +// 取消美团外卖理由转使用三方配送 +func (c *PurchaseHandler) CancelLogisticsByWmOrderId(order *model.GoodsOrder, reasonCode, detailContent, appPoiCode, orderId string) error { + return getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), "").CancelLogisticsByWmOrderId(reasonCode, detailContent, appPoiCode, orderId) +} + +// 获取订单配送状态 +func (c *PurchaseHandler) OrderLogisticsStatus(orderId int64) (int64, error) { + return 0, nil +} + +// GetOrderSettleAccounts 获取订单结算信息 +func (c *PurchaseHandler) GetOrderSettleAccounts(order *model.GoodsOrder) (int64, error) { + oderDetail, err := getAPI(order.VendorOrgCode, 0, order.VendorStoreID).OrderGetOrderDetail(utils.Str2Int64(order.VendorOrderID), true) + if err != nil { + return 0, err + } + + if poiReceiveDetailStr := utils.Interface2String(oderDetail["poi_receive_detail"]); poiReceiveDetailStr != "" { + var poiReceiveDetail *mtwmapi.PoiReceiveDetailInfo + utils.UnmarshalUseNumber([]byte(poiReceiveDetailStr), &poiReceiveDetail) + if poiReceiveDetail != nil { + return poiReceiveDetail.WmPoiReceiveCent, nil + } + } + return 0, nil +} diff --git a/business/partner/purchase/tao_vegetable/order_afs.go b/business/partner/purchase/tao_vegetable/order_afs.go new file mode 100644 index 000000000..e7ff38001 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/order_afs.go @@ -0,0 +1,251 @@ +package mtwm + +import ( + "errors" + "fmt" + "git.rosy.net.cn/jx-callback/globals/api" + "net/url" + "strings" + + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" + "git.rosy.net.cn/jx-callback/business/partner" + "git.rosy.net.cn/jx-callback/globals" +) + +var ( + // AfsVendorStatus2StatusMap = map[int]int{ + // mtwmapi.ResTypePending: model.AfsOrderStatusWait4Approve, + // mtwmapi.ResTypeMerchantRefused: model.AfsOrderStatusFailed, + // mtwmapi.ResTypeMerchantAgreed: model.AfsOrderStatusFinished, + // mtwmapi.ResTypeCSRefused: model.AfsOrderStatusFailed, + // mtwmapi.ResTypeCSAgreed: model.AfsOrderStatusFinished, + // mtwmapi.ResTypeTimeoutAutoAgreed: model.AfsOrderStatusFinished, + // mtwmapi.ResTypeAutoAgreed: model.AfsOrderStatusFinished, + // mtwmapi.ResTypeUserCancelApply: model.AfsOrderStatusFailed, + // mtwmapi.ResTypeUserCancelComplain: model.AfsOrderStatusFailed, + // } + AfsVendorStatus2StatusMap = map[string]int{ + mtwmapi.NotifyTypeApply: model.AfsOrderStatusWait4Approve, + mtwmapi.NotifyTypePartyApply: model.AfsOrderStatusWait4Approve, + mtwmapi.NotifyTypeSuccess: model.AfsOrderStatusFinished, + mtwmapi.NotifyTypeReject: model.AfsOrderStatusFailed, + mtwmapi.NotifyTypeCancelRefund: model.AfsOrderStatusFailed, + mtwmapi.NotifyTypeCancelRefundComplaint: model.AfsOrderStatusFailed, + } +) + +func (c *PurchaseHandler) isAfsMsg(msg *mtwmapi.CallbackMsg) bool { + if msg.Cmd == mtwmapi.MsgTypeOrderRefund || msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { + // refundData := msg.Data.(*mtwmapi.CallbackRefundInfo) + orderID := utils.Str2Int64(GetOrderIDFromMsg(msg)) + order, _ := partner.CurOrderManager.LoadOrder(utils.Int64ToStr(orderID), model.VendorIDMTWM) + if order != nil { + //status, err := getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromOrder(order), order.VendorStoreID).OrderViewStatus(orderID) + //if err == nil { + //return utils.Int2Str(status) == mtwmapi.OrderStatusFinished + return true //TODO 有的美团订单售前退款,也当做售后处理试试 + //} + } + } + return false +} + +func (c *PurchaseHandler) OnAfsOrderMsg(msg *mtwmapi.CallbackMsg) (retVal *mtwmapi.CallbackResponse) { + jxutils.CallMsgHandlerAsync(func() { + retVal = c.onAfsOrderMsg(msg) + }, jxutils.ComposeUniversalOrderID(GetOrderIDFromMsg(msg), model.VendorIDMTWM)) + return retVal +} + +// todo 对于退款与部分退款,order.go与这个文件中对于状态的处理不一致 +func (c *PurchaseHandler) onAfsOrderMsg(msg *mtwmapi.CallbackMsg) (retVal *mtwmapi.CallbackResponse) { + var err error + orderStatus := c.callbackAfsMsg2Status(msg) + needCallNew := orderStatus.Status == model.AfsOrderStatusWait4Approve || orderStatus.Status == model.AfsOrderStatusNew + if !needCallNew { + _, err := partner.CurOrderManager.LoadAfsOrder(orderStatus.VendorOrderID, orderStatus.VendorID) + if err != nil { + if dao.IsNoRowsError(err) { + needCallNew = true + } else { + return mtwmapi.Err2CallbackResponse(err, "") + } + } + } + + if needCallNew { + var afsOrder *model.AfsOrder + refundData := msg.Data.(*mtwmapi.CallbackRefundInfo) + if msg.Cmd == mtwmapi.MsgTypeOrderPartialRefund { + afsOrder = &model.AfsOrder{ + VendorID: model.VendorIDMTWM, + AfsOrderID: orderStatus.VendorOrderID, + VendorOrderID: orderStatus.RefVendorOrderID, + VendorStoreID: "", + StoreID: 0, + AfsCreatedAt: utils.Timestamp2Time(refundData.Timestamp), + VendorAppealType: "", + AppealType: model.AfsAppealTypeRefund, + VendorReasonType: "", + ReasonType: model.AfsReasonNotOthers, + ReasonDesc: utils.LimitUTF8StringLen(refundData.Reason, 1024), + ReasonImgList: utils.LimitUTF8StringLen(strings.Join(refundData.PictureList, ","), 1024), + RefundType: model.AfsTypePartRefund, + + VendorOrgCode: msg.AppID, + // FreightUserMoney: afsInfo.OrderFreightMoney, + // AfsFreightMoney: afsInfo.AfsFreight, + // BoxMoney: afsInfo.PackagingMoney, + // TongchengFreightMoney: afsInfo.TongchengFreightMoney, + // SkuBoxMoney: afsInfo.MealBoxMoney, + } + for _, sku := range refundData.FoodList { + orderSku := &model.OrderSkuFinancial{ + // VendorID: model.VendorIDMTWM, + AfsOrderID: afsOrder.AfsOrderID, + // VendorOrderID: afsOrder.VendorOrderID, + // VendorStoreID: afsOrder.VendorStoreID, + // StoreID: afsOrder.StoreID, + IsAfsOrder: 1, + + Count: sku.Count, + // ConfirmTime: afsOrder.AfsCreateAt, + VendorSkuID: sku.SkuID, + SkuID: int(utils.Str2Int64WithDefault(sku.SkuID, 0)), + Name: sku.FoodName, + UserMoney: jxutils.StandardPrice2Int(sku.RefundPrice)*int64(sku.Count) + jxutils.StandardPrice2Int(sku.BoxPrice)*int64(sku.BoxNum), + } + if orderSku.VendorSkuID == "" || orderSku.VendorSkuID == "0" { + orderSku.VendorSkuID = sku.AppFoodCode + } + + afsOrder.SkuUserMoney += orderSku.UserMoney + afsOrder.Skus = append(afsOrder.Skus, orderSku) + } + //afsOrder.PmSubsidyMoney += afsOrder.RefundMoney - afsOrder.SkuUserMoney + } else { + if afsOrder = c.createAfsOrder(msg.FormData); afsOrder != nil { + // if orderFinancial, err2 := partner.CurOrderManager.LoadOrderFinancial(orderStatus.RefVendorOrderID, model.VendorIDMTWM); err2 == nil { + // afsOrder = c.OrderFinancialDetail2Refund(orderFinancial, msg.FormData) + afsOrder.AfsOrderID = orderStatus.VendorOrderID + afsOrder.RefundType = model.AfsTypeFullRefund + afsOrder.AppealType = model.AfsAppealTypeRefund + afsOrder.VendorReasonType = "" + afsOrder.ReasonType = model.AfsReasonNotOthers + afsOrder.ReasonDesc = utils.LimitUTF8StringLen(refundData.Reason, 1024) + afsOrder.ReasonImgList = utils.LimitUTF8StringLen(strings.Join(refundData.PictureList, ","), 1024) + } + } + if afsOrder != nil { + //直接就来一个新的售后单,并且还是售后完成的 + if orderStatus.Status == model.AfsOrderStatusFinished { + afsOrder.AfsFinishedAt = afsOrder.AfsCreatedAt + } + err = partner.CurOrderManager.OnAfsOrderNew(afsOrder, orderStatus) + } + } else { + if err := partner.CurOrderManager.OnAfsOrderStatusChanged(orderStatus); err == nil { + // 订单回调全额退款接口时,将订单状态修改为取消 + refundData := msg.Data.(*mtwmapi.CallbackRefundInfo) + if refundData.NotifyType == "agree" && msg.Cmd == mtwmapi.MsgTypeOrderRefund { + order, _ := partner.CurOrderManager.LoadOrder(orderStatus.RefVendorOrderID, model.VendorIDMTWM) + order.Status = model.OrderStatusCanceled + dao.UpdateEntity(dao.GetDB(), order, "Status") + } + } + } + return mtwmapi.Err2CallbackResponse(err, "") +} + +func (p *PurchaseHandler) createAfsOrder(orderData url.Values) (afsOrder *model.AfsOrder) { + afsOrder, err := partner.CurOrderManager.CreateAfsOrderFromOrder(orderData.Get("order_id"), model.VendorIDMTWM) + if err == nil { + afsOrder.AfsOrderID = orderData.Get("refund_id") + afsOrder.AfsCreatedAt = utils.Timestamp2Time(utils.Str2Int64(orderData.Get("timestamp"))) + if afsOrder.AfsOrderID == "" { + afsOrder.AfsOrderID = afsOrder.VendorOrderID + } + } else { + afsOrder = nil + } + return afsOrder +} + +func (c *PurchaseHandler) callbackAfsMsg2Status(msg *mtwmapi.CallbackMsg) (orderStatus *model.OrderStatus) { + refundData := msg.Data.(*mtwmapi.CallbackRefundInfo) + orderStatus = &model.OrderStatus{ + VendorID: model.VendorIDMTWM, + OrderType: model.OrderTypeAfsOrder, + RefVendorOrderID: utils.Int64ToStr(refundData.OrderID), + RefVendorID: model.VendorIDMTWM, + VendorStatus: fmt.Sprintf("%s:%d", refundData.NotifyType, refundData.ResType), + Status: c.GetAfsStatusFromVendorStatus(refundData.ResType, refundData.NotifyType), + StatusTime: utils.Timestamp2Time(refundData.Timestamp), + Remark: refundData.Reason, + } + if refundData.RefundID > 0 { + orderStatus.VendorOrderID = utils.Int64ToStr(refundData.RefundID) + } else { + orderStatus.VendorOrderID = orderStatus.RefVendorOrderID + } + return orderStatus +} + +func (c *PurchaseHandler) GetAfsStatusFromVendorStatus(resType int, notifyType string) int { + status := AfsVendorStatus2StatusMap[notifyType] + if status == model.AfsOrderStatusWait4Approve && resType != mtwmapi.ResTypePending { + status = model.AfsOrderStatusNew + } + return status +} + +// 审核售后单申请 +func (c *PurchaseHandler) AgreeOrRefuseRefund(ctx *jxcontext.Context, order *model.AfsOrder, approveType int, reason string) (err error) { + if globals.EnableMtwmStoreWrite { + if approveType == partner.AfsApproveTypeRefused { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromAfsOrder(order), order.VendorStoreID).OrderRefundReject(utils.Str2Int64(order.VendorOrderID), reason) + } else if approveType == partner.AfsApproveTypeRefusedToRefundMoney { + return errors.New("此平台暂时不支持") + } else { + err = getAPI(order.VendorOrgCode, jxutils.GetSaleStoreIDFromAfsOrder(order), order.VendorStoreID).OrderRefundAgree(utils.Str2Int64(order.VendorOrderID), reason) + } + } + return err +} + +// 确认收到退货 +func (c *PurchaseHandler) ConfirmReceivedReturnGoods(ctx *jxcontext.Context, order *model.AfsOrder) (err error) { + err = fmt.Errorf("内部错误,美团外卖平台不支持确认收到退货操作") + return err +} + +// 发起全款退款 +func (c *PurchaseHandler) RefundOrder(ctx *jxcontext.Context, order *model.GoodsOrder, reason string) (err error) { + return fmt.Errorf("%s不支持售后全额退款,请让买家发起退款", model.VendorChineseNames[model.VendorIDMTWM]) +} + +// 发起部分退款 +func (c *PurchaseHandler) PartRefundOrder(ctx *jxcontext.Context, order *model.GoodsOrder, refundSkuList []*model.OrderSku, reason string) (err error) { + return c.AdjustOrder(ctx, order, refundSkuList, reason) +} + +func (c *PurchaseHandler) GetOrderAfsInfo(ctx *jxcontext.Context, vendorOrderID, afsOrderID string) (orderAfsInfo *partner.OrderAfsInfo, err error) { + orderAfsInfo = &partner.OrderAfsInfo{} + var afsTotalShopMoney int64 + if list, err := api.MtwmAPI.GetOrderRefundDetail(utils.Str2Int64(vendorOrderID), 0); err == nil { + for _, v := range list { + if v.RefundPartialEstimateCharge.SettleAmount != "" { + afsTotalShopMoney += jxutils.StandardPrice2Int(utils.Str2Float64(v.RefundPartialEstimateCharge.SettleAmount)) + } + } + } + if order, err := partner.CurOrderManager.LoadOrder(vendorOrderID, model.VendorIDMTWM); err == nil { + orderAfsInfo.AfsTotalShopMoney = order.TotalShopMoney + afsTotalShopMoney + } + return orderAfsInfo, err +} diff --git a/business/partner/purchase/tao_vegetable/order_comment.go b/business/partner/purchase/tao_vegetable/order_comment.go new file mode 100644 index 000000000..d585b479a --- /dev/null +++ b/business/partner/purchase/tao_vegetable/order_comment.go @@ -0,0 +1,112 @@ +package mtwm + +import ( + "strings" + "time" + + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + + "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + "git.rosy.net.cn/jx-callback/business/partner" + + "git.rosy.net.cn/baseapi/utils" + "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/globals" +) + +const ( + RefreshCommentTime = 7 * 24 * time.Hour // 此值必须大于24小时 + RefreshCommentTimeInterval = 60 * time.Minute + BAD_COMMENTS_MAX_MODIFY_TIME = 24 * 6 // 小时 +) + +func (c *PurchaseHandler) StartRefreshComment() { + utils.AfterFuncWithRecover(5*time.Second, func() { + c.refreshCommentOnce() + }) +} + +func (c *PurchaseHandler) refreshCommentOnce() { + c.RefreshComment(time.Now().Add(-RefreshCommentTime), time.Now()) + utils.AfterFuncWithRecover(RefreshCommentTimeInterval, func() { + c.refreshCommentOnce() + }) +} + +func formalizeTagList(mtwmTagList string) (outTagList string) { + if mtwmTagList != "" { + outTagList = string(utils.Format4Output(strings.Split(mtwmTagList, ","), true)) + } + return outTagList +} + +func (c *PurchaseHandler) RefreshComment(fromTime, toTime time.Time) (err error) { + //storeMapList, err2 := dao.GetStoresMapList(dao.GetDB(), []int{model.VendorIDMTWM}, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "") + //if err = err2; err != nil { + // return err + //} + endDateStr := time.Now().Add(-24 * time.Hour).Format("20060102") + startDateStr := time.Now().Add(-RefreshCommentTime).Format("20060102") + storeIDs, _ := dao.GetOrderStoreIDs(dao.GetDB(), fromTime, toTime, model.VendorIDMTWM) + task := tasksch.NewParallelTask("mtwm RefreshComment", nil, jxcontext.AdminCtx, + func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { + storeID := batchItemList[0].(int) + storeDetail, _ := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDMTWM, "") + commentList, err2 := getAPI(storeDetail.VendorOrgCode, storeID, storeDetail.VendorStoreID).CommentQuery(storeDetail.VendorStoreID, startDateStr, endDateStr, 0, 0, mtwmapi.CommentReplyStatusNotReplied) + var orderCommentList []*model.OrderComment + if err = err2; err != nil { + return nil, err + } + for _, mtwmComment := range commentList { + createdTime, err := utils.TryStr2Time(mtwmComment.CommentTime) + if err == nil { + orderComment := &model.OrderComment{ + VendorOrderID: utils.Int64ToStr(mtwmComment.CommentID), // 美团评价不能得到订单号,以评价ID代替 + VendorID: model.VendorIDMTWM, + UserCommentID: utils.Int64ToStr(mtwmComment.CommentID), + VendorStoreID: storeDetail.VendorStoreID, + TagList: formalizeTagList(mtwmComment.CommentLables), + Score: int8(mtwmComment.FoodCommentScore), + ModifyDuration: BAD_COMMENTS_MAX_MODIFY_TIME, + OriginalMsg: string(utils.MustMarshal(mtwmComment)), + IsReplied: int8(mtwmComment.ReplyStatus), + StoreID: storeDetail.ID, + } + if orderComment.IsReplied == 0 { + orderComment.Content = mtwmComment.CommentContent + orderComment.CommentCreatedAt = createdTime + } else { + orderComment.Content = mtwmComment.AddComment + if updatedTime, err := utils.TryStr2Time(mtwmComment.CommentTime); err == nil { + orderComment.CommentCreatedAt = updatedTime + } + } + orderCommentList = append(orderCommentList, orderComment) + } + } + return orderCommentList, nil + }, storeIDs) + task.Run() + resultList, err2 := task.GetResult(0) + if err = err2; err != nil { + return err + } + var orderCommentList []*model.OrderComment + for _, result := range resultList { + orderComment := result.(*model.OrderComment) + orderCommentList = append(orderCommentList, orderComment) + } + if len(orderCommentList) > 0 { + err = partner.CurOrderManager.OnOrderComments(orderCommentList) + } + return err +} + +func (c *PurchaseHandler) ReplyOrderComment(ctx *jxcontext.Context, vendorOrgCode string, orderComment *model.OrderComment, replyComment string) (err error) { + if globals.EnableMtwmStoreWrite { + err = getAPI(vendorOrgCode, orderComment.StoreID, orderComment.VendorStoreID).CommentAddReply(orderComment.VendorStoreID, utils.Str2Int64(orderComment.UserCommentID), replyComment) + } + return err +} diff --git a/business/partner/purchase/tao_vegetable/order_test.go b/business/partner/purchase/tao_vegetable/order_test.go new file mode 100644 index 000000000..54585dc91 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/order_test.go @@ -0,0 +1,35 @@ +package mtwm + +import ( + "testing" + "time" + + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + + "git.rosy.net.cn/baseapi/utils" + _ "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" +) + +func TestGetOrder(t *testing.T) { + // result, err := CurPurchaseHandler.GetOrder("", "33437032333978492") + // if err != nil { + // t.Fatal(err) + // } + //t.Log(utils.Format4Output(result, false)) +} + +func TestGetOrderStatus(t *testing.T) { + result, err := CurPurchaseHandler.GetOrderStatus("", "71884881906304496") + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(result, false)) +} + +func TestListOrders(t *testing.T) { + result, err := CurPurchaseHandler.ListOrders(jxcontext.AdminCtx, "5873", nil, time.Now(), "14038247") + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(result, false)) +} diff --git a/business/partner/purchase/tao_vegetable/store.go b/business/partner/purchase/tao_vegetable/store.go new file mode 100644 index 000000000..37c813909 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/store.go @@ -0,0 +1,485 @@ +package mtwm + +import ( + "encoding/json" + "errors" + "fmt" + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/baseapi/utils/errlist" + "git.rosy.net.cn/jx-callback/business/jxcallback/scheduler" + "git.rosy.net.cn/jx-callback/business/jxstore/event" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/model/dao" + "git.rosy.net.cn/jx-callback/business/partner" + "git.rosy.net.cn/jx-callback/globals" + "git.rosy.net.cn/jx-callback/globals/api" + "math" + "regexp" + "strings" +) + +var ( + opTimeErrReg = regexp.MustCompile(`当前配送营业时间为:([\d:~,]*)`) + storeVendorOrgCodeMap = map[string]map[string]string{ + "589": map[string]string{ + "firstTag": mtwmapi.MtwmC4Tag, //经营品类 + "settlementID": "7030017", //结算ID + "poiCert": "1,2,5", //资质列表 + }, + "5873": map[string]string{ + "firstTag": mtwmapi.MtwmSCTag, + "settlementID": "", + "poiCert": "1,2,5,6", + }, + "4123": map[string]string{ + "firstTag": mtwmapi.MtwmSGTag, + "settlementID": "6572945", + "poiCert": "1,5", + }, + } + poiCertMap = map[string]string{ + "1": "门脸图", + "2": "环境图", + "5": "营业执照", + "6": "食品经营许可证", + } +) + +type tEbaiStoreInfo struct { + model.Store + VendorStoreID string `orm:"column(vendor_store_id)"` + RealLastOperator string + EbaiStoreStatus int + SyncStatus int + + ProvinceID int `orm:"column(province_id)"` + CityID int `orm:"column(city_id)"` + DistrictID int `orm:"column(district_id)"` +} + +func (p *PurchaseHandler) ReadStore(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID, vendorStoreName string) (retVal *dao.StoreDetail, err error) { + result, err := getAPIWithoutToken(vendorOrgCode).PoiGet(vendorStoreID) + if err == nil { + retVal = &dao.StoreDetail{ + Store: model.Store{ + Address: result.Address, + Tel1: result.Phone, + }, + } + retVal.OriginalName = result.Name + _, retVal.Name = jxutils.SplitStoreName(retVal.OriginalName, partner.StoreNameSeparator, globals.StoreNameMtwm) + + retVal.SetOpTime(openTimeMtwm2JX(result.ShippingTime)) + retVal.Status = bizStatusMtwm2JX(result.OpenLevel, result.IsOnline) + + tel2 := result.StandbyTel + if tel2 != "" && tel2 != retVal.Tel1 { + retVal.Tel2 = tel2 + } + + retVal.Lng = int(result.Longitude) + retVal.Lat = int(result.Latitude) + + lng := jxutils.IntCoordinate2Standard(retVal.Lng) + lat := jxutils.IntCoordinate2Standard(retVal.Lat) + db := dao.GetDB() + retVal.DistrictCode = api.AutonaviAPI.GetCoordinateDistrictCode(lng, lat) + city, err := dao.GetPlaceByCode(db, result.CityID) + retVal.CityName = city.Name + retVal.CityCode = result.CityID + + poiCode := result.AppPoiCode + retVal.VendorStoreID = vendorStoreID + retVal.ID = int(utils.Str2Int64WithDefault(poiCode, 0)) + retVal.DeliveryRangeType = model.DeliveryRangeTypePolygon + var deliveryRangeInfo []map[string]interface{} + deliveryRangeInfo, err = getAPIWithoutToken(vendorOrgCode).ShippingFetch(poiCode) + if err != nil { + deliveryRangeInfo, err = getAPIWithoutToken(vendorOrgCode).ShippingList(poiCode) + } + if err == nil { + if len(deliveryRangeInfo) > 0 { + retVal.DeliveryRange = rangeMtwm2JX(deliveryRangeInfo[0]["area"].(string)) + logisticsCode := utils.Interface2String(deliveryRangeInfo[0]["logistics_code"]) + if logisticsCode == "" || logisticsCode == mtwmapi.PeiSongTypeSelf { + retVal.DeliveryType = scheduler.StoreDeliveryTypeByStore + } else { + retVal.DeliveryType = scheduler.StoreDeliveryTypeByPlatform + } + } + } + return retVal, nil + } + return nil, err +} +func (p *PurchaseHandler) CreateStore(db *dao.DaoDB, storeID int, userName string) (err error) { + return p.UpdateStore(db, storeID, userName) +} + +func (p *PurchaseHandler) CreateStore2(db *dao.DaoDB, storeID int, userName string, params map[string]interface{}, storeDetail *dao.StoreDetail) (vendorStoreID string, err error) { + vendorOrgCode := params["vendorOrgCode"].(string) + if vendorOrgCode == "" { + return "", fmt.Errorf("平台账号必传!") + } + cityName := storeDetail.CityName + if strings.Contains(cityName, "市") { + cityName = strings.Replace(cityName, "市", "", strings.LastIndex(cityName, "市")) + } + shippingTime := "" + if storeDetail.OpenTime1 != 0 && storeDetail.CloseTime1 != 0 { + shippingTime += jxutils.JxOperationTime2StrTime(storeDetail.OpenTime1) + shippingTime += "-" + shippingTime += jxutils.JxOperationTime2StrTime(storeDetail.CloseTime1) + if storeDetail.OpenTime2 != 0 && storeDetail.CloseTime2 != 0 { + shippingTime += "," + shippingTime += jxutils.JxOperationTime2StrTime(storeDetail.OpenTime2) + shippingTime += "-" + shippingTime += jxutils.JxOperationTime2StrTime(storeDetail.CloseTime2) + } + } + vendorInfoMap := storeVendorOrgCodeMap[vendorOrgCode] + poiSettleSaveParam := &mtwmapi.PoiSettleSaveParam{ + Type: 1, //创建 + ApplyInfos: []*mtwmapi.ApplyInfo{ + &mtwmapi.ApplyInfo{ + AppPoiCode: utils.Int2Str(storeDetail.ID), + SettlementID: utils.Str2Int(vendorInfoMap["settlementID"]), //结算ID,暂时还没得 + MultiPoiBasicInfo: &mtwmapi.MultiPoiBasicInfo{ + Name: params["vendorStoreName"].(string), + City: cityName, + Address: storeDetail.Address, + Longitude: utils.Float64ToStr(jxutils.IntCoordinate2Standard(storeDetail.Lng)), + Latitude: utils.Float64ToStr(jxutils.IntCoordinate2Standard(storeDetail.Lat)), + FirstTag: vendorInfoMap["firstTag"], + CallCenter: storeDetail.Tel1, + ContactPhone: storeDetail.Tel1, + ContactName: storeDetail.IDName, + EcommerceAccountPhone: "18048531223", //石总的手机 + ShippingTime: shippingTime, + }, + MultiPoiShippingInfo: &mtwmapi.MultiPoiShippingInfo{ + ShippingType: 5, //1:商家自配 5:美团专送,101:美团快送 + //美团专送不需要输下面这俩 + // MinPrice: params["minPrice"].(float64), + // ShippingFee: params["shippingFee"].(float64), + }, + //资质 + }, + }, + } + switchCertType := func(certType string) (licensePic, licenseSocialCreditCode, licenseNumber, licenseLegalPerson, licenseAddress, licenseValidStartDate, licenseValidity string, isLongTime int) { + switch certType { + case "1": + licensePic = storeDetail.StoreFrontPic + case "2": + licensePic = storeDetail.StoreInPic + case "5": + licensePic = storeDetail.Licence + licenseSocialCreditCode = storeDetail.LicenceCode + licenseNumber = storeDetail.LicenceCode + licenseLegalPerson = storeDetail.LicenceOwnerName + licenseAddress = storeDetail.LicenceAddress + licenseValidStartDate = storeDetail.LicenceValid + if storeDetail.LicenceExpire == "" { + isLongTime = 1 + } else { + licenseValidity = storeDetail.LicenceExpire + } + case "6": + licensePic = storeDetail.Licence2Image + licenseSocialCreditCode = storeDetail.Licence2Code + licenseNumber = storeDetail.Licence2Code + licenseLegalPerson = storeDetail.LicenceOwnerName + licenseAddress = storeDetail.LicenceAddress + licenseValidStartDate = storeDetail.Licence2Valid + if storeDetail.Licence2Expire == "" { + isLongTime = 1 + } else { + licenseValidity = storeDetail.Licence2Expire + } + } + return licensePic, licenseSocialCreditCode, licenseNumber, licenseLegalPerson, licenseAddress, licenseValidStartDate, licenseValidity, isLongTime + } + var certs []*mtwmapi.MultiPoiCertInfo + for _, v := range strings.Split(vendorInfoMap["poiCert"], ",") { + cert := &mtwmapi.MultiPoiCertInfo{ + Type: utils.Str2Int(v), + LicenseName: poiCertMap[v], + } + cert.LicensePic, cert.LicenseSocialCreditCode, cert.LicenseNumber, cert.LicenseLegalPerson, cert.LicenseAddress, cert.LicenseValidStartDate, cert.LicenseValidity, cert.IsLongTime = switchCertType(v) + certs = append(certs, cert) + } + poiSettleSaveParam.ApplyInfos[0].MultiPoiCertInfos = certs + mtapi := getAPIWithoutToken(vendorOrgCode) + if vendorStoreID, err = mtapi.PoiSettleSave(poiSettleSaveParam); err == nil { + err = mtapi.PoiSettleAuditSubmit([]string{vendorStoreID}) + } + return vendorStoreID, err +} + +func (p *PurchaseHandler) DeleteStore(db *dao.DaoDB, storeID int, userName string) (err error) { + return err +} + +func (p *PurchaseHandler) UpdateStore(db *dao.DaoDB, storeID int, userName string) (err error) { + var name string + if db == nil { + db = dao.GetDB() + } + mtapi := getAPI(getStoreVendorOrgCode(storeID), storeID, "") + //获取本地store信息 + storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDMTWM, "") + if err != nil { + return err + } + errList := errlist.New() + //获取平台store信息 + remoteStoreInfo, err := mtapi.PoiGet(storeDetail.VendorStoreID) + if err != nil { + return err + } + mergedStoreStatus := jxutils.MergeStoreStatus(storeDetail.Status, storeDetail.VendorStatus) + name = remoteStoreInfo.Name + if storeDetail.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreName) != 0 { + if storeDetail.VendorStoreName != "" { + name = storeDetail.VendorStoreName + } + // else { + // name = jxutils.ComposeStoreName(storeDetail.Store.Name, model.VendorIDMTWM) + // } + } + store := fmt.Sprintf("门店id:%d,门店名称:%s,第三方门店状态:%d,本地修改前门店状态%d,本地门店修改后状态:%d,第三方平台Id(美团):%s", storeID, remoteStoreInfo.Name, remoteStoreInfo.IsOnline, storeDetail.Status, mergedStoreStatus, storeDetail.VendorOrgCode) + event.AddOperateEvent(jxcontext.AdminCtx, jxcontext.AdminCtx.GetTrackInfo(), store, "", "", 10, "UpdateStore") + // openLevel, isOnline := bizStatusJX2Mtwm(mergedStoreStatus) + //TODO 美团暂时不用那个电话 + phone := storeDetail.Tel1 + // if storeDetail.MarketManPhone != "" { + // phone = storeDetail.MarketManPhone + // } else { + // phone = model.VendorStoreTel + // } + params := map[string]interface{}{ + "name": name, //jxutils.ComposeStoreName(storeDetail.Store.Name, model.VendorIDMTWM), + "address": storeDetail.Address, // 美团好像地址也不能改的? + "longitude": jxutils.IntCoordinate2Standard(int(remoteStoreInfo.Longitude)), + "latitude": jxutils.IntCoordinate2Standard(int(remoteStoreInfo.Latitude)), + "phone": phone, + "shipping_fee": remoteStoreInfo.ShippingFee, + "shipping_time": remoteStoreInfo.ShippingTime, + "open_level": remoteStoreInfo.OpenLevel, + "is_online": remoteStoreInfo.IsOnline, + "third_tag_name": remoteStoreInfo.ThirdTagName, + "promotion_info": storeDetail.PromoteInfo, + } + if globals.EnableMtwmStoreWrite { + errList.AddErr(mtapi.PoiSave(storeDetail.VendorStoreID, params)) + } + // PoiSave有时会报错:商家已接入美团配送,不可修改门店配送相关信息,这里放弃信息修改 + // if err != nil { + // if utils.IsErrMatch(err, utils.Int2Str(mtwmapi.ErrCodeCanNotModifyStoreDeliveryInfo), nil) { + // if storeDetail.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreStatus) != 0 { + // err = p.UpdateStoreStatus(jxcontext.AdminCtx, storeDetail.VendorOrgCode, storeID, storeDetail.VendorStoreID, mergedStoreStatus) + // } else { + // err = nil + // } + // } + // errList.AddErr(err) + // } + if storeDetail.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreStatus) != 0 { + errList.AddErr(p.UpdateStoreStatus(jxcontext.AdminCtx, storeDetail.VendorOrgCode, storeID, storeDetail.VendorStoreID, mergedStoreStatus)) + } + errList.AddErr(p.UpdateStoreOpTime(jxcontext.AdminCtx, storeDetail.VendorOrgCode, storeID, storeDetail.VendorStoreID, storeDetail.GetOpTimeList())) + // errList.AddErr(p.UpdateStoreBoxFee(jxcontext.AdminCtx, storeDetail.VendorOrgCode, storeID, storeDetail.VendorStoreID)) + return errList.GetErrListAsOne() +} + +func (p *PurchaseHandler) RefreshAllStoresID(ctx *jxcontext.Context, parentTask tasksch.ITask, isAsync bool) (hint string, err error) { + return "", errors.New("美团外卖不支持此操作") +} + +func (p *PurchaseHandler) onStoreStatusChanged(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackResponse) { + var err error + vendorStoreID := msg.FormData.Get("app_poi_code") + storeStatus := 0 + if msg.Cmd == mtwmapi.MsgTypeStoreStatusChanged { + poiStatus := int(utils.Str2Int64(msg.FormData.Get("poi_status"))) + if poiStatus == mtwmapi.MsgPoiStatusOpened { + storeStatus = model.StoreStatusOpened + } else if poiStatus == mtwmapi.MsgPoiStatusClosed { + storeStatus = model.StoreStatusClosed + } else if poiStatus == mtwmapi.MsgPoiStatusOffline { + storeStatus = model.StoreStatusDisabled + } else { + storeStatus, err = p.GetStoreStatus(jxcontext.AdminCtx, "", 0, vendorStoreID) + } + } else if msg.Cmd == mtwmapi.MsgTypeStoreAuditStatusChanged { + auditDetails := []map[string]interface{}{} + auditDetail := msg.FormData.Get("audit_detail") + openFlag := false + openCount := 0 + closeFlag := false + if err = json.Unmarshal([]byte(auditDetail), &auditDetails); err == nil { + for _, v := range auditDetails { + if v["module_status"].(string) == "3" || v["module_status"].(string) == "5" || v["module_status"].(string) == "7" { + closeFlag = true + break + } + if v["module_status"].(string) == "6" { + openCount++ + } + } + if openCount == len(auditDetails) { + openFlag = true + } + if closeFlag { + storeStatus = model.StoreStatusDisabled + } else if openFlag { + storeStatus = model.StoreStatusOpened + } else { + storeStatus = model.StoreStatusClosed + } + } + } + + if err == nil { + err = partner.CurStoreManager.OnStoreStatusChanged(vendorStoreID, model.VendorIDMTWM, storeStatus) + } + response = mtwmapi.Err2CallbackResponse(err, "") + // 操作日志(美团外卖) + ctx := jxcontext.AdminCtx + store := fmt.Sprintf("美团外卖回调门店改变回调(营业状态/审核状态):门店id:%s,美团门店状态:%d.[121营业,120休息,18上线,19下线],本地修改后状态[%d]", vendorStoreID, int(utils.Str2Int64(msg.FormData.Get("poi_status"))), storeStatus) + event.AddOperateEvent(ctx, ctx.GetTrackInfo(), store, "", "", 10, "UpdateStore") + return response +} + +func (p *PurchaseHandler) GetStoreStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string) (storeStatus int, err error) { + result, err := getAPI(vendorOrgCode, storeID, "").PoiGet(vendorStoreID) + if err == nil { + return bizStatusMtwm2JX(result.OpenLevel, result.IsOnline), nil + } + return 0, err +} + +func (p *PurchaseHandler) EnableAutoAcceptOrder(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, isSetEnable bool) (err error) { + return err +} + +func (c *PurchaseHandler) UpdateStoreStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, status int) (err error) { + openLevel, isOnline := bizStatusJX2Mtwm(status) + if globals.EnableMtwmStoreWrite { + if isOnline != mtwmapi.PoiStatusOnline { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).PoiOffline(vendorStoreID) + } else { + if err = getAPI(vendorOrgCode, storeID, vendorStoreID).PoiOnline(vendorStoreID); err == nil { // 这个函数成功返回也并不表示上线成功。。。 + remoteStoreInfo, err2 := getAPI(vendorOrgCode, storeID, vendorStoreID).PoiGet(vendorStoreID) + if err = err2; err != nil { + return err + } + if remoteStoreInfo.IsOnline == mtwmapi.PoiStatusOnline { + if openLevel == mtwmapi.PoiOpenLevelHaveRest { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).PoiClose(vendorStoreID) + } else { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).PoiOpen(vendorStoreID) + } + } else { + err = errors.New("门店还未上线,不能修改营业状态") + } + store := fmt.Sprintf("美团外卖回调门店改变回调(营业状态/审核状态):门店id:%s,美团门店状态:%d.[121营业,120休息,18上线,19下线],本地修改后状态[%d]", vendorStoreID, remoteStoreInfo.OpenLevel, openLevel) + event.AddOperateEvent(ctx, ctx.GetTrackInfo(), store, "", "", 10, "UpdateStore") + } + } + } + return err +} + +func errOpStr2Int16(str string) []int16 { + list := strings.Split(str, "~") + if len(list) >= 2 { + return []int16{jxutils.StrTime2JxOperationTime(list[0]+":00", 0), jxutils.StrTime2JxOperationTime(list[1]+":00", 2359)} + } + return nil +} + +func getOpTimeListFromErr(err error) (opTimeList []int16) { + if errExt, ok := err.(*utils.ErrorWithCode); ok && errExt.IntCode() == mtwmapi.ErrCodeOpFailed { + if result := opTimeErrReg.FindStringSubmatch(errExt.ErrMsg()); len(result) >= 2 { + timeStrList := strings.Split(result[1], ",") + for _, v := range timeStrList { + v = utils.TrimBlankChar(v) + if len(v) == len("00:00~02:00") { + opTimeList = append(opTimeList, errOpStr2Int16(v)...) + } + } + } + } + return opTimeList +} + +// 此函数只是简单实现,不支持区间切分,只做单一区间限制 +func constrainOpTimeList(opTimeList, validOpTimeList []int16) (newOpTimeList []int16) { + for k := 0; k < len(opTimeList); k += 2 { + beginTime := opTimeList[k] + endTime := opTimeList[k+1] + for k2 := 0; k2 < len(validOpTimeList); k2 += 2 { + beginTime2 := validOpTimeList[k2] + endTime2 := validOpTimeList[k2+1] + if beginTime >= beginTime2 && beginTime <= endTime2 { + newOpTimeList = append(newOpTimeList, beginTime) + newOpTimeList = append(newOpTimeList, int16(math.Min(float64(endTime), float64(endTime2)))) + } else if beginTime2 >= beginTime && beginTime2 <= endTime { + newOpTimeList = append(newOpTimeList, beginTime2) + newOpTimeList = append(newOpTimeList, int16(math.Min(float64(endTime), float64(endTime2)))) + } + } + } + return newOpTimeList +} + +func (c *PurchaseHandler) UpdateStoreOpTime(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, opTimeList []int16) (err error) { + shippingTime := openTimeJX2Mtwm(opTimeList) + if globals.EnableMtwmStoreWrite { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).PoiShipTimeUpdate(vendorStoreID, shippingTime) + if err != nil { + shippingTime = "" + if validOpTimeList := getOpTimeListFromErr(err); len(validOpTimeList) > 0 { + shippingTime = openTimeJX2Mtwm(constrainOpTimeList(opTimeList, validOpTimeList)) + } + if shippingTime != "" { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).PoiShipTimeUpdate(vendorStoreID, shippingTime) + } + } + } + return err +} + +func (c *PurchaseHandler) GetAllStoresVendorID(ctx *jxcontext.Context, vendorOrgCode string) (vendorStoreIDs []string, err error) { + vendorStoreIDs, err = getAPIWithoutToken(vendorOrgCode).PoiGetIDs() + return vendorStoreIDs, err +} + +func (c *PurchaseHandler) UpdateStoreCustomID(ctx *jxcontext.Context, vendorOrgCode, vendorStoreID string, storeID int64) (err error) { + return err +} + +func (c *PurchaseHandler) UpdateStoreBoxFee(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string) (err error) { + boxFee, err := dao.GetSysConfigAsInt64(dao.GetDB(), model.ConfigSysMtwmBoxFee) + if err == nil { + if globals.EnableMtwmStoreWrite && globals.IsProductEnv() { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).PackagePriceUpdate(vendorStoreID, 1, int(boxFee)) + } + } + return err +} + +func (c *PurchaseHandler) UpdateStoreLineStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, lineStatus int) (err error) { + if lineStatus == model.StoreStatusOpened { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).PoiOnline(vendorStoreID) + } else { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).PoiOffline(vendorStoreID) + } + return err +} diff --git a/business/partner/purchase/tao_vegetable/store_sku2.go b/business/partner/purchase/tao_vegetable/store_sku2.go new file mode 100644 index 000000000..307e82c58 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/store_sku2.go @@ -0,0 +1,763 @@ +package mtwm + +import ( + "encoding/json" + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxutils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + "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/putils" + "git.rosy.net.cn/jx-callback/globals" + "regexp" + "strings" +) + +const ( + updateTypeStock = iota + updateTypeStatus + updateTypePrice +) + +var ( + sensitiveWordRegexp = regexp.MustCompile(`包含敏感词:(\[.*\])`) +) + +func (p *PurchaseHandler) GetStoreSkusBatchSize(funcID int) (batchSize int) { + switch funcID { + case partner.FuncUpdateStoreSkusStock, partner.FuncUpdateStoreSkusStatus, partner.FuncUpdateStoreSkusPrice: + batchSize = mtwmapi.MaxStoreSkuBatchSize + case partner.FuncDeleteStoreSkus: + batchSize = mtwmapi.MaxBatchDeleteSize + case partner.FuncCreateStoreSkus: + batchSize = 1 // 可考虑用批量操作 + case partner.FuncUpdateStoreSkus: + batchSize = 1 // mtwmapi.MaxStoreSkuBatchSize + case partner.FuncGetStoreSkusFullInfo: + batchSize = 1 + case partner.FuncCreateActs: + batchSize = mtwmapi.MaxRetailDiscountCreateBatchSize + case partner.FuncCancelActs: + batchSize = mtwmapi.MaxRetailDiscountDeleteBatchSize + } + return batchSize +} + +func getStoreVendorOrgCode(storeID int) (vendorOrgCode string) { + if storeMap, _ := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDTaoVegetable, ""); storeMap != nil { + return storeMap.VendorOrgCode + } + return vendorOrgCode +} + +// 门店分类 +func (p *PurchaseHandler) GetStoreAllCategories(ctx *jxcontext.Context, storeID int, vendorStoreID string) (cats []*partner.BareCategoryInfo, err error) { + remoteCats, err := getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailCatList(vendorStoreID) + if err == nil { + cats = convertVendorCatList(remoteCats) + } + return cats, err +} + +func convertVendorCatList(remoteCats []*mtwmapi.RetailCategoryInfo) (cats []*partner.BareCategoryInfo) { + for _, rCat := range remoteCats { + cat := &partner.BareCategoryInfo{ + VendorCatID: rCat.Code, + Name: rCat.Name, + Level: rCat.Level, + Seq: rCat.Sequence, + Children: convertVendorCatList(rCat.Children), + } + if cat.VendorCatID == "" { + cat.VendorCatID = rCat.Name + } + cats = append(cats, cat) + } + return cats +} + +func (p *PurchaseHandler) IsErrCategoryExist(err error) (isExist bool) { + return mtwmapi.IsErrCategoryExist(err) +} + +func (p *PurchaseHandler) IsErrCategoryNotExist(err error) (isNotExist bool) { + return mtwmapi.IsErrCategoryNotExist(err) +} + +func catCode2Str(catCode int) (catCodeStr string) { + if catCode > 0 { + catCodeStr = utils.Int2Str(catCode) + } + return catCodeStr +} + +func tryCatName2Code(originName string) (catCodeStr string) { + if intValue := utils.Str2Int64WithDefault(originName, 0); intValue > 0 { + catCodeStr = utils.Int64ToStr(intValue) + if catCodeStr != originName { + catCodeStr = "" + } + } + return catCodeStr +} + +func (p *PurchaseHandler) CreateStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error) { + level := 1 + if storeCat.ParentCatName != "" { + level = 2 + } + originName := "" + catName := storeCat.Name + catCode := storeCat.ID + + subCatName := "" + subCatCode := 0 + if storeCat.CatSyncStatus&model.SyncFlagNewMask == 0 { + // 修改一级分类 + originName = storeCat.VendorCatID + } + if level == 2 { // 二级分类 + // 创建二级分类 + originName = storeCat.ParentVendorCatID + catName = storeCat.ParentCatName + catCode = storeCat.ParentID + subCatName = storeCat.Name + subCatCode = storeCat.ID + if storeCat.CatSyncStatus&model.SyncFlagNewMask == 0 { + // 修改二级分类 + originName = storeCat.VendorCatID + catName = storeCat.Name + catCode = storeCat.ID + subCatName = "" + subCatCode = 0 + } + } + if catName == "" { + panic("catName is empty") + } + catName = utils.FilterEmoji(catName) + subCatName = utils.FilterEmoji(subCatName) + if globals.EnableMtwmStoreWrite { + param4Update := &mtwmapi.Param4UpdateCat{ + CategoryCodeOrigin: tryCatName2Code(originName), + CategoryNameOrigin: originName, + CategoryCode: catCode2Str(catCode), + SecondaryCategoryCode: catCode2Str(subCatCode), + SecondaryCategoryName: subCatName, + Sequence: storeCat.Seq, + } + api := getAPI(storeCat.VendorOrgCode, storeID, vendorStoreID) + err = api.RetailCatUpdate(vendorStoreID, catName, param4Update) + if storeCat.CatSyncStatus&model.SyncFlagNewMask == 0 && p.IsErrCategoryNotExist(err) && originName != "" { // 修改分类名,但分类不存在 + storeCat.CatSyncStatus |= model.SyncFlagNewMask + err = p.CreateStoreCategory(ctx, storeID, vendorStoreID, storeCat) + } + + // 门店内存在重复的分类:【柑桔柚类】 【底料】,请先删除重复分类后再操作。 + if err != nil && strings.Contains(err.Error(), "门店内存在重复的分类:") { + for _, v := range deleteRepeatCat(err.Error()) { + if len(v) > 0 { + if err2 := api.RetailCatDelete(vendorStoreID, "", v, model.YES); err != nil { + globals.SugarLogger.Errorf("RetailCatDelete delete err : [%v]", err2) + } + } + } + } + } + + if err == nil { + // storeCat.VendorCatID = utils.FilterEmoji(storeCat.Name) + storeCat.VendorCatID = utils.Int2Str(storeCat.ID) + } + return err +} + +// deleteRepeatCat 门店内存在重复的分类:【柑桔柚类】 【底料】 【火锅】,请先删除重复分类后再操作。 +func deleteRepeatCat(param string) []string { + firstIndex := strings.Index(param, "【") + lastIndex := strings.LastIndex(param, "】") + newParam := param[firstIndex:lastIndex] + deleteCat := make([]string, 0, 0) + for _, v := range strings.Split(newParam, "【") { + if strings.TrimSpace(v) == "" { + continue + } else if strings.Contains(v, "【") { + for _, v2 := range strings.Split(v, "【") { + if strings.TrimSpace(v) == "" { + continue + } + if strings.TrimSpace(v2) != "" { + deleteCat = append(deleteCat, v2) + } + } + } else if strings.Contains(v, "】") { + for _, v3 := range strings.Split(v, "】") { + if strings.TrimSpace(v3) == "" { + continue + } + if strings.TrimSpace(v3) != "" { + deleteCat = append(deleteCat, v3) + } + } + } else { + deleteCat = append(deleteCat, v) + } + } + return deleteCat +} + +func (p *PurchaseHandler) UpdateStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeCat *dao.SkuStoreCatInfo) (err error) { + return p.CreateStoreCategory(ctx, storeID, vendorStoreID, storeCat) +} + +func (p *PurchaseHandler) DeleteStoreCategory(ctx *jxcontext.Context, storeID int, vendorStoreID, vendorCatID string, level int) (err error) { + if false { + if globals.EnableMtwmStoreWrite { + err = getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailCatDelete(vendorStoreID, tryCatName2Code(vendorCatID), vendorCatID, model.NO) + } + } else { + var catCodes []string + if catCode := tryCatName2Code(vendorCatID); catCode != "" { + catCodes = []string{catCode} + } + if globals.EnableMtwmStoreWrite { + if level == 1 { + err = getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailCatSkuBatchDelete2(ctx.GetTrackInfo(), vendorStoreID, catCodes, []string{vendorCatID}, nil, nil, nil) + } else { + err = getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailCatSkuBatchDelete2(ctx.GetTrackInfo(), vendorStoreID, nil, nil, catCodes, []string{vendorCatID}, nil) + } + } + } + return err +} + +//批量更新商品进货价 +func BatchSetRestockingPrice(ctx *jxcontext.Context, storeID int, vendorStoreID string, param []*mtwmapi.SpuData) error { + if err := getAPI(getStoreVendorOrgCode(storeID), storeID, "").BatchSetRestockingPrice(ctx.GetTrackInfo(), vendorStoreID, param); err != nil { + return err + } + return nil +} + +// 门店商品 + +// 多门店平台不需要实现这个接口 + +func (p *PurchaseHandler) IsErrSkuExist(err error) (isExist bool) { + return false +} + +func (p *PurchaseHandler) IsErrSkuNotExist(err error) (isNotExist bool) { + return mtwmapi.IsErrSkuNotExist(err) +} + +// func duplicateStoreSkuList(storeSkuList []*dao.StoreSkuSyncInfo, index int) (newStoreSkuList []*dao.StoreSkuSyncInfo) { +// newStoreSkuList = make([]*dao.StoreSkuSyncInfo, len(storeSkuList)) +// for k, v := range storeSkuList { +// tmp := *v +// tmp.SkuName = fmt.Sprintf("%s.%d", tmp.SkuName, index) +// tmp.SkuID = index*1000000 + tmp.SkuID +// newStoreSkuList[k] = &tmp +// } +// return newStoreSkuList +// } + +func (p *PurchaseHandler) UpdateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { + failedList, err = p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, false) + // if err == nil && vendorStoreID == specialStoreID { + // for i := 0; i < 2; i++ { + // p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, duplicateStoreSkuList(storeSkuList, i+1), true) + // } + // } + return failedList, err +} + +func (p *PurchaseHandler) CreateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { + failedList, err = p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, storeSkuList, true) + // if err == nil && vendorStoreID == specialStoreID { + // for i := 0; i < 2; i++ { + // p.createOrUpdateStoreSkus(ctx, storeID, vendorStoreID, duplicateStoreSkuList(storeSkuList, i+1), true) + // } + // } + return failedList, err +} + +// 对于多门店平台来说,storeSkuList中只有SkuID与VendorSkuID有意义 +func (p *PurchaseHandler) createOrUpdateStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*dao.StoreSkuSyncInfo, isCreate bool) (failedList []*partner.StoreSkuInfoWithErr, err error) { + var syncType string + foodDataList := make([]map[string]interface{}, len(storeSkuList)) + if isCreate { + syncType = "创建商品" + } else { + syncType = "更新商品" + } + for i, storeSku := range storeSkuList { + isNeedUpdatePrice := isCreate //storeSku.SkuSyncStatus&(model.SyncFlagPriceMask|model.SyncFlagNewMask) != 0 + foodData := make(map[string]interface{}) + foodDataList[i] = foodData + foodData[mtwmapi.KeyAppFoodCode] = utils.Int2Str(storeSku.SkuID) + skus := []map[string]interface{}{ + map[string]interface{}{ + "sku_id": foodData[mtwmapi.KeyAppFoodCode], + }, + } + foodData["skus"] = skus + foodData["name"] = utils.LimitUTF8StringLen(storeSku.SkuName, mtwmapi.MaxSkuNameCharCount) + foodData["description"] = storeSku.Comment + if isNeedUpdatePrice { + foodData["price"] = jxutils.IntPrice2Standard(storeSku.VendorPrice) + } + if storeSku.MinOrderCount != 0 { + foodData["min_order_count"] = storeSku.MinOrderCount + } else { + foodData["min_order_count"] = 1 + } + foodData["unit"] = storeSku.Unit + attr := SwitchAttr(storeSku.VendorVendorCatID) + if attr != "" { + foodData["common_attr_value"] = attr + } + catCode := tryCatName2Code(storeSku.VendorCatID) + if catCode != "" { + foodData["category_code"] = catCode + } else { + foodData["category_name"] = storeSku.VendorCatID + } + foodData["is_sold_out"] = skuStatusJX2Mtwm(storeSku.MergedStatus) + if true { // vendorStoreID == specialStoreID { + img2 := storeSku.Img2 + img3 := storeSku.Img3 + img4 := storeSku.Img4 + img5 := storeSku.Img5 + if img2 == "" { + img2 = storeSku.Img + } + if img3 == "" { + img3 = storeSku.Img + } + if img4 == "" { + img4 = storeSku.Img + } + if img5 == "" { + img5 = storeSku.Img + } + if storeSku.ImgMix != "" && ((storeSku.BrandID == storeSku.ExBrandID && storeSku.ExBrandID != 0) || storeSku.ExBrandID == 0) { + foodData["picture"] = strings.Join(jxutils.BatchString2Slice(storeSku.ImgMix, img2, img3, img4, img5), ",") + } else { + foodData["picture"] = strings.Join(jxutils.BatchString2Slice(storeSku.Img, img2, img3, img4, img5), ",") + } + } else { + foodData["picture"] = strings.Join(jxutils.BatchString2Slice(storeSku.Img, storeSku.Img2), ",") + } + if storeSku.DescImg != "" { + foodData["picture_contents"] = storeSku.DescImg + } + foodData["sequence"] = storeSku.GetSeq() + if storeSku.VendorVendorCatID != 0 { + foodData["tag_id"] = utils.Int64ToStr(storeSku.VendorVendorCatID) + } else { + // foodData["tag_id"] = utils.Int64ToStr(defVendorCatID) + } + skus[0]["spec"] = jxutils.ComposeSkuSpec(storeSku.SpecQuality, storeSku.SpecUnit) + if isNeedUpdatePrice { + skus[0]["price"] = foodData["price"] + } + skus[0]["stock"] = stockCount2Mtwm(model.MaxStoreSkuStockQty) + if storeSku.Upc != "" { + skus[0]["upc"] = storeSku.Upc + } + skus[0]["ladder_box_num"] = storeSku.LadderBoxNum + boxPirce := 0 + if storeSku.MtLadderBoxPrice != 0 { + boxPirce = storeSku.MtLadderBoxPrice + } else { + boxPirce = storeSku.LadderBoxPrice + } + skus[0]["ladder_box_price"] = jxutils.IntPrice2Standard(int64(boxPirce)) + if foodData["tag_id"] != nil { + skus[0]["weight"] = storeSku.Weight // weight字段仅限服饰鞋帽、美妆、日用品、母婴、生鲜果蔬、生活超市下的便利店/超市门店品类的商家使用 + } + + } + if globals.EnableMtwmStoreWrite { + if len(foodDataList) == 1 { + foodDataList[0]["skus"] = string(utils.MustMarshal(foodDataList[0]["skus"])) + if err = getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailInitData(ctx.GetTrackInfo(), vendorStoreID, utils.Int2Str(storeSkuList[0].SkuID), foodDataList[0]); err != nil { + failedList = putils.GetErrMsg2FailedSingleList(storeSkuList, err, storeID, model.VendorChineseNames[model.VendorIDMTWM], syncType) + } + } else if len(foodDataList) > 0 { + failedFoodList, err2 := getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailBatchInitData(ctx.GetTrackInfo(), vendorStoreID, foodDataList) + if err = err2; err == nil { + if err = putils.GenPartialFailedErr(failedFoodList, len(failedFoodList)); err != nil { + failedList = SelectStoreSkuListByFoodList(storeSkuList, failedFoodList, storeID, model.VendorChineseNames[model.VendorIDMTWM], syncType) + // successList = putils.UnselectStoreSkuSyncListByVendorSkuIDs(storeSkuList, getAppFoodCodeList(failedFoodList)) + } + } else if err2 != nil && len(failedFoodList) == 0 { + if errExt, ok := err2.(*utils.ErrorWithCode); ok { + err = utils.UnmarshalUseNumber([]byte(errExt.ErrMsg()), &failedFoodList) + failedList = SelectStoreSkuListByFoodList(storeSkuList, failedFoodList, storeID, model.VendorChineseNames[model.VendorIDMTWM], syncType) + } + } + } + } + for _, storeSku := range storeSkuList { + storeSku.VendorSkuID = utils.Int2Str(storeSku.SkuID) + } + if len(failedList) > 0 { + err = nil + } + return failedList, err +} + +func getAppFoodCodeList(l []*mtwmapi.AppFoodResult) (vendorSkuIDs []string) { + if len(l) > 0 { + vendorSkuIDs = make([]string, len(l)) + for k, v := range l { + vendorSkuIDs[k] = v.AppFoodCode + } + } + return vendorSkuIDs +} + +func (p *PurchaseHandler) DeleteStoreSkus(ctx *jxcontext.Context, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { + if globals.EnableMtwmStoreWrite { + if len(storeSkuList) == 1 { + err = getAPI(storeSkuList[0].VendorOrgCode, storeID, vendorStoreID).RetailDelete(ctx.GetTrackInfo(), vendorStoreID, storeSkuList[0].VendorSkuID) + failedList = putils.GetErrMsg2FailedSingleList(storeSkuList, err, storeID, model.VendorChineseNames[model.VendorIDMTWM], "删除商品") + } else { + // todo 部分失败 + err = getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID).RetailCatSkuBatchDelete2(ctx.GetTrackInfo(), vendorStoreID, nil, nil, nil, nil, partner.BareStoreSkuInfoList(storeSkuList).GetVendorSkuIDList()) + if err != nil { + if errExt, ok := err.(*utils.ErrorWithCode); ok { + myMap := make(map[string][]*mtwmapi.AppFoodResult) + json.Unmarshal([]byte(errExt.ErrMsg()), &myMap) + failedList = SelectStoreSkuListByFoodList(storeSkuList, myMap["retail_error_list"], storeID, model.VendorChineseNames[model.VendorIDMTWM], "批量删除商品") + } + } + } + } + if len(failedList) > 0 { + err = nil + } + return failedList, err +} + +func SwitchAttr(vendorCatID int64) (attrs string) { + switch vendorCatID { + case 200002727: + return mtwmapi.MtwmSkuAttr200002727 + case 200001555: + return mtwmapi.MtwmSkuAttr200001555 + case 200002728: + return mtwmapi.MtwmSkuAttr200002728 + case 200001519, 200000592: + return mtwmapi.MtwmSkuAttr200000592 + case 200002704, 200002731: + return mtwmapi.MtwmSkuAttr200002731 + case 200002716: + return mtwmapi.MtwmSkuAttr200002716 + case 200002667, 200002713, 200002670: + return mtwmapi.MtwmSkuAttr200002670 + case 200002680: + return mtwmapi.MtwmSkuAttr200002680 + default: + return "" + } + return attrs +} + +func stockCount2Mtwm(stock int) (mtwmStock string) { + return utils.Int2Str(stock) +} + +func storeSku2Mtwm(storeSkuList []*partner.StoreSkuInfo, updateType int) (skuList []*mtwmapi.BareStoreFoodInfo) { + for _, storeSku := range storeSkuList { + skuInfo := &mtwmapi.BareStoreFoodInfo{ + AppFoodCode: storeSku.VendorSkuID, + Skus: []*mtwmapi.BareStoreSkuInfo{ + &mtwmapi.BareStoreSkuInfo{ + SkuID: storeSku.VendorSkuID, + }, + }, + } + if updateType == updateTypeStock { + skuInfo.Skus[0].Stock = stockCount2Mtwm(storeSku.Stock) + } else if updateType == updateTypePrice { + skuInfo.Skus[0].Price = jxutils.IntPrice2StandardString(storeSku.VendorPrice) + } else { + skuInfo.Skus = nil + } + skuList = append(skuList, skuInfo) + } + return skuList +} + +func (p *PurchaseHandler) UpdateStoreSkusStatus(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo, status int) (failedList []*partner.StoreSkuInfoWithErr, err error) { + skuList := storeSku2Mtwm(storeSkuList, updateTypeStatus) + mtwmStatus := skuStatusJX2Mtwm(status) + if globals.EnableMtwmStoreWrite { + failedFoodList, err2 := getAPI(vendorOrgCode, storeID, vendorStoreID).RetailSellStatus(ctx.GetTrackInfo(), vendorStoreID, skuList, mtwmStatus) + if err = err2; err == nil { + if len(failedFoodList) > 0 { + failedList = SelectStoreSkuListByFoodList(storeSkuList, failedFoodList, storeID, model.VendorChineseNames[model.VendorIDMTWM], "更新商品状态") + // successList = putils.UnselectStoreSkuListByVendorSkuIDs(storeSkuList, getAppFoodCodeList(failedFoodList)) + } + } else if err2 != nil && len(failedFoodList) == 0 { + if errExt, ok := err2.(*utils.ErrorWithCode); ok { + err = utils.UnmarshalUseNumber([]byte(errExt.ErrMsg()), &failedFoodList) + failedList = SelectStoreSkuListByFoodList(storeSkuList, failedFoodList, storeID, model.VendorChineseNames[model.VendorIDMTWM], "更新商品状态") + } + } + } + if len(failedList) > 0 { + err = nil + } + return failedList, err +} + +func (p *PurchaseHandler) UpdateStoreSkusPrice(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { + priceList := storeSku2Mtwm(storeSkuList, updateTypePrice) + if globals.EnableMtwmStoreWrite { + failedFoodList, err2 := getAPI(vendorOrgCode, storeID, vendorStoreID).RetailSkuPrice(ctx.GetTrackInfo(), vendorStoreID, priceList) + if err = err2; err == nil { + if len(failedFoodList) > 0 { + failedList = SelectStoreSkuListByFoodList(storeSkuList, failedFoodList, storeID, model.VendorChineseNames[model.VendorIDMTWM], "更新商品价格") + } + } else if err2 != nil && len(failedFoodList) == 0 { + if errExt, ok := err2.(*utils.ErrorWithCode); ok { + err = utils.UnmarshalUseNumber([]byte(errExt.ErrMsg()), &failedFoodList) + failedList = SelectStoreSkuListByFoodList(storeSkuList, failedFoodList, storeID, model.VendorChineseNames[model.VendorIDMTWM], "更新商品价格") + } + } + } + if len(failedList) > 0 { + err = nil + } + return failedList, err +} + +func (p *PurchaseHandler) UpdateStoreSkusStock(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { + stockList := storeSku2Mtwm(storeSkuList, updateTypeStock) + if globals.EnableMtwmStoreWrite { + failedFoodList, err2 := getAPI(vendorOrgCode, storeID, vendorStoreID).RetailSkuStock(ctx.GetTrackInfo(), vendorStoreID, stockList) + if err = err2; err == nil { + if len(failedFoodList) > 0 { + failedList = SelectStoreSkuListByFoodList(storeSkuList, failedFoodList, storeID, model.VendorChineseNames[model.VendorIDMTWM], "更新商品库存") + } + //if err = putils.GenPartialFailedErr(failedFoodList, len(failedFoodList)); err != nil { + // successList = putils.UnselectStoreSkuListByVendorSkuIDs(storeSkuList, getAppFoodCodeList(failedFoodList)) + // } + } else if err2 != nil && len(failedFoodList) == 0 { + if errExt, ok := err2.(*utils.ErrorWithCode); ok { + err = utils.UnmarshalUseNumber([]byte(errExt.ErrMsg()), &failedFoodList) + failedList = SelectStoreSkuListByFoodList(storeSkuList, failedFoodList, storeID, model.VendorChineseNames[model.VendorIDMTWM], "更新商品库存") + } + } + } + if len(failedList) > 0 { + err = nil + } + return failedList, err +} + +func mtwmSkuStatus2Jx(mtwmSkuStatus int) (jxSkuStatus int) { + if mtwmSkuStatus == mtwmapi.SellStatusOnline { + jxSkuStatus = model.SkuStatusNormal + } else { + jxSkuStatus = model.SkuStatusDontSale + } + return jxSkuStatus +} + +func (p *PurchaseHandler) GetStoreSkusFullInfo(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (skuNameList []*partner.SkuNameInfo, err error) { + if len(storeSkuList) == 1 { + skuInfo, err := getAPI(storeSkuList[0].VendorOrgCode, storeID, vendorStoreID).RetailGet(vendorStoreID, utils.Int2Str(storeSkuList[0].SkuID)) + if err != nil { + return nil, err + } + if skuName := vendorSku2Jx(skuInfo); skuName != nil { + skuNameList = append(skuNameList, skuName) + } + } else { + var storeSkuMap map[string]*partner.StoreSkuInfo + if storeSkuList != nil { + storeSkuMap = putils.StoreSkuList2MapByVendorSkuID(storeSkuList) + } + mtapi := getAPI(getStoreVendorOrgCode(storeID), storeID, vendorStoreID) + for { + // todo 待优化获取速度 + result, err := mtapi.RetailList(vendorStoreID, len(skuNameList), mtwmapi.GeneralMaxLimit) + if err != nil { + return nil, err + } + if storeSkuMap == nil { + skuNameList = append(skuNameList, vendorSkuList2Jx(result)...) + } else { + for _, v := range result { + if storeSkuMap[v.AppFoodCode] != nil { + if skuName := vendorSku2Jx(v); skuName != nil { + skuNameList = append(skuNameList, skuName) + } + } + } + } + if len(result) < mtwmapi.GeneralMaxLimit { + break + } + } + } + return skuNameList, err +} + +func vendorSku2Jx(appFood *mtwmapi.AppFood) (skuName *partner.SkuNameInfo) { + if len(appFood.SkuList) == 0 { + return nil + } + prefix, name, comment, specUnit, unit, specQuality := jxutils.SplitSkuName(appFood.Name) + vendorSku := appFood.SkuList[0] + weight := int(utils.Str2Int64WithDefault(vendorSku.Weight, 0)) + if weight <= 0 { + weight = jxutils.FormatSkuWeight(specQuality, specUnit) + } + skuID := int(utils.Str2Int64WithDefault(vendorSku.SkuID, 0)) + skuName = &partner.SkuNameInfo{ + NameID: int(utils.Str2Int64WithDefault(appFood.AppFoodCode, 0)), + VendorNameID: appFood.AppFoodCode, + UPC: appFood.SkuList[0].Upc, + Prefix: prefix, + Name: name, + Unit: unit, + Status: mtwmSkuStatus2Jx(appFood.IsSoldOut), //此处为之前一个bug 如果吧状态放到切片内层 对于内层函数中映射无法关联 永远获取到的初始值为0 + SkuList: []*partner.SkuInfo{ + &partner.SkuInfo{ + StoreSkuInfo: partner.StoreSkuInfo{ + VendorSkuID: vendorSku.SkuID, + SkuID: skuID, + IsSpecialty: appFood.IsSpecialty, + Stock: int(utils.Str2Int64WithDefault(vendorSku.Stock, partner.UnlimitedStoreSkuStock)), + VendorPrice: jxutils.StandardPrice2Int(utils.Str2Float64WithDefault(vendorSku.Price, 0)), + Status: mtwmSkuStatus2Jx(appFood.IsSoldOut), + }, + SkuName: appFood.Name, + Comment: comment, + SpecQuality: float64(specQuality), + SpecUnit: specUnit, + Weight: weight, + }, + }, + PictureList: appFood.PictureList, + } + if appFood.CategoryName != "" { + // todo, 因为当前我们用的是分类名操作这种方式,所以要返回分类名(而不是分类code) + skuName.VendorCatIDList = []string{appFood.CategoryName} + if appFood.SecondaryCategoryName != "" { + skuName.VendorCatIDList = append(skuName.VendorCatIDList, appFood.SecondaryCategoryName) + } + } + return skuName +} + +func vendorSkuList2Jx(appFoodList []*mtwmapi.AppFood) (skuNameList []*partner.SkuNameInfo) { + for _, appFood := range appFoodList { + if skuName := vendorSku2Jx(appFood); skuName != nil { + skuNameList = append(skuNameList, skuName) + } + } + return skuNameList +} + +func (p *PurchaseHandler) GetSensitiveWordRegexp() *regexp.Regexp { + return sensitiveWordRegexp +} + +//美团api返回 +func SelectStoreSkuListByFoodList(storeSkuList interface{}, foodList []*mtwmapi.AppFoodResult, storeID int, vendorName, syncType string) (selectedStoreSkuList []*partner.StoreSkuInfoWithErr) { + foodMap := make(map[string]string) + if len(foodList) > 0 { + for _, v := range foodList { + foodMap[v.AppFoodCode] = v.ErrorMsg + } + if storeSkuLists, ok := storeSkuList.([]*partner.StoreSkuInfo); ok { + for _, v := range storeSkuLists { + if foodMap[v.VendorSkuID] != "" { + foodFailed := &partner.StoreSkuInfoWithErr{ + StoreSkuInfo: v, + ErrMsg: foodMap[v.VendorSkuID], + StoreID: storeID, + VendoreName: vendorName, + SyncType: syncType, + } + selectedStoreSkuList = append(selectedStoreSkuList, foodFailed) + } + } + } + if storeSkuLists, ok := storeSkuList.([]*dao.StoreSkuSyncInfo); ok { + for _, v := range storeSkuLists { + if foodMap[v.VendorSkuID] != "" { + storeSkuInfo := &partner.StoreSkuInfo{ + SkuID: v.SkuID, + VendorSkuID: v.VendorSkuID, + NameID: v.NameID, + VendorNameID: v.VendorNameID, + VendorPrice: v.VendorPrice, + Status: v.Status, + } + foodFailed := &partner.StoreSkuInfoWithErr{ + StoreSkuInfo: storeSkuInfo, + ErrMsg: foodMap[v.VendorSkuID], + StoreID: storeID, + VendoreName: vendorName, + SyncType: syncType, + } + selectedStoreSkuList = append(selectedStoreSkuList, foodFailed) + } + } + } + } + return selectedStoreSkuList +} + +func (p *PurchaseHandler) CreateStoreSkusAct(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { + actStoreSkuList := putils.StoreSku2ActStoreSku(model.SyncFlagNewMask, vendorStoreID, storeSkuList) + failedList, err = createOneShopAct(putils.GetFixDirectDownAct(vendorOrgCode, storeID, 0), vendorStoreID, actStoreSkuList) + storeSkuMap := putils.StoreSkuList2MapBySkuID(storeSkuList) + for _, v := range actStoreSkuList { + storeSkuMap[v.SkuID].VendorActID = v.VendorActID + } + if len(failedList) > 0 { + err = nil + } + return failedList, err +} + +func (p *PurchaseHandler) CancelActs(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (failedList []*partner.StoreSkuInfoWithErr, err error) { + return cancelOneShopAct(putils.GetFixDirectDownAct(vendorOrgCode, storeID, 0), vendorStoreID, putils.StoreSku2ActStoreSku(model.SyncFlagDeletedMask, vendorStoreID, storeSkuList)) +} + +func (p *PurchaseHandler) UpdateStoreSkusSpecTag(ctx *jxcontext.Context, vendorOrgCode string, storeID int, vendorStoreID string, storeSkuList []*partner.StoreSkuInfo) (err error) { + var foodDataList = []map[string]interface{}{} + for _, v := range storeSkuList { + var foodData = make(map[string]interface{}) + if v.IsSpecialty == -1 { + v.IsSpecialty = 0 + } + foodData["is_specialty"] = v.IsSpecialty + foodData["app_food_code"] = v.SkuID + foodDataList = append(foodDataList, foodData) + } + if globals.EnableMtwmStoreWrite { + if len(foodDataList) == 1 { + err = getAPI(vendorOrgCode, storeID, vendorStoreID).RetailInitData(ctx.GetTrackInfo(), vendorStoreID, utils.Int2Str(storeSkuList[0].SkuID), foodDataList[0]) + } else if len(foodDataList) > 0 { + _, err = getAPI(vendorOrgCode, storeID, vendorStoreID).RetailBatchInitData(ctx.GetTrackInfo(), vendorStoreID, foodDataList) + } + } + return err +} + +func (p *PurchaseHandler) GetSkuCategoryIdByName(vendorOrgCode, skuName string) (vendorCategoryId string, err error) { + return "", err +} diff --git a/business/partner/purchase/tao_vegetable/store_sku2_test.go b/business/partner/purchase/tao_vegetable/store_sku2_test.go new file mode 100644 index 000000000..ceac4aa69 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/store_sku2_test.go @@ -0,0 +1,64 @@ +package mtwm + +import ( + "testing" + + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + "git.rosy.net.cn/jx-callback/business/partner" + // _ "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" +) + +func TestGetStoreSkusFullInfo(t *testing.T) { + skuNameList, err := CurPurchaseHandler.GetStoreSkusFullInfo(jxcontext.AdminCtx, nil, 2, "8967897", nil) + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(skuNameList, false)) + t.Log(len(skuNameList)) +} + +func TestGetStoreSkusBareInfo(t *testing.T) { + storeSkuList, err := CurPurchaseHandler.GetStoreSkusBareInfo(jxcontext.AdminCtx, "", nil, 2, "2523687", []*partner.StoreSkuInfo{ + &partner.StoreSkuInfo{ + SkuID: 16205, + }, + //&partner.StoreSkuInfo{ + // SkuID: 1306, + //}, + }) + if err != nil { + t.Fatal(err.Error()) + } + t.Log(utils.Format4Output(storeSkuList, false)) + t.Log(len(storeSkuList)) +} + +func TestDeleteStoreAllSkus(t *testing.T) { + err := CurPurchaseHandler.DeleteStoreAllSkus(jxcontext.AdminCtx, nil, 2, "2523687", true) + if err != nil { + t.Fatal(err) + } +} + +func TestDeleteStoreAllCategories(t *testing.T) { + err := CurPurchaseHandler.DeleteStoreAllCategories(jxcontext.AdminCtx, nil, 2, "2523687", true) + if err != nil { + t.Fatal(err) + } +} + +func TestGetStoreCategory(t *testing.T) { + _, err := CurPurchaseHandler.GetStoreCategory(jxcontext.AdminCtx, 2, "2523687", "不存在的分类") + if err == nil { + t.Fatal("应该找不到这个分类") + } + catName := "小月饼" + cat, err := CurPurchaseHandler.GetStoreCategory(jxcontext.AdminCtx, 2, "2523687", catName) + if err != nil { + t.Fatal(err) + } else if cat.Name != catName { + t.Fatal("没有找到正确的商家分类") + } + t.Log(utils.Format4Output(cat, false)) +} diff --git a/business/partner/purchase/tao_vegetable/store_sku_test.go b/business/partner/purchase/tao_vegetable/store_sku_test.go new file mode 100644 index 000000000..c702627c6 --- /dev/null +++ b/business/partner/purchase/tao_vegetable/store_sku_test.go @@ -0,0 +1,46 @@ +package mtwm + +import ( + "testing" + + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + // _ "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" +) + +// func TestSyncStoreCategory(t *testing.T) { +// hint, err := CurPurchaseHandler.SyncStoreCategory(jxcontext.AdminCtx, nil, testShopID, false) +// if err != nil { +// t.Fatal(err) +// } +// t.Log(hint) +// } + +// func TestSyncLocalStoreCategory(t *testing.T) { +// hint, err := CurPurchaseHandler.SyncLocalStoreCategory(jxcontext.AdminCtx, nil, testShopID, true, nil) +// if err != nil { +// t.Fatal(err) +// } +// t.Log(hint) +// } + +// func TestSyncStoreSkus(t *testing.T) { +// hint, err := CurPurchaseHandler.SyncStoreSkus(jxcontext.AdminCtx, nil, testShopID, nil, false, true) +// if err != nil { +// t.Fatal(err) +// } +// t.Log(hint) +// } + +func TestDeleteRemoteSkus(t *testing.T) { + err := CurPurchaseHandler.DeleteStoreAllSkus(jxcontext.AdminCtx, nil, testShopID, testShopVendorID, true) + if err != nil { + t.Fatal(err) + } +} + +func TestDeleteRemoteCategories(t *testing.T) { + err := CurPurchaseHandler.DeleteStoreAllCategories(jxcontext.AdminCtx, nil, testShopID, testShopVendorID, true) + if err != nil { + t.Fatal(err) + } +} diff --git a/business/partner/purchase/tao_vegetable/store_test.go b/business/partner/purchase/tao_vegetable/store_test.go new file mode 100644 index 000000000..5ae7129fa --- /dev/null +++ b/business/partner/purchase/tao_vegetable/store_test.go @@ -0,0 +1,43 @@ +package mtwm + +import ( + "testing" + + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + // _ "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" +) + +//func TestReadStore(t *testing.T) { +// store, err := CurPurchaseHandler.ReadStore(jxcontext.AdminCtx, "", "4351018") +// if err != nil { +// t.Fatal(err) +// } +// t.Log(utils.Format4Output(store, false)) +//} + +func TestUpdateStore(t *testing.T) { + err := CurPurchaseHandler.UpdateStore(nil, 100002, "test") + if err != nil { + t.Fatal(err) + } +} + +func TestConstrainOpTimeList(t *testing.T) { + timeList := constrainOpTimeList([]int16{830, 1800}, []int16{ + 0, + 200, + 930, + 1700, + }) + t.Log(utils.Format4Output(timeList, false)) + if timeList[0] != 930 || timeList[1] != 1700 { + t.Fatal("constrainOpTimeList failed") + } +} + +func TestGetOpTimeListFromErr(t *testing.T) { + err := utils.NewErrorIntCode("当前配送营业时间为:07:00~24:00", mtwmapi.ErrCodeOpFailed) + list := getOpTimeListFromErr(err) + t.Log(list) +} diff --git a/business/partner/purchase/tao_vegetable/waybill.go b/business/partner/purchase/tao_vegetable/waybill.go new file mode 100644 index 000000000..01be0cf0e --- /dev/null +++ b/business/partner/purchase/tao_vegetable/waybill.go @@ -0,0 +1,64 @@ +package mtwm + +import ( + "git.rosy.net.cn/baseapi/platformapi/mtwmapi" + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/model" + "git.rosy.net.cn/jx-callback/business/partner" +) + +var ( + VendorWaybillStatus2StatusMap = map[string]int{ + mtwmapi.WaybillStatusWait4Delivery: model.WaybillStatusNew, + mtwmapi.WaybillStatusPending: model.WaybillStatusPending, + mtwmapi.WaybillStatusAccepted: model.WaybillStatusAccepted, + mtwmapi.WaybillStatusCourierArrived: model.WaybillStatusCourierArrived, + mtwmapi.WaybillStatusPickedup: model.WaybillStatusDelivering, + mtwmapi.WaybillStatusDelivered: model.WaybillStatusDelivered, + mtwmapi.WaybillStatusCanceled: model.WaybillStatusCanceled, + } +) + +func (p *PurchaseHandler) GetWaybillStatusFromVendorStatus(vendorStatus string) int { + if status, ok := VendorWaybillStatus2StatusMap[vendorStatus]; ok { + return status + } + return model.WaybillStatusUnknown +} + +func (c *PurchaseHandler) onWaybillMsg(msg *mtwmapi.CallbackMsg) (response *mtwmapi.CallbackResponse) { + waybill := c.callbackMsg2Waybill(msg) + err := partner.CurOrderManager.OnWaybillStatusChanged(waybill) + if err == nil && waybill.Status == model.WaybillStatusDelivering { + c.postFakeMsg(waybill.VendorOrderID, FakeMsgType, mtwmapi.OrderStatusDelivering) + } + return mtwmapi.Err2CallbackResponse(err, "") +} + +func (c *PurchaseHandler) callbackMsg2Waybill(msg *mtwmapi.CallbackMsg) (retVal *model.Waybill) { + orderID := GetOrderIDFromMsg(msg) + vendorStatus := msg.FormData.Get("logistics_status") + retVal = &model.Waybill{ + VendorOrderID: orderID, + OrderVendorID: model.VendorIDMTWM, + VendorWaybillID: orderID, + WaybillVendorID: model.VendorIDMTWM, + CourierName: msg.FormData.Get("dispatcher_name"), + CourierMobile: msg.FormData.Get("dispatcher_mobile"), + VendorStatus: vendorStatus, + Status: c.GetWaybillStatusFromVendorStatus(vendorStatus), + StatusTime: getTimeFromTimestamp(utils.Str2Int64(msg.FormData.Get("time"))), + Remark: "", + VendorOrgCode: msg.AppID, + } + if retVal.StatusTime == utils.DefaultTimeValue { + retVal.StatusTime = getTimeFromTimestamp(utils.Str2Int64(msg.FormData.Get("timestamp"))) + } + + //if vendorStatus == "4" || vendorStatus == "8" { // 4:美团推送已经(确认骑手)订单 8:美团推送(骑手完成)订单 + // retVal.DesiredFee = utils.Float64TwoInt64(utils.Str2Float64WithDefault(msg.FormData.Get("shipping_fee"), 0)) // 订单优惠前的总费用 + // partner.CurOrderManager + //} + + return retVal +}