@@ -17,19 +17,28 @@ const (
ItemScore = 10
TotalScore = 100
StoreOpenTimeNormalScore = 12.0
SaleSkuNormalCount = 1000
SaleSkuScorePerUnit = float64 ( ItemScore ) / SaleSkuNormalCount
PromotionSkuNormalCount = 20
StoreOpenTimeNormalTime = 12.0 //小时
SaleSkuNormalCount = 1000
SaleSkuScorePerUnit = float64 ( ItemScore ) / SaleSkuNormalCount
PromotionSkuNormalCount = 20
AveragePickupTimeNormalTime = 10.0 //分钟
BadCommentOrderNormalRatio = 0.2
UnfinishOrderNormalRatio = 1.0
StoreRangeGoodRadius = 2000 //米
StoreRangeBadRadius = 1000 //米
)
var (
scoreStoreTimeList = [ ] string {
"23:30:00" ,
}
storeScoreDataWrapper StoreScoreDataWrapper
fullVendorList = map [ int ] bool {
model . VendorIDJD : true ,
model . VendorIDMTWM : true ,
model . VendorIDEBAI : true ,
}
allStoreSkus map [ int ] map [ int ] * cms . StoreSkuNameExt
)
type StoreScoreDataWrapper struct {
@@ -53,19 +62,6 @@ func (s *StoreScoreDataWrapper) InitData() {
s . storeScoreData = make ( map [ int ] * model . StoreScore )
}
/* 分日评分和周评分两种,周评分为最近一周的平均值
1 营业时间12小时及以上满分, 总分10分, 每少一个小时扣1分
2 可售商品数量大于1000, 总分10分, 按比例扣
3 平均捡货时间10分钟内为满分10分, 每超1分钟扣1分
4 差评订单和完成订单比例小于0.2%,得满分10分, 每增加0.1,%减1分
5 未完成订单和完成订单比小于1%, 得满分10分, 比例每增加5%, 分数减1
6 缺货订单和完成订单比小于1%得10分, 比例每增加0.1%减1分
7 促销品数量20个以上为满分10分, 每少2个扣1分
8 经营全平台满分10分, 每少一个平台扣2分(???一个都没有, 是否为0)
9 经营范围面积大于半径2km的圆得满分10分, 低于1km得分0
10 可售商品价格在附近5km内门店比较, 价格低于等于平均值的商品数占90%以上满分10分, 比例每降低10%减1分, 100%超标得0分
*/
func GetOpenTime ( opTimeList [ ] int16 ) ( opTime float64 ) {
opTime = 0
for index , _ := range opTimeList {
@@ -77,6 +73,7 @@ func GetOpenTime(opTimeList []int16) (opTime float64) {
return opTime
}
//营业时间12小时及以上满分, 总分10分, 每少一个小时扣1分
func ScoreStoreOpenTime ( storeList [ ] * cms . StoreExt ) {
for _ , storeInfo := range storeList {
storeID := storeInfo . ID
@@ -94,10 +91,10 @@ func ScoreStoreOpenTime(storeList []*cms.StoreExt) {
opTimeList := storeInfo . GetOpTimeList ( )
if len ( opTimeList ) > 0 && isStoreOpen && isVendorStoreOpen {
opTime := GetOpenTime ( opTimeList )
if opTime >= StoreOpenTimeNormalScor e {
if opTime >= StoreOpenTimeNormalTim e {
storeScoreData . StoreOpenTime = ItemScore
} else {
decScore := int ( math . Round ( StoreOpenTimeNormalScor e - opTime ) )
decScore := int ( math . Round ( StoreOpenTimeNormalTim e - opTime ) )
storeScoreData . StoreOpenTime = ItemScore - decScore
if storeScoreData . StoreOpenTime < 0 {
storeScoreData . StoreOpenTime = 0
@@ -108,6 +105,7 @@ func ScoreStoreOpenTime(storeList []*cms.StoreExt) {
}
}
//可售商品数量大于1000, 总分10分, 按比例扣
func ScoreSaleSkuCount ( ctx * jxcontext . Context , storeList [ ] * cms . StoreExt ) {
for _ , storeInfo := range storeList {
storeID := storeInfo . ID
@@ -128,25 +126,103 @@ func ScoreSaleSkuCount(ctx *jxcontext.Context, storeList []*cms.StoreExt) {
}
}
storeScoreData . SaleSkuCount = int ( math . Round ( float64 ( saleSkuCount ) * SaleSkuScorePerUnit ) )
if storeScoreData . SaleSkuCount > ItemScore {
storeScoreData . SaleSkuCount = ItemScore
}
}
}
func ScoreAveragePickupTime ( ) {
}
func ScoreBadReviewOrder ( ) {
}
func ScoreUnfinishOrder ( ) {
}
func ScoreLackStockOrder ( ) {
//平均捡货时间小于等于拣货截止时间为10分满分, 每超出1分钟, 减1分
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 {
totalScore := 0
for _ , value := range orderList {
statusTime := value . StatusTime . Unix ( )
pickDeadline := value . PickDeadline . Unix ( )
if statusTime <= pickDeadline {
totalScore += ItemScore
} else {
decScore := int ( math . Round ( float64 ( statusTime - pickDeadline ) / 60 ) )
tempScore := ItemScore - decScore
if tempScore < 0 {
tempScore = 0
}
totalScore += tempScore
}
}
storeScoreData . AveragePickupTime = totalScore / len ( orderList )
}
}
}
//差评订单和完成订单比例小于0.2%,得满分10分, 每增加0.1%减1分
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
}
}
}
}
//未完成订单和完成订单比小于1%, 得满分10分, 比例每增加5%, 分数减1
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
}
}
}
}
//缺货订单和完成订单比小于1%得10分, 比例每增加0.1%减1分
func ScoreAbsentGoodsOrder ( storeList [ ] * cms . StoreExt ) {
}
//促销品数量20个以上为满分10分, 每少2个扣1分
func ScorePromotionSku ( storeList [ ] * cms . StoreExt ) {
for _ , storeInfo := range storeList {
storeID := storeInfo . ID
@@ -163,7 +239,9 @@ func ScorePromotionSku(storeList []*cms.StoreExt) {
if err == nil {
actStoreSkuMap := make ( map [ int ] int )
for _ , value := range actStoreSkuList {
actStoreSkuMap [ value . SkuID ] = 1
if value . Type != model . ActSkuFake {
actStoreSkuMap [ value . SkuID ] = 1
}
}
promotionSkuCount := len ( actStoreSkuMap )
if promotionSkuCount >= PromotionSkuNormalCount {
@@ -179,6 +257,7 @@ func ScorePromotionSku(storeList []*cms.StoreExt) {
}
}
//经营全平台满分10分, 每少一个平台扣2分(???一个都没有, 是否为0)
func ScoreFullVendor ( storeList [ ] * cms . StoreExt ) {
fullVendorCount := len ( fullVendorList )
for _ , storeInfo := range storeList {
@@ -209,12 +288,108 @@ func ScoreFullVendor(storeList []*cms.StoreExt) {
}
}
func ScoreStoreRange ( ) {
func CalcPolygonArea ( points [ ] [ 2 ] float64 ) ( area float64 ) {
count := len ( points )
for i := 0 ; i < count - 1 ; i ++ {
area += ( points [ i ] [ 0 ] - points [ i + 1 ] [ 0 ] ) * ( points [ i ] [ 1 ] + points [ i + 1 ] [ 1 ] )
}
area += ( points [ count - 1 ] [ 0 ] - points [ 0 ] [ 0 ] ) * ( points [ count - 1 ] [ 1 ] + points [ 0 ] [ 1 ] )
area = math . Abs ( area ) / 2
return area
}
func ScoreSaleSkuPrice ( ) {
//经营范围面积大于半径2km的圆得满分10分, 低于1km得分0
func ScoreStoreRange ( 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 )
}
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
} else if area <= badArea {
storeScoreData . StoreRange = 0
} else {
diff := float64 ( goodArea - area )
ratio := float64 ( ItemScore ) / ( goodArea - badArea )
storeScoreData . StoreRange = ItemScore - int ( math . Round ( diff * ratio ) )
}
} else if storeInfo . DeliveryRangeType == model . DeliveryRangeTypeRadius {
deliveryRadius := utils . Str2Int64WithDefault ( storeInfo . DeliveryRange , 0 )
if deliveryRadius >= StoreRangeGoodRadius {
storeScoreData . StoreRange = ItemScore
} else if deliveryRadius <= StoreRangeBadRadius {
storeScoreData . StoreRange = 0
} else {
diff := float64 ( StoreRangeGoodRadius - deliveryRadius )
ratio := float64 ( ItemScore ) / ( StoreRangeGoodRadius - StoreRangeBadRadius )
storeScoreData . StoreRange = ItemScore - int ( math . Round ( diff * ratio ) )
}
}
}
}
func GetRangeStoreList ( storeID int , lng , lat , checkDist float64 , storeList [ ] * cms . StoreExt ) ( outStoreList [ ] * cms . StoreExt ) {
for _ , storeInfo := range storeList {
if storeInfo . ID == storeID {
outStoreList = append ( outStoreList , storeInfo )
} else {
distance := jxutils . EarthDistance ( lng , lat , storeInfo . FloatLng , storeInfo . FloatLat )
if distance <= checkDist {
outStoreList = append ( outStoreList , storeInfo )
}
}
}
return outStoreList
}
func GetAllStoreSkus ( ctx * jxcontext . Context , storeList [ ] * cms . StoreExt ) {
allStoreSkus = make ( map [ int ] map [ int ] * cms . StoreSkuNameExt )
for _ , storeInfo := range storeList {
storeID := storeInfo . ID
jxSkuInfoData , _ := cms . GetStoreSkus ( ctx , storeID , [ ] int { } , true , "" , true , map [ string ] interface { } { } , 0 , - 1 )
jxSkuMapData := make ( map [ int ] * cms . StoreSkuNameExt )
for _ , value := range jxSkuInfoData . SkuNames {
for _ , skuInfo := range value . Skus2 {
saleStatus := jxutils . MergeSkuStatus ( skuInfo . SkuStatus , skuInfo . StoreSkuStatus )
if saleStatus == model . SkuStatusNormal {
jxSkuMapData [ skuInfo . SkuID ] = value
}
}
}
allStoreSkus [ storeID ] = jxSkuMapData
}
}
func ClearAllStoreSkus ( ) {
allStoreSkus = nil
}
//得到所有门店的商品, map[int]*cms.StoreSkuNamesInfo
//得到距离某个门店5KM内的所有门店信息
//可售商品价格在附近5km内门店比较, 价格低于等于平均值的商品数占90%以上满分10分, 比例每降低10%减1分, 100%超标得0分
func ScoreSaleSkuPrice ( ctx * jxcontext . Context , storeList [ ] * cms . StoreExt ) {
GetAllStoreSkus ( ctx , storeList )
for _ , storeInfo := range storeList {
storeID := storeInfo . ID
storeScoreData := storeScoreDataWrapper . GetData ( storeID )
if storeScoreData == nil {
storeScoreData = & model . StoreScore { }
storeScoreData . StoreID = storeID
storeScoreDataWrapper . AppendData ( storeScoreData )
}
//rangeStoreList := GetRangeStoreList(storeID, storeInfo.FloatLng, storeInfo.FloatLat, 5, storeList)
}
}
func GetFilterStoreListEx ( storeList [ ] * cms . StoreExt , storeIDMap map [ int ] int ) ( outStoreList [ ] * cms . StoreExt ) {
@@ -245,6 +420,14 @@ func ScoreStore(ctx *jxcontext.Context, storeIDList []int) (retVal interface{},
ScoreStoreOpenTime ( storeList )
ScoreSaleSkuCount ( ctx , storeList )
ScoreAveragePickupTime ( storeList )
ScoreBadCommentOrder ( storeList )
ScoreUnfinishOrder ( storeList )
ScoreAbsentGoodsOrder ( storeList )
ScorePromotionSku ( storeList )
ScoreFullVendor ( storeList )
ScoreStoreRange ( storeList )
ScoreSaleSkuPrice ( ctx , storeList )
InsertStoreScore ( )
return retVal , err
@@ -252,17 +435,12 @@ func ScoreStore(ctx *jxcontext.Context, storeIDList []int) (retVal interface{},
func InsertStoreScore ( ) {
for _ , value := range storeScoreDataWrapper . storeScoreData {
scores := make ( map [ string ] int )
scores [ "storeOpenTime" ] = value . StoreOpenTime
scores [ "saleSkuCount" ] = value . SaleSkuCount
scores [ "averagePickupTime" ] = value . AveragePickupTime
scores [ "badReviewOrder" ] = value . BadReviewOrder
scores [ "unfinishOrder" ] = value . UnfinishOrder
scores [ "lackStockOrder" ] = value . LackStockOrder
scores [ "promotionSku" ] = value . PromotionSku
scores [ "fullVendor" ] = value . FullVendor
scores [ "storeRange" ] = value . StoreRange
scores [ "saleSkuPrice" ] = value . SaleSkuPrice
dao . InsertStoreScore ( value . StoreID , scores )
dao . InsertStoreScore ( value )
}
}
func ScheduleScoreStore ( ) {
ScheduleTimerFunc ( "ScheduleScoreStore" , func ( ) {
ScoreStore ( jxcontext . AdminCtx , [ ] int { } )
} , scoreStoreTimeList )
}