diff --git a/business/jxstore/cms/store_sku.go b/business/jxstore/cms/store_sku.go index c0fef36ac..af4482ce3 100644 --- a/business/jxstore/cms/store_sku.go +++ b/business/jxstore/cms/store_sku.go @@ -33,6 +33,45 @@ const ( // CopyStoreSkuModeAdd = "add" ) +type StoreSkuExt struct { + NameID int `orm:"column(name_id)" json:"nameID"` + SkuID int `orm:"column(sku_id)" json:"id"` + Comment string `orm:"size(255)" json:"comment"` + SkuCategoryID int `orm:"column(sku_category_id)" json:"categoryID"` + SkuSpecQuality float32 `json:"specQuality"` + SkuSpecUnit string `orm:"size(8)" json:"specUnit"` // 质量或容量 + Weight int `json:"weight"` // 重量/质量,单位为克,当相应的SkuName的SpecUnit为g或kg时,必须等于SpecQuality + JdID int64 `orm:"column(sku_jd_id);null;index" json:"jdID"` + SkuStatus int `json:"status"` + + BindCreatedAt time.Time `orm:"auto_now_add;type(datetime)" json:"createdAt"` + BindUpdatedAt time.Time `orm:"auto_now;type(datetime)" json:"updatedAt"` + BindLastOperator string `orm:"size(32)" json:"lastOperator"` // 最后操作员 + BindDeletedAt time.Time `orm:"type(datetime);default('1970-01-01 00:00:00')" json:"deletedAt"` + SubStoreID int `orm:"column(sub_store_id)" json:"subStoreID"` + BindPrice int `json:"price"` // 单位为分,不用int64的原因是这里不需要累加 + UnitPrice int `json:"unitPrice"` // 这个是一斤的门店商品价,放在这里的原因是避免额外增加一张store sku_name表,逻辑上要保证同一SKU NAME中的所有SKU这个字段的数据一致 + BindStatus int `json:"storeSkuStatus"` + + EbaiID int64 `orm:"column(ebai_id);index" json:"ebaiID"` + MtwmID int64 `orm:"column(mtwm_id)" json:"mtwmID"` // 这个也不是必须的,只是为了DAO取数据语句一致 + WscID int64 `orm:"column(wsc_id);index" json:"wscID"` // 表示微盟skuId + WscID2 int64 `orm:"column(wsc_id2);index" json:"wscID2"` // 表示微盟goodsId + + JdSyncStatus int8 `orm:"default(2)" json:"jdSyncStatus"` + + EbaiSyncStatus int8 `orm:"default(2)" json:"ebaiSyncStatus"` + MtwmSyncStatus int8 `orm:"default(2)" json:"mtwmSyncStatus"` + WscSyncStatus int8 `orm:"default(2)" json:"wscSyncStatus"` + + ActPrice int `json:"actPrice"` + EarningPrice int `json:"earningPrice"` + RealEarningPrice int `json:"realEarningPrice"` + + Count int `json:"count"` + Times int `json:"times"` +} + // GetStoreSkus用 type StoreSkuNameExt struct { StoreID int `orm:"column(store_id)" json:"storeID"` @@ -40,7 +79,8 @@ type StoreSkuNameExt struct { model.SkuName UnitPrice int `json:"unitPrice"` - Skus []map[string]interface{} `orm:"-" json:"skus"` + Skus []map[string]interface{} `orm:"-" json:"skus2,omitempty"` + Skus2 []*StoreSkuExt `orm:"-" json:"skus,omitempty"` SkusStr string `json:"-"` PendingOpType int8 `json:"pendingOpType"` // 取值同 StoreOpRequest.Type @@ -100,30 +140,18 @@ type StoreOpRequestInfo struct { UnitPrice int `json:"unitPrice"` } -// 待用于新的GetStoresSkus实现 -type tSkuInfo struct { - ID int `orm:"column(sku_id)" json:"id"` - CreatedAt time.Time `orm:"auto_now_add;type(datetime)" json:"createdAt"` - UpdatedAt time.Time `orm:"auto_now;type(datetime)" json:"updatedAt"` - LastOperator string `orm:"size(32)" json:"lastOperator"` // 最后操作员 - DeletedAt time.Time `orm:"type(datetime);default('1970-01-01 00:00:00')" json:"deletedAt"` +type tStoreNameBind struct { + StoreID int `orm:"column(store_id)"` + NameID int `orm:"column(name_id)"` + Name string +} - CategoryID int `orm:"column(category_id)" json:"categoryID"` // 特殊类别,一般用于秒杀,特价之类的特殊类别 - NameID int `orm:"column(name_id)" json:"nameID"` // todo 这个索引应该要求唯一 - SkuIndex int `json:"-"` - Comment string `orm:"size(255)" json:"comment"` - SpecQuality float32 `json:"specQuality"` - SpecUnit string `orm:"size(8)" json:"specUnit"` // 质量或容量 - Weight int `json:"weight"` // 重量/质量,单位为克,当相应的SkuName的SpecUnit为g或kg时,必须等于SpecQuality - Status int `json:"status"` +type tGetStoresSkusInfo struct { + StoreID int `orm:"column(store_id)"` + StoreName string - JdID int64 `orm:"column(jd_id);null;index" json:"jdID"` - EbaiID int64 `orm:"column(ebai_id);index"` - MtwmID string `orm:"column(mtwm_id);index;size(16)"` // 美团外卖没有ID,保存名字 - - JdSyncStatus int8 `orm:"default(2)" json:"jdSyncStatus"` - EbaiSyncStatus int8 `orm:"default(2)"` - MtwmSyncStatus int8 `orm:"default(2)"` + model.SkuName + StoreSkuExt } const ( @@ -139,7 +167,8 @@ func getGetStoresSkusBaseSQL(db *dao.DaoDB, storeIDs, skuIDs []int, isFocus bool sql = ` FROM sku_name t1 JOIN sku t2 ON t1.id = t2.name_id AND t2.deleted_at = ?/* AND t2.status = ?*/ - JOIN store t3 ON t3.id IN (` + dao.GenQuestionMarks(len(storeIDs)) + `)` + JOIN store t3 ON t3.deleted_at = ? + ` if !isFocus { sql += " LEFT" } @@ -151,7 +180,7 @@ func getGetStoresSkusBaseSQL(db *dao.DaoDB, storeIDs, skuIDs []int, isFocus bool sqlParams = []interface{}{ utils.DefaultTimeValue, // model.SkuStatusNormal, - storeIDs, + utils.DefaultTimeValue, utils.DefaultTimeValue, utils.DefaultTimeValue, utils.Bool2Int(isFocus), @@ -224,6 +253,10 @@ func getGetStoresSkusBaseSQL(db *dao.DaoDB, storeIDs, skuIDs []int, isFocus bool sql += " AND t1.unit = ?" sqlParams = append(sqlParams, params["unit"].(string)) } + if len(storeIDs) > 0 { + sql += " AND t3.id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")" + sqlParams = append(sqlParams, storeIDs) + } if len(skuIDs) > 0 { sql += " AND t2.id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")" sqlParams = append(sqlParams, skuIDs) @@ -269,9 +302,234 @@ func getGetStoresSkusBaseSQL(db *dao.DaoDB, storeIDs, skuIDs []int, isFocus bool return sql, sqlParams, err } +func GetStoresSkus(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bool, keyword string, isBySku bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *StoreSkuNamesInfo, err error) { + return GetStoresSkusNew(ctx, storeIDs, skuIDs, isFocus, keyword, isBySku, params, offset, pageSize) +} + +func GetStoresSkusNew(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bool, keyword string, isBySku bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *StoreSkuNamesInfo, err error) { + if !isFocus && !isBySku && (len(storeIDs) > 1 || len(storeIDs) == 0) { + return nil, fmt.Errorf("未关注按SkuName只能查询单店") + } + db := dao.GetDB() + sql, sqlParams, err := getGetStoresSkusBaseSQL(db, storeIDs, skuIDs, isFocus, keyword, isBySku, params) + if err != nil { + return nil, err + } + pageSize = jxutils.FormalizePageSize(pageSize) + sqlOffset := offset + sqlPageSize := pageSize + isSaleInfo := params["stFromTime"] != nil + if isSaleInfo { + sqlOffset = 0 + sqlPageSize = jxutils.FormalizePageSize(-1) + } + sqlParamsPage := []interface{}{sqlPageSize, sqlOffset} + + dao.Begin(db) + defer func() { + if r := recover(); r != nil { + dao.Rollback(db) + panic(r) + } + }() + + skuNamesInfo = &StoreSkuNamesInfo{} + if !isBySku { + sql2 := ` + SELECT SQL_CALC_FOUND_ROWS + t3.id store_id, t1.id name_id + ` + sql + ` + GROUP BY 1, 2 + ORDER BY 1, 2 + LIMIT ? OFFSET ? + ` + sqlParams2 := append([]interface{}{}, sqlParams...) + sqlParams2 = append(sqlParams2, sqlParamsPage) + var storeNameList []*tStoreNameBind + beginTime := time.Now() + if err = dao.GetRows(db, &storeNameList, sql2, sqlParams2...); err != nil { + dao.Rollback(db) + return nil, err + } + globals.SugarLogger.Debug(time.Now().Sub(beginTime)) + skuNamesInfo.TotalCount = dao.GetLastTotalRowCount(db) + sql += " AND (1 = 0" + for _, v := range storeNameList { + sql += " OR (t1.id = ? AND t3.id = ?)" + sqlParams = append(sqlParams, v.NameID, v.StoreID) + } + sql += `) + ORDER BY t3.id, t2.name_id, t2.id + ` + } else { + if isFocus { + sql += ` + ORDER BY t3.id, t2.name_id, t2.id + ` + } + sql += ` + LIMIT ? OFFSET ? + ` + sqlParams = append(sqlParams, sqlParamsPage) + } + sql = ` + SELECT SQL_CALC_FOUND_ROWS + t3.id store_id, t3.name store_name, t3.pay_percentage, + t1.*, + t2.name_id, t2.id sku_id, t2.spec_quality sku_spec_quality, t2.spec_unit sku_spec_unit, t2.weight, t2.jd_id sku_jd_id, + t2.comment, t2.category_id sku_category_id, t2.status sku_status, + + t4.created_at bind_created_at, t4.updated_at bind_updated_at, t4.last_operator bind_last_operator, t4.deleted_at bind_deleted_at, + t4.sub_store_id, t4.price bind_price, IF(t4.unit_price IS NOT NULL, t4.unit_price, t1.price) unit_price, t4.status bind_status, + t4.ebai_id, t4.mtwm_id, t4.wsc_id, t4.wsc_id2, + t4.jd_sync_status, t4.ebai_sync_status, t4.mtwm_sync_status, t4.wsc_sync_status + ` + sql + var tmpList []*tGetStoresSkusInfo + beginTime := time.Now() + if err = dao.GetRows(db, &tmpList, sql, sqlParams...); err != nil { + dao.Rollback(db) + return nil, err + } + if isBySku { + skuNamesInfo.TotalCount = dao.GetLastTotalRowCount(db) + } + dao.Commit(db) + globals.SugarLogger.Debug(time.Now().Sub(beginTime)) + storeNameMap := make(map[int64]*StoreSkuNameExt) + for _, v := range tmpList { + var storeName *StoreSkuNameExt + index := jxutils.Combine2Int(v.StoreID, v.ID) + if isBySku || storeNameMap[index] == nil { + storeName = &StoreSkuNameExt{ + StoreID: v.StoreID, + StoreName: v.StoreName, + SkuName: v.SkuName, + UnitPrice: v.UnitPrice, + } + if !isBySku { + storeNameMap[index] = storeName + } + skuNamesInfo.SkuNames = append(skuNamesInfo.SkuNames, storeName) + } else { + storeName = storeNameMap[index] + } + storeName.Skus2 = append(storeName.Skus2, &v.StoreSkuExt) + } + err = updateActPrice4StoreSkuName(db, storeIDs, skuIDs, skuNamesInfo) + if err == nil && isSaleInfo { + err = updateSaleInfo4StoreSkuName(ctx, db, offset, pageSize, params, skuNamesInfo) + } + // globals.SugarLogger.Debug(utils.Format4Output(skuNamesInfo, false)) + return skuNamesInfo, err +} + +func updateActPrice4StoreSkuName(db *dao.DaoDB, storeIDs, skuIDs []int, skuNamesInfo *StoreSkuNamesInfo) (err error) { + // 活动商品信息 + jxSkuPriceMap, err := dao.GetPromotionSkuPriceMap(db, model.VendorIDJX, storeIDs, skuIDs, time.Now(), time.Now()) + if err != nil { + globals.SugarLogger.Errorf("updateActPrice4StoreSkuName can not get sku promotion info for error:%v", err) + return err + } + jdSkuPriceMap, err2 := dao.GetPromotionSkuPriceMap(db, model.VendorIDJD, storeIDs, skuIDs, time.Now(), time.Now()) + if err = err2; err != nil { + globals.SugarLogger.Errorf("updateActPrice4StoreSkuName can not get sku promotion info for error:%v", err) + return err + } + for _, skuName := range skuNamesInfo.SkuNames { + if len(skuName.Skus2) > 0 { + for _, v := range skuName.Skus2 { + index := dao.GenSkuPriceMapKey(skuName.StoreID, v.SkuID) + if jdSkuPriceMap[index] != nil { + v.ActPrice = jdSkuPriceMap[index].Price + } + if jxSkuPriceMap[index] != nil { + v.EarningPrice = jxSkuPriceMap[index].EarningPrice + } + + v.RealEarningPrice = v.EarningPrice + if v.RealEarningPrice == 0 { + v.RealEarningPrice = int(jxutils.CaculateSkuEarningPrice(int64(v.BindPrice), int64(v.BindPrice), skuName.PayPercentage)) + } + } + } else { + skuName.UnitPrice = skuName.Price + } + } + return err +} + +func updateSaleInfo4StoreSkuName(ctx *jxcontext.Context, db *dao.DaoDB, offset, pageSize int, params map[string]interface{}, skuNamesInfo *StoreSkuNamesInfo) (err error) { + var ( + saleInfoList []*SkuSaleInfo + timeList []time.Time + fromCount, toCount int + ) + storeIDMap := make(map[int]int) + skuIDMap := make(map[int]int) + saleInfoMap := make(map[int64]*SkuSaleInfo) + for _, skuName := range skuNamesInfo.SkuNames { + storeIDMap[skuName.StoreID] = 1 + for _, sku := range skuName.Skus2 { + skuIDMap[sku.SkuID] = 1 + } + } + storeIDs := jxutils.IntMap2List(storeIDMap) + skuIDs := jxutils.IntMap2List(skuIDMap) + toTimeStr := "" + if params["stToTime"] != nil { + toTimeStr = params["stToTime"].(string) + } + if timeList, err = jxutils.BatchStr2Time(params["stFromTime"].(string), toTimeStr); err != nil { + return err + } + if params["stFromCount"] != nil { + fromCount = params["stFromCount"].(int) + } + toCount = math.MaxInt32 + if params["stToCount"] != nil { + toCount = params["stToCount"].(int) + } + // 不能用SQL筛除,否则不能区分是没有销量,还是不在条件中 + if saleInfoList, err = GetStoresSkusSaleInfo(ctx, storeIDs, skuIDs, timeList[0], timeList[1], 0, math.MaxInt32); err != nil { + return err + } + for _, saleInfo := range saleInfoList { + saleInfoMap[jxutils.Combine2Int(saleInfo.StoreID, saleInfo.SkuID)] = saleInfo + } + var newSkuNames []*StoreSkuNameExt + for _, skuName := range skuNamesInfo.SkuNames { + var newSkus []*StoreSkuExt + for _, sku := range skuName.Skus2 { + saleInfo := saleInfoMap[jxutils.Combine2Int(skuName.StoreID, sku.SkuID)] + if saleInfo == nil && fromCount == 0 { + saleInfo = &SkuSaleInfo{} + } + if saleInfo != nil && saleInfo.Count >= fromCount && saleInfo.Count <= toCount { + sku.Times = saleInfo.Times + sku.Count = saleInfo.Count + newSkus = append(newSkus, sku) + } + } + if len(newSkus) > 0 { + skuName.Skus2 = newSkus + newSkuNames = append(newSkuNames, skuName) + } + } + skuNamesInfo.TotalCount = len(newSkuNames) + skuNamesInfo.SkuNames = nil + if offset < skuNamesInfo.TotalCount { + endIndex := offset + pageSize + if endIndex > skuNamesInfo.TotalCount { + endIndex = skuNamesInfo.TotalCount + } + skuNamesInfo.SkuNames = newSkuNames[offset:endIndex] + } + return err +} + // 商品不可售,直接排除 // 如果门店商品是可售状态,那么会忽略区域限制。否则有区域限制 -func GetStoresSkus(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bool, keyword string, isBySku bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *StoreSkuNamesInfo, err error) { +func GetStoresSkusOld(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bool, keyword string, isBySku bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *StoreSkuNamesInfo, err error) { db := dao.GetDB() sql, sqlParams, err := getGetStoresSkusBaseSQL(db, storeIDs, skuIDs, isFocus, keyword, isBySku, params) if err != nil { @@ -355,24 +613,22 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bool, // globals.SugarLogger.Debug(sqlData, sqlParams) if err = dao.GetRows(db, &skuNamesInfo.SkuNames, sqlData, sqlParams...); err == nil { skuNamesInfo.TotalCount = dao.GetLastTotalRowCount(db) + dao.Commit(db) // 活动商品信息 jxSkuPriceMap, err2 := dao.GetPromotionSkuPriceMap(db, model.VendorIDJX, storeIDs, skuIDs, time.Now(), time.Now()) if err = err2; err != nil { - dao.Rollback(db) globals.SugarLogger.Errorf("GetStoresSkus can not get sku promotion info for error:%v", err) return nil, err } jdSkuPriceMap, err2 := dao.GetPromotionSkuPriceMap(db, model.VendorIDJD, storeIDs, skuIDs, time.Now(), time.Now()) if err = err2; err != nil { - dao.Rollback(db) globals.SugarLogger.Errorf("GetStoresSkus can not get sku promotion info for error:%v", err) return nil, err } for _, skuName := range skuNamesInfo.SkuNames { if skuName.SkusStr != "" { if err = utils.UnmarshalUseNumber([]byte(skuName.SkusStr), &skuName.Skus); err != nil { - dao.Rollback(db) return nil, err } if len(skuName.Skus) > 0 { @@ -423,7 +679,6 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bool, toTimeStr = params["stToTime"].(string) } if timeList, err = jxutils.BatchStr2Time(params["stFromTime"].(string), toTimeStr); err != nil { - dao.Rollback(db) return nil, err } if params["stFromCount"] != nil { @@ -435,7 +690,6 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bool, } // 不能用SQL筛除,否则不能区分是没有销量,还是不在条件中 if saleInfoList, err = GetStoresSkusSaleInfo(ctx, storeIDs, skuIDs, timeList[0], timeList[1], 0, math.MaxInt32); err != nil { - dao.Rollback(db) return nil, err } for _, saleInfo := range saleInfoList { @@ -494,8 +748,9 @@ func GetStoresSkus(ctx *jxcontext.Context, storeIDs, skuIDs []int, isFocus bool, } } } + } else { + dao.Rollback(db) } - dao.Commit(db) return skuNamesInfo, err } @@ -582,7 +837,6 @@ func GetStoresSkusSaleInfo(ctx *jxcontext.Context, storeIDs []int, skuIDs []int, FROM order_sku t1 JOIN goods_order t2 ON t1.vendor_order_id = t2.vendor_order_id AND t1.vendor_id = t2.vendor_id AND t2.status = ? WHERE t1.order_created_at >= ? AND t1.order_created_at <= ? - AND IF(t2.jx_store_id <> 0, jx_store_id, store_id) IN (` + dao.GenQuestionMarks(len(storeIDs)) + `) ` if utils.IsTimeZero(toTime) { toTime = time.Now() @@ -591,7 +845,12 @@ func GetStoresSkusSaleInfo(ctx *jxcontext.Context, storeIDs []int, skuIDs []int, model.OrderStatusFinished, fromTime, toTime, - storeIDs, + } + if len(storeIDs) > 0 { + sql += ` + AND IF(t2.jx_store_id <> 0, jx_store_id, store_id) IN (` + dao.GenQuestionMarks(len(storeIDs)) + `) + ` + sqlParams = append(sqlParams, storeIDs) } if len(skuIDs) > 0 { sql += ` @@ -709,11 +968,7 @@ func checkStoresSkusSaleCity(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []i return nil } sql += " AND t2.id IN (" + dao.GenQuestionMarks(len(nameIDs)) + ")" - var invalidList []*struct { - StoreID int `orm:"column(store_id)"` - NameID int `orm:"column(name_id)"` - Name string - } + var invalidList []*tStoreNameBind if err = dao.GetRows(db, &invalidList, sql, utils.DefaultTimeValue, storeIDs, nameIDs); err == nil { if len(invalidList) > 0 { errMsg := "" diff --git a/controllers/cms_store_sku.go b/controllers/cms_store_sku.go index 3299d8896..50deb7e76 100644 --- a/controllers/cms_store_sku.go +++ b/controllers/cms_store_sku.go @@ -59,7 +59,7 @@ func (c *StoreSkuController) GetStoreSkus() { // @Title 得到商家商品信息 // @Description 得到商家商品信息,如下条件之间是与的关系。对于没有认领的商品,按城市限制。但对于已经认领的商品就不限制了,因为已经在平台上可售,可以操作(改价等等) // @Param token header string true "认证token" -// @Param storeIDs query string true "门店ID" +// @Param storeIDs query string false "门店ID" // @Param isFocus query bool true "是否已关注(认领)" // @Param keyword query string false "查询关键字(可以为空,为空表示不限制)" // @Param nameIDs query string false "SkuName ID列表对象"