门店评分

This commit is contained in:
Rosy-zhudan
2019-09-05 08:37:23 +08:00
parent 2afa452d78
commit 4eaccdc917
7 changed files with 297 additions and 63 deletions

View File

@@ -2,8 +2,9 @@ package misc
import (
"testing"
"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
)
func TestStartOrEndOpStore(t *testing.T) {
StartOrEndOpStore(1, true, 0, 0, false, true)
StartOrEndOpStore(jxcontext.AdminCtx, true, []int{}, []int{}, 0, 0, false, true)
}

View File

@@ -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 >= StoreOpenTimeNormalScore {
if opTime >= StoreOpenTimeNormalTime {
storeScoreData.StoreOpenTime = ItemScore
} else {
decScore := int(math.Round(StoreOpenTimeNormalScore - opTime))
decScore := int(math.Round(StoreOpenTimeNormalTime - 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)
}

View File

@@ -0,0 +1,17 @@
package misc
import (
"testing"
"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"
)
func init() {
testinit.Init()
api2.Init()
}
func TestScoreStore(t *testing.T) {
ScoreStore(jxcontext.AdminCtx, []int{})
}

View File

@@ -73,3 +73,8 @@ type OrderFinancialSkuExt struct {
OrderSkuFinancial
Image string `json:"image"`
}
type OrderPickupTime struct {
StatusTime time.Time `orm:"type(datetime)" json:"statusTime"`
PickDeadline time.Time `orm:"type(datetime)" json:"pickDeadline"`
}

View File

@@ -290,3 +290,46 @@ func GetStoreAfsOrderSkuList(db *DaoDB, storeIDs []int, finishedAtBegin, finishe
err = GetRows(db, &afsSkuList, sql, sqlParams...)
return afsSkuList, err
}
func GetDailyFinishOrderList(db *DaoDB, storeID int) (orderList []*model.OrderPickupTime, err error) {
sql := `
select t2.status_time, t1.pick_deadline
from goods_order t1
left join order_status t2 on t1.vendor_order_id = t2.vendor_order_id
where t1.jx_store_id = ? and t2.order_type = ? and t2.status = ? and DATE_FORMAT(t1.order_finished_at, '%Y-%m-%d') = DATE_FORMAT(NOW(), '%Y-%m-%d')
`
sqlParams := []interface{}{
storeID,
1,
model.OrderStatusFinishedPickup,
}
return orderList, GetRows(db, &orderList, sql, sqlParams...)
}
func GetDailyBadCommentOrderCount(db *DaoDB, storeID int) (num int64, err error) {
sql := `select count(*) from jx_bad_comments where DATE_FORMAT(createtime, '%Y-%m-%d') = DATE_FORMAT(NOW(), '%Y-%m-%d') and jxstoreid = ?`
sqlParams := []interface{}{
storeID,
}
return ExecuteSQL(db, sql, sqlParams)
}
func GetDailyUnFinishOrderCount(db *DaoDB, storeID int) (num int64, err error) {
return GetDailyEndOrderCount(db, storeID, []int{model.OrderStatusCanceled})
}
func GetDailyFinishOrderCount(db *DaoDB, storeID int) (num int64, err error) {
return GetDailyEndOrderCount(db, storeID, []int{model.OrderStatusFinished})
}
func GetDailyEndOrderCount(db *DaoDB, storeID int, statusList []int) (num int64, err error) {
sql := `select count(*) from goods_order
where DATE_FORMAT(order_finished_at, '%Y-%m-%d') = DATE_FORMAT(NOW(), '%Y-%m-%d')
and jx_store_id = ?
and status in (` + GenQuestionMarks(len(statusList)) + `)`
sqlParams := []interface{}{
storeID,
}
sqlParams = append(sqlParams, statusList)
return ExecuteSQL(db, sql, sqlParams)
}

View File

@@ -6,18 +6,8 @@ import (
"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"]
// func InsertStoreScore(storeID int, scores map[string]int) error {
func InsertStoreScore(storeScore *model.StoreScore) error {
storeScore.CreatedAt = time.Now()
return CreateEntity(nil, storeScore)
}

View File

@@ -10,9 +10,9 @@ type StoreScore struct {
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"`
BadCommentOrder int `orm:"column(bad_comment_order)" json:"badCommentOrder"`
UnfinishOrder int `orm:"column(unfinish_order)" json:"unfinishOrder"`
LackStockOrder int `orm:"column(lack_stock_order)" json:"lackStockOrder"`
AbsentGoodsOrder int `orm:"column(absent_Goods_order)" json:"absentGoodsOrder"`
PromotionSku int `orm:"column(promotion_sku)" json:"promotionSku"`
FullVendor int `orm:"column(full_vendor)" json:"fullVendor"`
StoreRange int `orm:"column(store_range)" json:"storeRange"`