Merge branch 'mark' of e.coding.net:rosydev/jx-callback into mark
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
@@ -18,17 +19,17 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
canWriteTolocal = false
|
||||
needStatistic = true
|
||||
canWriteTolocal = false
|
||||
needStatistic = true
|
||||
isFilterToBeCreateAndNotSale = true
|
||||
parallelCount = 5
|
||||
fileExt = ".xlsx"
|
||||
parallelCount = 5
|
||||
fileExt = ".xlsx"
|
||||
)
|
||||
|
||||
var (
|
||||
diffFileName = map[bool]string {
|
||||
true : "export/JXCSAndVendorSkuDiff",
|
||||
false : "export/JXGYAndVendorSkuDiff",
|
||||
diffFileName = map[bool]string{
|
||||
true: "export/JXCSAndVendorSkuDiff",
|
||||
false: "export/JXGYAndVendorSkuDiff",
|
||||
}
|
||||
vendorNameList = map[int]string{
|
||||
model.VendorIDMTWM: model.VendorChineseNames[model.VendorIDMTWM],
|
||||
@@ -86,7 +87,7 @@ type DiffData struct {
|
||||
SkuID string `json:"SkuID"`
|
||||
SyncStatus string `json:"同步状态"`
|
||||
ToBeCreate string `json:"待创建"`
|
||||
ToBeDel string `json:"待删除"`
|
||||
ToBeDel string `json:"待删除"`
|
||||
JxSkuName string `json:"京西商品名"`
|
||||
VendorSkuName string `json:"平台商品名"`
|
||||
JxStatus string `json:"京西可售状态"`
|
||||
@@ -94,11 +95,11 @@ type DiffData struct {
|
||||
}
|
||||
|
||||
type StatisticData struct {
|
||||
JxVendorStatus string `json:"京西和平台商品状态"`
|
||||
ToBeCreate string `json:"待创建"`
|
||||
JxSaleStatus string `json:"京西可售状态"`
|
||||
Count string `json:"数量"`
|
||||
Percent string `json:"占比"`
|
||||
JxVendorStatus string `json:"京西和平台商品状态"`
|
||||
ToBeCreate string `json:"待创建"`
|
||||
JxSaleStatus string `json:"京西可售状态"`
|
||||
Count string `json:"数量"`
|
||||
Percent string `json:"占比"`
|
||||
}
|
||||
|
||||
func (d *DiffDataLock) AppendData(vendorID int, diffData DiffData) {
|
||||
@@ -111,14 +112,6 @@ func (d *DiffDataLock) InitData() {
|
||||
d.diffDataMap = make(map[int][]DiffData)
|
||||
}
|
||||
|
||||
func IsMultiStore(vendorID int) bool {
|
||||
if _, ok := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IMultipleStoresHandler); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func InitMultiStoreData() {
|
||||
multiStoreAllSkuInfoMap = make(map[int]map[int]*partner.SkuNameInfo)
|
||||
multiStoreAllSkuInfoList = make(map[int][]*partner.StoreSkuInfo)
|
||||
@@ -141,7 +134,7 @@ func GetMultiStoreAllSkuInfo(ctx *jxcontext.Context, vendorMap map[int]bool) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if IsMultiStore(vendorID) {
|
||||
if partner.IsMultiStore(vendorID) {
|
||||
multiHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IMultipleStoresHandler)
|
||||
allSkuNameInfoList, err := multiHandler.GetSkus(ctx, 0, "", "")
|
||||
if err != nil {
|
||||
@@ -267,7 +260,7 @@ func CompareJxAndVendor(vendorID int, storeIDStr, vendorStoreID, storeName strin
|
||||
syncStatus := utils.Int2Str(int(status))
|
||||
toBeCreate := GetBoolName(model.IsSyncStatusNeedCreate(status))
|
||||
toBeDel := GetBoolName(model.IsSyncStatusNeedDelete(status))
|
||||
|
||||
|
||||
if vendorSkuInfo != nil {
|
||||
vendorSkuDetailName := vendorSkuInfo.SkuList[0].SkuName
|
||||
vendorSkuSaleStatusName := GetSkuSaleStatusName(vendorSkuInfo.SkuList[0].Status)
|
||||
@@ -276,7 +269,7 @@ func CompareJxAndVendor(vendorID int, storeIDStr, vendorStoreID, storeName strin
|
||||
isNameDiff := jxSkuDetailName != vendorSkuDetailName
|
||||
if jxSkuDetailName != "" && vendorSkuDetailName != "" && strings.Contains(jxSkuDetailName, vendorSkuDetailName) {
|
||||
isNameDiff = false
|
||||
}
|
||||
}
|
||||
if isSaleStatusDiff || isNameDiff {
|
||||
outPutData := DiffData{storeIDStr, vendorStoreID, storeName, skuIDStr, syncStatus, toBeCreate, toBeDel, jxSkuDetailName, vendorSkuDetailName, jxSkuSaleStatusName, vendorSkuSaleStatusName}
|
||||
diffData.AppendData(vendorID, outPutData)
|
||||
@@ -334,7 +327,7 @@ func CheckSkuDiffBetweenJxAndVendor(ctx *jxcontext.Context, vendorIDList []int,
|
||||
var filterJxSkuInfoMap map[int]*StoreSkuNameExt
|
||||
for _, vendorListValue := range jxStoreInfoListValue.StoreMaps {
|
||||
vendorID := int(utils.MustInterface2Int64(vendorListValue["vendorID"]))
|
||||
|
||||
|
||||
if isGetJxSkuInfoData == false { //only get once jx sku info list every store id
|
||||
isGetJxSkuInfoData = true
|
||||
jxSkuInfoData, _ := GetStoreSkus(ctx, storeID, []int{}, true, "", true, map[string]interface{}{}, 0, -1)
|
||||
@@ -344,7 +337,7 @@ func CheckSkuDiffBetweenJxAndVendor(ctx *jxcontext.Context, vendorIDList []int,
|
||||
vendorStoreID := utils.Interface2String(vendorListValue["vendorStoreID"])
|
||||
baseapi.SugarLogger.Debugf("CheckSkuDiffBetweenJxAndVendor storeID:%d vendorID:%d vendorStoreID:%s vendorListValue:%v", storeID, vendorID, vendorStoreID, vendorListValue)
|
||||
|
||||
if IsMultiStore(vendorID) {
|
||||
if partner.IsMultiStore(vendorID) {
|
||||
singleStoreHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler)
|
||||
allSkuInfoList := GetMultiStoreAllSkuInfoList(vendorID)
|
||||
skuBareInfoList, err := singleStoreHandler.GetStoreSkusBareInfo(ctx, task, storeID, vendorStoreID, allSkuInfoList)
|
||||
@@ -433,14 +426,14 @@ func AddStatisticSheet(sheetName string, data []DiffData) (sheet *excel.Obj2Exce
|
||||
}
|
||||
}
|
||||
for index, value := range count {
|
||||
percent[index] = float32(value * 100) / float32(totalCount)
|
||||
percent[index] = float32(value*100) / float32(totalCount)
|
||||
countStr := utils.Int2Str(value)
|
||||
percentStr := fmt.Sprintf("%.2f%%", percent[index])
|
||||
subStatisticData := statisticDataList[index]
|
||||
statisticData[index] = StatisticData{subStatisticData[0], subStatisticData[1], subStatisticData[2], countStr, percentStr}
|
||||
}
|
||||
sheetName = sheetName + "统计"
|
||||
sheet = &excel.Obj2ExcelSheetConfig {
|
||||
sheet = &excel.Obj2ExcelSheetConfig{
|
||||
Title: sheetName,
|
||||
Data: statisticData,
|
||||
CaptionList: statisticTitleList,
|
||||
|
||||
@@ -104,6 +104,7 @@ func Init() {
|
||||
ScheduleTimerFunc("UpdateActStatusByTime", func() {
|
||||
dao.UpdateActStatusByTime(dao.GetDB(), time.Now().Add(-48*time.Hour))
|
||||
}, updateActStatusTimeList)
|
||||
ScheduleScoreStore()
|
||||
}
|
||||
ScheduleTimerFunc("AutoSaleStoreSku", func() {
|
||||
cms.AutoSaleStoreSku(jxcontext.AdminCtx, nil, false)
|
||||
|
||||
@@ -2,8 +2,10 @@ 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, nil, nil, 0, 0, false, true)
|
||||
}
|
||||
|
||||
780
business/jxstore/misc/store_score.go
Normal file
780
business/jxstore/misc/store_score.go
Normal file
@@ -0,0 +1,780 @@
|
||||
package misc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/baseapi"
|
||||
"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/jxutils/tasksch"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
"git.rosy.net.cn/jx-callback/business/model/dao"
|
||||
"git.rosy.net.cn/jx-callback/globals/refutil"
|
||||
)
|
||||
|
||||
const (
|
||||
EnableScheduleScoreStore = true
|
||||
ParallelCount = 1
|
||||
|
||||
GoldMedalScore = 90
|
||||
SilverMedalScore = 80
|
||||
BronzeMedalScore = 70
|
||||
GoldMedalLevel = 1
|
||||
SilverMedalLevel = 2
|
||||
BronzeMedalLevel = 3
|
||||
|
||||
ItemTotalScore = 10
|
||||
|
||||
StoreOpenTimeNormalTime = 12.0 //小时
|
||||
SaleSkuNormalCount = 1000 //数量
|
||||
SaleSkuScorePerUnit = float64(ItemTotalScore) / SaleSkuNormalCount //分数
|
||||
PromotionSkuNormalCount = 20 //数量
|
||||
AveragePickupTimeNormalTime = 10.0 //分钟
|
||||
BadCommentOrderNormalRatio = 0.2 //百分比
|
||||
UnfinishOrderNormalRatio = 1.0 //百分比
|
||||
AbsentGoodsOrderNormalRatio = 1.0 //百分比
|
||||
StoreRangeGoodRadius = 2.0 //千米
|
||||
StoreRangeBadRadius = 1.0 //千米
|
||||
SaleSkuPriceRatio = 90 //千米
|
||||
SaleSkuCheckRange = 5.0 //千米
|
||||
)
|
||||
|
||||
var (
|
||||
scoreStoreTimeList = []string{
|
||||
"22:00:00",
|
||||
}
|
||||
scoreStoreCheckTimeEnd = "23:30:00"
|
||||
fullVendorList = map[int]bool{
|
||||
model.VendorIDJD: true,
|
||||
model.VendorIDMTWM: true,
|
||||
model.VendorIDEBAI: true,
|
||||
}
|
||||
storeScoreFieldName = []string{
|
||||
model.FieldStoreOpenTime,
|
||||
model.FieldSaleSkuCount,
|
||||
model.FieldAveragePickupTime,
|
||||
model.FieldBadCommentOrder,
|
||||
model.FieldUnfinishOrder,
|
||||
model.FieldAbsentGoodsOrder,
|
||||
model.FieldPromotionSku,
|
||||
model.FieldFullVendor,
|
||||
model.FieldStoreRange,
|
||||
model.FieldSaleSkuPrice,
|
||||
}
|
||||
|
||||
storeScoreDataWrapper StoreScoreDataWrapper
|
||||
allStoreSkusWrapper AllStoreSkusWrapper
|
||||
scoreDate time.Time
|
||||
isScoring bool
|
||||
)
|
||||
|
||||
type AllStoreSkusWrapper struct {
|
||||
allStoreSkus map[int]map[int]int
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
func (a *AllStoreSkusWrapper) InitData() {
|
||||
a.allStoreSkus = make(map[int]map[int]int)
|
||||
}
|
||||
|
||||
func (a *AllStoreSkusWrapper) ClearData() {
|
||||
a.allStoreSkus = nil
|
||||
}
|
||||
|
||||
func (a *AllStoreSkusWrapper) SetData(storeID int, skuMapData map[int]int) {
|
||||
a.locker.Lock()
|
||||
defer a.locker.Unlock()
|
||||
a.allStoreSkus[storeID] = skuMapData
|
||||
}
|
||||
|
||||
func (a *AllStoreSkusWrapper) GetData(storeID int) map[int]int {
|
||||
a.locker.RLock()
|
||||
defer a.locker.RUnlock()
|
||||
return a.allStoreSkus[storeID]
|
||||
}
|
||||
|
||||
type StoreScoreDataWrapper struct {
|
||||
storeScoreData map[int]*model.StoreScore
|
||||
dailyBadCommentOrderCount map[int]int
|
||||
dailyUnFinishOrderCount map[int]int
|
||||
dailyFinishOrderCount map[int]int
|
||||
dailyAbsentGoodsOrderCount map[int]int
|
||||
locker sync.RWMutex
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) InitData() {
|
||||
s.storeScoreData = make(map[int]*model.StoreScore)
|
||||
s.dailyBadCommentOrderCount = make(map[int]int)
|
||||
s.dailyUnFinishOrderCount = make(map[int]int)
|
||||
s.dailyFinishOrderCount = make(map[int]int)
|
||||
s.dailyAbsentGoodsOrderCount = make(map[int]int)
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) ClearData() {
|
||||
s.storeScoreData = nil
|
||||
s.dailyBadCommentOrderCount = nil
|
||||
s.dailyUnFinishOrderCount = nil
|
||||
s.dailyFinishOrderCount = nil
|
||||
s.dailyAbsentGoodsOrderCount = nil
|
||||
}
|
||||
|
||||
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
|
||||
data.ScoreDate = scoreDate
|
||||
s.storeScoreData[storeID] = data
|
||||
}
|
||||
valueInfo := reflect.ValueOf(data).Elem()
|
||||
valueInfo.FieldByName(valueName).SetInt(int64(value))
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetDailyBadCommentOrderCount(storeCountList []*model.StoreCount) {
|
||||
s.locker.Lock()
|
||||
defer s.locker.Unlock()
|
||||
for _, value := range storeCountList {
|
||||
s.dailyBadCommentOrderCount[value.StoreID] = value.Count
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) GetDailyBadCommentOrderCount(storeID int) int {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
if count, ok := s.dailyBadCommentOrderCount[storeID]; ok {
|
||||
return count
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetDailyUnFinishOrderCount(storeCountList []*model.StoreCount) {
|
||||
for _, value := range storeCountList {
|
||||
s.dailyUnFinishOrderCount[value.StoreID] = value.Count
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) GetDailyUnFinishOrderCount(storeID int) int {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
if count, ok := s.dailyUnFinishOrderCount[storeID]; ok {
|
||||
return count
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetDailyFinishOrderCount(storeCountList []*model.StoreCount) {
|
||||
for _, value := range storeCountList {
|
||||
s.dailyFinishOrderCount[value.StoreID] = value.Count
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) GetDailyFinishOrderCount(storeID int) int {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
if count, ok := s.dailyFinishOrderCount[storeID]; ok {
|
||||
return count
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) SetDailyAbsentGoodsOrderCount(storeCountList []*model.StoreCount) {
|
||||
for _, value := range storeCountList {
|
||||
s.dailyAbsentGoodsOrderCount[value.StoreID] = value.Count
|
||||
}
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) GetDailyAbsentGoodsOrderCount(storeID int) int {
|
||||
s.locker.RLock()
|
||||
defer s.locker.RUnlock()
|
||||
if count, ok := s.dailyAbsentGoodsOrderCount[storeID]; ok {
|
||||
return count
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s *StoreScoreDataWrapper) InsertStoreScore() {
|
||||
for _, value := range s.storeScoreData {
|
||||
dao.InsertStoreScore(value)
|
||||
}
|
||||
}
|
||||
|
||||
//得到所有门店的可售商品(优化内存,只存商品的价格)
|
||||
func GetAllStoreSkus(ctx *jxcontext.Context, parentTask tasksch.ITask, storeList []*cms.StoreExt) {
|
||||
allStoreSkusWrapper.InitData()
|
||||
taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeInfo := batchItemList[0].(*cms.StoreExt)
|
||||
storeID := storeInfo.ID
|
||||
jxSkuInfoData, _ := cms.GetStoreSkus(ctx, storeID, []int{}, true, "", true, map[string]interface{}{}, 0, -1)
|
||||
jxSkuPriceMapData := make(map[int]int)
|
||||
for _, value := range jxSkuInfoData.SkuNames {
|
||||
for _, skuInfo := range value.Skus2 {
|
||||
saleStatus := jxutils.MergeSkuStatus(skuInfo.SkuStatus, skuInfo.StoreSkuStatus)
|
||||
if saleStatus == model.SkuStatusNormal {
|
||||
jxSkuPriceMapData[skuInfo.SkuID] = skuInfo.BindPrice
|
||||
}
|
||||
}
|
||||
}
|
||||
allStoreSkusWrapper.SetData(storeID, jxSkuPriceMapData)
|
||||
return retVal, err
|
||||
}
|
||||
taskParallel := tasksch.NewParallelTask("得到所有门店商品", tasksch.NewParallelConfig().SetParallelCount(ParallelCount), ctx, taskFunc, storeList)
|
||||
tasksch.HandleTask(taskParallel, parentTask, true).Run()
|
||||
taskParallel.GetResult(0)
|
||||
}
|
||||
|
||||
func GetOpenTime(opTimeList []int16) (opTime float64) {
|
||||
opTime = 0
|
||||
for index, _ := range opTimeList {
|
||||
if index%2 == 1 {
|
||||
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
|
||||
}
|
||||
|
||||
//营业时间12小时及以上满分,总分10分,每少一个小时扣1分
|
||||
func ScoreStoreOpenTime(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
storeStatus := storeInfo.Status
|
||||
isStoreOpen := storeStatus == model.StoreStatusOpened
|
||||
finalScore := 0
|
||||
if isStoreOpen {
|
||||
for _, storeMap := range storeInfo.StoreMaps {
|
||||
isSyncStoreSku := int(utils.MustInterface2Int64(storeMap["isSync"]))
|
||||
vendorStoreStatus := int(utils.MustInterface2Int64(storeMap["status"]))
|
||||
isVendorStoreOpen := vendorStoreStatus == model.StoreStatusOpened
|
||||
opTimeList := storeInfo.GetOpTimeList()
|
||||
if len(opTimeList) > 0 && isStoreOpen && isVendorStoreOpen && isSyncStoreSku != 0 {
|
||||
opTime := GetOpenTime(opTimeList)
|
||||
if opTime >= StoreOpenTimeNormalTime {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round(StoreOpenTimeNormalTime - opTime))
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldStoreOpenTime, finalScore)
|
||||
}
|
||||
|
||||
//可售商品数量大于1000,总分10分,按比例扣
|
||||
func ScoreSaleSkuCount(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
skusMapData := allStoreSkusWrapper.GetData(storeID)
|
||||
finalScore := 0
|
||||
if len(skusMapData) > 0 {
|
||||
saleSkuCount := len(skusMapData)
|
||||
finalScore = int(math.Round(float64(saleSkuCount) * SaleSkuScorePerUnit))
|
||||
if finalScore > ItemTotalScore {
|
||||
finalScore = ItemTotalScore
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldSaleSkuCount, finalScore)
|
||||
}
|
||||
|
||||
//平均捡货时间小于等于拣货截止时间为10分满分,每超出1分钟,减1分
|
||||
func ScoreAveragePickupTime(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
db := dao.GetDB()
|
||||
orderList, err := dao.GetDailyFinishOrderList(db, storeID, scoreDate)
|
||||
orderListCount := len(orderList)
|
||||
finalScore := 0
|
||||
if err == nil && orderListCount > 0 {
|
||||
totalScore := 0
|
||||
for _, value := range orderList {
|
||||
statusTime := value.StatusTime.Unix()
|
||||
pickDeadline := value.PickDeadline.Unix()
|
||||
if statusTime <= pickDeadline {
|
||||
totalScore += ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round(float64(statusTime-pickDeadline) / 60))
|
||||
tempScore := ItemTotalScore - decScore
|
||||
if tempScore < 0 {
|
||||
tempScore = 0
|
||||
}
|
||||
totalScore += tempScore
|
||||
}
|
||||
}
|
||||
finalScore = totalScore / orderListCount
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldAveragePickupTime, finalScore)
|
||||
}
|
||||
|
||||
//差评订单和完成订单比例小于0.2%,得满分10分,每增加0.1%减1分
|
||||
func ScoreBadCommentOrder(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
badCommentOrderCount := storeScoreDataWrapper.GetDailyBadCommentOrderCount(storeID)
|
||||
finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
|
||||
finalScore := 0
|
||||
if finishOrderCount > 0 {
|
||||
badCommentOrderRatio := float64(badCommentOrderCount) * 100 / float64(finishOrderCount)
|
||||
if badCommentOrderRatio <= BadCommentOrderNormalRatio {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round((badCommentOrderRatio - BadCommentOrderNormalRatio) / 0.1))
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldBadCommentOrder, finalScore)
|
||||
}
|
||||
|
||||
//未完成订单和完成订单比小于1%,得满分10分,比例每增加5%,分数减1
|
||||
func ScoreUnfinishOrder(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
unFinishOrderCount := storeScoreDataWrapper.GetDailyUnFinishOrderCount(storeID)
|
||||
finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
|
||||
finalScore := 0
|
||||
if finishOrderCount > 0 {
|
||||
unfinishOrderRatio := float64(unFinishOrderCount) * 100 / float64(finishOrderCount)
|
||||
if unfinishOrderRatio <= UnfinishOrderNormalRatio {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round((unfinishOrderRatio - UnfinishOrderNormalRatio) / 5))
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldUnfinishOrder, finalScore)
|
||||
}
|
||||
|
||||
//缺货订单和完成订单比小于1%得10分,比例每增加0.1%减1分
|
||||
func ScoreAbsentGoodsOrder(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
absentGoodsOrderCount := storeScoreDataWrapper.GetDailyAbsentGoodsOrderCount(storeID)
|
||||
finishOrderCount := storeScoreDataWrapper.GetDailyFinishOrderCount(storeID)
|
||||
finalScore := 0
|
||||
if finishOrderCount > 0 {
|
||||
absentGoodsOrderRatio := float64(absentGoodsOrderCount) * 100 / float64(finishOrderCount)
|
||||
if absentGoodsOrderRatio <= AbsentGoodsOrderNormalRatio {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := int(math.Round((absentGoodsOrderRatio - AbsentGoodsOrderNormalRatio) / 0.1))
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldAbsentGoodsOrder, finalScore)
|
||||
}
|
||||
|
||||
//促销品数量20个以上为满分10分,每少2个扣1分
|
||||
func ScorePromotionSku(storeInfo *cms.StoreExt) {
|
||||
storeID := storeInfo.ID
|
||||
db := dao.GetDB()
|
||||
beginTime := time.Now()
|
||||
endTime := time.Now()
|
||||
actStoreSkuList, err := dao.GetEffectiveActStoreSkuInfo(db, -1, nil, []int{storeID}, nil, beginTime, endTime)
|
||||
finalScore := 0
|
||||
if err == nil && len(actStoreSkuList) > 0 {
|
||||
actStoreSkuMap := make(map[int]int)
|
||||
for _, value := range actStoreSkuList {
|
||||
if value.Type != model.ActSkuFake {
|
||||
actStoreSkuMap[value.SkuID] = 1
|
||||
}
|
||||
}
|
||||
promotionSkuCount := len(actStoreSkuMap)
|
||||
if promotionSkuCount >= PromotionSkuNormalCount {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := (PromotionSkuNormalCount - promotionSkuCount) / 2
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldPromotionSku, finalScore)
|
||||
}
|
||||
|
||||
//经营全平台满分10分,每少一个平台扣2分(一个平台没有得0分)
|
||||
func ScoreFullVendor(storeInfo *cms.StoreExt) {
|
||||
fullVendorCount := len(fullVendorList)
|
||||
finalScore := 0
|
||||
storeID := storeInfo.ID
|
||||
storeStatus := storeInfo.Status
|
||||
isStoreOpen := storeStatus == model.StoreStatusOpened
|
||||
count := 0
|
||||
for _, storeMap := range storeInfo.StoreMaps {
|
||||
isSyncStoreSku := int(utils.MustInterface2Int64(storeMap["isSync"]))
|
||||
vendorStoreStatus := int(utils.MustInterface2Int64(storeMap["status"]))
|
||||
isVendorStoreOpen := vendorStoreStatus == model.StoreStatusOpened
|
||||
opTimeList := storeInfo.GetOpTimeList()
|
||||
if len(opTimeList) > 0 && isStoreOpen && isVendorStoreOpen && isSyncStoreSku != 0 {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count > 0 {
|
||||
if count == fullVendorCount {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := (fullVendorCount - count) * 2
|
||||
finalScore = ItemTotalScore - decScore
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldFullVendor, finalScore)
|
||||
}
|
||||
|
||||
//经营范围面积大于半径2km的圆得满分10分,低于1km得分0
|
||||
func ScoreStoreRange(storeInfo *cms.StoreExt) {
|
||||
finalScore := 0
|
||||
storeID := storeInfo.ID
|
||||
if storeInfo.DeliveryRangeType == model.DeliveryRangeTypePolygon {
|
||||
if storeInfo.DeliveryRange != "" {
|
||||
points := jxutils.CoordinateStr2Points(storeInfo.DeliveryRange)
|
||||
area := jxutils.CalcPolygonAreaAutonavi(points)
|
||||
goodArea := math.Pi * StoreRangeGoodRadius * StoreRangeGoodRadius
|
||||
badArea := math.Pi * StoreRangeBadRadius * StoreRangeBadRadius
|
||||
if area >= goodArea {
|
||||
finalScore = ItemTotalScore
|
||||
} else if area <= badArea {
|
||||
finalScore = 0
|
||||
} else {
|
||||
diff := goodArea - area
|
||||
ratio := float64(ItemTotalScore) / (goodArea - badArea)
|
||||
finalScore = ItemTotalScore - int(math.Round(diff*ratio))
|
||||
}
|
||||
}
|
||||
} else if storeInfo.DeliveryRangeType == model.DeliveryRangeTypeRadius {
|
||||
deliveryRadius := utils.Str2Float64WithDefault(storeInfo.DeliveryRange, 0) / 1000
|
||||
if deliveryRadius >= StoreRangeGoodRadius {
|
||||
finalScore = ItemTotalScore
|
||||
} else if deliveryRadius <= StoreRangeBadRadius {
|
||||
finalScore = 0
|
||||
} else {
|
||||
diff := StoreRangeGoodRadius - deliveryRadius
|
||||
ratio := float64(ItemTotalScore) / (StoreRangeGoodRadius - StoreRangeBadRadius)
|
||||
finalScore = ItemTotalScore - int(math.Round(diff*ratio))
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldStoreRange, finalScore)
|
||||
}
|
||||
|
||||
//得到距离某个门店多少KM内的所有门店信息
|
||||
func GetRangeStoreList(storeID int, lng, lat, checkRange 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 <= checkRange {
|
||||
outStoreList = append(outStoreList, storeInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return outStoreList
|
||||
}
|
||||
|
||||
//得到给定门店列表里的同一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
|
||||
storeSkuMapData := allStoreSkusWrapper.GetData(storeID)
|
||||
for skuID, skuPrice := range storeSkuMapData {
|
||||
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) {
|
||||
storeSkuMapData := allStoreSkusWrapper.GetData(storeID)
|
||||
for skuID, skuPrice := range storeSkuMapData {
|
||||
skuAvgPrice := skusAveragePrice[skuID]
|
||||
if skuPrice <= skuAvgPrice {
|
||||
count++
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
//可售商品价格在附近5km内门店比较,价格低于等于平均值的商品数占90%以上满分10分,比例每降低10%减1分,100%超标得0分
|
||||
func ScoreSaleSkuPrice(storeInfo *cms.StoreExt, storeList []*cms.StoreExt) {
|
||||
finalScore := 0
|
||||
storeID := storeInfo.ID
|
||||
totalCount := len(allStoreSkusWrapper.GetData(storeID))
|
||||
if totalCount > 0 {
|
||||
rangeStoreList := GetRangeStoreList(storeID, storeInfo.FloatLng, storeInfo.FloatLat, SaleSkuCheckRange, storeList)
|
||||
skusAveragePrice := GetStoreSkusAveragePrice(rangeStoreList)
|
||||
count := GetSkusCountLessEqualAvgPrice(storeID, skusAveragePrice)
|
||||
if count > 0 {
|
||||
ratio := int(math.Round(float64(count) * 100 / float64(totalCount)))
|
||||
if ratio >= SaleSkuPriceRatio {
|
||||
finalScore = ItemTotalScore
|
||||
} else {
|
||||
decScore := (SaleSkuPriceRatio - ratio) / 10
|
||||
finalScore = ItemTotalScore - decScore
|
||||
if finalScore < 0 {
|
||||
finalScore = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
storeScoreDataWrapper.SetData(storeID, model.FieldSaleSkuPrice, finalScore)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
var tempStoreMaps []map[string]interface{}
|
||||
for _, vendorStoreInfo := range storeInfo.StoreMaps {
|
||||
vendorID := int(utils.MustInterface2Int64(vendorStoreInfo["vendorID"]))
|
||||
if _, ok := fullVendorList[vendorID]; !ok {
|
||||
continue
|
||||
}
|
||||
tempStoreMaps = append(tempStoreMaps, vendorStoreInfo)
|
||||
}
|
||||
if len(tempStoreMaps) > 0 {
|
||||
storeInfo.StoreMaps = tempStoreMaps
|
||||
outStoreList = append(outStoreList, storeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
return outStoreList
|
||||
}
|
||||
|
||||
func ScoreStore(ctx *jxcontext.Context, storeIDList []int) (retVal interface{}, err error) {
|
||||
isScoring = true
|
||||
scoreDate = utils.GetCurDate()
|
||||
var storeList []*cms.StoreExt
|
||||
taskCount := 5
|
||||
taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
|
||||
switch step {
|
||||
case 0:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step0 begin")
|
||||
storeScoreDataWrapper.InitData()
|
||||
storeList, err = GetStoreList(ctx)
|
||||
storeIDMap := jxutils.IntList2Map(storeIDList)
|
||||
storeList = GetFilterStoreListEx(storeList, storeIDMap)
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step0 end")
|
||||
case 1:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step1 begin")
|
||||
GetAllStoreSkus(ctx, task, storeList)
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step1 end")
|
||||
case 2:
|
||||
db := dao.GetDB()
|
||||
storeCountList, _ := dao.GetDailyBadCommentOrderCount(db, scoreDate)
|
||||
storeScoreDataWrapper.SetDailyBadCommentOrderCount(storeCountList)
|
||||
storeCountList, _ = dao.GetDailyUnFinishOrderCount(db, scoreDate)
|
||||
storeScoreDataWrapper.SetDailyUnFinishOrderCount(storeCountList)
|
||||
storeCountList, _ = dao.GetDailyFinishOrderCount(db, scoreDate)
|
||||
storeScoreDataWrapper.SetDailyFinishOrderCount(storeCountList)
|
||||
storeCountList, _ = dao.GetDailyAbsentGoodsOrderCount(db, scoreDate)
|
||||
storeScoreDataWrapper.SetDailyAbsentGoodsOrderCount(storeCountList)
|
||||
case 3:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step2 begin")
|
||||
taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
|
||||
storeInfo := batchItemList[0].(*cms.StoreExt)
|
||||
storeID := storeInfo.ID
|
||||
baseapi.SugarLogger.Debugf("Begin store id:%d", storeID)
|
||||
ScoreStoreOpenTime(storeInfo)
|
||||
ScoreSaleSkuCount(storeInfo)
|
||||
ScoreAveragePickupTime(storeInfo)
|
||||
ScoreBadCommentOrder(storeInfo)
|
||||
ScoreUnfinishOrder(storeInfo)
|
||||
ScoreAbsentGoodsOrder(storeInfo)
|
||||
ScorePromotionSku(storeInfo)
|
||||
ScoreFullVendor(storeInfo)
|
||||
ScoreStoreRange(storeInfo)
|
||||
ScoreSaleSkuPrice(storeInfo, storeList)
|
||||
baseapi.SugarLogger.Debugf("End store id:%d", storeID)
|
||||
return retVal, err
|
||||
}
|
||||
taskParallel := tasksch.NewParallelTask("计算门店得分", tasksch.NewParallelConfig().SetParallelCount(ParallelCount), ctx, taskFunc, storeList)
|
||||
tasksch.HandleTask(taskParallel, task, true).Run()
|
||||
taskParallel.GetResult(0)
|
||||
_, err = taskParallel.GetResult(0)
|
||||
if err != nil {
|
||||
baseapi.SugarLogger.Debugf("ScoreStore taskParallel error:%v", err)
|
||||
}
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step2 end")
|
||||
case 4:
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step3 begin")
|
||||
storeScoreDataWrapper.InsertStoreScore()
|
||||
storeScoreDataWrapper.ClearData()
|
||||
allStoreSkusWrapper.ClearData()
|
||||
baseapi.SugarLogger.Debugf("ScoreStore step3 end")
|
||||
isScoring = false
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
taskSeq := tasksch.NewSeqTask("门店评分-序列任务", ctx, taskSeqFunc, taskCount)
|
||||
tasksch.HandleTask(taskSeq, nil, true).Run()
|
||||
|
||||
return retVal, err
|
||||
}
|
||||
|
||||
func ScheduleScoreStore() {
|
||||
if EnableScheduleScoreStore {
|
||||
ScheduleTimerFunc("ScheduleScoreStore", func() {
|
||||
if !isScoring {
|
||||
ScoreStore(jxcontext.AdminCtx, nil)
|
||||
}
|
||||
}, scoreStoreTimeList)
|
||||
CheckScoreStore()
|
||||
}
|
||||
}
|
||||
|
||||
func CheckScoreStore() {
|
||||
if !isScoring {
|
||||
curTime := time.Now()
|
||||
checkTimeStr1 := fmt.Sprintf("%s %s", utils.Time2DateStr(curTime), scoreStoreTimeList[0])
|
||||
checkTime1 := utils.Str2Time(checkTimeStr1)
|
||||
checkTimeStr2 := fmt.Sprintf("%s %s", utils.Time2DateStr(curTime), scoreStoreCheckTimeEnd)
|
||||
checkTime2 := utils.Str2Time(checkTimeStr2)
|
||||
if curTime.Unix() >= checkTime1.Unix() && curTime.Unix() <= checkTime2.Unix() {
|
||||
db := dao.GetDB()
|
||||
hasStoreScoreData, err := dao.CheckHasStoreScoreData(db, curTime)
|
||||
if err == nil && !hasStoreScoreData {
|
||||
ScoreStore(jxcontext.AdminCtx, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Time2Week(t time.Time) int {
|
||||
yearDay := t.YearDay()
|
||||
yearFirstDay := t.AddDate(0, 0, -yearDay+1)
|
||||
firstDayInWeek := int(yearFirstDay.Weekday())
|
||||
|
||||
firstWeekDays := 1
|
||||
if firstDayInWeek != 0 {
|
||||
firstWeekDays = 7 - firstDayInWeek + 1
|
||||
}
|
||||
var week int
|
||||
if yearDay <= firstWeekDays {
|
||||
week = 1
|
||||
} else {
|
||||
tempWeek := (float64(yearDay) - float64(firstWeekDays)) / float64(7)
|
||||
week = int(math.Ceil(tempWeek)) + 1
|
||||
}
|
||||
|
||||
return week
|
||||
}
|
||||
|
||||
func SplitToSingleWeekDataList(storeScoreList []*model.StoreScoreEx) (weekDataList [][]*model.StoreScoreEx) {
|
||||
singelWeekData := []*model.StoreScoreEx{}
|
||||
weekIndex := 0
|
||||
for _, value := range storeScoreList {
|
||||
if weekIndex == 0 {
|
||||
weekIndex = Time2Week(value.CreatedAt)
|
||||
}
|
||||
if weekIndex == Time2Week(value.CreatedAt) {
|
||||
singelWeekData = append(singelWeekData, value)
|
||||
} else {
|
||||
weekDataList = append(weekDataList, singelWeekData)
|
||||
singelWeekData = []*model.StoreScoreEx{}
|
||||
weekIndex = 0
|
||||
singelWeekData = append(singelWeekData, value)
|
||||
}
|
||||
}
|
||||
if len(singelWeekData) > 0 {
|
||||
weekDataList = append(weekDataList, singelWeekData)
|
||||
}
|
||||
|
||||
return weekDataList
|
||||
}
|
||||
|
||||
func GetStoreScoreLevel(score int) int {
|
||||
level := 0
|
||||
if score >= GoldMedalScore {
|
||||
level = GoldMedalLevel
|
||||
} else if score >= SilverMedalScore {
|
||||
level = SilverMedalLevel
|
||||
} else if score >= BronzeMedalScore {
|
||||
level = BronzeMedalLevel
|
||||
}
|
||||
|
||||
return level
|
||||
}
|
||||
|
||||
func GetWeeklyStoreScore(storeID, weekIndex int) (outWeeklyStoreScoreDataList []*model.WeeklyStoreScore, err error) {
|
||||
db := dao.GetDB()
|
||||
storeScoreList, err := dao.GetLatestWeeklyStoreScoreList(db, storeID, 5)
|
||||
if err == nil && len(storeScoreList) > 0 {
|
||||
weeklyStoreScoreDataList := []*model.WeeklyStoreScore{}
|
||||
weekDataList := SplitToSingleWeekDataList(storeScoreList)
|
||||
for weekIndex, weekData := range weekDataList {
|
||||
weeklyData := &model.WeeklyStoreScore{}
|
||||
weeklyData.ID = weekIndex
|
||||
weeklyData.ItemTotalScore = ItemTotalScore
|
||||
weeklyData.StoreID = storeID
|
||||
weeklyStoreScoreDataList = append(weeklyStoreScoreDataList, weeklyData)
|
||||
weekDataCount := len(weekData)
|
||||
for dayIndex, dayData := range weekData {
|
||||
for _, fieldName := range storeScoreFieldName {
|
||||
srcFieldValue := refutil.GetObjFieldByName(dayData, fieldName).(int)
|
||||
destFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
|
||||
refutil.SetObjFieldByName(weeklyData, fieldName, destFieldValue+srcFieldValue)
|
||||
}
|
||||
if weekDataCount == 1 {
|
||||
weeklyData.BeginTime = dayData.CreatedAt
|
||||
weeklyData.EndTime = dayData.CreatedAt
|
||||
} else {
|
||||
if dayIndex == 0 {
|
||||
weeklyData.EndTime = dayData.CreatedAt
|
||||
} else if dayIndex == weekDataCount-1 {
|
||||
weeklyData.BeginTime = dayData.CreatedAt
|
||||
}
|
||||
}
|
||||
weeklyData.StoreName = dayData.StoreName
|
||||
}
|
||||
for _, fieldName := range storeScoreFieldName {
|
||||
destFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
|
||||
refutil.SetObjFieldByName(weeklyData, fieldName, int(math.Round(float64(destFieldValue)/float64(weekDataCount))))
|
||||
}
|
||||
for _, fieldName := range storeScoreFieldName {
|
||||
srcFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
|
||||
destFieldValue := refutil.GetObjFieldByName(weeklyData, model.FieldTotalScore).(int)
|
||||
refutil.SetObjFieldByName(weeklyData, model.FieldTotalScore, destFieldValue+srcFieldValue)
|
||||
}
|
||||
weeklyData.Level = GetStoreScoreLevel(weeklyData.TotalScore)
|
||||
}
|
||||
if weekIndex == -1 {
|
||||
outWeeklyStoreScoreDataList = weeklyStoreScoreDataList
|
||||
} else {
|
||||
outWeeklyStoreScoreDataList = []*model.WeeklyStoreScore{weeklyStoreScoreDataList[weekIndex]}
|
||||
}
|
||||
}
|
||||
|
||||
return outWeeklyStoreScoreDataList, err
|
||||
}
|
||||
24
business/jxstore/misc/store_score_test.go
Normal file
24
business/jxstore/misc/store_score_test.go
Normal file
@@ -0,0 +1,24 @@
|
||||
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/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() {
|
||||
testinit.Init()
|
||||
api2.Init()
|
||||
}
|
||||
|
||||
func TestScoreStore(t *testing.T) {
|
||||
ScoreStore(jxcontext.AdminCtx, []int{})
|
||||
}
|
||||
@@ -290,3 +290,71 @@ func GetStoreAfsOrderSkuList(db *DaoDB, storeIDs []int, finishedAtBegin, finishe
|
||||
err = GetRows(db, &afsSkuList, sql, sqlParams...)
|
||||
return afsSkuList, err
|
||||
}
|
||||
|
||||
func GetDailyFinishOrderList(db *DaoDB, storeID int, dateTime time.Time) (orderList []*model.OrderPickupTime, err error) {
|
||||
sql := `
|
||||
SELECT t2.status_time, t1.pick_deadline
|
||||
FROM goods_order t1
|
||||
JOIN order_status t2 ON t1.vendor_order_id = t2.vendor_order_id AND t1.vendor_id = t2.vendor_id
|
||||
WHERE t1.jx_store_id = ? AND t2.order_type = ? AND t2.status = ? AND DATE(t1.order_finished_at) = DATE(?)
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
storeID,
|
||||
1,
|
||||
model.OrderStatusFinishedPickup,
|
||||
dateTime,
|
||||
}
|
||||
return orderList, GetRows(db, &orderList, sql, sqlParams...)
|
||||
}
|
||||
|
||||
func GetDailyBadCommentOrderCount(db *DaoDB, dateTime time.Time) (storeCountList []*model.StoreCount, err error) {
|
||||
sql := `
|
||||
SELECT jxstoreid store_id, COUNT(*) count
|
||||
FROM jx_bad_comments
|
||||
WHERE DATE(createtime) = DATE(?)
|
||||
GROUP BY jxstoreid
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
dateTime,
|
||||
}
|
||||
err = GetRows(db, &storeCountList, sql, sqlParams)
|
||||
|
||||
return storeCountList, err
|
||||
}
|
||||
|
||||
func GetDailyUnFinishOrderCount(db *DaoDB, dateTime time.Time) (storeCountList []*model.StoreCount, err error) {
|
||||
return GetDailyEndOrderCount(db, []int{model.OrderStatusCanceled}, false, dateTime)
|
||||
}
|
||||
|
||||
func GetDailyFinishOrderCount(db *DaoDB, dateTime time.Time) (storeCountList []*model.StoreCount, err error) {
|
||||
return GetDailyEndOrderCount(db, []int{model.OrderStatusFinished}, false, dateTime)
|
||||
}
|
||||
|
||||
func GetDailyAbsentGoodsOrderCount(db *DaoDB, dateTime time.Time) (storeCountList []*model.StoreCount, err error) {
|
||||
return GetDailyEndOrderCount(db, []int{model.OrderStatusFinished, model.OrderStatusCanceled}, true, dateTime)
|
||||
}
|
||||
|
||||
func GetDailyEndOrderCount(db *DaoDB, statusList []int, isAbsentOrder bool, dateTime time.Time) (storeCountList []*model.StoreCount, err error) {
|
||||
sql := `
|
||||
SELECT jx_store_id store_id, COUNT(*) count
|
||||
FROM goods_order
|
||||
WHERE DATE(order_finished_at) = DATE(?)
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
dateTime,
|
||||
}
|
||||
if len(statusList) > 0 {
|
||||
sql += ` AND status IN (` + GenQuestionMarks(len(statusList)) + `)`
|
||||
sqlParams = append(sqlParams, statusList)
|
||||
}
|
||||
if isAbsentOrder {
|
||||
sql += `
|
||||
AND adjust_count > 0
|
||||
`
|
||||
}
|
||||
sql += `
|
||||
GROUP BY jx_store_id`
|
||||
err = GetRow(db, &storeCountList, sql, sqlParams)
|
||||
|
||||
return storeCountList, err
|
||||
}
|
||||
|
||||
67
business/model/dao/store_score.go
Normal file
67
business/model/dao/store_score.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
)
|
||||
|
||||
func InsertStoreScore(storeScore *model.StoreScore) error {
|
||||
storeScore.CreatedAt = time.Now()
|
||||
return CreateEntity(nil, storeScore)
|
||||
}
|
||||
|
||||
func GetLatestWeeklyStoreScoreList(db *DaoDB, storeID, weekNum int) (storeScoreList []*model.StoreScoreEx, err error) {
|
||||
sql := `
|
||||
SELECT t2.name store_name, t1.* FROM store_score t1
|
||||
JOIN store t2 ON t1.store_id = t2.id
|
||||
WHERE t1.store_id = ?
|
||||
AND DATE(t1.score_date) >= DATE_SUB(
|
||||
DATE_SUB(
|
||||
CURDATE(),
|
||||
INTERVAL
|
||||
IF (
|
||||
DAYOFWEEK(CURDATE()) - 1 = 0,
|
||||
7,
|
||||
DAYOFWEEK(CURDATE()) - 1
|
||||
) DAY
|
||||
),
|
||||
INTERVAL ? DAY
|
||||
)
|
||||
AND DATE(t1.score_date) <= DATE_SUB(
|
||||
CURDATE(),
|
||||
INTERVAL
|
||||
IF (
|
||||
DAYOFWEEK(CURDATE()) - 1 = 0,
|
||||
7,
|
||||
DAYOFWEEK(CURDATE()) - 1
|
||||
) DAY
|
||||
)
|
||||
ORDER BY score_date DESC
|
||||
`
|
||||
if weekNum <= 0 {
|
||||
weekNum = 1
|
||||
}
|
||||
diffDays := weekNum*7 - 1
|
||||
sqlParams := []interface{}{
|
||||
storeID,
|
||||
diffDays,
|
||||
}
|
||||
err = GetRows(db, &storeScoreList, sql, sqlParams)
|
||||
return storeScoreList, err
|
||||
}
|
||||
|
||||
func CheckHasStoreScoreData(db *DaoDB, dateTime time.Time) (hasStoreScoreData bool, err error) {
|
||||
sql := `
|
||||
SELECT COUNT(*) count
|
||||
FROM store_score
|
||||
WHERE DATE(score_date) = DATE(?)
|
||||
`
|
||||
sqlParams := []interface{}{
|
||||
dateTime,
|
||||
}
|
||||
count := 0
|
||||
err = GetRow(db, &count, sql, sqlParams)
|
||||
hasStoreScoreData = count > 0
|
||||
return hasStoreScoreData, err
|
||||
}
|
||||
60
business/model/store_score.go
Normal file
60
business/model/store_score.go
Normal file
@@ -0,0 +1,60 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
FieldStoreOpenTime = "StoreOpenTime"
|
||||
FieldSaleSkuCount = "SaleSkuCount"
|
||||
FieldAveragePickupTime = "AveragePickupTime"
|
||||
FieldBadCommentOrder = "BadCommentOrder"
|
||||
FieldUnfinishOrder = "UnfinishOrder"
|
||||
FieldAbsentGoodsOrder = "AbsentGoodsOrder"
|
||||
FieldPromotionSku = "PromotionSku"
|
||||
FieldFullVendor = "FullVendor"
|
||||
FieldStoreRange = "StoreRange"
|
||||
FieldSaleSkuPrice = "SaleSkuPrice"
|
||||
|
||||
FieldTotalScore = "TotalScore"
|
||||
)
|
||||
|
||||
type StoreScore struct {
|
||||
ID int `orm:"column(id)" json:"id"`
|
||||
CreatedAt time.Time `orm:"auto_now_add;type(datetime)" json:"createdAt"`
|
||||
ScoreDate time.Time `orm:"auto_now_add;type(datetime)" json:"scoreDate"`
|
||||
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"`
|
||||
BadCommentOrder int `orm:"column(bad_comment_order)" json:"badCommentOrder"`
|
||||
UnfinishOrder int `orm:"column(unfinish_order)" json:"unfinishOrder"`
|
||||
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"`
|
||||
SaleSkuPrice int `orm:"column(sale_sku_price)" json:"saleSkuPrice"`
|
||||
}
|
||||
|
||||
type StoreScoreEx struct {
|
||||
StoreScore
|
||||
StoreName string `json:"storeName"`
|
||||
}
|
||||
|
||||
type WeeklyStoreScore struct {
|
||||
StoreScoreEx
|
||||
BeginTime time.Time `json:"beginTime"`
|
||||
EndTime time.Time `json:"endTime"`
|
||||
TotalScore int `json:"totalScore"`
|
||||
ItemTotalScore int `json:"itemTotalScore"`
|
||||
Level int `json:"level"`
|
||||
}
|
||||
|
||||
type StoreCount struct {
|
||||
StoreID int `orm:"column(store_id)"`
|
||||
Count int
|
||||
}
|
||||
|
||||
type OrderPickupTime struct {
|
||||
StatusTime time.Time
|
||||
PickDeadline time.Time
|
||||
}
|
||||
@@ -263,3 +263,11 @@ func GetSingleStoreVendorIDs() (vendorIDs []int) {
|
||||
}
|
||||
return vendorIDs
|
||||
}
|
||||
|
||||
func IsMultiStore(vendorID int) bool {
|
||||
if _, ok := GetPurchasePlatformFromVendorID(vendorID).(IMultipleStoresHandler); ok {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package controllers
|
||||
import (
|
||||
"git.rosy.net.cn/baseapi/utils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/cms"
|
||||
"git.rosy.net.cn/jx-callback/business/jxstore/misc"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils"
|
||||
"git.rosy.net.cn/jx-callback/business/jxutils/netprinter"
|
||||
"git.rosy.net.cn/jx-callback/business/model"
|
||||
@@ -422,3 +423,36 @@ func (c *StoreController) SyncStoresQualify() {
|
||||
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 *StoreController) ScoreStore() {
|
||||
c.callScoreStore(func(params *tStoreScoreStoreParams) (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
|
||||
})
|
||||
}
|
||||
|
||||
// @Title 得到门店近期周平均分数数据
|
||||
// @Description 得到门店近期周平均分数数据
|
||||
// @Param token header string true "认证token"
|
||||
// @Param storeID query int true "京西门店ID"
|
||||
// @Param weekIndex query int true "周索引(起始索引为0, -1为所有周数据)"
|
||||
// @Success 200 {object} controllers.CallResult
|
||||
// @Failure 200 {object} controllers.CallResult
|
||||
// @router /GetWeeklyStoreScore [get]
|
||||
func (c *StoreController) GetWeeklyStoreScore() {
|
||||
c.callGetWeeklyStoreScore(func(params *tStoreGetWeeklyStoreScoreParams) (retVal interface{}, errCode string, err error) {
|
||||
retVal, err = misc.GetWeeklyStoreScore(params.StoreID, params.WeekIndex)
|
||||
return retVal, "", err
|
||||
})
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func Init() {
|
||||
|
||||
orm.RegisterModel(&model.CasbinRule{})
|
||||
orm.RegisterModel(&model.SensitiveWord{})
|
||||
|
||||
orm.RegisterModel(&model.StoreScore{})
|
||||
orm.RegisterModel(&model.FoodRecipe{}, &model.FoodRecipeStep{}, &model.FoodRecipeItem{}, &model.FoodRecipeItemChoice{}, &model.FoodRecipeUser{})
|
||||
|
||||
// create table
|
||||
|
||||
@@ -475,6 +475,15 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:FoodRecipeController"],
|
||||
beego.ControllerComments{
|
||||
Method: "CreateFoodRecipe",
|
||||
Router: `/CreateFoodRecipe`,
|
||||
AllowHTTPMethods: []string{"post"},
|
||||
MethodParams: param.Make(),
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:InitDataController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:InitDataController"],
|
||||
beego.ControllerComments{
|
||||
Method: "BuildSkuFromEbaiStore",
|
||||
@@ -1296,6 +1305,24 @@ func init() {
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreController"],
|
||||
beego.ControllerComments{
|
||||
Method: "GetWeeklyStoreScore",
|
||||
Router: `/GetWeeklyStoreScore`,
|
||||
AllowHTTPMethods: []string{"get"},
|
||||
MethodParams: param.Make(),
|
||||
Filters: nil,
|
||||
Params: nil})
|
||||
|
||||
beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreController"],
|
||||
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:StoreController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:StoreController"],
|
||||
beego.ControllerComments{
|
||||
Method: "SyncStoresQualify",
|
||||
|
||||
Reference in New Issue
Block a user