门店评分
This commit is contained in:
268
business/jxstore/misc/store_score.go
Normal file
268
business/jxstore/misc/store_score.go
Normal file
@@ -0,0 +1,268 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
)
|
||||
|
||||
const (
|
||||
ItemScore = 10
|
||||
TotalScore = 100
|
||||
|
||||
StoreOpenTimeNormalScore = 12.0
|
||||
SaleSkuNormalCount = 1000
|
||||
SaleSkuScorePerUnit = float64(ItemScore) / SaleSkuNormalCount
|
||||
PromotionSkuNormalCount = 20
|
||||
)
|
||||
|
||||
var (
|
||||
storeScoreDataWrapper StoreScoreDataWrapper
|
||||
fullVendorList = map[int]bool{
|
||||
model.VendorIDJD: true,
|
||||
model.VendorIDMTWM: true,
|
||||
model.VendorIDEBAI: true,
|
||||
}
|
||||
)
|
||||
|
||||
type StoreScoreDataWrapper struct {
|
||||
storeScoreData map[int]*model.StoreScore
|
||||
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)
|
||||
}
|
||||
|
||||
/* 分日评分和周评分两种,周评分为最近一周的平均值
|
||||
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 {
|
||||
if index%2 == 1 {
|
||||
diff := opTimeList[index] - opTimeList[index-1]
|
||||
opTime += float64(diff) / 100
|
||||
}
|
||||
}
|
||||
return opTime
|
||||
}
|
||||
|
||||
func ScoreStoreOpenTime(storeList []*cms.StoreExt) {
|
||||
for _, storeInfo := range storeList {
|
||||
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 {
|
||||
opTime := GetOpenTime(opTimeList)
|
||||
if opTime >= StoreOpenTimeNormalScore {
|
||||
storeScoreData.StoreOpenTime = ItemScore
|
||||
} else {
|
||||
decScore := int(math.Round(StoreOpenTimeNormalScore - opTime))
|
||||
storeScoreData.StoreOpenTime = ItemScore - decScore
|
||||
if storeScoreData.StoreOpenTime < 0 {
|
||||
storeScoreData.StoreOpenTime = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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++
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreData.SaleSkuCount = int(math.Round(float64(saleSkuCount) * SaleSkuScorePerUnit))
|
||||
}
|
||||
}
|
||||
|
||||
func ScoreAveragePickupTime() {
|
||||
|
||||
}
|
||||
|
||||
func ScoreBadReviewOrder() {
|
||||
|
||||
}
|
||||
|
||||
func ScoreUnfinishOrder() {
|
||||
|
||||
}
|
||||
|
||||
func ScoreLackStockOrder() {
|
||||
|
||||
}
|
||||
|
||||
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 {
|
||||
actStoreSkuMap := make(map[int]int)
|
||||
for _, value := range actStoreSkuList {
|
||||
actStoreSkuMap[value.SkuID] = 1
|
||||
}
|
||||
promotionSkuCount := len(actStoreSkuMap)
|
||||
if promotionSkuCount >= PromotionSkuNormalCount {
|
||||
storeScoreData.PromotionSku = ItemScore
|
||||
} else {
|
||||
decScore := (PromotionSkuNormalCount - promotionSkuCount) / 2
|
||||
storeScoreData.PromotionSku = ItemScore - decScore
|
||||
if storeScoreData.PromotionSku < 0 {
|
||||
storeScoreData.PromotionSku = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ScoreFullVendor(storeList []*cms.StoreExt) {
|
||||
fullVendorCount := len(fullVendorList)
|
||||
for _, storeInfo := range storeList {
|
||||
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"]))
|
||||
isVendorStoreOpen := vendorStoreStatus == model.StoreStatusOpened
|
||||
opTimeList := storeInfo.GetOpTimeList()
|
||||
if len(opTimeList) > 0 && isStoreOpen && isVendorStoreOpen {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count == fullVendorCount {
|
||||
storeScoreData.FullVendor = ItemScore
|
||||
} else {
|
||||
decScore := (fullVendorCount - count) * 2
|
||||
storeScoreData.FullVendor = ItemScore - decScore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ScoreStoreRange() {
|
||||
|
||||
}
|
||||
|
||||
func ScoreSaleSkuPrice() {
|
||||
|
||||
}
|
||||
|
||||
func GetFilterStoreListEx(storeList []*cms.StoreExt, storeIDMap map[int]int) (outStoreList []*cms.StoreExt) {
|
||||
for _, storeInfo := range storeList {
|
||||
storeID := storeInfo.ID
|
||||
if len(storeIDMap) > 0 {
|
||||
if _, ok := storeIDMap[storeID]; !ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
for _, vendorStoreInfo := range storeInfo.StoreMaps {
|
||||
vendorID := int(utils.MustInterface2Int64(vendorStoreInfo["vendorID"]))
|
||||
if _, ok := fullVendorList[vendorID]; !ok {
|
||||
continue
|
||||
}
|
||||
outStoreList = append(outStoreList, storeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return outStoreList
|
||||
}
|
||||
|
||||
func ScoreStore(ctx *jxcontext.Context, storeIDList []int) (retVal interface{}, err error) {
|
||||
storeScoreDataWrapper.InitData()
|
||||
storeList, err := GetStoreList(ctx)
|
||||
storeIDMap := jxutils.IntList2Map(storeIDList)
|
||||
storeList = GetFilterStoreListEx(storeList, storeIDMap)
|
||||
|
||||
ScoreStoreOpenTime(storeList)
|
||||
ScoreSaleSkuCount(ctx, storeList)
|
||||
InsertStoreScore()
|
||||
|
||||
return retVal, err
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
23
business/model/dao/store_score.go
Normal file
23
business/model/dao/store_score.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
func InsertStoreScore(storeID int, scores map[string]int) error {
|
||||
storeScore := &model.StoreScore{CreatedAt: time.Now(), StoreID: storeID}
|
||||
storeScore.StoreOpenTime = scores["storeOpenTime"]
|
||||
storeScore.SaleSkuCount = scores["saleSkuCount"]
|
||||
storeScore.AveragePickupTime = scores["averagePickupTime"]
|
||||
storeScore.BadReviewOrder = scores["badReviewOrder"]
|
||||
storeScore.UnfinishOrder = scores["unfinishOrder"]
|
||||
storeScore.LackStockOrder = scores["lackStockOrder"]
|
||||
storeScore.PromotionSku = scores["promotionSku"]
|
||||
storeScore.FullVendor = scores["fullVendor"]
|
||||
storeScore.StoreRange = scores["storeRange"]
|
||||
storeScore.SaleSkuPrice = scores["saleSkuPrice"]
|
||||
|
||||
return CreateEntity(nil, storeScore)
|
||||
}
|
||||
20
business/model/store_score.go
Normal file
20
business/model/store_score.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type StoreScore struct {
|
||||
ID int `orm:"column(id)" json:"id"`
|
||||
CreatedAt time.Time `orm:"auto_now_add;type(datetime)" json:"createdAt"`
|
||||
StoreID int `orm:"column(store_id)" json:"storeID"`
|
||||
|
||||
StoreOpenTime int `orm:"column(store_open_time)" json:"storeOpenTime"`
|
||||
SaleSkuCount int `orm:"column(sale_sku_count)" json:"saleSkuCount"`
|
||||
AveragePickupTime int `orm:"column(average_pickup_time)" json:"averagePickupTime"`
|
||||
BadReviewOrder int `orm:"column(bad_review_order)" json:"badReviewOrder"`
|
||||
UnfinishOrder int `orm:"column(unfinish_order)" json:"unfinishOrder"`
|
||||
LackStockOrder int `orm:"column(lack_stock_order)" json:"lackStockOrder"`
|
||||
PromotionSku int `orm:"column(promotion_sku)" json:"promotionSku"`
|
||||
FullVendor int `orm:"column(full_vendor)" json:"fullVendor"`
|
||||
StoreRange int `orm:"column(store_range)" json:"storeRange"`
|
||||
SaleSkuPrice int `orm:"column(sale_sku_price)" json:"saleSkuPrice"`
|
||||
}
|
||||
@@ -242,3 +242,11 @@ func GetSingleStoreVendorIDs() (vendorIDs []int) {
|
||||
}
|
||||
return vendorIDs
|
||||
}
|
||||
|
||||
func IsMultiStore(vendorID int) bool {
|
||||
if _, ok := GetPurchasePlatformFromVendorID(vendorID).(IMultipleStoresHandler); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ func Init() {
|
||||
|
||||
orm.RegisterModel(&model.CasbinRule{})
|
||||
orm.RegisterModel(&model.SensitiveWord{})
|
||||
orm.RegisterModel(&model.StoreScore{})
|
||||
// create table
|
||||
orm.RunSyncdb("default", false, true)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user