From 4caa33cac5441c8b19934c495394e7638ce019d2 Mon Sep 17 00:00:00 2001 From: gazebo Date: Tue, 17 Sep 2019 09:33:36 +0800 Subject: [PATCH 1/7] + UpdateFoodRecipe --- business/userstore/food_recipe.go | 156 +++++++++++++++++++++--------- 1 file changed, 111 insertions(+), 45 deletions(-) diff --git a/business/userstore/food_recipe.go b/business/userstore/food_recipe.go index 4eadec204..31a26f5d3 100644 --- a/business/userstore/food_recipe.go +++ b/business/userstore/food_recipe.go @@ -19,7 +19,65 @@ type FoodRecipeStepParam struct { Img string `orm:"size(48)" json:"img"` } +func updateFoodRecipeItemAndStep(ctx *jxcontext.Context, db *dao.DaoDB, recipeID int, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { + for k, v := range itemList { + if len(v.SkuIDs) == 0 { + return fmt.Errorf("SkuIDs必须要有值") + } + skuIDs, err2 := dao.GetSkus(db, v.SkuIDs, nil, nil, nil) + if err = err2; err != nil { + return err + } + if len(v.SkuIDs) != len(skuIDs) { + return fmt.Errorf("某些SkuIDs不存在") + } + + item := &model.FoodRecipeItem{ + RecipeID: recipeID, + Index: int8(k + 1), + Name: v.Name, + } + dao.WrapAddIDCULEntity(item, ctx.GetUserName()) + if err = dao.CreateEntity(db, item); err != nil { + return err + } + for k2, v2 := range v.SkuIDs { + choice := &model.FoodRecipeItemChoice{ + RecipeID: recipeID, + Index: item.Index, + SkuID: v2, + + ChoiceIndex: int8(k2 + 1), + } + dao.WrapAddIDCULEntity(choice, ctx.GetUserName()) + if err = dao.CreateEntity(db, choice); err != nil { + return err + } + } + } + + for k, v := range stepList { + step := &model.FoodRecipeStep{ + RecipeID: recipeID, + Index: int8(k + 1), + Name: v.Name, + } + dao.WrapAddIDCULEntity(step, ctx.GetUserName()) + if err = dao.CreateEntity(db, step); err != nil { + return err + } + } + return err +} + func CreateFoodRecipe(ctx *jxcontext.Context, foodRecipe *model.FoodRecipe, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { + if len(itemList) == 0 { + return fmt.Errorf("必须要有配料") + } + if len(stepList) == 0 { + return fmt.Errorf("必须要有操作步骤") + } + db := dao.GetDB() dao.Begin(db) defer func() { @@ -36,58 +94,66 @@ func CreateFoodRecipe(ctx *jxcontext.Context, foodRecipe *model.FoodRecipe, item if err = dao.CreateEntity(db, foodRecipe); err != nil { return err } - for k, v := range itemList { - if len(v.SkuIDs) == 0 { - return fmt.Errorf("SkuIDs必须要有值") - } - skuIDs, err2 := dao.GetSkus(db, v.SkuIDs, nil, nil, nil) - if err = err2; err != nil { - return err - } - if len(v.SkuIDs) != len(skuIDs) { - return fmt.Errorf("某些SkuIDs不存在") - } - - item := &model.FoodRecipeItem{ - RecipeID: foodRecipe.ID, - Index: int8(k + 1), - Name: v.Name, - } - dao.WrapAddIDCULEntity(item, ctx.GetUserName()) - if err = dao.CreateEntity(db, item); err != nil { - return err - } - for k2, v2 := range v.SkuIDs { - choice := &model.FoodRecipeItemChoice{ - RecipeID: foodRecipe.ID, - Index: item.Index, - SkuID: v2, - - ChoiceIndex: int8(k2 + 1), - } - dao.WrapAddIDCULEntity(choice, ctx.GetUserName()) - if err = dao.CreateEntity(db, choice); err != nil { - return err - } - } + if err = updateFoodRecipeItemAndStep(ctx, db, foodRecipe.ID, itemList, stepList); err != nil { + return err } - for k, v := range stepList { - step := &model.FoodRecipeStep{ - RecipeID: foodRecipe.ID, - Index: int8(k + 1), - Name: v.Name, + dao.Commit(db) + return err +} + +func UpdateFoodRecipe(ctx *jxcontext.Context, recipeID int, mapData map[string]interface{}, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { + db := dao.GetDB() + + localRecipe := &model.FoodRecipe{} + localRecipe.ID = recipeID + if err = dao.GetEntity(db, localRecipe); err != nil { + return err + } + valid := dao.StrictMakeMapByStructObject(mapData, localRecipe, ctx.GetUserName()) + + dao.Begin(db) + defer func() { + if r := recover(); r != nil || err != nil { + dao.Rollback(db) + if r != nil { + panic(r) + } } - dao.WrapAddIDCULEntity(step, ctx.GetUserName()) - if err = dao.CreateEntity(db, step); err != nil { + }() + + if len(valid) > 0 { + if _, err = dao.UpdateEntityLogically(db, localRecipe, valid, ctx.GetUserName(), nil); err != nil { return err } } + if len(itemList) > 0 { + choice := &model.FoodRecipeItemChoice{ + RecipeID: recipeID, + } + if _, err = dao.DeleteEntity(db, choice, "RecipeID"); err != nil { + return err + } + + recipeItems := &model.FoodRecipeItem{ + RecipeID: recipeID, + } + if _, err = dao.DeleteEntity(db, recipeItems, "RecipeID"); err != nil { + return err + } + } + if len(stepList) > 0 { + recipeSteps := &model.FoodRecipeStep{ + RecipeID: recipeID, + } + if _, err = dao.DeleteEntity(db, recipeSteps, "RecipeID"); err != nil { + return err + } + } + if err = updateFoodRecipeItemAndStep(ctx, db, recipeID, itemList, stepList); err != nil { + return err + } dao.Commit(db) return err } - -func UpdateFoodRecipe(ctx *jxcontext.Context, foodRecipe *model.FoodRecipe, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { - return err -} From 6bf829696318aa00d22fe7fde7f3b9c7992777af Mon Sep 17 00:00:00 2001 From: gazebo Date: Tue, 17 Sep 2019 10:17:10 +0800 Subject: [PATCH 2/7] + QueryFoodRecipes --- business/model/dao/food_recipe.go | 25 +++++++++++++++++++++++++ business/userstore/food_recipe.go | 5 +++++ 2 files changed, 30 insertions(+) diff --git a/business/model/dao/food_recipe.go b/business/model/dao/food_recipe.go index 1f44d6887..376647d4f 100644 --- a/business/model/dao/food_recipe.go +++ b/business/model/dao/food_recipe.go @@ -1,2 +1,27 @@ package dao +import "git.rosy.net.cn/jx-callback/business/model" + +func QueryFoodRecipes(db *DaoDB, keyword, userID string) (recipeList []*model.FoodRecipe, err error) { + sql := ` + SELECT t1.* + FROM food_recipe t1 + WHERE 1 = 1` + sqlParams := []interface{}{} + if keyword != "" { + keywordLike := "%" + keyword + "%" + sql += " AND (t1.name LIKE ?" + sqlParams = append(sqlParams, keywordLike) + sql += ")" + } + if userID != "" { + sql += " AND t1.author_id = ?" + sqlParams = append(sqlParams, userID) + } + err = GetRows(db, &recipeList, sql, sqlParams...) + return recipeList, err +} + +func GetRecommendFoodRecipes(db *DaoDB, keyword, userID string) (recipeList []*model.FoodRecipe, err error) { + return QueryFoodRecipes(db, keyword, userID) +} diff --git a/business/userstore/food_recipe.go b/business/userstore/food_recipe.go index 31a26f5d3..7e58fb389 100644 --- a/business/userstore/food_recipe.go +++ b/business/userstore/food_recipe.go @@ -157,3 +157,8 @@ func UpdateFoodRecipe(ctx *jxcontext.Context, recipeID int, mapData map[string]i return err } + +func QueryFoodRecipes(ctx *jxcontext.Context, keyword, userID string) (recipeList []*model.FoodRecipe, err error) { + recipeList, err = dao.QueryFoodRecipes(dao.GetDB(), keyword, userID) + return recipeList, err +} From 7bc963c31f499d3b34fdbfb15fc23633068fc284 Mon Sep 17 00:00:00 2001 From: gazebo Date: Tue, 17 Sep 2019 10:47:09 +0800 Subject: [PATCH 3/7] =?UTF-8?q?-=20=E4=BF=AE=E5=A4=8D=E9=97=A8=E5=BA=97?= =?UTF-8?q?=E6=8A=A5=E8=AD=A6=E9=97=A8=E5=BA=97=E6=8C=89=E5=9F=8E=E5=B8=82?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- business/jxstore/cms/store.go | 44 +++++++++++++++++++++++++++++- business/jxstore/cms/store_test.go | 4 +-- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/business/jxstore/cms/store.go b/business/jxstore/cms/store.go index f9d3ea169..9a9aa6940 100644 --- a/business/jxstore/cms/store.go +++ b/business/jxstore/cms/store.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "math" + "sort" "strconv" "strings" "time" @@ -1561,6 +1562,28 @@ func getAllUsers4Store(ctx *jxcontext.Context, db *dao.DaoDB, store *model.Store return userList } +type tStoreIDList struct { + StoreIDList []int + StoreMap map[int]*model.Store +} + +func (l *tStoreIDList) Len() int { + return len(l.StoreIDList) +} + +func (l *tStoreIDList) Less(i, j int) bool { + storei := l.StoreMap[l.StoreIDList[i]] + storej := l.StoreMap[l.StoreIDList[j]] + if storei.CityCode == storej.CityCode { + return storei.ID < storej.ID + } + return storei.CityCode < storej.CityCode +} + +func (l *tStoreIDList) Swap(i, j int) { + l.StoreIDList[i], l.StoreIDList[j] = l.StoreIDList[j], l.StoreIDList[i] +} + func SendAlarmVendorSnapshot(ctx *jxcontext.Context, parentTask tasksch.ITask, prevSnapshotList, curSnapshotList []*model.VendorStoreSnapshot) (err error) { if len(prevSnapshotList) == 0 { return nil @@ -1646,17 +1669,37 @@ func SendAlarmVendorSnapshot(ctx *jxcontext.Context, parentTask tasksch.ITask, p } if len(userList) > 0 { + allStores, err := dao.GetStoreList(db, nil, nil, "") + if err != nil { + return err + } + allStoreMap := make(map[int]*model.Store) + for _, v := range allStores { + allStoreMap[v.ID] = v + } + const fixTitle = "门店状态变化" title := fmt.Sprintf("%s:%s-->%s", fixTitle, utils.Time2Str(prevSnapshotList[0].SnapshotAt), utils.Time2Str(curSnapshotList[0].SnapshotAt)) task := tasksch.NewParallelTask("SendAlarmVendorSnapshot", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { user := batchItemList[0].(*model.User) + if user.GetMobile() != "18048531223" { + return nil, nil + } var excelURL string if user.Type&model.UserTypeOperator != 0 { var dataList []map[string]interface{} captionList := []string{"京西门店ID", "门店名", "城市"} isFirstRow := true + + storeIDList := &tStoreIDList{ + StoreMap: allStoreMap, + } for storeID := range userMap[user.GetID()] { + storeIDList.StoreIDList = append(storeIDList.StoreIDList, storeID) + } + sort.Sort(storeIDList) + for _, storeID := range storeIDList.StoreIDList { prevAlarmMap := prevSnapshotMap2[storeID] curAlarmMap := curSnapshotMap2[storeID] data := map[string]interface{}{ @@ -1789,7 +1832,6 @@ func SaveAndSendAlarmVendorSnapshot(ctx *jxcontext.Context, vendorIDs, storeIDs err = SaveStoresVendorSnapshot(db, curSnapshotAt, curSnapshotList) case 2: prevSnapshotList, err = dao.GetVendorStoreSnapshot(db, prevSnapshotAt) - curSnapshotList, err = dao.GetVendorStoreSnapshot(db, curSnapshotAt) // 因为排序的原因,重新取一下 case 3: err = SendAlarmVendorSnapshot(ctx, task, prevSnapshotList, curSnapshotList) } diff --git a/business/jxstore/cms/store_test.go b/business/jxstore/cms/store_test.go index 20f9b7bc5..e934e2886 100644 --- a/business/jxstore/cms/store_test.go +++ b/business/jxstore/cms/store_test.go @@ -20,8 +20,8 @@ func TestGetStoresVendorSnapshot(t *testing.T) { func TestSendAlarmVendorSnapshot(t *testing.T) { db := dao.GetDB() - prevSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-09 10:00:00")) - curSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-09 11:00:00")) + prevSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-17 08:00:00")) + curSnapshotList, _ := dao.GetVendorStoreSnapshot(db, utils.Str2Time("2019-09-17 10:00:00")) err := SendAlarmVendorSnapshot(jxcontext.AdminCtx, nil, prevSnapshotList, curSnapshotList) if err != nil { t.Fatal(err) From 9c7ae161869699249bc05b45a3e57707c2ed91d8 Mon Sep 17 00:00:00 2001 From: xujianhua Date: Tue, 17 Sep 2019 11:12:33 +0800 Subject: [PATCH 4/7] =?UTF-8?q?Accept=20Merge=20Request=20#3:=20(don=20->?= =?UTF-8?q?=20mark)=20Merge=20Request:=20=E9=97=A8=E5=BA=97=E8=AF=84?= =?UTF-8?q?=E5=88=86=20Created=20By:=20@zhudan=20Accepted=20By:=20@xujianh?= =?UTF-8?q?ua=20URL:=20https://rosydev.coding.net/p/jx-callback/d/jx-callb?= =?UTF-8?q?ack/git/merge/3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- business/jxstore/cms/store_sku_check.go | 49 +- business/jxstore/misc/misc.go | 1 + business/jxstore/misc/misc2_test.go | 4 +- business/jxstore/misc/store_score.go | 780 ++++++++++++++++++++++ business/jxstore/misc/store_score_test.go | 24 + business/model/dao/dao_order.go | 68 ++ business/model/dao/store_score.go | 67 ++ business/model/store_score.go | 60 ++ business/partner/partner.go | 8 + controllers/cms_store.go | 34 + globals/beegodb/beegodb.go | 2 +- routers/commentsRouter_controllers.go | 27 + 12 files changed, 1094 insertions(+), 30 deletions(-) create mode 100644 business/jxstore/misc/store_score.go create mode 100644 business/jxstore/misc/store_score_test.go create mode 100644 business/model/dao/store_score.go create mode 100644 business/model/store_score.go diff --git a/business/jxstore/cms/store_sku_check.go b/business/jxstore/cms/store_sku_check.go index 34dd94871..eed0d6c1f 100644 --- a/business/jxstore/cms/store_sku_check.go +++ b/business/jxstore/cms/store_sku_check.go @@ -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, diff --git a/business/jxstore/misc/misc.go b/business/jxstore/misc/misc.go index f1e4c563e..1034089ff 100644 --- a/business/jxstore/misc/misc.go +++ b/business/jxstore/misc/misc.go @@ -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) diff --git a/business/jxstore/misc/misc2_test.go b/business/jxstore/misc/misc2_test.go index 38d1a9be5..e6627a75a 100644 --- a/business/jxstore/misc/misc2_test.go +++ b/business/jxstore/misc/misc2_test.go @@ -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) } diff --git a/business/jxstore/misc/store_score.go b/business/jxstore/misc/store_score.go new file mode 100644 index 000000000..e4949b1aa --- /dev/null +++ b/business/jxstore/misc/store_score.go @@ -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 +} diff --git a/business/jxstore/misc/store_score_test.go b/business/jxstore/misc/store_score_test.go new file mode 100644 index 000000000..01e0a42bb --- /dev/null +++ b/business/jxstore/misc/store_score_test.go @@ -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{}) +} diff --git a/business/model/dao/dao_order.go b/business/model/dao/dao_order.go index 1438e7c7d..a365377ed 100644 --- a/business/model/dao/dao_order.go +++ b/business/model/dao/dao_order.go @@ -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 +} diff --git a/business/model/dao/store_score.go b/business/model/dao/store_score.go new file mode 100644 index 000000000..604a3b52c --- /dev/null +++ b/business/model/dao/store_score.go @@ -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 +} diff --git a/business/model/store_score.go b/business/model/store_score.go new file mode 100644 index 000000000..84596ee80 --- /dev/null +++ b/business/model/store_score.go @@ -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 +} diff --git a/business/partner/partner.go b/business/partner/partner.go index 8208d7a20..cf94a9148 100644 --- a/business/partner/partner.go +++ b/business/partner/partner.go @@ -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 +} diff --git a/controllers/cms_store.go b/controllers/cms_store.go index f7fe8d61d..95b1c7b1b 100644 --- a/controllers/cms_store.go +++ b/controllers/cms_store.go @@ -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 + }) +} diff --git a/globals/beegodb/beegodb.go b/globals/beegodb/beegodb.go index 93bd2994e..70da7febd 100644 --- a/globals/beegodb/beegodb.go +++ b/globals/beegodb/beegodb.go @@ -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 diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 4208035e1..86bd3bf1f 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -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", From b54f86f00c83ac0af6b19e089ad508150039cc57 Mon Sep 17 00:00:00 2001 From: gazebo Date: Tue, 17 Sep 2019 11:30:07 +0800 Subject: [PATCH 5/7] =?UTF-8?q?-=20IsSyncStatusSec=20fixed=20to=20IsSyncSt?= =?UTF-8?q?atusSeq=20-=20=E4=BF=AE=E6=94=B9=EF=BC=88=E6=88=96=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=EF=BC=89=E5=8D=95=E5=B9=B3=E5=8F=B0=E9=97=A8=E5=BA=97?= =?UTF-8?q?=E5=95=86=E5=93=81=E6=97=B6=EF=BC=8C=E5=A6=82=E6=9E=9C=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E5=B9=B3=E5=8F=B0=E5=88=86=E7=B1=BBID=EF=BC=8C?= =?UTF-8?q?=E6=8A=A5=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- business/jxstore/cms/sync_store_sku.go | 20 ++++++++++++++------ business/model/model.go | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/business/jxstore/cms/sync_store_sku.go b/business/jxstore/cms/sync_store_sku.go index 374999657..dcee5ff63 100644 --- a/business/jxstore/cms/sync_store_sku.go +++ b/business/jxstore/cms/sync_store_sku.go @@ -336,8 +336,12 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo offlineList = append(offlineList, bareSku) } } else { - if sku.MergedStatus == model.SkuStatusNormal /*&& !dao.IsVendorThingIDEmpty(sku.VendorCatID)*/ { - createList = append(createList, sku) + if sku.MergedStatus == model.SkuStatusNormal { + if dao.IsVendorThingIDEmpty(sku.VendorCatID) { + globals.SugarLogger.Warnf("syncStoreSkuNew 创建门店:%d商品:%d,但没有平台分类ID", sku.StoreID, sku.SkuID) + } else { + createList = append(createList, sku) + } } } isNeedReorder = true @@ -351,9 +355,13 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo } else { isAdded2Update := false // 修改商品信息时不改价(以免活动引起的失败),而用单独的改价来改 - if (model.IsSyncStatusUpdate(sku.StoreSkuSyncStatus) || (model.IsSyncStatusSec(sku.StoreSkuSyncStatus) && reorderHandler == nil)) && singleStoreHandler != nil { - isAdded2Update = true - updateList = append(updateList, calVendorPrice4StoreSku(sku, storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage))) + if (model.IsSyncStatusUpdate(sku.StoreSkuSyncStatus) || (model.IsSyncStatusSeq(sku.StoreSkuSyncStatus) && reorderHandler == nil)) && singleStoreHandler != nil { + if dao.IsVendorThingIDEmpty(sku.VendorCatID) { + globals.SugarLogger.Warnf("syncStoreSkuNew 修改门店:%d商品:%d,但没有平台分类ID", sku.StoreID, sku.SkuID) + } else { + isAdded2Update = true + updateList = append(updateList, calVendorPrice4StoreSku(sku, storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage))) + } } if model.IsSyncStatusPrice(sku.StoreSkuSyncStatus) { bareSku = storeSkuSyncInfo2Bare(calVendorPrice4StoreSku(sku, storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage))) @@ -378,7 +386,7 @@ func syncStoreSkuNew(ctx *jxcontext.Context, parentTask tasksch.ITask, isFull bo } } } - isNeedReorder = model.IsSyncStatusSec(sku.StoreSkuSyncStatus) + isNeedReorder = model.IsSyncStatusSeq(sku.StoreSkuSyncStatus) } } if isNeedReorder && reorderHandler != nil && sku.VendorCatID != "" { diff --git a/business/model/model.go b/business/model/model.go index d49ac05bc..e52ed3b1d 100644 --- a/business/model/model.go +++ b/business/model/model.go @@ -114,7 +114,7 @@ func IsSyncStatusPrice(syncStatus int8) bool { return (syncStatus & SyncFlagPriceMask) != 0 } -func IsSyncStatusSec(syncStatus int8) bool { +func IsSyncStatusSeq(syncStatus int8) bool { return (syncStatus & SyncFlagSeqMask) != 0 } From bf8fd2f25543e6587f182c221ec5b38dc5615608 Mon Sep 17 00:00:00 2001 From: gazebo Date: Tue, 17 Sep 2019 14:36:39 +0800 Subject: [PATCH 6/7] =?UTF-8?q?-=20=E8=8F=9C=E8=B0=B1API=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?OK?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- business/model/dao/food_recipe.go | 101 ++++++++++++++++-- business/model/dao/food_recipe_test.go | 40 +++++++ business/model/dao/store_test.go | 2 +- business/userstore/food_recipe.go | 141 ++++++++++++++++++++++++- controllers/cmd_food_recipe.go | 88 ++++++++++++++- routers/commentsRouter_controllers.go | 45 ++++++++ 6 files changed, 401 insertions(+), 16 deletions(-) create mode 100644 business/model/dao/food_recipe_test.go diff --git a/business/model/dao/food_recipe.go b/business/model/dao/food_recipe.go index 376647d4f..bac34ad31 100644 --- a/business/model/dao/food_recipe.go +++ b/business/model/dao/food_recipe.go @@ -1,27 +1,108 @@ package dao -import "git.rosy.net.cn/jx-callback/business/model" +import ( + "git.rosy.net.cn/baseapi/utils" + "git.rosy.net.cn/jx-callback/business/model" +) -func QueryFoodRecipes(db *DaoDB, keyword, userID string) (recipeList []*model.FoodRecipe, err error) { - sql := ` - SELECT t1.* - FROM food_recipe t1 - WHERE 1 = 1` - sqlParams := []interface{}{} +type FoodRecipeWithAction struct { + model.FoodRecipe + ActionType int8 `json:"actionType"` +} + +func QueryFoodRecipes(db *DaoDB, keyword string, recipeID int, authorID, userID string) (recipeList []*FoodRecipeWithAction, err error) { + var sql string + var sqlParams []interface{} + if userID != "" { + sql = ` + SELECT t1.*, t2.action_type + FROM food_recipe t1 + LEFT JOIN food_recipe_user t2 ON t2.recipe_id = t1.id AND t2.user_id = ? AND t2.deleted_at = ? + WHERE t1.deleted_at = ?` + sqlParams = []interface{}{ + userID, + utils.DefaultTimeValue, + utils.DefaultTimeValue, + } + } else { + sql = ` + SELECT t1.* + FROM food_recipe t1 + WHERE t1.deleted_at = ?` + sqlParams = []interface{}{ + utils.DefaultTimeValue, + } + } if keyword != "" { keywordLike := "%" + keyword + "%" sql += " AND (t1.name LIKE ?" sqlParams = append(sqlParams, keywordLike) sql += ")" } - if userID != "" { + if recipeID > 0 { + sql += " AND t1.id = ?" + sqlParams = append(sqlParams, recipeID) + } + if authorID != "" { sql += " AND t1.author_id = ?" - sqlParams = append(sqlParams, userID) + sqlParams = append(sqlParams, authorID) } err = GetRows(db, &recipeList, sql, sqlParams...) return recipeList, err } func GetRecommendFoodRecipes(db *DaoDB, keyword, userID string) (recipeList []*model.FoodRecipe, err error) { - return QueryFoodRecipes(db, keyword, userID) + if list, err := QueryFoodRecipes(db, keyword, 0, userID, ""); err == nil { + recipeList = FoodRecipeWithActionList2Recipe(list) + } + return recipeList, err +} + +func FoodRecipeWithActionList2Recipe(recipeList []*FoodRecipeWithAction) (outRecipeList []*model.FoodRecipe) { + for _, v := range recipeList { + outRecipeList = append(outRecipeList, &v.FoodRecipe) + } + return outRecipeList +} + +func QueryFoodRecipesItems(db *DaoDB, recipeID int) (recipeItemList []*model.FoodRecipeItem, err error) { + sql := ` + SELECT t1.* + FROM food_recipe_item t1 + WHERE t1.deleted_at = ? AND t1.recipe_id = ?` + sqlParams := []interface{}{ + utils.DefaultTimeValue, + recipeID, + } + sql += " ORDER BY t1.index" + err = GetRows(db, &recipeItemList, sql, sqlParams...) + return recipeItemList, err +} + +func QueryFoodRecipesItemChoices(db *DaoDB, recipeID int) (recipeItemChoiceList []*model.FoodRecipeItemChoice, err error) { + sql := ` + SELECT t1.* + FROM food_recipe_item_choice t1 + WHERE t1.deleted_at = ? AND t1.recipe_id = ?` + sqlParams := []interface{}{ + utils.DefaultTimeValue, + recipeID, + } + sql += " ORDER BY t1.index, t1.choice_index" + err = GetRows(db, &recipeItemChoiceList, sql, sqlParams...) + return recipeItemChoiceList, err +} + +func QueryFoodRecipesSteps(db *DaoDB, recipeID int) (recipeStepList []*model.FoodRecipeStep, err error) { + sql := ` + SELECT t1.* + FROM food_recipe_step t1 + WHERE t1.deleted_at = ? AND t1.recipe_id = ?` + sqlParams := []interface{}{ + utils.DefaultTimeValue, + recipeID, + } + sql += " ORDER BY t1.index" + err = GetRows(db, &recipeStepList, sql, sqlParams...) + return recipeStepList, err } diff --git a/business/model/dao/food_recipe_test.go b/business/model/dao/food_recipe_test.go new file mode 100644 index 000000000..689ee2439 --- /dev/null +++ b/business/model/dao/food_recipe_test.go @@ -0,0 +1,40 @@ +package dao + +import ( + "testing" +) + +func TestQueryRecipes(t *testing.T) { + db := GetDB() + recipeList, err := QueryFoodRecipes(db, "", -1, "", "") + if err != nil { + t.Fatal(err) + } + if len(recipeList) > 0 { + t.Fatal("should not return list") + } + + recipeItemList, err := QueryFoodRecipesItems(db, -1) + if err != nil { + t.Fatal(err) + } + if len(recipeItemList) > 0 { + t.Fatal("should not return list") + } + + choiceList, err := QueryFoodRecipesItemChoices(db, -1) + if err != nil { + t.Fatal(err) + } + if len(choiceList) > 0 { + t.Fatal("should not return list") + } + + stepList, err := QueryFoodRecipesSteps(db, -1) + if err != nil { + t.Fatal(err) + } + if len(stepList) > 0 { + t.Fatal("should not return list") + } +} diff --git a/business/model/dao/store_test.go b/business/model/dao/store_test.go index 71e84092b..d0455d9e6 100644 --- a/business/model/dao/store_test.go +++ b/business/model/dao/store_test.go @@ -32,7 +32,7 @@ func TestFormalizeStoreStatus(t *testing.T) { } func TestGetStoreList4Role(t *testing.T) { - storeList, err := GetStoreList(GetDB(), "NiuBi") + storeList, err := GetStoreList(GetDB(), nil, nil, "NiuBi") t.Log(utils.Format4Output(storeList, false)) if err != nil { t.Fatal(err) diff --git a/business/userstore/food_recipe.go b/business/userstore/food_recipe.go index 7e58fb389..c1639cf96 100644 --- a/business/userstore/food_recipe.go +++ b/business/userstore/food_recipe.go @@ -19,6 +19,17 @@ type FoodRecipeStepParam struct { Img string `orm:"size(48)" json:"img"` } +type FoodRecipeItem struct { + model.FoodRecipeItem + ItemChoiceList []*model.FoodRecipeItemChoice +} + +type FoodRecipeDetail struct { + model.FoodRecipe + ItemList []*FoodRecipeItem + StepList []*model.FoodRecipeStep +} + func updateFoodRecipeItemAndStep(ctx *jxcontext.Context, db *dao.DaoDB, recipeID int, itemList []*FoodRecipeItemParam, stepList []*FoodRecipeStepParam) (err error) { for k, v := range itemList { if len(v.SkuIDs) == 0 { @@ -29,7 +40,7 @@ func updateFoodRecipeItemAndStep(ctx *jxcontext.Context, db *dao.DaoDB, recipeID return err } if len(v.SkuIDs) != len(skuIDs) { - return fmt.Errorf("某些SkuIDs不存在") + return fmt.Errorf("某些SkuID不存在") } item := &model.FoodRecipeItem{ @@ -158,7 +169,131 @@ func UpdateFoodRecipe(ctx *jxcontext.Context, recipeID int, mapData map[string]i return err } -func QueryFoodRecipes(ctx *jxcontext.Context, keyword, userID string) (recipeList []*model.FoodRecipe, err error) { - recipeList, err = dao.QueryFoodRecipes(dao.GetDB(), keyword, userID) +func QueryFoodRecipes(ctx *jxcontext.Context, keyword, authorID string) (recipeList []*dao.FoodRecipeWithAction, err error) { + _, userID := ctx.GetMobileAndUserID() + recipeList, err = dao.QueryFoodRecipes(dao.GetDB(), keyword, 0, authorID, userID) return recipeList, err } + +func GetRecommendFoodRecipes(ctx *jxcontext.Context, keyword string) (recipeList []*model.FoodRecipe, err error) { + _, userID := ctx.GetMobileAndUserID() + recipeList, err = dao.GetRecommendFoodRecipes(dao.GetDB(), keyword, userID) + return recipeList, err +} + +func GetRecipeDetail(ctx *jxcontext.Context, recipeID int) (recipeDetail *FoodRecipeDetail, err error) { + _, userID := ctx.GetMobileAndUserID() + db := dao.GetDB() + recipeList, err := dao.QueryFoodRecipes(db, "", recipeID, "", userID) + if err != nil { + return nil, err + } + if len(recipeList) == 0 { + return nil, fmt.Errorf("找不到菜谱:%d", recipeID) + } + + itemList, err := dao.QueryFoodRecipesItems(db, recipeID) + if err != nil { + return nil, err + } + + itemChoiceList, err := dao.QueryFoodRecipesItemChoices(db, recipeID) + if err != nil { + return nil, err + } + choiceMap := make(map[int8][]*model.FoodRecipeItemChoice) + for _, v := range itemChoiceList { + choiceMap[v.Index] = append(choiceMap[v.Index], v) + } + + stepList, err := dao.QueryFoodRecipesSteps(db, recipeID) + if err != nil { + return nil, err + } + + recipeDetail = &FoodRecipeDetail{ + FoodRecipe: recipeList[0].FoodRecipe, + StepList: stepList, + } + for _, v := range itemList { + recipeDetail.ItemList = append(recipeDetail.ItemList, &FoodRecipeItem{ + FoodRecipeItem: *v, + ItemChoiceList: choiceMap[v.Index], + }) + } + return recipeDetail, err +} + +func VoteFoodRecipe(ctx *jxcontext.Context, recipeID, voteType int) (err error) { + _, userID := ctx.GetMobileAndUserID() + if userID == "" { + return fmt.Errorf("内部错误,找不到用户") + } + + db := dao.GetDB() + recipeList, err := dao.QueryFoodRecipes(db, "", recipeID, "", userID) + if err != nil { + return err + } + if len(recipeList) == 0 { + return fmt.Errorf("找不到菜谱:%d", recipeID) + } + recipe := &recipeList[0].FoodRecipe + + dao.Begin(db) + defer func() { + if r := recover(); r != nil || err != nil { + dao.Rollback(db) + if r != nil { + panic(r) + } + } + }() + + recipeUser := &model.FoodRecipeUser{ + RecipeID: recipeID, + UserID: userID, + } + if err = dao.GetEntity(db, recipeUser); err != nil { + if dao.IsNoRowsError(err) { + return err + } + } + + actionMask := int8(0) + if voteType > 0 { + recipe.UpvoteCount++ + actionMask = model.RecipeActionUpvote + } else if voteType < 0 { + recipe.DownvoteCount++ + actionMask = model.RecipeActionDownvote + } + if recipeUser.ActionType&model.RecipeActionUpvote != 0 { + recipe.UpvoteCount-- + } else if recipeUser.ActionType&model.RecipeActionDownvote != 0 { + recipe.DownvoteCount-- + } + + if recipeUser.ActionType&actionMask != 0 || recipeUser.ActionType&(model.RecipeActionUpvote|model.RecipeActionDownvote) == 0 { + return fmt.Errorf("已经做过此操作了") + } + + recipeUser.ActionType &= actionMask | ^(model.RecipeActionUpvote | model.RecipeActionDownvote) + if recipeUser.ID == 0 { + dao.WrapAddIDCULDEntity(recipeUser, ctx.GetUserName()) + err = dao.CreateEntity(db, recipeUser) + } else { + recipeUser.LastOperator = ctx.GetUserName() + _, err = dao.UpdateEntity(db, recipeUser) + } + if err != nil { + return err + } + _, err = dao.UpdateEntity(db, recipe, "UpvoteCount", "DownvoteCount") + if err != nil { + return err + } + + dao.Commit(db) + return err +} diff --git a/controllers/cmd_food_recipe.go b/controllers/cmd_food_recipe.go index f0ace9f50..95637cbb5 100644 --- a/controllers/cmd_food_recipe.go +++ b/controllers/cmd_food_recipe.go @@ -11,8 +11,8 @@ type FoodRecipeController struct { beego.Controller } -// @Title 创建活动 -// @Description 创建活动 +// @Title 创建菜谱 +// @Description 创建菜谱 // @Param token header string true "认证token" // @Param name formData string true "菜谱名" // @Param description formData string true "菜谱描述" @@ -41,3 +41,87 @@ func (c *FoodRecipeController) CreateFoodRecipe() { return retVal, "", err }) } + +// @Title 编辑菜谱 +// @Description 编辑菜谱 +// @Param token header string true "认证token" +// @Param recipeID formData int true "菜谱ID" +// @Param name formData string false "菜谱名" +// @Param description formData string false "菜谱描述" +// @Param img formData string false "图片"" +// @Param timeInMinute formData string false "大约需要时间(分钟)" +// @Param recipeItems formData string false "菜谱配料" +// @Param recipeSteps formData string false "菜谱步骤" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /UpdateFoodRecipe [put] +func (c *FoodRecipeController) UpdateFoodRecipe() { + c.callUpdateFoodRecipe(func(params *tFoodrecipeUpdateFoodRecipeParams) (retVal interface{}, errCode string, err error) { + var ( + itemList []*userstore.FoodRecipeItemParam + stepList []*userstore.FoodRecipeStepParam + ) + if err = jxutils.Strings2Objs(params.RecipeItems, &itemList, params.RecipeSteps, &stepList); err == nil { + err = userstore.UpdateFoodRecipe(params.Ctx, params.RecipeID, params.MapData, itemList, stepList) + } + return retVal, "", err + }) +} + +// @Title 查询菜谱列表 +// @Description 查询菜谱列表 +// @Param token header string true "认证token" +// @Param keyword query string false "关键字" +// @Param userID query string false "厂商ID" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /QueryFoodRecipes [get] +func (c *FoodRecipeController) QueryFoodRecipes() { + c.callQueryFoodRecipes(func(params *tFoodrecipeQueryFoodRecipesParams) (retVal interface{}, errCode string, err error) { + retVal, err = userstore.QueryFoodRecipes(params.Ctx, params.Keyword, params.UserID) + return retVal, "", err + }) +} + +// @Title 得到我的推荐菜谱列表 +// @Description 得到我的推荐菜谱列表 +// @Param token header string true "认证token" +// @Param keyword query string false "关键字" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /GetRecommendFoodRecipes [get] +func (c *FoodRecipeController) GetRecommendFoodRecipes() { + c.callGetRecommendFoodRecipes(func(params *tFoodrecipeGetRecommendFoodRecipesParams) (retVal interface{}, errCode string, err error) { + retVal, err = userstore.GetRecommendFoodRecipes(params.Ctx, params.Keyword) + return retVal, "", err + }) +} + +// @Title 得到菜谱详情 +// @Description 得到菜谱详情 +// @Param token header string true "认证token" +// @Param recipeID query int true "菜谱ID" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /GetRecipeDetail [get] +func (c *FoodRecipeController) GetRecipeDetail() { + c.callGetRecipeDetail(func(params *tFoodrecipeGetRecipeDetailParams) (retVal interface{}, errCode string, err error) { + retVal, err = userstore.GetRecipeDetail(params.Ctx, params.RecipeID) + return retVal, "", err + }) +} + +// @Title 对菜谱投票 +// @Description 对菜谱投票 +// @Param token header string true "认证token" +// @Param recipeID formData int true "菜谱ID" +// @Param voteType formData int true "-1:踩,0:取消之前的投票,1:赞" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /VoteFoodRecipe [post] +func (c *FoodRecipeController) VoteFoodRecipe() { + c.callVoteFoodRecipe(func(params *tFoodrecipeVoteFoodRecipeParams) (retVal interface{}, errCode string, err error) { + err = userstore.VoteFoodRecipe(params.Ctx, params.RecipeID, params.VoteType) + return retVal, "", err + }) +} diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 86bd3bf1f..45e453cf0 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -484,6 +484,51 @@ 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: "GetRecipeDetail", + Router: `/GetRecipeDetail`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + 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: "GetRecommendFoodRecipes", + Router: `/GetRecommendFoodRecipes`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + 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: "QueryFoodRecipes", + Router: `/QueryFoodRecipes`, + AllowHTTPMethods: []string{"get"}, + MethodParams: param.Make(), + 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: "UpdateFoodRecipe", + Router: `/UpdateFoodRecipe`, + AllowHTTPMethods: []string{"put"}, + MethodParams: param.Make(), + 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: "VoteFoodRecipe", + Router: `/VoteFoodRecipe`, + 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", From f4b6516b4da98101bcfe34b2262fcc9161172ca2 Mon Sep 17 00:00:00 2001 From: gazebo Date: Tue, 17 Sep 2019 16:02:44 +0800 Subject: [PATCH 7/7] =?UTF-8?q?+=20=E6=B7=BB=E5=8A=A0=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=93=8D=E4=BD=9C=E7=9B=B8=E5=85=B3=E7=9A=84?= =?UTF-8?q?API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- business/jxstore/cms/cms.go | 50 ++++++++++++++++++++++++--- business/jxstore/cms/cms_test.go | 9 +++++ business/jxutils/jxutils.go | 31 +++++++++++++++++ business/model/api_config.go | 2 ++ business/model/common.go | 20 ++++------- business/model/dao/common.go | 23 ++++++++++++ controllers/cms.go | 21 +++++++++-- globals/beegodb/beegodb.go | 1 + routers/commentsRouter_controllers.go | 9 +++++ 9 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 business/model/dao/common.go diff --git a/business/jxstore/cms/cms.go b/business/jxstore/cms/cms.go index 2e8ad0f10..5aa6154da 100644 --- a/business/jxstore/cms/cms.go +++ b/business/jxstore/cms/cms.go @@ -103,10 +103,7 @@ func GetServiceInfo(ctx *jxcontext.Context) interface{} { func GetQiniuUploadToken(ctx *jxcontext.Context, suffix, hashCode string) (upTokenInfo map[string]interface{}, err error) { imgURL := "" if hashCode != "" { - db := dao.GetDB() - if skuName, err := dao.GetSkuNameByHashCode(db, hashCode); err == nil { - imgURL = skuName.Img - } + imgURL, _ = GetDataResource(ctx, hashCode) } putPolicy := storage.PutPolicy{ @@ -123,6 +120,51 @@ func GetQiniuUploadToken(ctx *jxcontext.Context, suffix, hashCode string) (upTok return upTokenInfo, err } +func RegisterDataResource(ctx *jxcontext.Context, name, resourceURL, mimeType, hashCode string) (dataRes *model.DataResource, err error) { + if model.ValideMimeTypes[mimeType] == 0 { + return nil, fmt.Errorf("MIME type:%s非法", mimeType) + } + dataRes = &model.DataResource{ + Name: name, + HashCode: hashCode, + ResoureType: mimeType, + MainURL: resourceURL, + } + vendorID := jxutils.GuessDataResourceVendor(resourceURL) + switch vendorID { + case model.VendorIDQiNiuCloud: + dataRes.QiniuURL = resourceURL + case model.VendorIDEBAI: + dataRes.EbaiURL = resourceURL + case model.VendorIDMTWM: + dataRes.MtwmURL = resourceURL + } + + dao.WrapAddIDCULEntity(dataRes, ctx.GetUserName()) + if err = dao.CreateEntity(dao.GetDB(), dataRes); err != nil { + dataRes = nil + } + return dataRes, err +} + +func GetDataResource(ctx *jxcontext.Context, hashCode string) (resourceURL string, err error) { + db := dao.GetDB() + dataRes, err := dao.GetDataResource(db, hashCode, "") + if err != nil { + if dao.IsNoRowsError(err) { + skuName, err2 := dao.GetSkuNameByHashCode(db, hashCode) + if err = err2; err == nil { + resourceURL = skuName.Img + } else if dao.IsNoRowsError(err) { + err = nil + } + } + } else { + resourceURL = dataRes.MainURL + } + return resourceURL, err +} + func GetPlaces(ctx *jxcontext.Context, keyword string, includeDisabled bool, params map[string]interface{}) ([]*model.Place, error) { sql := ` SELECT * diff --git a/business/jxstore/cms/cms_test.go b/business/jxstore/cms/cms_test.go index 8ff644fc3..c874fdc6f 100644 --- a/business/jxstore/cms/cms_test.go +++ b/business/jxstore/cms/cms_test.go @@ -4,6 +4,7 @@ import ( "fmt" "testing" + "git.rosy.net.cn/baseapi/utils" "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" @@ -27,3 +28,11 @@ func TestGetQiniuUploadToken(t *testing.T) { } fmt.Print(token) } + +func TestGetDataResource(t *testing.T) { + dataRes, err := GetDataResource(jxcontext.AdminCtx, "1D3E4A8259F359FB4CF47D541843950D") + if err != nil { + t.Fatal(err) + } + t.Log(utils.Format4Output(dataRes, false)) +} diff --git a/business/jxutils/jxutils.go b/business/jxutils/jxutils.go index 1366dd7da..4ea6c2da8 100644 --- a/business/jxutils/jxutils.go +++ b/business/jxutils/jxutils.go @@ -27,6 +27,21 @@ import ( var ( routinePool *routinepool.Pool skuNamePat *regexp.Regexp + + resourceTypeMap = map[int][]string{ + model.VendorIDQiNiuCloud: []string{ + "image.jxc4.com", + }, + model.VendorIDJD: []string{ + "img30.360buyimg.com", + }, + model.VendorIDMTWM: []string{ + "", + }, + model.VendorIDEBAI: []string{ + "image-star.elemecdn.com", + }, + } ) type OrderSkuList []*model.OrderSku @@ -692,3 +707,19 @@ func GetAuthType4Vendor(vendorID int) (authType string) { } return authType } + +func GuessDataResourceVendor(resourceURL string) (vendorID int) { + vendorID = -1 + for tmpVendorID, urlList := range resourceTypeMap { + for _, v := range urlList { + if strings.Index(resourceURL, "//"+v) >= 0 { + vendorID = tmpVendorID + break + } + } + if vendorID >= 0 { + break + } + } + return vendorID +} diff --git a/business/model/api_config.go b/business/model/api_config.go index 65b682864..3ec830c3e 100644 --- a/business/model/api_config.go +++ b/business/model/api_config.go @@ -72,6 +72,8 @@ var ( VendorIDXiaoWM: "XiaoWM", VendorIDYiLianYun: "Yilianyun", VendorIDZhongWu: "ZhongWu", + + VendorIDQiNiuCloud: "Qiniu", } VendorTypeName = map[int]string{ diff --git a/business/model/common.go b/business/model/common.go index 5745c8662..6f1f886c3 100644 --- a/business/model/common.go +++ b/business/model/common.go @@ -13,19 +13,13 @@ var ( type DataResource struct { ModelIDCUL - HashCode string `orm:"size(48);unique" json:"hash_code"` - ResoureType string // 资料的mime type + HashCode string `orm:"size(48);unique" json:"hashCode"` - Name string `orm:"size(48)" json:"name"` + ResoureType string `orm:"size(48)" json:"resoureType"` // 资料的mime type + Name string `orm:"size(48);index" json:"name"` - MainURL string - QiniuURL string - EbaiURL string - MtwmURL string -} - -func (*DataResource) TableUnique() [][]string { - return [][]string{ - []string{"HashCode", "ResoureType"}, - } + MainURL string `orm:"size(1024);column(main_url);index" json:"mainURL"` + QiniuURL string `orm:"size(1024);column(qiniu_url);index" json:"qiniuURL"` + EbaiURL string `orm:"size(1024);column(ebai_url);index" json:"ebaiURL"` + MtwmURL string `orm:"size(1024);column(mtwm_url);index" json:"mtwmURL"` } diff --git a/business/model/dao/common.go b/business/model/dao/common.go new file mode 100644 index 000000000..6c01fdadb --- /dev/null +++ b/business/model/dao/common.go @@ -0,0 +1,23 @@ +package dao + +import ( + "git.rosy.net.cn/jx-callback/business/model" +) + +func GetDataResource(db *DaoDB, hashCode, fullURL string) (dataRes *model.DataResource, err error) { + sql := ` + SELECT t1.* + FROM data_resource t1 + WHERE 1 = 1` + sqlParams := []interface{}{} + if hashCode != "" { + sql += " AND t1.hash_code = ?" + sqlParams = append(sqlParams, hashCode) + } + if fullURL != "" { + sql += " AND (t1.main_url = ? OR t1.qiniu_url = ? OR t1.ebai_url = ? OR t1.mtwm_url = ?)" + sqlParams = append(sqlParams, fullURL, fullURL, fullURL, fullURL) + } + err = GetRow(db, &dataRes, sql, sqlParams...) + return dataRes, err +} diff --git a/controllers/cms.go b/controllers/cms.go index 325f588de..94dcd24dd 100644 --- a/controllers/cms.go +++ b/controllers/cms.go @@ -17,7 +17,7 @@ type CmsController struct { // @Title 得到地点(省,城市,区)信息 // @Description 得到地点(省,城市,区)信息。 -// @Param token header string true "认证token"// @Param keyword query string false "查询关键字(可以为空,为空表示不限制)" +// @Param token header string true "认证token" // @Param keyword query string false "查询关键字(可以为空,为空表示不限制)" // @Param parentCode query int false "上级地点code,这个指的是国家标准CODE(中国为:100000,北京为:110000,北京市为:110100),不是数据库中的ID" // @Param level query int false "地点级别:省为1,市为2,区为3,注意直辖市也要分省与市级" @@ -104,6 +104,23 @@ func (c *CmsController) GetQiniuUploadToken() { }) } +// @Title 注册数据资源 +// @Description 注册数据资源 +// @Param token header string true "认证token" +// @Param hashCode formData string true "md5" +// @Param resourceURL formData string true "资源URL" +// @Param mimeType formData string true "资源MIME类型,当前只支持:image/jpeg,image/png,video/mpeg,video/mp4" +// @Param name formData string faslse "资源名" +// @Success 200 {object} controllers.CallResult +// @Failure 200 {object} controllers.CallResult +// @router /RegisterDataResource [post] +func (c *CmsController) RegisterDataResource() { + c.callRegisterDataResource(func(params *tCmsRegisterDataResourceParams) (retVal interface{}, errCode string, err error) { + retVal, err = cms.RegisterDataResource(params.Ctx, params.Name, params.ResourceURL, params.MimeType, params.HashCode) + return retVal, "", err + }) +} + // @Title 根据坐标得到区码 // @Description 根据坐标得到区码,坐标都为火星坐标(有些市是没有区的,比如东莞,这种情况下返回的区码是一个假的区域,即市的编码加上9000000) // @Param token header string true "认证token" @@ -295,4 +312,4 @@ func (c *CmsController) CreateQrOrBarCode() { retVal, err = jxutils.CreateQrOrBarCode(params.Width, params.Height, params.Codetype, params.SrcData) return retVal, "", err }) -} \ No newline at end of file +} diff --git a/globals/beegodb/beegodb.go b/globals/beegodb/beegodb.go index 70da7febd..dded52e0e 100644 --- a/globals/beegodb/beegodb.go +++ b/globals/beegodb/beegodb.go @@ -56,6 +56,7 @@ func Init() { orm.RegisterModel(&model.SensitiveWord{}) orm.RegisterModel(&model.StoreScore{}) orm.RegisterModel(&model.FoodRecipe{}, &model.FoodRecipeStep{}, &model.FoodRecipeItem{}, &model.FoodRecipeItemChoice{}, &model.FoodRecipeUser{}) + orm.RegisterModel(&model.DataResource{}) // create table orm.RunSyncdb("default", false, true) diff --git a/routers/commentsRouter_controllers.go b/routers/commentsRouter_controllers.go index 45e453cf0..0ae93a495 100644 --- a/routers/commentsRouter_controllers.go +++ b/routers/commentsRouter_controllers.go @@ -385,6 +385,15 @@ func init() { Filters: nil, Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:CmsController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:CmsController"], + beego.ControllerComments{ + Method: "RegisterDataResource", + Router: `/RegisterDataResource`, + AllowHTTPMethods: []string{"post"}, + MethodParams: param.Make(), + Filters: nil, + Params: nil}) + beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:CmsController"] = append(beego.GlobalControllerRouter["git.rosy.net.cn/jx-callback/controllers:CmsController"], beego.ControllerComments{ Method: "SendMsg2Somebody",