diff --git a/business/jxstore/misc/store_score.go b/business/jxstore/misc/store_score.go index bc34910b4..9d016ed65 100644 --- a/business/jxstore/misc/store_score.go +++ b/business/jxstore/misc/store_score.go @@ -2,6 +2,7 @@ package misc import ( "math" + "reflect" "sync" "time" @@ -24,8 +25,9 @@ const ( AveragePickupTimeNormalTime = 10.0 //分钟 BadCommentOrderNormalRatio = 0.2 UnfinishOrderNormalRatio = 1.0 - StoreRangeGoodRadius = 2000 //米 - StoreRangeBadRadius = 1000 //米 + StoreRangeGoodRadius = 2.0 //千米 + StoreRangeBadRadius = 1.0 //千米 + SaleSkuPriceRatio = 90 //百分比 ) var ( @@ -46,28 +48,32 @@ type StoreScoreDataWrapper struct { locker sync.RWMutex } -func (s *StoreScoreDataWrapper) AppendData(data *model.StoreScore) { - s.locker.Lock() - defer s.locker.Unlock() - s.storeScoreData[data.StoreID] = data -} - -func (s *StoreScoreDataWrapper) GetData(storeID int) *model.StoreScore { - s.locker.RLock() - defer s.locker.RUnlock() - return s.storeScoreData[storeID] -} - func (s *StoreScoreDataWrapper) InitData() { s.storeScoreData = make(map[int]*model.StoreScore) } +func (s *StoreScoreDataWrapper) SetData(storeID int, valueName string, value int) { + s.locker.Lock() + defer s.locker.Unlock() + data := s.storeScoreData[storeID] + if data == nil { + data = &model.StoreScore{} + data.StoreID = storeID + s.storeScoreData[storeID] = data + } + valueInfo := reflect.ValueOf(data).Elem() + valueInfo.FieldByName(valueName).SetInt(int64(value)) +} + func GetOpenTime(opTimeList []int16) (opTime float64) { opTime = 0 for index, _ := range opTimeList { if index%2 == 1 { - diff := opTimeList[index] - opTimeList[index-1] - opTime += float64(diff) / 100 + endTime := opTimeList[index] + startTime := opTimeList[index-1] + diffHour := float64(endTime/100 - startTime/100) + diffMin := float64(endTime%100-startTime%100) / 60 + opTime += diffHour + diffMin } } return opTime @@ -79,27 +85,23 @@ func ScoreStoreOpenTime(storeList []*cms.StoreExt) { storeID := storeInfo.ID storeStatus := storeInfo.Status isStoreOpen := storeStatus == model.StoreStatusOpened - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) - } for _, storeMap := range storeInfo.StoreMaps { vendorStoreStatus := int(utils.MustInterface2Int64(storeMap["status"])) isVendorStoreOpen := vendorStoreStatus == model.StoreStatusOpened opTimeList := storeInfo.GetOpTimeList() if len(opTimeList) > 0 && isStoreOpen && isVendorStoreOpen { + finalScore := 0 opTime := GetOpenTime(opTimeList) if opTime >= StoreOpenTimeNormalTime { - storeScoreData.StoreOpenTime = ItemScore + finalScore = ItemScore } else { decScore := int(math.Round(StoreOpenTimeNormalTime - opTime)) - storeScoreData.StoreOpenTime = ItemScore - decScore - if storeScoreData.StoreOpenTime < 0 { - storeScoreData.StoreOpenTime = 0 + finalScore = ItemScore - decScore + if finalScore < 0 { + finalScore = 0 } } + storeScoreDataWrapper.SetData(storeID, "StoreOpenTime", finalScore) } } } @@ -109,25 +111,23 @@ func ScoreStoreOpenTime(storeList []*cms.StoreExt) { func ScoreSaleSkuCount(ctx *jxcontext.Context, storeList []*cms.StoreExt) { for _, storeInfo := range storeList { storeID := storeInfo.ID - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) - } - jxSkuInfoData, _ := cms.GetStoreSkus(ctx, storeID, []int{}, true, "", true, map[string]interface{}{}, 0, -1) - saleSkuCount := 0 - for _, value := range jxSkuInfoData.SkuNames { - for _, skuInfo := range value.Skus2 { - saleStatus := jxutils.MergeSkuStatus(skuInfo.SkuStatus, skuInfo.StoreSkuStatus) - if saleStatus == model.SkuStatusNormal { - saleSkuCount++ + jxSkuInfoData, err := cms.GetStoreSkus(ctx, storeID, []int{}, true, "", true, map[string]interface{}{}, 0, -1) + if err == nil && len(jxSkuInfoData.SkuNames) > 0 { + finalScore := 0 + saleSkuCount := 0 + for _, value := range jxSkuInfoData.SkuNames { + for _, skuInfo := range value.Skus2 { + saleStatus := jxutils.MergeSkuStatus(skuInfo.SkuStatus, skuInfo.StoreSkuStatus) + if saleStatus == model.SkuStatusNormal { + saleSkuCount++ + } } } - } - storeScoreData.SaleSkuCount = int(math.Round(float64(saleSkuCount) * SaleSkuScorePerUnit)) - if storeScoreData.SaleSkuCount > ItemScore { - storeScoreData.SaleSkuCount = ItemScore + finalScore = int(math.Round(float64(saleSkuCount) * SaleSkuScorePerUnit)) + if finalScore > ItemScore { + finalScore = ItemScore + } + storeScoreDataWrapper.SetData(storeID, "SaleSkuCount", finalScore) } } } @@ -136,15 +136,10 @@ func ScoreSaleSkuCount(ctx *jxcontext.Context, storeList []*cms.StoreExt) { func ScoreAveragePickupTime(storeList []*cms.StoreExt) { for _, storeInfo := range storeList { storeID := storeInfo.ID - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) - } db := dao.GetDB() orderList, err := dao.GetDailyFinishOrderList(db, storeID) - if err == nil { + orderListCount := len(orderList) + if err == nil && orderListCount > 0 { totalScore := 0 for _, value := range orderList { statusTime := value.StatusTime.Unix() @@ -160,7 +155,8 @@ func ScoreAveragePickupTime(storeList []*cms.StoreExt) { totalScore += tempScore } } - storeScoreData.AveragePickupTime = totalScore / len(orderList) + finalScore := totalScore / orderListCount + storeScoreDataWrapper.SetData(storeID, "AveragePickupTime", finalScore) } } } @@ -169,24 +165,22 @@ func ScoreAveragePickupTime(storeList []*cms.StoreExt) { func ScoreBadCommentOrder(storeList []*cms.StoreExt) { for _, storeInfo := range storeList { storeID := storeInfo.ID - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) - } db := dao.GetDB() badCommentOrderCount, _ := dao.GetDailyBadCommentOrderCount(db, storeID) finishOrderCount, _ := dao.GetDailyFinishOrderCount(db, storeID) - badCommentOrderRatio := float64(badCommentOrderCount) * 100 / float64(finishOrderCount) - if badCommentOrderRatio <= BadCommentOrderNormalRatio { - storeScoreData.BadCommentOrder = ItemScore - } else { - decScore := int(math.Round((badCommentOrderRatio - BadCommentOrderNormalRatio) / 0.1)) - storeScoreData.BadCommentOrder = ItemScore - decScore - if storeScoreData.BadCommentOrder < 0 { - storeScoreData.BadCommentOrder = 0 + if finishOrderCount > 0 { + finalScore := 0 + badCommentOrderRatio := float64(badCommentOrderCount) * 100 / float64(finishOrderCount) + if badCommentOrderRatio <= BadCommentOrderNormalRatio { + finalScore = ItemScore + } else { + decScore := int(math.Round((badCommentOrderRatio - BadCommentOrderNormalRatio) / 0.1)) + finalScore = ItemScore - decScore + if finalScore < 0 { + finalScore = 0 + } } + storeScoreDataWrapper.SetData(storeID, "BadCommentOrder", finalScore) } } } @@ -195,24 +189,22 @@ func ScoreBadCommentOrder(storeList []*cms.StoreExt) { func ScoreUnfinishOrder(storeList []*cms.StoreExt) { for _, storeInfo := range storeList { storeID := storeInfo.ID - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) - } db := dao.GetDB() unFinishOrderCount, _ := dao.GetDailyUnFinishOrderCount(db, storeID) finishOrderCount, _ := dao.GetDailyFinishOrderCount(db, storeID) - unfinishOrderRatio := float64(unFinishOrderCount) * 100 / float64(finishOrderCount) - if unfinishOrderRatio <= UnfinishOrderNormalRatio { - storeScoreData.UnfinishOrder = ItemScore - } else { - decScore := int(math.Round((unfinishOrderRatio - UnfinishOrderNormalRatio) / 5)) - storeScoreData.UnfinishOrder = ItemScore - decScore - if storeScoreData.UnfinishOrder < 0 { - storeScoreData.UnfinishOrder = 0 + if finishOrderCount > 0 { + finalScore := 0 + unfinishOrderRatio := float64(unFinishOrderCount) * 100 / float64(finishOrderCount) + if unfinishOrderRatio <= UnfinishOrderNormalRatio { + finalScore = ItemScore + } else { + decScore := int(math.Round((unfinishOrderRatio - UnfinishOrderNormalRatio) / 5)) + finalScore = ItemScore - decScore + if finalScore < 0 { + finalScore = 0 + } } + storeScoreDataWrapper.SetData(storeID, "UnfinishOrder", finalScore) } } } @@ -226,17 +218,12 @@ func ScoreAbsentGoodsOrder(storeList []*cms.StoreExt) { func ScorePromotionSku(storeList []*cms.StoreExt) { for _, storeInfo := range storeList { storeID := storeInfo.ID - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) - } db := dao.GetDB() beginTime := time.Now() endTime := time.Now() actStoreSkuList, err := dao.GetEffectiveActStoreSkuInfo(db, -1, []int{}, []int{storeID}, []int{}, beginTime, endTime) - if err == nil { + if err == nil && len(actStoreSkuList) > 0 { + finalScore := 0 actStoreSkuMap := make(map[int]int) for _, value := range actStoreSkuList { if value.Type != model.ActSkuFake { @@ -245,14 +232,15 @@ func ScorePromotionSku(storeList []*cms.StoreExt) { } promotionSkuCount := len(actStoreSkuMap) if promotionSkuCount >= PromotionSkuNormalCount { - storeScoreData.PromotionSku = ItemScore + finalScore = ItemScore } else { decScore := (PromotionSkuNormalCount - promotionSkuCount) / 2 - storeScoreData.PromotionSku = ItemScore - decScore - if storeScoreData.PromotionSku < 0 { - storeScoreData.PromotionSku = 0 + finalScore = ItemScore - decScore + if finalScore < 0 { + finalScore = 0 } } + storeScoreDataWrapper.SetData(storeID, "PromotionSku", finalScore) } } } @@ -261,15 +249,10 @@ func ScorePromotionSku(storeList []*cms.StoreExt) { func ScoreFullVendor(storeList []*cms.StoreExt) { fullVendorCount := len(fullVendorList) for _, storeInfo := range storeList { + finalScore := 0 storeID := storeInfo.ID storeStatus := storeInfo.Status isStoreOpen := storeStatus == model.StoreStatusOpened - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) - } count := 0 for _, storeMap := range storeInfo.StoreMaps { vendorStoreStatus := int(utils.MustInterface2Int64(storeMap["status"])) @@ -280,11 +263,12 @@ func ScoreFullVendor(storeList []*cms.StoreExt) { } } if count == fullVendorCount { - storeScoreData.FullVendor = ItemScore + finalScore = ItemScore } else { decScore := (fullVendorCount - count) * 2 - storeScoreData.FullVendor = ItemScore - decScore + finalScore = ItemScore - decScore } + storeScoreDataWrapper.SetData(storeID, "FullVendor", finalScore) } } @@ -302,42 +286,39 @@ func CalcPolygonArea(points [][2]float64) (area float64) { //经营范围面积大于半径2km的圆得满分10分,低于1km得分0 func ScoreStoreRange(storeList []*cms.StoreExt) { for _, storeInfo := range storeList { + finalScore := 0 storeID := storeInfo.ID - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) - } if storeInfo.DeliveryRangeType == model.DeliveryRangeTypePolygon { points := jxutils.CoordinateStr2Points(storeInfo.DeliveryRange) area := CalcPolygonArea(points) goodArea := math.Pi * StoreRangeGoodRadius * StoreRangeGoodRadius badArea := math.Pi * StoreRangeBadRadius * StoreRangeBadRadius if area >= goodArea { - storeScoreData.StoreRange = ItemScore + finalScore = ItemScore } else if area <= badArea { - storeScoreData.StoreRange = 0 + finalScore = 0 } else { - diff := float64(goodArea - area) + diff := goodArea - area ratio := float64(ItemScore) / (goodArea - badArea) - storeScoreData.StoreRange = ItemScore - int(math.Round(diff*ratio)) + finalScore = ItemScore - int(math.Round(diff*ratio)) } } else if storeInfo.DeliveryRangeType == model.DeliveryRangeTypeRadius { - deliveryRadius := utils.Str2Int64WithDefault(storeInfo.DeliveryRange, 0) + deliveryRadius := utils.Str2Float64WithDefault(storeInfo.DeliveryRange, 0) if deliveryRadius >= StoreRangeGoodRadius { - storeScoreData.StoreRange = ItemScore + finalScore = ItemScore } else if deliveryRadius <= StoreRangeBadRadius { - storeScoreData.StoreRange = 0 + finalScore = 0 } else { - diff := float64(StoreRangeGoodRadius - deliveryRadius) + diff := StoreRangeGoodRadius - deliveryRadius ratio := float64(ItemScore) / (StoreRangeGoodRadius - StoreRangeBadRadius) - storeScoreData.StoreRange = ItemScore - int(math.Round(diff*ratio)) + finalScore = ItemScore - int(math.Round(diff*ratio)) } } + storeScoreDataWrapper.SetData(storeID, "StoreRange", finalScore) } } +//得到距离某个门店多少KM内的所有门店信息 func GetRangeStoreList(storeID int, lng, lat, checkDist float64, storeList []*cms.StoreExt) (outStoreList []*cms.StoreExt) { for _, storeInfo := range storeList { if storeInfo.ID == storeID { @@ -353,6 +334,7 @@ func GetRangeStoreList(storeID int, lng, lat, checkDist float64, storeList []*cm return outStoreList } +//得到所有门店的商品 func GetAllStoreSkus(ctx *jxcontext.Context, storeList []*cms.StoreExt) { allStoreSkus = make(map[int]map[int]*cms.StoreSkuNameExt) for _, storeInfo := range storeList { @@ -375,21 +357,64 @@ func ClearAllStoreSkus() { allStoreSkus = nil } -//得到所有门店的商品,map[int]*cms.StoreSkuNamesInfo -//得到距离某个门店5KM内的所有门店信息 +//得到给定门店列表里的同一SkuID商品的平均价格 +func GetStoreSkusAveragePrice(storeList []*cms.StoreExt) map[int]int { + skusTotalPrice := make(map[int]int) + skusCount := make(map[int]int) + skusAveragePrice := make(map[int]int) + for _, storeInfo := range storeList { + storeID := storeInfo.ID + for _, value := range allStoreSkus[storeID] { + skuID := value.Skus2[0].SkuID + skuPrice := value.Skus2[0].BindPrice + skusTotalPrice[skuID] += skuPrice + skusCount[skuID]++ + } + } + for id, totalPrice := range skusTotalPrice { + skusAveragePrice[id] = int(math.Round(float64(totalPrice) / float64(skusCount[id]))) + } + return skusAveragePrice +} + +func GetSkusCountLessEqualAvgPrice(storeID int, skusAveragePrice map[int]int) (count int) { + for _, value := range allStoreSkus[storeID] { + skuID := value.Skus2[0].SkuID + skuPrice := value.Skus2[0].BindPrice + skuAvgPrice := skusAveragePrice[skuID] + if skuPrice <= skuAvgPrice { + count++ + } + } + + return count +} + //可售商品价格在附近5km内门店比较,价格低于等于平均值的商品数占90%以上满分10分,比例每降低10%减1分,100%超标得0分 func ScoreSaleSkuPrice(ctx *jxcontext.Context, storeList []*cms.StoreExt) { GetAllStoreSkus(ctx, storeList) for _, storeInfo := range storeList { + finalScore := 0 storeID := storeInfo.ID - storeScoreData := storeScoreDataWrapper.GetData(storeID) - if storeScoreData == nil { - storeScoreData = &model.StoreScore{} - storeScoreData.StoreID = storeID - storeScoreDataWrapper.AppendData(storeScoreData) + totalCount := len(allStoreSkus[storeID]) + if totalCount > 0 { + rangeStoreList := GetRangeStoreList(storeID, storeInfo.FloatLng, storeInfo.FloatLat, 5, storeList) + skusAveragePrice := GetStoreSkusAveragePrice(rangeStoreList) + count := GetSkusCountLessEqualAvgPrice(storeID, skusAveragePrice) + ratio := int(math.Round(float64(count) * 100 / float64(totalCount))) + if ratio >= SaleSkuPriceRatio { + finalScore = ItemScore + } else { + decScore := (SaleSkuPriceRatio - ratio) / 10 + finalScore = ItemScore - decScore + if finalScore < 0 { + finalScore = 0 + } + } + storeScoreDataWrapper.SetData(storeID, "SaleSkuPrice", finalScore) } - //rangeStoreList := GetRangeStoreList(storeID, storeInfo.FloatLng, storeInfo.FloatLat, 5, storeList) } + ClearAllStoreSkus() } func GetFilterStoreListEx(storeList []*cms.StoreExt, storeIDMap map[int]int) (outStoreList []*cms.StoreExt) { diff --git a/business/jxstore/misc/store_score_test.go b/business/jxstore/misc/store_score_test.go index 7d4798157..01e0a42bb 100644 --- a/business/jxstore/misc/store_score_test.go +++ b/business/jxstore/misc/store_score_test.go @@ -2,9 +2,16 @@ package misc import ( "testing" + + "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" "git.rosy.net.cn/jx-callback/globals/api2" "git.rosy.net.cn/jx-callback/globals/testinit" - "git.rosy.net.cn/jx-callback/business/jxutils/jxcontext" + + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/ebai" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/elm" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/jd" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm" + _ "git.rosy.net.cn/jx-callback/business/partner/purchase/weimob/wsc" ) func init() { diff --git a/controllers/temp_op.go b/controllers/temp_op.go index ca73868e1..dc3603980 100644 --- a/controllers/temp_op.go +++ b/controllers/temp_op.go @@ -316,3 +316,21 @@ func (c *TempOpController) CheckSkuDiffBetweenJxAndVendor() { return retVal, "", err }) } + +// @Title 门店评分 +// @Description 门店评分 +// @Param token header string true "认证token" +// @Param storeIDs formData string false "京西门店ID列表" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /ScoreStore [post] +func (c *TempOpController) ScoreStore() { + c.callScoreStore(func(params *tTempopScoreStoreParams) (retVal interface{}, errCode string, err error) { + var storeIDList []int + if err = jxutils.Strings2Objs(params.StoreIDs, &storeIDList); err == nil { + misc.ScoreStore(params.Ctx, storeIDList) + } + + return retVal, "", err + }) +} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index c6cd714d0..b5564a00e 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -1674,6 +1674,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:TempOpController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:TempOpController"], + beego.ControllerComments{ + Method: "ScoreStore", + Router: `/ScoreStore`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:TempOpController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:TempOpController"], beego.ControllerComments{ Method: "TestIt",