package cms import ( "errors" "fmt" "io" "math" "mime/multipart" "sort" "strconv" "strings" "sync" "time" "unicode" "git.rosy.net.cn/baseapi/platformapi/mtwmapi" "git.rosy.net.cn/jx-callback/business/auth2" beego "github.com/astaxie/beego/server/web" "git.rosy.net.cn/baseapi/platformapi/jdshopapi" "git.rosy.net.cn/jx-callback/globals/api" "git.rosy.net.cn/jx-callback/globals/refutil" "git.rosy.net.cn/jx-callback/business/jxstore/event" "git.rosy.net.cn/jx-callback/business/jxstore/permission" "git.rosy.net.cn/baseapi" "git.rosy.net.cn/jx-callback/business/jxutils/ddmsg" "git.rosy.net.cn/jx-callback/business/jxutils/excel" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" "git.rosy.net.cn/jx-callback/business/partner/purchase/jd" "git.rosy.net.cn/jx-callback/globals/api/apimanager" "git.rosy.net.cn/jx-callback/business/partner" "git.rosy.net.cn/baseapi/platformapi/dingdingapi" "git.rosy.net.cn/baseapi/platformapi/jdapi" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/baseapi/utils/errlist" "git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin" "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/weixinmsg" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/globals" "github.com/360EntSecGroup-Skylar/excelize" "github.com/astaxie/beego/client/orm" ) const ( MaxSkuUnitPrice = 500000 CopyStoreSkuModeFresh = "fresh" // 全新复制 CopyStoreSkuModeUpdate = "update" // 增量复制 CopyStoreSkuModeUpdatePrice = "updatePrice" // 增量复制价格 upcSpecName1 = "(新老包装随机发货)" ) //通用写入Excel type ExcelParam struct { DataList interface{} SheetName string TitleList []string } // UpdateStoreSku用,API调用时 type StoreSkuBindSkuInfo struct { SkuID int `json:"skuID"` IsSale int `json:"isSale,omitempty"` // -1:不可售,0:忽略,1:可售 Stock *int `json:"stock"` // ElmID int64 `json:"elmID,omitempty"` // EbaiID int64 `json:"ebaiID,omitempty"` MtLadderBoxPrice int `json:"mtLadderBoxPrice"` } // UpdateStoreSku用,API调用时 type StoreSkuBindInfo struct { StoreID int `json:"storeID"` NameID int `json:"nameID"` UnitPrice int `json:"unitPrice"` // 对于是份的SKU就是单价(每斤价格),其它则为总价 IsFocus int `json:"isFocus"` // -1:不关注,0:忽略,1:关注 IsSale int `json:"isSale"` // -1:不可售,0:忽略,1:可售 SubStoreID int `json:"subStoreID,omitempty"` StatusSaleBegin int16 `json:"statusSaleBegin" validate:"max=2359,min=1,ltfield=StatusSaleEnd"` //商品可售时间范围 StatusSaleEnd int16 `json:"statusSaleEnd" validate:"max=2359,min=1"` UPC string `json:"upc"` MtLadderBoxPrice int `json:"mtLadderBoxPrice"` Skus []*StoreSkuBindSkuInfo `json:"skus,omitempty"` } type tStoreSkuBindAndSpec struct { model.StoreSkuBind SkuStatus int SkuNameStatus int Name string SpecQuality float32 SpecUnit string SkuNamePrice int SkuNameUnit string RealSkuID int `orm:"column(real_sku_id)"` ExdSkuID string `orm:"column(exd_sku_id)"` StoreName string ChangePriceType int8 `json:"changePriceType"` // 修改价格类型,即是否需要审核 NameID int `orm:"column(name_id)" json:"nameID"` } type SkuSaleInfo struct { StoreID int `orm:"column(store_id)"` SkuID int `orm:"column(sku_id)"` Times int // 销售的次数 Count int // 销售的总份数 } type StoreOpRequestInfo struct { model.StoreOpRequest StoreName string `json:"storeName"` SkuNamePrefix string `json:"skuNamePrefix"` SkuNameName string `json:"skuNameName"` UnitPrice int `json:"unitPrice"` } type tStoreNameBind struct { StoreID int `orm:"column(store_id)"` NameID int `orm:"column(name_id)"` Name string } type tGetStoresSkusInfo struct { StoreID int `orm:"column(store_id)"` StoreName string model.SkuName PayPercentage int `json:"-"` dao.StoreSkuExt RealMidUnitPrice int `json:"realMidUnitPrice"` //真实的该商品的全国中位价 YbSkuName string AuditUnitPrice int } type SheetParam struct { OutSkuIDCol int SkuNameIDCol int SkuPriceCol int SkuNameCol int SkuUnitCol int SkuRow int } type DataVendorStoreSkuPrice struct { StoreID string `json:"门店ID"` StoreName string `json:"门店名"` SkuID int `json:"商品ID"` SkuName string `json:"商品名"` VendorPrice string `json:"平台价"` } type DataSuccess struct { NameID int `json:"商品NameID"` Name string `json:"商品名称"` Unit string `json:"单位"` OrgPrice float64 `json:"原价"` NowPrice float64 `json:"现价"` MixPrice float64 `json:"涨跌"` } type DataFailed struct { NameID int `json:"商品NameID"` Name string `json:"商品名称"` Comment string `json:"备注"` } type DataLock struct { dataSuccessList []DataSuccess dataFailedList []DataFailed locker sync.RWMutex } type tUpdateStoresSkus struct { StoreID int SkuBindInfos []*StoreSkuBindInfo } type tStoreSkusSecKill struct { StoreID int `orm:"column(store_id)"` VendorID int `orm:"column(vendor_id)"` SecKillCount int SecKillCount2 int OperatorPhoneList []string MarketManPhone string NoticeMsg string } type JdStoreSkus struct { JdStoreID int `json:"jdStoreID"` JdSkuID int `json:"jdSkuID"` Price int `json:"price"` } type tUpdateSkuSpecTag struct { StoreID int `json:"storeID"` SkuID int `json:"skuID"` IsSpec int `json:"isSpec"` } type MatterStock struct { SkuID int `json:"skuID"` SkuNameID int `json:"skuNameID"` Name string `json:"name"` Stock int `json:"stock"` } type ActStoreSkuParam struct { model.ActStoreSku ActualActPrice int64 `json:"actualActPrice,omitempty"` // 单品级活动用,创建活动时商品的活动价格 VendorPrice int64 `json:"vendorPrice,omitempty"` // 创建活动时的平台价格 ErrMsg string `json:"errMsg,omitempty"` } const ( maxStoreNameBind = 10000 // 最大门店SkuName bind个数 maxStoreNameBind2 = 10000 // 最大门店乘SkuName个数 AutoSaleAtStr = "22:00:00" ) var ( asyncOpMobileMap = map[string]int{ "18982250714": 1, // 老赵 "18180948107": 1, // 徐 // "13684045763": 1, // 周 } dataLock DataLock titleListVendorStoreSkuPrice = []string{ "门店ID", "门店名", "商品ID", "商品名", "平台价", } titleListSuccess = []string{ "商品NameID", "商品名称", "单位", "原价", "现价", "涨跌", } titleListFailed = []string{ "商品NameID", "商品名称", "备注", } autoNotFoucsStoreMap = map[int]int{ 667088: 667088, model.MatterStoreID: model.MatterStoreID, 103038: 103038, 300397: 300397, } ) func GetStoresSkusForStore(ctx *jxcontext.Context, storeID int, isFocus, isAct bool, keyword string, categoryID, status, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo2, err error) { //权限 //if permission.IsRoled(ctx) { // if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil { // if storeIDsMap[storeID] == 0 { // return skuNamesInfo, err // } // } //} var ( sqlParams []interface{} db = dao.GetDB() SkuNames []*dao.StoreSkuNameExt2 ) sql := ` SELECT SQL_CALC_FOUND_ROWS DISTINCT a.*, e.id store_id, c.unit_price -- ,b.id sku_id, b.spec_quality sku_spec_quality, -- b.spec_unit sku_spec_unit, b.status sku_status, c.stock, c.price bind_price, -- c.status store_sku_status FROM sku_name a JOIN sku b ON b.name_id = a.id AND b.deleted_at = ? JOIN store e ON e.deleted_at = ? ` sqlParams = append(sqlParams, utils.DefaultTimeValue, utils.DefaultTimeValue, ) if !isFocus { sql += ` LEFT ` } sql += ` JOIN store_sku_bind c ON c.sku_id = b.id AND c.deleted_at = ? AND c.store_id = e.id ` sqlParams = append(sqlParams, utils.DefaultTimeValue) if status != -1 { sql += " AND c.status = ?" sqlParams = append(sqlParams, status) } sql += ` LEFT JOIN sku_name_place_bind f ON a.id = f.name_id AND e.city_code = f.place_code ` if isAct { sql += ` JOIN ( SELECT t2.store_id, t2.sku_id, MIN(IF(t3.actual_act_price <= 0, NULL, t3.actual_act_price)) actual_act_price, /*non-zero min value*/ MIN(IF(t2.earning_price <= 0, NULL, t2.earning_price)) earning_price /*non-zero min value*/ FROM act t1 JOIN act_store_sku t2 ON t2.act_id = t1.id AND t2.deleted_at = ? JOIN act_store_sku_map t3 ON t3.bind_id = t2.id AND t3.act_id = t1.id AND (t3.sync_status & ? = 0 OR t1.type = ?) JOIN act_map t4 ON t4.act_id = t1.id AND t4.vendor_id = t3.vendor_id AND t4.deleted_at = ? AND (t4.sync_status & ? = 0 OR t1.type = ?) WHERE t1.deleted_at = ? AND t1.status = ? AND NOT (t1.begin_at > ? OR t1.end_at < ?) AND t2.store_id = ? GROUP BY 1,2 ) ta ON ta.store_id = e.id AND ta.sku_id = b.id ` sqlParams = append(sqlParams, utils.DefaultTimeValue, model.SyncFlagNewMask, model.ActSkuFake, utils.DefaultTimeValue, model.SyncFlagNewMask, model.ActSkuFake, utils.DefaultTimeValue, model.ActStatusCreated, time.Now(), time.Now(), storeID, ) } sql += ` WHERE a.deleted_at = ? AND (a.is_global = 1 OR f.id IS NOT NULL OR 1 = ?) AND e.id = ? ` sqlParams = append(sqlParams, utils.DefaultTimeValue, utils.Bool2Int(isFocus), storeID) if categoryID != 0 { cat := &model.SkuCategory{} cat.ID = categoryID if err = dao.GetEntity(db, cat); err == nil { sql += " AND (a.category_id = ?" sqlParams = append(sqlParams, cat.ID) if cat.Level == 1 { sql += " OR a.category_id IN (SELECT id FROM sku_category WHERE parent_id = ?)" sqlParams = append(sqlParams, cat.ID) } sql += ")" } } if isFocus { sql += " AND a.status = ? AND b.status = ?" sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal) } else { sql += " AND c.sku_id IS NULL AND (a.status = ? AND b.status = ?)" sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal) } if keyword != "" { sql += " AND a.name LIKE ?" sqlParams = append(sqlParams, "%"+keyword+"%") } sql += ` LIMIT ? OFFSET ? ` //var tmpList []*tGetStoresSkusInfo pageSize = jxutils.FormalizePageSize(pageSize) offset = jxutils.FormalizePageOffset(offset) sqlParams = append(sqlParams, pageSize, offset) txDB, _ := dao.Begin(db) defer dao.Commit(db, txDB) if err = dao.GetRowsTx(txDB, &SkuNames, sql, sqlParams...); err == nil { skuNamesInfo = &dao.StoreSkuNamesInfo2{ TotalCount: dao.GetLastTotalRowCount2(db, txDB), } // skuNamesInfo.TotalCount = dao.GetLastTotalRowCount2(db, txDB) task := tasksch.NewParallelTask("uuu", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { v := batchItemList[0].(*dao.StoreSkuNameExt2) var skus []*dao.StoreSkuExt sql2 := ` SELECT a.id sku_id, a.spec_quality sku_spec_quality, a.spec_unit sku_spec_unit, a.status sku_status, b.stock, b.price bind_price, b.unit_price, b.status store_sku_status FROM sku a JOIN sku_name c ON a.name_id = c.id AND c.deleted_at = ? AND c.status = ? ` sqlParams2 := []interface{}{ utils.DefaultTimeValue, model.SkuStatusNormal, } if !isFocus { sql2 += ` LEFT ` } sql2 += ` JOIN store_sku_bind b ON a.id = b.sku_id AND b.deleted_at = ? AND b.store_id = ? WHERE a.deleted_at = ? AND a.name_id = ? AND a.status = ? ` sqlParams2 = append(sqlParams2, utils.DefaultTimeValue, storeID, utils.DefaultTimeValue, v.ID, model.SkuStatusNormal, ) if status != -1 { sql2 += " AND b.status = ?" sqlParams2 = append(sqlParams2, status) } if err = dao.GetRows(db, &skus, sql2, sqlParams2); err == nil { v.Skus = skus } return retVal, err }, SkuNames) tasksch.HandleTask(task, nil, false).Run() task.GetResult(0) //for _, v := range SkuNames { // //} skuNamesInfo.SkuNames = SkuNames //storeNameMap := make(map[int64]*dao.StoreSkuNameExt) //for _, v := range tmpList { // var storeName *dao.StoreSkuNameExt // index := jxutils.Combine2Int(v.StoreID, v.ID) // if storeNameMap[index] == nil { // storeName = &dao.StoreSkuNameExt{ // StoreID: v.StoreID, // StoreName: v.StoreName, // SkuName: v.SkuName, // UnitPrice: v.UnitPrice, // PayPercentage: v.PayPercentage, // RealMidUnitPrice: v.RealMidUnitPrice, // YbSkuName: v.YbSkuName, // AuditUnitPrice: v.AuditUnitPrice, // } // storeNameMap[index] = storeName // skuNamesInfo.SkuNames = append(skuNamesInfo.SkuNames, storeName) // } else { // storeName = storeNameMap[index] // } // storeName.Skus = append(storeName.Skus, &v.StoreSkuExt) //} //if err == nil { // if isFocus { // if err == nil { // storeIDs, skuIDs := GetStoreAndSkuIDsFromInfo(skuNamesInfo) // err = dao.UpdateActPrice4StoreSkuNameNew(db, storeIDs, skuIDs, skuNamesInfo, -1) // } // } else { // err = updateUnitPrice4StoreSkuNameNew(db, skuNamesInfo) // } //} } return skuNamesInfo, err } func GetStoreSkus(ctx *jxcontext.Context, storeID int, skuIDs []int, isFocus bool, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) { return GetStoresSkus(ctx, []int{storeID}, skuIDs, nil, isFocus, false, 0, keyword, isBySku, isAct, params, offset, pageSize) } func getGetStoresSkusBaseSQL(db *dao.DaoDB, storeIDs, skuIDs []int, upcs []string, isFocus bool, keyword string, isBySku, isAct, isHighPrice bool, priceType int, actVendorID int, params map[string]interface{}) (sql string, sqlParams []interface{}, err error) { sql = ` FROM sku_name t1 JOIN sku t2 FORCE INDEX(PRIMARY) ON t1.id = t2.name_id AND t2.deleted_at = ?/* AND t2.status = ?*/ JOIN store t3 ON t3.deleted_at = ? LEFT JOIN store_map sm ON sm.store_id = t3.id AND sm.vendor_id = ? AND sm.deleted_at = ? LEFT JOIN store_map smm ON smm.store_id = t3.id AND smm.deleted_at = ? AND smm.vendor_id = ? LEFT JOIN thing_map t2m ON t2m.thing_type = ? AND t2m.thing_id = t2.id AND t2m.vendor_id = sm.vendor_id AND t2m.vendor_org_code = sm.vendor_org_code AND t2m.deleted_at = ? ` sqlParams = []interface{}{ utils.DefaultTimeValue, // model.SkuStatusNormal, utils.DefaultTimeValue, model.VendorIDJD, utils.DefaultTimeValue, // TODO 这里直接用JD有问题 utils.DefaultTimeValue, model.VendorIDYB, model.ThingTypeSku, utils.DefaultTimeValue, } if isFocus { if isAct { sql += ` JOIN ( SELECT t2.store_id, t2.sku_id, MIN(IF(t3.actual_act_price <= 0, NULL, t3.actual_act_price)) actual_act_price, /*non-zero min value*/ MIN(IF(t2.earning_price <= 0, NULL, t2.earning_price)) earning_price /*non-zero min value*/ FROM act t1 JOIN act_store_sku t2 ON t2.act_id = t1.id AND t2.deleted_at = ? JOIN act_store_sku_map t3 ON t3.bind_id = t2.id AND t3.act_id = t1.id AND (t3.sync_status & ? = 0 OR t1.type = ?) JOIN act_map t4 ON t4.act_id = t1.id AND t4.vendor_id = t3.vendor_id AND t4.deleted_at = ? AND (t4.sync_status & ? = 0 OR t1.type = ?) WHERE t1.deleted_at = ? AND t1.status = ? AND NOT (t1.begin_at > ? OR t1.end_at < ?)` sqlParams = append(sqlParams, []interface{}{ utils.DefaultTimeValue, model.SyncFlagNewMask, model.ActSkuFake, utils.DefaultTimeValue, model.SyncFlagNewMask, model.ActSkuFake, utils.DefaultTimeValue, model.ActStatusCreated, time.Now(), time.Now(), }) if actVendorID >= 0 { sql += " AND t1.vendor_mask & ? <> 0" sqlParams = append(sqlParams, model.GetVendorMask(actVendorID)) } if len(storeIDs) > 0 { sql += " AND t2.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")" sqlParams = append(sqlParams, storeIDs) } if len(skuIDs) > 0 { sql += " AND t2.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")" sqlParams = append(sqlParams, skuIDs) } sql += ` GROUP BY 1,2 ) ta ON ta.store_id = t3.id AND ta.sku_id = t2.id` } } else { sql += " LEFT" } sql += ` JOIN store_sku_bind t4 ON t4.store_id = t3.id AND t4.sku_id = t2.id AND t4.deleted_at = ? LEFT JOIN sku_name_place_bind t5 ON t1.id = t5.name_id AND t3.city_code = t5.place_code LEFT JOIN price_refer_snapshot t6 ON t6.city_code = ? AND t6.sku_id = t2.id AND t6.snapshot_at = ? LEFT JOIN store_sku_audit t7 ON t7.store_id = t3.id AND t7.name_id = t1.id AND t7.status = ? AND t7.deleted_at = ? WHERE t1.deleted_at = ? AND (t1.is_global = 1 OR t5.id IS NOT NULL OR 1 = ?)/* AND t1.status = ?*/ ` sqlParams = append(sqlParams, []interface{}{ utils.DefaultTimeValue, 0, utils.Time2Date(time.Now().AddDate(0, 0, -1)), model.StoreAuditStatusOnline, utils.DefaultTimeValue, utils.DefaultTimeValue, utils.Bool2Int(isFocus), }) if isHighPrice || priceType == 1 { sql += " AND t4.unit_price > t6.mid_unit_price / IF(t3.pay_percentage < 50 , 70, t3.pay_percentage) * 1.2 * 100" } if priceType == -1 { sql += " AND t4.unit_price < t6.mid_unit_price / IF(t3.pay_percentage < 50 , 70, t3.pay_percentage) / 1.2 * 100" } if isFocus { sql += " AND ((t2.status = ? AND t1.status = ?) OR t4.status = ?)" sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal, model.SkuStatusNormal) } else { sql += " AND t4.sku_id IS NULL AND (t2.status = ? AND t1.status = ?)" sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal) } if keyword != "" { keywordLike := "%" + keyword + "%" sql += " AND (t1.name LIKE ? OR t1.prefix LIKE ? OR t1.upc LIKE ? OR t2.comment LIKE ?" sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike) if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil { sql += ` OR t1.id = ? OR t2.id = ? OR (SELECT COUNT(*) FROM thing_map tm WHERE tm.vendor_org_code = sm.vendor_org_code AND tm.thing_type = ? AND tm.thing_id = t2.id AND tm.deleted_at = ? AND tm.vendor_thing_id = ?) > 0 ` sqlParams = append(sqlParams, keywordInt64, keywordInt64, model.ThingTypeSku, utils.DefaultTimeValue, keywordInt64) if isFocus { sql += " OR t4.ebai_id = ? OR t4.mtwm_id = ?" sqlParams = append(sqlParams, keywordInt64, keywordInt64) } } sql += ")" } if params["nameID"] != nil { sql += " AND t1.id = ?" sqlParams = append(sqlParams, params["nameID"].(int)) } if params["nameIDs"] != nil { var nameIDs []int if err = utils.UnmarshalUseNumber([]byte(params["nameIDs"].(string)), &nameIDs); err != nil { return "", nil, err } if len(nameIDs) > 0 { sql += " AND t1.id IN (" + dao.GenQuestionMarks(len(nameIDs)) + ")" sqlParams = append(sqlParams, nameIDs) } } if params["categoryID"] != nil { cat := &model.SkuCategory{} cat.ID = params["categoryID"].(int) if err = dao.GetEntity(db, cat); err != nil { return "", nil, err } sql += " AND (t1.category_id = ?" sqlParams = append(sqlParams, cat.ID) if cat.Level == 1 { sql += " OR t1.category_id IN (SELECT id FROM sku_category WHERE parent_id = ?)" sqlParams = append(sqlParams, cat.ID) } sql += ")" } if params["name"] != nil { sql += " AND t1.name LIKE ?" sqlParams = append(sqlParams, "%"+params["name"].(string)+"%") } if params["prefix"] != nil { sql += " AND t1.prefix LIKE ?" sqlParams = append(sqlParams, "%"+params["prefix"].(string)+"%") } if params["unit"] != nil { sql += " AND t1.unit = ?" sqlParams = append(sqlParams, params["unit"].(string)) } if len(storeIDs) > 0 { sql += " AND t3.id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")" sqlParams = append(sqlParams, storeIDs) if len(storeIDs) == 1 { sql += " AND IF(INSTR(t3.name,'" + model.ExdStoreName + "') > 0, t2.exd_sku_id <> '', t2.exd_sku_id = '')" } } if len(upcs) > 0 { sql += " AND t1.upc IN (" + dao.GenQuestionMarks(len(upcs)) + ")" sqlParams = append(sqlParams, upcs) } if len(skuIDs) > 0 { sql += " AND t2.id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")" sqlParams = append(sqlParams, skuIDs) } else if params["skuID"] != nil { skuID, ok := params["skuID"].(int) if ok { skuIDs = append(skuIDs, skuID) sql += " AND t2.id = ?" sqlParams = append(sqlParams, skuID) } } if lockTimeStr, ok := params["lockTime"].(string); ok && lockTimeStr != "" { if timeList, err2 := jxutils.BatchStr2Time(lockTimeStr); err2 == nil { sql += " AND (t4.jd_lock_time > ? OR t4.mtwm_lock_time > ? OR t4.ebai_lock_time > ? OR t4.jx_lock_time > ?)" sqlParams = append(sqlParams, timeList[0], timeList[0], timeList[0], timeList[0]) } } if isFocus { if params["fromStatus"] != nil { fromStatus := params["fromStatus"].(int) toStatus := fromStatus if params["toStatus"] != nil { toStatus = params["toStatus"].(int) } sql += " AND t4.status >= ? AND t4.status <= ?" sqlParams = append(sqlParams, fromStatus, toStatus) } if params["jdSyncStatus"] != nil || params["ebaiSyncStatus"] != nil || params["mtwmSyncStatus"] != nil { realVendorMap, err2 := getValidStoreVendorMap(db, storeIDs) if err = err2; err != nil { return "", nil, err } sql += " AND ( 1 = 0" if params["jdSyncStatus"] != nil && realVendorMap[model.VendorIDJD] == 1 { sql += " OR (t4.jd_sync_status & ? <> 0 AND t1.status = ? AND t2.status = ?)" sqlParams = append(sqlParams, params["jdSyncStatus"], model.SkuStatusNormal, model.SkuStatusNormal) } if params["ebaiSyncStatus"] != nil && realVendorMap[model.VendorIDEBAI] == 1 { sql += " OR (t4.ebai_sync_status & ? <> 0 AND NOT (t4.ebai_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )" sqlParams = append(sqlParams, params["ebaiSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal) } if params["mtwmSyncStatus"] != nil && realVendorMap[model.VendorIDMTWM] == 1 { sql += " OR (t4.mtwm_sync_status & ? <> 0 AND NOT (t4.mtwm_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )" sqlParams = append(sqlParams, params["mtwmSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal) } sql += ")" } } if isFocus { /*前台传入的最大值和最小值设置*/ if params["highestPrice"] != "" && params["highestPrice"] != nil { //highestPrice := utils.Interface2Float64WithDefault(params["highestPrice"], 0) * 100 sql += " AND t4.unit_price <= ? " sqlParams = append(sqlParams, params["highestPrice"]) } if params["minimumPrice"] != "" && params["minimumPrice"] != nil { //minimumPrice := utils.Interface2Float64WithDefault(params["minimumPrice"], 0) * 100 sql += " AND t4.unit_price >= ? " sqlParams = append(sqlParams, params["minimumPrice"]) } } return sql, sqlParams, err } func GetStoresSkus(ctx *jxcontext.Context, storeIDs, skuIDs []int, upcs []string, isFocus, isHighPrice bool, priceType int, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) { //权限 if permission.IsRoled(ctx) { if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil { var storeIDs2 []int if len(storeIDs) > 0 { for _, v := range storeIDs { if storeIDsMap[v] != 0 { storeIDs2 = append(storeIDs2, v) } } if len(storeIDs2) == 0 { storeIDs2 = append(storeIDs2, -1) } } else { for k, _ := range storeIDsMap { storeIDs2 = append(storeIDs2, k) } } storeIDs = nil storeIDs = storeIDs2 } } return GetStoresSkusNew(ctx, storeIDs, skuIDs, upcs, isFocus, isHighPrice, priceType, keyword, isBySku, isAct, params, offset, pageSize) } func GetStoresSkusNew(ctx *jxcontext.Context, storeIDs, skuIDs []int, upcs []string, isFocus, isHighPrice bool, priceType int, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) { if !isFocus && !isBySku && (len(storeIDs) > 1 || len(storeIDs) == 0) { return nil, fmt.Errorf("未关注按SkuName只能查询单店") } if len(storeIDs) == 0 && len(skuIDs) == 0 && pageSize == -1 { return nil, fmt.Errorf("GetStoresSkus必须指定storeIDs或skuIDs或分页") } actVendorID := -1 if params["actVendorID"] != nil { actVendorID = int(utils.Interface2Int64WithDefault(params["actVendorID"], -1)) } var highestPrice, minimumPrice float64 if params["highestPrice"] != nil { if highestPrice, err = strconv.ParseFloat(params["highestPrice"].(string), 64); err != nil { delete(params, "highestPrice") } else { params["highestPrice"] = highestPrice * 100 } } if params["minimumPrice"] != nil { if minimumPrice, err = strconv.ParseFloat(params["minimumPrice"].(string), 64); err != nil { delete(params, "minimumPrice") } else { params["minimumPrice"] = minimumPrice * 100 } } if !(highestPrice > 0 && highestPrice > minimumPrice) || !(highestPrice > 0) { delete(params, "highestPrice") } if !(minimumPrice >= 0 && highestPrice > 0 && highestPrice > minimumPrice) || !(minimumPrice >= 0) { delete(params, "minimumPrice") } db := dao.GetDB() sql, sqlParams, err := getGetStoresSkusBaseSQL(db, storeIDs, skuIDs, upcs, isFocus, keyword, isBySku, isAct, isHighPrice, priceType, actVendorID, params) if err != nil { return nil, err } pageSize = jxutils.FormalizePageSize(pageSize) offset = jxutils.FormalizePageOffset(offset) sqlOffset := offset sqlPageSize := pageSize isSaleInfo := params["stFromTime"] != nil if isSaleInfo { sqlOffset = 0 sqlPageSize = jxutils.FormalizePageSize(-1) } sqlParamsPage := []interface{}{sqlPageSize, sqlOffset} txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db, txDB) panic(r) } }() catOrderBy := "" if params["categoryID"] != nil { catOrderBy = "t2.seq, " } skuNamesInfo = &dao.StoreSkuNamesInfo{} if !isBySku && sqlPageSize != model.UnlimitedPageSize { sql2 := ` SELECT SQL_CALC_FOUND_ROWS t3.id store_id, t1.id name_id ` + sql + ` GROUP BY 1, 2 ORDER BY 1, 2 LIMIT ? OFFSET ? ` sqlParams2 := append([]interface{}{}, sqlParams...) sqlParams2 = append(sqlParams2, sqlParamsPage) var storeNameList []*tStoreNameBind beginTime := time.Now() if err = dao.GetRowsTx(txDB, &storeNameList, sql2, sqlParams2...); err != nil { dao.Rollback(db, txDB) return nil, err } globals.SugarLogger.Debugf("GetStoresSkusNew get result1:%v", time.Now().Sub(beginTime)) skuNamesInfo.TotalCount = dao.GetLastTotalRowCount2(db, txDB) sql += " AND (1 = 0" for _, v := range storeNameList { sql += " OR (t1.id = ? AND t3.id = ?)" sqlParams = append(sqlParams, v.NameID, v.StoreID) } sql += fmt.Sprintf(`) ORDER BY %s t3.id, t2.name_id, t2.id`, catOrderBy) } else { if isFocus { sql += fmt.Sprintf(` ORDER BY %s t3.id, t2.name_id, t2.id`, catOrderBy) } sql += ` LIMIT ? OFFSET ?` sqlParams = append(sqlParams, sqlParamsPage) } sql = ` SELECT SQL_CALC_FOUND_ROWS t3.id store_id, t3.name store_name, t3.pay_percentage, t1.*, t2.name_id, t2.id sku_id, t2.spec_quality sku_spec_quality, t2.spec_unit sku_spec_unit, t2.weight, t2m.vendor_thing_id sku_jd_id, t2.comment, t2.category_id sku_category_id, t2.status sku_status, t2.eclp_id, t4.created_at bind_created_at, t4.updated_at bind_updated_at, t4.last_operator bind_last_operator, t4.deleted_at bind_deleted_at, t4.sub_store_id, t4.price bind_price, IF(t4.unit_price IS NOT NULL, t4.unit_price, t1.price) unit_price, t4.status store_sku_status, t4.auto_sale_at, t4.ebai_id, t4.mtwm_id, t4.yb_id, CONCAT(smm.yb_store_prefix,t1.yb_name_suffix) yb_sku_name, t4.jds_id, t4.jds_ware_id, t4.jd_sync_status, t4.ebai_sync_status, t4.mtwm_sync_status, t4.yb_sync_status, t4.jds_sync_status, t4.jd_price, t4.ebai_price, t4.mtwm_price, t4.jx_price, t4.yb_price, t4.jds_price, t4.jd_lock_time, t4.ebai_lock_time, t4.mtwm_lock_time, t4.jx_lock_time, t4.yb_lock_time, t4.jds_lock_time, t4.status_sale_begin, t4.status_sale_end, t4.stock, t4.mt_ladder_box_price, t6.mid_unit_price real_mid_unit_price, t7.unit_price audit_unit_price ` + sql if isHighPrice || priceType != 0 { sql += " , t4.unit_price DESC LIMIT 99" } var tmpList []*tGetStoresSkusInfo beginTime := time.Now() if err = dao.GetRowsTx(txDB, &tmpList, sql, sqlParams...); err != nil { dao.Rollback(db, txDB) return nil, err } if isBySku { skuNamesInfo.TotalCount = dao.GetLastTotalRowCount2(db, txDB) } dao.Commit(db, txDB) globals.SugarLogger.Debugf("GetStoresSkusNew get result2:%v", time.Now().Sub(beginTime)) storeNameMap := make(map[int64]*dao.StoreSkuNameExt) for _, v := range tmpList { var storeName *dao.StoreSkuNameExt index := jxutils.Combine2Int(v.StoreID, v.ID) if isBySku || storeNameMap[index] == nil { storeName = &dao.StoreSkuNameExt{ StoreID: v.StoreID, StoreName: v.StoreName, SkuName: v.SkuName, UnitPrice: v.UnitPrice, PayPercentage: v.PayPercentage, RealMidUnitPrice: v.RealMidUnitPrice, YbSkuName: v.YbSkuName, AuditUnitPrice: v.AuditUnitPrice, } if !isBySku { storeNameMap[index] = storeName } skuNamesInfo.SkuNames = append(skuNamesInfo.SkuNames, storeName) } else { storeName = storeNameMap[index] } storeName.Skus = append(storeName.Skus, &v.StoreSkuExt) } if err == nil { if isFocus { if isSaleInfo { beginTime := time.Now() err = updateSaleInfo4StoreSkuName(ctx, db, storeIDs, skuIDs, params, skuNamesInfo, offset, pageSize) globals.SugarLogger.Debugf("GetStoresSkusNew updateSaleInfo4StoreSkuName:%v", time.Now().Sub(beginTime)) } if err == nil { if true { //!(offset == 0 && pageSize == model.UnlimitedPageSize) { storeIDs, skuIDs = GetStoreAndSkuIDsFromInfo(skuNamesInfo) } beginTime := time.Now() err = dao.UpdateActPrice4StoreSkuNameNew(db, storeIDs, skuIDs, skuNamesInfo, actVendorID) globals.SugarLogger.Debugf("GetStoresSkusNew updateActPrice4StoreSkuName:%v", time.Now().Sub(beginTime)) } } else { err = updateUnitPrice4StoreSkuNameNew(db, skuNamesInfo) } } // globals.SugarLogger.Debug(utils.Format4Output(skuNamesInfo, false)) return skuNamesInfo, err } func GetStoreAndSkuIDsFromInfo(skuNamesInfo *dao.StoreSkuNamesInfo) (storeIDs, skuIDs []int) { storeIDMap := make(map[int]int) skuIDMap := make(map[int]int) for _, skuName := range skuNamesInfo.SkuNames { storeIDMap[skuName.StoreID] = 1 for _, sku := range skuName.Skus { skuIDMap[sku.SkuID] = 1 } } return jxutils.IntMap2List(storeIDMap), jxutils.IntMap2List(skuIDMap) } // 根据已经部分关注的商品,得到已经存在的门店商品单价 func updateUnitPrice4StoreSkuNameNew(db *dao.DaoDB, skuNamesInfo *dao.StoreSkuNamesInfo) (err error) { storeIDMap := make(map[int]int) skuNameIDMap := make(map[int]int) for _, skuName := range skuNamesInfo.SkuNames { storeIDMap[skuName.StoreID] = 1 skuNameIDMap[skuName.SkuName.ID] = 1 } storeSkuNameInfo, err := dao.GetExistingStoreSkuNameInfo(db, jxutils.IntMap2List(storeIDMap), jxutils.IntMap2List(skuNameIDMap)) if err == nil { infoMap := make(map[int64]*dao.StoreSkuNameInfo) for _, v := range storeSkuNameInfo { infoMap[jxutils.Combine2Int(v.StoreID, v.NameID)] = v } for _, skuName := range skuNamesInfo.SkuNames { if tmpInfo := infoMap[jxutils.Combine2Int(skuName.StoreID, skuName.SkuName.ID)]; tmpInfo != nil { skuName.UnitPrice = int(tmpInfo.UnitPrice) } } } return err } func updateSaleInfo4StoreSkuName(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs, skuIDs []int, params map[string]interface{}, skuNamesInfo *dao.StoreSkuNamesInfo, offset, pageSize int) (err error) { var ( saleInfoList []*SkuSaleInfo timeList []time.Time fromCount, toCount int ) saleInfoMap := make(map[int64]*SkuSaleInfo) toTimeStr := "" if params["stToTime"] != nil { toTimeStr = params["stToTime"].(string) } if timeList, err = jxutils.BatchStr2Time(params["stFromTime"].(string), toTimeStr); err != nil { return err } if params["stFromCount"] != nil { fromCount = params["stFromCount"].(int) } toCount = math.MaxInt32 if params["stToCount"] != nil { toCount = params["stToCount"].(int) } // 不能用SQL筛除,否则不能区分是没有销量,还是不在条件中 if saleInfoList, err = GetStoresSkusSaleInfo(ctx, storeIDs, skuIDs, timeList[0], timeList[1], 0, math.MaxInt32); err != nil { return err } for _, saleInfo := range saleInfoList { saleInfoMap[jxutils.Combine2Int(saleInfo.StoreID, saleInfo.SkuID)] = saleInfo } var newSkuNames []*dao.StoreSkuNameExt for _, skuName := range skuNamesInfo.SkuNames { var newSkus []*dao.StoreSkuExt for _, sku := range skuName.Skus { saleInfo := saleInfoMap[jxutils.Combine2Int(skuName.StoreID, sku.SkuID)] if saleInfo == nil && fromCount == 0 { saleInfo = &SkuSaleInfo{} } if saleInfo != nil && saleInfo.Count >= fromCount && saleInfo.Count <= toCount { sku.Times = saleInfo.Times sku.Count = saleInfo.Count newSkus = append(newSkus, sku) } } if len(newSkus) > 0 { skuName.Skus = newSkus newSkuNames = append(newSkuNames, skuName) } } skuNamesInfo.TotalCount = len(newSkuNames) skuNamesInfo.SkuNames = nil if offset < skuNamesInfo.TotalCount { endIndex := offset + pageSize if endIndex > skuNamesInfo.TotalCount { endIndex = skuNamesInfo.TotalCount } skuNamesInfo.SkuNames = newSkuNames[offset:endIndex] } return err } func getValidStoreVendorMap(db *dao.DaoDB, storeIDs []int) (realVendorMap map[int]int, err error) { storeMapList, err := dao.GetStoresMapList(db, nil, storeIDs, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "") if err != nil { return nil, err } realVendorMap = make(map[int]int) for _, v := range storeMapList { if v.IsSync != 0 { realVendorMap[v.VendorID] = 1 } } return realVendorMap, nil } func GetStoreAbnormalSkuCount(ctx *jxcontext.Context, storeID, syncStatus int, isBySku bool, params map[string]interface{}) (count int, err error) { db := dao.GetDB() realVendorMap, err2 := getValidStoreVendorMap(db, []int{storeID}) if err = err2; err != nil { return 0, err } sql := ` SELECT COUNT(*) ct` if !isBySku { sql += ` FROM ( SELECT DISTINCT t3.id` } sql += ` FROM store_sku_bind t1 JOIN sku t2 ON t2.id = t1.sku_id AND t2.deleted_at = ? JOIN sku_name t3 ON t3.id = t2.name_id AND t3.deleted_at = ? WHERE t1.deleted_at = ? AND t1.store_id = ? AND ((t2.status = ? AND t3.status = ?) OR t1.status = ?) AND (1 = 0` sqlParams := []interface{}{ utils.DefaultTimeValue, utils.DefaultTimeValue, utils.DefaultTimeValue, storeID, model.SkuStatusNormal, model.SkuStatusNormal, model.SkuStatusNormal, } for _, vendorID := range []int{model.VendorIDJD, model.VendorIDEBAI, model.VendorIDMTWM} { if realVendorMap[vendorID] != 0 { prefix := dao.ConvertDBFieldPrefix(model.VendorNames[vendorID]) sql += fmt.Sprintf(" OR (t1.%s_sync_status & ? <> 0 AND t1.%s_sync_status & ? = 0", prefix, prefix) sqlParams = append(sqlParams, syncStatus, model.SyncFlagDeletedMask|model.SyncFlagNewMask) if model.MultiStoresVendorMap[vendorID] == 1 { sql += fmt.Sprintf(" AND t2.%s_id <> 0 AND t2.status = ? AND t3.status = ?", prefix) sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal) } sql += ")" } } sql += ")" if params["fromStatus"] != nil { fromStatus := params["fromStatus"].(int) toStatus := fromStatus if params["toStatus"] != nil { toStatus = params["toStatus"].(int) } sql += " AND t1.status >= ? AND t1.status <= ?" sqlParams = append(sqlParams, fromStatus, toStatus) } if !isBySku { sql += ` ) t1` } err = dao.GetRow(db, &count, sql, sqlParams...) return count, err } func GetStoresSkusSaleInfo(ctx *jxcontext.Context, storeIDs []int, skuIDs []int, fromTime, toTime time.Time, fromCount, toCount int) (saleInfoList []*SkuSaleInfo, err error) { globals.SugarLogger.Debugf("GetStoresSkusSaleInfo storeIDs:%v, fromTime:%v, toTime:%v, fromCount:%d, toCount:%d", storeIDs, fromTime, toTime, fromCount, toCount) db := dao.GetDB() sql := ` SELECT IF(t2.jx_store_id <> 0, jx_store_id, store_id) store_id, t1.sku_id, COUNT(*) times, SUM(count) count FROM order_sku t1 JOIN goods_order t2 ON t1.vendor_order_id = t2.vendor_order_id AND t1.vendor_id = t2.vendor_id AND t2.status = ? WHERE t1.order_created_at >= ? AND t1.order_created_at <= ? ` if utils.IsTimeZero(toTime) { toTime = time.Now() } sqlParams := []interface{}{ model.OrderStatusFinished, fromTime, toTime, } if len(storeIDs) > 0 { sql += ` AND IF(t2.jx_store_id <> 0, jx_store_id, store_id) IN (` + dao.GenQuestionMarks(len(storeIDs)) + `) ` sqlParams = append(sqlParams, storeIDs) } if len(skuIDs) > 0 { sql += ` AND IF(t1.jx_sku_id <> 0, t1.jx_sku_id, t1.sku_id) IN (` + dao.GenQuestionMarks(len(skuIDs)) + `)` sqlParams = append(sqlParams, skuIDs) } sql += ` GROUP BY 1,2 HAVING count >= ? AND count <= ? ` sqlParams = append(sqlParams, fromCount, toCount) if err = dao.GetRows(db, &saleInfoList, sql, sqlParams...); err == nil { // globals.SugarLogger.Debug(utils.Format4Output(saleInfoList, false)) return saleInfoList, nil } return nil, err } type GetStoresSkusSaleInfoNewResult struct { VendorID int `orm:"column(vendor_id)" json:"vendorID"` StoreID int `orm:"column(store_id)" json:"storeID"` StoreName string `json:"storeName"` SkuID int `orm:"column(sku_id)" json:"skuID"` SkuName string `json:"skuName"` SaleCount int `json:"saleCount"` //销量 } func GetStoresSkusSaleInfoNew(ctx *jxcontext.Context, vendorIDs, storeIDs, skuIDs, skuNameIDs []int, fromTime, toTime string, saleCountBegin, saleCountEnd, sortType int, keyword string, offset, pageSize int) (pageInfo *model.PagedInfo, err error) { var ( db = dao.GetDB() list []*GetStoresSkusSaleInfoNewResult ) if len(storeIDs) == 0 && len(skuIDs) == 0 && keyword == "" { return nil, fmt.Errorf("请至少输入一个条件查询!") } if fromTime == "" && toTime == "" { return nil, fmt.Errorf("必须选择一段时间!") } sql := ` SELECT SQL_CALC_FOUND_ROWS t1.* FROM ( SELECT d.id store_id, d.name store_name, c.id sku_id, e.name sku_name, a.vendor_id, SUM(a.count) sale_count FROM order_sku a LEFT JOIN goods_order b ON a.vendor_id = b.vendor_id AND a.vendor_order_id = b.vendor_order_id LEFT JOIN sku c ON IF(a.jx_sku_id = 0, a.sku_id, a.jx_sku_id) = c.id LEFT JOIN sku_name e ON e.id = c.name_id LEFT JOIN store d ON IF(b.jx_store_id = 0, b.store_id, b.jx_store_id) = d.id WHERE a.order_created_at > ? AND a.order_created_at < ? ` sqlParams := []interface{}{utils.Str2Time(fromTime), utils.Str2Time(toTime)} if len(vendorIDs) > 0 { sql += " AND a.vendor_id IN (" + dao.GenQuestionMarks(len(vendorIDs)) + ")" sqlParams = append(sqlParams, vendorIDs) } if len(storeIDs) > 0 { sql += " AND d.id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")" sqlParams = append(sqlParams, storeIDs) } if len(skuIDs) > 0 { sql += " AND c.id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")" sqlParams = append(sqlParams, skuIDs) } if len(skuNameIDs) > 0 { sql += " AND e.id IN (" + dao.GenQuestionMarks(len(skuNameIDs)) + ")" sqlParams = append(sqlParams, skuNameIDs) } if keyword != "" { sql += " AND (e.name LIKE ? OR d.name LIKE ? OR d.id = ? OR a.vendor_order_id = ?)" sqlParams = append(sqlParams, "%"+keyword+"%", "%"+keyword+"%", keyword, keyword) } sql += " GROUP BY 1, 2, 3, 4, 5)t1 WHERE 1 = 1" if saleCountBegin != 0 { sql += " AND t1.sale_count >= ?" sqlParams = append(sqlParams, saleCountBegin) } if saleCountEnd != 0 { sql += " AND t1.sale_count <= ?" sqlParams = append(sqlParams, saleCountEnd) } if sortType != 0 { if sortType > 0 { sql += " ORDER BY t1.sale_count" } else { sql += " ORDER BY t1.sale_count DESC" } } sql += " LIMIT ? OFFSET ?" pageSize = jxutils.FormalizePageSize(pageSize) offset = jxutils.FormalizePageOffset(offset) sqlParams = append(sqlParams, pageSize, offset) txDB, _ := dao.Begin(db) defer dao.Commit(db, txDB) if err = dao.GetRowsTx(txDB, &list, sql, sqlParams...); err == nil { pageInfo = &model.PagedInfo{ TotalCount: dao.GetLastTotalRowCount2(db, txDB), Data: list, } } return pageInfo, err } func asyncStoreSkuOpFilter(ctx *jxcontext.Context, isAsync bool) bool { if !isAsync { authType := ctx.GetLoginInfo().GetAuthType() if authType == weixin.AuthTypeMini || authType == weixin.AuthTypeMP { userMobile, _ := ctx.GetMobileAndUserID() isAsync = asyncOpMobileMap[userMobile] == 0 } } return isAsync } func UpdateStoreSku(ctx *jxcontext.Context, causeFlag, storeID int, skuBindInfo *StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) { return UpdateStoreSkus(ctx, causeFlag, storeID, []*StoreSkuBindInfo{skuBindInfo}, isAsync, isContinueWhenError) } func UpdateStoreSkus(ctx *jxcontext.Context, causeFlag, storeID int, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) { return UpdateStoresSkus(ctx, causeFlag, []int{storeID}, skuBindInfos, false, false, isAsync, isContinueWhenError) } func UpdateStoresSkus(ctx *jxcontext.Context, causeFlag int, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isScale, isRefreshHigh, isAsync, isContinueWhenError bool) (hint string, err error) { globals.SugarLogger.Debugf("UpdateStoresSkus:%s, storeIDs:%v, skuBindInfos:%s", ctx.GetTrackInfo(), storeIDs, utils.Format4Output(skuBindInfos, true)) if beego.BConfig.RunMode == "jxgy" { doStoreSkuAuditForGy(ctx, storeIDs, skuBindInfos) var num int64 db := dao.GetDB() skuIDs, err := updateStoresSkusWithoutSync(ctx, db, storeIDs, skuBindInfos, isScale, isRefreshHigh) if err != nil { return "", err } isAsync = asyncStoreSkuOpFilter(ctx, isAsync) num = int64(len(skuIDs)) if num > 0 { hint, err = CurVendorSync.SyncStoresSkus(ctx, nil, causeFlag, db, nil, storeIDs, skuIDs, false, isAsync, isContinueWhenError) } if num == 0 || !isAsync || hint == "" { hint = utils.Int64ToStr(num) } } else { flag, _ := doStoreSkuAudit(ctx, storeIDs, skuBindInfos) if !flag { var num int64 db := dao.GetDB() skuIDs, err := updateStoresSkusWithoutSync(ctx, db, storeIDs, skuBindInfos, isScale, isRefreshHigh) if err != nil { return "", err } isAsync = asyncStoreSkuOpFilter(ctx, isAsync) num = int64(len(skuIDs)) if num > 0 { hint, err = CurVendorSync.SyncStoresSkus(ctx, nil, causeFlag, db, nil, storeIDs, skuIDs, false, isAsync, isContinueWhenError) } if num == 0 || !isAsync || hint == "" { hint = utils.Int64ToStr(num) } } } return hint, err } func UpdateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isRefreshHigh bool) (err error) { db := dao.GetDB() if len(storeIDs) == 0 { stores, _ := dao.GetStoreList(db, nil, nil, nil, nil, nil, "") for _, v := range stores { storeIDs = append(storeIDs, v.ID) } } updateStoresSkusWithoutSync(ctx, db, storeIDs, skuBindInfos, false, isRefreshHigh) return err } func UpdateStoresSkusByBind(ctx *jxcontext.Context, parentTask tasksch.ITask, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError, isFos bool) (hint string, err error) { // if len(skuBindInfos) > maxStoreNameBind { // return "", fmt.Errorf("门店商品信息大于%d", maxStoreNameBind) // } skuBindInfosMap := make(map[int][]*StoreSkuBindInfo) storeIDMap := make(map[int]int) for _, v := range skuBindInfos { if v.StoreID > 0 { skuBindInfosMap[v.StoreID] = append(skuBindInfosMap[v.StoreID], v) storeIDMap[v.StoreID] = 1 } } storeIDs := jxutils.IntMap2List(storeIDMap) sort.Ints(storeIDs) var num int64 skuIDMap := make(map[int]int) db := dao.GetDB() // txDB , _ := dao.Begin(db) // defer func() { // if r := recover(); r != nil { // dao.Rollback(db, txDB) // panic(r) // } // }() for _, storeID := range storeIDs { skuIDs, err2 := updateStoresSkusWithoutSync(ctx, db, []int{storeID}, skuBindInfosMap[storeID], false, false) if err = err2; err != nil { // dao.Rollback(db, txDB) return "", err } for _, v := range skuIDs { skuIDMap[v] = 1 } num += int64(len(skuIDs)) } // dao.Commit(db, txDB) isAsync = asyncStoreSkuOpFilter(ctx, isAsync) if num > 0 { skuIDs := jxutils.IntMap2List(skuIDMap) hint, err = CurVendorSync.SyncStoresSkus(ctx, parentTask, 0, db, nil, storeIDs, skuIDs, isFos, isAsync, isContinueWhenError) } if num == 0 || !isAsync || hint == "" { hint = utils.Int64ToStr(num) } return hint, err } func checkStoresSkusSaleCity(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (err error) { sql := ` SELECT t1.id store_id, t2.id name_id, t2.name FROM store t1 JOIN sku_name t2 ON t2.is_global = 0 AND t2.deleted_at = ? LEFT JOIN sku_name_place_bind t3 ON t2.id = t3.name_id AND t1.city_code = t3.place_code WHERE t3.id IS NULL ` sql += " AND t1.id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")" nameIDs := make([]int, 0) for _, v := range skuBindInfos { if v.IsFocus == 1 { nameIDs = append(nameIDs, v.NameID) } else { for _, v2 := range v.Skus { if v2.IsSale == 1 { nameIDs = append(nameIDs, v.NameID) break } } } } if len(nameIDs) == 0 { return nil } sql += " AND t2.id IN (" + dao.GenQuestionMarks(len(nameIDs)) + ")" var invalidList []*tStoreNameBind if err = dao.GetRows(db, &invalidList, sql, utils.DefaultTimeValue, storeIDs, nameIDs); err == nil { if len(invalidList) > 0 { errMsg := "" for _, v := range invalidList { errMsg += fmt.Sprintf("门店:%d,Name:%d(%s)销售区域错误!\n", v.StoreID, v.NameID, v.Name) } err = errors.New(errMsg) } } return err } func uniqueStoreIDs(storeIDs []int) []int { storeIDMap := make(map[int]int) for _, v := range storeIDs { storeIDMap[v] = 1 } return jxutils.IntMap2List(storeIDMap) } func uniqueStoreNameBind(skuBindInfos []*StoreSkuBindInfo) (outSkuBindInfos []*StoreSkuBindInfo) { nameIDMap := make(map[int]int) upcMap := make(map[string]int) for _, v := range skuBindInfos { if v.NameID != 0 { if nameIDMap[v.NameID] != 1 { outSkuBindInfos = append(outSkuBindInfos, v) nameIDMap[v.NameID] = 1 } } else if v.UPC != "" { if upcMap[v.UPC] != 1 { outSkuBindInfos = append(outSkuBindInfos, v) upcMap[v.UPC] = 1 } } } return outSkuBindInfos } func updateStoresSkusWithoutSync(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isScale, isRefreshHigh bool) (needSyncSkus []int, err error) { // if len(storeIDs)*len(skuBindInfos) > maxStoreNameBind2 { // return nil, fmt.Errorf("门店商品信息大于%d", maxStoreNameBind2) // } storeIDs = uniqueStoreIDs(storeIDs) skuBindInfos = uniqueStoreNameBind(skuBindInfos) sort.Ints(storeIDs) // globals.SugarLogger.Debugf("updateStoresSkusWithoutSync, storeIDs:%v, skuBindInfos:%s", storeIDs, utils.Format4Output(skuBindInfos, false)) if db == nil { db = dao.GetDB() } // if err = checkStoresSkusSaleCity(ctx, db, storeIDs, skuBindInfos); err != nil { // return nil, err // } isUserCanDirectChangePrice := true if user := ctx.GetFullUser(); user != nil { isUserCanDirectChangePrice = user.Type&model.UserTypeOperator != 0 } userName := ctx.GetUserName() needSyncIDMap := make(map[int]int) isSyncSkus := false var appCodeList []string txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db, txDB) panic(r) } }() for _, storeID := range storeIDs { // todo 可以考虑在需要更新价格再获取 storeDetail, err := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDJX, "") if err != nil || storeDetail == nil { continue } if beego.BConfig.RunMode == "prod" || beego.BConfig.RunMode == "beta" { if len(storeIDs) > 1 { if storeDetail.StoreLevel == "D" || storeDetail.StoreLevel == "E" { continue } } } scaleFactor := float64(1) if isScale { scaleFactor = 100 / float64(jxutils.ConstrainPayPercentage(storeDetail.PayPercentage)) } for _, skuBindInfo := range skuBindInfos { // 关注且没有给价时,需要尝试从store_sku_bind中得到已有的单价 needGetExistingUnitPrice := skuBindInfo.UnitPrice == 0 && skuBindInfo.IsFocus == 1 inSkuBinds := skuBindInfo.Skus var allBinds []*tStoreSkuBindAndSpec sql := ` SELECT t2.*, t1.id real_sku_id, t1.status sku_status, t1.spec_quality, t1.spec_unit, t1.exd_sku_id,` if needGetExistingUnitPrice { sql += " IF(t5.unit_price > 0, t5.unit_price, t3.price) sku_name_price," } sql += ` t3.unit sku_name_unit, t3.name, t3.status sku_name_status, t3.id name_id, ts.change_price_type, ts.name store_name FROM sku t1 JOIN store ts ON ts.id = ? AND ts.deleted_at = ? LEFT JOIN store_sku_bind t2 ON t2.sku_id = t1.id AND t2.store_id = ts.id AND t2.deleted_at = ? JOIN sku_name t3 ON t1.name_id = t3.id AND t3.deleted_at = ?` sqlParams := []interface{}{ storeID, utils.DefaultTimeValue, utils.DefaultTimeValue, utils.DefaultTimeValue, } if needGetExistingUnitPrice { sql += ` LEFT JOIN ( SELECT t7.store_id, t8.name_id, CAST(AVG(t7.unit_price) AS SIGNED) unit_price FROM store_sku_bind t7 JOIN sku t8 ON t8.id = t7.sku_id ` if skuBindInfo.NameID != 0 { sql += " AND t8.name_id = ?" sqlParams = append(sqlParams, skuBindInfo.NameID) } else if skuBindInfo.UPC != "" { sql += " JOIN sku_name t9 ON t9.id = t8.name_id AND t9.upc = ?" sqlParams = append(sqlParams, skuBindInfo.UPC) } sql += ` WHERE t7.deleted_at = ? AND t7.store_id = ? GROUP BY 1,2 ) t5 ON t5.store_id = ts.id AND t5.name_id = t1.name_id` sqlParams = append(sqlParams, utils.DefaultTimeValue, storeID) } sql += " WHERE 1 = 1" if skuBindInfo.NameID != 0 { sql += " AND t1.name_id = ?" sqlParams = append(sqlParams, skuBindInfo.NameID) } else if skuBindInfo.UPC != "" { sql += " AND t3.upc = ?" sqlParams = append(sqlParams, skuBindInfo.UPC) } sql += ` AND t1.deleted_at = ? FOR UPDATE` sqlParams = append(sqlParams, utils.DefaultTimeValue) // globals.SugarLogger.Debug(sql) if err = dao.GetRows(db, &allBinds, sql, sqlParams...); err == nil { if len(allBinds) > 0 { globals.SugarLogger.Debug(utils.Format4Output(allBinds, true)) inSkuBinsMap := make(map[int]*StoreSkuBindSkuInfo, len(inSkuBinds)) for _, v := range inSkuBinds { inSkuBinsMap[v.SkuID] = v } unitPrice := 0 if skuBindInfo.UnitPrice != 0 { if skuBindInfo.UnitPrice > MaxSkuUnitPrice { dao.Rollback(db, txDB) return nil, fmt.Errorf("商品:%s价格:%s太夸张", allBinds[0].Name, jxutils.IntPrice2StandardCurrencyString(int64(skuBindInfo.UnitPrice))) } unitPrice = skuBindInfo.UnitPrice if isRefreshHigh { if allBinds[0].UnitPrice <= unitPrice { continue } } } else { unitPrice = allBinds[0].UnitPrice if unitPrice == 0 { unitPrice = allBinds[0].SkuNamePrice } } unitPrice = int(float64(unitPrice) * scaleFactor) for _, v := range allBinds { var num int64 inSkuBind := inSkuBinsMap[v.RealSkuID] isCanChangePrice := (isUserCanDirectChangePrice || jxutils.TranslateStorePriceType(v.ChangePriceType) != model.StoreChangePriceTypeBossDisabled) var skuBind *model.StoreSkuBind if v.ID == 0 { // if v.ExdSkuID == "" { if (strings.Contains(v.StoreName, model.ExdStoreName) && v.ExdSkuID != "") || (!strings.Contains(v.StoreName, model.ExdStoreName) && v.ExdSkuID == "") { if skuBindInfo.IsFocus == 1 && v.SkuNameStatus == model.SkuStatusNormal && v.SkuStatus == model.SkuStatusNormal && isCanChangePrice { skuBind = &model.StoreSkuBind{ StoreID: storeID, SkuID: v.RealSkuID, SubStoreID: skuBindInfo.SubStoreID, // todo 这个应该从用户信息中自动获得 UnitPrice: unitPrice, Price: jxutils.CaculateSkuPrice(unitPrice, v.SpecQuality, v.SpecUnit, v.SkuNameUnit), Status: model.StoreSkuBindStatusDontSale, // 缺省不可售? } skuBind.JxPrice = jxutils.CaculatePriceByPricePack(storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage), skuBind.Price) if tmpStatus := getSkuSaleStatus(inSkuBind, skuBindInfo); tmpStatus != model.StoreSkuBindStatusNA { skuBind.Status = tmpStatus if inSkuBind != nil { if inSkuBind.Stock != nil { skuBind.Stock = *inSkuBind.Stock } else { if tmpStatus == model.StoreSkuBindStatusNormal { skuBind.Stock = model.MaxStoreSkuStockQty } else { skuBind.Stock = 0 } } } else { if tmpStatus == model.StoreSkuBindStatusNormal { skuBind.Stock = model.MaxStoreSkuStockQty } else { skuBind.Stock = 0 } } } if globals.IsAddEvent { err = AddEventDetail(db, ctx, model.OperateAdd, v.RealSkuID, model.ThingTypeSku, storeID, "", utils.Int2Str(skuBind.UnitPrice)) } setStoreSkuBindStatus(skuBind, model.SyncFlagNewMask) dao.WrapAddIDCULDEntity(skuBind, userName) if deletedSku := dao.GetDeletedStoreSkuBind(db, skuBind.StoreID, skuBind.SkuID); deletedSku == nil { if err = dao.CreateEntity(db, skuBind); err != nil { dao.Rollback(db, txDB) return nil, err } num = 1 } else { // 需要处理,在删除某个门店商品,同步失败的情况下,又把商品重新关注。 // 所以统一处理成恢复删除的记录,这样避免问题 skuBind.ID = deletedSku.ID // vendorSkuID的赋值意义不大 skuBind.MtwmID = deletedSku.MtwmID skuBind.EbaiID = deletedSku.EbaiID skuBind.JdsID = deletedSku.JdsID skuBind.JdsWareID = deletedSku.JdsWareID if num, err = dao.UpdateEntity(db, skuBind); err != nil { dao.Rollback(db, txDB) return nil, err } } //下面这段很难受 { //可售了的才整 if skuBind.Status == model.StoreSkuBindStatusNormal { //如果是京东关注,要去建商品 list1, _ := dao.GetStoresMapList(db, []int{model.VendorIDJD}, []int{storeID}, nil, model.StoreStatusAll, 1, "", "", "") //表示这个门店绑定了京东 if len(list1) > 0 { vendorOrgCodes, _ := dao.GetVendorOrgCode(db, model.VendorIDJD, list1[0].VendorOrgCode, model.VendorOrgTypePlatform) //thingmap里肯定存在,再判断有没有同步上去 thingMaps, _ := dao.GetThingMapList(db, model.ThingTypeSku, []int{model.VendorIDJD}, []int{v.RealSkuID}, []string{list1[0].VendorOrgCode}) if len(thingMaps) > 0 { //如果平台ID为空(未创建到京东) if thingMaps[0].VendorThingID == "" { isSyncSkus = true appCodeList = append(appCodeList, list1[0].VendorOrgCode) //并且同步标志还没有带待创建(因为addskuname现在建到thingmap上不会带待创建标志了) if !model.IsSyncStatusNew(thingMaps[0].SyncStatus) { OnCreateThing(ctx, db, vendorOrgCodes, int64(v.RealSkuID), model.ThingTypeSku, model.SyncFlagNewMask, false) } } } else { //万一不存在 isSyncSkus = true appCodeList = append(appCodeList, list1[0].VendorOrgCode) OnCreateThing(ctx, db, vendorOrgCodes, int64(v.RealSkuID), model.ThingTypeSku, model.SyncFlagNewMask, false) } } } } } } } else { beforeMsg := *v skuBind = &v.StoreSkuBind if skuBindInfo.IsFocus == -1 && isCanChangePrice { if globals.IsAddEvent { err = AddEventDetail(db, ctx, model.OperateDelete, skuBind.SkuID, model.ThingTypeSku, storeID, "", "") } if globals.IsStoreSkuAct { dao.DeleteEntity(db, &model.StoreSkuAct{ StoreID: skuBind.StoreID, SkuID: skuBind.SkuID, }, "StoreID", "SkuID") } if num, err = dao.DeleteEntityLogically(db, skuBind, map[string]interface{}{ model.FieldStatus: model.StoreSkuBindStatusDeleted, model.FieldJdSyncStatus: model.SyncFlagDeletedMask, model.FieldEbaiSyncStatus: model.SyncFlagDeletedMask, model.FieldMtwmSyncStatus: model.SyncFlagDeletedMask, model.FieldYbSyncStatus: model.SyncFlagDeletedMask, model.FieldJdsSyncStatus: model.SyncFlagDeletedMask, }, userName, nil); err != nil { dao.Rollback(db, txDB) return nil, err } //删除待审核商品信息 storeAudits, err := dao.GetStoreSkuAuditLight(db, []int{storeID}, []int{v.NameID}, model.StoreAuditStatusOnline) if len(storeAudits) > 0 { storeAudits[0].DeletedAt = time.Now() if _, err = dao.UpdateEntity(db, storeAudits[0], "DeletedAt"); err != nil { dao.Rollback(db, txDB) return nil, err } } } else { // 用了SELECT FOR UPDATE后,只更新修改字段是没有必要的,暂时保留 updateFieldMap := make(map[string]interface{}) if skuBindInfo.IsFocus == 1 { // 关注之后再关注不操作 // skuBind.Status = model.StoreSkuBindStatusDontSale // 缺省不可售? // skuBind.DeletedAt = utils.DefaultTimeValue // skuBind.UnitPrice = unitPrice // skuBind.Price = jxutils.CaculateSkuPrice(unitPrice, v.SpecQuality, v.SpecUnit, v.SkuNameUnit) // setStoreSkuBindStatus(skuBind, model.SyncFlagPriceMask|model.SyncFlagSaleMask) // updateFieldMap[model.FieldStatus] = 1 // updateFieldMap[model.FieldDeletedAt] = 1 // updateFieldMap["UnitPrice"] = 1 // updateFieldMap["Price"] = 1 } if tmpStatus := getSkuSaleStatus(inSkuBind, skuBindInfo); tmpStatus != model.StoreSkuBindStatusNA { if tmpStatus != skuBind.Status { updateFieldMap[model.FieldStatus] = 1 } skuBind.Status = tmpStatus setStoreSkuBindStatus(skuBind, model.SyncFlagSaleMask) setStoreSkuBindStatus(skuBind, model.SyncFlagStockMask) if tmpStatus == model.StoreSkuBindStatusNormal { skuBind.Stock = model.MaxStoreSkuStockQty //下面这段很难受 { //如果是京东关注,要去建商品 list1, _ := dao.GetStoresMapList(db, []int{model.VendorIDJD}, []int{storeID}, nil, model.StoreStatusAll, 1, "", "", "") //表示这个门店绑定了京东 if len(list1) > 0 { vendorOrgCodes, _ := dao.GetVendorOrgCode(db, model.VendorIDJD, list1[0].VendorOrgCode, model.VendorOrgTypePlatform) //thingmap里肯定存在,再判断有没有同步上去 thingMaps, _ := dao.GetThingMapList(db, model.ThingTypeSku, []int{model.VendorIDJD}, []int{v.RealSkuID}, []string{list1[0].VendorOrgCode}) if len(thingMaps) > 0 { //如果平台ID为空(未创建到京东) if thingMaps[0].VendorThingID == "" { isSyncSkus = true appCodeList = append(appCodeList, list1[0].VendorOrgCode) //并且同步标志还没有带待创建(因为addskuname现在建到thingmap上不会带待创建标志了) if !model.IsSyncStatusNew(thingMaps[0].SyncStatus) { OnCreateThing(ctx, db, vendorOrgCodes, int64(v.RealSkuID), model.ThingTypeSku, model.SyncFlagNewMask, false) } } } else { //万一不存在 isSyncSkus = true appCodeList = append(appCodeList, list1[0].VendorOrgCode) OnCreateThing(ctx, db, vendorOrgCodes, int64(v.RealSkuID), model.ThingTypeSku, model.SyncFlagNewMask, false) } } } } else { skuBind.Stock = 0 } } if inSkuBind != nil { if inSkuBind.Stock != nil { updateFieldMap["Stock"] = 1 skuBind.Stock = *inSkuBind.Stock setStoreSkuBindStatus(skuBind, model.SyncFlagStockMask) } if inSkuBind.MtLadderBoxPrice != 0 { updateFieldMap["MtLadderBoxPrice"] = 1 skuBind.MtLadderBoxPrice = inSkuBind.MtLadderBoxPrice setStoreSkuBindStatus(skuBind, model.SyncFlagModifiedMask) } } if skuBindInfo.MtLadderBoxPrice != 0 { updateFieldMap["MtLadderBoxPrice"] = 1 skuBind.MtLadderBoxPrice = skuBindInfo.MtLadderBoxPrice setStoreSkuBindStatus(skuBind, model.SyncFlagModifiedMask) } if skuBindInfo.UnitPrice != 0 && isCanChangePrice { // 这里是否需要加此条件限制 price := jxutils.CaculateSkuPrice(unitPrice, v.SpecQuality, v.SpecUnit, v.SkuNameUnit) jxPrice := jxutils.CaculatePriceByPricePack(storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage), price) if unitPrice != skuBind.UnitPrice || price != skuBind.Price || jxPrice != skuBind.JxPrice { if price != skuBind.Price { setStoreSkuBindStatus(skuBind, model.SyncFlagPriceMask) updateFieldMap["Price"] = 1 } skuBind.UnitPrice = unitPrice skuBind.Price = price skuBind.JxPrice = jxPrice updateFieldMap["UnitPrice"] = 1 updateFieldMap["JxPrice"] = 1 //TODO 2020-09-08 如果改价时商品在做直降,要取消这个商品的直降,再通过改价比例修改活动价,再上这个直降 // checkActDirectDown(ctx, skuBind, beforeMsg) } } if skuBindInfo.StatusSaleBegin != 0 && skuBindInfo.StatusSaleEnd != 0 { if err := ValidateStruct(skuBindInfo); err != nil { globals.SugarLogger.Infof("更改商品:%s, 可售时间不合法!时间范围:[%v] 至 [%v]", allBinds[0].Name, skuBindInfo.StatusSaleBegin, skuBindInfo.StatusSaleEnd) } else { if skuBind.StatusSaleBegin != skuBindInfo.StatusSaleBegin || skuBind.StatusSaleEnd != skuBindInfo.StatusSaleEnd { updateFieldMap["StatusSaleBegin"] = 1 updateFieldMap["StatusSaleEnd"] = 1 } skuBind.StatusSaleBegin = skuBindInfo.StatusSaleBegin skuBind.StatusSaleEnd = skuBindInfo.StatusSaleEnd } } if skuBindInfo.StatusSaleBegin == 0 && skuBindInfo.StatusSaleEnd == 0 { if skuBind.StatusSaleBegin != skuBindInfo.StatusSaleBegin || skuBind.StatusSaleEnd != skuBindInfo.StatusSaleEnd { updateFieldMap["StatusSaleBegin"] = 1 updateFieldMap["StatusSaleEnd"] = 1 } skuBind.StatusSaleBegin = skuBindInfo.StatusSaleBegin skuBind.StatusSaleEnd = skuBindInfo.StatusSaleEnd } if globals.IsAddEvent { if len(updateFieldMap) > 0 { mapAfter := refutil.FindMapAndStructMixed(updateFieldMap, skuBind) mapBefore := refutil.FindMapAndStructMixed(updateFieldMap, beforeMsg) err = AddEventDetail(db, ctx, model.OperateUpdate, v.RealSkuID, model.ThingTypeSku, storeID, BuildDiffData(mapBefore), BuildDiffData(mapAfter)) } } if len(updateFieldMap) > 0 { updateFieldMap[model.FieldJdSyncStatus] = 1 updateFieldMap[model.FieldEbaiSyncStatus] = 1 updateFieldMap[model.FieldMtwmSyncStatus] = 1 updateFieldMap[model.FieldUpdatedAt] = 1 updateFieldMap[model.FieldLastOperator] = 1 dao.WrapUpdateULEntity(skuBind, userName) if skuBind.Status == model.SkuStatusNormal { updateFieldMap["AutoSaleAt"] = 1 skuBind.AutoSaleAt = utils.DefaultTimeValue } if num, err = dao.UpdateEntity(db, skuBind /*, utils.Map2KeySlice(updateFieldMap)...*/); err != nil { dao.Rollback(db, txDB) return nil, err } } } } if skuBind != nil && num == 1 { needSyncIDMap[skuBind.SkuID] = 1 } } } } else { dao.Rollback(db, txDB) return nil, err } } } dao.Commit(db, txDB) skuIDs := jxutils.IntMap2List(needSyncIDMap) if isSyncSkus { _, err = SyncSkus(ctx, nil, []int{model.VendorIDJD}, appCodeList, nil, skuIDs, true) } return skuIDs, err } // func checkActDirectDown(ctx *jxcontext.Context, skuBind *model.StoreSkuBind, beforeSkuBind tStoreSkuBindAndSpec) (err error) { // var ( // originPrice = beforeSkuBind.Price // price = skuBind.Price // db = dao.GetDB() // pageSize = 9999 // // percent = price / originPrice // ) // if paged, _ := dao.QueryActs(db, 0, 0, pageSize, -1, "", -1, []int{model.ActStatusCreated}, []int{model.ActSkuDirectDown}, nil, skuBind.StoreID, []int{skuBind.SkuID}, 0, utils.ZeroTimeValue, utils.ZeroTimeValue, time.Now().AddDate(0, -2, 0), time.Now()); len(paged.Data) > 0 { // for _, act := range paged.Data { // if _, actStoreSkus, err := dao.GetActStoreSkuVendorList(db, act.ID, nil, []int{skuBind.StoreID}, []int{skuBind.SkuID}, "", 0, pageSize); err == nil && len(actStoreSkus) > 0 { // var ( // handler = partner.GetPurchasePlatformFromVendorID(actStoreSkus[0].VendorID).(partner.IPurchasePlatformActHandler) // actStoreSkuParam []*ActStoreSkuParam // ) // for _, actStoreSku := range actStoreSkus { // aa := &ActStoreSkuParam{ // ActStoreSku: model.ActStoreSku{ // StoreID: skuBind.StoreID, // SkuID: actStoreSku.SkuID, // ActID: act.ID, // }, // } // actStoreSkuParam = append(actStoreSkuParam, aa) // } // DeleteActStoreSkuBind(ctx, db, act.ID, actStoreSkuParam) // actMap, err := dao.GetActVendorInfo(db, act.ID, []int{actStoreSkus[0].VendorID}) // if err != nil { // return err // } // actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, act.ID, nil, nil, nil) // if err != nil { // return err // } // handler.SyncAct(ctx, nil, actMap[actStoreSkus[0].VendorID], nil, actStoreSkuMap[actStoreSkus[0].VendorID]) // } // } // } // return err // } func getSkuSaleStatus(inSkuBind *StoreSkuBindSkuInfo, skuNameBindInfo *StoreSkuBindInfo) int { tempSale := 0 if inSkuBind != nil { tempSale = inSkuBind.IsSale } if tempSale == 0 { tempSale = skuNameBindInfo.IsSale } if tempSale == -1 { return model.StoreSkuBindStatusDontSale } else if tempSale == 1 { return model.StoreSkuBindStatusNormal } return model.StoreSkuBindStatusNA } func AddEventDetail(db *dao.DaoDB, ctx *jxcontext.Context, operateType, thingID, thingType, storeID int, beforeData, afterData string) (err error) { if ctx.GetUserName() == "jxadmin" { return err } operateEventDetail := &model.OperateEventDetail{ OperateType: operateType, ThingID: thingID, ThingType: thingType, StoreID: storeID, AccessUUID: ctx.GetTrackInfo()[0:strings.Index(ctx.GetTrackInfo(), ",")], BeforeData: beforeData, AfterData: afterData, } err = event.AddOperateEventDetail(db, operateEventDetail) return err } func formatAutoSaleTime(autoSaleTime time.Time) (outAutoSaleTime time.Time) { if utils.IsTimeZero(autoSaleTime) { outAutoSaleTime = utils.DefaultTimeValue } else { outAutoSaleTime = jxutils.GetNextTimeFromList(time.Now(), []string{AutoSaleAtStr}) } return outAutoSaleTime } // todo 应该用updateStoresSkusWithoutSync实现 func updateStoreSkusSaleWithoutSync(ctx *jxcontext.Context, storeID int, skuBindSkuInfos []*StoreSkuBindSkuInfo, autoSaleTime time.Time, ignoreDontSale bool, userName string) (needSyncSkus []int, err error) { var num int64 db := dao.GetDB() needSyncIDMap := make(map[int]int) skuIDMap := make(map[int]int) skuBindSkuInfosMap := make(map[int]*StoreSkuBindSkuInfo) for _, v := range skuBindSkuInfos { skuIDMap[v.SkuID] = 1 skuBindSkuInfosMap[v.SkuID] = v } txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db, txDB) panic(r) } }() storeSkuList, err := dao.GetStoresSkusInfo(db, []int{storeID}, jxutils.IntMap2List(skuIDMap)) if err != nil { dao.Rollback(db, txDB) return nil, err } autoSaleTime = formatAutoSaleTime(autoSaleTime) for _, skuBind := range storeSkuList { if v := skuBindSkuInfosMap[skuBind.SkuID]; v != nil && v.IsSale != 0 { if !(!utils.IsTimeZero(autoSaleTime) && ignoreDontSale && skuBind.Status == model.StoreSkuBindStatusDontSale) { statusResult := skuBind.Status if v.IsSale == -1 || !utils.IsTimeZero(autoSaleTime) { skuBind.Status = model.StoreSkuBindStatusDontSale } else if v.IsSale == 1 { skuBind.Status = model.StoreSkuBindStatusNormal } kvs := map[string]interface{}{ model.FieldLastOperator: ctx.GetUserName(), model.FieldUpdatedAt: time.Now(), model.FieldStatus: skuBind.Status, model.FieldJdSyncStatus: skuBind.JdSyncStatus | model.SyncFlagSaleMask, model.FieldEbaiSyncStatus: skuBind.EbaiSyncStatus | model.SyncFlagSaleMask, model.FieldMtwmSyncStatus: skuBind.MtwmSyncStatus | model.SyncFlagSaleMask, } if utils.IsTimeZero(autoSaleTime) || skuBind.Status == model.SkuStatusNormal { kvs["AutoSaleAt"] = utils.DefaultTimeValue } else { kvs["AutoSaleAt"] = autoSaleTime } if globals.IsAddEvent { var status int if v.IsSale == -1 { status = model.StoreSkuBindStatusDontSale } else { status = model.StoreSkuBindStatusNormal } if status != statusResult { mapAfter := make(map[string]interface{}) mapAfter["Status"] = status mapBefore := make(map[string]interface{}) mapBefore["Status"] = statusResult err = AddEventDetail(db, ctx, model.OperateUpdate, v.SkuID, model.ThingTypeSku, storeID, BuildDiffData(mapBefore), BuildDiffData(mapAfter)) } } if num, err = dao.UpdateEntityLogically(db, skuBind, kvs, userName, nil); err != nil { dao.Rollback(db, txDB) return nil, err } if num == 1 { needSyncIDMap[v.SkuID] = 1 } } } } dao.Commit(db, txDB) needSyncSkus = jxutils.IntMap2List(needSyncIDMap) return needSyncSkus, err } func BuildDiffData(mapData map[string]interface{}) string { dd := utils.MustMarshal(mapData) result := utils.LimitUTF8StringLen(string(dd), 3200) return result } func uniqueStoreSkuBind(skuBindSkuInfos []*StoreSkuBindSkuInfo) (outSkuBindSkuInfos []*StoreSkuBindSkuInfo) { skuIDMap := make(map[int]int) for _, v := range skuBindSkuInfos { if skuIDMap[v.SkuID] != 1 { outSkuBindSkuInfos = append(outSkuBindSkuInfos, v) skuIDMap[v.SkuID] = 1 } } return outSkuBindSkuInfos } func UpdateStoresSkusSale(ctx *jxcontext.Context, storeIDs []int, skuBindSkuInfos []*StoreSkuBindSkuInfo, autoSaleTime time.Time, ignoreDontSale bool, userName string, isAsync, isContinueWhenError bool) (hint string, err error) { storeIDs = uniqueStoreIDs(storeIDs) skuBindSkuInfos = uniqueStoreSkuBind(skuBindSkuInfos) var num int64 for _, storeID := range storeIDs { skuIDs, err2 := updateStoreSkusSaleWithoutSync(ctx, storeID, skuBindSkuInfos, autoSaleTime, ignoreDontSale, userName) if err = err2; err != nil { return "", err } num += int64(len(skuIDs)) } isAsync = asyncStoreSkuOpFilter(ctx, isAsync) if num > 0 { skuIDs := make([]int, 0) for _, v := range skuBindSkuInfos { skuIDs = append(skuIDs, v.SkuID) } db := dao.GetDB() hint, err = CurVendorSync.SyncStoresSkus(ctx, nil, 0, db, nil, storeIDs, skuIDs, false, isAsync, isContinueWhenError) } if num == 0 || !isAsync || hint == "" { hint = utils.Int64ToStr(num) } return hint, err } func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID int, toStoreIDs []int, copyMode string, isScale bool, params map[string]interface{}, userName string) (num int64, err error) { if copyMode != CopyStoreSkuModeFresh && copyMode != CopyStoreSkuModeUpdate && copyMode != CopyStoreSkuModeUpdatePrice { return 0, fmt.Errorf("不支持的拷贝模式:%s", copyMode) } //权限 if permission.IsRoled(ctx) { if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil { if storeIDsMap[fromStoreID] == 0 { return 0, fmt.Errorf("抱歉,您无权更改他人店铺商品 [%v]", fromStoreID) } for _, v := range toStoreIDs { if storeIDsMap[v] == 0 { return 0, fmt.Errorf("抱歉,您无权更改他人店铺商品 [%v]", v) } } } } db := dao.GetDB() fromStore, err := checkStoreExisting(db, fromStoreID) if err != nil { return 0, err } sqlCatAndSku := "" sqlCatAndSkuParams := make([]interface{}, 0) if params["categoryIDs"] != nil { var cats []int if err = utils.UnmarshalUseNumber([]byte(params["categoryIDs"].(string)), &cats); err != nil { return 0, err } if len(cats) > 0 { sqlCatAndSku += " AND (t3.category_id IN (" + dao.GenQuestionMarks(len(cats)) + ") OR t4.parent_id IN (" + dao.GenQuestionMarks(len(cats)) + "))" sqlCatAndSkuParams = append(sqlCatAndSkuParams, cats, cats) } } if params["skuIDs"] != nil { var skus []int if err = utils.UnmarshalUseNumber([]byte(params["skuIDs"].(string)), &skus); err != nil { return 0, err } if len(skus) > 0 { sqlCatAndSku += " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(skus)) + ")" sqlCatAndSkuParams = append(sqlCatAndSkuParams, skus) } } pricePercentage := 100 if params["pricePercentage"] != nil { pricePercentage = params["pricePercentage"].(int) } errList := errlist.New() for _, toStoreID := range toStoreIDs { toStore, err := checkStoreExisting(db, toStoreID) if err != nil { errList.AddErr(err) break } now := time.Now() if fromStoreID == toStoreID { sql := ` UPDATE store_sku_bind t1 JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? JOIN sku_name t3 ON t2.name_id = t3.id AND t2.deleted_at = ? LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t2.deleted_at = ? SET t1.last_operator = ?, t1.updated_at = ?, t1.price = t1.price * ? / 100, t1.jx_price = t1.jx_price * ? / 100, t1.unit_price = t1.unit_price * ? / 100, t1.jd_sync_status = t1.jd_sync_status | ?, t1.mtwm_sync_status = t1.mtwm_sync_status | ?, t1.ebai_sync_status = t1.ebai_sync_status | ? WHERE t1.store_id = ? AND t1.deleted_at = ? ` sqlParams := []interface{}{ utils.DefaultTimeValue, utils.DefaultTimeValue, utils.DefaultTimeValue, userName, now, pricePercentage, pricePercentage, pricePercentage, model.SyncFlagPriceMask, model.SyncFlagPriceMask, model.SyncFlagPriceMask, toStoreID, utils.DefaultTimeValue, } sql += sqlCatAndSku sqlParams = append(sqlParams, sqlCatAndSkuParams) num2, err2 := dao.ExecuteSQL(db, sql, sqlParams) if err2 != nil { errList.AddErr(err2) } else { num += num2 } break } txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db, txDB) panic(r) } }() if copyMode == CopyStoreSkuModeFresh { // 将toStore中存在,但fromStore中不存在的置删除标志 sqlDelete := ` UPDATE store_sku_bind t1 LEFT JOIN store_sku_bind t0 ON t0.store_id = ? AND t0.sku_id = t1.sku_id AND t0.deleted_at = ? JOIN sku t2 ON t1.sku_id = t2.id/* AND t2.deleted_at = ?*/ JOIN sku_name t3 ON t2.name_id = t3.id/* AND t2.deleted_at = ?*/ LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t2.deleted_at = ? SET t1.deleted_at = ?, t1.updated_at = ?, t1.last_operator = ?, t1.status = ?, t1.jd_sync_status = IF((t1.jd_sync_status & ?) <> 0, 0, ?), t1.mtwm_sync_status = IF((t1.mtwm_sync_status & ?) <> 0, 0, ?), t1.ebai_sync_status = IF((t1.ebai_sync_status & ?) <> 0, 0, ?) WHERE t1.store_id = ? AND t1.deleted_at = ? AND t0.id IS NULL ` sqlDeleteParams := []interface{}{ fromStoreID, utils.DefaultTimeValue, // utils.DefaultTimeValue, // utils.DefaultTimeValue, utils.DefaultTimeValue, now, now, userName, model.StoreSkuBindStatusDeleted, model.SyncFlagNewMask, model.SyncFlagDeletedMask, model.SyncFlagNewMask, model.SyncFlagDeletedMask, model.SyncFlagNewMask, model.SyncFlagDeletedMask, toStoreID, utils.DefaultTimeValue, } sqlDelete += sqlCatAndSku sqlDeleteParams = append(sqlDeleteParams, sqlCatAndSkuParams) // globals.SugarLogger.Debug(sqlDelete) num2, err2 := dao.ExecuteSQL(db, sqlDelete, sqlDeleteParams) if err = err2; err != nil { errList.AddErr(err) dao.Rollback(db, txDB) break } num += num2 globals.SugarLogger.Debugf("CopyStoreSkus fromStoreID:%d, toStoreID:%d, trackInfo:%s num1:%d", fromStoreID, toStoreID, ctx.GetTrackInfo(), num2) } isModifyStatus := 1 syncStatus := model.SyncFlagStoreSkuOnlyMask if copyMode == CopyStoreSkuModeUpdatePrice { isModifyStatus = 0 syncStatus = model.SyncFlagPriceMask } scaleFactor := float64(1) if isScale { fromPayPercentage := jxutils.ConstrainPayPercentage(fromStore.PayPercentage) toPayPercentage := jxutils.ConstrainPayPercentage(toStore.PayPercentage) scaleFactor = float64(fromPayPercentage) / float64(toPayPercentage) } // 处理toStore中与fromStore中都存在的 sql := ` UPDATE store_sku_bind t1 JOIN store t11 ON t11.id = t1.store_id LEFT JOIN store_sku_bind t0 ON t0.store_id = ? AND t0.sku_id = t1.sku_id AND t0.deleted_at = ? JOIN store t01 ON t01.id = t0.store_id JOIN sku t2 ON t1.sku_id = t2.id/* AND t2.deleted_at = ?*/ JOIN sku_name t3 ON t2.name_id = t3.id/* AND t3.deleted_at = ?*/ LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ? SET t1.last_operator = ?, t1.updated_at = ?, t1.sub_store_id = 0, t1.price = IF(t0.price * ? / 100 > 0, t0.price * ? / 100, 1) * ?, t1.jx_price = IF(t0.jx_price * ? / 100 > 0, t0.jx_price * ? / 100, 1) * ?, t1.unit_price = IF(t0.unit_price * ? / 100 > 0, t0.unit_price * ? / 100, 1) * ?, t1.status = IF(? = 0, t1.status, t0.status), t1.jd_sync_status = t1.jd_sync_status | ?, t1.mtwm_sync_status = t1.mtwm_sync_status | ?, t1.ebai_sync_status = t1.ebai_sync_status | ?, t1.stock = t0.stock WHERE t1.store_id = ? AND t1.deleted_at = ? AND t0.id IS NOT NULL ` sqlParams := []interface{}{ fromStoreID, utils.DefaultTimeValue, // utils.DefaultTimeValue, // utils.DefaultTimeValue, utils.DefaultTimeValue, userName, now, pricePercentage, pricePercentage, scaleFactor, pricePercentage, pricePercentage, scaleFactor, pricePercentage, pricePercentage, scaleFactor, isModifyStatus, syncStatus, syncStatus, syncStatus, toStoreID, utils.DefaultTimeValue, } sql += sqlCatAndSku sqlParams = append(sqlParams, sqlCatAndSkuParams) // globals.SugarLogger.Debug(sql) num2, err2 := dao.ExecuteSQL(db, sql, sqlParams) globals.SugarLogger.Debugf("CopyStoreSkus fromStoreID:%d, toStoreID:%d, trackInfo:%s num2:%d", fromStoreID, toStoreID, ctx.GetTrackInfo(), num2) if err = err2; err != nil { errList.AddErr(err) dao.Rollback(db, txDB) break } num += num2 // 添加toStore中不存在,但fromStore存在的 sql = ` INSERT INTO store_sku_bind(created_at, updated_at, last_operator, deleted_at, store_id, sku_id, sub_store_id, price, jx_price, unit_price, status, jd_sync_status, ebai_sync_status, mtwm_sync_status, stock) SELECT ?, ?, ?, ?, ?, t1.sku_id, 0, IF(t1.price * ? / 100 > 0, t1.price * ? / 100, 1) * ?, IF(t1.jx_price * ? / 100 > 0, t1.jx_price * ? / 100, 1) * ?, IF(t1.unit_price * ? / 100 > 0, t1.unit_price * ? / 100, 1) * ?, IF(? = 0, ?, t1.status), ?, ?, ?, t1.stock FROM store_sku_bind t1 JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ? JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ? LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ? LEFT JOIN store_sku_bind t0 ON t1.sku_id = t0.sku_id AND t0.store_id = ? AND t0.deleted_at = ? WHERE t1.store_id = ? AND t1.deleted_at = ? ` sqlParams = []interface{}{ now, now, userName, utils.DefaultTimeValue, toStoreID, pricePercentage, pricePercentage, scaleFactor, pricePercentage, pricePercentage, scaleFactor, pricePercentage, pricePercentage, scaleFactor, isModifyStatus, model.SkuStatusDontSale, model.SyncFlagNewMask, model.SyncFlagNewMask, model.SyncFlagNewMask, utils.DefaultTimeValue, utils.DefaultTimeValue, utils.DefaultTimeValue, toStoreID, utils.DefaultTimeValue, fromStoreID, utils.DefaultTimeValue, } sql += sqlCatAndSku + " AND t0.id IS NULL" sqlParams = append(sqlParams, sqlCatAndSkuParams) num2, err = dao.ExecuteSQL(db, sql, sqlParams) if err != nil { errList.AddErr(err) dao.Rollback(db, txDB) break } num += num2 //上方insert会无视目标门店中未关注的商品(以前关注,后来取消关注),所以这里批量删一下 sql2 := ` DELETE FROM store_sku_bind WHERE store_id = ? AND sku_id IN ( SELECT b.sku_id FROM ( SELECT store_id,sku_id,count(*) FROM store_sku_bind WHERE store_id = ? GROUP BY 1,2 HAVING count(*) > 1)b ) AND deleted_at <> ? ` sqlParams2 := []interface{}{ toStoreID, toStoreID, utils.DefaultTimeValue, } _, err = dao.ExecuteSQL(db, sql2, sqlParams2) if err != nil { errList.AddErr(err) dao.Rollback(db, txDB) break } globals.SugarLogger.Debugf("CopyStoreSkus fromStoreID:%d, toStoreID:%d, trackInfo:%s num3:%d", fromStoreID, toStoreID, ctx.GetTrackInfo(), num2) dao.Commit(db, txDB) //同一商品若源门店的规格少于要复制到的门店,则在复制的门店里的其他规格的unitprice不会变, //导致复制的门店的商品unitprice不尽相同,这里先处理一下 type tStore struct { NameID int `orm:"column(name_id)"` StoreID int `orm:"column(store_id)"` } var resultList []*tStore sql3 := ` SELECT a.name_id, a.store_id FROM ( SELECT DISTINCT a.unit_price, b.name_id, a.store_id FROM store_sku_bind a, sku b, store c WHERE a.sku_id = b.id AND c.id = a.store_id AND c.deleted_at = ? AND a.store_id = ? AND a.deleted_at = ?)a GROUP BY 1, 2 HAVING COUNT(a.unit_price) > 1 ` sqlParams3 := []interface{}{utils.DefaultTimeValue, toStoreID, utils.DefaultTimeValue} err = dao.GetRows(db, &resultList, sql3, sqlParams3) if len(resultList) > 0 { var skuBindInfos []*StoreSkuBindInfo for _, v := range resultList { storeSkus, _ := dao.GetStoreSkusByNameIDs(db, []int{v.StoreID}, v.NameID) unitPirce := storeSkus[0].UnitPrice skuBindInfo := &StoreSkuBindInfo{ StoreID: v.StoreID, NameID: v.NameID, UnitPrice: int(unitPirce), } skuBindInfos = append(skuBindInfos, skuBindInfo) } _, err = updateStoresSkusWithoutSync(ctx, db, []int{toStoreID}, skuBindInfos, false, false) } } if globals.IsAddEvent { mapAfter := make(map[string]interface{}) mapAfter["ToStoreIDs"] = toStoreIDs mapAfter["CopyMode"] = copyMode mapAfter["IsScale"] = isScale mapAfter["PricePercentage"] = pricePercentage mapBefore := make(map[string]interface{}) mapBefore["FromStoreID"] = fromStoreID err = AddEventDetail(db, ctx, model.OperateCopyStoreSkus, 0, model.ThingTypeSku, fromStoreID, BuildDiffData(mapBefore), BuildDiffData(mapAfter)) } return num, errList.GetErrListAsOne() } func shouldPendingStorePriceChange(ctx *jxcontext.Context, storeID int, skuBindInfo *StoreSkuBindInfo) (shouldPending bool, err error) { if globals.EnablePendingChange { if skuBindInfo.IsFocus != 1 && (ctx.GetLoginType() == weixin.AuthTypeMP || ctx.GetLoginType() == weixin.AuthTypeMini || ctx.GetUserName() == "fakeboss") { db := dao.GetDB() store := &model.Store{} store.ID = storeID if err = dao.GetEntity(db, store); err != nil { return false, err } return jxutils.TranslateStorePriceType(store.ChangePriceType) == model.StoreChangePriceTypeNeedApprove, nil } } return false, nil } func filterStorePriceChange(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (filteredStoreIDs []int, filteredSkuBindInfos []*StoreSkuBindInfo, err error) { globals.SugarLogger.Debug("filterStorePriceChange") if globals.EnablePendingChange { db := dao.GetDB() txDB, _ := dao.Begin(db) defer dao.Rollback(db, txDB) for _, storeID := range storeIDs { for _, skuBindInfo := range skuBindInfos { shouldPending, err2 := shouldPendingStorePriceChange(ctx, storeID, skuBindInfo) if err = err2; err != nil { return nil, nil, err } if shouldPending && (skuBindInfo.UnitPrice != 0 || skuBindInfo.IsFocus == 1) { var ( opInfoList []*StoreOpRequestInfo sql string sqlParams []interface{} opType int8 ) if skuBindInfo.IsFocus == 1 { opType = model.RequestTypeFocusSkuName sql = ` SELECT DISTINCT t1.*, t3.unit_price FROM store_op_request t1 JOIN sku t2 ON t2.name_id = t1.item_id AND t2.deleted_at = ? LEFT JOIN store_sku_bind t3 ON t3.store_id = t1.store_id AND t3.sku_id = t2.id AND t3.deleted_at = ? WHERE t1.store_id = ? AND t1.item_id = ? AND t1.deleted_at = ? AND t1.type IN (?, ?)` sqlParams = []interface{}{ utils.DefaultTimeValue, utils.DefaultTimeValue, storeID, skuBindInfo.NameID, utils.DefaultTimeValue, model.RequestTypeChangePrice, model.RequestTypeFocusSkuName, } } else { opType = model.RequestTypeChangePrice sql = ` SELECT DISTINCT t3.*, t1.unit_price FROM store_sku_bind t1 JOIN sku t2 ON t2.id = t1.sku_id AND t2.name_id = ? AND t2.deleted_at = ? LEFT JOIN store_op_request t3 ON t3.store_id = t1.store_id AND t3.item_id = t2.name_id AND t3.deleted_at = ? AND t3.type IN (?, ?) WHERE t1.store_id = ? AND t1.deleted_at = ?` sqlParams = []interface{}{ skuBindInfo.NameID, utils.DefaultTimeValue, utils.DefaultTimeValue, model.RequestTypeChangePrice, model.RequestTypeFocusSkuName, storeID, utils.DefaultTimeValue, } } if err = dao.GetRows(db, &opInfoList, sql, sqlParams...); err != nil { return nil, nil, err } if len(opInfoList) > 1 { panic(fmt.Sprintf("filterStorePriceChange more than one row, storeID:%d, skuBindInfo:%s, result:%s", storeID, utils.Format4Output(skuBindInfo, false), utils.Format4Output(opInfoList, false))) } existRow := len(opInfoList) == 1 existRequestOp := existRow && opInfoList[0].ID != 0 var changeReq *model.StoreOpRequest if existRequestOp { if opInfoList[0].Type != opType { return nil, nil, fmt.Errorf("filterStorePriceChange,关注与修改价格只应该存在一个待审核请求,已存在的:%s,新来的,storeID:%d, skuBindInfo:%s", utils.Format4Output(opInfoList[0], false), storeID, utils.Format4Output(skuBindInfo, false)) } changeReq = &opInfoList[0].StoreOpRequest } else { changeReq = &model.StoreOpRequest{} } if existRequestOp && opInfoList[0].UnitPrice == skuBindInfo.UnitPrice { changeReq.Status = model.RequestStatusCanceled changeReq.DeletedAt = time.Now() } else { changeReq.Status = model.RequestStatusNew changeReq.Type = opType changeReq.StoreID = storeID changeReq.ItemID = skuBindInfo.NameID changeReq.UserID = ctx.GetUserName() changeReq.IntParam1 = skuBindInfo.UnitPrice changeReq.IntParam2 = skuBindInfo.IsSale if len(skuBindInfo.Skus) > 0 { changeReq.JsonParam = string(utils.MustMarshal(skuBindInfo.Skus)) } } if existRequestOp { dao.WrapUpdateULEntity(changeReq, ctx.GetUserName()) if _, err = dao.UpdateEntity(db, changeReq); err != nil { return nil, nil, err } } else { if !existRow || opInfoList[0].UnitPrice != skuBindInfo.UnitPrice { dao.WrapAddIDCULDEntity(changeReq, ctx.GetUserName()) if err = dao.CreateEntity(db, changeReq); err != nil { return nil, nil, err } } } // 去除价格相关的部分 if skuBindInfo.IsFocus == 1 { skuBindInfo.IsFocus = 0 } skuBindInfo.UnitPrice = 0 } } } dao.Commit(db, txDB) } return storeIDs, skuBindInfos, nil } func AcceptStoreOpRequests(ctx *jxcontext.Context, reqIDs []int) (err error) { if globals.EnablePendingChange { if len(reqIDs) > 0 { subErrors := make(map[int]error) infoMap, err2 := getStoreOpRequestsInfo(reqIDs) if err = err2; err != nil { return err } for reqID, op := range infoMap { if op.Status == model.RequestStatusNew { skuBindInfo := &StoreSkuBindInfo{ NameID: op.ItemID, UnitPrice: op.IntParam1, IsSale: op.IntParam2, } if op.Type == model.RequestTypeFocusSkuName { skuBindInfo.IsFocus = 1 } if op.JsonParam != "" { if err2 = utils.UnmarshalUseNumber([]byte(op.JsonParam), &skuBindInfo.Skus); err2 != nil { subErrors[reqID] = err2 } } if err2 == nil { _, err2 := UpdateStoresSkus(ctx, 0, []int{op.StoreID}, []*StoreSkuBindInfo{skuBindInfo}, false, false, false, false) isLocalSucess := true if err2 != nil { subErrors[reqID] = err2 if !isSyncError(err2) { isLocalSucess = false } } if isLocalSucess { weixinmsg.NotifyStoreOpRequestStatus(true, op.StoreID, op.ItemID, jxutils.ComposeSpuName(op.SkuNamePrefix, op.SkuNameName, 0), op.UnitPrice, op.IntParam1, "") if err2 := changeStoreOpStatus(ctx, []int{reqID}, model.RequestStatusAccepted, ""); err2 != nil { subErrors[reqID] = err2 } } } } } if len(subErrors) > 0 { errMsg := "" for k, v := range subErrors { errMsg += fmt.Sprintf("req:%d, error:%s\n", k, v.Error()) } err = errors.New(errMsg) } } } return err } func RejectStoreOpRequests(ctx *jxcontext.Context, reqIDs []int, rejectReason string) (err error) { infoMap, err := getStoreOpRequestsInfo(reqIDs) if err != nil { return err } if err = changeStoreOpStatus(ctx, reqIDs, model.RequestStatusRejected, rejectReason); err == nil { for _, info := range infoMap { weixinmsg.NotifyStoreOpRequestStatus(false, info.StoreID, info.ItemID, jxutils.ComposeSpuName(info.SkuNamePrefix, info.SkuNameName, 0), info.UnitPrice, info.IntParam1, rejectReason) } } return err } // 当前些函数只针对type为: RequestTypeChangePrice与RequestTypeFocusSkuName的查询才有效 func GetStoreOpRequests(ctx *jxcontext.Context, fromTime, toTime time.Time, keyword string, storeIDs, itemIDs, typeList, statusList []int, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) { if globals.EnablePendingChange { sql := ` SELECT SQL_CALC_FOUND_ROWS t1.id, t1.created_at, t1.updated_at, t1.last_operator, t1.deleted_at, t1.type, t1.store_id, t1.item_id, t1.status, t1.user_id, t1.int_param1, t1.int_param2, t1.remark, t2.name store_name, t3.prefix sku_name_prefix, t3.name sku_name_name, MAX(IF(t1.status = ?, t5.unit_price, t1.int_param0)) unit_price FROM store_op_request t1 JOIN store t2 ON t1.store_id = t2.id JOIN sku_name t3 ON t1.item_id = t3.id AND t3.deleted_at = ? JOIN sku t4 ON t3.id = t4.name_id AND t4.deleted_at = ? LEFT JOIN store_sku_bind t5 ON t1.store_id = t5.store_id AND t4.id = t5.sku_id AND t5.deleted_at = ? WHERE 1 = 1` sqlParams := []interface{}{ model.RequestStatusNew, utils.DefaultTimeValue, utils.DefaultTimeValue, utils.DefaultTimeValue, } if keyword != "" { keywordLike := "%" + keyword + "%" sql += " AND ( t2.name LIKE ? OR t3.name LIKE ?" sqlParams = append(sqlParams, keywordLike, keywordLike) if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil { sql += " OR t1.store_id = ? OR t1.item_id = ?" sqlParams = append(sqlParams, keywordInt64, keywordInt64) } sql += ")" } if fromTime != utils.DefaultTimeValue { sql += " AND t1.created_at >= ?" sqlParams = append(sqlParams, fromTime) } if toTime != utils.DefaultTimeValue { sql += " AND t1.created_at <= ?" sqlParams = append(sqlParams, toTime) } if len(storeIDs) > 0 { sql += " AND t1.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")" sqlParams = append(sqlParams, storeIDs) } if len(itemIDs) > 0 { sql += " AND t1.item_id IN (" + dao.GenQuestionMarks(len(itemIDs)) + ")" sqlParams = append(sqlParams, itemIDs) } if len(typeList) > 0 { sql += " AND t1.type IN (" + dao.GenQuestionMarks(len(typeList)) + ")" sqlParams = append(sqlParams, typeList) } if len(statusList) > 0 { sql += " AND t1.status IN (" + dao.GenQuestionMarks(len(statusList)) + ")" sqlParams = append(sqlParams, statusList) } sql += ` GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 ORDER BY 1 DESC LIMIT ? OFFSET ?` pageSize = jxutils.FormalizePageSize(pageSize) sqlOffset := offset sqlPageSize := pageSize sqlParams = append(sqlParams, sqlPageSize, sqlOffset) db := dao.GetDB() // globals.SugarLogger.Debug(sql) // globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false)) var requestList []*StoreOpRequestInfo txDB, _ := dao.Begin(db) defer dao.Commit(db, txDB) if err = dao.GetRowsTx(txDB, &requestList, sql, sqlParams...); err == nil { return &model.PagedInfo{ TotalCount: dao.GetLastTotalRowCount2(db, txDB), Data: requestList, }, nil } } return nil, err } func getStoreOpRequestsInfo(reqIDs []int) (infoMap map[int]*StoreOpRequestInfo, err error) { infoMap = make(map[int]*StoreOpRequestInfo) if len(reqIDs) > 0 { sql := ` SELECT DISTINCT t1.*, t4.name store_name, t2.prefix sku_name_prefix, t2.name sku_name_name, t3.unit_price FROM store_op_request t1 JOIN sku_name t2 ON t2.id = t1.item_id JOIN sku t5 ON t5.name_id = t1.item_id AND t5.deleted_at = ? LEFT JOIN store_sku_bind t3 ON t3.store_id = t1.store_id AND t3.sku_id = t5.id AND t3.deleted_at = ? JOIN store t4 ON t4.id = t1.store_id WHERE t1.id IN (` + dao.GenQuestionMarks(len(reqIDs)) + ")" sqlParams := []interface{}{ utils.DefaultTimeValue, utils.DefaultTimeValue, reqIDs, } db := dao.GetDB() var infoList []*StoreOpRequestInfo if err = dao.GetRows(db, &infoList, sql, sqlParams...); err == nil { for _, v := range infoList { infoMap[v.ID] = v } if len(reqIDs) > len(infoMap) { missingReqIDs := []int{} for _, reqID := range reqIDs { if infoMap[reqID] == nil { missingReqIDs = append(missingReqIDs, reqID) } } err = fmt.Errorf("不能找到如下的请求ID:%s", utils.Format4Output(missingReqIDs, true)) } } } else { err = errors.New("没有找到指定的请求ID") } return infoMap, err } func changeStoreOpStatus(ctx *jxcontext.Context, reqIDs []int, status int8, rejectReason string) (err error) { globals.SugarLogger.Debugf("changeStoreOpStatus, reqIDs:%v", reqIDs) if globals.EnablePendingChange { if len(reqIDs) > 0 { infoMap, err2 := getStoreOpRequestsInfo(reqIDs) if err = err2; err != nil { return err } db := dao.GetDB() txDB, _ := dao.Begin(db) defer dao.Rollback(db, txDB) for _, reqID := range reqIDs { op := &model.StoreOpRequest{} op.Remark = rejectReason op.Status = status if infoMap[reqID] != nil { op.IntParam0 = infoMap[reqID].UnitPrice } dao.WrapUpdateULEntity(op, ctx.GetUserName()) op.DeletedAt = time.Now() op.ID = reqID // globals.SugarLogger.Debug(utils.Format4Output(op, false)) if _, err = dao.UpdateEntity(db, op, "IntParam0", "Remark", "Status", "DeletedAt", "LastOperator", "UpdatedAt"); err != nil { return err } } dao.Commit(db, txDB) } } return err } func setStoreSkuBindStatus(skuBind *model.StoreSkuBind, status int8) { skuBind.JdSyncStatus |= status skuBind.EbaiSyncStatus |= status skuBind.MtwmSyncStatus |= status skuBind.YbSyncStatus |= status skuBind.JdsSyncStatus |= status } func checkStoreExisting(db *dao.DaoDB, storeID int) (store *model.Store, err error) { store = &model.Store{} store.ID = storeID if err = dao.GetEntity(db, store); err != nil { if err == orm.ErrNoRows { return nil, fmt.Errorf("门店:%d不存在", storeID) } return nil, err } return store, nil } func RefreshStoresSkuByVendor(ctx *jxcontext.Context, storeIDs []int, vendorID int, isAsync bool) (hint string, err error) { if vendorID != model.VendorIDJD { return "", fmt.Errorf("此功能当前只支持京东到家平台") } db := dao.GetDB() storeMapList, err := dao.GetStoresMapList(db, []int{vendorID}, storeIDs, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "") if err != nil { return "", err } if len(storeMapList) != len(storeIDs) { return "", fmt.Errorf("门店绑定信息不匹配,请确定门店绑定且只绑定了京东平台") } storeMap := make(map[int]*dao.StoreDetail) for _, v := range storeMapList { if v.VendorID != vendorID { return "", fmt.Errorf("门店%d绑定的不(只)是京东", v.StoreID) } storeMap[v.StoreID], err = dao.GetStoreDetail(db, v.StoreID, v.VendorID, "") if err != nil { return "", err } } skuList, err := dao.GetSkusWithVendor(db, []int{vendorID}, nil, nil, nil, false) if err != nil { return "", err } skuNameMap := make(map[int]*model.SkuName) skuMap := make(map[int]*dao.StoreSkuSyncInfo) bareStoreSkuMap := make(map[string][]*partner.StoreSkuInfo) for _, sku := range skuList { if skuNameMap[sku.NameID] == nil { skuNameMap[sku.NameID] = &model.SkuName{ Unit: sku.Unit, } } skuMap[sku.ID] = sku bareStoreSkuMap[sku.VendorOrgCode] = append(bareStoreSkuMap[sku.VendorOrgCode], &partner.StoreSkuInfo{ SkuID: sku.ID, VendorSkuID: sku.VendorSkuID, }) } handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler) var storeSkuList []*model.StoreSkuBind rootTask := tasksch.NewParallelTask(fmt.Sprintf("根据厂家门店商品信息相应刷新本地数据:%v", storeIDs), tasksch.NewParallelConfig().SetParallelCount(1), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { oneStoreMap := batchItemList[0].(*model.StoreMap) subTask := tasksch.NewSeqTask(fmt.Sprintf("根据厂家门店商品信息相应刷新本地数据:%s", model.VendorChineseNames[oneStoreMap.VendorID]), ctx, func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: bareStoreSkuList, err2 := handler.GetStoreSkusBareInfo(ctx, oneStoreMap.VendorOrgCode, task, oneStoreMap.StoreID, oneStoreMap.VendorStoreID, bareStoreSkuMap[oneStoreMap.VendorOrgCode]) // globals.SugarLogger.Debug(utils.Format4Output(bareStoreSkuList, false)) if err = err2; err == nil || len(bareStoreSkuList) > 0 { err = nil // todo 如果部分失败,强制忽略错误 for _, v := range bareStoreSkuList { storeSkuList = append(storeSkuList, &model.StoreSkuBind{ StoreID: oneStoreMap.StoreID, SkuID: v.SkuID, Status: v.Status, Price: int(v.VendorPrice), }) } } case 1: if len(storeSkuList) > 0 { for _, v := range storeSkuList { sku := skuMap[v.SkuID] skuName := skuNameMap[sku.NameID] if skuName.IsGlobal == 0 && (jxutils.IsSkuSpecial(sku.SpecQuality, sku.SpecUnit) || skuName.Unit != model.SpecialUnit) { skuName.Price = v.Price // 标准价 skuName.IsGlobal = 1 } } for _, v := range storeSkuList { sku := skuMap[v.SkuID] skuName := skuNameMap[sku.NameID] if skuName.IsGlobal == 0 { if skuName.Price == 0 { skuName.Price = jxutils.CaculateUnitPrice(v.Price, sku.SpecQuality, sku.SpecUnit, skuName.Unit) } else { skuName.Price = (skuName.Price + jxutils.CaculateUnitPrice(v.Price, sku.SpecQuality, sku.SpecUnit, skuName.Unit)) / 2 } } } for _, v := range storeSkuList { pricePercentage, priceAdd := jxutils.GetPricePercentageByVendorPrice(storeMap[v.StoreID].PricePercentagePackObj, v.Price, int(storeMap[v.StoreID].PricePercentage)) skuName := skuNameMap[skuMap[v.SkuID].NameID] v.Price = jxutils.CaculateSkuPriceFromVendor(v.Price, pricePercentage, priceAdd) v.UnitPrice = jxutils.CaculateSkuPriceFromVendor(skuName.Price, pricePercentage, priceAdd) dao.WrapAddIDCULDEntity(v, ctx.GetUserName()) setStoreSkuBindStatus(v, model.SyncFlagNewMask) v.JdSyncStatus = 0 } } case 2: if len(storeSkuList) > 0 { txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil || err != nil { dao.Rollback(db, txDB) if r != nil { panic(r) } } }() if _, err = dao.ExecuteSQL(db, ` DELETE t1 FROM store_sku_bind t1 WHERE t1.store_id IN ( `+dao.GenQuestionMarks(len(storeIDs))+")", storeIDs); err == nil { if err = dao.CreateMultiEntities(db, storeSkuList); err == nil { hint = utils.Int2Str(len(storeSkuList)) dao.Commit(db, txDB) } } } } return nil, err }, 3) tasksch.HandleTask(subTask, task, true).Run() _, err = subTask.GetResult(0) return retVal, err }, storeMapList) tasksch.ManageTask(rootTask).Run() if isAsync { hint = rootTask.GetID() } else { _, err = rootTask.GetResult(0) } return hint, err } func GetVendorStoreSkusInfo(ctx *jxcontext.Context, storeID int, vendorIDs, skuIDs []int, isContinueWhenError bool) (skuVendorMap map[int][]*partner.StoreSkuInfo, err error) { globals.SugarLogger.Debugf("GetVendorStoreSkusInfo, storeID:%d, vendorIDs:%v, skuID:%v", storeID, vendorIDs, skuIDs) db := dao.GetDB() var locker sync.RWMutex skuVendorMap = make(map[int][]*partner.StoreSkuInfo) _, err = CurVendorSync.LoopStoresMap(ctx, db, fmt.Sprintf("GetVendorStoreSkusInfo storeID:%d", storeID), false, false, vendorIDs, []int{storeID}, func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) { loopMapInfo := batchItemList[0].(*LoopStoreMapInfo) if handler, _ := partner.GetPurchasePlatformFromVendorID(loopMapInfo.VendorID).(partner.IPurchasePlatformStoreSkuHandler); handler != nil { storeSkuList, err2 := dao.GetStoreSkus2(db, loopMapInfo.VendorID, storeID, skuIDs, false) if err = err2; err == nil && len(storeSkuList) > 0 { bareStoreSkuInfoList := make([]*partner.StoreSkuInfo, len(skuIDs)) for k, v := range storeSkuList { bareStoreSkuInfoList[k] = &partner.StoreSkuInfo{ SkuID: v.SkuID, VendorSkuID: v.VendorSkuID, } } outBareStoreSkuInfoList, err2 := handler.GetStoreSkusBareInfo(ctx, loopMapInfo.StoreMapList[0].VendorOrgCode, t, loopMapInfo.StoreMapList[0].StoreID, loopMapInfo.StoreMapList[0].VendorStoreID, bareStoreSkuInfoList) if err = err2; err == nil && outBareStoreSkuInfoList != nil { locker.Lock() defer locker.Unlock() skuVendorMap[loopMapInfo.VendorID] = outBareStoreSkuInfoList } } } return nil, err }, true) if err != nil { skuVendorMap = nil } return skuVendorMap, err } func SyncJdStoreProducts(ctx *jxcontext.Context, storeIDs, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { db := dao.GetDB() isManageIt := len(storeIDs) != 1 || len(skuIDs) == 0 || len(skuIDs) > 8 hint, err = CurVendorSync.LoopStoresMap(ctx, db, fmt.Sprintf("京东商家商品状态同步:%v", storeIDs), isAsync, isManageIt, []int{model.VendorIDJD}, storeIDs, func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { loopMapInfo := batchItemList[0].(*LoopStoreMapInfo) if handler := partner.GetPurchasePlatformFromVendorID(loopMapInfo.VendorID); handler != nil { jdHandler := handler.(*jd.PurchaseHandler) hint, err2 := jdHandler.SyncStoreProducts(ctx, loopMapInfo.StoreMapList[0].VendorOrgCode, t, loopMapInfo.StoreMapList[0].StoreID, skuIDs, false, isContinueWhenError) if err = err2; err == nil { retVal = []interface{}{hint} } } return retVal, partner.AddVendorInfo2Err(err, loopMapInfo.VendorID) }, isContinueWhenError) return hint, err } func GetMissingStoreSkuFromOrder(ctx *jxcontext.Context, fromTime time.Time) (missingList []*StoreSkuBindInfo, err error) { storeSkuList, err := dao.GetMissingStoreSkuFromOrder(dao.GetDB(), nil, fromTime) if err == nil { storeSkuNameMap := make(map[int64]*StoreSkuBindInfo) for _, v := range storeSkuList { skuName := storeSkuNameMap[jxutils.Combine2Int(v.StoreID, v.NameID)] if skuName == nil { skuName = &StoreSkuBindInfo{ StoreID: v.StoreID, NameID: v.NameID, IsFocus: 1, IsSale: 1, // 这里没有考虑平台价格比例 UnitPrice: jxutils.CaculateUnitPrice(v.RefPrice, v.SpecQuality, v.SpecUnit, v.Unit), } missingList = append(missingList, skuName) } skuName.Skus = append(skuName.Skus, &StoreSkuBindSkuInfo{ SkuID: v.SkuID, }) } } return missingList, err } func AutoSaleStoreSku(ctx *jxcontext.Context, storeIDs []int, isNeedSync bool) (err error) { db := dao.GetDB() storeSkuList, err := dao.GetAutoSaleStoreSku(db, storeIDs) if err != nil { return err } storeSkuMap := make(map[int][]*model.StoreSkuBind) for _, v := range storeSkuList { storeSkuMap[v.StoreID] = append(storeSkuMap[v.StoreID], v) } now := time.Now() for storeID, storeSkuList := range storeSkuMap { var skuIDs []int for _, storeSku := range storeSkuList { if now.Sub(storeSku.AutoSaleAt) > 0 { storeSku.AutoSaleAt = utils.DefaultTimeValue if storeSku.Status != model.SkuStatusNormal { storeSku.Status = model.SkuStatusNormal skuIDs = append(skuIDs, storeSku.SkuID) } if _, err = dao.UpdateEntity(db, storeSku, "AutoSaleAt", model.FieldStatus); err != nil { return err } } } if isNeedSync && len(skuIDs) > 0 { if _, err = CurVendorSync.SyncStoresSkus(ctx, nil, model.SyncFlagSaleMask, db, nil, []int{storeID}, skuIDs, false, true, true); err != nil { return err } } } return err } func ReCalculateJxPrice(db *dao.DaoDB, ctx *jxcontext.Context, storeIDs []int) (hint string, err error) { task := tasksch.NewParallelTask("刷新京西平台价格", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { storeID := batchItemList[0].(int) if storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJX, ""); err == nil { if storeSkuList, err := dao.GetStoresSkusInfo(db, []int{storeID}, nil); err == nil { for _, skuBind := range storeSkuList { skuBind.JxPrice = jxutils.CaculatePriceByPricePack(storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage), skuBind.Price) dao.UpdateEntity(db, skuBind, "JxPrice") } } else { return nil, err } } else { return nil, err } return retVal, err }, storeIDs) tasksch.HandleTask(task, nil, true).Run() hint = task.GetID() return hint, err } func ReCalculateJxPriceLight(db *dao.DaoDB, ctx *jxcontext.Context, storeID int) (err error) { if storeID != 0 { if storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJX, ""); err == nil { if storeSkuList, err := dao.GetStoresSkusInfo(db, []int{storeID}, nil); err == nil { for _, skuBind := range storeSkuList { skuBind.JxPrice = jxutils.CaculatePriceByPricePack(storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage), skuBind.Price) dao.UpdateEntity(db, skuBind) } } else { return err } } else { return err } } return err } func GetTopSkusByStoreIDs(ctx *jxcontext.Context, storeIDs []int) (storeSkuNameExt2 []*dao.StoreSkuNameExt, err error) { var ( db = dao.GetDB() skuMap = make(map[int]*dao.StoreSkuNameExt) ) if len(storeIDs) == 0 { return storeSkuNameExt2, err } storeSkuNameExt, err := dao.GetTopSkusByStoreIDs(db, storeIDs) if err != nil { return nil, err } for _, v := range storeSkuNameExt { if skuMap[v.SkuID] == nil { skuMap[v.SkuID] = v } if skuMap[v.SkuID] != nil && v.Count != 0 { skuMap[v.SkuID] = v } } for _, v := range skuMap { storeSkuNameExt2 = append(storeSkuNameExt2, v) } for i := 0; i < len(storeSkuNameExt2)-1; i++ { for j := 0; j < len(storeSkuNameExt2)-i-1; j++ { if storeSkuNameExt2[j].Count < storeSkuNameExt2[j+1].Count { tmp := storeSkuNameExt2[j] storeSkuNameExt2[j] = storeSkuNameExt2[j+1] storeSkuNameExt2[j+1] = tmp } } } return storeSkuNameExt2, err } func GetTopSkusByCityCode(ctx *jxcontext.Context, cityCode, storeID int) (skuNameAndPlaceList []*dao.SkuNameAndPlace, err error) { db := dao.GetDB() orderCreate := time.Now().AddDate(0, -1, 0) var skuNameAndPlace []*dao.SkuNameAndPlace if cityCode > 0 { skuNameAndPlace, err = dao.GetTopSkusByCityCode(db, cityCode, orderCreate) } else { skuNameAndPlace, err = dao.GetTopSkusByNoCityCode(db) } if storeID > 0 { var skuNameList []*model.SkuName //不可售的商品nameID列表 sql := ` SELECT DISTINCT b.name_id id FROM store_sku_bind a JOIN sku b ON a.sku_id = b.id AND b.deleted_at = ? WHERE a.deleted_at = ? AND a.store_id = ? AND a.status <> ? AND b.name_id NOT IN(SELECT DISTINCT b.name_id FROM store_sku_bind a JOIN sku b ON a.sku_id = b.id AND b.deleted_at = ? WHERE a.deleted_at = ? AND a.store_id = ? AND a.status = ?) ` sqlParams := []interface{}{ utils.DefaultTimeValue, utils.DefaultTimeValue, storeID, model.StoreSkuBindStatusNormal, utils.DefaultTimeValue, utils.DefaultTimeValue, storeID, model.StoreSkuBindStatusNormal, } err = dao.GetRows(db, &skuNameList, sql, sqlParams...) var skuNameMap = make(map[int]*model.SkuName) for _, v := range skuNameList { skuNameMap[v.ID] = v } store, err := dao.GetStoreDetail(db, storeID, -1, "") if err != nil { return nil, err } var payPercentage int if store.PayPercentage < 50 { payPercentage = 70 } else { payPercentage = store.PayPercentage } for _, v := range skuNameAndPlace { if skuNameMap[v.ID] != nil { var skuList []*model.SkuAndName storeSkuSyncInfo, _ := dao.GetStoreSkusAndSkuName(db, []int{storeID}, nil, []int{v.ID}) for _, storeSkuSync := range storeSkuSyncInfo { sku, _ := dao.GetSkus(db, []int{storeSkuSync.ID}, nil, nil, nil, nil) sku[0].Price = int(storeSkuSync.Price) skuList = append(skuList, sku...) } v.UnitPrice = int(storeSkuSyncInfo[0].UnitPrice) v.Skus = skuList for _, vv := range skuList { var ( storeSkuNameExt []*dao.StoreSkuNameExt skusList []*dao.StoreSkuExt ) vv.StoreSkuStatus = model.StoreSkuBindStatusDontSale skus := &dao.StoreSkuExt{ SkuID: vv.ID, BindPrice: vv.Price, } skusList = append(skusList, skus) storeSkuName := &dao.StoreSkuNameExt{ Skus: skusList, StoreID: storeID, PayPercentage: payPercentage, } storeSkuNameExt = append(storeSkuNameExt, storeSkuName) skuNamesInfo := &dao.StoreSkuNamesInfo{ SkuNames: storeSkuNameExt, } dao.UpdateActPrice4StoreSkuNameNew(db, []int{storeID}, []int{vv.ID}, skuNamesInfo, -1) if skuNamesInfo.SkuNames[0].Skus[0] != nil { vv.ActPrice = skuNamesInfo.SkuNames[0].Skus[0].ActPrice vv.ActID = skuNamesInfo.SkuNames[0].Skus[0].ActID vv.ActType = skuNamesInfo.SkuNames[0].Skus[0].ActType vv.EarningPrice = skuNamesInfo.SkuNames[0].Skus[0].EarningPrice vv.EarningActID = skuNamesInfo.SkuNames[0].Skus[0].EarningActID } } skuNameAndPlaceList = append(skuNameAndPlaceList, v) } } } else { skuNameAndPlaceList = append(skuNameAndPlaceList, skuNameAndPlace...) } i := 1 for _, v := range skuNameAndPlaceList { v.Sequence = i i++ } if len(skuNameAndPlaceList) <= 100 { return skuNameAndPlaceList, err } else { return skuNameAndPlaceList[0:100], err } } func GetTopCategoriesByStoreIDs(ctx *jxcontext.Context, storeIDs []int) (skuCategory []*model.SkuCategory, err error) { var ( skuCategory2 []*model.SkuCategory skuCategoryMap = make(map[int]*model.SkuCategory) limit = 10 ) if len(storeIDs) == 0 { return skuCategory, err } db := dao.GetDB() skuCategory, err = dao.GetTopCategoriesByStoreIDs(db, storeIDs, limit) //若大于等于10个就不用做下面的操作 if len(skuCategory) >= limit { return skuCategory, err } if len(skuCategory) > 0 { for _, v := range skuCategory { skuCategoryMap[v.ID] = v } } //推荐分类,若不满10个,则填满10个 if (len(skuCategory) < limit && len(skuCategory) > 0) || len(skuCategory) == 0 { skuCategory2, err = dao.GetCategories(db, -1, 1, nil, false) if len(skuCategory2) > 0 { for _, v := range skuCategory2 { if skuCategoryMap[v.ID] == nil { if !strings.Contains(v.Name, "赠品") { skuCategory = append(skuCategory, v) } } if len(skuCategory) >= limit { break } } } } if err != nil { return nil, err } return skuCategory, err } func RefershStoreSkusMidPrice(ctx *jxcontext.Context, storeIDs []int, isCountry bool) (err error) { db := dao.GetDB() if len(storeIDs) == 0 { return err } for _, v := range storeIDs { var skuBindInfos []*StoreSkuBindInfo store, err := dao.GetStoreDetail(db, v, -1, "") if err != nil { return err } var payPercentage int if store.PayPercentage < 50 { payPercentage = 70 } else { payPercentage = store.PayPercentage } storeSkuList, err := dao.GetStoresSkusInfo(db, []int{v}, nil) for _, storeSku := range storeSkuList { var priceReferList []*model.PriceReferSnapshot if isCountry { priceReferList, err = dao.GetPriceReferSnapshotNoPage(db, []int{0}, []int{storeSku.SkuID}, nil, utils.Time2Date(time.Now().AddDate(0, 0, -1))) } else { priceReferList, err = dao.GetPriceReferSnapshotNoPage(db, []int{store.CityCode}, []int{storeSku.SkuID}, nil, utils.Time2Date(time.Now().AddDate(0, 0, -1))) } if err != nil { return err } if len(priceReferList) > 0 { //TODO 高于中位价20%才刷, 2020-05-08 if priceReferList[0].MidUnitPrice >= 500 { if storeSku.UnitPrice > priceReferList[0].MidUnitPrice/payPercentage*120 { skuBindInfo := &StoreSkuBindInfo{ NameID: priceReferList[0].NameID, UnitPrice: priceReferList[0].MidUnitPrice / payPercentage * 120, } skuBindInfos = append(skuBindInfos, skuBindInfo) } } } } updateStoresSkusWithoutSync(ctx, db, []int{v}, skuBindInfos, false, false) } if err == nil { CreateStorePriceScore(ctx) } return err } func RefreshJxPriceByExcel(ctx *jxcontext.Context, storeIDs []int, files []*multipart.FileHeader, isAsync, isContinueWhenError bool) (hint string, err error) { if len(files) == 0 { return "", errors.New("没有文件上传!") } if len(storeIDs) == 0 { return "", errors.New("请选择至少一个门店!") } fileHeader := files[0] file, err := fileHeader.Open() hint, err = RefreshJxPriceByExcelBin(ctx, storeIDs, file, true, true) file.Close() return hint, err } func RefreshJxPriceByExcelBin(ctx *jxcontext.Context, storeIDs []int, reader io.Reader, isAsync, isContinueWhenError bool) (hint string, err error) { var ( storeSkuNamePriceList []*model.StoreSkuNamePrice storeSkuNamePriceListUpdate []*model.StoreSkuNamePrice skuBindInfosInter []interface{} skuBindInfoList []*StoreSkuBindInfo errMsg string isErr bool = false nameMap = make(map[string]string) ) dataLock.dataFailedList = dataLock.dataFailedList[0:0] dataLock.dataSuccessList = dataLock.dataSuccessList[0:0] sheetParam := &SheetParam{ SkuNameIDCol: 5, SkuPriceCol: 3, SkuNameCol: 1, SkuRow: 1, SkuUnitCol: 2, OutSkuIDCol: 0, } taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: // xlsx, err := excelize.OpenFile("111.xlsx") xlsx, err := excelize.OpenReader(reader) if err != nil { return "", err } rows, _ := xlsx.GetRows(xlsx.GetSheetName(1)) for rowNum, row := range rows { if rowNum < sheetParam.SkuRow { continue } storeSkuNamePrice := &model.StoreSkuNamePrice{} errMsg += GetCellIntoStruct(rowNum, row, sheetParam, storeSkuNamePrice, nameMap) storeSkuNamePriceList = append(storeSkuNamePriceList, storeSkuNamePrice) } if errMsg != "" { return "", errors.New(errMsg) } else { isErr = true } case 1: db := dao.GetDB() storeSkuNamePriceListOrg, _ := dao.GetStoreSkuNamePrice(db) CreateOrUpdateStoreSkuNamePriceByExcel(db, ctx, storeSkuNamePriceList, storeSkuNamePriceListOrg) storeSkuNamePriceListNew, _ := dao.GetStoreSkuNamePrice(db) storeSkuNamePriceMapNew := StoreSkuNamePriceList2Map(ctx, storeSkuNamePriceListNew) for _, v := range storeSkuNamePriceList { if storeSkuNamePriceMapNew[v.OutSkuID] != nil { storeSkuNamePriceListUpdate = append(storeSkuNamePriceListUpdate, storeSkuNamePriceMapNew[v.OutSkuID]) } } taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { storeSkuNamePrice := batchItemList[0].(*model.StoreSkuNamePrice) var skuBindInfos []*StoreSkuBindInfo nameIDGroup := strings.Split(storeSkuNamePrice.NameIDGroup, ",") for _, v := range nameIDGroup { if v != "" { nameID := int(utils.Str2Int64(v)) for _, vv := range storeIDs { skuList, err2 := dao.GetStoreSkusByNameIDs(db, []int{vv}, nameID) err = err2 if len(skuList) > 0 { unitPrice := 0 if skuList[0].Unit == model.UnitNames[0] { if storeSkuNamePrice.Unit == "KG" { unitPrice = storeSkuNamePrice.Price / 2 } else { unitPrice = storeSkuNamePrice.Price } } else { unitPrice = storeSkuNamePrice.Price } storeSkuBindInfo := &StoreSkuBindInfo{ NameID: nameID, UnitPrice: unitPrice, } skuBindInfos = append(skuBindInfos, storeSkuBindInfo) outSuccess := DataSuccess{ NameID: nameID, Name: skuList[0].Name, Unit: storeSkuNamePrice.Unit, OrgPrice: utils.Str2Float64(utils.Int64ToStr(skuList[0].UnitPrice)) / 100, NowPrice: utils.Str2Float64(utils.Int64ToStr(int64(unitPrice))) / 100, MixPrice: utils.Str2Float64(utils.Int64ToStr(int64(unitPrice)-skuList[0].UnitPrice)) / 100, } dataLock.AppendDataSuccess(outSuccess) } else { //京西xx门店没有关注该商品 outFailed := DataFailed{ NameID: nameID, Name: storeSkuNamePrice.Name, Comment: fmt.Sprintf("京西[%v]门店没有关注该商品,商品nameID[%v],Excel上商品名[%v]", vv, nameID, storeSkuNamePrice.Name), } dataLock.AppendDataFailed(outFailed) } } } else { //nameID为空,还未填写nameID outFailed := DataFailed{ NameID: 0, Name: storeSkuNamePrice.Name, Comment: fmt.Sprintf("商品nameID为空!,还未填写商品nameID,Excel上商品名[%v]", storeSkuNamePrice.Name), } dataLock.AppendDataFailed(outFailed) } } retVal = skuBindInfos return retVal, err } taskParallel := tasksch.NewParallelTask("刷新京西价", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, storeSkuNamePriceListUpdate) tasksch.HandleTask(taskParallel, task, true).Run() skuBindInfosInter, err = taskParallel.GetResult(0) case 2: //更新京西价 for _, v := range skuBindInfosInter { skuBindInfoList = append(skuBindInfoList, v.(*StoreSkuBindInfo)) } if isErr { UpdateStoresSkus(ctx, 0, storeIDs, skuBindInfoList, false, false, isAsync, isContinueWhenError) } case 3: //写Excel WriteToExcelJx(task, dataLock.dataSuccessList, dataLock.dataFailedList) } return result, err } taskSeq := tasksch.NewSeqTask2("根据Excel刷新京西价", ctx, isContinueWhenError, taskSeqFunc, 4) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func WriteToExcelJx(task *tasksch.SeqTask, dataSuccess []DataSuccess, dataFailed []DataFailed) (err error) { var sheetList1 []*excel.Obj2ExcelSheetConfig var sheetList2 []*excel.Obj2ExcelSheetConfig var downloadURL1, downloadURL2, fileName1, fileName2 string excelConf1 := &excel.Obj2ExcelSheetConfig{ Title: "sheet1", Data: dataSuccess, CaptionList: titleListSuccess, } sheetList1 = append(sheetList1, excelConf1) excelConf2 := &excel.Obj2ExcelSheetConfig{ Title: "sheet1", Data: dataFailed, CaptionList: titleListFailed, } sheetList2 = append(sheetList2, excelConf2) if excelConf1 != nil { downloadURL1, fileName1, err = jxutils.UploadExeclAndPushMsg(sheetList1, "京西已更新商品") } else { baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!") } if excelConf2 != nil { downloadURL2, fileName2, err = jxutils.UploadExeclAndPushMsg(sheetList2, "未更新商品") } else { baseapi.SugarLogger.Debug("WriteToExcel: dataFailed is nil!") } if err != nil { baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName1, fileName2, err) } else { noticeMsg := fmt.Sprintf("[详情点我]path1=%s, path2=%s \n", downloadURL1, downloadURL2) task.SetNoticeMsg(noticeMsg) baseapi.SugarLogger.Debugf("WriteToExcel:upload %s ,%s success, downloadURL1:%s ,downloadURL2:%s", fileName1, fileName2, downloadURL1, downloadURL2) } return err } func CreateOrUpdateStoreSkuNamePriceByExcel(db *dao.DaoDB, ctx *jxcontext.Context, storeSkuNamePriceList []*model.StoreSkuNamePrice, storeSkuNamePriceListOrg []*model.StoreSkuNamePrice) (err error) { storeSkuNamePriceMap := StoreSkuNamePriceList2Map(ctx, storeSkuNamePriceListOrg) txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil || err != nil { dao.Rollback(db, txDB) if r != nil { panic(r) } } }() for _, v := range storeSkuNamePriceList { dao.WrapAddIDCULDEntity(v, ctx.GetUserName()) if storeSkuNamePriceMap[v.OutSkuID] != nil { v.ID = storeSkuNamePriceMap[v.OutSkuID].ID v.UpdatedAt = time.Now() dao.UpdateEntity(db, v) } else { dao.CreateEntity(db, v) } } dao.Commit(db, txDB) return err } func StoreSkuNamePriceList2Map(ctx *jxcontext.Context, storeSkuNamePriceList []*model.StoreSkuNamePrice) (result map[string]*model.StoreSkuNamePrice) { result = make(map[string]*model.StoreSkuNamePrice, len(storeSkuNamePriceList)) for _, v := range storeSkuNamePriceList { result[v.OutSkuID] = v } return result } func GetCellIntoStruct(rowNum int, row []string, sheetParam *SheetParam, storeSkuNamePrice *model.StoreSkuNamePrice, nameMap map[string]string) (errMsg string) { for k, cell := range row { if k == sheetParam.OutSkuIDCol { if IsChineseChar(cell) { return fmt.Sprintf("Excel格式排版发生了变化!在[%v]列,[%v]行附近可能增加或减少了一列", k+1, rowNum+1) } storeSkuNamePrice.OutSkuID = cell } if k == sheetParam.SkuNameCol { storeSkuNamePrice.Name = cell } if k == sheetParam.SkuNameIDCol { cellReplace := strings.ReplaceAll(cell, ",", ",") if cellReplace != "" { if cellReplace[len(cellReplace)-1:len(cellReplace)] == "," { cellReplace = cellReplace[0 : len(cellReplace)-1] } nameIDs := strings.Split(cellReplace, ",") for _, v := range nameIDs { if nameMap[v] != "" { return fmt.Sprintf(" Excel中含有重复的nameID![%v]列,[%v]行,nameID [%v]\n", k+1, rowNum+1, v) } else { nameMap[v] = v } } } storeSkuNamePrice.NameIDGroup = cellReplace } if k == sheetParam.SkuPriceCol { if IsChineseChar(cell) { return fmt.Sprintf("Excel格式排版发生了变化!在[%v]列,[%v]行附近可能增加或减少了一列", k+1, rowNum+1) } storeSkuNamePrice.Price = int(utils.Float64TwoInt64(utils.Str2Float64(cell) * 100)) } if k == sheetParam.SkuUnitCol { storeSkuNamePrice.Unit = cell } } return errMsg } func (d *DataLock) AppendDataSuccess(dataSuccess DataSuccess) { d.locker.Lock() defer d.locker.Unlock() d.dataSuccessList = append(d.dataSuccessList, dataSuccess) } func (d *DataLock) AppendDataFailed(dataFailed DataFailed) { d.locker.Lock() defer d.locker.Unlock() d.dataFailedList = append(d.dataFailedList, dataFailed) } func IsChineseChar(str string) bool { for _, r := range str { if unicode.Is(unicode.Scripts["Han"], r) { return true } } return false } func GetStoreCategories(ctx *jxcontext.Context, storeID, parentID int) (catList []*model.SkuCategory, err error) { return dao.GetStoreSkuCategories(dao.GetDB(), storeID, parentID) } func GetVendorStoreSkuPrice(ctx *jxcontext.Context, vendorIDs []int, skuID int, isAsync, isContinueWhenError bool) (hint string, err error) { var ( storeSkuListJD []DataVendorStoreSkuPrice storeSkuListMT []DataVendorStoreSkuPrice storeSkuListEB []DataVendorStoreSkuPrice excelParamList []ExcelParam ) taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: for _, v := range vendorIDs { vendorID := v handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler) handlerStore := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IStoreHandler) for _, v := range apimanager.CurAPIManager.GetAppOrgCodeList(vendorID) { vendorStoreIDs, err2 := handlerStore.GetAllStoresVendorID(ctx, v) err = err2 taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { vendorStoreID := batchItemList[0].(string) var ( inStoreSkuList []*partner.StoreSkuInfo storeDetail *dao.StoreDetail inStoreSku = &partner.StoreSkuInfo{} outStoreSkuList []*partner.StoreSkuInfo ) db := dao.GetDB() skuList, err := dao.GetSkusWithVendor(db, []int{vendorID}, []string{v}, nil, []int{skuID}, false) if err != nil { return retVal, err } if partner.IsMultiStore(vendorID) { multiHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IMultipleStoresHandler) storeDetail, err = multiHandler.ReadStore(ctx, v, vendorStoreID) if len(skuList) > 0 { inStoreSku.VendorSkuID = skuList[0].VendorSkuID } } else { singleHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreHandler) storeDetail, err = singleHandler.ReadStore(ctx, v, vendorStoreID) inStoreSku.SkuID = skuID } inStoreSkuList = append(inStoreSkuList, inStoreSku) storeDetail2, err := dao.GetStoreDetailByVendorStoreID(db, vendorStoreID, vendorID, "") if storeDetail2 != nil { outStoreSkuList, err = handler.GetStoreSkusBareInfo(ctx, v, task, storeDetail2.ID, vendorStoreID, inStoreSkuList) } else { outStoreSkuList, err = handler.GetStoreSkusBareInfo(ctx, v, task, 0, vendorStoreID, inStoreSkuList) } if storeDetail != nil { if len(outStoreSkuList) == 0 { data := DataVendorStoreSkuPrice{ StoreID: vendorStoreID, StoreName: storeDetail.Name, SkuID: skuID, SkuName: skuList[0].Name, VendorPrice: "", } retVal = []DataVendorStoreSkuPrice{data} } else { data := DataVendorStoreSkuPrice{ StoreID: vendorStoreID, StoreName: storeDetail.Name, SkuID: skuID, SkuName: skuList[0].Name, VendorPrice: utils.Float64ToStr(utils.Str2Float64(utils.Int64ToStr(outStoreSkuList[0].VendorPrice)) / 100), } retVal = []DataVendorStoreSkuPrice{data} } } return retVal, err } taskParallel := tasksch.NewParallelTask("获取各平台所有门店某商品价格", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, vendorStoreIDs) tasksch.HandleTask(taskParallel, task, true).Run() storeSkuList, _ := taskParallel.GetResult(0) for _, v := range storeSkuList { if vendorID == model.VendorIDJD { storeSkuListJD = append(storeSkuListJD, v.(DataVendorStoreSkuPrice)) } if vendorID == model.VendorIDEBAI { storeSkuListEB = append(storeSkuListEB, v.(DataVendorStoreSkuPrice)) } if vendorID == model.VendorIDMTWM { storeSkuListMT = append(storeSkuListMT, v.(DataVendorStoreSkuPrice)) } } } } excelParam1 := ExcelParam{ DataList: storeSkuListJD, TitleList: titleListVendorStoreSkuPrice, SheetName: "京东平台", } excelParam2 := ExcelParam{ DataList: storeSkuListEB, TitleList: titleListVendorStoreSkuPrice, SheetName: "饿百平台", } excelParam3 := ExcelParam{ DataList: storeSkuListMT, TitleList: titleListVendorStoreSkuPrice, SheetName: "美团平台", } excelParamList = append(excelParamList, excelParam1, excelParam2, excelParam3) case 1: WriteToExcelNormal(task, "各平台"+utils.Int2Str(skuID)+"商品所有门店价格", excelParamList) } return result, err } taskSeq := tasksch.NewSeqTask2("获取各平台所有门店某商品价格-序列任务", ctx, isContinueWhenError, taskSeqFunc, 2) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func WriteToExcelNormal(task *tasksch.SeqTask, fileName string, excelParam []ExcelParam) (err error) { var sheetList []*excel.Obj2ExcelSheetConfig for _, v := range excelParam { if v.DataList != nil { excelConf := &excel.Obj2ExcelSheetConfig{ Title: v.SheetName, Data: v.DataList, CaptionList: v.TitleList, } sheetList = append(sheetList, excelConf) } } downloadURL, fileNameResult, err := jxutils.UploadExeclAndPushMsg(sheetList, fileName) if err != nil { baseapi.SugarLogger.Errorf("WriteToExcel:upload %s failed error:%v", fileNameResult, err) } else { noticeMsg := fmt.Sprintf("[详情点我]path=%s \n", downloadURL) task.SetNoticeMsg(noticeMsg) baseapi.SugarLogger.Debugf("WriteToExcel:upload %s success, downloadURL:%s", fileNameResult, downloadURL) } return err } func FocusStoreSkusByExcel(ctx *jxcontext.Context, files []*multipart.FileHeader, isAsync, isContinueWhenError bool) (hint string, err error) { if len(files) == 0 { return "", errors.New("没有文件上传!") } fileHeader := files[0] file, err := fileHeader.Open() hint, err = FocusStoreSkusByExcelBin(ctx, file, isAsync, isContinueWhenError) file.Close() return hint, err } func FocusStoreSkusByExcelBin(ctx *jxcontext.Context, reader io.Reader, isAsync, isContinueWhenError bool) (hint string, err error) { var ( skuMap = make(map[int]int) db = dao.GetDB() skuIDs []int result1 []interface{} ) sheetParam := &SheetParam{ OutSkuIDCol: 1, SkuPriceCol: 3, SkuRow: 1, } taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: // xlsx, err := excelize.OpenFile("111.xlsx") xlsx, err := excelize.OpenReader(reader) if err != nil { return result, err } rows, _ := xlsx.GetRows(xlsx.GetSheetName(1)) for rowNum, row := range rows { if rowNum < sheetParam.SkuRow { continue } GetCellForFocusStoreSkus(db, rowNum, row, sheetParam, skuMap) } case 1: for k, _ := range skuMap { skuIDs = append(skuIDs, k) } skuList, err := dao.GetSkus(db, skuIDs, nil, nil, nil, nil) storeList, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "") if err != nil && len(skuList) == 0 { return result, err } taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { store := batchItemList[0].(*model.Store) var ( skuNameMap = make(map[int]int) skuInfoMap = make(map[int][]*StoreSkuBindSkuInfo) ) for _, v := range skuList { var ( price int specQuality float64 ) focusList, _ := dao.GetStoreSkuBindByNameID(db, store.ID, v.NameID, model.StoreSkuBindStatusNormal) //有关注过 if len(focusList) > 0 { price = focusList[0].UnitPrice skuInfoMap[v.NameID] = append(skuInfoMap[v.NameID], &StoreSkuBindSkuInfo{ SkuID: v.ID, IsSale: 1, }) skuNameMap[v.NameID] = price } else { if v.Unit == model.SpecialUnit { if v.SpecUnit == model.SpecUnitNames[1] || v.SpecUnit == model.SpecUnitNames[2] { specQuality = float64(v.SpecQuality) * 1000 } else { specQuality = float64(v.SpecQuality) } price = int(utils.Float64TwoInt64(utils.Int2Float64(model.SpecialSpecQuality) / specQuality * utils.Int2Float64(skuMap[v.ID]))) } else { price = skuMap[v.ID] } if skuNameMap[v.NameID] < price { skuNameMap[v.NameID] = price } } } var storeSkuBindInfo []*StoreSkuBindInfo for k, v := range skuNameMap { skuBindInfo := &StoreSkuBindInfo{ StoreID: store.ID, NameID: k, UnitPrice: v, IsFocus: 1, IsSale: 1, Skus: skuInfoMap[k], } storeSkuBindInfo = append(storeSkuBindInfo, skuBindInfo) } retVal = storeSkuBindInfo return retVal, err } taskParallel := tasksch.NewParallelTask("根据skuID关注商品", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, storeList) tasksch.HandleTask(taskParallel, task, true).Run() result1, _ = taskParallel.GetResult(0) case 2: var skuBindInfos []*StoreSkuBindInfo for _, v := range result1 { skuBindInfos = append(skuBindInfos, v.(*StoreSkuBindInfo)) } UpdateStoresSkusByBind(ctx, task, skuBindInfos, true, true, false) } return result, err } taskSeq := tasksch.NewSeqTask2("根据Excel中skuID批量关注商品", ctx, isContinueWhenError, taskSeqFunc, 3) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func GetCellForFocusStoreSkus(db *dao.DaoDB, rowNum int, row []string, sheetParam *SheetParam, skuMap map[int]int) { var ( skuID int price int ) for k, cell := range row { if k == sheetParam.OutSkuIDCol { skuID = int(utils.Str2Int64(cell)) } if k == sheetParam.SkuPriceCol { price = int(utils.Float64TwoInt64(utils.Str2Float64(cell) * 100)) } } skuMap[skuID] = price } func FocusStoreSkusBySku(ctx *jxcontext.Context, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) { var result1 []interface{} db := dao.GetDB() taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { var ( price int payPercentage int ) skuBindInfo := batchItemList[0].(*StoreSkuBindInfo) storeID := skuBindInfo.StoreID skuID := skuBindInfo.Skus[0].SkuID store, err := dao.GetStoreDetail(db, storeID, -1, "") if len(skuBindInfos) > 1 { if store.StoreLevel == "D" || store.StoreLevel == "E" { return retVal, err } } if err != nil { return retVal, err } skuList, err := dao.GetSkus(db, []int{skuID}, nil, nil, nil, nil) if err != nil { return retVal, err } if len(skuList) == 0 { return retVal, fmt.Errorf("未查询到此商品!商品id :[%d]", skuID) } focusList, _ := dao.GetStoreSkuBindByNameID(db, storeID, skuList[0].NameID, model.StoreSkuBindStatusNormal) //有关注过 if len(focusList) > 0 { price = focusList[0].UnitPrice } else { if store.PayPercentage < 50 { payPercentage = 70 } else { payPercentage = store.PayPercentage } priceReferList, _ := dao.GetPriceReferSnapshotNoPage(db, []int{0}, nil, []int{skuList[0].NameID}, utils.Time2Date(time.Now().AddDate(0, 0, -1))) if len(priceReferList) > 0 { if payPercentage == 100 { price = priceReferList[0].MidUnitPrice * payPercentage / 100 } else { price = priceReferList[0].MidUnitPrice / payPercentage * 100 } } } skuBindSkuInfo := &StoreSkuBindSkuInfo{ SkuID: skuID, IsSale: 1, } var skuBindSkuInfos []*StoreSkuBindSkuInfo skuBindSkuInfos = append(skuBindSkuInfos, skuBindSkuInfo) skuBindInfo2 := &StoreSkuBindInfo{ StoreID: storeID, NameID: skuList[0].NameID, UnitPrice: price, IsFocus: 1, Skus: skuBindSkuInfos, } retVal = []*StoreSkuBindInfo{skuBindInfo2} return retVal, err } taskParallel := tasksch.NewParallelTask("根据skuID部分关注商品", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, skuBindInfos) tasksch.HandleTask(taskParallel, task, true).Run() result1, _ = taskParallel.GetResult(0) case 1: var skuBindInfos []*StoreSkuBindInfo for _, v := range result1 { skuBindInfos = append(skuBindInfos, v.(*StoreSkuBindInfo)) } hint, err = UpdateStoresSkusByBind(ctx, task, skuBindInfos, isAsync, isContinueWhenError, true) } return result, err } taskSeq := tasksch.NewSeqTask2("根据skuID批量部分关注商品", ctx, isContinueWhenError, taskSeqFunc, 2) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func AutoFocusStoreSkusForTopSkus(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) { db := dao.GetDB() var ( result1 []interface{} ) storeList, err := dao.GetStoreList(db, nil, nil, nil, []int{1}, nil, "") taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { v := batchItemList[0].(*model.Store) var ( skuName []*model.SkuName skuNameMap = make(map[int]int) ) //物料店不自动关注畅销品 if autoNotFoucsStoreMap[v.ID] != 0 { return retVal, err } sql := ` SELECT DISTINCT a.name_id id FROM sku a LEFT JOIN (SELECT DISTINCT b.name_id FROM store_sku_bind a JOIN sku b ON a.sku_id = b.id WHERE a.deleted_at = ? AND store_id = ?)b ON a.name_id = b.name_id WHERE a.status = ? AND a.deleted_at = ? AND b.name_id IS NULL ` sqlParams := []interface{}{ utils.DefaultTimeValue, v.ID, model.SkuStatusNormal, utils.DefaultTimeValue, } err = dao.GetRows(db, &skuName, sql, sqlParams...) for _, name := range skuName { skuNameMap[name.ID] = name.ID } skuNameAndPlaceList, err2 := GetTopSkusByCityCode(ctx, 0, 0) //skuNameAndPlaceList, err2 := GetTopSkusByCityCode(ctx, v.CityCode, 0) if err2 != nil { return retVal, err2 } var payPercentage int if v.PayPercentage < 50 { payPercentage = 70 } else { payPercentage = v.PayPercentage } if len(skuNameAndPlaceList) > 0 { var skuBindInfoList []*StoreSkuBindInfo for _, vv := range skuNameAndPlaceList { if skuNameMap[vv.ID] != 0 { globals.SugarLogger.Debugf("testAutoFocusStoreSkusForTopSkus,storeID:%v,nameID:%v", v.ID, vv.ID) storeSkuBindInfo := &StoreSkuBindInfo{ StoreID: v.ID, NameID: vv.ID, // UnitPrice: priceReferList[0].MidUnitPrice * payPercentage / 100, IsFocus: 1, IsSale: 0, } priceReferList, err := dao.GetPriceReferSnapshotNoPage(db, []int{vv.CityCode}, nil, []int{vv.ID}, utils.Time2Date(time.Now().AddDate(0, 0, -1))) if err == nil && len(priceReferList) > 0 { storeSkuBindInfo.UnitPrice = priceReferList[0].MidUnitPrice / payPercentage * 100 } skuBindInfoList = append(skuBindInfoList, storeSkuBindInfo) } } retVal = skuBindInfoList } return retVal, err } taskParallel := tasksch.NewParallelTask("自动关注畅销品-设置数据", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, storeList) tasksch.HandleTask(taskParallel, task, true).Run() result1, _ = taskParallel.GetResult(0) case 1: var skuBindInfos []*StoreSkuBindInfo for _, v := range result1 { skuBindInfos = append(skuBindInfos, v.(*StoreSkuBindInfo)) } hint, err = UpdateStoresSkusByBind(ctx, nil, skuBindInfos, isAsync, isContinueWhenError, false) } return result, err } taskSeq := tasksch.NewSeqTask2("自动关注畅销品", ctx, isContinueWhenError, taskSeqFunc, 2) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func AutoFocusStoreSkusWithoutFocus(ctx *jxcontext.Context, skuIDs []int, isSync bool) (err error) { var ( nameMap = make(map[int]*StoreSkuBindInfo) ) db := dao.GetDB() storeList, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "") for _, v := range storeList { storeSkuList, _ := dao.GetStoreSkusAndSkuName(db, []int{v.ID}, skuIDs, nil) for _, vv := range storeSkuList { if nameMap[vv.ID] != nil { nameMap[vv.ID].Skus = append(nameMap[vv.ID].Skus, &StoreSkuBindSkuInfo{ SkuID: vv.SkuID, }) } else { skuBindInfo := &StoreSkuBindInfo{ UnitPrice: int(vv.UnitPrice), NameID: vv.NameID, StoreID: v.ID, Skus: []*StoreSkuBindSkuInfo{}, } nameMap[vv.ID] = skuBindInfo } } } for _, v := range nameMap { var skuBindInfoList []*StoreSkuBindInfo skuBindInfoResult := &StoreSkuBindInfo{ NameID: v.NameID, UnitPrice: v.UnitPrice, IsFocus: 1, } var skuBindSkuList []*StoreSkuBindSkuInfo skuMap := make(map[int]int) skuList, _ := dao.GetSkus(db, nil, []int{v.NameID}, nil, nil, nil) if len(v.Skus) != len(skuList) { for _, skus := range v.Skus { skuMap[skus.SkuID] = 1 } for _, vv := range skuList { if skuMap[vv.ID] != 1 { continue } skuBindSkuList = append(skuBindSkuList, &StoreSkuBindSkuInfo{ SkuID: vv.ID, IsSale: 0, }) } } skuBindInfoList = append(skuBindInfoList, skuBindInfoResult) if isSync { UpdateStoreSkus(ctx, 0, v.StoreID, skuBindInfoList, true, true) } else { updateStoresSkusWithoutSync(ctx, db, []int{v.StoreID}, skuBindInfoList, false, false) } } return err } func UpdateStoreSkusSpecTag(ctx *jxcontext.Context, vendorIDs []int, files []*multipart.FileHeader, isAsync, isContinueWhenError bool) (hint string, err error) { if len(files) == 0 { return "", errors.New("没有文件上传!") } fileHeader := files[0] file, err := fileHeader.Open() hint, err = UpdateStoreSkusSpecTagBin(ctx, file, vendorIDs, isAsync, isContinueWhenError) file.Close() return hint, err } func UpdateStoreSkusSpecTagBin(ctx *jxcontext.Context, reader io.Reader, vendorIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { var ( db = dao.GetDB() results []*tUpdateSkuSpecTag ) taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: // xlsx, err := excelize.OpenFile("111.xlsx") xlsx, err := excelize.OpenReader(reader) if err != nil { return result, err } rows, _ := xlsx.GetRows(xlsx.GetSheetName(1)) for rowNum, row := range rows { if rowNum < 1 { continue } var ( skuMap = &tUpdateSkuSpecTag{} storeID int skuID int isSpec int ) for k, cell := range row { if cell != "" { if k == 0 { storeID = int(utils.Str2Int64(cell)) } if k == 1 { skuID = int(utils.Str2Int64(cell)) } if k == 2 { isSpec = int(utils.Str2Int64(cell)) } } } skuMap.SkuID = skuID skuMap.IsSpec = isSpec skuMap.StoreID = storeID results = append(results, skuMap) } case 1: for i := 0; i < len(results)/2; i++ { tmp := results[i] results[i] = results[len(results)-i-1] results[len(results)-i-1] = tmp } //权限 if permission.IsRoled(ctx) { if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil { for _, v := range results { if storeIDsMap[v.StoreID] == 0 { continue } store, err := dao.GetStoreDetail(db, v.StoreID, model.VendorIDMTWM, "") if err != nil || store == nil { continue } mtapi := apimanager.CurAPIManager.GetAPI(model.VendorIDMTWM, store.VendorOrgCode).(*mtwmapi.API) food, err := mtapi.RetailGet(store.VendorStoreID, utils.Int2Str(v.SkuID)) if err != nil || food == nil { continue } var foodData = make(map[string]interface{}) if v.IsSpec != 0 && v.IsSpec == -1 { v.IsSpec = 0 } foodData["is_specialty"] = v.IsSpec foodData["price"] = food.Price if globals.EnableMtwmStoreWrite { err = mtapi.RetailInitData(ctx.GetTrackInfo(), store.VendorStoreID, utils.Int2Str(v.SkuID), foodData) } } } } } return result, err } taskSeq := tasksch.NewSeqTask2("修改平台力荐商品", ctx, isContinueWhenError, taskSeqFunc, 2) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func SendSeckillSkusCountMsg(ctx *jxcontext.Context, vendorIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) { // 1. 如果爆品低于8个,报警 type1 // 2. 爆品价格低于1元商品小于5个,报警 type2 var ( type1Count = 8 type2Count = 5 ddMsgresult []interface{} ) db := dao.GetDB() storeList, err := dao.GetStoresMapList(db, vendorIDs, nil, []int{model.StoreStatusClosed, model.StoreStatusHaveRest, model.StoreStatusOpened}, model.StoreStatusOpened, model.StoreIsSyncAll, "", "", "") taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { store := batchItemList[0].(*model.StoreMap) var type1, type2 int switch store.VendorID { case model.VendorIDEBAI: result, _ := api.EbaiAPI.GetStoresShowWindowSkus(utils.Str2Int64(store.VendorStoreID)) for _, v := range result { type1++ if v.SalePrice < 1 { type2++ } } case model.VendorIDMTWM: handler := partner.GetPurchasePlatformFromVendorID(store.VendorID).(partner.ISingleStoreStoreSkuHandler) remoteSkuList, err := handler.GetStoreSkusFullInfo(ctx, nil, store.StoreID, store.VendorStoreID, nil) if err != nil { return retVal, err } for _, v := range remoteSkuList { for _, vv := range v.SkuList { if vv.IsSpecialty == 1 { type1++ } if vv.IsSpecialty == 1 && vv.VendorPrice < 100 { type2++ } } } case model.VendorIDJD: var storeSecKill []*tStoreSkusSecKill sql := ` SELECT t1.store_id,count(*) sec_kill_count, count(t1.price < 100 or NULL) sec_kill_count2 FROM( SELECT a.store_id, a.sku_id, d.type, MIN(e.actual_act_price) price FROM store_sku_bind a LEFT JOIN act_store_sku b ON a.store_id = b.store_id AND b.sku_id = a.sku_id LEFT JOIN act_map c ON c.act_id = b.act_id LEFT JOIN act d ON d.id = c.act_id LEFT JOIN act_store_sku_map e ON e.store_id = a.store_id AND e.sku_id = a.sku_id AND e.vendor_id = c.vendor_id AND e.act_id = d.id WHERE 1=1 AND a.store_id = ? AND c.vendor_id = ? AND NOW() BETWEEN d.begin_at AND d.end_at AND d.type = ? AND a.status = ? AND a.deleted_at = ? AND e.actual_act_price <> 0 GROUP BY 1,2,3)t1 GROUP BY 1 ` sqlParams := []interface{}{ store.StoreID, store.VendorID, model.ActSkuSecKill, model.StoreSkuBindStatusNormal, utils.DefaultTimeValue, } err = dao.GetRows(db, &storeSecKill, sql, sqlParams...) if len(storeSecKill) > 0 { type1 = storeSecKill[0].SecKillCount type2 = storeSecKill[0].SecKillCount2 } else { type1 = 0 type2 = 0 } } if type1 < type1Count || type2 < type2Count { storeDetail, _ := dao.GetStoreDetail(db, store.StoreID, store.VendorID, "") var ( type1Str = "爆品数量低于8个!" type2Str = "爆品价格小于1元的爆品数量低于5个!" typeResult = "" ) operatorNameList := jxutils.BatchString2Slice(storeDetail.OperatorName, storeDetail.OperatorName2, storeDetail.OperatorName3) operatorPhoneList := jxutils.BatchString2Slice(storeDetail.OperatorPhone, storeDetail.OperatorPhone2, storeDetail.OperatorPhone3) if type1 < type1Count { typeResult += type1Str } if type2 < type2Count { typeResult += type2Str } var result = &tStoreSkusSecKill{} noticeMsg := fmt.Sprintf("运营负责人:[%v],市场负责人:[%v],门店ID:[%v],平台门店ID[%v],门店名:[%v],平台:[%v],警告类型:[%v]\n", strings.Join(operatorNameList, ","), storeDetail.MarketManName, store.StoreID, storeDetail.VendorStoreID, store.StoreName, model.VendorChineseNames[store.VendorID], typeResult) result.OperatorPhoneList = operatorPhoneList result.MarketManPhone = storeDetail.MarketManPhone result.NoticeMsg = noticeMsg retVal = []*tStoreSkusSecKill{result} } return retVal, err } taskParallel := tasksch.NewParallelTask("获取各平台爆品数量", tasksch.NewParallelConfig().SetParallelCount(parallelCount), ctx, taskFunc, storeList) tasksch.HandleTask(taskParallel, task, true).Run() ddMsgresult, err = taskParallel.GetResult(0) case 1: var ( operaterMap = make(map[string]string) marketMap = make(map[string]string) ) for _, v := range ddMsgresult { ddm := v.(*tStoreSkusSecKill) for _, phone := range ddm.OperatorPhoneList { if operaterMap[phone] != "" { operaterMap[phone] += ddm.NoticeMsg } else { operaterMap[phone] = ddm.NoticeMsg } } if marketMap[ddm.MarketManPhone] != "" { marketMap[ddm.MarketManPhone] += ddm.NoticeMsg } else { marketMap[ddm.MarketManPhone] = ddm.NoticeMsg } } for k, v := range operaterMap { if marketMap[k] != "" { continue } user, err := dao.GetUserByID(db, "mobile", k) if user != nil && err == nil { ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "警告!门店爆品数量异常!", v) } globals.SugarLogger.Debugf("SendSeckillSkusCountMsg: [%v]", v) } for k, v := range marketMap { if operaterMap[k] != "" { continue } user, err := dao.GetUserByID(db, "mobile", k) if user != nil && err == nil { ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "警告!门店爆品数量异常!", v) } globals.SugarLogger.Debugf("SendSeckillSkusCountMsg: [%v]", v) } } return result, err } taskSeq := tasksch.NewSeqTask2("爆品预警", ctx, isContinueWhenError, taskSeqFunc, 2) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func RefreshJxPriceByVendor(ctx *jxcontext.Context, jdStoreSkus []*JdStoreSkus, vendorID int, ignoreLow, isAsync, isContinueWhenError bool) (hint string, err error) { var ( db = dao.GetDB() jdMap = make(map[int][]*JdStoreSkus) jxMap = make(map[int]map[int]int) param []*StoreSkuBindInfo ) taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: if vendorID == model.VendorIDJD { for _, v := range jdStoreSkus { var ( pricePercentagePack []*model.PricePercentageItem cats []*model.ThingMap skus []*model.SkuAndName vendorPrice int specQuality float64 ) sql := ` SELECT t1.* FROM thing_map t1 WHERE t1.deleted_at = ? AND t1.thing_type = ? AND t1.vendor_thing_id = ? ` sqlParams := []interface{}{ utils.DefaultTimeValue, model.ThingTypeSku, v.JdSkuID, } err = dao.GetRows(db, &cats, sql, sqlParams...) if len(cats) > 0 { skus, err = dao.GetSkus(db, []int{int(cats[0].ThingID)}, nil, nil, nil, nil) if err != nil || len(skus) == 0 { return result, fmt.Errorf("没有找到该京西skuID!,京西skuID :[%v]", cats[0].ThingID) } } else { return result, fmt.Errorf("没有找到该京东skuID对应的京西skuID!,京东skuID :[%v]", v.JdSkuID) } store, err := dao.GetStoreDetailByVendorStoreID(db, utils.Int2Str(v.JdStoreID), vendorID, "") if err != nil || store == nil { return result, fmt.Errorf("没有找到该京东门店对应的京西门店!,京东门店ID :[%v]", v.JdStoreID) } err = jxutils.Strings2Objs(store.PricePercentagePackStr, &pricePercentagePack) if skus[0].Unit == model.SpecialUnit { if skus[0].SpecUnit == model.SpecUnitNames[1] || skus[0].SpecUnit == model.SpecUnitNames[2] { specQuality = float64(skus[0].SpecQuality) * 1000 } else { specQuality = float64(skus[0].SpecQuality) } vendorPrice = int(utils.Float64TwoInt64(specQuality / utils.Int2Float64(model.SpecialSpecQuality) * utils.Int2Float64(v.Price))) } else { vendorPrice = v.Price } jxPrice := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, vendorPrice) if skus[0].Unit == model.SpecialUnit { jxPrice = int(utils.Float64TwoInt64(utils.Int2Float64(jxPrice) * utils.Int2Float64(model.SpecialSpecQuality) / specQuality)) } jdMap[store.ID] = append(jdMap[store.ID], &JdStoreSkus{ JdSkuID: skus[0].NameID, Price: jxPrice, }) } for k, v := range jdMap { var skuNameMap = make(map[int]int) for _, vv := range v { if skuNameMap[vv.JdSkuID] != 0 { if skuNameMap[vv.JdSkuID] > vv.Price { skuNameMap[vv.JdSkuID] = vv.Price } } else { skuNameMap[vv.JdSkuID] = vv.Price } } jxMap[k] = skuNameMap } for k, v := range jxMap { for kk, vv := range v { result, err := dao.GetStoreSkuBindByNameID(db, k, kk, model.SkuStatusNormal) if len(result) > 0 && err == nil { if ignoreLow { if result[0].UnitPrice > vv { storeSkuBindInfo := &StoreSkuBindInfo{ StoreID: k, NameID: kk, UnitPrice: vv, } param = append(param, storeSkuBindInfo) } } else { storeSkuBindInfo := &StoreSkuBindInfo{ StoreID: k, NameID: kk, UnitPrice: vv, } param = append(param, storeSkuBindInfo) } } } } } else if vendorID == model.VendorIDMTWM { for _, v := range jdStoreSkus { var ( pricePercentagePack []*model.PricePercentageItem vendorPrice int specQuality float64 ) skus, _ := dao.GetSkus(db, []int{v.JdSkuID}, nil, nil, nil, nil) if len(skus) == 0 { continue } store, _ := dao.GetStoreDetail(db, v.JdStoreID, vendorID, "") err = jxutils.Strings2Objs(store.PricePercentagePackStr, &pricePercentagePack) if skus[0].Unit == model.SpecialUnit { if skus[0].SpecUnit == model.SpecUnitNames[1] || skus[0].SpecUnit == model.SpecUnitNames[2] { specQuality = float64(skus[0].SpecQuality) * 1000 } else { specQuality = float64(skus[0].SpecQuality) } vendorPrice = int(utils.Float64TwoInt64(specQuality / utils.Int2Float64(model.SpecialSpecQuality) * utils.Int2Float64(v.Price))) } else { vendorPrice = v.Price } jxPrice := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, vendorPrice) if skus[0].Unit == model.SpecialUnit { jxPrice = int(utils.Float64TwoInt64(utils.Int2Float64(jxPrice) * utils.Int2Float64(model.SpecialSpecQuality) / specQuality)) } jdMap[v.JdStoreID] = append(jdMap[v.JdStoreID], &JdStoreSkus{ JdSkuID: skus[0].NameID, Price: jxPrice, }) } for k, v := range jdMap { var skuNameMap = make(map[int]int) for _, vv := range v { if skuNameMap[vv.JdSkuID] != 0 { if skuNameMap[vv.JdSkuID] > vv.Price { skuNameMap[vv.JdSkuID] = vv.Price } } else { skuNameMap[vv.JdSkuID] = vv.Price } } jxMap[k] = skuNameMap } for k, v := range jxMap { for kk, vv := range v { result, err := dao.GetStoreSkuBindByNameID(db, k, kk, model.SkuStatusNormal) if len(result) > 0 && err == nil { if ignoreLow { if result[0].UnitPrice > vv { storeSkuBindInfo := &StoreSkuBindInfo{ StoreID: k, NameID: kk, UnitPrice: vv, } param = append(param, storeSkuBindInfo) } } else { storeSkuBindInfo := &StoreSkuBindInfo{ StoreID: k, NameID: kk, UnitPrice: vv, } param = append(param, storeSkuBindInfo) } } } } } case 1: _, err = UpdateStoresSkusByBind(ctx, nil, param, isAsync, isContinueWhenError, false) } return result, err } taskSeq := tasksch.NewSeqTask2("根据京东平台价刷新京西平台价", ctx, isContinueWhenError, taskSeqFunc, 2) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func RefreshJxPriceByVendor2(ctx *jxcontext.Context, storeIDs []int, vendorID int, isAsync, isContinueWhenError bool) (hint string, err error) { var ( db = dao.GetDB() param []*StoreSkuBindInfo ) taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { switch step { case 0: for _, storeID := range storeIDs { var ( pricePercentagePack []*model.PricePercentageItem skuNameMap = make(map[int]int64) ) storeDetail, _ := dao.GetStoreDetail(db, storeID, vendorID, "") err = jxutils.Strings2Objs(storeDetail.PricePercentagePackStr, &pricePercentagePack) if partner.IsMultiStore(vendorID) { mulitStoreSkuHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler) skuList, err := dao.GetSkusWithVendor(db, []int{vendorID}, nil, nil, nil, false) if err != nil { return "", err } bareStoreSkuMap := make(map[string][]*partner.StoreSkuInfo) for _, sku := range skuList { bareStoreSkuMap[sku.VendorOrgCode] = append(bareStoreSkuMap[sku.VendorOrgCode], &partner.StoreSkuInfo{ SkuID: sku.ID, VendorSkuID: sku.VendorSkuID, }) } for _, orgCode := range apimanager.CurAPIManager.GetAppOrgCodeList(vendorID) { outStoreSkuList, _ := mulitStoreSkuHandler.GetStoreSkusBareInfo(ctx, orgCode, task, storeID, storeDetail.VendorStoreID, bareStoreSkuMap[orgCode]) for _, sku := range outStoreSkuList { price, nameID := GetSkuNamePrice(db, sku.SkuID, sku.VendorPrice) if skuNameMap[nameID] < price { skuNameMap[nameID] = price } } for k, v := range skuNameMap { unitPrice := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, int(v)) storeSkuBindInfo := &StoreSkuBindInfo{ StoreID: storeID, NameID: k, UnitPrice: unitPrice, } param = append(param, storeSkuBindInfo) } } } else { singleStoreHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreStoreSkuHandler) outSkuNameList, _ := singleStoreHandler.GetStoreSkusFullInfo(ctx, task, storeID, storeDetail.VendorStoreID, nil) for _, skuName := range outSkuNameList { sku := skuName.SkuList[0] price, nameID := GetSkuNamePrice(db, sku.SkuID, sku.VendorPrice) if skuNameMap[nameID] < price { skuNameMap[nameID] = price } } for k, v := range skuNameMap { unitPrice := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, int(v)) storeSkuBindInfo := &StoreSkuBindInfo{ StoreID: storeID, NameID: k, UnitPrice: unitPrice, } param = append(param, storeSkuBindInfo) } } } case 1: _, err = UpdateStoresSkusByBind(ctx, nil, param, isAsync, isContinueWhenError, false) } return result, err } taskSeq := tasksch.NewSeqTask2("根据平台价刷新京西平台价", ctx, isContinueWhenError, taskSeqFunc, 2) tasksch.HandleTask(taskSeq, nil, true).Run() if !isAsync { _, err = taskSeq.GetResult(0) hint = "1" } else { hint = taskSeq.GetID() } return hint, err } func GetSkuNamePrice(db *dao.DaoDB, skuID int, orgPrice int64) (price int64, nameID int) { var ( specQuality float64 ) skuList, _ := dao.GetSkus(db, []int{skuID}, nil, nil, nil, nil) for _, v := range skuList { if v.Unit == model.SpecialUnit { if v.SpecUnit == model.SpecUnitNames[1] || v.SpecUnit == model.SpecUnitNames[2] { specQuality = float64(v.SpecQuality) * 1000 } else { specQuality = float64(v.SpecQuality) } price = utils.Float64TwoInt64(utils.Int2Float64(model.SpecialSpecQuality) / specQuality * utils.Int2Float64(int(orgPrice))) } else { price = orgPrice } nameID = v.NameID } return price, nameID } func BackUpStoreSkuBind(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) { var ( db = dao.GetDB() snapshotAt = utils.Time2Date(time.Now()) ) storeSkuBindHis := &model.StoreSkuBindHistory{ SnapshotAt: snapshotAt.AddDate(0, 0, -5), } dao.DeleteEntity(db, storeSkuBindHis, "SnapshotAt") storeList, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "") task := tasksch.NewParallelTask("BackUpStoreSkuBind", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { store := batchItemList[0].(*model.Store) storeSku, err := dao.GetStoresSkusInfo(db, []int{store.ID}, nil) var storeSkuBindHiss []*model.StoreSkuBindHistory for _, v := range storeSku { storeSkuBindHis := &model.StoreSkuBindHistory{} storeSkuBindHis.StoreSkuBind = *v storeSkuBindHis.StoreSkuBindID = v.ID storeSkuBindHis.StoreSkuBind.ID = 0 storeSkuBindHis.SnapshotAt = utils.Time2Date(snapshotAt) storeSkuBindHiss = append(storeSkuBindHiss, storeSkuBindHis) } if len(storeSkuBindHiss) > 0 { dao.CreateMultiEntities(db, storeSkuBindHiss) } return retVal, err }, storeList) tasksch.HandleTask(task, nil, true).Run() if isAsync { hint = task.GetID() } else { _, err = task.GetResult(0) hint = "1" } return hint, err } func ReturnStoreSkuBind(ctx *jxcontext.Context, snapshotAt string, storeIDs, skuIDs []int) (err error) { var ( spAt = utils.Time2Date(utils.Str2Time(snapshotAt)) db = dao.GetDB() ) if len(storeIDs) == 0 { return fmt.Errorf("必须选择一个门店!") } if len(storeIDs) > 1 { return fmt.Errorf("暂时只支持一次操作一个门店!") } //删除原门店商品 sql := `DELETE FROM store_sku_bind WHERE store_id IN(` + dao.GenQuestionMarks(len(storeIDs)) + `)` sqlParams := []interface{}{storeIDs} if len(skuIDs) > 0 { sql += " AND sku_id IN(" + dao.GenQuestionMarks(len(skuIDs)) + ")" sqlParams = append(sqlParams, skuIDs) } _, err = dao.ExecuteSQL(db, sql, sqlParams) //查询备份的门店商品 var storeHiss []*model.StoreSkuBindHistory sql2 := `SELECT * FROM store_sku_bind_history WHERE snapshot_at = ? AND store_id IN(` + dao.GenQuestionMarks(len(storeIDs)) + `)` sqlParams2 := []interface{}{spAt, storeIDs} if len(skuIDs) > 0 { sql2 += " AND sku_id IN(" + dao.GenQuestionMarks(len(skuIDs)) + ")" sqlParams2 = append(sqlParams2, skuIDs) } err = dao.GetRows(db, &storeHiss, sql2, sqlParams2) //插入到现在 var storeSkus []*model.StoreSkuBind for _, v := range storeHiss { storeSku := &model.StoreSkuBind{} storeSku = &v.StoreSkuBind storeSku.ID = v.StoreSkuBindID storeSkus = append(storeSkus, storeSku) } if len(storeSkus) > 0 { dao.CreateMultiEntities(db, storeSkus) } return err } func RefreshMatterStock(ctx *jxcontext.Context, skuID int) (err error) { var ( db = dao.GetDB() skuBindInfos []*StoreSkuBindInfo ) if skuID != 0 { skus, err := dao.GetSkus(db, []int{skuID}, nil, nil, nil, nil) if err != nil || len(skus) == 0 { return err } result, err := api.JdEclpAPI.QueryStock(skus[0].EclpID) if err != nil || result == nil { return err } var isSale = 0 if len(result) == 0 { isSale = -1 } else { if result[0].UsableNum > 0 { isSale = 1 } else { isSale = -1 } } storeSkuList, _ := dao.GetStoresSkusInfo(db, nil, []int{skuID}) for _, vv := range storeSkuList { var storeSkuBindSkus []*StoreSkuBindSkuInfo storeSkuBindSku := &StoreSkuBindSkuInfo{ SkuID: vv.SkuID, IsSale: isSale, } storeSkuBindSkus = append(storeSkuBindSkus, storeSkuBindSku) storeSkuBind := &StoreSkuBindInfo{ StoreID: vv.StoreID, // NameID: v.SkuNameID, Skus: storeSkuBindSkus, } skuBindInfos = append(skuBindInfos, storeSkuBind) } } else { result, err := api.JdEclpAPI.QueryStock("") if err != nil { return err } for _, v := range result { skus, err := dao.GetSkus(db, nil, nil, nil, nil, []string{v.GoodsNo}) if err != nil || len(skus) == 0 { continue } var isSale = 0 if v.UsableNum > 0 { isSale = 1 } else { isSale = -1 } storeSkuList, _ := dao.GetStoresSkusInfo(db, nil, []int{skus[0].ID}) for _, vv := range storeSkuList { var storeSkuBindSkus []*StoreSkuBindSkuInfo storeSkuBindSku := &StoreSkuBindSkuInfo{ SkuID: vv.SkuID, IsSale: isSale, } storeSkuBindSkus = append(storeSkuBindSkus, storeSkuBindSku) storeSkuBind := &StoreSkuBindInfo{ StoreID: vv.StoreID, NameID: skus[0].NameID, Skus: storeSkuBindSkus, } skuBindInfos = append(skuBindInfos, storeSkuBind) } } } //物料店666666 updateStoresSkusWithoutSync(ctx, db, []int{666666}, skuBindInfos, false, false) return err } func buildStoreSkuBindInfosAndFocus(ctx *jxcontext.Context, db *dao.DaoDB, store *dao.StoreDetail, v *partner.SkuNameInfo, nameID int) (err error) { var ( pricePercentagePack []*model.PricePercentageItem pricePercentagePack2 []*model.PricePercentageItem jxPrice = 0 ) skus, _ := dao.GetSkus(db, nil, []int{nameID}, nil, nil, nil) err = jxutils.Strings2Objs(store.PricePercentagePackStr, &pricePercentagePack) price := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, int(v.SkuList[0].VendorPrice)) store2, _ := dao.GetStoreDetail(db, store.ID, model.VendorIDJX, "") if store2 != nil { err = jxutils.Strings2Objs(store2.PricePercentagePackStr, &pricePercentagePack2) jxPrice = jxutils.CaculatePriceByPricePack(pricePercentagePack2, 0, price) } skuBind := &model.StoreSkuBind{ StoreID: store.ID, UnitPrice: price, Price: price, Status: model.StoreSkuBindStatusNormal, YbID: utils.Str2Int64(v.SkuList[0].VendorSkuID), YbPrice: int(v.SkuList[0].VendorPrice), JxPrice: jxPrice, YbSyncStatus: 0, MtwmSyncStatus: model.SyncFlagNewMask, JdSyncStatus: model.SyncFlagNewMask, EbaiSyncStatus: model.SyncFlagNewMask, } if len(skus) > 0 { skuBind.SkuID = skus[0].ID } dao.WrapAddIDCULDEntity(skuBind, ctx.GetUserName()) err = dao.CreateEntity(db, skuBind) return err } func CreateSkusAndFocusFromWx(ctx *jxcontext.Context, productInfo *jdapi.ProductInfo, price, storeID int) (err error) { var ( db = dao.GetDB() skuBindInfos []*StoreSkuBindInfo outSkuNameExt *model.SkuNameExt ) if productInfo == nil { return fmt.Errorf("未查询到相关商品!") } if price == 0 { return fmt.Errorf("请输入商品价格!") } focusSku := func(nameID int) { skuBindInfo := &StoreSkuBindInfo{ StoreID: storeID, NameID: nameID, UnitPrice: price, IsFocus: 1, IsSale: 1, } skuBindInfos = append(skuBindInfos, skuBindInfo) _, err := UpdateStoresSkusByBind(ctx, nil, skuBindInfos, true, true, false) if err != nil { err = nil } } skuNames, err := dao.GetSkuNames(db, nil, []string{productInfo.UpcCode}, "", false) if err != nil { return err } if len(skuNames) == 0 { if productInfo.Name == "" { var originName string if strings.Contains(productInfo.OriginalName, upcSpecName1) { originName = strings.ReplaceAll(productInfo.OriginalName, upcSpecName1, "") } if strings.Contains(productInfo.OriginalName, "*") { originName = strings.ReplaceAll(productInfo.OriginalName, "*", "") } _, name, _, specUnit, unit, specQuality := jxutils.SplitSkuName(originName) productInfo.Name = name productInfo.SpecQuality = specQuality productInfo.Unit = unit productInfo.SpecUnit = specUnit } if productInfo.Name == "" { if result, err := api.MtwmAPI.GetStandardProductListWithCond(productInfo.UpcCode); err == nil && result != nil { productInfo.OriginalName = result.Name productInfo.OriginalSpec = result.Spec productInfo.Name = result.Name productInfo.SpecUnit = result.SpecUnit productInfo.Unit = result.Unit productInfo.SpecQuality = float32(result.SpecNew) productInfo.ImgList = strings.Split(result.Pic, ",") productInfo.Weight = float32(result.Weight) productInfo.BrandName = result.BrandNamePath } } skuNameExt := &model.SkuNameExt{ SkuName: model.SkuName{ Name: productInfo.Name, Upc: &productInfo.UpcCode, Status: model.SkuStatusNormal, CategoryID: model.NoCatCatgoryID, IsGlobal: model.YES, Unit: productInfo.Unit, }, Skus: []*model.SkuWithVendor{ &model.SkuWithVendor{ Sku: &model.Sku{}, }, }, // Places: []int{510100}, //默认成都 } skuNameExt.Price = price skuNameExt.Skus[0].SpecQuality = productInfo.SpecQuality skuNameExt.Skus[0].SpecUnit = productInfo.SpecUnit skuNameExt.Skus[0].Weight = int(utils.Str2Int64(utils.Float64ToStr(math.Round(float64(productInfo.Weight))))) skuNameExt.Skus[0].Status = model.SkuStatusNormal if len(productInfo.ImgList) > 0 { setImgs(skuNameExt, productInfo.ImgList) } //可能就是没查出来 if skuNameExt.Name == "" { skuNameExt.Name = productInfo.OriginalName skuNameExt.Unit = model.UnitNames[5] skuNameExt.Skus[0].SpecQuality = 100 skuNameExt.Skus[0].SpecUnit = model.SpecUnitNames[0] skuNameExt.Skus[0].Weight = 100 } outSkuNameExt, err = AddSkuName(ctx, skuNameExt, ctx.GetUserName()) if err != nil { if _, ok := err.(*SyncError); ok { err = nil } else { return err } } else { focusSku(outSkuNameExt.ID) } } else { focusSku(skuNames[0].ID) } return err } func SyncMatterC4ToGy(ctx *jxcontext.Context, isContinueWhenError, isAsync bool) (hint string, err error) { var ( db = dao.GetDB() skus []*model.Sku skusgy []*model.Sku eclpIDs []string addMatters []*model.Sku deleteMatters []*model.Sku updateMatters []*model.Sku ) if globals.IsMainProductEnv() { return "", fmt.Errorf("此接口只允许在果园上调用!") } task := tasksch.NewParallelTask("同步物料商品从菜市到果园", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { step := batchItemList[0].(int) var ( skusMap = make(map[string]*model.Sku) skusgyMap = make(map[string]*model.Sku) ) switch step { case 0: sql := "SELECT * FROM jxd_dev_0.sku WHERE deleted_at = ? AND eclp_id <> ''" sqlParams := []interface{}{utils.DefaultTimeValue} err = dao.GetRows(db, &skus, sql, sqlParams) for _, c4 := range skus { eclpIDs = append(eclpIDs, c4.EclpID) skusMap[c4.EclpID] = c4 } sql2 := "SELECT * FROM sku WHERE deleted_at = ? AND eclp_id IN (" + dao.GenQuestionMarks(len(eclpIDs)) + ")" sqlParams = append(sqlParams, eclpIDs) err = dao.GetRows(db, &skusgy, sql2, sqlParams) for _, gy := range skusgy { skusgyMap[gy.EclpID] = gy if skusMap[gy.EclpID] == nil { deleteMatters = append(deleteMatters, skusgyMap[gy.EclpID]) } } for _, c4 := range skus { if skusgyMap[c4.EclpID] == nil { addMatters = append(addMatters, skusMap[c4.EclpID]) } else { updateMatters = append(updateMatters, skusMap[c4.EclpID]) } } case 1: if len(deleteMatters) > 0 { for _, v := range deleteMatters { _, err = DeleteSkuName(ctx, v.NameID, ctx.GetUserName()) } } if len(addMatters) > 0 { for _, v := range addMatters { var ( skuName *model.SkuName skuNames []*model.SkuName ) sql := ` SELECT t1.* FROM jxd_dev_0.sku_name t1 WHERE t1.deleted_at = ? AND t1.id = ? ` sqlParams := []interface{}{utils.DefaultTimeValue, v.NameID} err = dao.GetRows(db, &skuNames, sql, sqlParams) if len(skuNames) > 0 { skuName = skuNames[0] } skuNameExt := &model.SkuNameExt{ SkuName: model.SkuName{}, Skus: []*model.SkuWithVendor{ &model.SkuWithVendor{ Sku: &model.Sku{}, }, }, } skuNameExt.Unit = skuName.Unit skuNameExt.Name = skuName.Name skuNameExt.Upc = skuName.Upc skuNameExt.Img = skuName.Img skuNameExt.Img2 = skuName.Img2 skuNameExt.DescImg = skuName.DescImg skuNameExt.Price = skuName.Price skuNameExt.IsGlobal = model.YES skuNameExt.Status = model.SkuStatusNormal // skuNameExt.ID = v.NameID skuNameExt.CategoryID = 3024 //默认全放物料分类下 skuNameExt.DeletedAt = utils.DefaultTimeValue skuNameExt.CreatedAt = time.Now() skuNameExt.LastOperator = ctx.GetUserName() txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db, txDB) panic(r) } }() if err = dao.CreateEntity(db, &skuNameExt.SkuName); err != nil { dao.Rollback(db, txDB) return retVal, err } // skuNameExt.Skus[0].ID = v.ID skuNameExt.Skus[0].SpecQuality = v.SpecQuality skuNameExt.Skus[0].SpecUnit = v.SpecUnit skuNameExt.Skus[0].EclpID = v.EclpID skuNameExt.Skus[0].Status = model.SkuStatusNormal skuNameExt.Skus[0].NameID = skuNameExt.ID skuNameExt.Skus[0].DeletedAt = utils.DefaultTimeValue skuNameExt.Skus[0].CreatedAt = time.Now() skuNameExt.Skus[0].LastOperator = ctx.GetUserName() if err = dao.CreateEntity(db, skuNameExt.Skus[0].Sku); err != nil { dao.Rollback(db, txDB) return retVal, err } dao.Commit(db, txDB) focusC4Matters(ctx, db, v) } } if len(updateMatters) > 0 { for _, v := range updateMatters { var ( skuName *model.SkuName skuNames []*model.SkuName ) sql := ` SELECT t1.* FROM jxd_dev_0.sku_name t1, jxd_dev_0.sku t2 WHERE t1.id = t2.name_id AND t1.deleted_at = ? AND t2.eclp_id = ? ` sqlParams := []interface{}{utils.DefaultTimeValue, v.EclpID} err = dao.GetRows(db, &skuNames, sql, sqlParams) if len(skuNames) > 0 { skuName = skuNames[0] } txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db, txDB) panic(r) } }() if v != nil { sql := ` UPDATE sku SET spec_quality = ?, spec_unit = ?, eclp_id = ?, last_operator = ? WHERE eclp_id = ? AND deleted_at = ? ` sqlParams := []interface{}{v.SpecQuality, v.SpecUnit, v.EclpID, ctx.GetUserName(), v.EclpID, utils.DefaultTimeValue} if _, err = dao.ExecuteSQL(db, sql, sqlParams); err != nil { dao.Rollback(db, txDB) return retVal, err } } if skuName != nil { sql := ` UPDATE sku_name a JOIN sku b ON a.id = b.name_id SET a.name = ?, a.img = ?, a.img2 = ?, a.desc_img = ?, a.unit = ?, a.price = ?, a.last_operator = ? WHERE b.eclp_id = ? AND a.deleted_at = ? AND 1=1 ` sqlParams := []interface{}{skuName.Name, skuName.Img, skuName.Img2, skuName.DescImg, skuName.Unit, skuName.Price, ctx.GetUserName(), v.EclpID, utils.DefaultTimeValue} if _, err = dao.ExecuteSQL(db, sql, sqlParams); err != nil { dao.Rollback(db, txDB) return retVal, err } } dao.Commit(db, txDB) focusC4Matters(ctx, db, v) } } } return retVal, err }, []int{0, 1}) tasksch.HandleTask(task, nil, true).Run() if !isAsync { _, err = task.GetResult(0) hint = "1" } else { hint = task.GetID() } return hint, err } func focusC4Matters(ctx *jxcontext.Context, db *dao.DaoDB, v *model.Sku) (err error) { var storeSkus []*model.StoreSkuBind sql := ` SELECT * FROM jxd_dev_0.store_sku_bind a, jxd_dev_0.sku b WHERE a.sku_id = b.id AND a.store_id = ? AND b.eclp_id = ? AND a.deleted_at = ? ` sqlParams2 := []interface{}{model.MatterStoreID, v.EclpID, utils.DefaultTimeValue} err = dao.GetRows(db, &storeSkus, sql, sqlParams2) if err != nil { return err } if len(storeSkus) > 0 { var ( skuBindInfos []*StoreSkuBindInfo skuName2 *model.SkuName ) sql := ` SELECT a.* FROM sku_name a, sku b WHERE a.id = b.name_id AND b.eclp_id = ? AND a.deleted_at = ? ` sqlParams := []interface{}{v.EclpID, utils.DefaultTimeValue} err = dao.GetRow(db, &skuName2, sql, sqlParams) if skuName2 != nil { skuBindInfo := &StoreSkuBindInfo{ NameID: skuName2.ID, IsFocus: 1, UnitPrice: skuName2.Price, } skuBindInfos = append(skuBindInfos, skuBindInfo) } updateStoresSkusWithoutSync(ctx, db, []int{model.MatterStoreID}, skuBindInfos, false, false) } return err } func RefreshJdsSkusStatus(ctx *jxcontext.Context) (err error) { handler := partner.GetPurchasePlatformFromVendorID(model.VendorIDJDShop).(partner.ISingleStoreStoreSkuHandler) remoteSkuList, err := handler.GetStoreSkusFullInfo(ctx, nil, 0, "", nil) for _, v := range remoteSkuList { if v.Status == jdshopapi.JdsSkuStatus2 { reason, err := api.JdShopAPI.FindOpReason(utils.Str2Int64(v.VendorNameID)) if err == nil && reason == jdshopapi.PassReason { err = api.JdShopAPI.UpOrDown(utils.Str2Int64(v.VendorNameID), jdshopapi.JdsSkuStatus1) } } } return err } func GetStoreSkuAudit(ctx *jxcontext.Context, storeIDs, nameIDs, skuIDs, statuss, types []int, name, remark, keyword, cityName, marketManPhone, applyTimeStart, applyTimeEnd, auditTimeStart, auditTimeEnd string, pageSize, offset int) (pagedInfo *model.PagedInfo, err error) { var ( applyTimeStartp, applyTimeEndp, auditTimeStartp, auditTimeEndp time.Time ) if applyTimeStart != "" { applyTimeStartp = utils.Str2Time(applyTimeStart) } if applyTimeEnd != "" { applyTimeEndp = utils.Str2Time(applyTimeEnd) } if auditTimeStart != "" { auditTimeStartp = utils.Str2Time(auditTimeStart) } if auditTimeEnd != "" { auditTimeEndp = utils.Str2Time(auditTimeEnd) } //权限 if permission.IsRoled(ctx) { if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil { var storeIDs2 []int if len(storeIDs) > 0 { for _, v := range storeIDs { if storeIDsMap[v] != 0 { storeIDs2 = append(storeIDs2, v) } } if len(storeIDs2) == 0 { storeIDs2 = append(storeIDs2, -1) } } else { for k, _ := range storeIDsMap { storeIDs2 = append(storeIDs2, k) } } storeIDs = nil storeIDs = storeIDs2 } } pagedInfo, err = dao.GetStoreSkuAudit(dao.GetDB(), storeIDs, nameIDs, skuIDs, statuss, types, name, remark, keyword, marketManPhone, cityName, applyTimeStartp, applyTimeEndp, auditTimeStartp, auditTimeEndp, pageSize, offset) return pagedInfo, err } func doStoreSkuAudit(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (isAudit bool, err error) { globals.SugarLogger.Debugf("doStoreSkuAudit storeIDs: %v", storeIDs) time.Sleep(time.Second / 5) db := dao.GetDB() for _, storeID := range storeIDs { stores, _ := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "") //扣点的门店改价不进审核 if len(stores) > 0 { if stores[0].PayPercentage <= 50 || stores[0].StoreLevel == "E" || stores[0].StoreLevel == "D" { globals.SugarLogger.Debugf("doStoreSkuAudit return0 storeID : %v", storeID) return false, err } } for _, skuBindInfo := range skuBindInfos { globals.SugarLogger.Debugf("doStoreSkuAudit storeID: %v , nameID: %v", storeID, skuBindInfo.NameID) storeAudits, err := dao.GetStoreSkuAuditLight(db, []int{storeID}, []int{skuBindInfo.NameID}, model.StoreAuditStatusOnline) //取消关注,可售排除 if skuBindInfo.IsFocus == -1 || skuBindInfo.IsSale != 0 || skuBindInfo.UnitPrice == 0 { globals.SugarLogger.Debugf("doStoreSkuAudit return1 storeID : %v nameID: %v", storeID, skuBindInfo.NameID) return false, err } if ctx.GetLoginType() != weixin.AuthTypeMP && ctx.GetLoginType() != weixin.AuthTypeMini && ctx.GetLoginType() != weixin.AuthTypeWxApp && ctx.GetLoginType() != auth2.AuthTypeMobile { globals.SugarLogger.Debugf("doStoreSkuAudit return3 storeID : %v loginType: %v", storeID, ctx.GetLoginType()) authInfo, err := ctx.GetV2AuthInfo() if err == nil && authInfo != nil && (ctx.GetFullUser().Type&model.UserTypeOperator) != 0 { if len(storeAudits) > 0 { storeAudits[0].DeletedAt = time.Now() dao.UpdateEntity(db, storeAudits[0], "DeletedAt") // if globals.IsProductEnv() { // skuAndNames, err := dao.GetSkus(db, nil, []int{skuBindInfo.NameID}, nil, nil, nil) // if len(skuAndNames) > 0 && err == nil { // weixinmsg.NotifyStoreOpRequestStatus(true, storeID, skuBindInfo.NameID, jxutils.ComposeSpuName(skuAndNames[0].Prefix, skuAndNames[0].Name, 0), storeAudits[0].OriginUnitPrice, skuBindInfo.UnitPrice, "") // } // } } globals.SugarLogger.Debugf("doStoreSkuAudit return3 storeID : %v nameID: %v", storeID, skuBindInfo.NameID) return false, err } } storeSkuAudit := &model.StoreSkuAudit{ StoreID: storeID, NameID: skuBindInfo.NameID, Status: model.StoreAuditStatusOnline, UnitPrice: skuBindInfo.UnitPrice, } //如果是关注改价 if skuBindInfo.IsFocus == 1 { if len(storeAudits) > 0 { storeAudits[0].DeletedAt = time.Now() dao.UpdateEntity(db, storeAudits[0], "DeletedAt") } storeSkuAudit.Type = model.StoreSkuAuditTypeFocus } else { storeSkus, err := dao.GetStoreSkusByNameIDs(db, []int{storeID}, skuBindInfo.NameID) if len(storeSkus) > 0 { //如果改的价比原价低排除 if skuBindInfo.UnitPrice <= int(storeSkus[0].UnitPrice) { if len(storeAudits) > 0 { storeAudits[0].DeletedAt = time.Now() dao.UpdateEntity(db, storeAudits[0], "DeletedAt") } globals.SugarLogger.Debugf("doStoreSkuAudit return4 storeID : %v nameID: %v", storeID, skuBindInfo.NameID) return false, err } else { if len(storeAudits) > 0 { storeAudits[0].DeletedAt = time.Now() dao.UpdateEntity(db, storeAudits[0], "DeletedAt") } globals.SugarLogger.Debugf("doStoreSkuAudit cover storeID : %v nameID: %v", storeID, skuBindInfo.NameID) } } else { return false, fmt.Errorf("未查询到该门店商品价,storeID: %v, nameID: %V", storeID, skuBindInfo.NameID) } storeSkuAudit.Type = model.StoreSkuAuditTypePrice storeSkuAudit.OriginUnitPrice = int(storeSkus[0].UnitPrice) } dao.WrapAddIDCULDEntity(storeSkuAudit, ctx.GetUserName()) err = dao.CreateEntity(db, storeSkuAudit) } } return true, err } func doStoreSkuAuditForGy(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (isAudit bool, err error) { globals.SugarLogger.Debugf("doStoreSkuAuditForGy storeIDs: %v", storeIDs) time.Sleep(time.Second / 5) db := dao.GetDB() for _, storeID := range storeIDs { // stores, _ := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "") //扣点的门店改价不进审核 // if len(stores) > 0 { // if stores[0].PayPercentage <= 50 || stores[0].StoreLevel == "E" || stores[0].StoreLevel == "D" { // globals.SugarLogger.Debugf("doStoreSkuAudit return0 storeID : %v", storeID) // return false, err // } // } for _, skuBindInfo := range skuBindInfos { globals.SugarLogger.Debugf("doStoreSkuAudit storeID: %v , nameID: %v", storeID, skuBindInfo.NameID) storeAudits, err := dao.GetStoreSkuAuditLight(db, []int{storeID}, []int{skuBindInfo.NameID}, model.StoreAuditStatusOnline) //取消关注,可售排除 if skuBindInfo.IsFocus == -1 || skuBindInfo.IsSale != 0 || skuBindInfo.UnitPrice == 0 { globals.SugarLogger.Debugf("doStoreSkuAudit return1 storeID : %v nameID: %v", storeID, skuBindInfo.NameID) return false, err } if ctx.GetLoginType() != weixin.AuthTypeMP && ctx.GetLoginType() != weixin.AuthTypeMini && ctx.GetLoginType() != weixin.AuthTypeWxApp && ctx.GetLoginType() != auth2.AuthTypeMobile { globals.SugarLogger.Debugf("doStoreSkuAudit return3 storeID : %v loginType: %v", storeID, ctx.GetLoginType()) authInfo, err := ctx.GetV2AuthInfo() if err == nil && authInfo != nil && (ctx.GetFullUser().Type&model.UserTypeOperator) != 0 { if len(storeAudits) > 0 { storeAudits[0].DeletedAt = time.Now() dao.UpdateEntity(db, storeAudits[0], "DeletedAt") } globals.SugarLogger.Debugf("doStoreSkuAudit return3 storeID : %v nameID: %v", storeID, skuBindInfo.NameID) return false, err } } storeSkuAudit := &model.StoreSkuAudit{ StoreID: storeID, NameID: skuBindInfo.NameID, Status: model.StoreAuditStatusCreated, UnitPrice: skuBindInfo.UnitPrice, } //如果是关注改价 if skuBindInfo.IsFocus == 1 { if len(storeAudits) > 0 { storeAudits[0].DeletedAt = time.Now() dao.UpdateEntity(db, storeAudits[0], "DeletedAt") } storeSkuAudit.Type = model.StoreSkuAuditTypeFocus } else { storeSkus, _ := dao.GetStoreSkusByNameIDs(db, []int{storeID}, skuBindInfo.NameID) if len(storeSkus) > 0 { if len(storeAudits) > 0 { storeAudits[0].DeletedAt = time.Now() dao.UpdateEntity(db, storeAudits[0], "DeletedAt") } globals.SugarLogger.Debugf("doStoreSkuAudit cover storeID : %v nameID: %v", storeID, skuBindInfo.NameID) } else { return false, fmt.Errorf("未查询到该门店商品价,storeID: %v, nameID: %V", storeID, skuBindInfo.NameID) } storeSkuAudit.Type = model.StoreSkuAuditTypePrice storeSkuAudit.OriginUnitPrice = int(storeSkus[0].UnitPrice) } dao.WrapAddIDCULDEntity(storeSkuAudit, ctx.GetUserName()) err = dao.CreateEntity(db, storeSkuAudit) } } return true, err } func StoreSkuPriceAudit(ctx *jxcontext.Context, storeSkuAudits []*model.StoreSkuAudit, status int, isAsync, isContinueWhenError bool) (storeSkuAudits2 []*model.StoreSkuAudit, hint string, err error) { db := dao.GetDB() if status == model.StoreAuditStatusOnline { return nil, "", fmt.Errorf("审核标志不正确!") } //证明是预审核 if status == 2 { for _, v := range storeSkuAudits { skuList, _ := dao.GetStoreSkusByNameIDs(db, []int{v.StoreID}, v.NameID) if len(skuList) > 0 { if int64(v.AuditPrice) > skuList[0].UnitPrice*2 { storeSkuAudits2 = append(storeSkuAudits2, v) } } } return storeSkuAudits2, "", err } deletedDuplicateWaitAuditData(ctx, db) task := tasksch.NewParallelTask("StoreSkuPriceAudit", tasksch.NewParallelConfig().SetParallelCount(5).SetIsContinueWhenError(isContinueWhenError), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { storeAudit := batchItemList[0].(*model.StoreSkuAudit) storeAudits, err := dao.GetStoreSkuAuditLight(db, []int{storeAudit.StoreID}, []int{storeAudit.NameID}, model.StoreAuditStatusOnline) if len(storeAudits) == 0 || err != nil { return retVal, err } if len(storeAudits) > 1 { return retVal, fmt.Errorf("查询到该门店该商品的待审核信息大于1条!storeID: %v, nameID: %v", storeAudit.StoreID, storeAudit.NameID) } //审核通过 if status == model.StoreAuditStatusCreated { storeAudits[0].UserID = ctx.GetUserID() storeAudits[0].Status = model.StoreAuditStatusCreated storeAudits[0].Remark = storeAudit.Remark storeAudits[0].AuditPrice = storeAudit.AuditPrice _, err = dao.UpdateEntity(db, storeAudits[0], "UserID", "Status", "Remark", "AuditPrice") var skuBindInfos = []*StoreSkuBindInfo{ &StoreSkuBindInfo{ NameID: storeAudit.NameID, UnitPrice: storeAudits[0].UnitPrice, }, } //证明填了额外的审核价格 if storeAudit.AuditPrice != 0 { skuBindInfos[0].UnitPrice = storeAudit.AuditPrice } //如果是关注改价 if storeAudits[0].Type == model.StoreSkuAuditTypeFocus { skuBindInfos[0].IsFocus = 1 } var num int64 db := dao.GetDB() skuIDs, err := updateStoresSkusWithoutSync(ctx, db, []int{storeAudit.StoreID}, skuBindInfos, false, false) if err != nil { return "", err } isAsync = asyncStoreSkuOpFilter(ctx, isAsync) num = int64(len(skuIDs)) if num > 0 { hint, err = CurVendorSync.SyncStoresSkus(ctx, nil, 0, db, nil, []int{storeAudit.StoreID}, skuIDs, false, isAsync, isContinueWhenError) } if num == 0 || !isAsync || hint == "" { hint = utils.Int64ToStr(num) } } else if status == model.StoreAuditStatusRejected { storeAudits[0].UserID = ctx.GetUserID() storeAudits[0].Status = model.StoreAuditStatusRejected storeAudits[0].Remark = storeAudit.Remark _, err = dao.UpdateEntity(db, storeAudits[0], "UserID", "Status", "Remark") } else { return retVal, fmt.Errorf("审核标志不正确!") } //TODO 暂时先不推消息了 if err == nil { // if globals.IsProductEnv() { // skuAndNames, err := dao.GetSkus(db, nil, []int{storeAudit.NameID}, nil, nil, nil) // if len(skuAndNames) > 0 && err == nil { // price := 0 // if storeAudit.AuditPrice != 0 { // price = storeAudit.AuditPrice // } else { // price = storeAudits[0].UnitPrice // } // weixinmsg.NotifyStoreOpRequestStatus(flag, storeAudit.StoreID, storeAudit.NameID, jxutils.ComposeSpuName(skuAndNames[0].Prefix, skuAndNames[0].Name, 0), storeAudits[0].OriginUnitPrice, price, storeAudit.Remark) // } // } } return retVal, err }, storeSkuAudits) tasksch.HandleTask(task, nil, true).Run() if isAsync { hint = task.GetID() } else { _, err = task.GetResult(0) hint = "1" } return nil, hint, err } func deletedDuplicateWaitAuditData(ctx *jxcontext.Context, db *dao.DaoDB) { var ( duplicateDatas []*model.StoreSkuAudit duplicateStore = make(map[int][]*model.StoreSkuAudit) ) sql := ` SELECT a.* FROM store_sku_audit a , ( SELECT count(*), store_id ,name_id ,status, deleted_at FROM store_sku_audit WHERE status = 0 AND deleted_at = ? GROUP BY 2,3,4,5 HAVING count(*) > 1 ) b WHERE a.store_id = b.store_id AND a.name_id = b.name_id AND a.status= b.status AND a.deleted_at = b.deleted_at ` sqlParams := []interface{}{utils.DefaultTimeValue} dao.GetRows(db, &duplicateDatas, sql, sqlParams) for _, v := range duplicateDatas { duplicateStore[v.StoreID] = append(duplicateStore[v.StoreID], v) } for _, v := range duplicateStore { duplicateSkuName := make(map[int][]*model.StoreSkuAudit) for _, vv := range v { duplicateSkuName[vv.NameID] = append(duplicateSkuName[vv.NameID], vv) } for _, vv := range duplicateSkuName { for i := 0; i < len(vv)-1; i++ { dao.DeleteEntity(db, vv[i]) } } } } func GetSpecialtyStoreSkus(ctx *jxcontext.Context, storeIDs, vendorIDs []int) (err error) { type SpecialtyStoreSkus struct { StoreID int `json:"门店ID"` StoreName string `json:"门店名"` SkuID int `json:"SkuID"` SkuName string `json:"商品名"` Price float64 `json:"平台价"` VendorName string `json:"平台名"` } var ( db = dao.GetDB() specialtyStoreSkus []*SpecialtyStoreSkus excelTitle = []string{ "门店ID", "门店名", "SkuID", "商品名", "平台价", "平台名", } sheetList []*excel.Obj2ExcelSheetConfig downloadURL, fileName string ) for _, v := range vendorIDs { for _, vv := range storeIDs { storeDetail, err := dao.GetStoreDetail(db, vv, v, "") if err != nil || storeDetail == nil { continue } if partner.IsMultiStore(v) { var ( page = 1 vendorSkuIDs []string ) _, totalCount, _ := api.JdPageAPI.GetJdTopSkus(storeDetail.VendorStoreID, page) for ; page < totalCount/10+1; page++ { result, _, _ := api.JdPageAPI.GetJdTopSkus(storeDetail.VendorStoreID, page) vendorSkuIDs = append(vendorSkuIDs, result...) } for _, vvv := range vendorSkuIDs { thingMap := &model.ThingMap{} sql := ` SELECT * FROM thing_map WHERE thing_type = 3 AND vendor_thing_id = ? AND vendor_org_code = 320406 AND deleted_at = ? ` sqlParams := []interface{}{ vvv, utils.DefaultTimeValue, } dao.GetRow(db, &thingMap, sql, sqlParams) skus, _ := dao.GetSkus(db, []int{int(thingMap.ThingID)}, nil, nil, nil, nil) specialtyStoreSku := &SpecialtyStoreSkus{ StoreID: vv, StoreName: storeDetail.Name, SkuID: int(thingMap.ThingID), SkuName: skus[0].Name, Price: 0, VendorName: model.VendorNames[v], } specialtyStoreSkus = append(specialtyStoreSkus, specialtyStoreSku) } } else { handler := partner.GetPurchasePlatformFromVendorID(v).(partner.ISingleStoreStoreSkuHandler) skuList, _ := handler.GetStoreSkusFullInfo(ctx, nil, vv, storeDetail.VendorStoreID, nil) for _, sku := range skuList { if sku.SkuList[0].IsSpecialty == model.YES { specialtyStoreSku := &SpecialtyStoreSkus{ StoreID: vv, StoreName: storeDetail.Name, SkuID: sku.SkuList[0].SkuID, SkuName: sku.Name, Price: float64(sku.SkuList[0].VendorPrice) / 100, VendorName: model.VendorNames[v], } specialtyStoreSkus = append(specialtyStoreSkus, specialtyStoreSku) } } } } } excelConf := &excel.Obj2ExcelSheetConfig{ Title: "sheet1", Data: specialtyStoreSkus, CaptionList: excelTitle, } sheetList = append(sheetList, excelConf) if excelConf != nil { downloadURL, fileName, err = jxutils.UploadExeclAndPushMsg(sheetList, "力荐或置顶商品") } else { baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!") } if err != nil { baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName, err) } else { noticeMsg := fmt.Sprintf("[详情点我]%s/billshow/?normal=true&path=%s \n", globals.BackstageHost, downloadURL) ddmsg.SendUserMessage(dingdingapi.MsgTyeText, ctx.GetUserID(), "异步任务完成", noticeMsg) baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess downloadURL: [%v]", downloadURL) } return err } func checkActUpdate(actID int, actMap map[int]*model.Act2) (err error) { if len(actMap) == 0 { return fmt.Errorf("活动%d不存在或已被取消", actID) } errList := errlist.New() for vendorID, act := range actMap { if vendorID == model.VendorIDEBAI && act.CreateType != model.ActCreateTypeAPI { errList.AddErr(fmt.Errorf("饿百平台不支持修改或取消网页活动")) } } return errList.GetErrListAsOne() } func DeleteActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actID int, actStoreSkuParam []*ActStoreSkuParam) (originSyncStatus int8, err error) { actMap, err := dao.GetActVendorInfo(db, actID, nil) if err != nil { return 0, err } if err = checkActUpdate(actID, actMap); err != nil { return 0, err } actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, actID, nil, nil, nil) if err != nil { return 0, err } act := actMap[partner.GetVendorIDsFromActMap(actMap)[0]] if act.Status != model.ActStatusCreated || time.Now().Sub(act.EndAt) > 0 { return 0, fmt.Errorf("当前活动状态:%s,不能进行此操作,或已过期", model.ActStatusName[act.Status]) } txDB, _ := dao.Begin(db) defer func() { if r := recover(); r != nil || err != nil { dao.Rollback(db, txDB) if r != nil { panic(r) } } }() actStoreSkuParamMap := make(map[int64]*ActStoreSkuParam) for _, v := range actStoreSkuParam { actStoreSkuParamMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = v if _, err = dao.DeleteEntityLogically(db, &model.ActStoreSku{}, nil, ctx.GetUserName(), map[string]interface{}{ model.FieldActID: actID, model.FieldStoreID: v.StoreID, model.FieldSkuID: v.SkuID, }); err != nil { return 0, err } } isNeedCancelAct := true for vendorID, act := range actMap { originSyncStatus |= act.SyncStatus isDeleteAll := true isDeleteAtLeastOne := false if true { //actStoreSkuParam != nil { actStoreSkuMap := partner.SplitActStoreSku(actStoreSkuMap[vendorID]) for storeID := range actStoreSkuMap { for _, actStoreSku := range actStoreSkuMap[storeID] { if actStoreSkuParam == nil || actStoreSkuParamMap[jxutils.Combine2Int(actStoreSku.StoreID, actStoreSku.SkuID)] != nil { if act.Type == model.ActSkuFake { _, err = dao.DeleteEntityLogically(db, &model.ActStoreSkuMap{}, nil, ctx.GetUserName(), map[string]interface{}{ model.FieldActID: actID, model.FieldStoreID: actStoreSku.StoreID, model.FieldSkuID: actStoreSku.SkuID, }) } else { _, err = dao.UpdateEntityLogically(db, partner.ActStoreSku2ActStoreSkuMap(actStoreSku), map[string]interface{}{ model.FieldSyncStatus: actStoreSku.SyncStatus | model.SyncFlagDeletedMask, }, ctx.GetUserName(), nil) } if err != nil { return 0, err } isDeleteAtLeastOne = true } else { isNeedCancelAct = false isDeleteAll = false } } } } else { isDeleteAll = true isDeleteAtLeastOne = true } if isDeleteAll || isDeleteAtLeastOne { syncStatus := int8(model.SyncFlagModifiedMask) if isDeleteAll { syncStatus = model.SyncFlagDeletedMask } syncStatus |= act.SyncStatus if act.Type != model.ActSkuFake && vendorID != model.VendorIDJX { if _, err = dao.UpdateEntityLogically(db, partner.Act2ActMap(act), map[string]interface{}{ model.FieldSyncStatus: syncStatus, }, ctx.GetUserName(), nil); err != nil { return 0, err } } } if isDeleteAll != isNeedCancelAct { globals.SugarLogger.Warnf("deleteActStoreBind, actID:%d isDeleteAll:%t != isNeedCancelAct:%t", act.ID, isDeleteAll, isNeedCancelAct) } } if isNeedCancelAct { act := &model.Act{} act.ID = actID if _, err = dao.UpdateEntityLogically(db, act, map[string]interface{}{ model.FieldStatus: model.ActStatusCanceled, }, ctx.GetUserName(), nil); err != nil { return 0, err } } dao.Commit(db, txDB) return originSyncStatus, err } func GetVendorStoreSkus(ctx *jxcontext.Context, storeID, vendorID int) (err error) { type SpecialtyStoreSkus struct { Upc string `json:"商品upc码"` SkuName string `json:"商品名"` Unit string `json:"单位"` Weight int `json:"重量(g)"` Price int64 `json:"售价"` } var ( db = dao.GetDB() specialtyStoreSkus []*SpecialtyStoreSkus excelTitle = []string{ "商品upc码", "商品名", "单位", "重量(g)", "售价", } sheetList []*excel.Obj2ExcelSheetConfig downloadURL, fileName string ) storeDetail, err := dao.GetStoreDetail(db, storeID, vendorID, "") if err != nil || storeDetail == nil { return err } if partner.IsMultiStore(vendorID) { // handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler) // skuBareInfoList, _ := handler.GetStoreSkusBareInfo(ctx, storeDetail.VendorOrgCode, nil, storeID, storeDetail.VendorStoreID, nil) return fmt.Errorf("暂不支持京东!") } else { handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreStoreSkuHandler) skuList, _ := handler.GetStoreSkusFullInfo(ctx, nil, storeID, storeDetail.VendorStoreID, nil) for _, sku := range skuList { if sku.UPC != "" { skus := &SpecialtyStoreSkus{ Upc: sku.UPC, SkuName: sku.Name, Unit: sku.Unit, Weight: sku.SkuList[0].Weight, Price: sku.SkuList[0].VendorPrice, } specialtyStoreSkus = append(specialtyStoreSkus, skus) } } } excelConf := &excel.Obj2ExcelSheetConfig{ Title: "sheet1", Data: specialtyStoreSkus, CaptionList: excelTitle, } sheetList = append(sheetList, excelConf) if excelConf != nil { downloadURL, fileName, err = jxutils.UploadExeclAndPushMsg(sheetList, "平台门店标品") } else { baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!") } if err != nil { baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName, err) } else { noticeMsg := fmt.Sprintf("[详情点我]%s/billshow/?normal=true&path=%s \n", globals.BackstageHost, downloadURL) ddmsg.SendUserMessage(dingdingapi.MsgTyeText, ctx.GetUserID(), "异步任务完成", noticeMsg) baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess downloadURL: [%v]", downloadURL) } return err } func CopyMtToJd(ctx *jxcontext.Context, mtStoreID, mtOrgCode, jdStoreID, jdOrgCode string) (err error) { type funcType func(mtID, parentID string, catInfo []*mtwmapi.RetailCategoryInfo) var ( //mtAccessToken = "" db = dao.GetDB() catMap = make(map[string]string) LoopCatChild funcType ) //mtStoreID = "11655829" //mtOrgCode = "589" //jdStoreID = "12116911" //jdOrgCode = "364156" jd := jd.GetAPI(jdOrgCode) //获取token mtapi := apimanager.CurAPIManager.GetAPI(model.VendorIDMTWM, mtOrgCode).(*mtwmapi.API) //if token, err := mtapi.GetAccessToken2(mtStoreID); err == nil && token != nil { // mtAccessToken = token.AccessToken //} //mtapi.SetToken(mtAccessToken) LoopCatChild = func(mtID, parentID string, catInfo []*mtwmapi.RetailCategoryInfo) { if len(catInfo) > 0 { for j := len(catInfo) - 1; j > -1; j-- { result, _ := jd.AddShopCategory(utils.Str2Int64(parentID), catInfo[j].Name, catInfo[j].Level, catInfo[j].Sequence, "") if result != "" { catMap[mtID] = result } LoopCatChild(catInfo[j].Code, result, catInfo[j].Children) } } } task := tasksch.NewParallelTask("美团到京东", tasksch.NewParallelConfig().SetIsContinueWhenError(true).SetParallelCount(1), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { step := batchItemList[0].(int) switch step { case 0: //建分类 jdCatList, _ := jd.QueryCategoriesByOrgCode() if len(jdCatList) == 0 { catList, _ := mtapi.RetailCatList(mtStoreID) for i := len(catList) - 1; i > -1; i-- { catID, _ := jd.AddShopCategory(0, catList[i].Name, catList[i].Level, catList[i].Sequence, "") if catID != "" { catMap[catList[i].Code] = catID } //LoopCatChild(catList[i].Code, catID, catList[i].Children) //美团好像只能建两级分类? for j := len(catList[i].Children) - 1; j > -1; j-- { catID2, _ := jd.AddShopCategory(utils.Str2Int64(catID), catList[i].Children[j].Name, catList[i].Children[j].Level, catList[i].Children[j].Sequence, "") if catID2 != "" { catMap[catList[i].Children[j].Code] = catID2 } } } } case 1: //建商品 //skuList, _ := mtapi.RetailListAll(mtStoreID) skuList, _ := mtapi.RetailList(mtStoreID, 0, 2) task2 := tasksch.NewParallelTask("美团到京东, 建商品", tasksch.NewParallelConfig().SetIsContinueWhenError(true).SetParallelCount(1), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { mtSku := batchItemList[0].(*mtwmapi.AppFood) catMaps, _ := dao.GetMtJdCategoryMap(db, utils.Int2Str(mtSku.TagID), "") if len(catMaps) == 0 { results, _ := dao.GetVendorCategoriesWithMap(db, model.VendorIDMTWM, mtSku.TagID) results2, _ := dao.GetVendorCategoriesWithMap(db, model.VendorIDMTWM, utils.Str2Int(results[0].ParentID)) results3, _ := dao.GetVendorCategoriesWithMap(db, model.VendorIDMTWM, utils.Str2Int(results2[0].ParentID)) return retVal, fmt.Errorf("该商品美团分类还未映射到京东,商品名:[%v],美团分类:[%v] ->[%v] ->[%v]。", mtSku.Name, results3[0].Name, results2[0].Name, results[0].Name) } param := &jdapi.OpSkuParam{ TraceID: ctx.GetTrackInfo(), OutSkuID: mtSku.AppFoodCode, ShopCategories: []int64{}, CategoryID: utils.Str2Int64(catMaps[0].JdID), BrandID: 35247, SkuName: mtSku.Name, SkuPrice: int(jxutils.StandardPrice2Int(mtSku.Price)), Weight: float64(jxutils.IntWeight2Float(utils.Str2Int(mtSku.SkuList[0].Weight))), FixedStatus: 1, IsSale: -1, Upc: mtSku.SkuList[0].Upc, Images: mtSku.PictureList, } if mtSku.SecondaryCategoryCode != "" { param.ShopCategories = append(param.ShopCategories, utils.Str2Int64(catMap[mtSku.SecondaryCategoryCode])) } else if mtSku.CategoryCode != "" { param.ShopCategories = append(param.ShopCategories, utils.Str2Int64(catMap[mtSku.CategoryCode])) } param.ProductDesc = fmt.Sprintf(`一张图片`, mtSku.PictureContents) skuID, err := jd.AddSku2(param) if skuID != "" { jd.UpdateCurrentQty(ctx.GetTrackInfo(), jdStoreID, utils.Str2Int64(skuID), utils.Str2Int(mtSku.SkuList[0].Stock)) jd.UpdateVendibility(ctx.GetTrackInfo(), []*jdapi.QueryStockRequest{ &jdapi.QueryStockRequest{ StationNo: jdStoreID, SkuId: utils.Str2Int64(skuID), DoSale: mtSku.IsSoldOut, }, }) } return retVal, err }, skuList) tasksch.HandleTask(task2, task, true).Run() _, err = task2.GetResult(0) if err != nil { task.SetNoticeMsg(err.Error()) } } return retVal, err }, []int{0, 1}) tasksch.HandleTask(task, nil, true).Run() task.GetID() return err } func UpdateMtCatToJd(ctx *jxcontext.Context, mtCatID, jdCatID string) (err error) { var ( db = dao.GetDB() ) list, _ := dao.GetMtJdCategoryMap(db, mtCatID, jdCatID) if len(list) > 0 { return } list2, _ := dao.GetMtJdCategoryMap(db, mtCatID, "") if len(list2) > 0 { if list2[0].JdID == jdCatID { return } else { list2[0].JdID = jdCatID dao.UpdateEntity(db, list2[0], "JdID") } } else { catMap := &model.MtJdCategoryMap{ MtID: mtCatID, JdID: jdCatID, } dao.CreateEntity(db, catMap) } return err }