门店评分
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
|
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.CasbinRule{})
|
||||||
orm.RegisterModel(&model.SensitiveWord{})
|
orm.RegisterModel(&model.SensitiveWord{})
|
||||||
|
orm.RegisterModel(&model.StoreScore{})
|
||||||
// create table
|
// create table
|
||||||
orm.RunSyncdb("default", false, true)
|
orm.RunSyncdb("default", false, true)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user