diff --git a/business/jxstore/cms/store_sku.go b/business/jxstore/cms/store_sku.go index cfed3a02e..a09004cd9 100644 --- a/business/jxstore/cms/store_sku.go +++ b/business/jxstore/cms/store_sku.go @@ -8,6 +8,10 @@ import ( "strconv" "time" + "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + + "git.rosy.net.cn/jx-callback/business/partner" + "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxcallback/auth/weixin" "git.rosy.net.cn/jx-callback/business/jxutils" @@ -1349,3 +1353,109 @@ func checkStoreExisting(db *dao.DaoDB, storeID int) (err error) { } return 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, nil, storeIDs, model.StoreStatusAll) + if err != nil { + return "", err + } + if len(storeMapList) != len(storeIDs) { + return "", fmt.Errorf("门店绑定信息不匹配,请确定门店绑定且只绑定了京东平台") + } + storeMap := make(map[int]*model.StoreMap) + for _, v := range storeMapList { + if v.VendorID != vendorID { + return "", fmt.Errorf("门店%d绑定的不(只)是京东", v.StoreID) + } + storeMap[v.StoreID] = v + } + + handler := partner.GetPurchasePlatformFromVendorID(vendorID) + var storeSkuList []*model.StoreSkuBind + rootTask := tasksch.NewSeqTask(fmt.Sprintf("根据厂家门店商品信息相应刷新本地数据:%v", storeIDs), ctx, + func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) { + switch step { + case 0: + storeSkuList, err = handler.GetStoresSku(ctx, task, storeIDs) + case 1: + if len(storeSkuList) > 0 { + var skuList []*model.SkuAndName + skuList, err = dao.GetSkus(db, nil, nil, nil, nil) + if err == nil { + skuNameMap := make(map[int]*model.SkuName) + skuMap := make(map[int]*model.SkuAndName) + for _, sku := range skuList { + if skuNameMap[sku.NameID] == nil { + skuNameMap[sku.NameID] = &model.SkuName{ + Unit: sku.Unit, + } + } + skuMap[sku.ID] = sku + } + 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 := int(storeMap[v.StoreID].PricePercentage) + skuName := skuNameMap[skuMap[v.SkuID].NameID] + v.Price = jxutils.CaculateSkuPriceFromVendor(v.Price, pricePercentage, 0) + v.UnitPrice = jxutils.CaculateSkuPriceFromVendor(skuName.Price, pricePercentage, 0) + dao.WrapAddIDCULDEntity(v, ctx.GetUserName()) + setStoreSkuBindStatus(v, model.SyncFlagNewMask) + v.JdSyncStatus = 0 + } + } + } + case 2: + if len(storeSkuList) > 0 { + dao.Begin(db) + defer func() { + if r := recover(); r != nil || err != nil { + dao.Rollback(db) + 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) + } + } + } + } + return nil, err + }, 3) + tasksch.ManageTask(rootTask).Run() + if isAsync { + hint = rootTask.GetID() + } else { + _, err = rootTask.GetResult(0) + } + return hint, err +} diff --git a/business/jxutils/jxutils_cms.go b/business/jxutils/jxutils_cms.go index 88538c761..47105d484 100644 --- a/business/jxutils/jxutils_cms.go +++ b/business/jxutils/jxutils_cms.go @@ -153,19 +153,24 @@ func Int64Map2List(int64Map map[int64]int) []int64 { return retVal } -// 计算SKU价格,unitPrice为一斤的单价,specQuality为质量,单位为克 -func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int { - if skuNameUnit != "份" { - return unitPrice - } +func RegularizeSkuQuality(specQuality float32, specUnit string) (g int) { lowerSpecUnit := strings.ToLower(specUnit) if lowerSpecUnit == "kg" || lowerSpecUnit == "l" { specQuality *= 1000 } - price := int(math.Round(float64(float32(unitPrice) * specQuality / 500))) - if specQuality < 250 { + return int(specQuality) +} + +// 计算SKU价格,unitPrice为一斤的单价,specQuality为质量,单位为克 +func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int { + if skuNameUnit != model.SpecialUnit { + return unitPrice + } + specQuality2 := RegularizeSkuQuality(specQuality, specUnit) + price := int(math.Round(float64(unitPrice * specQuality2 / model.SpecialSpecQuality))) + if specQuality2 < 250 { price = price * 110 / 100 - } else if specQuality < 500 { + } else if specQuality2 < 500 { price = price * 105 / 100 } if price <= 0 { @@ -174,6 +179,24 @@ func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNa return price } +// 计算SKU标准价格,CaculateSkuPrice的逆过程 +func CaculateUnitPrice(skuPrice int, specQuality float32, specUnit string, skuNameUnit string) (unitPrice int) { + if skuNameUnit != model.SpecialUnit { + return skuPrice + } + specQuality2 := RegularizeSkuQuality(specQuality, specUnit) + unitPrice = skuPrice * model.SpecialSpecQuality / specQuality2 + if specQuality2 < 250 { + unitPrice = unitPrice * 100 / 110 + } else if specQuality2 < 500 { + unitPrice = unitPrice * 100 / 105 + } + if unitPrice <= 0 { + unitPrice = 1 + } + return unitPrice +} + func GetSliceLen(list interface{}) int { return reflect.ValueOf(list).Len() } @@ -186,11 +209,30 @@ func CaculateSkuVendorPrice(price, percentage, catPercentage int) int { catPercentage = 100 } percentage = percentage * catPercentage / 100 - storePrice := int(math.Round(float64(price*percentage) / 100)) - if storePrice < 0 { - storePrice = 0 + vendorPrice := int(math.Round(float64(price*percentage) / 100)) + if vendorPrice < 0 { + vendorPrice = 0 } - return storePrice + return vendorPrice +} + +func CaculateSkuPriceFromVendor(vendorPrice, percentage, catPercentage int) int { + if percentage <= 10 || percentage >= 400 { + percentage = 100 + } + if catPercentage <= 10 || catPercentage >= 400 { + catPercentage = 100 + } + percentage = percentage * catPercentage / 100 + price := int(math.Round(float64(vendorPrice * 100 / percentage))) + if price < 0 { + price = 0 + } + return price +} + +func IsSkuSpecial(specQuality float32, specUnit string) bool { + return int(specQuality) == model.SpecialSpecQuality && (specUnit == model.SpecialSpecUnit || specUnit == model.SpecialSpecUnit2) } // 生成一个不重复的临时ID diff --git a/business/model/dao/sku.go b/business/model/dao/sku.go index cb1aa7d08..bedf9d785 100644 --- a/business/model/dao/sku.go +++ b/business/model/dao/sku.go @@ -55,6 +55,47 @@ func GetSkuNameByHashCode(db *DaoDB, hashCode string) (skuName *model.SkuName, e return nil, err } +func GetSkus(db *DaoDB, skuIDs, nameIDs, statuss, catIDs []int) (skuList []*model.SkuAndName, err error) { + sql := ` + SELECT t1.*, t2.name, t2.unit + FROM sku t1 + JOIN sku_name t2 ON t2.id = t1.name_id AND t2.deleted_at = ? + ` + sqlWhere := ` + WHERE t1.deleted_at = ? + ` + sqlParams := []interface{}{ + utils.DefaultTimeValue, + utils.DefaultTimeValue, + } + if len(skuIDs) > 0 { + sqlWhere += " AND t1.id IN (" + GenQuestionMarks(len(skuIDs)) + ")" + sqlParams = append(sqlParams, skuIDs) + } + if len(nameIDs) > 0 { + sqlWhere += " AND t1.name_id IN (" + GenQuestionMarks(len(nameIDs)) + ")" + sqlParams = append(sqlParams, nameIDs) + } + if len(statuss) > 0 { + sqlWhere += " AND t1.status IN (" + GenQuestionMarks(len(statuss)) + ") AND t2.status IN (" + GenQuestionMarks(len(statuss)) + ")" + sqlParams = append(sqlParams, statuss, statuss) + } + if len(catIDs) > 0 { + sql += ` + JOIN sku_category t3 ON t3.id = t2.category_id + LEFT JOIN sku_category t3p ON t3p.id = t3.parent_id + ` + sqlWhere += " AND (t3.id IN (" + GenQuestionMarks(len(catIDs)) + ")" + sqlWhere += " OR t3p.id IN (" + GenQuestionMarks(len(catIDs)) + ") )" + sqlParams = append(sqlParams, catIDs, catIDs) + } + sql += sqlWhere + if err = GetRows(db, &skuList, sql, sqlParams...); err == nil { + return skuList, nil + } + return nil, err +} + func GetSkuNames(db *DaoDB, nameIDs []int) (skuNameList []*model.SkuName, err error) { sql := ` SELECT * diff --git a/business/model/dao/store.go b/business/model/dao/store.go index 7614a371a..2031c4713 100644 --- a/business/model/dao/store.go +++ b/business/model/dao/store.go @@ -181,6 +181,33 @@ func GetStoreCourierList(db *DaoDB, storeID, status int) (courierStoreList []*mo return nil, err } +func GetStoresMapList(db *DaoDB, vendorIDs, storeIDs []int, status int) (storeMapList []*model.StoreMap, err error) { + sql := ` + SELECT t1.* + FROM store_map t1 + WHERE t1.deleted_at = ? + ` + sqlParams := []interface{}{ + utils.DefaultTimeValue, + } + if len(vendorIDs) > 0 { + sql += " AND t1.vendor_id IN (" + GenQuestionMarks(len(vendorIDs)) + ")" + sqlParams = append(sqlParams, vendorIDs) + } + if len(storeIDs) > 0 { + sql += " AND t1.store_id IN (" + GenQuestionMarks(len(storeIDs)) + ")" + sqlParams = append(sqlParams, storeIDs) + } + if status != model.StoreStatusAll { + sql += " AND t1.status = ?" + sqlParams = append(sqlParams, status) + } + if err = GetRows(db, &storeMapList, sql, sqlParams...); err == nil { + return storeMapList, nil + } + return nil, err +} + // 此函数在检测到一个门店的所有平台状态一样,且不为StoreStatusOpened时, // 将平台门店状态全部改为StoreStatusOpened,则把京西门店状态改为之前那个统一的平台门店状态 func FormalizeStoreStatus(db *DaoDB, storeID, storeStatus int) (err error) { diff --git a/business/model/sku.go b/business/model/sku.go index b51e50286..2a4fd330e 100644 --- a/business/model/sku.go +++ b/business/model/sku.go @@ -89,6 +89,7 @@ var ( SpecialUnit = "份" SpecialSpecQuality = 500 SpecialSpecUnit = "g" + SpecialSpecUnit2 = "ml" ) var ( @@ -215,6 +216,12 @@ type Sku struct { LinkID int `orm:"column(link_id);null;index" json:"linkID"` } +type SkuAndName struct { + Sku + Name string + Unit string +} + // func (*Sku) TableUnique() [][]string { // return [][]string{ // []string{"JdID", "DeletedAt"}, diff --git a/business/partner/partner.go b/business/partner/partner.go index d03b6cd8c..09f1d5e38 100644 --- a/business/partner/partner.go +++ b/business/partner/partner.go @@ -231,6 +231,8 @@ type IPurchasePlatformHandler interface { UploadImg(ctx *jxcontext.Context, imgURL string, imgData []byte, imgName string) (imgHint string, err error) GetStoreStatus(ctx *jxcontext.Context, vendorStoreID string) (storeStatus int, err error) GetVendorCategories(ctx *jxcontext.Context) (vendorCats []*model.SkuVendorCategory, err error) + + GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) } // db *dao.DaoDB, diff --git a/business/partner/purchase/ebai/store_sku.go b/business/partner/purchase/ebai/store_sku.go index 66f1bdfd3..51fe733a9 100644 --- a/business/partner/purchase/ebai/store_sku.go +++ b/business/partner/purchase/ebai/store_sku.go @@ -619,3 +619,7 @@ func formatName(name string) string { func jxCatSeq2Ebai(seq int) int { return 10000 - seq } + +func (p *PurchaseHandler) GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) { + return storeSkuList, err +} diff --git a/business/partner/purchase/elm/store_sku.go b/business/partner/purchase/elm/store_sku.go index 24a194a17..bef0f2dee 100644 --- a/business/partner/purchase/elm/store_sku.go +++ b/business/partner/purchase/elm/store_sku.go @@ -3,6 +3,7 @@ package elm import ( "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/business/jxutils/tasksch" + "git.rosy.net.cn/jx-callback/business/model" ) func (p *PurchaseHandler) SyncStoreCategory(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync bool) (hint string, err error) { @@ -24,3 +25,7 @@ func (p *PurchaseHandler) FullSyncStoreSkus(ctx *jxcontext.Context, parentTask t func (p *PurchaseHandler) DeleteRemoteStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeID int, isAsync, isContinueWhenError bool) (hint string, err error) { return hint, err } + +func (p *PurchaseHandler) GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) { + return storeSkuList, err +} diff --git a/business/partner/purchase/jd/store_sku.go b/business/partner/purchase/jd/store_sku.go index e6622a217..48c25baec 100644 --- a/business/partner/purchase/jd/store_sku.go +++ b/business/partner/purchase/jd/store_sku.go @@ -281,3 +281,79 @@ func constrainPrice(price int) int { } return price } + +func (p *PurchaseHandler) GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) { + db := dao.GetDB() + skuList, err := dao.GetSkus(db, nil, nil, []int{model.SkuStatusNormal}, nil) + if err != nil { + return nil, err + } + var skuInfoList []*jdapi.BaseStockCenterRequest + skuMap := make(map[int64]int) + for _, sku := range skuList { + if !jxutils.IsFakeID(sku.JdID) { + skuInfoList = append(skuInfoList, &jdapi.BaseStockCenterRequest{ + SkuId: sku.JdID, + }) + skuMap[sku.JdID] = sku.ID + } + } + for _, storeID := range storeIDs { + storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJD) + if err != nil { + return nil, err + } + for _, sku := range skuInfoList { + sku.StationNo = storeDetail.VendorStoreID + } + task := tasksch.NewParallelTask("jd 获取京东门店商品信息", tasksch.NewParallelConfig().SetBatchSize(jdapi.MaxStoreSkuBatchSize), ctx, + func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { + batchSkuInfoList := make([]*jdapi.BaseStockCenterRequest, len(batchItemList)) + batchSkuList := make([]int64, len(batchItemList)) + for k, v := range batchItemList { + batchSkuInfoList[k] = v.(*jdapi.BaseStockCenterRequest) + batchSkuList[k] = batchSkuInfoList[k].SkuId + } + stockInfo, err := api.JdAPI.QueryOpenUseable(batchSkuInfoList) + if err != nil { + return nil, err + } + priceInfo, err := api.JdAPI.GetStationInfoList(storeDetail.VendorStoreID, batchSkuList) + if err != nil { + return nil, err + } + var batchStoreSkuList []*model.StoreSkuBind + batchStoreSkuMap := make(map[int64]*model.StoreSkuBind) + for _, v := range stockInfo { + if v.UsableQty > 0 { + batchSku := &model.StoreSkuBind{ + StoreID: storeID, + SkuID: skuMap[v.SkuID], + } + if v.Vendibility == 0 { + batchSku.Status = model.SkuStatusNormal + } else { + batchSku.Status = model.SkuStatusDontSale + } + batchStoreSkuMap[v.SkuID] = batchSku + batchStoreSkuList = append(batchStoreSkuList, batchSku) + } + } + for _, v := range priceInfo { + if storeSku := batchStoreSkuMap[v.SkuID]; storeSku != nil { + storeSku.Price = int(v.Price) + } + } + return batchStoreSkuList, err + }, skuInfoList) + tasksch.AddChild(parentTask, task).Run() + result, err := task.GetResult(0) + if err != nil { + return nil, err + } + for _, v := range result { + storeSkuList = append(storeSkuList, v.(*model.StoreSkuBind)) + } + } + return storeSkuList, err +} diff --git a/business/partner/purchase/mtwm/store_sku.go b/business/partner/purchase/mtwm/store_sku.go index efd518486..36f9b013b 100644 --- a/business/partner/purchase/mtwm/store_sku.go +++ b/business/partner/purchase/mtwm/store_sku.go @@ -497,3 +497,7 @@ func (p *PurchaseHandler) DeleteRemoteStoreSkus(ctx *jxcontext.Context, parentTa } return rootTask.ID, err } + +func (p *PurchaseHandler) GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) { + return storeSkuList, err +} diff --git a/business/partner/purchase/weimob/wsc/store_sku.go b/business/partner/purchase/weimob/wsc/store_sku.go index f9dd0e7b2..a8cbd2cc1 100644 --- a/business/partner/purchase/weimob/wsc/store_sku.go +++ b/business/partner/purchase/weimob/wsc/store_sku.go @@ -318,3 +318,7 @@ func composeFakeDelName(name string) string { // func ComposeJXVendorSkuIDFromGoodsAndSkuID(goodsID, skuID int64) (vendorSkuID string) { // return utils.Int64ToStr(skuID) + "," + utils.Int64ToStr(goodsID) // } + +func (p *PurchaseHandler) GetStoresSku(ctx *jxcontext.Context, parentTask tasksch.ITask, storeIDs []int) (storeSkuList []*model.StoreSkuBind, err error) { + return storeSkuList, err +} diff --git a/controllers/cms_store_sku.go b/controllers/cms_store_sku.go index e8f34e053..42f630733 100644 --- a/controllers/cms_store_sku.go +++ b/controllers/cms_store_sku.go @@ -304,3 +304,22 @@ func (c *StoreSkuController) HandleStoreOpRequest() { return retVal, "", err }) } + +// @Title 根据厂家门店商品信息相应刷新本地数据 +// @Description 根据厂家门店商品信息相应刷新本地数据 +// @Param token header string true "认证token" +// @Param storeIDs formData string true "门店ID列表" +// @Param vendorID formData int true "厂商ID(当前只支持京东)" +// @Param isAsync formData bool false "是否异步操作" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /RefreshStoresSkuByVendor [put] +func (c *StoreSkuController) RefreshStoresSkuByVendor() { + c.callRefreshStoresSkuByVendor(func(params *tStoreSkuRefreshStoresSkuByVendorParams) (retVal interface{}, errCode string, err error) { + var storeIDList []int + if err = jxutils.Strings2Objs(params.StoreIDs, &storeIDList); err == nil { + retVal, err = cms.RefreshStoresSkuByVendor(params.Ctx, storeIDList, params.VendorID, params.IsAsync) + } + return retVal, "", err + }) +} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index ce5c318ca..3f34535c9 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -1222,6 +1222,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"], + beego.ControllerComments{ + Method: "RefreshStoresSkuByVendor", + Router: `/RefreshStoresSkuByVendor`, + AllowHTTPMethods: []string{"put"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreSkuController"], beego.ControllerComments{ Method: "SyncStoresSkus",