package cms

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"math"
	"mime/multipart"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
	"unicode"

	"git.rosy.net.cn/jx-callback/business/partner/purchase/mtwm"
	"git.rosy.net.cn/jx-callback/business/partner/purchase/tiktok_store"

	"git.rosy.net.cn/baseapi/platformapi/mtwmapi"

	"git.rosy.net.cn/jx-callback/business/auth2"

	beego "github.com/astaxie/beego/server/web"

	"git.rosy.net.cn/baseapi/platformapi/jdshopapi"

	"git.rosy.net.cn/jx-callback/globals/api"

	"git.rosy.net.cn/jx-callback/globals/refutil"

	"git.rosy.net.cn/jx-callback/business/jxstore/event"
	"git.rosy.net.cn/jx-callback/business/jxstore/permission"

	"git.rosy.net.cn/baseapi"

	"git.rosy.net.cn/jx-callback/business/jxutils/ddmsg"
	"git.rosy.net.cn/jx-callback/business/jxutils/excel"
	"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
	"git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
	"git.rosy.net.cn/jx-callback/globals/api/apimanager"

	"git.rosy.net.cn/jx-callback/business/partner"

	"git.rosy.net.cn/baseapi/platformapi/dingdingapi"
	"git.rosy.net.cn/baseapi/platformapi/jdapi"
	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/baseapi/utils/errlist"
	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
	"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/weixinmsg"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
	"git.rosy.net.cn/jx-callback/globals"
	"github.com/360EntSecGroup-Skylar/excelize"
	"github.com/astaxie/beego/client/orm"
)

const (
	MaxSkuUnitPrice = 500000

	CopyStoreSkuModeFresh       = "fresh"       // 全新复制
	CopyStoreSkuModeUpdate      = "update"      // 增量复制
	CopyStoreSkuModeUpdatePrice = "updatePrice" // 增量复制价格

	upcSpecName1 = "（新老包装随机发货）"
)

//通用写入Excel
type ExcelParam struct {
	DataList  interface{}
	SheetName string
	TitleList []string
}

// UpdateStoreSku用，API调用时
type StoreSkuBindSkuInfo struct {
	SkuID        int    `json:"skuID"`
	IsSale       int    `json:"isSale,omitempty"` // -1：不可售，0：忽略，1：可售
	Stock        *int   `json:"stock"`
	LocationCode string `json:"locationCode"` // 物料货架码
	// ElmID  int64 `json:"elmID,omitempty"`
	// EbaiID int64 `json:"ebaiID,omitempty"`
	MtLadderBoxPrice int `json:"mtLadderBoxPrice"`
}

// UpdateStoreSku用，API调用时
type StoreSkuBindInfo struct {
	StoreID          int                    `json:"storeID"`
	NameID           int                    `json:"nameID"`
	UnitPrice        int                    `json:"unitPrice"` // 对于是份的SKU就是单价（每斤价格），其它则为总价
	IsFocus          int                    `json:"isFocus"`   // -1：不关注，0：忽略，1：关注
	IsSale           int                    `json:"isSale"`    // -1：不可售，0：忽略，1：可售
	SubStoreID       int                    `json:"subStoreID,omitempty"`
	StatusSaleBegin  *int16                 `json:"statusSaleBegin" validate:"max=2359,min=1,ltfield=StatusSaleEnd"` //商品可售时间范围
	StatusSaleEnd    *int16                 `json:"statusSaleEnd" validate:"max=2359,min=1"`
	UPC              string                 `json:"upc"`
	MtLadderBoxPrice int                    `json:"mtLadderBoxPrice"`
	Skus             []*StoreSkuBindSkuInfo `json:"skus,omitempty"`
}

type tStoreSkuBindAndSpec struct {
	model.StoreSkuBind
	SkuStatus       int
	SkuNameStatus   int
	Name            string
	SpecQuality     float32
	SpecUnit        string
	SkuNamePrice    int
	SkuNameUnit     string
	RealSkuID       int    `orm:"column(real_sku_id)"`
	ExdSkuID        string `orm:"column(exd_sku_id)"`
	StoreName       string
	ChangePriceType int8 `json:"changePriceType"` // 修改价格类型，即是否需要审核
	NameID          int  `orm:"column(name_id)" json:"nameID"`
}

type SkuSaleInfo struct {
	StoreID int `orm:"column(store_id)"`
	SkuID   int `orm:"column(sku_id)"`
	Times   int // 销售的次数
	Count   int // 销售的总份数
}

type StoreOpRequestInfo struct {
	model.StoreOpRequest
	StoreName     string `json:"storeName"`
	SkuNamePrefix string `json:"skuNamePrefix"`
	SkuNameName   string `json:"skuNameName"`
	UnitPrice     int    `json:"unitPrice"`
}

type tStoreNameBind struct {
	StoreID int `orm:"column(store_id)"`
	NameID  int `orm:"column(name_id)"`
	Name    string
}

type tGetStoresSkusInfo struct {
	StoreID   int `orm:"column(store_id)"`
	StoreName string

	model.SkuName
	PayPercentage int `json:"-"`
	dao.StoreSkuExt
	RealMidUnitPrice int `json:"realMidUnitPrice"` //真实的该商品的全国中位价
	YbSkuName        string
	AuditUnitPrice   int
}

type tGetStoresSkusInfoContainCategoryName struct {
	StoreID   int `orm:"column(store_id)"`
	StoreName string
	model.SkuName
	PayPercentage int `json:"-"`
	dao.StoreSkuExt
	RealMidUnitPrice int `json:"realMidUnitPrice"` //真实的该商品的全国中位价
	YbSkuName        string
	AuditUnitPrice   int
	CategoryName     string `orm:"column(categoryName)"`
}

type SheetParam struct {
	OutSkuIDCol  int
	SkuNameIDCol int
	SkuPriceCol  int
	SkuNameCol   int
	SkuUnitCol   int
	SkuRow       int
}

type DataVendorStoreSkuPrice struct {
	StoreID     string `json:"门店ID"`
	StoreName   string `json:"门店名"`
	SkuID       int    `json:"商品ID"`
	SkuName     string `json:"商品名"`
	VendorPrice string `json:"平台价"`
}

type DataSuccess struct {
	NameID   int     `json:"商品NameID"`
	Name     string  `json:"商品名称"`
	Unit     string  `json:"单位"`
	OrgPrice float64 `json:"原价"`
	NowPrice float64 `json:"现价"`
	MixPrice float64 `json:"涨跌"`
}

type DataFailed struct {
	NameID  int    `json:"商品NameID"`
	Name    string `json:"商品名称"`
	Comment string `json:"备注"`
}

type DataLock struct {
	dataSuccessList []DataSuccess
	dataFailedList  []DataFailed
	locker          sync.RWMutex
}

type tUpdateStoresSkus struct {
	StoreID      int
	SkuBindInfos []*StoreSkuBindInfo
}

type tStoreSkusSecKill struct {
	StoreID           int `orm:"column(store_id)"`
	VendorID          int `orm:"column(vendor_id)"`
	SecKillCount      int
	SecKillCount2     int
	OperatorPhoneList []string
	MarketManPhone    string
	NoticeMsg         string
}

type JdStoreSkus struct {
	JdStoreID int `json:"jdStoreID"`
	JdSkuID   int `json:"jdSkuID"`
	Price     int `json:"price"`
}

type tUpdateSkuSpecTag struct {
	StoreID int `json:"storeID"`
	SkuID   int `json:"skuID"`
	IsSpec  int `json:"isSpec"`
}

type MatterStock struct {
	SkuID     int    `json:"skuID"`
	SkuNameID int    `json:"skuNameID"`
	Name      string `json:"name"`
	Stock     int    `json:"stock"`
}

type ActStoreSkuParam struct {
	model.ActStoreSku

	ActualActPrice int64  `json:"actualActPrice,omitempty"` // 单品级活动用，创建活动时商品的活动价格
	VendorPrice    int64  `json:"vendorPrice,omitempty"`    // 创建活动时的平台价格
	ErrMsg         string `json:"errMsg,omitempty"`
}

const (
	maxStoreNameBind  = 10000 // 最大门店SkuName bind个数
	maxStoreNameBind2 = 10000 // 最大门店乘SkuName个数

	AutoSaleAtStr = "22:00:00"
)

var (
	asyncOpMobileMap = map[string]int{
		"18982250714": 1, // 老赵
		"18180948107": 1, // 徐
		// "13684045763": 1, // 周
	}
	dataLock                     DataLock
	titleListVendorStoreSkuPrice = []string{
		"门店ID",
		"门店名",
		"商品ID",
		"商品名",
		"平台价",
	}
	titleListSuccess = []string{
		"商品NameID",
		"商品名称",
		"单位",
		"原价",
		"现价",
		"涨跌",
	}
	titleListFailed = []string{
		"商品NameID",
		"商品名称",
		"备注",
	}
	autoNotFoucsStoreMap = map[int]int{
		667088:              667088,
		model.MatterStoreID: model.MatterStoreID,
		103038:              103038,
		300397:              300397,
	}
)

func GetStoresSkusForStore(ctx *jxcontext.Context, storeID int, isFocus, isAct bool, keyword string, categoryID, status, offset, pageSize int, stock int, locationCode bool) (skuNamesInfo *dao.StoreSkuNamesInfo2, err error) {
	var (
		sqlParams []interface{}
		db        = dao.GetDB()
		SkuNames  []*dao.StoreSkuNameExt2
	)
	sql := `
			SELECT SQL_CALC_FOUND_ROWS DISTINCT a.*, e.id store_id, c.unit_price ,t7.unit_price audit_unit_price
			--	,b.id sku_id, b.spec_quality sku_spec_quality, 
			--	b.spec_unit sku_spec_unit, b.status sku_status,	c.stock, c.price bind_price,
			--	c.status store_sku_status
			  FROM sku_name a 
		 	  JOIN sku b ON b.name_id = a.id AND b.deleted_at = ?
			  JOIN store e ON e.deleted_at = ? 
	`
	sqlParams = append(sqlParams,
		utils.DefaultTimeValue,
		utils.DefaultTimeValue,
	)
	if !isFocus {
		sql += `
			LEFT 
		`
	}
	sql += `
		JOIN store_sku_bind c ON c.sku_id = b.id  AND c.deleted_at = ? AND c.store_id = e.id
	`
	sqlParams = append(sqlParams, utils.DefaultTimeValue)
	if status != -1 {
		sql += " AND c.status = ?"
		sqlParams = append(sqlParams, status)
	}
	sql += `
	    LEFT JOIN sku_name_place_bind f ON a.id = f.name_id AND e.city_code = f.place_code
		LEFT JOIN store_sku_audit t7 ON t7.store_id = e.id AND t7.name_id = a.id AND t7.status = ? AND t7.deleted_at = ?
	`
	sqlParams = append(sqlParams, 0, utils.DefaultTimeValue)
	if isAct {
		sql += `
		JOIN (
			SELECT t2.store_id, t2.sku_id,
			MIN(IF(t3.actual_act_price <= 0, NULL, t3.actual_act_price)) actual_act_price, /*non-zero min value*/
			MIN(IF(t2.earning_price <= 0, NULL, t2.earning_price)) earning_price /*non-zero min value*/
			FROM act t1
			JOIN act_store_sku t2 ON t2.act_id = t1.id AND t2.deleted_at = ?
			JOIN act_store_sku_map t3 ON t3.bind_id = t2.id AND t3.act_id = t1.id AND (t3.sync_status & ? = 0 OR t1.type = ?)
			JOIN act_map t4 ON t4.act_id = t1.id AND t4.vendor_id = t3.vendor_id AND t4.deleted_at = ? AND (t4.sync_status & ? = 0 OR t1.type = ?)
			WHERE t1.deleted_at = ? AND t1.status = ? AND NOT (t1.begin_at > ? OR t1.end_at < ?)
			AND t2.store_id = ?
			GROUP BY 1,2
		) ta ON ta.store_id = e.id AND ta.sku_id = b.id
		`
		sqlParams = append(sqlParams,
			utils.DefaultTimeValue,
			model.SyncFlagNewMask,
			model.ActSkuFake,
			utils.DefaultTimeValue,
			model.SyncFlagNewMask,
			model.ActSkuFake,
			utils.DefaultTimeValue,
			model.ActStatusCreated,
			time.Now(),
			time.Now(),
			storeID,
		)
	}
	sql += `
			 WHERE a.deleted_at = ? 
			   AND (a.is_global = 1 OR f.id IS NOT NULL OR 1 = ?)
			   AND e.id = ?
	`
	sqlParams = append(sqlParams, utils.DefaultTimeValue, utils.Bool2Int(isFocus), storeID)
	if categoryID != 0 {
		cat := &model.SkuCategory{}
		cat.ID = categoryID
		if err = dao.GetEntity(db, cat); err == nil {
			sql += " AND (a.category_id = ?"
			sqlParams = append(sqlParams, cat.ID)
			if cat.Level == 1 {
				sql += " OR a.category_id IN (SELECT id FROM sku_category WHERE parent_id = ?)"
				sqlParams = append(sqlParams, cat.ID)
			}
			sql += ")"
		}
	}
	if isFocus {
		sql += " AND a.status = ? AND b.status = ?"
		sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal)
	} else {
		sql += " AND c.sku_id IS NULL AND (a.status = ? AND b.status = ?)"
		sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal)
	}
	if stock == 3 {
		sql += " AND c.stock = 0"
	} else if stock == 2 {
		sql += " AND c.stock <> 0"
	}
	if locationCode {
		sql += " AND (c.location_code = '' OR c.location_code = 'EMPTY_VALUE') "
	}

	if keyword != "" {
		sql += " AND ( a.name LIKE ? OR c.location_code LIKE ? )"
		sqlParams = append(sqlParams, "%"+keyword+"%", "%"+keyword+"%")
	}

	sql += `
		LIMIT ? OFFSET ?
	`
	pageSize = jxutils.FormalizePageSize(pageSize)
	offset = jxutils.FormalizePageOffset(offset)
	sqlParams = append(sqlParams, pageSize, offset)
	txDB, _ := dao.Begin(db)
	defer dao.Commit(db, txDB)
	if err = dao.GetRowsTx(txDB, &SkuNames, sql, sqlParams...); err == nil {
		skuNamesInfo = &dao.StoreSkuNamesInfo2{
			TotalCount: dao.GetLastTotalRowCount2(db, txDB),
		}
		// skuNamesInfo.TotalCount = dao.GetLastTotalRowCount2(db, txDB)
		task := tasksch.NewParallelTask("uuu", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
			func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				v := batchItemList[0].(*dao.StoreSkuNameExt2)
				var skus []*dao.StoreSkuExt
				sql2 := `	
			SELECT a.id sku_id, a.spec_quality sku_spec_quality, a.spec_unit sku_spec_unit, a.status sku_status, a.comment,
			b.stock, b.price bind_price, b.unit_price,b.jd_sync_status,b.mtwm_id,b.mtwm_sync_status,b.ebai_id,b.ebai_sync_status,
			b.status store_sku_status,b.location_code 
			FROM sku a
			JOIN sku_name c ON a.name_id = c.id AND c.deleted_at = ? AND c.status = ?
		`

				sqlParams2 := []interface{}{
					utils.DefaultTimeValue, model.SkuStatusNormal,
				}
				if !isFocus {
					sql2 += `
				LEFT
			`
				}
				sql2 += ` JOIN store_sku_bind b ON a.id = b.sku_id AND b.deleted_at = ? AND b.store_id = ?
			WHERE a.deleted_at = ? AND a.name_id = ? AND a.status = ?
		`
				sqlParams2 = append(sqlParams2, utils.DefaultTimeValue, storeID,
					utils.DefaultTimeValue, v.ID, model.SkuStatusNormal,
				)
				if status != -1 {
					sql2 += " AND b.status = ?"
					sqlParams2 = append(sqlParams2, status)
				}
				if err = dao.GetRows(db, &skus, sql2, sqlParams2); err == nil {
					v.Skus = skus
				}
				return retVal, err
			}, SkuNames)
		tasksch.HandleTask(task, nil, false).Run()
		task.GetResult(0)
		//for _, v := range SkuNames {
		//
		//}
		skuNamesInfo.SkuNames = SkuNames
		//storeNameMap := make(map[int64]*dao.StoreSkuNameExt)
		//for _, v := range tmpList {
		//	var storeName *dao.StoreSkuNameExt
		//	index := jxutils.Combine2Int(v.StoreID, v.ID)
		//	if storeNameMap[index] == nil {
		//		storeName = &dao.StoreSkuNameExt{
		//			StoreID:          v.StoreID,
		//			StoreName:        v.StoreName,
		//			SkuName:          v.SkuName,
		//			UnitPrice:        v.UnitPrice,
		//			PayPercentage:    v.PayPercentage,
		//			RealMidUnitPrice: v.RealMidUnitPrice,
		//			YbSkuName:        v.YbSkuName,
		//			AuditUnitPrice:   v.AuditUnitPrice,
		//		}
		//		storeNameMap[index] = storeName
		//		skuNamesInfo.SkuNames = append(skuNamesInfo.SkuNames, storeName)
		//	} else {
		//		storeName = storeNameMap[index]
		//	}
		//	storeName.Skus = append(storeName.Skus, &v.StoreSkuExt)
		//}
		//if err == nil {
		//	if isFocus {
		//		if err == nil {
		//			storeIDs, skuIDs := GetStoreAndSkuIDsFromInfo(skuNamesInfo)
		//			err = dao.UpdateActPrice4StoreSkuNameNew(db, storeIDs, skuIDs, skuNamesInfo, -1)
		//		}
		//	} else {
		//		err = updateUnitPrice4StoreSkuNameNew(db, skuNamesInfo)
		//	}
		//}
	}
	return skuNamesInfo, err
}

func GetStoreSkus(ctx *jxcontext.Context, storeID int, skuIDs []int, isFocus bool, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) {
	return GetStoresSkus(ctx, []int{storeID}, skuIDs, nil, isFocus, false, 0, keyword, isBySku, isAct, params, offset, pageSize)
}

func GetStoreSkus2(ctx *jxcontext.Context, storeID int, skuIDs []int, isFocus bool, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) {
	return GetStoresSkus2(ctx, []int{storeID}, skuIDs, nil, isFocus, false, 0, keyword, isBySku, isAct, params, offset, pageSize)
}

func getGetStoresSkusBaseSQL(db *dao.DaoDB, storeIDs, skuIDs []int, upcs []string, isFocus bool, keyword string, isBySku, isAct, isHighPrice bool, priceType int, actVendorID int, params map[string]interface{}) (sql string, sqlParams []interface{}, err error) {
	// 查看指定商店,对应的商品数据和商品价格等参数说明
	sql = `
	FROM sku_name t1
	JOIN sku t2 FORCE INDEX(PRIMARY) ON t1.id = t2.name_id AND t2.deleted_at = ?/* AND t2.status = ?*/
	JOIN store t3 ON t3.deleted_at = ?
	LEFT JOIN store_map sm ON sm.store_id = t3.id AND sm.vendor_id = ? AND sm.deleted_at = ?
	LEFT JOIN store_map smm ON smm.store_id = t3.id AND smm.deleted_at = ? AND smm.vendor_id = ?
	LEFT JOIN thing_map t2m ON t2m.thing_type = ? AND t2m.thing_id = t2.id AND t2m.vendor_id = sm.vendor_id AND t2m.vendor_org_code = sm.vendor_org_code AND t2m.deleted_at = ?
	`
	sqlParams = []interface{}{
		utils.DefaultTimeValue,
		// model.SkuStatusNormal,
		utils.DefaultTimeValue,
		model.VendorIDJD, utils.DefaultTimeValue, // TODO 这里直接用JD有问题
		utils.DefaultTimeValue, model.VendorIDYB,
		model.ThingTypeSku, utils.DefaultTimeValue,
	}
	if isFocus {
		if isAct { // 查看活动
			sql += `
			JOIN (
				SELECT t2.store_id, t2.sku_id,
				MIN(IF(t3.actual_act_price <= 0, NULL, t3.actual_act_price)) actual_act_price, /*non-zero min value*/
				MIN(IF(t2.earning_price <= 0, NULL, t2.earning_price)) earning_price /*non-zero min value*/
				FROM act t1
				JOIN act_store_sku t2 ON t2.act_id = t1.id AND t2.deleted_at = ?
				JOIN act_store_sku_map t3 ON t3.bind_id = t2.id AND t3.act_id = t1.id AND (t3.sync_status & ? = 0 OR t1.type = ?)
				JOIN act_map t4 ON t4.act_id = t1.id AND t4.vendor_id = t3.vendor_id AND t4.deleted_at = ? AND (t4.sync_status & ? = 0 OR t1.type = ?)
				WHERE t1.deleted_at = ? AND t1.status = ? AND NOT (t1.begin_at > ? OR t1.end_at < ?)`
			sqlParams = append(sqlParams, []interface{}{
				utils.DefaultTimeValue,
				model.SyncFlagNewMask,
				model.ActSkuFake,
				utils.DefaultTimeValue,
				model.SyncFlagNewMask,
				model.ActSkuFake,
				utils.DefaultTimeValue,
				model.ActStatusCreated,
				time.Now(),
				time.Now(),
			})
			if actVendorID >= 0 {
				sql += " AND t1.vendor_mask & ? <> 0"
				sqlParams = append(sqlParams, model.GetVendorMask(actVendorID))
			}
			if len(storeIDs) > 0 {
				sql += " AND t2.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
				sqlParams = append(sqlParams, storeIDs)
			}
			if len(skuIDs) > 0 {
				sql += " AND t2.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
				sqlParams = append(sqlParams, skuIDs)
			}
			sql += `
				GROUP BY 1, 2
				UNION 
				SELECT a.store_id, a.sku_id, 0 actual_act_price, 0 earning_price
				  FROM act_mtwm_vendor a
				 WHERE 1 = 1
			`
			if len(storeIDs) > 0 {
				sql += " AND a.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
				sqlParams = append(sqlParams, storeIDs)
			}
			if len(skuIDs) > 0 {
				sql += " AND a.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
				sqlParams = append(sqlParams, skuIDs)
			}
			if actVendorID >= 0 && actVendorID != model.VendorIDMTWM {
				sql += " AND 1 = 2"
			}
			sql += ` GROUP BY 1, 2, 3
				UNION 
				SELECT a.store_id, a.sku_id, 0 actual_act_price, 0 earning_price
				  FROM act_ebai_vendor_sku a
				 WHERE 1 = 1
			`
			if len(storeIDs) > 0 {
				sql += " AND a.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
				sqlParams = append(sqlParams, storeIDs)
			}
			if len(skuIDs) > 0 {
				sql += " AND a.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
				sqlParams = append(sqlParams, skuIDs)
			}
			if actVendorID >= 0 && actVendorID != model.VendorIDEBAI {
				sql += " AND 1 = 2"
			}
			sql += ` GROUP BY 1, 2, 3	
			) ta ON ta.store_id = t3.id AND ta.sku_id = t2.id`
		}
	} else {
		sql += " LEFT"
	}
	sql += `
	JOIN store_sku_bind t4 ON t4.store_id = t3.id AND t4.sku_id = t2.id AND t4.deleted_at = ?
	LEFT JOIN sku_name_place_bind t5 ON t1.id = t5.name_id AND t3.city_code = t5.place_code
	LEFT JOIN price_refer_snapshot t6 ON t6.city_code = ? AND t6.sku_id = t2.id AND t6.snapshot_at = ?
	LEFT JOIN store_sku_audit t7 ON t7.store_id = t3.id AND t7.name_id = t1.id AND t7.status = ? AND t7.deleted_at = ?
	WHERE t1.deleted_at = ? AND (t1.is_global = 1 OR t5.id IS NOT NULL OR 1 = ?)/* AND t1.status = ?*/
	`
	sqlParams = append(sqlParams, []interface{}{
		utils.DefaultTimeValue,
		0, utils.Time2Date(time.Now().AddDate(0, 0, -1)),
		model.StoreAuditStatusOnline, utils.DefaultTimeValue,
		utils.DefaultTimeValue,
		utils.Bool2Int(isFocus),
	})
	if isHighPrice || priceType == 1 {
		sql += " AND t4.unit_price > t6.mid_unit_price / IF(t3.pay_percentage < 50 , 70, t3.pay_percentage) * 1.2 * 100"
	}
	if priceType == -1 {
		sql += " AND t4.unit_price < t6.mid_unit_price / IF(t3.pay_percentage < 50 , 70, t3.pay_percentage) / 1.2 * 100"
	}
	if isFocus {
		sql += " 					  AND ((t2.status = ? AND t1.status = ?) OR t4.status = ?)"
		sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal, model.SkuStatusNormal)
	} else {
		sql += " AND t4.sku_id IS NULL AND (t2.status = ? AND t1.status = ?)"
		sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal)
	}
	if keyword != "" {
		keywordLike := "%" + keyword + "%"
		sql += " AND (t1.name LIKE ? OR t1.prefix LIKE ? OR t1.upc LIKE ? OR t2.comment LIKE ? OR t4.location_code LIKE ? "
		sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike)

		if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
			sql += `
			OR t1.id = ? OR t2.id = ?
			OR (SELECT COUNT(*) FROM thing_map tm WHERE tm.vendor_org_code = sm.vendor_org_code AND tm.thing_type = ? AND tm.thing_id = t2.id AND tm.deleted_at = ? AND tm.vendor_thing_id = ?) > 0
			`
			sqlParams = append(sqlParams,
				keywordInt64, keywordInt64,
				model.ThingTypeSku, utils.DefaultTimeValue, keywordInt64)
			if isFocus {
				sql += " OR t4.ebai_id = ? OR t4.mtwm_id = ? OR t4.dd_id = ? OR t4.tao_id = ?"
				sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64, keywordInt64)
			}
		}
		sql += ")"
	}

	if params["nameID"] != nil {
		sql += " AND t1.id = ?"
		sqlParams = append(sqlParams, params["nameID"].(int))
	}
	if params["nameIDs"] != nil {
		var nameIDs []int
		if err = utils.UnmarshalUseNumber([]byte(params["nameIDs"].(string)), &nameIDs); err != nil {
			return "", nil, err
		}
		if len(nameIDs) > 0 {
			sql += " AND t1.id IN (" + dao.GenQuestionMarks(len(nameIDs)) + ")"
			sqlParams = append(sqlParams, nameIDs)
		}
	}
	if params["categoryID"] != nil {
		cat := &model.SkuCategory{}
		cat.ID = params["categoryID"].(int)
		if err = dao.GetEntity(db, cat); err != nil {
			return "", nil, err
		}
		sql += " AND (t1.category_id = ?"
		sqlParams = append(sqlParams, cat.ID)
		if cat.Level == 1 {
			sql += " OR t1.category_id IN (SELECT id FROM sku_category WHERE parent_id = ?)"
			sqlParams = append(sqlParams, cat.ID)
		}
		sql += ")"
	}
	if params["name"] != nil {
		sql += " AND t1.name LIKE ?"
		sqlParams = append(sqlParams, "%"+params["name"].(string)+"%")
	}
	if params["prefix"] != nil {
		sql += " AND t1.prefix LIKE ?"
		sqlParams = append(sqlParams, "%"+params["prefix"].(string)+"%")
	}
	if params["unit"] != nil {
		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(storeIDs) == 1 {
			sql += " AND IF(INSTR(t3.name,'" + model.ExdStoreName + "') > 0, t2.exd_sku_id <> '', t2.exd_sku_id = '')"
		}
	}
	if len(upcs) > 0 {
		sql += " AND t1.upc IN (" + dao.GenQuestionMarks(len(upcs)) + ")"
		sqlParams = append(sqlParams, upcs)
	}
	if len(skuIDs) > 0 {
		sql += " AND t2.id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
		sqlParams = append(sqlParams, skuIDs)
	} else if params["skuID"] != nil {
		skuID, ok := params["skuID"].(int)
		if ok {
			skuIDs = append(skuIDs, skuID)
			sql += " AND t2.id = ?"
			sqlParams = append(sqlParams, skuID)
		}
	}
	if lockTimeStr, ok := params["lockTime"].(string); ok && lockTimeStr != "" {
		if timeList, err2 := jxutils.BatchStr2Time(lockTimeStr); err2 == nil {
			sql += " AND (t4.jd_lock_time > ? OR t4.mtwm_lock_time > ? OR t4.dd_lock_time > ? OR t4.ebai_lock_time > ? OR t4.jx_lock_time > ?)"
			sqlParams = append(sqlParams, timeList[0], timeList[0], timeList[0], timeList[0], timeList[0])
		}
	}
	if isFocus {
		if params["fromStatus"] != nil {
			fromStatus := params["fromStatus"].(int)
			toStatus := fromStatus
			if params["toStatus"] != nil {
				toStatus = params["toStatus"].(int)
			}
			sql += " AND t4.status >= ? AND t4.status <= ?"
			sqlParams = append(sqlParams, fromStatus, toStatus)
		}
		if params["jdSyncStatus"] != nil || params["ebaiSyncStatus"] != nil || params["mtwmSyncStatus"] != nil || params["taoSyncStatus"] != nil {
			realVendorMap, err2 := getValidStoreVendorMap(db, storeIDs)
			if err = err2; err != nil {
				return "", nil, err
			}
			sql += " AND ( 1 = 0"
			if params["jdSyncStatus"] != nil && realVendorMap[model.VendorIDJD] == 1 {
				sql += " OR (t4.jd_sync_status & ? <> 0 AND t1.status = ? AND t2.status = ?)"
				sqlParams = append(sqlParams, params["jdSyncStatus"], model.SkuStatusNormal, model.SkuStatusNormal)
			}
			if params["ebaiSyncStatus"] != nil && realVendorMap[model.VendorIDEBAI] == 1 {
				sql += " OR (t4.ebai_sync_status & ? <> 0 AND NOT (t4.ebai_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )"
				sqlParams = append(sqlParams, params["ebaiSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			if params["ddSyncStatus"] != nil && realVendorMap[model.VendorIDDD] == 1 {
				sql += " OR (t4.dd_sync_status & ? <> 0 AND NOT (t4.dd_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )"
				sqlParams = append(sqlParams, params["ddSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			if params["mtwmSyncStatus"] != nil && realVendorMap[model.VendorIDMTWM] == 1 {
				sql += " OR (t4.mtwm_sync_status & ? <> 0 AND NOT (t4.mtwm_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )"
				sqlParams = append(sqlParams, params["mtwmSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			if params["taoSyncStatus"] != nil && realVendorMap[model.VendorIDTaoVegetable] == 1 {
				sql += " OR (t4.tao_sync_status & ? <> 0 AND NOT (t4.tao_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )"
				sqlParams = append(sqlParams, params["taoSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			sql += ")"
		}
	}
	if isFocus {
		/*前台传入的最大值和最小值设置*/
		if params["highestPrice"] != "" && params["highestPrice"] != nil {
			//highestPrice := utils.Interface2Float64WithDefault(params["highestPrice"], 0) * 100
			sql += " AND t4.unit_price <= ? "
			sqlParams = append(sqlParams, params["highestPrice"])
		}
		if params["minimumPrice"] != "" && params["minimumPrice"] != nil {
			//minimumPrice := utils.Interface2Float64WithDefault(params["minimumPrice"], 0) * 100
			sql += " AND t4.unit_price >= ? "
			sqlParams = append(sqlParams, params["minimumPrice"])
		}
	}
	if params["minStock"] != nil {
		sql += " AND t4.stock >= ? "
		sqlParams = append(sqlParams, params["minStock"])
	}
	if params["maxStock"] != nil {
		sql += " AND t4.stock <= ? "
		sqlParams = append(sqlParams, params["maxStock"])
	}
	if params["isLocationCode"] != "" && params["isLocationCode"] != nil {
		sql += " AND (t4.location_code = '' OR t4.location_code = 'EMPTY_VALUE')  "
	}
	return sql, sqlParams, err
}

func getGetStoresSkusBaseSQL2(db *dao.DaoDB, storeIDs, skuIDs []int, upcs []string, isFocus bool, keyword string, isBySku, isAct, isHighPrice bool, priceType int, actVendorID int, params map[string]interface{}) (sql string, sqlParams []interface{}, err error) {
	// 查看指定商店,对应的商品数据和商品价格等参数说明
	sql = `
	FROM sku_name t1
	JOIN sku t2 FORCE INDEX(PRIMARY) ON t1.id = t2.name_id AND t2.deleted_at = ?/* AND t2.status = ?*/
	JOIN store t3 ON t3.deleted_at = ?
	LEFT JOIN store_map sm ON sm.store_id = t3.id AND sm.vendor_id = ? AND sm.deleted_at = ?
	LEFT JOIN store_map smm ON smm.store_id = t3.id AND smm.deleted_at = ? AND smm.vendor_id = ?
	LEFT JOIN thing_map t2m ON t2m.thing_type = ? AND t2m.thing_id = t2.id AND t2m.vendor_id = sm.vendor_id AND t2m.vendor_org_code = sm.vendor_org_code AND t2m.deleted_at = ?
    LEFT JOIN sku_category t8 ON t1.category_id= t8.id`
	sqlParams = []interface{}{
		utils.DefaultTimeValue,
		// model.SkuStatusNormal,
		utils.DefaultTimeValue,
		model.VendorIDJD, utils.DefaultTimeValue, // TODO 这里直接用JD有问题
		utils.DefaultTimeValue, model.VendorIDYB,
		model.ThingTypeSku, utils.DefaultTimeValue,
	}
	if isFocus {
		if isAct { // 查看活动
			sql += `
			JOIN (
				SELECT t2.store_id, t2.sku_id,
				MIN(IF(t3.actual_act_price <= 0, NULL, t3.actual_act_price)) actual_act_price, /*non-zero min value*/
				MIN(IF(t2.earning_price <= 0, NULL, t2.earning_price)) earning_price /*non-zero min value*/
				FROM act t1
				JOIN act_store_sku t2 ON t2.act_id = t1.id AND t2.deleted_at = ?
				JOIN act_store_sku_map t3 ON t3.bind_id = t2.id AND t3.act_id = t1.id AND (t3.sync_status & ? = 0 OR t1.type = ?)
				JOIN act_map t4 ON t4.act_id = t1.id AND t4.vendor_id = t3.vendor_id AND t4.deleted_at = ? AND (t4.sync_status & ? = 0 OR t1.type = ?)
				WHERE t1.deleted_at = ? AND t1.status = ? AND NOT (t1.begin_at > ? OR t1.end_at < ?)`
			sqlParams = append(sqlParams, []interface{}{
				utils.DefaultTimeValue,
				model.SyncFlagNewMask,
				model.ActSkuFake,
				utils.DefaultTimeValue,
				model.SyncFlagNewMask,
				model.ActSkuFake,
				utils.DefaultTimeValue,
				model.ActStatusCreated,
				time.Now(),
				time.Now(),
			})
			if actVendorID >= 0 {
				sql += " AND t1.vendor_mask & ? <> 0"
				sqlParams = append(sqlParams, model.GetVendorMask(actVendorID))
			}
			if len(storeIDs) > 0 {
				sql += " AND t2.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
				sqlParams = append(sqlParams, storeIDs)
			}
			if len(skuIDs) > 0 {
				sql += " AND t2.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
				sqlParams = append(sqlParams, skuIDs)
			}
			sql += `
				GROUP BY 1, 2
				UNION 
				SELECT a.store_id, a.sku_id, 0 actual_act_price, 0 earning_price
				  FROM act_mtwm_vendor a
				 WHERE 1 = 1
			`
			if len(storeIDs) > 0 {
				sql += " AND a.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
				sqlParams = append(sqlParams, storeIDs)
			}
			if len(skuIDs) > 0 {
				sql += " AND a.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
				sqlParams = append(sqlParams, skuIDs)
			}
			if actVendorID >= 0 && actVendorID != model.VendorIDMTWM {
				sql += " AND 1 = 2"
			}
			sql += ` GROUP BY 1, 2, 3
				UNION 
				SELECT a.store_id, a.sku_id, 0 actual_act_price, 0 earning_price
				  FROM act_ebai_vendor_sku a
				 WHERE 1 = 1
			`
			if len(storeIDs) > 0 {
				sql += " AND a.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
				sqlParams = append(sqlParams, storeIDs)
			}
			if len(skuIDs) > 0 {
				sql += " AND a.sku_id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
				sqlParams = append(sqlParams, skuIDs)
			}
			if actVendorID >= 0 && actVendorID != model.VendorIDEBAI {
				sql += " AND 1 = 2"
			}
			sql += ` GROUP BY 1, 2, 3	
			) ta ON ta.store_id = t3.id AND ta.sku_id = t2.id`
		}
	} else {
		sql += " LEFT"
	}
	sql += `
	JOIN store_sku_bind t4 ON t4.store_id = t3.id AND t4.sku_id = t2.id AND t4.deleted_at = ?
	LEFT JOIN sku_name_place_bind t5 ON t1.id = t5.name_id AND t3.city_code = t5.place_code
	LEFT JOIN price_refer_snapshot t6 ON t6.city_code = ? AND t6.sku_id = t2.id AND t6.snapshot_at = ?
	LEFT JOIN store_sku_audit t7 ON t7.store_id = t3.id AND t7.name_id = t1.id AND t7.status = ? AND t7.deleted_at = ?
	WHERE t1.deleted_at = ? AND (t1.is_global = 1 OR t5.id IS NOT NULL OR 1 = ?)/* AND t1.status = ?*/
	`
	sqlParams = append(sqlParams, []interface{}{
		utils.DefaultTimeValue,
		0, utils.Time2Date(time.Now().AddDate(0, 0, -1)),
		model.StoreAuditStatusOnline, utils.DefaultTimeValue,
		utils.DefaultTimeValue,
		utils.Bool2Int(isFocus),
	})
	if isHighPrice || priceType == 1 {
		sql += " AND t4.unit_price > t6.mid_unit_price / IF(t3.pay_percentage < 50 , 70, t3.pay_percentage) * 1.2 * 100"
	}
	if priceType == -1 {
		sql += " AND t4.unit_price < t6.mid_unit_price / IF(t3.pay_percentage < 50 , 70, t3.pay_percentage) / 1.2 * 100"
	}
	if isFocus {
		sql += " 					  AND ((t2.status = ? AND t1.status = ?) OR t4.status = ?)"
		sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal, model.SkuStatusNormal)
	} else {
		sql += " AND t4.sku_id IS NULL AND (t2.status = ? AND t1.status = ?)"
		sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal)
	}
	if keyword != "" {
		keywordLike := "%" + keyword + "%"
		sql += " AND (t1.name LIKE ? OR t1.prefix LIKE ? OR t1.upc LIKE ? OR t2.comment LIKE ?"
		sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike)

		if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
			sql += `
			OR t1.id = ? OR t2.id = ?
			OR (SELECT COUNT(*) FROM thing_map tm WHERE tm.vendor_org_code = sm.vendor_org_code AND tm.thing_type = ? AND tm.thing_id = t2.id AND tm.deleted_at = ? AND tm.vendor_thing_id = ?) > 0
			`
			sqlParams = append(sqlParams,
				keywordInt64, keywordInt64,
				model.ThingTypeSku, utils.DefaultTimeValue, keywordInt64)
			if isFocus {
				sql += " OR t4.ebai_id = ? OR t4.mtwm_id = ? OR t4.dd_id = ? OR t4.tao_id = ?"
				sqlParams = append(sqlParams, keywordInt64, keywordInt64, keywordInt64, keywordInt64)
			}
		}
		sql += ")"
	}

	if params["nameID"] != nil {
		sql += " AND t1.id = ?"
		sqlParams = append(sqlParams, params["nameID"].(int))
	}
	if params["nameIDs"] != nil {
		var nameIDs []int
		if err = utils.UnmarshalUseNumber([]byte(params["nameIDs"].(string)), &nameIDs); err != nil {
			return "", nil, err
		}
		if len(nameIDs) > 0 {
			sql += " AND t1.id IN (" + dao.GenQuestionMarks(len(nameIDs)) + ")"
			sqlParams = append(sqlParams, nameIDs)
		}
	}
	if params["categoryID"] != nil {
		cat := &model.SkuCategory{}
		cat.ID = params["categoryID"].(int)
		if err = dao.GetEntity(db, cat); err != nil {
			return "", nil, err
		}
		sql += " AND (t1.category_id = ?"
		sqlParams = append(sqlParams, cat.ID)
		if cat.Level == 1 {
			sql += " OR t1.category_id IN (SELECT id FROM sku_category WHERE parent_id = ?)"
			sqlParams = append(sqlParams, cat.ID)
		}
		sql += ")"
	}
	if params["name"] != nil {
		sql += " AND t1.name LIKE ?"
		sqlParams = append(sqlParams, "%"+params["name"].(string)+"%")
	}
	if params["prefix"] != nil {
		sql += " AND t1.prefix LIKE ?"
		sqlParams = append(sqlParams, "%"+params["prefix"].(string)+"%")
	}
	if params["unit"] != nil {
		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(storeIDs) == 1 {
			sql += " AND IF(INSTR(t3.name,'" + model.ExdStoreName + "') > 0, t2.exd_sku_id <> '', t2.exd_sku_id = '')"
		}
	}
	if len(upcs) > 0 {
		sql += " AND t1.upc IN (" + dao.GenQuestionMarks(len(upcs)) + ")"
		sqlParams = append(sqlParams, upcs)
	}
	if len(skuIDs) > 0 {
		sql += " AND t2.id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
		sqlParams = append(sqlParams, skuIDs)
	} else if params["skuID"] != nil {
		skuID, ok := params["skuID"].(int)
		if ok {
			skuIDs = append(skuIDs, skuID)
			sql += " AND t2.id = ?"
			sqlParams = append(sqlParams, skuID)
		}
	}
	if lockTimeStr, ok := params["lockTime"].(string); ok && lockTimeStr != "" {
		if timeList, err2 := jxutils.BatchStr2Time(lockTimeStr); err2 == nil {
			sql += " AND (t4.jd_lock_time > ? OR t4.mtwm_lock_time > ? OR t4.mtwm_lock_time > ? OR t4.ebai_lock_time > ? OR t4.jx_lock_time > ?)"
			sqlParams = append(sqlParams, timeList[0], timeList[0], timeList[0], timeList[0], timeList[0])
		}
	}
	if isFocus {
		if params["fromStatus"] != nil {
			fromStatus := params["fromStatus"].(int)
			toStatus := fromStatus
			if params["toStatus"] != nil {
				toStatus = params["toStatus"].(int)
			}
			sql += " AND t4.status >= ? AND t4.status <= ?"
			sqlParams = append(sqlParams, fromStatus, toStatus)
		}
		if params["jdSyncStatus"] != nil || params["ebaiSyncStatus"] != nil || params["mtwmSyncStatus"] != nil || params["taoSyncStatus"] != nil {
			realVendorMap, err2 := getValidStoreVendorMap(db, storeIDs)
			if err = err2; err != nil {
				return "", nil, err
			}
			sql += " AND ( 1 = 0"
			if params["jdSyncStatus"] != nil && realVendorMap[model.VendorIDJD] == 1 {
				sql += " OR (t4.jd_sync_status & ? <> 0 AND t1.status = ? AND t2.status = ?)"
				sqlParams = append(sqlParams, params["jdSyncStatus"], model.SkuStatusNormal, model.SkuStatusNormal)
			}
			if params["ebaiSyncStatus"] != nil && realVendorMap[model.VendorIDEBAI] == 1 {
				sql += " OR (t4.ebai_sync_status & ? <> 0 AND NOT (t4.ebai_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )"
				sqlParams = append(sqlParams, params["ebaiSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			if params["mtwmSyncStatus"] != nil && realVendorMap[model.VendorIDMTWM] == 1 {
				sql += " OR (t4.mtwm_sync_status & ? <> 0 AND NOT (t4.mtwm_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )"
				sqlParams = append(sqlParams, params["mtwmSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			if params["ddSyncStatus"] != nil && realVendorMap[model.VendorIDDD] == 1 {
				sql += " OR (t4.dd_sync_status & ? <> 0 AND NOT (t4.dd_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )"
				sqlParams = append(sqlParams, params["ddSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			if params["taoSyncStatus"] != nil && realVendorMap[model.VendorIDTaoVegetable] == 1 {
				sql += " OR (t4.tao_sync_status & ? <> 0 AND NOT (t4.tao_sync_status & ? <> 0 AND (t4.status <> ? OR t2.status <> ?)) )"
				sqlParams = append(sqlParams, params["taoSyncStatus"], model.SyncFlagNewMask, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			sql += ")"
		}
	}
	if isFocus {
		/*前台传入的最大值和最小值设置*/
		if params["highestPrice"] != "" && params["highestPrice"] != nil {
			//highestPrice := utils.Interface2Float64WithDefault(params["highestPrice"], 0) * 100
			sql += " AND t4.unit_price <= ? "
			sqlParams = append(sqlParams, params["highestPrice"])
		}
		if params["minimumPrice"] != "" && params["minimumPrice"] != nil {
			//minimumPrice := utils.Interface2Float64WithDefault(params["minimumPrice"], 0) * 100
			sql += " AND t4.unit_price >= ? "
			sqlParams = append(sqlParams, params["minimumPrice"])
		}
	}
	return sql, sqlParams, err
}

func GetStoresSkus(ctx *jxcontext.Context, storeIDs, skuIDs []int, upcs []string, isFocus, isHighPrice bool, priceType int, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) {
	// 获取门店品牌如果门店不是b2b品牌则不展示b2b商品

	// 管理员进入
	//if /*permission.IsRoled(ctx)*/ true {
	//	// 获取管理员绑定门店
	//	if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
	//		var storeIDs2 []int
	//		if len(storeIDs) > 0 {
	//			for _, v := range storeIDs {
	//				if storeIDsMap[v] != 0 {
	//					storeIDs2 = append(storeIDs2, v)
	//				}
	//			}
	//			if len(storeIDs2) == 0 {
	//				storeIDs2 = append(storeIDs2, -1)
	//			}
	//		} else {
	//			for k, _ := range storeIDsMap {
	//				storeIDs2 = append(storeIDs2, k)
	//			}
	//		}
	//		storeIDs = nil
	//		storeIDs = storeIDs2
	//	}
	//}
	return GetStoresSkusNew(ctx, storeIDs, skuIDs, upcs, isFocus, isHighPrice, priceType, keyword, isBySku, isAct, params, offset, pageSize)
}

func GetStoresSkus2(ctx *jxcontext.Context, storeIDs, skuIDs []int, upcs []string, isFocus, isHighPrice bool, priceType int, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) {
	return GetStoresSkusNew2(ctx, storeIDs, skuIDs, upcs, isFocus, isHighPrice, priceType, keyword, isBySku, isAct, params, offset, pageSize)
}

func GetStoresSkusNew(ctx *jxcontext.Context, storeIDs, skuIDs []int, upcs []string, isFocus, isHighPrice bool, priceType int, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) {
	if !isFocus && !isBySku && (len(storeIDs) > 1 || len(storeIDs) == 0) {
		return nil, fmt.Errorf("未关注按SkuName只能查询单店")
	}
	if len(storeIDs) == 0 && len(skuIDs) == 0 && pageSize == -1 {
		return nil, fmt.Errorf("GetStoresSkus必须指定storeIDs或skuIDs或分页")
	}
	actVendorID := -1
	if params["actVendorID"] != nil {
		actVendorID = int(utils.Interface2Int64WithDefault(params["actVendorID"], -1))
	}
	var highestPrice, minimumPrice float64
	if params["highestPrice"] != nil {
		if highestPrice, err = strconv.ParseFloat(params["highestPrice"].(string), 64); err != nil {
			delete(params, "highestPrice")
		} else {
			params["highestPrice"] = highestPrice * 100
		}
	}
	if params["minimumPrice"] != nil {
		if minimumPrice, err = strconv.ParseFloat(params["minimumPrice"].(string), 64); err != nil {
			delete(params, "minimumPrice")
		} else {
			params["minimumPrice"] = minimumPrice * 100
		}
	}
	if !(highestPrice > 0 && highestPrice > minimumPrice) || !(highestPrice > 0) {
		delete(params, "highestPrice")
	}
	if !(minimumPrice >= 0 && highestPrice > 0 && highestPrice > minimumPrice) || !(minimumPrice >= 0) {
		delete(params, "minimumPrice")
	}
	db := dao.GetDB()
	sql, sqlParams, err := getGetStoresSkusBaseSQL(db, storeIDs, skuIDs, upcs, isFocus, keyword, isBySku, isAct, isHighPrice, priceType, actVendorID, params)
	if err != nil {
		return nil, err
	}
	pageSize = jxutils.FormalizePageSize(pageSize)
	offset = jxutils.FormalizePageOffset(offset)
	sqlOffset := offset
	sqlPageSize := pageSize
	isSaleInfo := params["stFromTime"] != nil
	if isSaleInfo {
		sqlOffset = 0
		sqlPageSize = jxutils.FormalizePageSize(-1)
	}
	sqlParamsPage := []interface{}{sqlPageSize, sqlOffset}

	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	catOrderBy := ""
	if params["categoryID"] != nil {
		catOrderBy = "t2.seq, "
	}

	skuNamesInfo = &dao.StoreSkuNamesInfo{}
	if !isBySku && sqlPageSize != model.UnlimitedPageSize {
		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
		if err = dao.GetRowsTx(txDB, &storeNameList, sql2, sqlParams2...); err != nil {
			dao.Rollback(db, txDB)
			return nil, err
		}
		skuNamesInfo.TotalCount = dao.GetLastTotalRowCount2(db, txDB)
		sql += " AND (1 = 0"
		for _, v := range storeNameList {
			sql += " OR (t1.id = ? AND t3.id = ?)"
			sqlParams = append(sqlParams, v.NameID, v.StoreID)
		}
		sql += fmt.Sprintf(`)
		ORDER BY %s t3.id, t2.name_id, t2.id`, catOrderBy)
	} else {
		if isFocus {
			sql += fmt.Sprintf(`
			ORDER BY %s t3.id, t2.name_id, t2.id`, catOrderBy)
		}
		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, t2m.vendor_thing_id sku_jd_id,t2m.remark jd_sku_remark,
	t2.comment, t2.category_id sku_category_id, t2.status sku_status, t2.eclp_id,
	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 store_sku_status, t4.auto_sale_at,
	t4.ebai_id, t4.mtwm_id, t4.tao_id,t4.dd_id, t4.yb_id, CONCAT(smm.yb_store_prefix,t1.yb_name_suffix) yb_sku_name, t4.jds_id, t4.jds_ware_id,
	t4.jd_sync_status, t4.ebai_sync_status, t4.mtwm_sync_status,t4.tao_sync_status , t4.dd_sync_status, t4.yb_sync_status, t4.jds_sync_status,
	t4.jd_price, t4.ebai_price, t4.mtwm_price,t4.tao_price, t4.jx_price, t4.dd_price, t4.yb_price, t4.jds_price,
	t4.jd_lock_time, t4.ebai_lock_time, t4.mtwm_lock_time,t4.tao_lock_time, t4.dd_lock_time, t4.jx_lock_time, t4.yb_lock_time, t4.jds_lock_time,
	t4.status_sale_begin, t4.status_sale_end, t4.stock, t4.mt_ladder_box_price,t4.location_code,
	t6.mid_unit_price real_mid_unit_price,
	t7.unit_price audit_unit_price
	` + sql
	if isHighPrice || priceType != 0 {
		sql += " , t4.unit_price DESC LIMIT 99"
	}
	var tmpList []*tGetStoresSkusInfo
	if err = dao.GetRowsTx(txDB, &tmpList, sql, sqlParams...); err != nil {
		dao.Rollback(db, txDB)
		return nil, err
	}
	if isBySku {
		skuNamesInfo.TotalCount = dao.GetLastTotalRowCount2(db, txDB)
	}
	dao.Commit(db, txDB)
	storeNameMap := make(map[int64]*dao.StoreSkuNameExt)
	for _, v := range tmpList {
		var storeName *dao.StoreSkuNameExt
		index := jxutils.Combine2Int(v.StoreID, v.ID)
		if isBySku || storeNameMap[index] == nil {
			storeName = &dao.StoreSkuNameExt{
				StoreID:          v.StoreID,
				StoreName:        v.StoreName,
				SkuName:          v.SkuName,
				UnitPrice:        v.UnitPrice,
				PayPercentage:    v.PayPercentage,
				RealMidUnitPrice: v.RealMidUnitPrice,
				YbSkuName:        v.YbSkuName,
				AuditUnitPrice:   v.AuditUnitPrice,
			}
			if !isBySku {
				storeNameMap[index] = storeName
			}
			skuNamesInfo.SkuNames = append(skuNamesInfo.SkuNames, storeName)
		} else {
			storeName = storeNameMap[index]
		}
		storeName.Skus = append(storeName.Skus, &v.StoreSkuExt)
	}
	if err == nil {
		if isFocus {
			if isSaleInfo {
				beginTime := time.Now()
				err = updateSaleInfo4StoreSkuName(ctx, db, storeIDs, skuIDs, params, skuNamesInfo, offset, pageSize)
				globals.SugarLogger.Debugf("GetStoresSkusNew updateSaleInfo4StoreSkuName:%v", time.Now().Sub(beginTime))
			}
			if err == nil {
				if true { //!(offset == 0 && pageSize == model.UnlimitedPageSize) {
					storeIDs, skuIDs = GetStoreAndSkuIDsFromInfo(skuNamesInfo)
				}
				beginTime := time.Now()
				err = dao.UpdateActPrice4StoreSkuNameNew(db, storeIDs, skuIDs, skuNamesInfo, actVendorID)
				globals.SugarLogger.Debugf("GetStoresSkusNew updateActPrice4StoreSkuName:%v", time.Now().Sub(beginTime))
			}
		} else {
			err = updateUnitPrice4StoreSkuNameNew(db, skuNamesInfo)
		}
	}
	return skuNamesInfo, err
}

func GetStoresSkusNew2(ctx *jxcontext.Context, storeIDs, skuIDs []int, upcs []string, isFocus, isHighPrice bool, priceType int, keyword string, isBySku, isAct bool, params map[string]interface{}, offset, pageSize int) (skuNamesInfo *dao.StoreSkuNamesInfo, err error) {
	if !isFocus && !isBySku && (len(storeIDs) > 1 || len(storeIDs) == 0) {
		return nil, fmt.Errorf("未关注按SkuName只能查询单店")
	}
	if len(storeIDs) == 0 && len(skuIDs) == 0 && pageSize == -1 {
		return nil, fmt.Errorf("GetStoresSkus必须指定storeIDs或skuIDs或分页")
	}
	actVendorID := -1
	if params["actVendorID"] != nil {
		actVendorID = int(utils.Interface2Int64WithDefault(params["actVendorID"], -1))
	}
	var highestPrice, minimumPrice float64
	if params["highestPrice"] != nil {
		if highestPrice, err = strconv.ParseFloat(params["highestPrice"].(string), 64); err != nil {
			delete(params, "highestPrice")
		} else {
			params["highestPrice"] = highestPrice * 100
		}
	}
	if params["minimumPrice"] != nil {
		if minimumPrice, err = strconv.ParseFloat(params["minimumPrice"].(string), 64); err != nil {
			delete(params, "minimumPrice")
		} else {
			params["minimumPrice"] = minimumPrice * 100
		}
	}
	if !(highestPrice > 0 && highestPrice > minimumPrice) || !(highestPrice > 0) {
		delete(params, "highestPrice")
	}
	if !(minimumPrice >= 0 && highestPrice > 0 && highestPrice > minimumPrice) || !(minimumPrice >= 0) {
		delete(params, "minimumPrice")
	}
	db := dao.GetDB()
	sql, sqlParams, err := getGetStoresSkusBaseSQL2(db, storeIDs, skuIDs, upcs, isFocus, keyword, isBySku, isAct, isHighPrice, priceType, actVendorID, params)
	if err != nil {
		return nil, err
	}
	pageSize = jxutils.FormalizePageSize(pageSize)
	offset = jxutils.FormalizePageOffset(offset)
	sqlOffset := offset
	sqlPageSize := pageSize
	isSaleInfo := params["stFromTime"] != nil
	if isSaleInfo {
		sqlOffset = 0
		sqlPageSize = jxutils.FormalizePageSize(-1)
	}
	sqlParamsPage := []interface{}{sqlPageSize, sqlOffset}

	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	catOrderBy := ""
	if params["categoryID"] != nil {
		catOrderBy = "t2.seq, "
	}

	skuNamesInfo = &dao.StoreSkuNamesInfo{}
	if !isBySku && sqlPageSize != model.UnlimitedPageSize {
		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
		if err = dao.GetRowsTx(txDB, &storeNameList, sql2, sqlParams2...); err != nil {
			dao.Rollback(db, txDB)
			return nil, err
		}
		skuNamesInfo.TotalCount = dao.GetLastTotalRowCount2(db, txDB)
		sql += " AND (1 = 0"
		for _, v := range storeNameList {
			sql += " OR (t1.id = ? AND t3.id = ?)"
			sqlParams = append(sqlParams, v.NameID, v.StoreID)
		}
		sql += fmt.Sprintf(`)
		ORDER BY %s t3.id, t2.name_id, t2.id`, catOrderBy)
	} else {
		if isFocus {
			sql += fmt.Sprintf(`
			ORDER BY %s t3.id, t2.name_id, t2.id`, catOrderBy)
		}
		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, t2m.vendor_thing_id sku_jd_id,
	t2.comment, t2.category_id sku_category_id, t2.status sku_status, t2.eclp_id,
	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 store_sku_status, t4.auto_sale_at,
	t4.ebai_id, t4.mtwm_id,t4.dd_id,t4.tao_id, t4.yb_id, CONCAT(smm.yb_store_prefix,t1.yb_name_suffix) yb_sku_name, t4.jds_id, t4.jds_ware_id,
	t4.jd_sync_status, t4.ebai_sync_status, t4.mtwm_sync_status,  t4.tao_sync_status, t4.dd_sync_status, t4.yb_sync_status, t4.jds_sync_status,
	t4.jd_price, t4.ebai_price, t4.mtwm_price, t4.tao_price, t4.dd_price, t4.jx_price, t4.yb_price, t4.jds_price,
	t4.jd_lock_time, t4.ebai_lock_time, t4.mtwm_lock_time,t4.tao_lock_time, t4.dd_lock_time, t4.jx_lock_time, t4.yb_lock_time, t4.jds_lock_time,
	t4.status_sale_begin, t4.status_sale_end, t4.stock, t4.mt_ladder_box_price,
	t6.mid_unit_price real_mid_unit_price,
	t7.unit_price audit_unit_price,t8.name categoryName
	` + sql
	if isHighPrice || priceType != 0 {
		sql += " , t4.unit_price DESC LIMIT 99"
	}
	var tmpList []*tGetStoresSkusInfo
	if err = dao.GetRowsTx(txDB, &tmpList, sql, sqlParams...); err != nil {
		dao.Rollback(db, txDB)
		return nil, err
	}
	if isBySku {
		skuNamesInfo.TotalCount = dao.GetLastTotalRowCount2(db, txDB)
	}
	dao.Commit(db, txDB)
	storeNameMap := make(map[int64]*dao.StoreSkuNameExt)
	for _, v := range tmpList {
		var storeName *dao.StoreSkuNameExt
		index := jxutils.Combine2Int(v.StoreID, v.ID)
		if isBySku || storeNameMap[index] == nil {
			storeName = &dao.StoreSkuNameExt{
				StoreID:          v.StoreID,
				StoreName:        v.StoreName,
				SkuName:          v.SkuName,
				UnitPrice:        v.UnitPrice,
				PayPercentage:    v.PayPercentage,
				RealMidUnitPrice: v.RealMidUnitPrice,
				YbSkuName:        v.YbSkuName,
				AuditUnitPrice:   v.AuditUnitPrice,
			}
			if !isBySku {
				storeNameMap[index] = storeName
			}
			skuNamesInfo.SkuNames = append(skuNamesInfo.SkuNames, storeName)
		} else {
			storeName = storeNameMap[index]
		}
		storeName.Skus = append(storeName.Skus, &v.StoreSkuExt)
	}
	if err == nil {
		if isFocus {
			if isSaleInfo {
				beginTime := time.Now()
				err = updateSaleInfo4StoreSkuName(ctx, db, storeIDs, skuIDs, params, skuNamesInfo, offset, pageSize)
				globals.SugarLogger.Debugf("GetStoresSkusNew updateSaleInfo4StoreSkuName:%v", time.Now().Sub(beginTime))
			}
			if err == nil {
				if true { //!(offset == 0 && pageSize == model.UnlimitedPageSize) {
					storeIDs, skuIDs = GetStoreAndSkuIDsFromInfo(skuNamesInfo)
				}
				beginTime := time.Now()
				err = dao.UpdateActPrice4StoreSkuNameNew(db, storeIDs, skuIDs, skuNamesInfo, actVendorID)
				globals.SugarLogger.Debugf("GetStoresSkusNew updateActPrice4StoreSkuName:%v", time.Now().Sub(beginTime))
			}
		} else {
			err = updateUnitPrice4StoreSkuNameNew(db, skuNamesInfo)
		}
	}
	return skuNamesInfo, err
}

func GetStoreAndSkuIDsFromInfo(skuNamesInfo *dao.StoreSkuNamesInfo) (storeIDs, skuIDs []int) {
	storeIDMap := make(map[int]int)
	skuIDMap := make(map[int]int)
	for _, skuName := range skuNamesInfo.SkuNames {
		storeIDMap[skuName.StoreID] = 1
		for _, sku := range skuName.Skus {
			skuIDMap[sku.SkuID] = 1
		}
	}
	return jxutils.IntMap2List(storeIDMap), jxutils.IntMap2List(skuIDMap)
}

// 根据已经部分关注的商品，得到已经存在的门店商品单价
func updateUnitPrice4StoreSkuNameNew(db *dao.DaoDB, skuNamesInfo *dao.StoreSkuNamesInfo) (err error) {
	storeIDMap := make(map[int]int)
	skuNameIDMap := make(map[int]int)
	for _, skuName := range skuNamesInfo.SkuNames {
		storeIDMap[skuName.StoreID] = 1
		skuNameIDMap[skuName.SkuName.ID] = 1
	}
	storeSkuNameInfo, err := dao.GetExistingStoreSkuNameInfo(db, jxutils.IntMap2List(storeIDMap), jxutils.IntMap2List(skuNameIDMap))
	if err == nil {
		infoMap := make(map[int64]*dao.StoreSkuNameInfo)
		for _, v := range storeSkuNameInfo {
			infoMap[jxutils.Combine2Int(v.StoreID, v.NameID)] = v
		}
		for _, skuName := range skuNamesInfo.SkuNames {
			if tmpInfo := infoMap[jxutils.Combine2Int(skuName.StoreID, skuName.SkuName.ID)]; tmpInfo != nil {
				skuName.UnitPrice = int(tmpInfo.UnitPrice)
			}
		}
	}
	return err
}

func updateSaleInfo4StoreSkuName(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs, skuIDs []int, params map[string]interface{}, skuNamesInfo *dao.StoreSkuNamesInfo, offset, pageSize int) (err error) {
	var (
		saleInfoList       []*SkuSaleInfo
		timeList           []time.Time
		fromCount, toCount int
	)
	saleInfoMap := make(map[int64]*SkuSaleInfo)

	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 []*dao.StoreSkuNameExt
	for _, skuName := range skuNamesInfo.SkuNames {
		var newSkus []*dao.StoreSkuExt
		for _, sku := range skuName.Skus {
			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.Skus = 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 getValidStoreVendorMap(db *dao.DaoDB, storeIDs []int) (realVendorMap map[int]int, err error) {
	storeMapList, err := dao.GetStoresMapList(db, nil, storeIDs, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "")
	if err != nil {
		return nil, err
	}
	realVendorMap = make(map[int]int)
	for _, v := range storeMapList {
		if v.IsSync != 0 {
			realVendorMap[v.VendorID] = 1
		}
	}
	return realVendorMap, nil
}

func GetStoreAbnormalSkuCount(ctx *jxcontext.Context, storeID, syncStatus int, isBySku bool, params map[string]interface{}) (count int, err error) {
	db := dao.GetDB()
	realVendorMap, err2 := getValidStoreVendorMap(db, []int{storeID})
	if err = err2; err != nil {
		return 0, err
	}

	sql := `
	SELECT COUNT(*) ct`
	if !isBySku {
		sql += `
		FROM (
		SELECT DISTINCT t3.id`
	}
	sql += `
		FROM store_sku_bind t1
		JOIN sku t2 ON t2.id = t1.sku_id AND t2.deleted_at = ?
		JOIN sku_name t3 ON t3.id = t2.name_id AND t3.deleted_at = ?
		WHERE t1.deleted_at = ? AND t1.store_id = ? AND
		((t2.status = ? AND t3.status = ?) OR t1.status = ?) AND
		(1 = 0`
	sqlParams := []interface{}{
		utils.DefaultTimeValue,
		utils.DefaultTimeValue,
		utils.DefaultTimeValue,
		storeID,
		model.SkuStatusNormal,
		model.SkuStatusNormal,
		model.SkuStatusNormal,
	}
	for _, vendorID := range []int{model.VendorIDJD, model.VendorIDEBAI, model.VendorIDMTWM} {
		if realVendorMap[vendorID] != 0 {
			prefix := dao.ConvertDBFieldPrefix(model.VendorNames[vendorID])
			sql += fmt.Sprintf(" OR (t1.%s_sync_status & ? <> 0 AND t1.%s_sync_status & ? = 0", prefix, prefix)
			sqlParams = append(sqlParams, syncStatus, model.SyncFlagDeletedMask|model.SyncFlagNewMask)
			if model.MultiStoresVendorMap[vendorID] == 1 {
				sql += fmt.Sprintf(" AND t2.%s_id <> 0 AND t2.status = ? AND t3.status = ?", prefix)
				sqlParams = append(sqlParams, model.SkuStatusNormal, model.SkuStatusNormal)
			}
			sql += ")"
		}
	}
	sql += ")"
	if params["fromStatus"] != nil {
		fromStatus := params["fromStatus"].(int)
		toStatus := fromStatus
		if params["toStatus"] != nil {
			toStatus = params["toStatus"].(int)
		}
		sql += " AND t1.status >= ? AND t1.status <= ?"
		sqlParams = append(sqlParams, fromStatus, toStatus)
	}
	if !isBySku {
		sql += `
		) t1`
	}
	err = dao.GetRow(db, &count, sql, sqlParams...)
	return count, err
}

func GetStoresSkusSaleInfo(ctx *jxcontext.Context, storeIDs []int, skuIDs []int, fromTime, toTime time.Time, fromCount, toCount int) (saleInfoList []*SkuSaleInfo, err error) {
	db := dao.GetDB()
	sql := `
	SELECT IF(t2.jx_store_id <> 0, jx_store_id, store_id) store_id, t1.sku_id, COUNT(*) times, SUM(count) count
	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 <= ?
	`
	if utils.IsTimeZero(toTime) {
		toTime = time.Now()
	}
	sqlParams := []interface{}{
		model.OrderStatusFinished,
		fromTime,
		toTime,
	}
	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 += `
		AND IF(t1.jx_sku_id <> 0, t1.jx_sku_id, t1.sku_id) IN (` + dao.GenQuestionMarks(len(skuIDs)) + `)`
		sqlParams = append(sqlParams, skuIDs)
	}
	sql += `
	GROUP BY 1,2
	HAVING count >= ? AND  count <= ?
	`
	sqlParams = append(sqlParams, fromCount, toCount)
	if err = dao.GetRows(db, &saleInfoList, sql, sqlParams...); err == nil {
		return saleInfoList, nil
	}
	return nil, err
}

type GetStoresSkusSaleInfoNewResult struct {
	VendorID      int     `orm:"column(vendor_id)" json:"vendorID"`
	StoreID       int     `orm:"column(store_id)" json:"storeID"`
	StoreName     string  `json:"storeName"`
	SkuID         int     `orm:"column(sku_id)" json:"skuID"`
	SkuNameID     int     `orm:"column(name_id)" json:"skuNameID"`
	SkuName       string  `json:"skuName"`
	SaleCount     int     `json:"saleCount"`     //销量
	JdSaleCount   int     `json:"jdSaleCount"`   //销量
	MtSaleCount   int     `json:"mtSaleCount"`   //销量
	EbaiSaleCount int     `json:"ebaiSaleCount"` //销量
	Status        int     `json:"status"`
	SpecQuality   float64 `json:"specQuality"`
	SpecUnit      string  `json:"specUnit"`
}

func GetStoresSkusSaleInfoNew(ctx *jxcontext.Context, storeIDs, skuIDs, skuNameIDs []int, fromTime, toTime string, dataMap map[string]interface{}, sortType int, keyword string, offset, pageSize int) (pageInfo *model.PagedInfo, err error) {
	var (
		db   = dao.GetDB()
		list []*GetStoresSkusSaleInfoNewResult
	)
	if len(storeIDs) == 0 && len(skuIDs) == 0 && keyword == "" {
		return nil, fmt.Errorf("请至少输入一个条件查询！")
	}
	if fromTime == "" && toTime == "" {
		return nil, fmt.Errorf("必须选择一段时间！")
	}
	sql := `
		SELECT SQL_CALC_FOUND_ROWS t1.* FROM (
		SELECT d.id store_id, d.name store_name, c.id sku_id, e.name sku_name, e.id name_id,
			   c.spec_quality, c.spec_unit, t.status, SUM(IFNULL(a.count,0)) sale_count,
 				SUM(IF(b.vendor_id = 0, IFNULL(a.count,0), 0)) jd_sale_count,
				SUM(IF(b.vendor_id = 1, IFNULL(a.count,0), 0)) mt_sale_count,
				SUM(IF(b.vendor_id = 3, IFNULL(a.count,0), 0)) ebai_sale_count
          FROM store_sku_bind t
 	 LEFT JOIN order_sku a ON t.sku_id = IF(a.jx_sku_id = 0, a.sku_id, a.jx_sku_id)
	 LEFT JOIN goods_order b ON t.store_id = IF(b.jx_store_id = 0, b.store_id, b.jx_store_id) AND a.vendor_id = b.vendor_id AND a.vendor_order_id = b.vendor_order_id 
	 LEFT JOIN sku c ON t.sku_id = c.id
	 LEFT JOIN sku_name e ON e.id = c.name_id
     LEFT JOIN store d ON IF(b.jx_store_id = 0, b.store_id, b.jx_store_id) = d.id
		 WHERE a.order_created_at > ? AND a.order_created_at < ?
	`
	sqlParams := []interface{}{utils.Str2Time(fromTime), utils.Str2Time(toTime)}
	//if len(vendorIDs) > 0 {
	//	sql += " AND a.vendor_id IN (" + dao.GenQuestionMarks(len(vendorIDs)) + ")"
	//	sqlParams = append(sqlParams, vendorIDs)
	//}
	if len(storeIDs) > 0 {
		sql += " AND d.id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
		sqlParams = append(sqlParams, storeIDs)
	}
	if len(skuIDs) > 0 {
		sql += " AND c.id IN (" + dao.GenQuestionMarks(len(skuIDs)) + ")"
		sqlParams = append(sqlParams, skuIDs)
	}
	if len(skuNameIDs) > 0 {
		sql += " AND e.id IN (" + dao.GenQuestionMarks(len(skuNameIDs)) + ")"
		sqlParams = append(sqlParams, skuNameIDs)
	}
	if keyword != "" {
		sql += " AND (e.name LIKE ? OR d.name LIKE ? OR d.id = ? OR a.vendor_order_id = ?)"
		sqlParams = append(sqlParams, "%"+keyword+"%", "%"+keyword+"%", keyword, keyword)
	}
	sql += " GROUP BY  1, 2, 3, 4, 5, 6, 7, 8"
	if dataMap["saleCountBegin"] != nil || dataMap["saleCountEnd"] != nil {
		sql += " HAVING"
		if dataMap["saleCountBegin"] != nil {
			sql += " sale_count >= ?"
			sqlParams = append(sqlParams, utils.MustInterface2Int64(dataMap["saleCountBegin"]))
		}
		if dataMap["saleCountBegin"] != nil && dataMap["saleCountEnd"] != nil {
			sql += " AND"
		}
		if dataMap["saleCountEnd"] != nil {
			sql += " sale_count <= ?"
			sqlParams = append(sqlParams, utils.MustInterface2Int64(dataMap["saleCountEnd"]))
		}
	}

	sql += " )t1 WHERE 1 = 1"
	if sortType != 0 {
		if sortType > 0 {
			sql += " ORDER BY t1.sale_count"
		} else {
			sql += " ORDER BY t1.sale_count DESC"
		}
	}
	sql += " LIMIT ? OFFSET ?"
	pageSize = jxutils.FormalizePageSize(pageSize)
	offset = jxutils.FormalizePageOffset(offset)
	sqlParams = append(sqlParams, pageSize, offset)
	txDB, _ := dao.Begin(db)
	defer dao.Commit(db, txDB)
	fmt.Println(sql)
	fmt.Println(sqlParams)
	if err = dao.GetRowsTx(txDB, &list, sql, sqlParams...); err == nil {
		pageInfo = &model.PagedInfo{
			TotalCount: dao.GetLastTotalRowCount2(db, txDB),
			Data:       list,
		}
	}
	return pageInfo, err
}

func asyncStoreSkuOpFilter(ctx *jxcontext.Context, isAsync bool) bool {
	if !isAsync {
		authType := ctx.GetLoginInfo().GetAuthType()
		if authType == weixin.AuthTypeMini || authType == weixin.AuthTypeMP {
			userMobile, _ := ctx.GetMobileAndUserID()
			isAsync = asyncOpMobileMap[userMobile] == 0
		}
	}
	return isAsync
}

func UpdateStoreSku(ctx *jxcontext.Context, causeFlag, storeID int, skuBindInfo *StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
	return UpdateStoreSkus(ctx, causeFlag, storeID, []*StoreSkuBindInfo{skuBindInfo}, isAsync, isContinueWhenError)
}

func UpdateStoreSkus(ctx *jxcontext.Context, causeFlag, storeID int, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
	return UpdateStoresSkus(ctx, causeFlag, []int{storeID}, skuBindInfos, false, false, isAsync, isContinueWhenError)
}

func UpdateStoresSkus(ctx *jxcontext.Context, causeFlag int, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isScale, isRefreshHigh, isAsync, isContinueWhenError bool) (hint string, err error) {
	if beego.BConfig.RunMode == model.ServerTypeFruits || beego.BConfig.RunMode == model.ServerTypePet {
		doStoreSkuAuditForGy(ctx, storeIDs, skuBindInfos)
		var num int64
		db := dao.GetDB()
		skuIDs, err := updateStoresSkusWithoutSync(ctx, db, storeIDs, skuBindInfos, isScale, isRefreshHigh)
		if err != nil {
			return "", err
		}
		isAsync = asyncStoreSkuOpFilter(ctx, isAsync)
		num = int64(len(skuIDs))
		if num > 0 {
			hint, err = CurVendorSync.SyncStoresSkus(ctx, nil, causeFlag, db, nil, storeIDs, skuIDs, true, isAsync, isContinueWhenError)
		}
		if num == 0 || !isAsync || hint == "" {
			hint = utils.Int64ToStr(num)
		}
	} else {
		flag, _ := doStoreSkuAudit(ctx, storeIDs, skuBindInfos)
		if !flag {
			var num int64
			db := dao.GetDB()
			skuIDs, err := updateStoresSkusWithoutSync(ctx, db, storeIDs, skuBindInfos, isScale, isRefreshHigh) // 这个方法再通知京东创建商品
			if err != nil {
				return "", err
			}
			isAsync = asyncStoreSkuOpFilter(ctx, isAsync)
			num = int64(len(skuIDs))
			if num > 0 { // 这个方法非京东的创建商品,京东的改为同步库存,上下架等!
				isForce := false
				for _, v := range skuBindInfos[0].Skus {
					if v.Stock != nil {
						isForce = true
						break
					}
				}
				hint, err = CurVendorSync.SyncStoresSkus(ctx, nil, causeFlag, db, nil, storeIDs, skuIDs, isForce, isAsync, isContinueWhenError)
			}
			if num == 0 || !isAsync || hint == "" {
				hint = utils.Int64ToStr(num)
			}
			AddEventDetail(db, ctx, model.OperateUpdate, model.VendorIDUnknown, model.ThingTypeSku, storeIDs[0], "", "商品操作记录:"+utils.Format4Output(skuBindInfos, false))
		}
	}
	return hint, err
}

func UpdateStoresSkusWithoutSync(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isRefreshHigh bool) (err error) {
	db := dao.GetDB()
	if len(storeIDs) == 0 {
		stores, _ := dao.GetStoreList(db, nil, nil, nil, nil, nil, "")
		for _, v := range stores {
			storeIDs = append(storeIDs, v.ID)
		}
	}
	updateStoresSkusWithoutSync(ctx, db, storeIDs, skuBindInfos, false, isRefreshHigh)
	return err
}

func UpdateStoresSkusByBind(ctx *jxcontext.Context, parentTask tasksch.ITask, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError, isFos bool) (hint string, err error) {
	// if len(skuBindInfos) > maxStoreNameBind {
	// 	return "", fmt.Errorf("门店商品信息大于%d", maxStoreNameBind)
	// }
	skuBindInfosMap := make(map[int][]*StoreSkuBindInfo)
	storeIDMap := make(map[int]int)
	for _, v := range skuBindInfos {
		if v.StoreID > 0 {
			skuBindInfosMap[v.StoreID] = append(skuBindInfosMap[v.StoreID], v)
			storeIDMap[v.StoreID] = 1
		}
	}
	storeIDs := jxutils.IntMap2List(storeIDMap)
	sort.Ints(storeIDs)
	var num int64
	skuIDMap := make(map[int]int)
	db := dao.GetDB()
	// txDB , _ := dao.Begin(db)
	// defer func() {
	// 	if r := recover(); r != nil {
	// 		dao.Rollback(db, txDB)
	// 		panic(r)
	// 	}
	// }()
	for _, storeID := range storeIDs {
		skuIDs, err2 := updateStoresSkusWithoutSync(ctx, db, []int{storeID}, skuBindInfosMap[storeID], false, false)
		if err = err2; err != nil {
			// dao.Rollback(db, txDB)
			return "", err
		}
		for _, v := range skuIDs {
			skuIDMap[v] = 1
		}
		num += int64(len(skuIDs))
	}
	// dao.Commit(db, txDB)
	isAsync = asyncStoreSkuOpFilter(ctx, isAsync)
	if num > 0 {
		skuIDs := jxutils.IntMap2List(skuIDMap)
		hint, err = CurVendorSync.SyncStoresSkus(ctx, parentTask, 0, db, nil, storeIDs, skuIDs, isFos, isAsync, isContinueWhenError)
	}
	if num == 0 || !isAsync || hint == "" {
		hint = utils.Int64ToStr(num)
	}
	return hint, err
}

func checkStoresSkusSaleCity(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (err error) {
	sql := `
	SELECT t1.id store_id, t2.id name_id, t2.name
	FROM store t1
	JOIN sku_name t2 ON t2.is_global = 0 AND t2.deleted_at = ?
	LEFT JOIN sku_name_place_bind t3 ON t2.id = t3.name_id AND t1.city_code = t3.place_code
	WHERE t3.id IS NULL
	`
	sql += " AND t1.id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
	nameIDs := make([]int, 0)
	for _, v := range skuBindInfos {
		if v.IsFocus == 1 {
			nameIDs = append(nameIDs, v.NameID)
		} else {
			for _, v2 := range v.Skus {
				if v2.IsSale == 1 {
					nameIDs = append(nameIDs, v.NameID)
					break
				}
			}
		}
	}
	if len(nameIDs) == 0 {
		return nil
	}
	sql += " AND t2.id IN (" + dao.GenQuestionMarks(len(nameIDs)) + ")"
	var invalidList []*tStoreNameBind
	if err = dao.GetRows(db, &invalidList, sql, utils.DefaultTimeValue, storeIDs, nameIDs); err == nil {
		if len(invalidList) > 0 {
			errMsg := ""
			for _, v := range invalidList {
				errMsg += fmt.Sprintf("门店:%d，Name:%d(%s)销售区域错误！\n", v.StoreID, v.NameID, v.Name)
			}
			err = errors.New(errMsg)
		}
	}
	return err
}

func uniqueStoreIDs(storeIDs []int) []int {
	storeIDMap := make(map[int]int)
	for _, v := range storeIDs {
		storeIDMap[v] = 1
	}
	return jxutils.IntMap2List(storeIDMap)
}

func uniqueStoreNameBind(skuBindInfos []*StoreSkuBindInfo) (outSkuBindInfos []*StoreSkuBindInfo) {
	nameIDMap := make(map[int]int)
	upcMap := make(map[string]int)
	for _, v := range skuBindInfos {
		if v.NameID != 0 {
			if nameIDMap[v.NameID] != 1 {
				outSkuBindInfos = append(outSkuBindInfos, v)
				nameIDMap[v.NameID] = 1
			}
		} else if v.UPC != "" {
			if upcMap[v.UPC] != 1 {
				outSkuBindInfos = append(outSkuBindInfos, v)
				upcMap[v.UPC] = 1
			}
		}
	}
	return outSkuBindInfos
}

func updateStoresSkusWithoutSync(ctx *jxcontext.Context, db *dao.DaoDB, storeIDs []int, skuBindInfos []*StoreSkuBindInfo, isScale, isRefreshHigh bool) (needSyncSkus []int, err error) {
	// if len(storeIDs)*len(skuBindInfos) > maxStoreNameBind2 {
	// 	return nil, fmt.Errorf("门店商品信息大于%d", maxStoreNameBind2)
	// }
	storeIDs = uniqueStoreIDs(storeIDs)
	skuBindInfos = uniqueStoreNameBind(skuBindInfos)

	sort.Ints(storeIDs)
	if db == nil {
		db = dao.GetDB()
	}
	// if err = checkStoresSkusSaleCity(ctx, db, storeIDs, skuBindInfos); err != nil {
	// 	return nil, err
	// }
	isUserCanDirectChangePrice := true
	if user := ctx.GetFullUser(); user != nil {
		isUserCanDirectChangePrice = user.Type&model.UserTypeOperator != 0
	}
	userName := ctx.GetUserName()
	needSyncIDMap := make(map[int]int)
	isSyncSkus := false
	var appCodeList []string
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	for _, storeID := range storeIDs {
		// todo 可以考虑在需要更新价格再获取
		storeDetail, err := dao.GetStoreDetail(dao.GetDB(), storeID, model.VendorIDJX, "")
		if err != nil || storeDetail == nil {
			continue
		}
		if beego.BConfig.RunMode == "prod" || beego.BConfig.RunMode == "beta" {
			if len(storeIDs) > 1 {
				if storeDetail.StoreLevel == "D" || storeDetail.StoreLevel == "E" {
					continue
				}
			}
		}
		scaleFactor := float64(1)
		if isScale {
			scaleFactor = 100 / float64(jxutils.ConstrainPayPercentage(storeDetail.PayPercentage))
		}
		for _, skuBindInfo := range skuBindInfos {
			// 关注且没有给价时，需要尝试从store_sku_bind中得到已有的单价
			needGetExistingUnitPrice := skuBindInfo.UnitPrice == 0 && skuBindInfo.IsFocus == 1
			// 批量关注时补全sku,主要是为了补商品库存
			if len(skuBindInfo.Skus) > 0 && skuBindInfo.Skus[0].SkuID == 0 {
				skuList, _ := dao.GetSkus(db, nil, []int{skuBindInfo.NameID}, nil, nil, nil)
				skuBindInfoSkus := make([]*StoreSkuBindSkuInfo, 0, len(skuList))
				for _, sl := range skuList {
					skuBindInfoSkus = append(skuBindInfoSkus, &StoreSkuBindSkuInfo{
						SkuID:  sl.ID,
						IsSale: skuBindInfo.IsSale,
						Stock:  skuBindInfo.Skus[0].Stock,
					})
				}
				skuBindInfo.Skus = skuBindInfoSkus
			}
			inSkuBinds := skuBindInfo.Skus

			var allBinds []*tStoreSkuBindAndSpec
			sql := `
			SELECT
			t2.*,
			t1.id real_sku_id, t1.status sku_status, t1.spec_quality, t1.spec_unit, t1.exd_sku_id,`
			if needGetExistingUnitPrice {
				sql += " IF(t5.unit_price > 0, t5.unit_price, t3.price) sku_name_price,"
			}
			sql += `
			t3.unit sku_name_unit, t3.name, t3.status sku_name_status, t3.id name_id,
			ts.change_price_type, ts.name store_name
			FROM sku t1
			JOIN store ts ON ts.id = ? AND ts.deleted_at = ?
			LEFT JOIN store_sku_bind t2 ON t2.sku_id = t1.id AND t2.store_id = ts.id AND t2.deleted_at = ?
			JOIN sku_name t3 ON t1.name_id = t3.id AND t3.deleted_at = ?`
			sqlParams := []interface{}{
				storeID,
				utils.DefaultTimeValue,
				utils.DefaultTimeValue,
				utils.DefaultTimeValue,
			}
			if needGetExistingUnitPrice {
				sql += `
				LEFT JOIN (
					SELECT t7.store_id, t8.name_id, CAST(AVG(t7.unit_price) AS SIGNED) unit_price
					FROM store_sku_bind t7
					JOIN sku t8 ON t8.id = t7.sku_id
					`
				if skuBindInfo.NameID != 0 {
					sql += " AND t8.name_id = ?"
					sqlParams = append(sqlParams, skuBindInfo.NameID)
				} else if skuBindInfo.UPC != "" {
					sql += " JOIN sku_name t9 ON t9.id = t8.name_id AND t9.upc = ?"
					sqlParams = append(sqlParams, skuBindInfo.UPC)
				}
				sql += `
					WHERE t7.deleted_at = ? AND t7.store_id = ?
					GROUP BY 1,2
				) t5 ON t5.store_id = ts.id AND t5.name_id = t1.name_id`
				sqlParams = append(sqlParams, utils.DefaultTimeValue, storeID)
			}
			sql += " WHERE 1 = 1"
			if skuBindInfo.NameID != 0 {
				sql += " AND t1.name_id = ?"
				sqlParams = append(sqlParams, skuBindInfo.NameID)
			} else if skuBindInfo.UPC != "" {
				sql += " AND t3.upc = ?"
				sqlParams = append(sqlParams, skuBindInfo.UPC)
			}
			sql += ` AND t1.deleted_at = ?
			FOR UPDATE`
			sqlParams = append(sqlParams, utils.DefaultTimeValue)
			// globals.SugarLogger.Debug(sql)
			if err = dao.GetRows(db, &allBinds, sql, sqlParams...); err == nil {
				if len(allBinds) > 0 {
					inSkuBinsMap := make(map[int]*StoreSkuBindSkuInfo, len(inSkuBinds))
					for _, v := range inSkuBinds {
						inSkuBinsMap[v.SkuID] = v
					}
					unitPrice := 0
					if skuBindInfo.UnitPrice != 0 {
						if skuBindInfo.UnitPrice > MaxSkuUnitPrice {
							dao.Rollback(db, txDB)
							return nil, fmt.Errorf("商品:%s价格:%s太夸张", allBinds[0].Name, jxutils.IntPrice2StandardCurrencyString(int64(skuBindInfo.UnitPrice)))
						}
						unitPrice = skuBindInfo.UnitPrice
						if isRefreshHigh {
							if allBinds[0].UnitPrice <= unitPrice {
								continue
							}
						}
					} else {
						unitPrice = allBinds[0].UnitPrice
						if unitPrice == 0 {
							unitPrice = allBinds[0].SkuNamePrice
						}
					}
					unitPrice = int(math.Floor(float64(unitPrice)*scaleFactor/10+0.5)) * 10 //四舍五入 价格为100倍 角分为十位个位 除以10 + 0.5对分位向下取整（完成四舍五入的方法）
					for _, v := range allBinds {
						if v.Stock == 0 && beego.BConfig.RunMode != model.ServerTypePet {
							v.Stock = model.MaxStoreSkuStockQty
						}
						var num int64
						inSkuBind := inSkuBinsMap[v.RealSkuID]
						isCanChangePrice := isUserCanDirectChangePrice || jxutils.TranslateStorePriceType(v.ChangePriceType) != model.StoreChangePriceTypeBossDisabled
						var skuBind *model.StoreSkuBind
						if v.ID == 0 {
							// if v.ExdSkuID == "" {
							if (strings.Contains(v.StoreName, model.ExdStoreName) && v.ExdSkuID != "") || (!strings.Contains(v.StoreName, model.ExdStoreName) && v.ExdSkuID == "") {
								if skuBindInfo.IsFocus == 1 && v.SkuNameStatus == model.SkuStatusNormal && v.SkuStatus == model.SkuStatusNormal && isCanChangePrice {
									skuBind = &model.StoreSkuBind{
										StoreID:    storeID,
										SkuID:      v.RealSkuID,
										SubStoreID: skuBindInfo.SubStoreID, // todo 这个应该从用户信息中自动获得
										UnitPrice:  unitPrice,
										Price:      jxutils.CaculateSkuPrice(unitPrice, v.SpecQuality, v.SpecUnit, v.SkuNameUnit),
										Status:     model.StoreSkuBindStatusDontSale, // 缺省不可售？
									}
									skuBind.JxPrice = jxutils.CaculatePriceByPricePack(storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage), skuBind.Price)
									if tmpStatus := getSkuSaleStatus(inSkuBind, skuBindInfo); tmpStatus != model.StoreSkuBindStatusNA {
										skuBind.Status = tmpStatus
										//if inSkuBind != nil {
										//	if inSkuBind.Stock != nil {
										//		skuBind.Stock = *inSkuBind.Stock
										//	} else {
										//		skuBind.Stock = model.MaxStoreSkuStockQty
										//	}
										//} else {
										//	if tmpStatus == model.StoreSkuBindStatusNormal {
										//		skuBind.Stock = model.MaxStoreSkuStockQty
										//	}
										//}
									}
									if inSkuBind != nil && inSkuBind.Stock != nil {
										skuBind.Stock = *inSkuBind.Stock
									}
									if inSkuBind != nil && inSkuBind.LocationCode != "" {
										skuBind.LocationCode = inSkuBind.LocationCode
									}
									if globals.IsAddEvent {
										err = AddEventDetail(db, ctx, model.OperateAdd, v.RealSkuID, model.ThingTypeSku, storeID, "", utils.Int2Str(skuBind.UnitPrice))
									}
									setStoreSkuBindStatus(skuBind, model.SyncFlagNewMask)
									dao.WrapAddIDCULDEntity(skuBind, userName)
									if deletedSku := dao.GetDeletedStoreSkuBind(db, skuBind.StoreID, skuBind.SkuID); deletedSku == nil {
										if err = dao.CreateEntity(db, skuBind); err != nil {
											dao.Rollback(db, txDB)
											return nil, err
										}
										num = 1
									} else {
										// 需要处理，在删除某个门店商品，同步失败的情况下，又把商品重新关注。
										// 所以统一处理成恢复删除的记录，这样避免问题
										skuBind.ID = deletedSku.ID
										// vendorSkuID的赋值意义不大
										skuBind.MtwmID = deletedSku.MtwmID
										skuBind.TaoID = deletedSku.TaoID
										skuBind.EbaiID = deletedSku.EbaiID
										skuBind.JdsID = deletedSku.JdsID
										skuBind.JdsWareID = deletedSku.JdsWareID
										skuBind.DdID = deletedSku.DdID
										if num, err = dao.UpdateEntity(db, skuBind); err != nil {
											dao.Rollback(db, txDB)
											return nil, err
										}
									}
									//下面这段很难受
									{
										//可售了的才整
										if skuBind.Status == model.StoreSkuBindStatusNormal {
											//如果是京东关注，要去建商品
											list1, _ := dao.GetStoresMapList(db, []int{model.VendorIDJD}, []int{storeID}, nil, model.StoreStatusAll, 1, "", "", "")
											//表示这个门店绑定了京东
											if len(list1) > 0 {
												vendorOrgCodes, _ := dao.GetVendorOrgCode(db, model.VendorIDJD, list1[0].VendorOrgCode, model.VendorOrgTypePlatform)
												//thingmap里肯定存在，再判断有没有同步上去
												thingMaps, _ := dao.GetThingMapList(db, model.ThingTypeSku, []int{model.VendorIDJD}, []int{v.RealSkuID}, []string{list1[0].VendorOrgCode})
												if len(thingMaps) > 0 {
													//如果平台ID为空（未创建到京东）
													if thingMaps[0].VendorThingID == "" {
														isSyncSkus = true
														appCodeList = append(appCodeList, list1[0].VendorOrgCode)
														//并且同步标志还没有带待创建（因为addskuname现在建到thingmap上不会带待创建标志了）
														if !model.IsSyncStatusNew(thingMaps[0].SyncStatus) {
															OnCreateThing(ctx, db, vendorOrgCodes, int64(v.RealSkuID), model.ThingTypeSku, model.SyncFlagNewMask, false)
														}
													}
												} else {
													//万一不存在
													isSyncSkus = true
													appCodeList = append(appCodeList, list1[0].VendorOrgCode)
													OnCreateThing(ctx, db, vendorOrgCodes, int64(v.RealSkuID), model.ThingTypeSku, model.SyncFlagNewMask, false)
												}
											}
										}
									}
								}
							}
						} else {
							beforeMsg := *v
							skuBind = &v.StoreSkuBind
							if skuBindInfo.IsFocus == -1 && isCanChangePrice {
								if globals.IsAddEvent {
									err = AddEventDetail(db, ctx, model.OperateDelete, skuBind.SkuID, model.ThingTypeSku, storeID, "", "")
								}
								if globals.IsStoreSkuAct {
									dao.DeleteEntity(db, &model.StoreSkuAct{
										StoreID: skuBind.StoreID,
										SkuID:   skuBind.SkuID,
									}, "StoreID", "SkuID")
								}
								if num, err = dao.DeleteEntityLogically(db, skuBind, map[string]interface{}{
									model.FieldStatus:         model.StoreSkuBindStatusDeleted,
									model.FieldJdSyncStatus:   model.SyncFlagDeletedMask,
									model.FieldEbaiSyncStatus: model.SyncFlagDeletedMask,
									model.FieldMtwmSyncStatus: model.SyncFlagDeletedMask,
									model.FieldTaoSyncStatus:  model.SyncFlagDeletedMask,
									model.FieldYbSyncStatus:   model.SyncFlagDeletedMask,
									model.FieldJdsSyncStatus:  model.SyncFlagDeletedMask,
									model.FieldDdSyncStatus:   model.SyncFlagDeletedMask,
								}, userName, nil); err != nil {
									dao.Rollback(db, txDB)
									return nil, err
								}
								//删除待审核商品信息
								storeAudits, err := dao.GetStoreSkuAuditLight(db, []int{storeID}, []int{v.NameID}, model.StoreAuditStatusOnline)
								if len(storeAudits) > 0 {
									storeAudits[0].DeletedAt = time.Now()
									if _, err = dao.UpdateEntity(db, storeAudits[0], "DeletedAt"); err != nil {
										dao.Rollback(db, txDB)
										return nil, err
									}
								}
							} else {
								// 用了SELECT FOR UPDATE后，只更新修改字段是没有必要的，暂时保留
								updateFieldMap := make(map[string]interface{})
								if skuBindInfo.IsFocus == 1 { // 关注之后再关注不操作
									// skuBind.Status = model.StoreSkuBindStatusDontSale // 缺省不可售？
									// skuBind.DeletedAt = utils.DefaultTimeValue
									// skuBind.UnitPrice = unitPrice
									// skuBind.Price = jxutils.CaculateSkuPrice(unitPrice, v.SpecQuality, v.SpecUnit, v.SkuNameUnit)
									// setStoreSkuBindStatus(skuBind, model.SyncFlagPriceMask|model.SyncFlagSaleMask)
									// updateFieldMap[model.FieldStatus] = 1
									// updateFieldMap[model.FieldDeletedAt] = 1
									// updateFieldMap["UnitPrice"] = 1
									// updateFieldMap["Price"] = 1
								}
								if tmpStatus := getSkuSaleStatus(inSkuBind, skuBindInfo); tmpStatus != model.StoreSkuBindStatusNA {
									if tmpStatus != skuBind.Status {
										updateFieldMap[model.FieldStatus] = 1
									}
									skuBind.Status = tmpStatus
									setStoreSkuBindStatus(skuBind, model.SyncFlagSaleMask)
									setStoreSkuBindStatus(skuBind, model.SyncFlagStockMask)
									if tmpStatus == model.StoreSkuBindStatusNormal {
										switch beego.BConfig.RunMode {
										case model.ServerTypePet:
											if inSkuBind.Stock != nil {
												skuBind.Stock = *inSkuBind.Stock
											} else {
												skuBind.Stock = 0
											}
										default:
											skuBind.Stock = model.MaxStoreSkuStockQty
										}
										//下面这段很难受
										{
											//如果是京东关注，要去建商品
											list1, _ := dao.GetStoresMapList(db, []int{model.VendorIDJD}, []int{storeID}, nil, model.StoreStatusAll, 1, "", "", "")
											//表示这个门店绑定了京东
											if len(list1) > 0 {
												vendorOrgCodes, _ := dao.GetVendorOrgCode(db, model.VendorIDJD, list1[0].VendorOrgCode, model.VendorOrgTypePlatform)
												//thingmap里肯定存在，再判断有没有同步上去
												thingMaps, _ := dao.GetThingMapList(db, model.ThingTypeSku, []int{model.VendorIDJD}, []int{v.RealSkuID}, []string{list1[0].VendorOrgCode})
												if len(thingMaps) > 0 {
													//如果平台ID为空（未创建到京东）
													if thingMaps[0].VendorThingID == "" {
														isSyncSkus = true
														appCodeList = append(appCodeList, list1[0].VendorOrgCode)
														//并且同步标志还没有带待创建（因为addskuname现在建到thingmap上不会带待创建标志了）
														if !model.IsSyncStatusNew(thingMaps[0].SyncStatus) {
															OnCreateThing(ctx, db, vendorOrgCodes, int64(v.RealSkuID), model.ThingTypeSku, model.SyncFlagNewMask, false)
														}
													}
												} else {
													//万一不存在
													isSyncSkus = true
													appCodeList = append(appCodeList, list1[0].VendorOrgCode)
													OnCreateThing(ctx, db, vendorOrgCodes, int64(v.RealSkuID), model.ThingTypeSku, model.SyncFlagNewMask, false)
												}
											}
										}
									} else {
										skuBind.Stock = 0
									}
								}
								if inSkuBind != nil {
									if inSkuBind.Stock != nil {
										updateFieldMap["Stock"] = 1
										skuBind.Stock = *inSkuBind.Stock
										setStoreSkuBindStatus(skuBind, model.SyncFlagStockMask)
									}

									if inSkuBind.LocationCode != "" {
										updateFieldMap["LocationCode"] = 1
										skuBind.LocationCode = inSkuBind.LocationCode
										setStoreSkuBindStatus(skuBind, model.SyncFlagStockMask)
									}
									if inSkuBind.MtLadderBoxPrice != 0 {
										updateFieldMap["MtLadderBoxPrice"] = 1
										skuBind.MtLadderBoxPrice = inSkuBind.MtLadderBoxPrice
										setStoreSkuBindStatus(skuBind, model.SyncFlagModifiedMask)
									}
								}
								if skuBindInfo.MtLadderBoxPrice != 0 {
									updateFieldMap["MtLadderBoxPrice"] = 1
									skuBind.MtLadderBoxPrice = skuBindInfo.MtLadderBoxPrice
									setStoreSkuBindStatus(skuBind, model.SyncFlagModifiedMask)
								}
								if skuBindInfo.UnitPrice != 0 && isCanChangePrice { // 这里是否需要加此条件限制
									price := jxutils.CaculateSkuPrice(unitPrice, v.SpecQuality, v.SpecUnit, v.SkuNameUnit)
									jxPrice := jxutils.CaculatePriceByPricePack(storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage), price)
									if unitPrice != skuBind.UnitPrice || price != skuBind.Price || jxPrice != skuBind.JxPrice {
										if price != skuBind.Price {
											setStoreSkuBindStatus(skuBind, model.SyncFlagPriceMask)
											updateFieldMap["Price"] = 1
										}
										skuBind.UnitPrice = unitPrice
										skuBind.Price = price
										skuBind.JxPrice = jxPrice
										updateFieldMap["UnitPrice"] = 1
										updateFieldMap["JxPrice"] = 1

										//TODO 2020-09-08 如果改价时商品在做直降，要取消这个商品的直降，再通过改价比例修改活动价，再上这个直降
										// checkActDirectDown(ctx, skuBind, beforeMsg)
									}
								}
								if skuBindInfo.StatusSaleBegin != nil && skuBindInfo.StatusSaleEnd != nil {
									//if err := ValidateStruct(skuBindInfo); err != nil {
									//	globals.SugarLogger.Infof("更改商品:%s, 可售时间不合法！时间范围：[%v] 至 [%v]", allBinds[0].Name, skuBindInfo.StatusSaleBegin, skuBindInfo.StatusSaleEnd)
									//} else {
									if *skuBindInfo.StatusSaleBegin < *skuBindInfo.StatusSaleEnd || (*skuBindInfo.StatusSaleBegin == 0 && *skuBindInfo.StatusSaleEnd == 0) {
										if skuBind.StatusSaleBegin != *skuBindInfo.StatusSaleBegin || skuBind.StatusSaleEnd != *skuBindInfo.StatusSaleEnd {
											updateFieldMap["StatusSaleBegin"] = 1
											updateFieldMap["StatusSaleEnd"] = 1
										}
										skuBind.StatusSaleBegin = *skuBindInfo.StatusSaleBegin
										skuBind.StatusSaleEnd = *skuBindInfo.StatusSaleEnd
									}

									//}
								}

								if globals.IsAddEvent {
									if len(updateFieldMap) > 0 {
										mapAfter := refutil.FindMapAndStructMixed(updateFieldMap, skuBind)
										mapBefore := refutil.FindMapAndStructMixed(updateFieldMap, beforeMsg)
										err = AddEventDetail(db, ctx, model.OperateUpdate, v.RealSkuID, model.ThingTypeSku, storeID, BuildDiffData(mapBefore), BuildDiffData(mapAfter))
									}
								}
								if len(updateFieldMap) > 0 {
									updateFieldMap[model.FieldJdSyncStatus] = 1
									updateFieldMap[model.FieldEbaiSyncStatus] = 1
									updateFieldMap[model.FieldMtwmSyncStatus] = 1
									updateFieldMap[model.FieldTaoSyncStatus] = 1
									updateFieldMap[model.FieldDdSyncStatus] = 1
									updateFieldMap[model.FieldUpdatedAt] = 1
									updateFieldMap[model.FieldLastOperator] = 1
									name := userName
									if ctx.GetFullUser().Type&model.UserTypeOperator != 0 {
										name = ctx.GetFullUser().Name
									}
									dao.WrapUpdateULEntity(skuBind, name)
									if skuBind.Status == model.SkuStatusNormal {
										updateFieldMap["AutoSaleAt"] = 1
										skuBind.AutoSaleAt = utils.DefaultTimeValue
									}
									if num, err = dao.UpdateEntity(db, skuBind /*, utils.Map2KeySlice(updateFieldMap)...*/); err != nil {
										dao.Rollback(db, txDB)
										return nil, err
									}
								}
							}
						}
						if skuBind != nil && num == 1 {
							needSyncIDMap[skuBind.SkuID] = 1
						}
					}
				}
			} else {
				dao.Rollback(db, txDB)
				return nil, err
			}
		}
	}
	dao.Commit(db, txDB)
	skuIDs := jxutils.IntMap2List(needSyncIDMap)
	if isSyncSkus {
		_, err = SyncSkus(ctx, nil, []int{model.VendorIDJD}, appCodeList, nil, skuIDs, true)
	}
	return skuIDs, err
}

// func checkActDirectDown(ctx *jxcontext.Context, skuBind *model.StoreSkuBind, beforeSkuBind tStoreSkuBindAndSpec) (err error) {
// 	var (
// 		originPrice = beforeSkuBind.Price
// 		price       = skuBind.Price
// 		db          = dao.GetDB()
// 		pageSize    = 9999
// 		// percent     = price / originPrice
// 	)
// 	if paged, _ := dao.QueryActs(db, 0, 0, pageSize, -1, "", -1, []int{model.ActStatusCreated}, []int{model.ActSkuDirectDown}, nil, skuBind.StoreID, []int{skuBind.SkuID}, 0, utils.ZeroTimeValue, utils.ZeroTimeValue, time.Now().AddDate(0, -2, 0), time.Now()); len(paged.Data) > 0 {
// 		for _, act := range paged.Data {
// 			if _, actStoreSkus, err := dao.GetActStoreSkuVendorList(db, act.ID, nil, []int{skuBind.StoreID}, []int{skuBind.SkuID}, "", 0, pageSize); err == nil && len(actStoreSkus) > 0 {
// 				var (
// 					handler          = partner.GetPurchasePlatformFromVendorID(actStoreSkus[0].VendorID).(partner.IPurchasePlatformActHandler)
// 					actStoreSkuParam []*ActStoreSkuParam
// 				)
// 				for _, actStoreSku := range actStoreSkus {
// 					aa := &ActStoreSkuParam{
// 						ActStoreSku: model.ActStoreSku{
// 							StoreID: skuBind.StoreID,
// 							SkuID:   actStoreSku.SkuID,
// 							ActID:   act.ID,
// 						},
// 					}
// 					actStoreSkuParam = append(actStoreSkuParam, aa)
// 				}
// 				DeleteActStoreSkuBind(ctx, db, act.ID, actStoreSkuParam)
// 				actMap, err := dao.GetActVendorInfo(db, act.ID, []int{actStoreSkus[0].VendorID})
// 				if err != nil {
// 					return err
// 				}
// 				actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, act.ID, nil, nil, nil)
// 				if err != nil {
// 					return err
// 				}
// 				handler.SyncAct(ctx, nil, actMap[actStoreSkus[0].VendorID], nil, actStoreSkuMap[actStoreSkus[0].VendorID])
// 			}
// 		}
// 	}
// 	return err
// }

func getSkuSaleStatus(inSkuBind *StoreSkuBindSkuInfo, skuNameBindInfo *StoreSkuBindInfo) int {
	tempSale := 0
	if inSkuBind != nil {
		tempSale = inSkuBind.IsSale
	}
	if tempSale == 0 {
		tempSale = skuNameBindInfo.IsSale
	}
	if tempSale == -1 {
		return model.StoreSkuBindStatusDontSale
	} else if tempSale == 1 {
		return model.StoreSkuBindStatusNormal
	}
	return model.StoreSkuBindStatusNA
}

func AddEventDetail(db *dao.DaoDB, ctx *jxcontext.Context, operateType, thingID, thingType, storeID int, beforeData, afterData string) (err error) {
	if ctx.GetUserName() == "jxadmin" {
		return err
	}
	operateEventDetail := &model.OperateEventDetail{
		OperateType: operateType,
		ThingID:     thingID,
		ThingType:   thingType,
		StoreID:     storeID,
		AccessUUID:  ctx.GetTrackInfo()[0:strings.Index(ctx.GetTrackInfo(), ",")],
		BeforeData:  beforeData,
		AfterData:   afterData,
	}
	err = event.AddOperateEventDetail(db, operateEventDetail)
	return err
}

func formatAutoSaleTime(autoSaleTime time.Time) (outAutoSaleTime time.Time) {
	if utils.IsTimeZero(autoSaleTime) {
		outAutoSaleTime = utils.DefaultTimeValue
	} else {
		outAutoSaleTime = jxutils.GetNextTimeFromList(time.Now(), []string{AutoSaleAtStr})
	}
	return outAutoSaleTime
}

// todo 应该用updateStoresSkusWithoutSync实现
func updateStoreSkusSaleWithoutSync(ctx *jxcontext.Context, storeID int, skuBindSkuInfos []*StoreSkuBindSkuInfo, autoSaleTime time.Time, ignoreDontSale bool, userName string) (needSyncSkus []int, err error) {
	var num int64
	db := dao.GetDB()
	needSyncIDMap := make(map[int]int)
	skuIDMap := make(map[int]int)
	skuBindSkuInfosMap := make(map[int]*StoreSkuBindSkuInfo)
	for _, v := range skuBindSkuInfos {
		skuIDMap[v.SkuID] = 1
		skuBindSkuInfosMap[v.SkuID] = v
	}

	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	storeSkuList, err := dao.GetStoresSkusInfo(db, []int{storeID}, jxutils.IntMap2List(skuIDMap))
	if err != nil {
		dao.Rollback(db, txDB)
		return nil, err
	}
	autoSaleTime = formatAutoSaleTime(autoSaleTime)
	for _, skuBind := range storeSkuList {
		if v := skuBindSkuInfosMap[skuBind.SkuID]; v != nil /*&& v.IsSale != 0*/ {
			if !(!utils.IsTimeZero(autoSaleTime) && ignoreDontSale && skuBind.Status == model.StoreSkuBindStatusDontSale) {
				statusResult := skuBind.Status
				if v.IsSale == -1 || !utils.IsTimeZero(autoSaleTime) {
					skuBind.Status = model.StoreSkuBindStatusDontSale
				} else if v.IsSale == 1 {
					skuBind.Status = model.StoreSkuBindStatusNormal
				}
				kvs := map[string]interface{}{
					model.FieldLastOperator:   ctx.GetUserName(),
					model.FieldUpdatedAt:      time.Now(),
					model.FieldStatus:         skuBind.Status,
					model.FieldJdSyncStatus:   skuBind.JdSyncStatus | model.SyncFlagSaleMask,
					model.FieldEbaiSyncStatus: skuBind.EbaiSyncStatus | model.SyncFlagSaleMask,
					model.FieldMtwmSyncStatus: skuBind.MtwmSyncStatus | model.SyncFlagSaleMask,
					model.FieldTaoSyncStatus:  skuBind.TaoSyncStatus | model.SyncFlagSaleMask,
					model.FieldDdSyncStatus:   skuBind.DdSyncStatus | model.SyncFlagSaleMask,
				}
				if v.Stock != nil {
					kvs["Stock"] = v.Stock
				}
				if utils.IsTimeZero(autoSaleTime) || skuBind.Status == model.SkuStatusNormal {
					kvs["AutoSaleAt"] = utils.DefaultTimeValue
				} else {
					kvs["AutoSaleAt"] = autoSaleTime
				}
				if globals.IsAddEvent {
					var status int
					if v.IsSale == -1 {
						status = model.StoreSkuBindStatusDontSale
					} else {
						status = model.StoreSkuBindStatusNormal
					}
					if status != statusResult {
						mapAfter := make(map[string]interface{})
						mapAfter["Status"] = status
						mapBefore := make(map[string]interface{})
						mapBefore["Status"] = statusResult
						err = AddEventDetail(db, ctx, model.OperateUpdate, v.SkuID, model.ThingTypeSku, storeID, BuildDiffData(mapBefore), BuildDiffData(mapAfter))
					}
				}
				if num, err = dao.UpdateEntityLogically(db, skuBind, kvs, userName, nil); err != nil {
					dao.Rollback(db, txDB)
					return nil, err
				}
				if num == 1 {
					needSyncIDMap[v.SkuID] = 1
				}
			}
		}
	}
	dao.Commit(db, txDB)
	needSyncSkus = jxutils.IntMap2List(needSyncIDMap)
	return needSyncSkus, err
}

func BuildDiffData(mapData map[string]interface{}) string {
	dd := utils.MustMarshal(mapData)
	result := utils.LimitUTF8StringLen(string(dd), 3200)
	return result
}

func uniqueStoreSkuBind(skuBindSkuInfos []*StoreSkuBindSkuInfo) (outSkuBindSkuInfos []*StoreSkuBindSkuInfo) {
	skuIDMap := make(map[int]int)
	for _, v := range skuBindSkuInfos {
		if skuIDMap[v.SkuID] != 1 {
			outSkuBindSkuInfos = append(outSkuBindSkuInfos, v)
			skuIDMap[v.SkuID] = 1
		}
	}
	return outSkuBindSkuInfos
}

func UpdateStoresSkusSale(ctx *jxcontext.Context, storeIDs []int, skuBindSkuInfos []*StoreSkuBindSkuInfo, autoSaleTime time.Time, ignoreDontSale bool, userName string, isAsync, isContinueWhenError bool) (hint string, err error) {
	storeIDs = uniqueStoreIDs(storeIDs)
	skuBindSkuInfos = uniqueStoreSkuBind(skuBindSkuInfos)

	var num int64
	for _, storeID := range storeIDs {
		skuIDs, err2 := updateStoreSkusSaleWithoutSync(ctx, storeID, skuBindSkuInfos, autoSaleTime, ignoreDontSale, userName)
		if err = err2; err != nil {
			return "", err
		}
		num += int64(len(skuIDs))
	}
	isAsync = asyncStoreSkuOpFilter(ctx, isAsync)
	if num > 0 {
		skuIDs := make([]int, 0)
		for _, v := range skuBindSkuInfos {
			skuIDs = append(skuIDs, v.SkuID)
		}
		hint, err = CurVendorSync.SyncStoresSkus(ctx, nil, 0, dao.GetDB(), nil, storeIDs, skuIDs, false, isAsync, isContinueWhenError)
	}
	if num == 0 || !isAsync || hint == "" {
		hint = utils.Int64ToStr(num)
	}
	return hint, err
}

func CopyStoreSkus(ctx *jxcontext.Context, fromStoreID int, toStoreIDs []int, copyMode string, isScale bool, params map[string]interface{}, userName string) (num int64, err error) {
	if copyMode != CopyStoreSkuModeFresh && copyMode != CopyStoreSkuModeUpdate && copyMode != CopyStoreSkuModeUpdatePrice {
		return 0, fmt.Errorf("不支持的拷贝模式：%s", copyMode)
	}
	//权限
	if permission.IsRoled(ctx) {
		if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
			if storeIDsMap[fromStoreID] == 0 {
				return 0, fmt.Errorf("抱歉，您无权更改他人店铺商品 [%v]", fromStoreID)
			}
			for _, v := range toStoreIDs {
				if storeIDsMap[v] == 0 {
					return 0, fmt.Errorf("抱歉，您无权更改他人店铺商品 [%v]", v)
				}
			}
		}
	}
	db := dao.GetDB()
	fromStore, err := checkStoreExisting(db, fromStoreID)
	if err != nil {
		return 0, err
	}
	sqlCatAndSku := ""
	sqlCatAndSkuParams := make([]interface{}, 0)
	if params["categoryIDs"] != nil {
		var cats []int
		if err = utils.UnmarshalUseNumber([]byte(params["categoryIDs"].(string)), &cats); err != nil {
			return 0, err
		}
		if len(cats) > 0 {
			sqlCatAndSku += " AND (t3.category_id IN (" + dao.GenQuestionMarks(len(cats)) + ") OR t4.parent_id IN (" + dao.GenQuestionMarks(len(cats)) + "))"
			sqlCatAndSkuParams = append(sqlCatAndSkuParams, cats, cats)
		}
	}
	if params["skuIDs"] != nil {
		var skus []int
		if err = utils.UnmarshalUseNumber([]byte(params["skuIDs"].(string)), &skus); err != nil {
			return 0, err
		}
		if len(skus) > 0 {
			sqlCatAndSku += " AND t1.sku_id IN (" + dao.GenQuestionMarks(len(skus)) + ")"
			sqlCatAndSkuParams = append(sqlCatAndSkuParams, skus)
		}
	}
	pricePercentage := 100
	if params["pricePercentage"] != nil {
		pricePercentage = params["pricePercentage"].(int)
	}
	errList := errlist.New()
	for _, toStoreID := range toStoreIDs {
		toStore, err := checkStoreExisting(db, toStoreID)
		if err != nil {
			errList.AddErr(err)
			break
		}
		now := time.Now()
		if fromStoreID == toStoreID {
			sql := `
		UPDATE store_sku_bind t1
		JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ?
		JOIN sku_name t3 ON t2.name_id = t3.id AND t2.deleted_at = ?
		LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t2.deleted_at = ?
			SET t1.last_operator = ?,
			t1.updated_at = ?,
			t1.price = t1.price * ? / 100,
			t1.jx_price = t1.jx_price * ? / 100,
			t1.unit_price = t1.unit_price * ? / 100,
			t1.jd_sync_status = t1.jd_sync_status | ?,
			t1.mtwm_sync_status = t1.mtwm_sync_status | ?,
			t1.ebai_sync_status = t1.ebai_sync_status | ?,
			t1.dd_sync_status = t1.dd_sync_status | ? 
		WHERE t1.store_id = ? AND t1.deleted_at = ?
		`
			sqlParams := []interface{}{
				utils.DefaultTimeValue,
				utils.DefaultTimeValue,
				utils.DefaultTimeValue,
				userName,
				now,
				pricePercentage,
				pricePercentage,
				pricePercentage,
				model.SyncFlagPriceMask,
				model.SyncFlagPriceMask,
				model.SyncFlagPriceMask,
				model.SyncFlagPriceMask,
				toStoreID,
				utils.DefaultTimeValue,
			}
			sql += sqlCatAndSku
			sqlParams = append(sqlParams, sqlCatAndSkuParams)
			num2, err2 := dao.ExecuteSQL(db, sql, sqlParams)
			if err2 != nil {
				errList.AddErr(err2)
			} else {
				num += num2
			}
			break
		}
		txDB, _ := dao.Begin(db)
		defer func() {
			if r := recover(); r != nil {
				dao.Rollback(db, txDB)
				panic(r)
			}
		}()
		if copyMode == CopyStoreSkuModeFresh {
			// 将toStore中存在，但fromStore中不存在的置删除标志
			sqlDelete := `
		UPDATE store_sku_bind t1
		LEFT JOIN store_sku_bind t0 ON t0.store_id = ? AND t0.sku_id = t1.sku_id AND t0.deleted_at = ?
		JOIN sku t2 ON t1.sku_id = t2.id
		JOIN sku_name t3 ON t2.name_id = t3.id
		LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t2.deleted_at = ?
		SET
			t1.deleted_at = ?,
			t1.updated_at = ?,
			t1.last_operator = ?,
			t1.status = ?,
			t1.jd_sync_status = IF((t1.jd_sync_status & ?) <> 0, 0, ?),
			t1.mtwm_sync_status = IF((t1.mtwm_sync_status & ?) <> 0, 0, ?),
			t1.ebai_sync_status = IF((t1.ebai_sync_status & ?) <> 0, 0, ?),
			t1.dd_sync_status = IF((t1.dd_sync_status & ?) <> 0, 0, ?)
		WHERE t1.store_id = ? AND t1.deleted_at = ? AND t0.id IS NULL
		`
			sqlDeleteParams := []interface{}{
				fromStoreID,
				utils.DefaultTimeValue,
				utils.DefaultTimeValue,
				now,
				now,
				userName,
				model.StoreSkuBindStatusDeleted,
				model.SyncFlagNewMask,
				model.SyncFlagDeletedMask,
				model.SyncFlagNewMask,
				model.SyncFlagDeletedMask,
				model.SyncFlagNewMask,
				model.SyncFlagDeletedMask,
				model.SyncFlagNewMask,
				model.SyncFlagDeletedMask,
				toStoreID,
				utils.DefaultTimeValue,
			}
			sqlDelete += sqlCatAndSku
			sqlDeleteParams = append(sqlDeleteParams, sqlCatAndSkuParams)
			num2, err2 := dao.ExecuteSQL(db, sqlDelete, sqlDeleteParams)
			if err = err2; err != nil {
				errList.AddErr(err)
				dao.Rollback(db, txDB)
				break
			}
			num += num2
		}
		isModifyStatus := 1
		syncStatus := model.SyncFlagStoreSkuOnlyMask
		if copyMode == CopyStoreSkuModeUpdatePrice {
			isModifyStatus = 0
			syncStatus = model.SyncFlagPriceMask
		}
		scaleFactor := float64(1)
		if isScale {
			fromPayPercentage := jxutils.ConstrainPayPercentage(fromStore.PayPercentage)
			toPayPercentage := jxutils.ConstrainPayPercentage(toStore.PayPercentage)
			scaleFactor = float64(fromPayPercentage) / float64(toPayPercentage)
		}
		// 处理toStore中与fromStore中都存在的
		sql := `
	UPDATE store_sku_bind t1
	JOIN store t11 ON t11.id = t1.store_id
	LEFT JOIN store_sku_bind t0 ON t0.store_id = ? AND t0.sku_id = t1.sku_id AND t0.deleted_at = ?
	JOIN store t01 ON t01.id = t0.store_id
	JOIN sku t2 ON t1.sku_id = t2.id/* AND t2.deleted_at = ?*/
	JOIN sku_name t3 ON t2.name_id = t3.id/* AND t3.deleted_at = ?*/
	LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ?
	SET
		t1.last_operator = ?,
		t1.updated_at = ?,
		t1.sub_store_id = 0,
		t1.price = IF(t0.price * ? / 100 > 0, t0.price * ? / 100, 1) * ?,
		t1.jx_price = IF(t0.jx_price * ? / 100 > 0, t0.jx_price * ? / 100, 1) * ?,
		t1.unit_price = IF(t0.unit_price * ? / 100 > 0, t0.unit_price * ? / 100, 1) * ?,
		t1.status = IF(? = 0, t1.status, t0.status),
		t1.jd_sync_status = t1.jd_sync_status | ?,
		t1.mtwm_sync_status = t1.mtwm_sync_status | ?,
		t1.ebai_sync_status = t1.ebai_sync_status | ?,
		t1.dd_sync_status = t1.dd_sync_status | ? ,
		t1.stock = t0.stock 
	WHERE t1.store_id = ? AND t1.deleted_at = ? AND t0.id IS NOT NULL
	`
		sqlParams := []interface{}{
			fromStoreID,
			utils.DefaultTimeValue,
			// utils.DefaultTimeValue,
			// utils.DefaultTimeValue,
			utils.DefaultTimeValue,
			userName,
			now,
			pricePercentage,
			pricePercentage,
			scaleFactor,
			pricePercentage,
			pricePercentage,
			scaleFactor,
			pricePercentage,
			pricePercentage,
			scaleFactor,
			isModifyStatus,
			syncStatus,
			syncStatus,
			syncStatus,
			syncStatus,
			toStoreID,
			utils.DefaultTimeValue,
		}
		sql += sqlCatAndSku
		sqlParams = append(sqlParams, sqlCatAndSkuParams)
		num2, err2 := dao.ExecuteSQL(db, sql, sqlParams)
		if err = err2; err != nil {
			errList.AddErr(err)
			dao.Rollback(db, txDB)
			break
		}
		num += num2
		// 添加toStore中不存在，但fromStore存在的
		sql = `
	INSERT INTO store_sku_bind(created_at, updated_at, last_operator, deleted_at, store_id, sku_id, sub_store_id, price, jx_price, unit_price, status,
	jd_sync_status, ebai_sync_status, mtwm_sync_status,dd_sync_status, stock)
	SELECT ?, ?, ?, ?, ?,
	t1.sku_id, 0,
	IF(t1.price * ? / 100 > 0, t1.price * ? / 100, 1) * ?,
	IF(t1.jx_price * ? / 100 > 0, t1.jx_price * ? / 100, 1) * ?,
	IF(t1.unit_price * ? / 100 > 0, t1.unit_price * ? / 100, 1) * ?,
	IF(? = 0, ?, t1.status), ?, ?, ?, ?, t1.stock
	FROM store_sku_bind t1
	JOIN sku t2 ON t1.sku_id = t2.id AND t2.deleted_at = ?
	JOIN sku_name t3 ON t2.name_id = t3.id AND t3.deleted_at = ?
	LEFT JOIN sku_category t4 ON t3.category_id = t4.id AND t4.deleted_at = ?
	LEFT JOIN store_sku_bind t0 ON t1.sku_id = t0.sku_id AND t0.store_id = ? AND t0.deleted_at = ?
	WHERE t1.store_id = ? AND t1.deleted_at = ?
	`
		sqlParams = []interface{}{
			now, now, userName, utils.DefaultTimeValue, toStoreID,
			pricePercentage,
			pricePercentage,
			scaleFactor,
			pricePercentage,
			pricePercentage,
			scaleFactor,
			pricePercentage,
			pricePercentage,
			scaleFactor,
			isModifyStatus,
			model.SkuStatusDontSale,
			model.SyncFlagNewMask,
			model.SyncFlagNewMask,
			model.SyncFlagNewMask,
			model.SyncFlagNewMask,
			utils.DefaultTimeValue,
			utils.DefaultTimeValue,
			utils.DefaultTimeValue,
			toStoreID,
			utils.DefaultTimeValue,
			fromStoreID,
			utils.DefaultTimeValue,
		}
		sql += sqlCatAndSku + " AND t0.id IS NULL"
		sqlParams = append(sqlParams, sqlCatAndSkuParams)
		num2, err = dao.ExecuteSQL(db, sql, sqlParams)
		if err != nil {
			errList.AddErr(err)
			dao.Rollback(db, txDB)
			break
		}
		num += num2
		//上方insert会无视目标门店中未关注的商品（以前关注，后来取消关注），所以这里批量删一下
		sql2 := `
		DELETE FROM store_sku_bind
		WHERE store_id = ? AND sku_id IN (
			SELECT b.sku_id FROM (
				SELECT store_id,sku_id,count(*)  
				FROM store_sku_bind 
				WHERE store_id = ?
				GROUP BY 1,2
				HAVING count(*) > 1)b
			)
			AND deleted_at <> ?
		`
		sqlParams2 := []interface{}{
			toStoreID, toStoreID, utils.DefaultTimeValue,
		}
		_, err = dao.ExecuteSQL(db, sql2, sqlParams2)
		if err != nil {
			errList.AddErr(err)
			dao.Rollback(db, txDB)
			break
		}
		dao.Commit(db, txDB)

		//同一商品若源门店的规格少于要复制到的门店，则在复制的门店里的其他规格的unitprice不会变，
		//导致复制的门店的商品unitprice不尽相同，这里先处理一下
		type tStore struct {
			NameID  int `orm:"column(name_id)"`
			StoreID int `orm:"column(store_id)"`
		}
		var resultList []*tStore
		sql3 := `
			SELECT a.name_id, a.store_id FROM (
				SELECT DISTINCT a.unit_price, b.name_id, a.store_id FROM store_sku_bind a, sku b, store c 
				WHERE a.sku_id = b.id
				AND c.id = a.store_id 
				AND c.deleted_at = ?
				AND a.store_id = ?
				AND a.deleted_at = ?)a
				GROUP BY 1, 2
				HAVING COUNT(a.unit_price) > 1
			`
		sqlParams3 := []interface{}{utils.DefaultTimeValue, toStoreID, utils.DefaultTimeValue}
		err = dao.GetRows(db, &resultList, sql3, sqlParams3)
		if len(resultList) > 0 {
			var skuBindInfos []*StoreSkuBindInfo
			for _, v := range resultList {
				storeSkus, _ := dao.GetStoreSkusByNameIDs(db, []int{v.StoreID}, v.NameID)
				unitPirce := storeSkus[0].UnitPrice
				skuBindInfo := &StoreSkuBindInfo{
					StoreID:   v.StoreID,
					NameID:    v.NameID,
					UnitPrice: int(unitPirce),
				}
				skuBindInfos = append(skuBindInfos, skuBindInfo)
			}
			_, err = updateStoresSkusWithoutSync(ctx, db, []int{toStoreID}, skuBindInfos, false, false)
		}
	}
	if globals.IsAddEvent {
		mapAfter := make(map[string]interface{})
		mapAfter["ToStoreIDs"] = toStoreIDs
		mapAfter["CopyMode"] = copyMode
		mapAfter["IsScale"] = isScale
		mapAfter["PricePercentage"] = pricePercentage
		mapBefore := make(map[string]interface{})
		mapBefore["FromStoreID"] = fromStoreID
		err = AddEventDetail(db, ctx, model.OperateCopyStoreSkus, 0, model.ThingTypeSku, fromStoreID, BuildDiffData(mapBefore), BuildDiffData(mapAfter))
	}
	return num, errList.GetErrListAsOne()
}

func shouldPendingStorePriceChange(ctx *jxcontext.Context, storeID int, skuBindInfo *StoreSkuBindInfo) (shouldPending bool, err error) {
	if globals.EnablePendingChange {
		if skuBindInfo.IsFocus != 1 && (ctx.GetLoginType() == weixin.AuthTypeMP || ctx.GetLoginType() == weixin.AuthTypeMini || ctx.GetUserName() == "fakeboss") {
			db := dao.GetDB()
			store := &model.Store{}
			store.ID = storeID
			if err = dao.GetEntity(db, store); err != nil {
				return false, err
			}
			return jxutils.TranslateStorePriceType(store.ChangePriceType) == model.StoreChangePriceTypeNeedApprove, nil
		}
	}
	return false, nil
}

func filterStorePriceChange(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (filteredStoreIDs []int, filteredSkuBindInfos []*StoreSkuBindInfo, err error) {
	if globals.EnablePendingChange {
		db := dao.GetDB()
		txDB, _ := dao.Begin(db)
		defer dao.Rollback(db, txDB)
		for _, storeID := range storeIDs {
			for _, skuBindInfo := range skuBindInfos {
				shouldPending, err2 := shouldPendingStorePriceChange(ctx, storeID, skuBindInfo)
				if err = err2; err != nil {
					return nil, nil, err
				}
				if shouldPending && (skuBindInfo.UnitPrice != 0 || skuBindInfo.IsFocus == 1) {
					var (
						opInfoList []*StoreOpRequestInfo
						sql        string
						sqlParams  []interface{}
						opType     int8
					)

					if skuBindInfo.IsFocus == 1 {
						opType = model.RequestTypeFocusSkuName
						sql = `
						SELECT DISTINCT t1.*, t3.unit_price
						FROM store_op_request t1
						JOIN sku t2 ON t2.name_id = t1.item_id AND t2.deleted_at = ?
						LEFT JOIN store_sku_bind t3 ON t3.store_id = t1.store_id AND t3.sku_id = t2.id AND t3.deleted_at = ?
						WHERE t1.store_id = ? AND t1.item_id = ? AND t1.deleted_at = ? AND t1.type IN (?, ?)`
						sqlParams = []interface{}{
							utils.DefaultTimeValue,
							utils.DefaultTimeValue,
							storeID,
							skuBindInfo.NameID,
							utils.DefaultTimeValue,
							model.RequestTypeChangePrice,
							model.RequestTypeFocusSkuName,
						}
					} else {
						opType = model.RequestTypeChangePrice
						sql = `
						SELECT DISTINCT t3.*, t1.unit_price
						FROM store_sku_bind t1
						JOIN sku t2 ON t2.id = t1.sku_id AND t2.name_id = ? AND t2.deleted_at = ?
						LEFT JOIN store_op_request t3 ON t3.store_id = t1.store_id AND t3.item_id = t2.name_id AND t3.deleted_at = ? AND t3.type IN (?, ?)
						WHERE t1.store_id = ? AND t1.deleted_at = ?`
						sqlParams = []interface{}{
							skuBindInfo.NameID,
							utils.DefaultTimeValue,
							utils.DefaultTimeValue,
							model.RequestTypeChangePrice,
							model.RequestTypeFocusSkuName,
							storeID,
							utils.DefaultTimeValue,
						}
					}

					if err = dao.GetRows(db, &opInfoList, sql, sqlParams...); err != nil {
						return nil, nil, err
					}
					if len(opInfoList) > 1 {
						panic(fmt.Sprintf("filterStorePriceChange more than one row, storeID:%d, skuBindInfo:%s, result:%s", storeID, utils.Format4Output(skuBindInfo, false), utils.Format4Output(opInfoList, false)))
					}
					existRow := len(opInfoList) == 1
					existRequestOp := existRow && opInfoList[0].ID != 0
					var changeReq *model.StoreOpRequest
					if existRequestOp {
						if opInfoList[0].Type != opType {
							return nil, nil, fmt.Errorf("filterStorePriceChange，关注与修改价格只应该存在一个待审核请求，已存在的:%s，新来的,storeID:%d, skuBindInfo:%s", utils.Format4Output(opInfoList[0], false), storeID, utils.Format4Output(skuBindInfo, false))
						}
						changeReq = &opInfoList[0].StoreOpRequest
					} else {
						changeReq = &model.StoreOpRequest{}
					}
					if existRequestOp && opInfoList[0].UnitPrice == skuBindInfo.UnitPrice {
						changeReq.Status = model.RequestStatusCanceled
						changeReq.DeletedAt = time.Now()
					} else {
						changeReq.Status = model.RequestStatusNew
						changeReq.Type = opType
						changeReq.StoreID = storeID
						changeReq.ItemID = skuBindInfo.NameID
						changeReq.UserID = ctx.GetUserName()
						changeReq.IntParam1 = skuBindInfo.UnitPrice
						changeReq.IntParam2 = skuBindInfo.IsSale
						if len(skuBindInfo.Skus) > 0 {
							changeReq.JsonParam = string(utils.MustMarshal(skuBindInfo.Skus))
						}
					}
					if existRequestOp {
						dao.WrapUpdateULEntity(changeReq, ctx.GetUserName())
						if _, err = dao.UpdateEntity(db, changeReq); err != nil {
							return nil, nil, err
						}
					} else {
						if !existRow || opInfoList[0].UnitPrice != skuBindInfo.UnitPrice {
							dao.WrapAddIDCULDEntity(changeReq, ctx.GetUserName())
							if err = dao.CreateEntity(db, changeReq); err != nil {
								return nil, nil, err
							}
						}
					}
					// 去除价格相关的部分
					if skuBindInfo.IsFocus == 1 {
						skuBindInfo.IsFocus = 0
					}
					skuBindInfo.UnitPrice = 0
				}
			}
		}
		dao.Commit(db, txDB)
	}
	return storeIDs, skuBindInfos, nil
}

func AcceptStoreOpRequests(ctx *jxcontext.Context, reqIDs []int) (err error) {
	if globals.EnablePendingChange {
		if len(reqIDs) > 0 {
			subErrors := make(map[int]error)
			infoMap, err2 := getStoreOpRequestsInfo(reqIDs)
			if err = err2; err != nil {
				return err
			}
			for reqID, op := range infoMap {
				if op.Status == model.RequestStatusNew {
					skuBindInfo := &StoreSkuBindInfo{
						NameID:    op.ItemID,
						UnitPrice: op.IntParam1,
						IsSale:    op.IntParam2,
					}
					if op.Type == model.RequestTypeFocusSkuName {
						skuBindInfo.IsFocus = 1
					}
					if op.JsonParam != "" {
						if err2 = utils.UnmarshalUseNumber([]byte(op.JsonParam), &skuBindInfo.Skus); err2 != nil {
							subErrors[reqID] = err2
						}
					}
					if err2 == nil {
						_, err2 := UpdateStoresSkus(ctx, 0, []int{op.StoreID}, []*StoreSkuBindInfo{skuBindInfo}, false, false, false, false)
						isLocalSucess := true
						if err2 != nil {
							subErrors[reqID] = err2
							if !isSyncError(err2) {
								isLocalSucess = false
							}
						}
						if isLocalSucess {
							weixinmsg.NotifyStoreOpRequestStatus(true, op.StoreID, op.ItemID, jxutils.ComposeSpuName(op.SkuNamePrefix, op.SkuNameName, 0), op.UnitPrice, op.IntParam1, "")
							if err2 := changeStoreOpStatus(ctx, []int{reqID}, model.RequestStatusAccepted, ""); err2 != nil {
								subErrors[reqID] = err2
							}
						}
					}
				}
			}
			if len(subErrors) > 0 {
				errMsg := ""
				for k, v := range subErrors {
					errMsg += fmt.Sprintf("req:%d, error:%s\n", k, v.Error())
				}
				err = errors.New(errMsg)
			}
		}
	}
	return err
}

func RejectStoreOpRequests(ctx *jxcontext.Context, reqIDs []int, rejectReason string) (err error) {
	infoMap, err := getStoreOpRequestsInfo(reqIDs)
	if err != nil {
		return err
	}
	if err = changeStoreOpStatus(ctx, reqIDs, model.RequestStatusRejected, rejectReason); err == nil {
		for _, info := range infoMap {
			weixinmsg.NotifyStoreOpRequestStatus(false, info.StoreID, info.ItemID, jxutils.ComposeSpuName(info.SkuNamePrefix, info.SkuNameName, 0), info.UnitPrice, info.IntParam1, rejectReason)
		}
	}
	return err
}

// 当前些函数只针对type为： RequestTypeChangePrice与RequestTypeFocusSkuName的查询才有效
func GetStoreOpRequests(ctx *jxcontext.Context, fromTime, toTime time.Time, keyword string, storeIDs, itemIDs, typeList, statusList []int, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
	if globals.EnablePendingChange {
		sql := `
		SELECT SQL_CALC_FOUND_ROWS
		t1.id, t1.created_at, t1.updated_at, t1.last_operator, t1.deleted_at,
		t1.type, t1.store_id, t1.item_id, t1.status, t1.user_id, t1.int_param1, t1.int_param2, t1.remark,
		t2.name store_name, t3.prefix sku_name_prefix, t3.name sku_name_name, MAX(IF(t1.status = ?, t5.unit_price, t1.int_param0)) unit_price
		FROM store_op_request t1
		JOIN store t2 ON t1.store_id = t2.id
		JOIN sku_name t3 ON t1.item_id = t3.id AND t3.deleted_at = ?
		JOIN sku t4 ON t3.id = t4.name_id AND t4.deleted_at = ?
		LEFT JOIN store_sku_bind t5 ON t1.store_id = t5.store_id AND t4.id = t5.sku_id AND t5.deleted_at = ?
		WHERE 1 = 1`
		sqlParams := []interface{}{
			model.RequestStatusNew,
			utils.DefaultTimeValue,
			utils.DefaultTimeValue,
			utils.DefaultTimeValue,
		}
		if keyword != "" {
			keywordLike := "%" + keyword + "%"
			sql += " AND ( t2.name LIKE ? OR t3.name LIKE ?"
			sqlParams = append(sqlParams, keywordLike, keywordLike)
			if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
				sql += " OR t1.store_id = ? OR t1.item_id = ?"
				sqlParams = append(sqlParams, keywordInt64, keywordInt64)
			}
			sql += ")"
		}
		if fromTime != utils.DefaultTimeValue {
			sql += " AND t1.created_at >= ?"
			sqlParams = append(sqlParams, fromTime)
		}
		if toTime != utils.DefaultTimeValue {
			sql += " AND t1.created_at <= ?"
			sqlParams = append(sqlParams, toTime)
		}
		if len(storeIDs) > 0 {
			sql += " AND t1.store_id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
			sqlParams = append(sqlParams, storeIDs)
		}
		if len(itemIDs) > 0 {
			sql += " AND t1.item_id IN (" + dao.GenQuestionMarks(len(itemIDs)) + ")"
			sqlParams = append(sqlParams, itemIDs)
		}
		if len(typeList) > 0 {
			sql += " AND t1.type IN (" + dao.GenQuestionMarks(len(typeList)) + ")"
			sqlParams = append(sqlParams, typeList)
		}
		if len(statusList) > 0 {
			sql += " AND t1.status IN (" + dao.GenQuestionMarks(len(statusList)) + ")"
			sqlParams = append(sqlParams, statusList)
		}

		sql += `
		GROUP BY 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
		ORDER BY 1 DESC
		LIMIT ? OFFSET ?`
		pageSize = jxutils.FormalizePageSize(pageSize)
		sqlOffset := offset
		sqlPageSize := pageSize
		sqlParams = append(sqlParams, sqlPageSize, sqlOffset)
		db := dao.GetDB()
		var requestList []*StoreOpRequestInfo
		txDB, _ := dao.Begin(db)
		defer dao.Commit(db, txDB)
		if err = dao.GetRowsTx(txDB, &requestList, sql, sqlParams...); err == nil {
			return &model.PagedInfo{
				TotalCount: dao.GetLastTotalRowCount2(db, txDB),
				Data:       requestList,
			}, nil
		}
	}
	return nil, err
}

func getStoreOpRequestsInfo(reqIDs []int) (infoMap map[int]*StoreOpRequestInfo, err error) {
	infoMap = make(map[int]*StoreOpRequestInfo)
	if len(reqIDs) > 0 {
		sql := `
		SELECT DISTINCT t1.*, t4.name store_name, t2.prefix sku_name_prefix, t2.name sku_name_name, t3.unit_price
		FROM store_op_request t1
		JOIN sku_name t2 ON t2.id = t1.item_id
		JOIN sku t5 ON t5.name_id = t1.item_id AND t5.deleted_at = ?
		LEFT JOIN store_sku_bind t3 ON t3.store_id = t1.store_id AND t3.sku_id = t5.id AND t3.deleted_at = ?
		JOIN store t4 ON t4.id = t1.store_id
		WHERE t1.id IN (` + dao.GenQuestionMarks(len(reqIDs)) + ")"
		sqlParams := []interface{}{
			utils.DefaultTimeValue,
			utils.DefaultTimeValue,
			reqIDs,
		}
		db := dao.GetDB()
		var infoList []*StoreOpRequestInfo
		if err = dao.GetRows(db, &infoList, sql, sqlParams...); err == nil {
			for _, v := range infoList {
				infoMap[v.ID] = v
			}
			if len(reqIDs) > len(infoMap) {
				missingReqIDs := []int{}
				for _, reqID := range reqIDs {
					if infoMap[reqID] == nil {
						missingReqIDs = append(missingReqIDs, reqID)
					}
				}
				err = fmt.Errorf("不能找到如下的请求ID:%s", utils.Format4Output(missingReqIDs, true))
			}
		}
	} else {
		err = errors.New("没有找到指定的请求ID")
	}
	return infoMap, err
}

func changeStoreOpStatus(ctx *jxcontext.Context, reqIDs []int, status int8, rejectReason string) (err error) {
	if globals.EnablePendingChange {
		if len(reqIDs) > 0 {
			infoMap, err2 := getStoreOpRequestsInfo(reqIDs)
			if err = err2; err != nil {
				return err
			}
			db := dao.GetDB()
			txDB, _ := dao.Begin(db)
			defer dao.Rollback(db, txDB)
			for _, reqID := range reqIDs {
				op := &model.StoreOpRequest{}
				op.Remark = rejectReason
				op.Status = status
				if infoMap[reqID] != nil {
					op.IntParam0 = infoMap[reqID].UnitPrice
				}
				dao.WrapUpdateULEntity(op, ctx.GetUserName())
				op.DeletedAt = time.Now()
				op.ID = reqID
				if _, err = dao.UpdateEntity(db, op, "IntParam0", "Remark", "Status", "DeletedAt", "LastOperator", "UpdatedAt"); err != nil {
					return err
				}
			}
			dao.Commit(db, txDB)
		}
	}
	return err
}

func setStoreSkuBindStatus(skuBind *model.StoreSkuBind, status int8) {
	skuBind.JdSyncStatus |= status
	skuBind.EbaiSyncStatus |= status
	skuBind.MtwmSyncStatus |= status
	skuBind.TaoSyncStatus |= status
	skuBind.YbSyncStatus |= status
	skuBind.JdsSyncStatus |= status
	skuBind.DdSyncStatus |= status
}

func checkStoreExisting(db *dao.DaoDB, storeID int) (store *model.Store, err error) {
	store = &model.Store{}
	store.ID = storeID
	if err = dao.GetEntity(db, store); err != nil {
		if err == orm.ErrNoRows {
			return nil, fmt.Errorf("门店：%d不存在", storeID)
		}
		return nil, err
	}
	return store, nil
}

func RefreshStoresSkuByVendor(ctx *jxcontext.Context, storeIDs []int, vendorID int, isAsync bool) (hint string, err error) {
	if vendorID != model.VendorIDJD {
		return "", fmt.Errorf("此功能当前只支持京东到家平台")
	}
	db := dao.GetDB()
	storeMapList, err := dao.GetStoresMapList(db, []int{vendorID}, storeIDs, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	if err != nil {
		return "", err
	}
	if len(storeMapList) != len(storeIDs) {
		return "", fmt.Errorf("门店绑定信息不匹配，请确定门店绑定且只绑定了京东平台")
	}
	storeMap := make(map[int]*dao.StoreDetail)
	for _, v := range storeMapList {
		if v.VendorID != vendorID {
			return "", fmt.Errorf("门店%d绑定的不（只）是京东", v.StoreID)
		}
		storeMap[v.StoreID], err = dao.GetStoreDetail(db, v.StoreID, v.VendorID, "")
		if err != nil {
			return "", err
		}
	}
	skuList, err := dao.GetSkusWithVendor(db, []int{vendorID}, nil, nil, nil, false)
	if err != nil {
		return "", err
	}
	skuNameMap := make(map[int]*model.SkuName)
	skuMap := make(map[int]*dao.StoreSkuSyncInfo)
	bareStoreSkuMap := make(map[string][]*partner.StoreSkuInfo)
	for _, sku := range skuList {
		if skuNameMap[sku.NameID] == nil {
			skuNameMap[sku.NameID] = &model.SkuName{
				Unit: sku.Unit,
			}
		}
		skuMap[sku.ID] = sku

		bareStoreSkuMap[sku.VendorOrgCode] = append(bareStoreSkuMap[sku.VendorOrgCode], &partner.StoreSkuInfo{
			SkuID:       sku.ID,
			VendorSkuID: sku.VendorSkuID,
		})
	}

	handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler)
	var storeSkuList []*model.StoreSkuBind
	rootTask := tasksch.NewParallelTask(fmt.Sprintf("根据厂家门店商品信息相应刷新本地数据:%v", storeIDs), tasksch.NewParallelConfig().SetParallelCount(1), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			oneStoreMap := batchItemList[0].(*model.StoreMap)
			subTask := tasksch.NewSeqTask(fmt.Sprintf("根据厂家门店商品信息相应刷新本地数据:%s", model.VendorChineseNames[oneStoreMap.VendorID]), ctx,
				func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
					switch step {
					case 0:
						bareStoreSkuList, err2 := handler.GetStoreSkusBareInfo(ctx, oneStoreMap.VendorOrgCode, task, oneStoreMap.StoreID, oneStoreMap.VendorStoreID, bareStoreSkuMap[oneStoreMap.VendorOrgCode])
						// globals.SugarLogger.Debug(utils.Format4Output(bareStoreSkuList, false))
						if err = err2; err == nil || len(bareStoreSkuList) > 0 {
							err = nil // todo 如果部分失败，强制忽略错误
							for _, v := range bareStoreSkuList {
								storeSkuList = append(storeSkuList, &model.StoreSkuBind{
									StoreID: oneStoreMap.StoreID,
									SkuID:   v.SkuID,
									Status:  v.Status,
									Price:   int(v.VendorPrice),
								})
							}
						}
					case 1:
						if len(storeSkuList) > 0 {
							for _, v := range storeSkuList {
								sku := skuMap[v.SkuID]
								skuName := skuNameMap[sku.NameID]
								if skuName.IsGlobal == 0 && (jxutils.IsSkuSpecial(sku.SpecQuality, sku.SpecUnit) || skuName.Unit != model.SpecialUnit) {
									skuName.Price = v.Price // 标准价
									skuName.IsGlobal = 1
								}
							}
							for _, v := range storeSkuList {
								sku := skuMap[v.SkuID]
								skuName := skuNameMap[sku.NameID]
								if skuName.IsGlobal == 0 {
									if skuName.Price == 0 {
										skuName.Price = jxutils.CaculateUnitPrice(v.Price, sku.SpecQuality, sku.SpecUnit, skuName.Unit)
									} else {
										skuName.Price = (skuName.Price + jxutils.CaculateUnitPrice(v.Price, sku.SpecQuality, sku.SpecUnit, skuName.Unit)) / 2
									}
								}
							}
							for _, v := range storeSkuList {
								pricePercentage, priceAdd := jxutils.GetPricePercentageByVendorPrice(storeMap[v.StoreID].PricePercentagePackObj, v.Price, int(storeMap[v.StoreID].PricePercentage))
								skuName := skuNameMap[skuMap[v.SkuID].NameID]
								v.Price = jxutils.CaculateSkuPriceFromVendor(v.Price, pricePercentage, priceAdd)
								v.UnitPrice = jxutils.CaculateSkuPriceFromVendor(skuName.Price, pricePercentage, priceAdd)
								dao.WrapAddIDCULDEntity(v, ctx.GetUserName())
								setStoreSkuBindStatus(v, model.SyncFlagNewMask)
								v.JdSyncStatus = 0
							}
						}
					case 2:
						if len(storeSkuList) > 0 {
							txDB, _ := dao.Begin(db)
							defer func() {
								if r := recover(); r != nil || err != nil {
									dao.Rollback(db, txDB)
									if r != nil {
										panic(r)
									}
								}
							}()
							if _, err = dao.ExecuteSQL(db, `
							DELETE t1
							FROM store_sku_bind t1
							WHERE t1.store_id IN (
							`+dao.GenQuestionMarks(len(storeIDs))+")", storeIDs); err == nil {
								if err = dao.CreateMultiEntities(db, storeSkuList); err == nil {
									hint = utils.Int2Str(len(storeSkuList))
									dao.Commit(db, txDB)
								}
							}
						}
					}
					return nil, err
				}, 3)
			tasksch.HandleTask(subTask, task, true).Run()
			_, err = subTask.GetResult(0)
			return retVal, err
		}, storeMapList)
	tasksch.ManageTask(rootTask).Run()
	if isAsync {
		hint = rootTask.GetID()
	} else {
		_, err = rootTask.GetResult(0)
	}
	return hint, err
}

func GetVendorStoreSkusInfo(ctx *jxcontext.Context, storeID int, vendorIDs, skuIDs []int, isContinueWhenError bool) (skuVendorMap map[int][]*partner.StoreSkuInfo, err error) {
	db := dao.GetDB()
	var locker sync.RWMutex
	skuVendorMap = make(map[int][]*partner.StoreSkuInfo)
	_, err = CurVendorSync.LoopStoresMap(ctx, db, fmt.Sprintf("GetVendorStoreSkusInfo storeID:%d", storeID), false, false, vendorIDs, []int{storeID},
		func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (interface{}, error) {
			loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
			if handler, _ := partner.GetPurchasePlatformFromVendorID(loopMapInfo.VendorID).(partner.IPurchasePlatformStoreSkuHandler); handler != nil {
				storeSkuList, err2 := dao.GetStoreSkus2(db, loopMapInfo.VendorID, storeID, skuIDs, false)
				if err = err2; err == nil && len(storeSkuList) > 0 {
					bareStoreSkuInfoList := make([]*partner.StoreSkuInfo, len(skuIDs))
					for k, v := range storeSkuList {
						bareStoreSkuInfoList[k] = &partner.StoreSkuInfo{
							SkuID:       v.SkuID,
							VendorSkuID: v.VendorSkuID,
						}
					}
					outBareStoreSkuInfoList, err2 := handler.GetStoreSkusBareInfo(ctx, loopMapInfo.StoreMapList[0].VendorOrgCode, t, loopMapInfo.StoreMapList[0].StoreID, loopMapInfo.StoreMapList[0].VendorStoreID, bareStoreSkuInfoList)
					if err = err2; err == nil && outBareStoreSkuInfoList != nil {
						locker.Lock()
						defer locker.Unlock()
						skuVendorMap[loopMapInfo.VendorID] = outBareStoreSkuInfoList
					}
				}
			}
			return nil, err
		}, true)
	if err != nil {
		skuVendorMap = nil
	}
	return skuVendorMap, err
}

type VendorStoreSkus struct {
	SkuID        int      `json:"商品ID"` // 两平台共同包含的ID 桥梁
	SkuName      string   `json:"商品名"`
	Status       int      `json:"上下架状态"`
	CategoryList []string `json:"商品类别列表"`
	Price        int64    `json:"售价"`
	Reason       string   `json:"原因"`
}

//@creat by 2020-03-09
//@Description :用于获取第三方平台一个门店下所有的商品数据
func GetVendorStoreSkusToStruct(ctx *jxcontext.Context, storeID, vendorID int) (err error) {

	var (
		db              = dao.GetDB()
		vendorStoreSkus []*VendorStoreSkus
		excelTitle      = []string{
			"商品ID",
			"商品名",
			"上下架状态",
			"商品类别列表",
			"售价",
			"原因",
		}
		sheetList             []*excel.Obj2ExcelSheetConfig
		downloadURL, fileName string
	)
	storeDetail, err := dao.GetStoreDetail(db, storeID, vendorID, "")
	if err != nil || storeDetail == nil {
		return err
	}
	if partner.IsMultiStore(vendorID) {
		// handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler)
		// skuBareInfoList, _ := handler.GetStoreSkusBareInfo(ctx, storeDetail.VendorOrgCode, nil, storeID, storeDetail.VendorStoreID, nil)
		return fmt.Errorf("暂不支持京东！")
	} else {
		handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreStoreSkuHandler)
		// 使用处理器获取后台所有的商品 存入一个Slice中
		skuList, _ := handler.GetStoreSkusFullInfo(ctx, nil, storeID, storeDetail.VendorStoreID, nil)
		// 设置一个默认原因
		var defaultReason = "不存在该商品待同步"
		for _, sku := range skuList {
			skus := &VendorStoreSkus{
				SkuID:        sku.NameID,
				SkuName:      sku.Name,
				Status:       sku.Status,
				CategoryList: sku.VendorCatIDList,
				Price:        sku.SkuList[0].VendorPrice,
				Reason:       defaultReason,
			}
			vendorStoreSkus = append(vendorStoreSkus, skus)
		}
	}

	tmpList, err := GetLocalStoreSkusByStoreID(storeID)
	if err != nil {
		globals.SugarLogger.Debug(err)
	} else {
		if len(tmpList) == 0 { //数据库中无匹配项 直接将第三方数据进行导出
			goto creatExec
		} else { //进行比较
			if result := ComPareLocalWithVendors(tmpList, vendorStoreSkus, vendorID); result != nil {
				// 将返回的切片进行覆盖
				vendorStoreSkus = result
				goto creatExec
			}
		}
	}
creatExec:
	excelConf := &excel.Obj2ExcelSheetConfig{
		Title:       "sheet1",
		Data:        vendorStoreSkus,
		CaptionList: excelTitle,
	}
	sheetList = append(sheetList, excelConf)
	if excelConf != nil {
		downloadURL, fileName, err = jxutils.UploadExeclAndPushMsg(sheetList, strconv.Itoa(storeID)+"diff")
	} else {
		baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!")
	}
	if err != nil {
		baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName, err)
	} else {
		noticeMsg := fmt.Sprintf("[店铺对比结果|详情点我]%s", downloadURL)
		ddmsg.SendUserMessage(dingdingapi.MsgTyeText, ctx.GetUserID(), "异步任务完成", noticeMsg)
		baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess downloadURL: [%v]", downloadURL)
	}
	return err
}

// 通过门店ID 查询数据库对应门店商品管理按SKU查询门店下的所有商品
func GetLocalStoreSkusByStoreID(storeID int) (tmp []*tGetStoresSkusInfoContainCategoryName, err error) {
	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, t2m.vendor_thing_id sku_jd_id,
	t2.comment, t2.category_id sku_category_id, t2.status sku_status, t2.eclp_id,
	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 store_sku_status, t4.auto_sale_at,
	t4.ebai_id, t4.mtwm_id,t4.tao_id,t4.dd_id, t4.yb_id, CONCAT(smm.yb_store_prefix,t1.yb_name_suffix) yb_sku_name, t4.jds_id, t4.jds_ware_id,
	t4.jd_sync_status, t4.ebai_sync_status, t4.mtwm_sync_status, t4.tao_sync_status,  t4.dd_sync_status,t4.yb_sync_status, t4.jds_sync_status,
	t4.jd_price, t4.ebai_price, t4.mtwm_price,t4.dd_price, t4.jx_price, t4.yb_price, t4.jds_price,t4.tao_price,
	t4.jd_lock_time, t4.ebai_lock_time, t4.mtwm_lock_time,t4.dd_lock_time, t4.jx_lock_time, t4.yb_lock_time, t4.jds_lock_time,t4.tao_lock_time,
	t4.status_sale_begin, t4.status_sale_end, t4.stock, t4.mt_ladder_box_price,
	t6.mid_unit_price real_mid_unit_price,
	t7.unit_price audit_unit_price,
    t8.name categoryName
	FROM sku_name t1
	JOIN sku t2 FORCE INDEX(PRIMARY) ON t1.id = t2.name_id AND t2.deleted_at = '1970-01-01 00:00:00 +0800'
	JOIN store t3 ON t3.deleted_at = '1970-01-01 00:00:00 +0800'
	LEFT JOIN store_map sm ON sm.store_id = t3.id AND sm.vendor_id = 0 AND sm.deleted_at = '1970-01-01 00:00:00 +0800'
	LEFT JOIN store_map smm ON smm.store_id = t3.id AND smm.deleted_at = '1970-01-01 00:00:00 +0800' AND smm.vendor_id = 4
	LEFT JOIN thing_map t2m ON t2m.thing_type = 3 AND t2m.thing_id = t2.id AND t2m.vendor_id = sm.vendor_id AND t2m.vendor_org_code = sm.vendor_org_code AND t2m.deleted_at = '1970-01-01 00:00:00 +0800'

	JOIN store_sku_bind t4 ON t4.store_id = t3.id AND t4.sku_id = t2.id AND t4.deleted_at = '1970-01-01 00:00:00 +0800'
	LEFT JOIN sku_name_place_bind t5 ON t1.id = t5.name_id AND t3.city_code = t5.place_code
	LEFT JOIN price_refer_snapshot t6 ON t6.city_code = 0 AND t6.sku_id = t2.id AND t6.snapshot_at = '2099-03-08 00:00:00 +0800'
	LEFT JOIN store_sku_audit t7 ON t7.store_id = t3.id AND t7.name_id = t1.id AND t7.status = 0 AND t7.deleted_at = '1970-01-01 00:00:00 +0800'
    LEFT JOIN sku_category t8 ON t1.category_id= t8.id
	WHERE t1.deleted_at = '1970-01-01 00:00:00 +0800' AND (t1.is_global = 1 OR t5.id IS NOT NULL OR 1 = 1)
	 					  AND ((t2.status = 1 AND t1.status = 1) OR t4.status = 1) AND t3.id IN (?) AND IF(INSTR(t3.name,'饿鲜达') > 0, t2.exd_sku_id <> '', t2.exd_sku_id = '') AND t4.status >= 0 AND t4.status <= 1 AND t4.unit_price <= 99900  AND t4.unit_price >= 0
			ORDER BY  t3.id, t2.name_id, t2.id`
	var tmpList []*tGetStoresSkusInfoContainCategoryName
	txDB, _ := dao.Begin(dao.GetDB())
	if err = dao.GetRowsTx(txDB, &tmpList, sql, storeID); err != nil {
		dao.Rollback(dao.GetDB(), txDB)

	}
	return tmpList, err
}

// 比较本地和第三方平台的差异 返回一个Excel 可接收实体
func ComPareLocalWithVendors(localSku []*tGetStoresSkusInfoContainCategoryName, vendorSkus []*VendorStoreSkus, vendorID int) (result []*VendorStoreSkus) {
	// 进行取参比较
	var localStoreSkus []*VendorStoreSkus
	var updateStoreSkus []*VendorStoreSkus
	var defaultReason = "平台不存在该商品待同步"
	for _, sku := range localSku {
		skus := &VendorStoreSkus{
			SkuID:        sku.StoreSkuExt.SkuID,
			SkuName:      sku.SkuName.Name,
			Status:       sku.Status,
			CategoryList: jxStatusTransForm(sku.CategoryName),              //此处需要使用函数进行转换 进行查询根据类别ID去sku_category查询返回一个子类别放入List中
			Price:        vendorPriceTransForm(&sku.StoreSkuExt, vendorID), //此处需要使用函数转换传入一个VendorID进行比较根据ID的不同返回对应不同的字段
			Reason:       defaultReason,
		}
		localStoreSkus = append(localStoreSkus, skus)
	}
	// 由于go语言中无法子定义struct比较规则 因此需要把数据提取出来进行处理
	// 进行比较处理   对两个slice 进行遍历 放到map中 以 skuID slice的形式进行存储 如果不存在则直接进行put操作 如果存在则进行比较类别，价格，以及上下架状态
	compareMap := make(map[int]*VendorStoreSkus)
	for _, local := range localStoreSkus {
		compareMap[local.SkuID] = local
	}
	for _, vendorSku := range vendorSkus {
		if value, ok := compareMap[vendorSku.SkuID]; ok { //进行逻辑处理
			// 进行比较  如果同时满足价格状态以及末级类别 则进行remove操作 否则什么也不做
			if value.Price == vendorSku.Price && value.Status == vendorSku.Status && value.CategoryList[0] == vendorSku.CategoryList[len(vendorSku.CategoryList)-1] {
				delete(compareMap, vendorSku.SkuID)
			} else { //对原因进行赋值并展示出来
				//需要考虑是否存在多种情况并存的情况可以更直观的看出差异所在
				var buffer bytes.Buffer
				var count = 1
				if value.Price != vendorSku.Price {
					buffer.WriteString(strconv.Itoa(count))
					buffer.WriteString(".")
					buffer.WriteString("价格不同步，平台价格:" + strconv.FormatInt(vendorSku.Price, 10))
					buffer.WriteString("   ")
					count++
				}
				if value.Status != vendorSku.Status {
					buffer.WriteString(strconv.Itoa(count))
					buffer.WriteString(".")
					buffer.WriteString("商品上下架状态不一致")
					buffer.WriteString("   ")
					count++
				}
				if vendorSku.CategoryList != nil { // Ebai 同步过得商品中的类别中都是nil
					if value.CategoryList[0] != vendorSku.CategoryList[len(vendorSku.CategoryList)-1] {
						buffer.WriteString(strconv.Itoa(count))
						buffer.WriteString(".")
						buffer.WriteString("类别不一致：平台类别为" + vendorSku.CategoryList[len(vendorSku.CategoryList)-1])
						count++
					}
				}
				value.Reason = buffer.String()
			}
		}
	}
	for _, v := range compareMap {
		updateStoreSkus = append(updateStoreSkus, v)
	}
	return updateStoreSkus
}

func jxStatusTransForm(categoryName string) []string {
	var result []string
	result = append(result, categoryName)
	return result
}

// 根据不同平台判断返回不同的价格类型
func vendorPriceTransForm(storeSkuExt *dao.StoreSkuExt, vendorID int) int64 {
	switch vendorID {
	case model.VendorIDMTWM:
		return int64(storeSkuExt.MtwmPrice)
	case model.VendorIDJD:
		return int64(storeSkuExt.JdPrice)
	case model.VendorIDEBAI:
		return int64(storeSkuExt.EbaiPrice)
	case model.VendorIDYB:
		return int64(storeSkuExt.YbPrice)
	case model.VendorIDJDShop:
		return int64(storeSkuExt.JdsPrice)
	case model.VendorIDDD:
		return int64(storeSkuExt.DdPrice)
	case model.VendorIDTaoVegetable:
		return int64(storeSkuExt.TaoPrice)
	default:
		return 0
	}
}
func SyncJdStoreProducts(ctx *jxcontext.Context, storeIDs, skuIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
	db := dao.GetDB()
	isManageIt := len(storeIDs) != 1 || len(skuIDs) == 0 || len(skuIDs) > 8
	hint, err = CurVendorSync.LoopStoresMap(ctx, db, fmt.Sprintf("京东商家商品状态同步:%v", storeIDs), isAsync, isManageIt, []int{model.VendorIDJD}, storeIDs,
		func(t *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			loopMapInfo := batchItemList[0].(*LoopStoreMapInfo)
			if handler := partner.GetPurchasePlatformFromVendorID(loopMapInfo.VendorID); handler != nil {
				jdHandler := handler.(*jd.PurchaseHandler)
				hint, err2 := jdHandler.SyncStoreProducts(ctx, loopMapInfo.StoreMapList[0].VendorOrgCode, t, loopMapInfo.StoreMapList[0].StoreID, skuIDs, false, isContinueWhenError)
				if err = err2; err == nil {
					retVal = []interface{}{hint}
				}
			}
			return retVal, partner.AddVendorInfo2Err(err, loopMapInfo.VendorID)
		}, isContinueWhenError)
	return hint, err
}

func GetMissingStoreSkuFromOrder(ctx *jxcontext.Context, fromTime time.Time) (missingList []*StoreSkuBindInfo, err error) {
	storeSkuList, err := dao.GetMissingStoreSkuFromOrder(dao.GetDB(), nil, fromTime)
	if err == nil {
		storeSkuNameMap := make(map[int64]*StoreSkuBindInfo)
		for _, v := range storeSkuList {
			skuName := storeSkuNameMap[jxutils.Combine2Int(v.StoreID, v.NameID)]
			if skuName == nil {
				skuName = &StoreSkuBindInfo{
					StoreID: v.StoreID,
					NameID:  v.NameID,
					IsFocus: 1,
					IsSale:  1,
					// 这里没有考虑平台价格比例
					UnitPrice: jxutils.CaculateUnitPrice(v.RefPrice, v.SpecQuality, v.SpecUnit, v.Unit),
				}
				missingList = append(missingList, skuName)
			}
			skuName.Skus = append(skuName.Skus, &StoreSkuBindSkuInfo{
				SkuID: v.SkuID,
			})
		}
	}
	return missingList, err
}

func AutoSaleStoreSku(ctx *jxcontext.Context, storeIDs []int, isNeedSync bool) (err error) {
	db := dao.GetDB()
	storeSkuList, err := dao.GetAutoSaleStoreSku(db, storeIDs)
	if err != nil {
		return err
	}
	storeSkuMap := make(map[int][]*model.StoreSkuBind)
	for _, v := range storeSkuList {
		storeSkuMap[v.StoreID] = append(storeSkuMap[v.StoreID], v)
	}
	for storeID, storeSkuList := range storeSkuMap {
		var skuIDs []int
		for _, storeSku := range storeSkuList {
			storeSku.AutoSaleAt = utils.DefaultTimeValue
			if storeSku.Status != model.SkuStatusNormal {
				storeSku.Status = model.SkuStatusNormal
				skuIDs = append(skuIDs, storeSku.SkuID)
			}
			if _, err = dao.UpdateEntity(db, storeSku, "AutoSaleAt", model.FieldStatus); err != nil {
				return err
			}
		}
		if isNeedSync && len(skuIDs) > 0 {
			if _, err = CurVendorSync.SyncStoresSkus(ctx, nil, model.SyncFlagSaleMask, db, nil, []int{storeID}, skuIDs, true, true, true); err != nil {
				return err
			}
		}
	}
	return err
}

func ReCalculateJxPrice(db *dao.DaoDB, ctx *jxcontext.Context, storeIDs []int) (hint string, err error) {
	task := tasksch.NewParallelTask("刷新京西平台价格", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeID := batchItemList[0].(int)
			if storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJX, ""); err == nil {
				if storeSkuList, err := dao.GetStoresSkusInfo(db, []int{storeID}, nil); err == nil {
					for _, skuBind := range storeSkuList {
						skuBind.JxPrice = jxutils.CaculatePriceByPricePack(storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage), skuBind.Price)
						dao.UpdateEntity(db, skuBind, "JxPrice")
					}
				} else {
					return nil, err
				}
			} else {
				return nil, err
			}
			return retVal, err
		}, storeIDs)
	tasksch.HandleTask(task, nil, true).Run()
	hint = task.GetID()
	return hint, err
}

func ReCalculateJxPriceLight(db *dao.DaoDB, ctx *jxcontext.Context, storeID int) (err error) {
	if storeID != 0 {
		if storeDetail, err := dao.GetStoreDetail(db, storeID, model.VendorIDJX, ""); err == nil {
			if storeSkuList, err := dao.GetStoresSkusInfo(db, []int{storeID}, nil); err == nil {
				for _, skuBind := range storeSkuList {
					skuBind.JxPrice = jxutils.CaculatePriceByPricePack(storeDetail.PricePercentagePackObj, int(storeDetail.PricePercentage), skuBind.Price)
					dao.UpdateEntity(db, skuBind)
				}
			} else {
				return err
			}
		} else {
			return err
		}
	}
	return err
}

func GetTopSkusByStoreIDs(ctx *jxcontext.Context, storeIDs []int) (storeSkuNameExt2 []*dao.StoreSkuNameExt, err error) {
	var (
		db              = dao.GetDB()
		skuMap          = make(map[int]*dao.StoreSkuNameExt)
		storeSkuNameExt = make([]*dao.StoreSkuNameExt, 0, 0)
	)
	if len(storeIDs) == 0 {
		return storeSkuNameExt2, err
	}

	for _, storeId := range storeIDs {
		storeSkuName, err := dao.GetTopSkusByStoreIDs(db, []int{storeId})
		if err != nil {
			return nil, err
		}
		storeSkuNameExt = append(storeSkuNameExt, storeSkuName...)
	}

	for _, v := range storeSkuNameExt {
		if skuMap[v.SkuID] == nil {
			skuMap[v.SkuID] = v
		} else if skuMap[v.SkuID] != nil && v.Count != 0 {
			skuMap[v.SkuID] = v
		}
	}
	for _, v := range skuMap {
		storeSkuNameExt2 = append(storeSkuNameExt2, v)
	}
	for i := 0; i < len(storeSkuNameExt2)-1; i++ {
		for j := 0; j < len(storeSkuNameExt2)-i-1; j++ {
			if storeSkuNameExt2[j].Count < storeSkuNameExt2[j+1].Count {
				tmp := storeSkuNameExt2[j]
				storeSkuNameExt2[j] = storeSkuNameExt2[j+1]
				storeSkuNameExt2[j+1] = tmp
			}
		}
	}

	return storeSkuNameExt2, err
}

// GetTopSkusByCityCode 获取当前城市订单销量前一百的商品,排除掉商户已经可售的商品剩下的为热销推荐
func GetTopSkusByCityCode(ctx *jxcontext.Context, cityCode, storeID int) (skuNameAndPlaceList []*dao.SkuNameAndPlace, err error) {
	db := dao.GetDB()
	store, err := dao.GetStoreDetail(db, storeID, -1, "")
	if err != nil {
		return nil, err
	}
	cityCode = store.CityCode
	orderCreate := time.Now().AddDate(0, -1, 0)
	var skuNameAndPlace []*dao.SkuNameAndPlace
	if cityCode > 0 {
		skuNameAndPlace, err = dao.GetTopSkusByCityCode(db, cityCode, orderCreate)
	} else {
		skuNameAndPlace, err = dao.GetTopSkusByNoCityCode(db)
	}
	if storeID > 0 {
		var skuNameList []*model.SkuName
		//不可售的商品nameID列表
		sql := `
			SELECT DISTINCT b.name_id id
			FROM store_sku_bind a
			JOIN sku b ON a.sku_id = b.id AND b.deleted_at = ?
			WHERE a.deleted_at = ?
			AND a.store_id = ?
			AND a.status <> ?
			AND b.name_id NOT IN(SELECT DISTINCT b.name_id
								FROM store_sku_bind a
								JOIN sku b ON a.sku_id = b.id AND b.deleted_at = ?
								WHERE a.deleted_at = ? 
								AND a.store_id = ?
								AND a.status = ?)			
		`
		sqlParams := []interface{}{
			utils.DefaultTimeValue,
			utils.DefaultTimeValue,
			storeID,
			model.StoreSkuBindStatusNormal,
			utils.DefaultTimeValue,
			utils.DefaultTimeValue,
			storeID,
			model.StoreSkuBindStatusNormal,
		}
		err = dao.GetRows(db, &skuNameList, sql, sqlParams...)
		var skuNameMap = make(map[int]*model.SkuName)
		for _, v := range skuNameList {
			skuNameMap[v.ID] = v
		}

		var payPercentage int
		if store.PayPercentage < 50 {
			payPercentage = 70
		} else {
			payPercentage = store.PayPercentage
		}
		for _, v := range skuNameAndPlace {
			if skuNameMap[v.ID] != nil {
				var skuList []*model.SkuAndName
				storeSkuSyncInfo, _ := dao.GetStoreSkusAndSkuName(db, []int{storeID}, nil, []int{v.ID})
				for _, storeSkuSync := range storeSkuSyncInfo {
					sku, _ := dao.GetSkus(db, []int{storeSkuSync.ID}, nil, nil, nil, nil)
					sku[0].Price = int(storeSkuSync.Price)
					skuList = append(skuList, sku...)
				}
				v.UnitPrice = int(storeSkuSyncInfo[0].UnitPrice)
				v.Skus = skuList
				for _, vv := range skuList {
					var (
						storeSkuNameExt []*dao.StoreSkuNameExt
						skusList        []*dao.StoreSkuExt
					)
					vv.StoreSkuStatus = model.StoreSkuBindStatusDontSale
					skus := &dao.StoreSkuExt{
						SkuID:     vv.ID,
						BindPrice: vv.Price,
					}
					skusList = append(skusList, skus)
					storeSkuName := &dao.StoreSkuNameExt{
						Skus:          skusList,
						StoreID:       storeID,
						PayPercentage: payPercentage,
					}
					storeSkuNameExt = append(storeSkuNameExt, storeSkuName)
					skuNamesInfo := &dao.StoreSkuNamesInfo{
						SkuNames: storeSkuNameExt,
					}
					dao.UpdateActPrice4StoreSkuNameNew(db, []int{storeID}, []int{vv.ID}, skuNamesInfo, -1)
					if skuNamesInfo.SkuNames[0].Skus[0] != nil {
						vv.ActPrice = skuNamesInfo.SkuNames[0].Skus[0].ActPrice
						vv.ActID = skuNamesInfo.SkuNames[0].Skus[0].ActID
						vv.ActType = skuNamesInfo.SkuNames[0].Skus[0].ActType
						vv.EarningPrice = skuNamesInfo.SkuNames[0].Skus[0].EarningPrice
						vv.EarningActID = skuNamesInfo.SkuNames[0].Skus[0].EarningActID
					}
				}
				skuNameAndPlaceList = append(skuNameAndPlaceList, v)
			}
		}
	} else {
		skuNameAndPlaceList = append(skuNameAndPlaceList, skuNameAndPlace...)
	}
	i := 1
	for _, v := range skuNameAndPlaceList {
		v.Sequence = i
		i++
	}
	if len(skuNameAndPlaceList) <= 100 {
		return skuNameAndPlaceList, err
	} else {
		return skuNameAndPlaceList[0:100], err
	}
}

func GetTopCategoriesByStoreIDs(ctx *jxcontext.Context, storeIDs []int) (skuCategory []*model.SkuCategory, err error) {
	var (
		skuCategory2   []*model.SkuCategory
		skuCategoryMap = make(map[int]*model.SkuCategory)
		limit          = 10
	)
	if len(storeIDs) == 0 {
		return skuCategory, err
	}
	db := dao.GetDB()
	skuCategory, err = dao.GetTopCategoriesByStoreIDs(db, storeIDs, limit)
	//若大于等于10个就不用做下面的操作
	if len(skuCategory) >= limit {
		return skuCategory, err
	}
	if len(skuCategory) > 0 {
		for _, v := range skuCategory {
			skuCategoryMap[v.ID] = v
		}
	}
	//推荐分类，若不满10个，则填满10个
	if (len(skuCategory) < limit && len(skuCategory) > 0) || len(skuCategory) == 0 {
		skuCategory2, err = dao.GetCategories(db, -1, 1, nil, false)
		if len(skuCategory2) > 0 {
			for _, v := range skuCategory2 {
				if skuCategoryMap[v.ID] == nil {
					if !strings.Contains(v.Name, "赠品") {
						skuCategory = append(skuCategory, v)
					}
				}
				if len(skuCategory) >= limit {
					break
				}
			}
		}
	}
	if err != nil {
		return nil, err
	}
	return skuCategory, err
}

func RefershStoreSkusMidPrice(ctx *jxcontext.Context, storeIDs []int, isCountry bool) (err error) {
	db := dao.GetDB()
	if len(storeIDs) == 0 {
		return err
	}
	for _, v := range storeIDs {
		var skuBindInfos []*StoreSkuBindInfo
		store, err := dao.GetStoreDetail(db, v, -1, "")
		if err != nil {
			return err
		}
		var payPercentage int
		if store.PayPercentage < 50 {
			payPercentage = 70
		} else {
			payPercentage = store.PayPercentage
		}
		storeSkuList, err := dao.GetStoresSkusInfo(db, []int{v}, nil)
		for _, storeSku := range storeSkuList {
			var priceReferList []*model.PriceReferSnapshot
			if isCountry {
				priceReferList, err = dao.GetPriceReferSnapshotNoPage(db, []int{0}, []int{storeSku.SkuID}, nil, utils.Time2Date(time.Now().AddDate(0, 0, -1)))
			} else {
				priceReferList, err = dao.GetPriceReferSnapshotNoPage(db, []int{store.CityCode}, []int{storeSku.SkuID}, nil, utils.Time2Date(time.Now().AddDate(0, 0, -1)))
			}
			if err != nil {
				return err
			}
			if len(priceReferList) > 0 {
				//TODO 高于中位价20%才刷， 2020-05-08
				if priceReferList[0].MidUnitPrice >= 500 {
					if storeSku.UnitPrice > priceReferList[0].MidUnitPrice/payPercentage*120 {
						skuBindInfo := &StoreSkuBindInfo{
							NameID:    priceReferList[0].NameID,
							UnitPrice: priceReferList[0].MidUnitPrice / payPercentage * 120,
						}
						skuBindInfos = append(skuBindInfos, skuBindInfo)
					}
				}
			}
		}
		updateStoresSkusWithoutSync(ctx, db, []int{v}, skuBindInfos, false, false)
	}
	if err == nil {
		CreateStorePriceScore(ctx)
	}
	return err
}

// 根据excel刷新门店商品信息
func RefreshJxPriceByExcel(ctx *jxcontext.Context, storeIDs []int, files []*multipart.FileHeader, isAsync, isContinueWhenError bool) (hint string, err error) {
	if len(files) == 0 {
		return "", errors.New("没有文件上传！")
	}
	if len(storeIDs) == 0 {
		return "", errors.New("请选择至少一个门店！")
	}
	fileHeader := files[0]
	file, err := fileHeader.Open()
	hint, err = RefreshJxPriceByExcelBin(ctx, storeIDs, file, true, true)
	file.Close()
	return hint, err
}

func RefreshJxPriceByExcelBin(ctx *jxcontext.Context, storeIDs []int, reader io.Reader, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		storeSkuNamePriceList       []*model.StoreSkuNamePrice
		storeSkuNamePriceListUpdate []*model.StoreSkuNamePrice
		skuBindInfosInter           []interface{}
		skuBindInfoList             []*StoreSkuBindInfo
		errMsg                      string
		isErr                       bool = false
		nameMap                          = make(map[string]string)
	)
	dataLock.dataFailedList = dataLock.dataFailedList[0:0]
	dataLock.dataSuccessList = dataLock.dataSuccessList[0:0]
	sheetParam := &SheetParam{
		SkuNameIDCol: 5,
		SkuPriceCol:  3,
		SkuNameCol:   1,
		SkuRow:       1,
		SkuUnitCol:   2,
		OutSkuIDCol:  0,
	}
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			// xlsx, err := excelize.OpenFile("111.xlsx")
			xlsx, err := excelize.OpenReader(reader)
			if err != nil {
				return "", err
			}
			rows, _ := xlsx.GetRows(xlsx.GetSheetName(1))
			for rowNum, row := range rows {
				if rowNum < sheetParam.SkuRow {
					continue
				}
				storeSkuNamePrice := &model.StoreSkuNamePrice{}
				errMsg += GetCellIntoStruct(rowNum, row, sheetParam, storeSkuNamePrice, nameMap)
				storeSkuNamePriceList = append(storeSkuNamePriceList, storeSkuNamePrice)
			}
			if errMsg != "" {
				return "", errors.New(errMsg)
			} else {
				isErr = true
			}
		case 1:
			db := dao.GetDB()
			storeSkuNamePriceListOrg, _ := dao.GetStoreSkuNamePrice(db)
			CreateOrUpdateStoreSkuNamePriceByExcel(db, ctx, storeSkuNamePriceList, storeSkuNamePriceListOrg)
			storeSkuNamePriceListNew, _ := dao.GetStoreSkuNamePrice(db)
			storeSkuNamePriceMapNew := StoreSkuNamePriceList2Map(ctx, storeSkuNamePriceListNew)
			for _, v := range storeSkuNamePriceList {
				if storeSkuNamePriceMapNew[v.OutSkuID] != nil {
					storeSkuNamePriceListUpdate = append(storeSkuNamePriceListUpdate, storeSkuNamePriceMapNew[v.OutSkuID])
				}
			}
			taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				storeSkuNamePrice := batchItemList[0].(*model.StoreSkuNamePrice)
				var skuBindInfos []*StoreSkuBindInfo
				nameIDGroup := strings.Split(storeSkuNamePrice.NameIDGroup, ",")
				for _, v := range nameIDGroup {
					if v != "" {
						nameID := int(utils.Str2Int64(v))
						for _, vv := range storeIDs {
							skuList, err2 := dao.GetStoreSkusByNameIDs(db, []int{vv}, nameID)
							err = err2
							if len(skuList) > 0 {
								unitPrice := 0
								if skuList[0].Unit == model.UnitNames[0] {
									if storeSkuNamePrice.Unit == "KG" {
										unitPrice = storeSkuNamePrice.Price / 2
									} else {
										unitPrice = storeSkuNamePrice.Price
									}
								} else {
									unitPrice = storeSkuNamePrice.Price
								}
								storeSkuBindInfo := &StoreSkuBindInfo{
									NameID:    nameID,
									UnitPrice: unitPrice,
								}
								skuBindInfos = append(skuBindInfos, storeSkuBindInfo)
								outSuccess := DataSuccess{
									NameID:   nameID,
									Name:     skuList[0].Name,
									Unit:     storeSkuNamePrice.Unit,
									OrgPrice: utils.Str2Float64(utils.Int64ToStr(skuList[0].UnitPrice)) / 100,
									NowPrice: utils.Str2Float64(utils.Int64ToStr(int64(unitPrice))) / 100,
									MixPrice: utils.Str2Float64(utils.Int64ToStr(int64(unitPrice)-skuList[0].UnitPrice)) / 100,
								}
								dataLock.AppendDataSuccess(outSuccess)
							} else {
								//京西xx门店没有关注该商品
								outFailed := DataFailed{
									NameID:  nameID,
									Name:    storeSkuNamePrice.Name,
									Comment: fmt.Sprintf("京西[%v]门店没有关注该商品，商品nameID[%v]，Excel上商品名[%v]", vv, nameID, storeSkuNamePrice.Name),
								}
								dataLock.AppendDataFailed(outFailed)
							}
						}
					} else {
						//nameID为空，还未填写nameID
						outFailed := DataFailed{
							NameID:  0,
							Name:    storeSkuNamePrice.Name,
							Comment: fmt.Sprintf("商品nameID为空！，还未填写商品nameID，Excel上商品名[%v]", storeSkuNamePrice.Name),
						}
						dataLock.AppendDataFailed(outFailed)
					}
				}
				retVal = skuBindInfos
				return retVal, err
			}
			taskParallel := tasksch.NewParallelTask("刷新京西价", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, storeSkuNamePriceListUpdate)
			tasksch.HandleTask(taskParallel, task, true).Run()
			skuBindInfosInter, err = taskParallel.GetResult(0)
		case 2:
			//更新京西价
			for _, v := range skuBindInfosInter {
				skuBindInfoList = append(skuBindInfoList, v.(*StoreSkuBindInfo))
			}
			if isErr {
				UpdateStoresSkus(ctx, 0, storeIDs, skuBindInfoList, false, false, isAsync, isContinueWhenError)
			}
		case 3:
			//写Excel
			WriteToExcelJx(task, dataLock.dataSuccessList, dataLock.dataFailedList)
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("根据Excel刷新京西价", ctx, isContinueWhenError, taskSeqFunc, 4)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func WriteToExcelJx(task *tasksch.SeqTask, dataSuccess []DataSuccess, dataFailed []DataFailed) (err error) {
	var sheetList1 []*excel.Obj2ExcelSheetConfig
	var sheetList2 []*excel.Obj2ExcelSheetConfig
	var downloadURL1, downloadURL2, fileName1, fileName2 string
	excelConf1 := &excel.Obj2ExcelSheetConfig{
		Title:       "sheet1",
		Data:        dataSuccess,
		CaptionList: titleListSuccess,
	}
	sheetList1 = append(sheetList1, excelConf1)
	excelConf2 := &excel.Obj2ExcelSheetConfig{
		Title:       "sheet1",
		Data:        dataFailed,
		CaptionList: titleListFailed,
	}
	sheetList2 = append(sheetList2, excelConf2)
	if excelConf1 != nil {
		downloadURL1, fileName1, err = jxutils.UploadExeclAndPushMsg(sheetList1, "京西已更新商品")
	} else {
		baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!")
	}
	if excelConf2 != nil {
		downloadURL2, fileName2, err = jxutils.UploadExeclAndPushMsg(sheetList2, "未更新商品")
	} else {
		baseapi.SugarLogger.Debug("WriteToExcel: dataFailed is nil!")
	}
	if err != nil {
		baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName1, fileName2, err)
	} else {
		noticeMsg := fmt.Sprintf("[详情点我]path1=%s, path2=%s  \n", downloadURL1, downloadURL2)
		task.SetNoticeMsg(noticeMsg)
		baseapi.SugarLogger.Debugf("WriteToExcel:upload %s ,%s success, downloadURL1:%s ,downloadURL2:%s", fileName1, fileName2, downloadURL1, downloadURL2)
	}
	return err
}

func CreateOrUpdateStoreSkuNamePriceByExcel(db *dao.DaoDB, ctx *jxcontext.Context, storeSkuNamePriceList []*model.StoreSkuNamePrice, storeSkuNamePriceListOrg []*model.StoreSkuNamePrice) (err error) {
	storeSkuNamePriceMap := StoreSkuNamePriceList2Map(ctx, storeSkuNamePriceListOrg)
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()
	for _, v := range storeSkuNamePriceList {
		dao.WrapAddIDCULDEntity(v, ctx.GetUserName())
		if storeSkuNamePriceMap[v.OutSkuID] != nil {
			v.ID = storeSkuNamePriceMap[v.OutSkuID].ID
			v.UpdatedAt = time.Now()
			dao.UpdateEntity(db, v)
		} else {
			dao.CreateEntity(db, v)
		}
	}
	dao.Commit(db, txDB)
	return err
}

func StoreSkuNamePriceList2Map(ctx *jxcontext.Context, storeSkuNamePriceList []*model.StoreSkuNamePrice) (result map[string]*model.StoreSkuNamePrice) {
	result = make(map[string]*model.StoreSkuNamePrice, len(storeSkuNamePriceList))
	for _, v := range storeSkuNamePriceList {
		result[v.OutSkuID] = v
	}
	return result
}

func GetCellIntoStruct(rowNum int, row []string, sheetParam *SheetParam, storeSkuNamePrice *model.StoreSkuNamePrice, nameMap map[string]string) (errMsg string) {
	for k, cell := range row {
		if k == sheetParam.OutSkuIDCol {
			if IsChineseChar(cell) {
				return fmt.Sprintf("Excel格式排版发生了变化！在[%v]列,[%v]行附近可能增加或减少了一列", k+1, rowNum+1)
			}
			storeSkuNamePrice.OutSkuID = cell
		}
		if k == sheetParam.SkuNameCol {
			storeSkuNamePrice.Name = cell
		}
		if k == sheetParam.SkuNameIDCol {
			cellReplace := strings.ReplaceAll(cell, "，", ",")
			if cellReplace != "" {
				if cellReplace[len(cellReplace)-1:len(cellReplace)] == "," {
					cellReplace = cellReplace[0 : len(cellReplace)-1]
				}
				nameIDs := strings.Split(cellReplace, ",")
				for _, v := range nameIDs {
					if nameMap[v] != "" {
						return fmt.Sprintf(" Excel中含有重复的nameID！[%v]列,[%v]行，nameID [%v]\n", k+1, rowNum+1, v)
					} else {
						nameMap[v] = v
					}
				}
			}
			storeSkuNamePrice.NameIDGroup = cellReplace
		}
		if k == sheetParam.SkuPriceCol {
			if IsChineseChar(cell) {
				return fmt.Sprintf("Excel格式排版发生了变化！在[%v]列,[%v]行附近可能增加或减少了一列", k+1, rowNum+1)
			}
			storeSkuNamePrice.Price = int(utils.Float64TwoInt64(utils.Str2Float64(cell) * 100))
		}
		if k == sheetParam.SkuUnitCol {
			storeSkuNamePrice.Unit = cell
		}
	}
	return errMsg
}

func (d *DataLock) AppendDataSuccess(dataSuccess DataSuccess) {
	d.locker.Lock()
	defer d.locker.Unlock()
	d.dataSuccessList = append(d.dataSuccessList, dataSuccess)
}

func (d *DataLock) AppendDataFailed(dataFailed DataFailed) {
	d.locker.Lock()
	defer d.locker.Unlock()
	d.dataFailedList = append(d.dataFailedList, dataFailed)
}

func IsChineseChar(str string) bool {
	for _, r := range str {
		if unicode.Is(unicode.Scripts["Han"], r) {
			return true
		}
	}
	return false
}

func GetStoreCategories(ctx *jxcontext.Context, storeID, parentID int) (catList []*model.SkuCategory, err error) {
	return dao.GetStoreSkuCategories(dao.GetDB(), storeID, parentID)
}

func GetVendorStoreSkuPrice(ctx *jxcontext.Context, vendorIDs []int, skuID int, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		storeSkuListJD []DataVendorStoreSkuPrice
		storeSkuListMT []DataVendorStoreSkuPrice
		storeSkuListEB []DataVendorStoreSkuPrice
		excelParamList []ExcelParam
	)
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			for _, v := range vendorIDs {
				vendorID := v
				handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler)
				handlerStore := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IStoreHandler)
				for _, v := range apimanager.CurAPIManager.GetAppOrgCodeList(vendorID) {
					vendorStoreIDs, err2 := handlerStore.GetAllStoresVendorID(ctx, v)
					err = err2
					taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
						vendorStoreID := batchItemList[0].(string)
						var (
							inStoreSkuList  []*partner.StoreSkuInfo
							storeDetail     *dao.StoreDetail
							inStoreSku      = &partner.StoreSkuInfo{}
							outStoreSkuList []*partner.StoreSkuInfo
						)
						db := dao.GetDB()
						skuList, err := dao.GetSkusWithVendor(db, []int{vendorID}, []string{v}, nil, []int{skuID}, false)
						if err != nil {
							return retVal, err
						}
						if partner.IsMultiStore(vendorID) {
							multiHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IMultipleStoresHandler)
							storeDetail, err = multiHandler.ReadStore(ctx, v, vendorStoreID, "")
							if len(skuList) > 0 {
								inStoreSku.VendorSkuID = skuList[0].VendorSkuID
							}
						} else {
							singleHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreHandler)
							storeDetail, err = singleHandler.ReadStore(ctx, v, vendorStoreID, "")
							inStoreSku.SkuID = skuID
						}
						inStoreSkuList = append(inStoreSkuList, inStoreSku)
						storeDetail2, err := dao.GetStoreDetailByVendorStoreID(db, vendorStoreID, vendorID, "")
						if storeDetail2 != nil {
							outStoreSkuList, err = handler.GetStoreSkusBareInfo(ctx, v, task, storeDetail2.ID, vendorStoreID, inStoreSkuList)
						} else {
							outStoreSkuList, err = handler.GetStoreSkusBareInfo(ctx, v, task, 0, vendorStoreID, inStoreSkuList)
						}
						if storeDetail != nil {
							if len(outStoreSkuList) == 0 {
								data := DataVendorStoreSkuPrice{
									StoreID:     vendorStoreID,
									StoreName:   storeDetail.Name,
									SkuID:       skuID,
									SkuName:     skuList[0].Name,
									VendorPrice: "",
								}
								retVal = []DataVendorStoreSkuPrice{data}
							} else {
								data := DataVendorStoreSkuPrice{
									StoreID:     vendorStoreID,
									StoreName:   storeDetail.Name,
									SkuID:       skuID,
									SkuName:     skuList[0].Name,
									VendorPrice: utils.Float64ToStr(utils.Str2Float64(utils.Int64ToStr(outStoreSkuList[0].VendorPrice)) / 100),
								}
								retVal = []DataVendorStoreSkuPrice{data}
							}
						}
						return retVal, err
					}
					taskParallel := tasksch.NewParallelTask("获取各平台所有门店某商品价格", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, vendorStoreIDs)
					tasksch.HandleTask(taskParallel, task, true).Run()
					storeSkuList, _ := taskParallel.GetResult(0)
					for _, v := range storeSkuList {
						if vendorID == model.VendorIDJD {
							storeSkuListJD = append(storeSkuListJD, v.(DataVendorStoreSkuPrice))
						}
						if vendorID == model.VendorIDEBAI {
							storeSkuListEB = append(storeSkuListEB, v.(DataVendorStoreSkuPrice))
						}
						if vendorID == model.VendorIDMTWM {
							storeSkuListMT = append(storeSkuListMT, v.(DataVendorStoreSkuPrice))
						}
					}
				}
			}
			excelParam1 := ExcelParam{
				DataList:  storeSkuListJD,
				TitleList: titleListVendorStoreSkuPrice,
				SheetName: "京东平台",
			}
			excelParam2 := ExcelParam{
				DataList:  storeSkuListEB,
				TitleList: titleListVendorStoreSkuPrice,
				SheetName: "饿百平台",
			}
			excelParam3 := ExcelParam{
				DataList:  storeSkuListMT,
				TitleList: titleListVendorStoreSkuPrice,
				SheetName: "美团平台",
			}
			excelParamList = append(excelParamList, excelParam1, excelParam2, excelParam3)
		case 1:
			WriteToExcelNormal(task, "各平台"+utils.Int2Str(skuID)+"商品所有门店价格", excelParamList)
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("获取各平台所有门店某商品价格-序列任务", ctx, isContinueWhenError, taskSeqFunc, 2)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func WriteToExcelNormal(task *tasksch.SeqTask, fileName string, excelParam []ExcelParam) (err error) {
	var sheetList []*excel.Obj2ExcelSheetConfig
	for _, v := range excelParam {
		if v.DataList != nil {
			excelConf := &excel.Obj2ExcelSheetConfig{
				Title:       v.SheetName,
				Data:        v.DataList,
				CaptionList: v.TitleList,
			}
			sheetList = append(sheetList, excelConf)
		}
	}
	downloadURL, fileNameResult, err := jxutils.UploadExeclAndPushMsg(sheetList, fileName)
	if err != nil {
		baseapi.SugarLogger.Errorf("WriteToExcel:upload %s failed error:%v", fileNameResult, err)
	} else {
		noticeMsg := fmt.Sprintf("[详情点我]path=%s \n", downloadURL)
		task.SetNoticeMsg(noticeMsg)
		baseapi.SugarLogger.Debugf("WriteToExcel:upload %s success, downloadURL:%s", fileNameResult, downloadURL)
	}
	return err
}

func FocusStoreSkusByExcel(ctx *jxcontext.Context, files []*multipart.FileHeader, isAsync, isContinueWhenError bool) (hint string, err error) {
	if len(files) == 0 {
		return "", errors.New("没有文件上传！")
	}
	fileHeader := files[0]
	file, err := fileHeader.Open()
	hint, err = FocusStoreSkusByExcelBin(ctx, file, isAsync, isContinueWhenError)
	file.Close()
	return hint, err
}

func FocusStoreSkusByExcelBin(ctx *jxcontext.Context, reader io.Reader, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		skuMap  = make(map[int]int)
		db      = dao.GetDB()
		skuIDs  []int
		result1 []interface{}
	)
	sheetParam := &SheetParam{
		OutSkuIDCol: 1,
		SkuPriceCol: 3,
		SkuRow:      1,
	}
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			// xlsx, err := excelize.OpenFile("111.xlsx")
			xlsx, err := excelize.OpenReader(reader)
			if err != nil {
				return result, err
			}
			rows, _ := xlsx.GetRows(xlsx.GetSheetName(1))
			for rowNum, row := range rows {
				if rowNum < sheetParam.SkuRow {
					continue
				}
				GetCellForFocusStoreSkus(db, rowNum, row, sheetParam, skuMap)
			}
		case 1:
			for k, _ := range skuMap {
				skuIDs = append(skuIDs, k)
			}
			skuList, err := dao.GetSkus(db, skuIDs, nil, nil, nil, nil)
			storeList, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "")
			if err != nil && len(skuList) == 0 {
				return result, err
			}
			taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				store := batchItemList[0].(*model.Store)
				var (
					skuNameMap = make(map[int]int)
					skuInfoMap = make(map[int][]*StoreSkuBindSkuInfo)
				)
				for _, v := range skuList {
					var (
						price       int
						specQuality float64
					)
					focusList, _ := dao.GetStoreSkuBindByNameID(db, store.ID, v.NameID, model.StoreSkuBindStatusNormal)
					//有关注过
					if len(focusList) > 0 {
						price = focusList[0].UnitPrice
						skuInfoMap[v.NameID] = append(skuInfoMap[v.NameID], &StoreSkuBindSkuInfo{
							SkuID:  v.ID,
							IsSale: 1,
						})
						skuNameMap[v.NameID] = price
					} else {
						if v.Unit == model.SpecialUnit {
							if v.SpecUnit == model.SpecUnitNames[1] || v.SpecUnit == model.SpecUnitNames[2] {
								specQuality = float64(v.SpecQuality) * 1000
							} else {
								specQuality = float64(v.SpecQuality)
							}
							price = int(utils.Float64TwoInt64(utils.Int2Float64(model.SpecialSpecQuality) / specQuality * utils.Int2Float64(skuMap[v.ID])))
						} else {
							price = skuMap[v.ID]
						}
						if skuNameMap[v.NameID] < price {
							skuNameMap[v.NameID] = price
						}
					}
				}
				var storeSkuBindInfo []*StoreSkuBindInfo
				for k, v := range skuNameMap {
					skuBindInfo := &StoreSkuBindInfo{
						StoreID:   store.ID,
						NameID:    k,
						UnitPrice: v,
						IsFocus:   1,
						IsSale:    1,
						Skus:      skuInfoMap[k],
					}
					storeSkuBindInfo = append(storeSkuBindInfo, skuBindInfo)
				}
				retVal = storeSkuBindInfo
				return retVal, err
			}
			taskParallel := tasksch.NewParallelTask("根据skuID关注商品", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, storeList)
			tasksch.HandleTask(taskParallel, task, true).Run()
			result1, _ = taskParallel.GetResult(0)
		case 2:
			var skuBindInfos []*StoreSkuBindInfo
			for _, v := range result1 {
				skuBindInfos = append(skuBindInfos, v.(*StoreSkuBindInfo))
			}
			UpdateStoresSkusByBind(ctx, task, skuBindInfos, true, true, false)
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("根据Excel中skuID批量关注商品", ctx, isContinueWhenError, taskSeqFunc, 3)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func GetCellForFocusStoreSkus(db *dao.DaoDB, rowNum int, row []string, sheetParam *SheetParam, skuMap map[int]int) {
	var (
		skuID int
		price int
	)
	for k, cell := range row {
		if k == sheetParam.OutSkuIDCol {
			skuID = int(utils.Str2Int64(cell))
		}
		if k == sheetParam.SkuPriceCol {
			price = int(utils.Float64TwoInt64(utils.Str2Float64(cell) * 100))
		}
	}
	skuMap[skuID] = price
}

func FocusStoreSkusBySku(ctx *jxcontext.Context, skuBindInfos []*StoreSkuBindInfo, isAsync, isContinueWhenError bool) (hint string, err error) {
	var result1 []interface{}
	db := dao.GetDB()
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				var (
					price         int
					payPercentage int
				)
				skuBindInfo := batchItemList[0].(*StoreSkuBindInfo)
				storeID := skuBindInfo.StoreID
				skuID := skuBindInfo.Skus[0].SkuID
				store, err := dao.GetStoreDetail(db, storeID, -1, "")
				if len(skuBindInfos) > 1 {
					if store.StoreLevel == "D" || store.StoreLevel == "E" {
						return retVal, err
					}
				}
				if err != nil {
					return retVal, err
				}
				skuList, err := dao.GetSkus(db, []int{skuID}, nil, nil, nil, nil)
				if err != nil {
					return retVal, err
				}
				if len(skuList) == 0 {
					return retVal, fmt.Errorf("未查询到此商品！商品id ：[%d]", skuID)
				}
				focusList, _ := dao.GetStoreSkuBindByNameID(db, storeID, skuList[0].NameID, model.StoreSkuBindStatusNormal)
				//有关注过
				if len(focusList) > 0 {
					price = focusList[0].UnitPrice
				} else {
					if store.PayPercentage < 50 {
						payPercentage = 70
					} else {
						payPercentage = store.PayPercentage
					}
					priceReferList, _ := dao.GetPriceReferSnapshotNoPage(db, []int{0}, nil, []int{skuList[0].NameID}, utils.Time2Date(time.Now().AddDate(0, 0, -1)))
					if len(priceReferList) > 0 {
						if payPercentage == 100 {
							price = priceReferList[0].MidUnitPrice * payPercentage / 100
						} else {
							price = priceReferList[0].MidUnitPrice / payPercentage * 100
						}
					}
				}
				skuBindSkuInfo := &StoreSkuBindSkuInfo{
					SkuID:  skuID,
					IsSale: 1,
				}
				var skuBindSkuInfos []*StoreSkuBindSkuInfo
				skuBindSkuInfos = append(skuBindSkuInfos, skuBindSkuInfo)
				skuBindInfo2 := &StoreSkuBindInfo{
					StoreID:   storeID,
					NameID:    skuList[0].NameID,
					UnitPrice: price,
					IsFocus:   1,
					Skus:      skuBindSkuInfos,
				}
				retVal = []*StoreSkuBindInfo{skuBindInfo2}
				return retVal, err
			}
			taskParallel := tasksch.NewParallelTask("根据skuID部分关注商品", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, skuBindInfos)
			tasksch.HandleTask(taskParallel, task, true).Run()
			result1, _ = taskParallel.GetResult(0)
		case 1:
			var skuBindInfos []*StoreSkuBindInfo
			for _, v := range result1 {
				skuBindInfos = append(skuBindInfos, v.(*StoreSkuBindInfo))
			}
			hint, err = UpdateStoresSkusByBind(ctx, task, skuBindInfos, isAsync, isContinueWhenError, true)
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("根据skuID批量部分关注商品", ctx, isContinueWhenError, taskSeqFunc, 2)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func AutoFocusStoreSkusForTopSkus(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) {
	db := dao.GetDB()
	var (
		result1 []interface{}
	)
	storeList, err := dao.GetStoreList(db, nil, nil, nil, []int{1}, nil, "")
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				v := batchItemList[0].(*model.Store)
				var (
					skuName    []*model.SkuName
					skuNameMap = make(map[int]int)
				)
				//物料店不自动关注畅销品
				if autoNotFoucsStoreMap[v.ID] != 0 {
					return retVal, err
				}
				sql := `
					SELECT DISTINCT a.name_id id
					FROM sku a
					LEFT JOIN (SELECT DISTINCT b.name_id 
								FROM store_sku_bind a
								JOIN sku b ON a.sku_id = b.id
								WHERE a.deleted_at = ?
								AND store_id = ?)b ON a.name_id = b.name_id
					WHERE a.status = ?	
					AND a.deleted_at = ?
					AND b.name_id IS NULL
				`
				sqlParams := []interface{}{
					utils.DefaultTimeValue,
					v.ID,
					model.SkuStatusNormal,
					utils.DefaultTimeValue,
				}
				err = dao.GetRows(db, &skuName, sql, sqlParams...)
				for _, name := range skuName {
					skuNameMap[name.ID] = name.ID
				}
				skuNameAndPlaceList, err2 := GetTopSkusByCityCode(ctx, 0, 0)
				//skuNameAndPlaceList, err2 := GetTopSkusByCityCode(ctx, v.CityCode, 0)
				if err2 != nil {
					return retVal, err2
				}
				var payPercentage int
				if v.PayPercentage < 50 {
					payPercentage = 70
				} else {
					payPercentage = v.PayPercentage
				}
				if len(skuNameAndPlaceList) > 0 {
					var skuBindInfoList []*StoreSkuBindInfo
					for _, vv := range skuNameAndPlaceList {
						if skuNameMap[vv.ID] != 0 {
							globals.SugarLogger.Debugf("testAutoFocusStoreSkusForTopSkus,storeID:%v,nameID:%v", v.ID, vv.ID)
							storeSkuBindInfo := &StoreSkuBindInfo{
								StoreID: v.ID,
								NameID:  vv.ID,
								// UnitPrice: priceReferList[0].MidUnitPrice * payPercentage / 100,
								IsFocus: 1,
								IsSale:  0,
							}
							priceReferList, err := dao.GetPriceReferSnapshotNoPage(db, []int{vv.CityCode}, nil, []int{vv.ID}, utils.Time2Date(time.Now().AddDate(0, 0, -1)))
							if err == nil && len(priceReferList) > 0 {
								storeSkuBindInfo.UnitPrice = priceReferList[0].MidUnitPrice / payPercentage * 100
							}
							skuBindInfoList = append(skuBindInfoList, storeSkuBindInfo)
						}
					}
					retVal = skuBindInfoList
				}
				return retVal, err
			}
			taskParallel := tasksch.NewParallelTask("自动关注畅销品-设置数据", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, storeList)
			tasksch.HandleTask(taskParallel, task, true).Run()
			result1, _ = taskParallel.GetResult(0)
		case 1:
			var skuBindInfos []*StoreSkuBindInfo
			for _, v := range result1 {
				skuBindInfos = append(skuBindInfos, v.(*StoreSkuBindInfo))
			}
			hint, err = UpdateStoresSkusByBind(ctx, nil, skuBindInfos, isAsync, isContinueWhenError, false)
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("自动关注畅销品", ctx, isContinueWhenError, taskSeqFunc, 2)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func AutoFocusStoreSkusWithoutFocus(ctx *jxcontext.Context, skuIDs []int, isSync bool) (err error) {
	var (
		nameMap = make(map[int]*StoreSkuBindInfo)
	)
	db := dao.GetDB()
	storeList, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "")
	for _, v := range storeList {
		storeSkuList, _ := dao.GetStoreSkusAndSkuName(db, []int{v.ID}, skuIDs, nil)
		for _, vv := range storeSkuList {
			if nameMap[vv.ID] != nil {
				nameMap[vv.ID].Skus = append(nameMap[vv.ID].Skus, &StoreSkuBindSkuInfo{
					SkuID: vv.SkuID,
				})
			} else {
				skuBindInfo := &StoreSkuBindInfo{
					UnitPrice: int(vv.UnitPrice),
					NameID:    vv.NameID,
					StoreID:   v.ID,
					Skus:      []*StoreSkuBindSkuInfo{},
				}
				nameMap[vv.ID] = skuBindInfo
			}
		}
	}
	for _, v := range nameMap {
		var skuBindInfoList []*StoreSkuBindInfo
		skuBindInfoResult := &StoreSkuBindInfo{
			NameID:    v.NameID,
			UnitPrice: v.UnitPrice,
			IsFocus:   1,
		}
		var skuBindSkuList []*StoreSkuBindSkuInfo
		skuMap := make(map[int]int)
		skuList, _ := dao.GetSkus(db, nil, []int{v.NameID}, nil, nil, nil)
		if len(v.Skus) != len(skuList) {
			for _, skus := range v.Skus {
				skuMap[skus.SkuID] = 1
			}
			for _, vv := range skuList {
				if skuMap[vv.ID] != 1 {
					continue
				}
				skuBindSkuList = append(skuBindSkuList, &StoreSkuBindSkuInfo{
					SkuID:  vv.ID,
					IsSale: 0,
				})
			}
		}
		skuBindInfoList = append(skuBindInfoList, skuBindInfoResult)
		if isSync {
			UpdateStoreSkus(ctx, 0, v.StoreID, skuBindInfoList, true, true)
		} else {
			updateStoresSkusWithoutSync(ctx, db, []int{v.StoreID}, skuBindInfoList, false, false)
		}
	}
	return err
}

func UpdateStoreSkusSpecTag(ctx *jxcontext.Context, vendorIDs []int, files []*multipart.FileHeader, isAsync, isContinueWhenError bool) (hint string, err error) {
	if len(files) == 0 {
		return "", errors.New("没有文件上传！")
	}
	fileHeader := files[0]
	file, err := fileHeader.Open()
	hint, err = UpdateStoreSkusSpecTagBin(ctx, file, vendorIDs, isAsync, isContinueWhenError)
	file.Close()
	return hint, err
}

func UpdateStoreSkusSpecTagBin(ctx *jxcontext.Context, reader io.Reader, vendorIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		db      = dao.GetDB()
		results []*tUpdateSkuSpecTag
	)
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			// xlsx, err := excelize.OpenFile("111.xlsx")
			xlsx, err := excelize.OpenReader(reader)
			if err != nil {
				return result, err
			}
			rows, _ := xlsx.GetRows(xlsx.GetSheetName(1))
			for rowNum, row := range rows {
				if rowNum < 1 {
					continue
				}
				var (
					skuMap  = &tUpdateSkuSpecTag{}
					storeID int
					skuID   int
					isSpec  int
				)
				for k, cell := range row {
					if cell != "" {
						if k == 0 {
							storeID = int(utils.Str2Int64(cell))
						}
						if k == 1 {
							skuID = int(utils.Str2Int64(cell))
						}
						if k == 2 {
							isSpec = int(utils.Str2Int64(cell))
						}
					}
				}
				skuMap.SkuID = skuID
				skuMap.IsSpec = isSpec
				skuMap.StoreID = storeID
				results = append(results, skuMap)
			}
		case 1:
			for i := 0; i < len(results)/2; i++ {
				tmp := results[i]
				results[i] = results[len(results)-i-1]
				results[len(results)-i-1] = tmp
			}
			//权限
			if permission.IsRoled(ctx) {
				if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
					for _, v := range results {
						if storeIDsMap[v.StoreID] == 0 {
							continue
						}
						store, err := dao.GetStoreDetail(db, v.StoreID, model.VendorIDMTWM, "")
						if err != nil || store == nil {
							continue
						}
						mtapi := apimanager.CurAPIManager.GetAPI(model.VendorIDMTWM, store.VendorOrgCode).(*mtwmapi.API)
						food, err := mtapi.RetailGet(store.VendorStoreID, utils.Int2Str(v.SkuID))
						if err != nil || food == nil {
							continue
						}
						var foodData = make(map[string]interface{})
						if v.IsSpec != 0 && v.IsSpec == -1 {
							v.IsSpec = 0
						}
						foodData["is_specialty"] = v.IsSpec
						foodData["price"] = food.Price
						if globals.EnableMtwmStoreWrite {
							err = mtapi.RetailInitData(ctx.GetTrackInfo(), store.VendorStoreID, utils.Int2Str(v.SkuID), foodData)
						}
					}
				}
			}
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("修改平台力荐商品", ctx, isContinueWhenError, taskSeqFunc, 2)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func SendSeckillSkusCountMsg(ctx *jxcontext.Context, vendorIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
	// 1. 如果爆品低于8个，报警   			type1
	// 2. 爆品价格低于1元商品小于5个，报警 	 type2
	var (
		type1Count  = 8
		type2Count  = 5
		ddMsgresult []interface{}
	)
	db := dao.GetDB()
	storeList, err := dao.GetStoresMapList(db, vendorIDs, nil, []int{model.StoreStatusClosed, model.StoreStatusHaveRest, model.StoreStatusOpened}, model.StoreStatusOpened, model.StoreIsSyncAll, "", "", "")
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				store := batchItemList[0].(*model.StoreMap)
				var type1, type2 int
				switch store.VendorID {
				case model.VendorIDEBAI:
					result, _ := api.EbaiAPI.GetStoresShowWindowSkus(utils.Str2Int64(store.VendorStoreID))
					for _, v := range result {
						type1++
						if v.SalePrice < 1 {
							type2++
						}
					}
				case model.VendorIDMTWM:
					handler := partner.GetPurchasePlatformFromVendorID(store.VendorID).(partner.ISingleStoreStoreSkuHandler)
					remoteSkuList, err := handler.GetStoreSkusFullInfo(ctx, nil, store.StoreID, store.VendorStoreID, nil)
					if err != nil {
						return retVal, err
					}
					for _, v := range remoteSkuList {
						for _, vv := range v.SkuList {
							if vv.IsSpecialty == 1 {
								type1++
							}
							if vv.IsSpecialty == 1 && vv.VendorPrice < 100 {
								type2++
							}
						}
					}
				case model.VendorIDJD:
					var storeSecKill []*tStoreSkusSecKill
					sql := `
					SELECT t1.store_id,count(*) sec_kill_count, count(t1.price < 100 or NULL) sec_kill_count2
					FROM(
						SELECT a.store_id, a.sku_id, d.type, MIN(e.actual_act_price) price
						FROM store_sku_bind a
						LEFT JOIN act_store_sku b ON a.store_id = b.store_id AND b.sku_id = a.sku_id
						LEFT JOIN act_map c ON c.act_id = b.act_id
						LEFT JOIN act d ON d.id = c.act_id
						LEFT JOIN act_store_sku_map e ON e.store_id = a.store_id AND e.sku_id = a.sku_id AND e.vendor_id = c.vendor_id AND e.act_id = d.id
						WHERE 1=1
						AND a.store_id = ?
						AND c.vendor_id = ?
						AND NOW() BETWEEN d.begin_at AND d.end_at
						AND d.type = ?
						AND a.status = ?
						AND a.deleted_at = ?
						AND e.actual_act_price <> 0
						GROUP BY 1,2,3)t1 
					GROUP BY 1
					`
					sqlParams := []interface{}{
						store.StoreID, store.VendorID,
						model.ActSkuSecKill, model.StoreSkuBindStatusNormal,
						utils.DefaultTimeValue,
					}
					err = dao.GetRows(db, &storeSecKill, sql, sqlParams...)
					if len(storeSecKill) > 0 {
						type1 = storeSecKill[0].SecKillCount
						type2 = storeSecKill[0].SecKillCount2
					} else {
						type1 = 0
						type2 = 0
					}
				}
				if type1 < type1Count || type2 < type2Count {
					storeDetail, _ := dao.GetStoreDetail(db, store.StoreID, store.VendorID, "")
					var (
						type1Str   = "爆品数量低于8个！"
						type2Str   = "爆品价格小于1元的爆品数量低于5个！"
						typeResult = ""
					)
					operatorNameList := jxutils.BatchString2Slice(storeDetail.OperatorName, storeDetail.OperatorName2, storeDetail.OperatorName3)
					operatorPhoneList := jxutils.BatchString2Slice(storeDetail.OperatorPhone, storeDetail.OperatorPhone2, storeDetail.OperatorPhone3)

					if type1 < type1Count {
						typeResult += type1Str
					}
					if type2 < type2Count {
						typeResult += type2Str
					}
					var result = &tStoreSkusSecKill{}
					noticeMsg := fmt.Sprintf("运营负责人：[%v]，市场负责人：[%v]，门店ID：[%v]，平台门店ID[%v]，门店名：[%v]，平台：[%v]，警告类型：[%v]\n", strings.Join(operatorNameList, ","), storeDetail.MarketManName, store.StoreID, storeDetail.VendorStoreID, store.StoreName, model.VendorChineseNames[store.VendorID], typeResult)
					result.OperatorPhoneList = operatorPhoneList
					result.MarketManPhone = storeDetail.MarketManPhone
					result.NoticeMsg = noticeMsg
					retVal = []*tStoreSkusSecKill{result}
				}
				return retVal, err
			}
			taskParallel := tasksch.NewParallelTask("获取各平台爆品数量", tasksch.NewParallelConfig().SetParallelCount(parallelCount), ctx, taskFunc, storeList)
			tasksch.HandleTask(taskParallel, task, true).Run()
			ddMsgresult, err = taskParallel.GetResult(0)
		case 1:
			var (
				operaterMap = make(map[string]string)
				marketMap   = make(map[string]string)
			)
			for _, v := range ddMsgresult {
				ddm := v.(*tStoreSkusSecKill)
				for _, phone := range ddm.OperatorPhoneList {
					if operaterMap[phone] != "" {
						operaterMap[phone] += ddm.NoticeMsg
					} else {
						operaterMap[phone] = ddm.NoticeMsg
					}
				}
				if marketMap[ddm.MarketManPhone] != "" {
					marketMap[ddm.MarketManPhone] += ddm.NoticeMsg
				} else {
					marketMap[ddm.MarketManPhone] = ddm.NoticeMsg
				}
			}
			for k, v := range operaterMap {
				if marketMap[k] != "" {
					continue
				}
				user, err := dao.GetUserByID(db, "mobile", k)
				if user != nil && err == nil {
					ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "警告！门店爆品数量异常！", v)
				}
			}
			for k, v := range marketMap {
				if operaterMap[k] != "" {
					continue
				}
				user, err := dao.GetUserByID(db, "mobile", k)
				if user != nil && err == nil {
					ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.UserID, "警告！门店爆品数量异常！", v)
				}
			}
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("爆品预警", ctx, isContinueWhenError, taskSeqFunc, 2)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func RefreshJxPriceByVendor(ctx *jxcontext.Context, jdStoreSkus []*JdStoreSkus, vendorID int, ignoreLow, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		db    = dao.GetDB()
		jdMap = make(map[int][]*JdStoreSkus)
		jxMap = make(map[int]map[int]int)
		param []*StoreSkuBindInfo
	)
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			if vendorID == model.VendorIDJD {
				for _, v := range jdStoreSkus {
					var (
						pricePercentagePack []*model.PricePercentageItem
						cats                []*model.ThingMap
						skus                []*model.SkuAndName
						vendorPrice         int
						specQuality         float64
					)
					sql := `
						SELECT t1.*
						FROM thing_map t1
						WHERE t1.deleted_at = ? AND t1.thing_type = ?
						AND t1.vendor_thing_id = ?
					`
					sqlParams := []interface{}{
						utils.DefaultTimeValue,
						model.ThingTypeSku,
						v.JdSkuID,
					}
					err = dao.GetRows(db, &cats, sql, sqlParams...)
					if len(cats) > 0 {
						skus, err = dao.GetSkus(db, []int{int(cats[0].ThingID)}, nil, nil, nil, nil)
						if err != nil || len(skus) == 0 {
							return result, fmt.Errorf("没有找到该京西skuID！,京西skuID :[%v]", cats[0].ThingID)
						}
					} else {
						return result, fmt.Errorf("没有找到该京东skuID对应的京西skuID！,京东skuID :[%v]", v.JdSkuID)
					}
					store, err := dao.GetStoreDetailByVendorStoreID(db, utils.Int2Str(v.JdStoreID), vendorID, "")
					if err != nil || store == nil {
						return result, fmt.Errorf("没有找到该京东门店对应的京西门店！,京东门店ID :[%v]", v.JdStoreID)
					}
					err = jxutils.Strings2Objs(store.PricePercentagePackStr, &pricePercentagePack)
					if skus[0].Unit == model.SpecialUnit {
						if skus[0].SpecUnit == model.SpecUnitNames[1] || skus[0].SpecUnit == model.SpecUnitNames[2] {
							specQuality = float64(skus[0].SpecQuality) * 1000
						} else {
							specQuality = float64(skus[0].SpecQuality)
						}
						vendorPrice = int(utils.Float64TwoInt64(specQuality / utils.Int2Float64(model.SpecialSpecQuality) * utils.Int2Float64(v.Price)))
					} else {
						vendorPrice = v.Price
					}
					jxPrice := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, vendorPrice)
					if skus[0].Unit == model.SpecialUnit {
						jxPrice = int(utils.Float64TwoInt64(utils.Int2Float64(jxPrice) * utils.Int2Float64(model.SpecialSpecQuality) / specQuality))
					}
					jdMap[store.ID] = append(jdMap[store.ID], &JdStoreSkus{
						JdSkuID: skus[0].NameID,
						Price:   jxPrice,
					})
				}
				for k, v := range jdMap {
					var skuNameMap = make(map[int]int)
					for _, vv := range v {
						if skuNameMap[vv.JdSkuID] != 0 {
							if skuNameMap[vv.JdSkuID] > vv.Price {
								skuNameMap[vv.JdSkuID] = vv.Price
							}
						} else {
							skuNameMap[vv.JdSkuID] = vv.Price
						}
					}
					jxMap[k] = skuNameMap
				}
				for k, v := range jxMap {
					for kk, vv := range v {
						result, err := dao.GetStoreSkuBindByNameID(db, k, kk, model.SkuStatusNormal)
						if len(result) > 0 && err == nil {
							if ignoreLow {
								if result[0].UnitPrice > vv {
									storeSkuBindInfo := &StoreSkuBindInfo{
										StoreID:   k,
										NameID:    kk,
										UnitPrice: vv,
									}
									param = append(param, storeSkuBindInfo)
								}
							} else {
								storeSkuBindInfo := &StoreSkuBindInfo{
									StoreID:   k,
									NameID:    kk,
									UnitPrice: vv,
								}
								param = append(param, storeSkuBindInfo)
							}
						}
					}
				}
			} else if vendorID == model.VendorIDMTWM {
				for _, v := range jdStoreSkus {
					var (
						pricePercentagePack []*model.PricePercentageItem
						vendorPrice         int
						specQuality         float64
					)
					skus, _ := dao.GetSkus(db, []int{v.JdSkuID}, nil, nil, nil, nil)
					if len(skus) == 0 {
						continue
					}
					store, _ := dao.GetStoreDetail(db, v.JdStoreID, vendorID, "")
					err = jxutils.Strings2Objs(store.PricePercentagePackStr, &pricePercentagePack)
					if skus[0].Unit == model.SpecialUnit {
						if skus[0].SpecUnit == model.SpecUnitNames[1] || skus[0].SpecUnit == model.SpecUnitNames[2] {
							specQuality = float64(skus[0].SpecQuality) * 1000
						} else {
							specQuality = float64(skus[0].SpecQuality)
						}
						vendorPrice = int(utils.Float64TwoInt64(specQuality / utils.Int2Float64(model.SpecialSpecQuality) * utils.Int2Float64(v.Price)))
					} else {
						vendorPrice = v.Price
					}
					jxPrice := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, vendorPrice)
					if skus[0].Unit == model.SpecialUnit {
						jxPrice = int(utils.Float64TwoInt64(utils.Int2Float64(jxPrice) * utils.Int2Float64(model.SpecialSpecQuality) / specQuality))
					}
					jdMap[v.JdStoreID] = append(jdMap[v.JdStoreID], &JdStoreSkus{
						JdSkuID: skus[0].NameID,
						Price:   jxPrice,
					})
				}
				for k, v := range jdMap {
					var skuNameMap = make(map[int]int)
					for _, vv := range v {
						if skuNameMap[vv.JdSkuID] != 0 {
							if skuNameMap[vv.JdSkuID] > vv.Price {
								skuNameMap[vv.JdSkuID] = vv.Price
							}
						} else {
							skuNameMap[vv.JdSkuID] = vv.Price
						}
					}
					jxMap[k] = skuNameMap
				}
				for k, v := range jxMap {
					for kk, vv := range v {
						result, err := dao.GetStoreSkuBindByNameID(db, k, kk, model.SkuStatusNormal)
						if len(result) > 0 && err == nil {
							if ignoreLow {
								if result[0].UnitPrice > vv {
									storeSkuBindInfo := &StoreSkuBindInfo{
										StoreID:   k,
										NameID:    kk,
										UnitPrice: vv,
									}
									param = append(param, storeSkuBindInfo)
								}
							} else {
								storeSkuBindInfo := &StoreSkuBindInfo{
									StoreID:   k,
									NameID:    kk,
									UnitPrice: vv,
								}
								param = append(param, storeSkuBindInfo)
							}
						}
					}
				}
			}
		case 1:
			_, err = UpdateStoresSkusByBind(ctx, nil, param, isAsync, isContinueWhenError, false)
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("根据京东平台价刷新京西平台价", ctx, isContinueWhenError, taskSeqFunc, 2)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func RefreshJxPriceByVendor2(ctx *jxcontext.Context, storeIDs []int, vendorID int, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		db    = dao.GetDB()
		param []*StoreSkuBindInfo
	)
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			for _, storeID := range storeIDs {
				var (
					pricePercentagePack []*model.PricePercentageItem
					skuNameMap          = make(map[int]int64)
				)
				storeDetail, _ := dao.GetStoreDetail(db, storeID, vendorID, "")
				err = jxutils.Strings2Objs(storeDetail.PricePercentagePackStr, &pricePercentagePack)
				if partner.IsMultiStore(vendorID) {
					mulitStoreSkuHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler)
					skuList, err := dao.GetSkusWithVendor(db, []int{vendorID}, nil, nil, nil, false)
					if err != nil {
						return "", err
					}
					bareStoreSkuMap := make(map[string][]*partner.StoreSkuInfo)
					for _, sku := range skuList {
						bareStoreSkuMap[sku.VendorOrgCode] = append(bareStoreSkuMap[sku.VendorOrgCode], &partner.StoreSkuInfo{
							SkuID:       sku.ID,
							VendorSkuID: sku.VendorSkuID,
						})
					}
					for _, orgCode := range apimanager.CurAPIManager.GetAppOrgCodeList(vendorID) {
						outStoreSkuList, _ := mulitStoreSkuHandler.GetStoreSkusBareInfo(ctx, orgCode, task, storeID, storeDetail.VendorStoreID, bareStoreSkuMap[orgCode])
						for _, sku := range outStoreSkuList {
							price, nameID := GetSkuNamePrice(db, sku.SkuID, sku.VendorPrice)
							if skuNameMap[nameID] < price {
								skuNameMap[nameID] = price
							}
						}
						for k, v := range skuNameMap {
							unitPrice := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, int(v))
							storeSkuBindInfo := &StoreSkuBindInfo{
								StoreID:   storeID,
								NameID:    k,
								UnitPrice: unitPrice,
							}
							param = append(param, storeSkuBindInfo)
						}
					}
				} else {
					singleStoreHandler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreStoreSkuHandler)
					outSkuNameList, _ := singleStoreHandler.GetStoreSkusFullInfo(ctx, task, storeID, storeDetail.VendorStoreID, nil)
					for _, skuName := range outSkuNameList {
						sku := skuName.SkuList[0]
						price, nameID := GetSkuNamePrice(db, sku.SkuID, sku.VendorPrice)
						if skuNameMap[nameID] < price {
							skuNameMap[nameID] = price
						}
					}
					for k, v := range skuNameMap {
						unitPrice := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, int(v))
						storeSkuBindInfo := &StoreSkuBindInfo{
							StoreID:   storeID,
							NameID:    k,
							UnitPrice: unitPrice,
						}
						param = append(param, storeSkuBindInfo)
					}
				}
			}
		case 1:
			_, err = UpdateStoresSkusByBind(ctx, nil, param, isAsync, isContinueWhenError, false)
		}
		return result, err
	}
	taskSeq := tasksch.NewSeqTask2("根据平台价刷新京西平台价", ctx, isContinueWhenError, taskSeqFunc, 2)
	tasksch.HandleTask(taskSeq, nil, true).Run()
	if !isAsync {
		_, err = taskSeq.GetResult(0)
		hint = "1"
	} else {
		hint = taskSeq.GetID()
	}
	return hint, err
}

func GetSkuNamePrice(db *dao.DaoDB, skuID int, orgPrice int64) (price int64, nameID int) {
	var (
		specQuality float64
	)
	skuList, _ := dao.GetSkus(db, []int{skuID}, nil, nil, nil, nil)
	for _, v := range skuList {
		if v.Unit == model.SpecialUnit {
			if v.SpecUnit == model.SpecUnitNames[1] || v.SpecUnit == model.SpecUnitNames[2] {
				specQuality = float64(v.SpecQuality) * 1000
			} else {
				specQuality = float64(v.SpecQuality)
			}
			price = utils.Float64TwoInt64(utils.Int2Float64(model.SpecialSpecQuality) / specQuality * utils.Int2Float64(int(orgPrice)))
		} else {
			price = orgPrice
		}
		nameID = v.NameID
	}
	return price, nameID
}

func BackUpStoreSkuBind(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		db         = dao.GetDB()
		snapshotAt = utils.Time2Date(time.Now())
	)
	storeSkuBindHis := &model.StoreSkuBindHistory{
		SnapshotAt: snapshotAt.AddDate(0, 0, -2),
	}
	dao.DeleteEntity(db, storeSkuBindHis, "SnapshotAt")
	storeList, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "")
	task := tasksch.NewParallelTask("BackUpStoreSkuBind", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			store := batchItemList[0].(*model.Store)
			storeSku, err := dao.GetStoresSkusInfo(db, []int{store.ID}, nil)
			var storeSkuBindHiss []*model.StoreSkuBindHistory
			for _, v := range storeSku {
				storeSkuBindHis := &model.StoreSkuBindHistory{}
				storeSkuBindHis.StoreSkuBind = *v
				storeSkuBindHis.StoreSkuBindID = v.ID
				storeSkuBindHis.StoreSkuBind.ID = 0
				storeSkuBindHis.SnapshotAt = utils.Time2Date(snapshotAt)
				storeSkuBindHiss = append(storeSkuBindHiss, storeSkuBindHis)
			}
			if len(storeSkuBindHiss) > 0 {
				dao.CreateMultiEntities(db, storeSkuBindHiss)
			}
			return retVal, err
		}, storeList)
	tasksch.HandleTask(task, nil, true).Run()
	if isAsync {
		hint = task.GetID()
	} else {
		_, err = task.GetResult(0)
		hint = "1"
	}
	return hint, err
}

func ReturnStoreSkuBind(ctx *jxcontext.Context, snapshotAt string, storeIDs, skuIDs []int) (err error) {
	var (
		spAt = utils.Time2Date(utils.Str2Time(snapshotAt))
		db   = dao.GetDB()
	)
	if len(storeIDs) == 0 {
		return fmt.Errorf("必须选择一个门店！")
	}
	if len(storeIDs) > 1 {
		return fmt.Errorf("暂时只支持一次操作一个门店！")
	}
	//删除原门店商品
	sql := `DELETE FROM store_sku_bind WHERE store_id IN(` + dao.GenQuestionMarks(len(storeIDs)) + `)`
	sqlParams := []interface{}{storeIDs}
	if len(skuIDs) > 0 {
		sql += " AND sku_id IN(" + dao.GenQuestionMarks(len(skuIDs)) + ")"
		sqlParams = append(sqlParams, skuIDs)
	}
	_, err = dao.ExecuteSQL(db, sql, sqlParams)
	//查询备份的门店商品
	var storeHiss []*model.StoreSkuBindHistory
	sql2 := `SELECT * FROM store_sku_bind_history WHERE snapshot_at = ? AND store_id IN(` + dao.GenQuestionMarks(len(storeIDs)) + `)`
	sqlParams2 := []interface{}{spAt, storeIDs}
	if len(skuIDs) > 0 {
		sql2 += " AND sku_id IN(" + dao.GenQuestionMarks(len(skuIDs)) + ")"
		sqlParams2 = append(sqlParams2, skuIDs)
	}
	err = dao.GetRows(db, &storeHiss, sql2, sqlParams2)
	//插入到现在
	var storeSkus []*model.StoreSkuBind
	for _, v := range storeHiss {
		storeSku := &model.StoreSkuBind{}
		storeSku = &v.StoreSkuBind
		storeSku.ID = v.StoreSkuBindID
		storeSkus = append(storeSkus, storeSku)
	}
	if len(storeSkus) > 0 {
		dao.CreateMultiEntities(db, storeSkus)
	}
	return err
}

func RefreshMatterStock(ctx *jxcontext.Context, skuID int) (err error) {
	//	var (
	//		db           = dao.GetDB()
	//		skuBindInfos []*StoreSkuBindInfo
	//	)
	//	if skuID != 0 {
	//		skus, err := dao.GetSkus(db, []int{skuID}, nil, nil, nil, nil)
	//		if err != nil || len(skus) == 0 {
	//			return err
	//		}
	//		result, err := api.JdEclpAPI.QueryStock(skus[0].EclpID)
	//		if err != nil || result == nil {
	//			return err
	//		}
	//		var isSale = 0
	//		if len(result) == 0 {
	//			isSale = -1
	//		} else {
	//			if result[0].UsableNum > 0 {
	//				isSale = 1
	//			} else {
	//				isSale = -1
	//			}
	//		}
	//		storeSkuList, _ := dao.GetStoresSkusInfo(db, nil, []int{skuID})
	//		for _, vv := range storeSkuList {
	//			var storeSkuBindSkus []*StoreSkuBindSkuInfo
	//			storeSkuBindSku := &StoreSkuBindSkuInfo{
	//				SkuID:  vv.SkuID,
	//				IsSale: isSale,
	//			}
	//			storeSkuBindSkus = append(storeSkuBindSkus, storeSkuBindSku)
	//			storeSkuBind := &StoreSkuBindInfo{
	//				StoreID: vv.StoreID,
	//				// NameID:  v.SkuNameID,
	//				Skus: storeSkuBindSkus,
	//			}
	//			skuBindInfos = append(skuBindInfos, storeSkuBind)
	//		}
	//	} else {
	//		go func() {
	//			result, _ := api.JdEclpAPI.QueryStock("")
	//			for _, v := range result {
	//				skus, err := dao.GetSkus(db, nil, nil, nil, nil, []string{v.GoodsNo})
	//				if err != nil || len(skus) == 0 {
	//					continue
	//				}
	//				var isSale = 0
	//				if v.UsableNum > 0 {
	//					isSale = 1
	//				} else {
	//					isSale = -1
	//				}
	//				storeSkuList, _ := dao.GetStoresSkusInfo(db, nil, []int{skus[0].ID})
	//				for _, vv := range storeSkuList {
	//					var storeSkuBindSkus []*StoreSkuBindSkuInfo
	//					storeSkuBindSku := &StoreSkuBindSkuInfo{
	//						SkuID:  vv.SkuID,
	//						IsSale: isSale,
	//					}
	//					storeSkuBindSkus = append(storeSkuBindSkus, storeSkuBindSku)
	//					storeSkuBind := &StoreSkuBindInfo{
	//						StoreID: vv.StoreID,
	//						NameID:  skus[0].NameID,
	//						Skus:    storeSkuBindSkus,
	//					}
	//					skuBindInfos = append(skuBindInfos, storeSkuBind)
	//				}
	//			}
	//			//物料店666666
	//			updateStoresSkusWithoutSync(ctx, db, []int{666666}, skuBindInfos, false, false)
	//		}()
	return
}

func buildStoreSkuBindInfosAndFocus(ctx *jxcontext.Context, db *dao.DaoDB, store *dao.StoreDetail, v *partner.SkuNameInfo, nameID int) (err error) {
	var (
		pricePercentagePack  []*model.PricePercentageItem
		pricePercentagePack2 []*model.PricePercentageItem
		jxPrice              = 0
	)
	skus, _ := dao.GetSkus(db, nil, []int{nameID}, nil, nil, nil)
	err = jxutils.Strings2Objs(store.PricePercentagePackStr, &pricePercentagePack)
	price := jxutils.CaculateJxPriceByPricePack(pricePercentagePack, 0, int(v.SkuList[0].VendorPrice))
	store2, _ := dao.GetStoreDetail(db, store.ID, model.VendorIDJX, "")
	if store2 != nil {
		err = jxutils.Strings2Objs(store2.PricePercentagePackStr, &pricePercentagePack2)
		jxPrice = jxutils.CaculatePriceByPricePack(pricePercentagePack2, 0, price)
	}
	skuBind := &model.StoreSkuBind{
		StoreID:        store.ID,
		UnitPrice:      price,
		Price:          price,
		Status:         model.StoreSkuBindStatusNormal,
		YbID:           utils.Str2Int64(v.SkuList[0].VendorSkuID),
		YbPrice:        int(v.SkuList[0].VendorPrice),
		JxPrice:        jxPrice,
		YbSyncStatus:   0,
		MtwmSyncStatus: model.SyncFlagNewMask,
		TaoSyncStatus:  model.SyncFlagNewMask,
		JdSyncStatus:   model.SyncFlagNewMask,
		EbaiSyncStatus: model.SyncFlagNewMask,
	}
	if len(skus) > 0 {
		skuBind.SkuID = skus[0].ID
	}
	dao.WrapAddIDCULDEntity(skuBind, ctx.GetUserName())
	err = dao.CreateEntity(db, skuBind)
	return err
}

func CreateSkusAndFocusFromWx(ctx *jxcontext.Context, productInfo *jdapi.ProductInfo, price, storeID int) (err error) {
	var (
		db            = dao.GetDB()
		skuBindInfos  []*StoreSkuBindInfo
		outSkuNameExt *model.SkuNameExt
	)

	if productInfo == nil {
		return fmt.Errorf("未查询到相关商品！")
	}
	if price == 0 {
		return fmt.Errorf("请输入商品价格！")
	}
	focusSku := func(nameID int) {
		skuBindInfo := &StoreSkuBindInfo{
			StoreID:   storeID,
			NameID:    nameID,
			UnitPrice: price,
			IsFocus:   1,
			IsSale:    1,
		}
		skuBindInfos = append(skuBindInfos, skuBindInfo)
		_, err := UpdateStoresSkusByBind(ctx, nil, skuBindInfos, true, true, false)
		if err != nil {
			err = nil
		}
	}
	skuNames, err := dao.GetSkuNames(db, nil, []string{productInfo.UpcCode}, "", false)
	if err != nil {
		return err
	}
	if len(skuNames) == 0 {
		if productInfo.Name == "" {
			var originName string
			if strings.Contains(productInfo.OriginalName, upcSpecName1) {
				originName = strings.ReplaceAll(productInfo.OriginalName, upcSpecName1, "")
			}
			if strings.Contains(productInfo.OriginalName, "*") {
				originName = strings.ReplaceAll(productInfo.OriginalName, "*", "")
			}
			_, name, _, specUnit, unit, specQuality := jxutils.SplitSkuName(originName)
			productInfo.Name = name
			productInfo.SpecQuality = specQuality
			productInfo.Unit = unit
			productInfo.SpecUnit = specUnit
		}
		if productInfo.Name == "" {
			if result, err := api.MtwmAPI.GetStandardProductListWithCond(productInfo.UpcCode); err == nil && result != nil {
				productInfo.OriginalName = result.Name
				productInfo.OriginalSpec = result.Spec
				productInfo.Name = result.Name
				productInfo.SpecUnit = result.SpecUnit
				productInfo.Unit = result.Unit
				productInfo.SpecQuality = float32(result.SpecNew)
				productInfo.ImgList = strings.Split(result.Pic, ",")
				productInfo.Weight = float32(result.Weight)
				productInfo.BrandName = result.BrandNamePath
			}
		}
		skuNameExt := &model.SkuNameExt{
			SkuName: model.SkuName{
				Name:       productInfo.Name,
				Upc:        &productInfo.UpcCode,
				Status:     model.SkuStatusNormal,
				CategoryID: model.NoCatCatgoryID,
				IsGlobal:   model.YES,
				Unit:       productInfo.Unit,
			},
			Skus: []*model.SkuWithVendor{
				&model.SkuWithVendor{
					Sku: &model.Sku{},
				},
			},
			// Places: []int{510100}, //默认成都
		}
		skuNameExt.Price = price
		skuNameExt.Skus[0].SpecQuality = productInfo.SpecQuality
		skuNameExt.Skus[0].SpecUnit = productInfo.SpecUnit
		skuNameExt.Skus[0].Weight = int(utils.Str2Int64(utils.Float64ToStr(math.Round(float64(productInfo.Weight)))))
		skuNameExt.Skus[0].Status = model.SkuStatusNormal
		if len(productInfo.ImgList) > 0 {
			setImgs(skuNameExt, productInfo.ImgList)
		}
		//可能就是没查出来
		if skuNameExt.Name == "" {
			skuNameExt.Name = productInfo.OriginalName
			skuNameExt.Unit = model.UnitNames[5]
			skuNameExt.Skus[0].SpecQuality = 100
			skuNameExt.Skus[0].SpecUnit = model.SpecUnitNames[0]
			skuNameExt.Skus[0].Weight = 100
		}
		outSkuNameExt, err = AddSkuName(ctx, skuNameExt, ctx.GetUserName())
		if err != nil {
			if _, ok := err.(*SyncError); ok {
				err = nil
			} else {
				return err
			}
		} else {
			focusSku(outSkuNameExt.ID)
		}
	} else {
		focusSku(skuNames[0].ID)
	}
	return err
}

func SyncMatterC4ToGy(ctx *jxcontext.Context, isContinueWhenError, isAsync bool) (hint string, err error) {
	var (
		db            = dao.GetDB()
		skus          []*model.Sku
		skusgy        []*model.Sku
		eclpIDs       []string
		addMatters    []*model.Sku
		deleteMatters []*model.Sku
		updateMatters []*model.Sku
	)
	if globals.IsMainProductEnv() {
		return "", fmt.Errorf("此接口只允许在果园上调用！")
	}
	task := tasksch.NewParallelTask("同步物料商品从菜市到果园", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			step := batchItemList[0].(int)
			var (
				skusMap   = make(map[string]*model.Sku)
				skusgyMap = make(map[string]*model.Sku)
			)
			switch step {
			case 0:
				sql := "SELECT * FROM jxd_dev_0.sku WHERE deleted_at = ? AND eclp_id <> ''"
				sqlParams := []interface{}{utils.DefaultTimeValue}
				err = dao.GetRows(db, &skus, sql, sqlParams)
				for _, c4 := range skus {
					eclpIDs = append(eclpIDs, c4.EclpID)
					skusMap[c4.EclpID] = c4
				}
				sql2 := "SELECT * FROM sku WHERE deleted_at = ? AND eclp_id IN (" + dao.GenQuestionMarks(len(eclpIDs)) + ")"
				sqlParams = append(sqlParams, eclpIDs)
				err = dao.GetRows(db, &skusgy, sql2, sqlParams)
				for _, gy := range skusgy {
					skusgyMap[gy.EclpID] = gy
					if skusMap[gy.EclpID] == nil {
						deleteMatters = append(deleteMatters, skusgyMap[gy.EclpID])
					}
				}
				for _, c4 := range skus {
					if skusgyMap[c4.EclpID] == nil {
						addMatters = append(addMatters, skusMap[c4.EclpID])
					} else {
						updateMatters = append(updateMatters, skusMap[c4.EclpID])
					}
				}
			case 1:
				if len(deleteMatters) > 0 {
					for _, v := range deleteMatters {
						_, err = DeleteSkuName(ctx, v.NameID, ctx.GetUserName())
					}
				}
				if len(addMatters) > 0 {
					for _, v := range addMatters {
						var (
							skuName  *model.SkuName
							skuNames []*model.SkuName
						)
						sql := `
							SELECT t1.*
							FROM jxd_dev_0.sku_name t1
							WHERE t1.deleted_at = ?
							AND t1.id = ?
						`
						sqlParams := []interface{}{utils.DefaultTimeValue, v.NameID}
						err = dao.GetRows(db, &skuNames, sql, sqlParams)
						if len(skuNames) > 0 {
							skuName = skuNames[0]
						}
						skuNameExt := &model.SkuNameExt{
							SkuName: model.SkuName{},
							Skus: []*model.SkuWithVendor{
								&model.SkuWithVendor{
									Sku: &model.Sku{},
								},
							},
						}
						skuNameExt.Unit = skuName.Unit
						skuNameExt.Name = skuName.Name
						skuNameExt.Upc = skuName.Upc
						skuNameExt.Img = skuName.Img
						skuNameExt.Img2 = skuName.Img2
						skuNameExt.DescImg = skuName.DescImg
						skuNameExt.Price = skuName.Price
						skuNameExt.IsGlobal = model.YES
						skuNameExt.Status = model.SkuStatusNormal
						// skuNameExt.ID = v.NameID
						skuNameExt.CategoryID = 3024 //默认全放物料分类下
						skuNameExt.DeletedAt = utils.DefaultTimeValue
						skuNameExt.CreatedAt = time.Now()
						skuNameExt.LastOperator = ctx.GetUserName()
						txDB, _ := dao.Begin(db)
						defer func() {
							if r := recover(); r != nil {
								dao.Rollback(db, txDB)
								panic(r)
							}
						}()
						if err = dao.CreateEntity(db, &skuNameExt.SkuName); err != nil {
							dao.Rollback(db, txDB)
							return retVal, err
						}
						// skuNameExt.Skus[0].ID = v.ID
						skuNameExt.Skus[0].SpecQuality = v.SpecQuality
						skuNameExt.Skus[0].SpecUnit = v.SpecUnit
						skuNameExt.Skus[0].EclpID = v.EclpID
						skuNameExt.Skus[0].Status = model.SkuStatusNormal
						skuNameExt.Skus[0].NameID = skuNameExt.ID
						skuNameExt.Skus[0].DeletedAt = utils.DefaultTimeValue
						skuNameExt.Skus[0].CreatedAt = time.Now()
						skuNameExt.Skus[0].LastOperator = ctx.GetUserName()
						if err = dao.CreateEntity(db, skuNameExt.Skus[0].Sku); err != nil {
							dao.Rollback(db, txDB)
							return retVal, err
						}
						dao.Commit(db, txDB)
						focusC4Matters(ctx, db, v)
					}
				}
				if len(updateMatters) > 0 {
					for _, v := range updateMatters {
						var (
							skuName  *model.SkuName
							skuNames []*model.SkuName
						)
						sql := `
							SELECT t1.*
							FROM jxd_dev_0.sku_name t1, jxd_dev_0.sku t2
							WHERE t1.id = t2.name_id 
							AND t1.deleted_at = ?
							AND t2.eclp_id = ?
						`
						sqlParams := []interface{}{utils.DefaultTimeValue, v.EclpID}
						err = dao.GetRows(db, &skuNames, sql, sqlParams)
						if len(skuNames) > 0 {
							skuName = skuNames[0]
						}
						txDB, _ := dao.Begin(db)
						defer func() {
							if r := recover(); r != nil {
								dao.Rollback(db, txDB)
								panic(r)
							}
						}()
						if v != nil {
							sql := `
								UPDATE sku SET spec_quality = ?, spec_unit = ?, eclp_id = ?, last_operator = ?
								WHERE eclp_id = ?
								AND deleted_at = ?
							`
							sqlParams := []interface{}{v.SpecQuality, v.SpecUnit, v.EclpID, ctx.GetUserName(), v.EclpID, utils.DefaultTimeValue}
							if _, err = dao.ExecuteSQL(db, sql, sqlParams); err != nil {
								dao.Rollback(db, txDB)
								return retVal, err
							}
						}
						if skuName != nil {
							sql := `
								UPDATE sku_name a
								JOIN sku b ON a.id = b.name_id
								SET a.name = ?, a.img = ?, a.img2 = ?, a.desc_img = ?, a.unit = ?, a.price = ?, a.last_operator = ?
								WHERE b.eclp_id = ?
								AND a.deleted_at = ?
								AND 1=1
							`
							sqlParams := []interface{}{skuName.Name, skuName.Img, skuName.Img2, skuName.DescImg, skuName.Unit, skuName.Price,
								ctx.GetUserName(), v.EclpID, utils.DefaultTimeValue}
							if _, err = dao.ExecuteSQL(db, sql, sqlParams); err != nil {
								dao.Rollback(db, txDB)
								return retVal, err
							}
						}
						dao.Commit(db, txDB)
						focusC4Matters(ctx, db, v)
					}
				}
			}
			return retVal, err
		}, []int{0, 1})
	tasksch.HandleTask(task, nil, true).Run()
	if !isAsync {
		_, err = task.GetResult(0)
		hint = "1"
	} else {
		hint = task.GetID()
	}
	return hint, err
}

func focusC4Matters(ctx *jxcontext.Context, db *dao.DaoDB, v *model.Sku) (err error) {
	var storeSkus []*model.StoreSkuBind
	sql := `
		SELECT * 
		FROM jxd_dev_0.store_sku_bind a, jxd_dev_0.sku b 
		WHERE a.sku_id = b.id
		AND a.store_id = ? 
		AND b.eclp_id = ? 
		AND a.deleted_at = ?
	`
	sqlParams2 := []interface{}{model.MatterStoreID, v.EclpID, utils.DefaultTimeValue}
	err = dao.GetRows(db, &storeSkus, sql, sqlParams2)
	if err != nil {
		return err
	}
	if len(storeSkus) > 0 {
		var (
			skuBindInfos []*StoreSkuBindInfo
			skuName2     *model.SkuName
		)
		sql := `
			SELECT a.* FROM sku_name a, sku b WHERE a.id = b.name_id AND b.eclp_id = ? AND a.deleted_at = ?
		`
		sqlParams := []interface{}{v.EclpID, utils.DefaultTimeValue}
		err = dao.GetRow(db, &skuName2, sql, sqlParams)
		if skuName2 != nil {
			skuBindInfo := &StoreSkuBindInfo{
				NameID:    skuName2.ID,
				IsFocus:   1,
				UnitPrice: skuName2.Price,
			}
			skuBindInfos = append(skuBindInfos, skuBindInfo)
		}
		updateStoresSkusWithoutSync(ctx, db, []int{model.MatterStoreID}, skuBindInfos, false, false)
	}
	return err
}

func RefreshJdsSkusStatus(ctx *jxcontext.Context) (err error) {
	handler := partner.GetPurchasePlatformFromVendorID(model.VendorIDJDShop).(partner.ISingleStoreStoreSkuHandler)
	remoteSkuList, err := handler.GetStoreSkusFullInfo(ctx, nil, 0, "", nil)
	for _, v := range remoteSkuList {
		if v.Status == jdshopapi.JdsSkuStatus2 {
			reason, err := api.JdShopAPI.FindOpReason(utils.Str2Int64(v.VendorNameID))
			if err == nil && reason == jdshopapi.PassReason {
				err = api.JdShopAPI.UpOrDown(utils.Str2Int64(v.VendorNameID), jdshopapi.JdsSkuStatus1)
			}
		}
	}
	return err
}

func GetStoreSkuAudit(ctx *jxcontext.Context, storeIDs, nameIDs, skuIDs, statuss, types []int, name, remark, keyword, cityName, marketManPhone, applyTimeStart, applyTimeEnd, auditTimeStart, auditTimeEnd string, pageSize, offset int) (pagedInfo *model.PagedInfo, err error) {
	var (
		applyTimeStartp, applyTimeEndp, auditTimeStartp, auditTimeEndp time.Time
	)
	if applyTimeStart != "" {
		applyTimeStartp = utils.Str2Time(applyTimeStart)
	}
	if applyTimeEnd != "" {
		applyTimeEndp = utils.Str2Time(applyTimeEnd)
	}
	if auditTimeStart != "" {
		auditTimeStartp = utils.Str2Time(auditTimeStart)
	}
	if auditTimeEnd != "" {
		auditTimeEndp = utils.Str2Time(auditTimeEnd)
	}
	//权限
	if permission.IsRoled(ctx) {
		if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
			var storeIDs2 []int
			if len(storeIDs) > 0 {
				for _, v := range storeIDs {
					if storeIDsMap[v] != 0 {
						storeIDs2 = append(storeIDs2, v)
					}
				}
				if len(storeIDs2) == 0 {
					storeIDs2 = append(storeIDs2, -1)
				}
			} else {
				for k, _ := range storeIDsMap {
					storeIDs2 = append(storeIDs2, k)
				}
			}
			storeIDs = nil
			storeIDs = storeIDs2
		}
	}
	pagedInfo, err = dao.GetStoreSkuAudit(dao.GetDB(), storeIDs, nameIDs, skuIDs, statuss, types, name, remark, keyword, marketManPhone, cityName, applyTimeStartp, applyTimeEndp, auditTimeStartp, auditTimeEndp, pageSize, offset)
	return pagedInfo, err
}

func doStoreSkuAudit(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (isAudit bool, err error) {
	time.Sleep(time.Second / 5)
	db := dao.GetDB()
	for _, storeID := range storeIDs {
		stores, _ := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "")
		//扣点的门店改价不进审核
		if len(stores) > 0 {
			if stores[0].PayPercentage <= 50 || stores[0].StoreLevel == "E" || stores[0].StoreLevel == "D" {
				return false, err
			}
		}
		for _, skuBindInfo := range skuBindInfos {
			storeAudits, err := dao.GetStoreSkuAuditLight(db, []int{storeID}, []int{skuBindInfo.NameID}, model.StoreAuditStatusOnline)
			//取消关注，可售排除
			if skuBindInfo.IsFocus == -1 || skuBindInfo.IsSale != 0 || skuBindInfo.UnitPrice == 0 {
				return false, err
			}

			if ctx.GetLoginType() != weixin.AuthTypeMP && ctx.GetLoginType() != weixin.AuthTypeMini && ctx.GetLoginType() != weixin.AuthTypeWxApp && ctx.GetLoginType() != weixin.AuthTypeWxAppCaishi && ctx.GetLoginType() != auth2.AuthTypeMobile {
				authInfo, err := ctx.GetV2AuthInfo()
				if err == nil && authInfo != nil && (ctx.GetFullUser().Type&model.UserTypeOperator) != 0 {
					if len(storeAudits) > 0 {
						storeAudits[0].DeletedAt = time.Now()
						dao.UpdateEntity(db, storeAudits[0], "DeletedAt")
						// if globals.IsProductEnv() {
						// 	skuAndNames, err := dao.GetSkus(db, nil, []int{skuBindInfo.NameID}, nil, nil, nil)
						// 	if len(skuAndNames) > 0 && err == nil {
						// 		weixinmsg.NotifyStoreOpRequestStatus(true, storeID, skuBindInfo.NameID, jxutils.ComposeSpuName(skuAndNames[0].Prefix, skuAndNames[0].Name, 0), storeAudits[0].OriginUnitPrice, skuBindInfo.UnitPrice, "")
						// 	}
						// }
					}
					return false, err
				}
			}

			storeSkuAudit := &model.StoreSkuAudit{
				StoreID:   storeID,
				NameID:    skuBindInfo.NameID,
				Status:    model.StoreAuditStatusOnline,
				UnitPrice: skuBindInfo.UnitPrice,
			}
			//如果是关注改价
			if skuBindInfo.IsFocus == 1 {
				if len(storeAudits) > 0 {
					storeAudits[0].DeletedAt = time.Now()
					dao.UpdateEntity(db, storeAudits[0], "DeletedAt")
				}
				storeSkuAudit.Type = model.StoreSkuAuditTypeFocus
			} else {
				storeSkus, err := dao.GetStoreSkusByNameIDs(db, []int{storeID}, skuBindInfo.NameID)
				if len(storeSkus) > 0 {
					//如果改的价比原价低排除
					if skuBindInfo.UnitPrice <= int(storeSkus[0].UnitPrice) {
						if len(storeAudits) > 0 {
							storeAudits[0].DeletedAt = time.Now()
							dao.UpdateEntity(db, storeAudits[0], "DeletedAt")
						}
						globals.SugarLogger.Debugf("doStoreSkuAudit return4 storeID : %v nameID: %v", storeID, skuBindInfo.NameID)
						return false, err
					} else {
						if len(storeAudits) > 0 {
							storeAudits[0].DeletedAt = time.Now()
							dao.UpdateEntity(db, storeAudits[0], "DeletedAt")
						}
					}
				} else {
					return false, fmt.Errorf("未查询到该门店商品价，storeID: %v, nameID: %v", storeID, skuBindInfo.NameID)
				}
				storeSkuAudit.Type = model.StoreSkuAuditTypePrice
				storeSkuAudit.OriginUnitPrice = int(storeSkus[0].UnitPrice)
			}
			dao.WrapAddIDCULDEntity(storeSkuAudit, ctx.GetUserName())
			err = dao.CreateEntity(db, storeSkuAudit)
		}
	}
	return true, err
}

func doStoreSkuAuditForGy(ctx *jxcontext.Context, storeIDs []int, skuBindInfos []*StoreSkuBindInfo) (isAudit bool, err error) {
	time.Sleep(time.Second / 5)
	db := dao.GetDB()
	for _, storeID := range storeIDs {
		// stores, _ := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "")
		// 扣点的门店改价不进审核
		// if len(stores) > 0 {
		// 	if stores[0].PayPercentage <= 50 || stores[0].StoreLevel == "E" || stores[0].StoreLevel == "D" {
		// 		globals.SugarLogger.Debugf("doStoreSkuAudit return0 storeID : %v", storeID)
		// 		return false, err
		// 	}
		// }
		for _, skuBindInfo := range skuBindInfos {
			storeAudits, err := dao.GetStoreSkuAuditLight(db, []int{storeID}, []int{skuBindInfo.NameID}, model.StoreAuditStatusOnline)
			//取消关注，可售排除
			if skuBindInfo.IsFocus == -1 || skuBindInfo.IsSale != 0 || skuBindInfo.UnitPrice == 0 {
				return false, err
			}

			if ctx.GetLoginType() != weixin.AuthTypeMP && ctx.GetLoginType() != weixin.AuthTypeMini && ctx.GetLoginType() != weixin.AuthTypeWxApp && ctx.GetLoginType() != weixin.AuthTypeWxAppCaishi && ctx.GetLoginType() != auth2.AuthTypeMobile {
				authInfo, err := ctx.GetV2AuthInfo()
				if err == nil && authInfo != nil && (ctx.GetFullUser().Type&model.UserTypeOperator) != 0 {
					if len(storeAudits) > 0 {
						storeAudits[0].DeletedAt = time.Now()
						dao.UpdateEntity(db, storeAudits[0], "DeletedAt")
					}
					globals.SugarLogger.Debugf("doStoreSkuAudit return3 storeID : %v nameID: %v", storeID, skuBindInfo.NameID)
					return false, err
				}
			}
			storeSkuAudit := &model.StoreSkuAudit{
				StoreID:   storeID,
				NameID:    skuBindInfo.NameID,
				Status:    model.StoreAuditStatusCreated,
				UnitPrice: skuBindInfo.UnitPrice,
			}
			//如果是关注改价
			if skuBindInfo.IsFocus == 1 {
				if len(storeAudits) > 0 {
					storeAudits[0].DeletedAt = time.Now()
					dao.UpdateEntity(db, storeAudits[0], "DeletedAt")
				}
				storeSkuAudit.Type = model.StoreSkuAuditTypeFocus
			} else {
				storeSkus, _ := dao.GetStoreSkusByNameIDs(db, []int{storeID}, skuBindInfo.NameID)
				if len(storeSkus) > 0 {
					if len(storeAudits) > 0 {
						storeAudits[0].DeletedAt = time.Now()
						dao.UpdateEntity(db, storeAudits[0], "DeletedAt")
					}
				} else {
					return false, fmt.Errorf("未查询到该门店商品价，storeID: %v, nameID: %v", storeID, skuBindInfo.NameID)
				}
				storeSkuAudit.Type = model.StoreSkuAuditTypePrice
				storeSkuAudit.OriginUnitPrice = int(storeSkus[0].UnitPrice)
			}
			dao.WrapAddIDCULDEntity(storeSkuAudit, ctx.GetUserName())
			err = dao.CreateEntity(db, storeSkuAudit)
		}
	}
	return true, err
}

func StoreSkuPriceAudit(ctx *jxcontext.Context, storeSkuAudits []*model.StoreSkuAudit, status int, isAsync, isContinueWhenError bool) (storeSkuAudits2 []*model.StoreSkuAudit, hint string, err error) {
	db := dao.GetDB()
	multiFlag := false
	if status == model.StoreAuditStatusOnline {
		return nil, "", fmt.Errorf("审核标志不正确！")
	}
	//证明是预审核
	if status == 2 {
		for _, v := range storeSkuAudits {
			skuList, _ := dao.GetStoreSkusByNameIDs(db, []int{v.StoreID}, v.NameID)
			if len(skuList) > 0 {
				if int64(v.AuditPrice) > skuList[0].UnitPrice*2 {
					storeSkuAudits2 = append(storeSkuAudits2, v)
				}
			}
		}
		return storeSkuAudits2, "", err
	}
	//批量的时候如果有改超出两倍价格或者在做活动的商品就滤掉
	if len(storeSkuAudits) > 1 {
		multiFlag = true
	}
	deletedDuplicateWaitAuditData(ctx, db)
	task := tasksch.NewParallelTask("StoreSkuPriceAudit", tasksch.NewParallelConfig().SetParallelCount(5).SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeAudit := batchItemList[0].(*model.StoreSkuAudit)
			storeAudits, err := dao.GetStoreSkuAuditLight(db, []int{storeAudit.StoreID}, []int{storeAudit.NameID}, model.StoreAuditStatusOnline)
			if len(storeAudits) == 0 || err != nil {
				return retVal, err
			}
			if len(storeAudits) > 1 {
				return retVal, fmt.Errorf("查询到该门店该商品的待审核信息大于1条！storeID: %v, nameID: %v", storeAudit.StoreID, storeAudit.NameID)
			}
			//审核通过
			if status == model.StoreAuditStatusCreated {
				if multiFlag {
					var skuIDList []int
					skuList, _ := dao.GetStoreSkusByNameIDs(db, []int{storeAudit.StoreID}, storeAudit.NameID)
					if len(skuList) > 0 {
						if int64(storeAudit.AuditPrice) > skuList[0].UnitPrice*2 {
							return nil, fmt.Errorf("修改的价格超出原价格两倍，请单个审核！门店ID:[%v]，商品nameID:[%v]，商品名：[%v]", storeAudit.StoreID, storeAudit.NameID, skuList[0].Name)
						}
						for _, v := range skuList {
							skuIDList = append(skuIDList, v.SkuID)
						}
					}
					actList, _ := dao.QueryActs(db, 0, 0, 1, -1, "", -1, []int{model.ActStatusCreated},
						[]int{model.ActSkuDirectDown, model.ActSkuSecKill, model.ActSkuDiscount}, nil, storeAudit.StoreID, skuIDList, 0,
						time.Now().AddDate(0, -3, 0), time.Now(), utils.ZeroTimeValue, utils.ZeroTimeValue)
					if actList != nil {
						if actList.Data != nil {
							if len(actList.Data) > 0 {
								return nil, fmt.Errorf("审核的商品正在做活动，请单个审核！活动ID:[%v]，门店ID:[%v]，商品nameID:[%v]，商品名：[%v]", actList.Data[0].ID, storeAudit.StoreID, storeAudit.NameID, skuList[0].Name)
							}
						}
					}
				}
				storeAudits[0].UserID = ctx.GetUserID()
				storeAudits[0].Status = model.StoreAuditStatusCreated
				storeAudits[0].Remark = storeAudit.Remark
				storeAudits[0].AuditPrice = storeAudit.AuditPrice
				_, err = dao.UpdateEntity(db, storeAudits[0], "UserID", "Status", "Remark", "AuditPrice")
				var skuBindInfos = []*StoreSkuBindInfo{
					&StoreSkuBindInfo{
						NameID:    storeAudit.NameID,
						UnitPrice: storeAudits[0].UnitPrice,
					},
				}
				//证明填了额外的审核价格
				if storeAudit.AuditPrice != 0 {
					skuBindInfos[0].UnitPrice = storeAudit.AuditPrice
				}
				//如果是关注改价
				if storeAudits[0].Type == model.StoreSkuAuditTypeFocus {
					skuBindInfos[0].IsFocus = 1
					skuBindInfos[0].IsSale = 1
				}
				var num int64
				db := dao.GetDB()
				skuIDs, err := updateStoresSkusWithoutSync(ctx, db, []int{storeAudit.StoreID}, skuBindInfos, false, false)
				if err != nil {
					return "", err
				}
				isAsync = asyncStoreSkuOpFilter(ctx, isAsync)
				num = int64(len(skuIDs))
				if num > 0 {
					hint, err = CurVendorSync.SyncStoresSkus(ctx, nil, 0, db, nil, []int{storeAudit.StoreID}, skuIDs, false, isAsync, isContinueWhenError)
				}
				if num == 0 || !isAsync || hint == "" {
					hint = utils.Int64ToStr(num)
				}
			} else if status == model.StoreAuditStatusRejected {
				storeAudits[0].UserID = ctx.GetUserID()
				storeAudits[0].Status = model.StoreAuditStatusRejected
				storeAudits[0].Remark = storeAudit.Remark
				_, err = dao.UpdateEntity(db, storeAudits[0], "UserID", "Status", "Remark")
			} else {
				return retVal, fmt.Errorf("审核标志不正确！")
			}
			//TODO 暂时先不推消息了
			if err == nil {
				// if globals.IsProductEnv() {
				// 	skuAndNames, err := dao.GetSkus(db, nil, []int{storeAudit.NameID}, nil, nil, nil)
				// 	if len(skuAndNames) > 0 && err == nil {
				// 		price := 0
				// 		if storeAudit.AuditPrice != 0 {
				// 			price = storeAudit.AuditPrice
				// 		} else {
				// 			price = storeAudits[0].UnitPrice
				// 		}
				// 		weixinmsg.NotifyStoreOpRequestStatus(flag, storeAudit.StoreID, storeAudit.NameID, jxutils.ComposeSpuName(skuAndNames[0].Prefix, skuAndNames[0].Name, 0), storeAudits[0].OriginUnitPrice, price, storeAudit.Remark)
				// 	}
				// }
			}
			return retVal, err
		}, storeSkuAudits)
	tasksch.HandleTask(task, nil, true).Run()
	if isAsync {
		hint = task.GetID()
	} else {
		_, err = task.GetResult(0)
		hint = "1"
	}
	return nil, hint, err
}

func deletedDuplicateWaitAuditData(ctx *jxcontext.Context, db *dao.DaoDB) {
	var (
		duplicateDatas []*model.StoreSkuAudit
		duplicateStore = make(map[int][]*model.StoreSkuAudit)
	)
	sql := `
		SELECT a.* 
		  FROM store_sku_audit a ,
		  (
			SELECT count(*), store_id ,name_id ,status, deleted_at
			  FROM store_sku_audit
			 WHERE status = 0
			   AND deleted_at = ?
		     GROUP BY 2,3,4,5
			HAVING count(*) > 1
		) b 
		WHERE a.store_id = b.store_id 
		  AND a.name_id = b.name_id 
		  AND a.status= b.status
		  AND a.deleted_at = b.deleted_at
	`
	sqlParams := []interface{}{utils.DefaultTimeValue}
	dao.GetRows(db, &duplicateDatas, sql, sqlParams)
	for _, v := range duplicateDatas {
		duplicateStore[v.StoreID] = append(duplicateStore[v.StoreID], v)
	}
	for _, v := range duplicateStore {
		duplicateSkuName := make(map[int][]*model.StoreSkuAudit)
		for _, vv := range v {
			duplicateSkuName[vv.NameID] = append(duplicateSkuName[vv.NameID], vv)
		}
		for _, vv := range duplicateSkuName {
			for i := 0; i < len(vv)-1; i++ {
				dao.DeleteEntity(db, vv[i])
			}
		}
	}
}

func GetSpecialtyStoreSkus(ctx *jxcontext.Context, storeIDs, vendorIDs []int) (err error) {
	type SpecialtyStoreSkus struct {
		StoreID    int     `json:"门店ID"`
		StoreName  string  `json:"门店名"`
		SkuID      int     `json:"SkuID"`
		SkuName    string  `json:"商品名"`
		Price      float64 `json:"平台价"`
		VendorName string  `json:"平台名"`
	}
	var (
		db                 = dao.GetDB()
		specialtyStoreSkus []*SpecialtyStoreSkus
		excelTitle         = []string{
			"门店ID",
			"门店名",
			"SkuID",
			"商品名",
			"平台价",
			"平台名",
		}
		sheetList             []*excel.Obj2ExcelSheetConfig
		downloadURL, fileName string
	)
	for _, v := range vendorIDs {
		for _, vv := range storeIDs {
			storeDetail, err := dao.GetStoreDetail(db, vv, v, "")
			if err != nil || storeDetail == nil {
				continue
			}
			if partner.IsMultiStore(v) {
				var (
					page         = 1
					vendorSkuIDs []string
				)
				_, totalCount, _ := api.JdPageAPI.GetJdTopSkus(storeDetail.VendorStoreID, page)
				for ; page < totalCount/10+1; page++ {
					result, _, _ := api.JdPageAPI.GetJdTopSkus(storeDetail.VendorStoreID, page)
					vendorSkuIDs = append(vendorSkuIDs, result...)
				}
				for _, vvv := range vendorSkuIDs {
					thingMap := &model.ThingMap{}
					sql := `
						SELECT * FROM thing_map WHERE thing_type = 3 AND vendor_thing_id = ? AND vendor_org_code = 320406
						AND deleted_at = ? 
					`
					sqlParams := []interface{}{
						vvv, utils.DefaultTimeValue,
					}
					dao.GetRow(db, &thingMap, sql, sqlParams)
					skus, _ := dao.GetSkus(db, []int{int(thingMap.ThingID)}, nil, nil, nil, nil)
					specialtyStoreSku := &SpecialtyStoreSkus{
						StoreID:    vv,
						StoreName:  storeDetail.Name,
						SkuID:      int(thingMap.ThingID),
						SkuName:    skus[0].Name,
						Price:      0,
						VendorName: model.VendorNames[v],
					}
					specialtyStoreSkus = append(specialtyStoreSkus, specialtyStoreSku)
				}
			} else {
				handler := partner.GetPurchasePlatformFromVendorID(v).(partner.ISingleStoreStoreSkuHandler)
				skuList, _ := handler.GetStoreSkusFullInfo(ctx, nil, vv, storeDetail.VendorStoreID, nil)
				for _, sku := range skuList {
					if sku.SkuList[0].IsSpecialty == model.YES {
						specialtyStoreSku := &SpecialtyStoreSkus{
							StoreID:    vv,
							StoreName:  storeDetail.Name,
							SkuID:      sku.SkuList[0].SkuID,
							SkuName:    sku.Name,
							Price:      float64(sku.SkuList[0].VendorPrice) / 100,
							VendorName: model.VendorNames[v],
						}
						specialtyStoreSkus = append(specialtyStoreSkus, specialtyStoreSku)
					}
				}
			}
		}
	}
	excelConf := &excel.Obj2ExcelSheetConfig{
		Title:       "sheet1",
		Data:        specialtyStoreSkus,
		CaptionList: excelTitle,
	}
	sheetList = append(sheetList, excelConf)
	if excelConf != nil {
		downloadURL, fileName, err = jxutils.UploadExeclAndPushMsg(sheetList, "力荐或置顶商品")
	} else {
		baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!")
	}
	if err != nil {
		baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName, err)
	} else {
		noticeMsg := fmt.Sprintf("[详情点我]%s/billshow/?normal=true&path=%s  \n", globals.BackstageHost, downloadURL)
		ddmsg.SendUserMessage(dingdingapi.MsgTyeText, ctx.GetUserID(), "异步任务完成", noticeMsg)
		baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess downloadURL: [%v]", downloadURL)
	}
	return err
}

func checkActUpdate(actID int, actMap map[int]*model.Act2) (err error) {
	if len(actMap) == 0 {
		return fmt.Errorf("活动%d不存在或已被取消", actID)
	}
	errList := errlist.New()
	for vendorID, act := range actMap {
		if vendorID == model.VendorIDEBAI && act.CreateType != model.ActCreateTypeAPI {
			errList.AddErr(fmt.Errorf("饿百平台不支持修改或取消网页活动"))
		}
	}
	return errList.GetErrListAsOne()
}

func DeleteActStoreSkuBind(ctx *jxcontext.Context, db *dao.DaoDB, actID int, actStoreSkuParam []*ActStoreSkuParam) (originSyncStatus int8, err error) {
	actMap, err := dao.GetActVendorInfo(db, actID, nil)
	if err != nil {
		return 0, err
	}
	if err = checkActUpdate(actID, actMap); err != nil {
		return 0, err
	}

	actStoreSkuMap, err := dao.GetActStoreSkuVendorInfo(db, actID, nil, nil, nil)
	if err != nil {
		return 0, err
	}
	act := actMap[partner.GetVendorIDsFromActMap(actMap)[0]]
	if act.Status != model.ActStatusCreated || time.Now().Sub(act.EndAt) > 0 {
		return 0, fmt.Errorf("当前活动状态:%s，不能进行此操作，或已过期", model.ActStatusName[act.Status])
	}

	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil || err != nil {
			dao.Rollback(db, txDB)
			if r != nil {
				panic(r)
			}
		}
	}()
	actStoreSkuParamMap := make(map[int64]*ActStoreSkuParam)
	for _, v := range actStoreSkuParam {
		actStoreSkuParamMap[jxutils.Combine2Int(v.StoreID, v.SkuID)] = v
		if _, err = dao.DeleteEntityLogically(db, &model.ActStoreSku{}, nil, ctx.GetUserName(),
			map[string]interface{}{
				model.FieldActID:   actID,
				model.FieldStoreID: v.StoreID,
				model.FieldSkuID:   v.SkuID,
			}); err != nil {
			return 0, err
		}
	}

	isNeedCancelAct := true
	for vendorID, act := range actMap {
		originSyncStatus |= act.SyncStatus
		isDeleteAll := true
		isDeleteAtLeastOne := false
		if true { //actStoreSkuParam != nil {
			actStoreSkuMap := partner.SplitActStoreSku(actStoreSkuMap[vendorID])
			for storeID := range actStoreSkuMap {
				for _, actStoreSku := range actStoreSkuMap[storeID] {
					if actStoreSkuParam == nil || actStoreSkuParamMap[jxutils.Combine2Int(actStoreSku.StoreID, actStoreSku.SkuID)] != nil {
						if act.Type == model.ActSkuFake {
							_, err = dao.DeleteEntityLogically(db, &model.ActStoreSkuMap{}, nil, ctx.GetUserName(),
								map[string]interface{}{
									model.FieldActID:   actID,
									model.FieldStoreID: actStoreSku.StoreID,
									model.FieldSkuID:   actStoreSku.SkuID,
								})
						} else {
							_, err = dao.UpdateEntityLogically(db, partner.ActStoreSku2ActStoreSkuMap(actStoreSku),
								map[string]interface{}{
									model.FieldSyncStatus: actStoreSku.SyncStatus | model.SyncFlagDeletedMask,
								}, ctx.GetUserName(), nil)
						}
						if err != nil {
							return 0, err
						}
						isDeleteAtLeastOne = true
					} else {
						isNeedCancelAct = false
						isDeleteAll = false
					}
				}
			}
		} else {
			isDeleteAll = true
			isDeleteAtLeastOne = true
		}
		if isDeleteAll || isDeleteAtLeastOne {
			syncStatus := int8(model.SyncFlagModifiedMask)
			if isDeleteAll {
				syncStatus = model.SyncFlagDeletedMask
			}
			syncStatus |= act.SyncStatus
			if act.Type != model.ActSkuFake && vendorID != model.VendorIDJX {
				if _, err = dao.UpdateEntityLogically(db, partner.Act2ActMap(act),
					map[string]interface{}{
						model.FieldSyncStatus: syncStatus,
					}, ctx.GetUserName(), nil); err != nil {
					return 0, err
				}
			}
		}
		if isDeleteAll != isNeedCancelAct {
			globals.SugarLogger.Warnf("deleteActStoreBind, actID:%d isDeleteAll:%t != isNeedCancelAct:%t", act.ID, isDeleteAll, isNeedCancelAct)
		}
	}

	if isNeedCancelAct {
		act := &model.Act{}
		act.ID = actID
		if _, err = dao.UpdateEntityLogically(db, act,
			map[string]interface{}{
				model.FieldStatus: model.ActStatusCanceled,
			}, ctx.GetUserName(), nil); err != nil {
			return 0, err
		}
	}
	dao.Commit(db, txDB)
	return originSyncStatus, err
}

func GetVendorStoreSkus(ctx *jxcontext.Context, storeID, vendorID int) (err error) {
	type SpecialtyStoreSkus struct {
		Upc     string `json:"商品upc码"`
		SkuName string `json:"商品名"`
		Unit    string `json:"单位"`
		Weight  int    `json:"重量(g)"`
		Price   int64  `json:"售价"`
	}
	var (
		db                 = dao.GetDB()
		specialtyStoreSkus []*SpecialtyStoreSkus
		excelTitle         = []string{
			"商品upc码",
			"商品名",
			"单位",
			"重量(g)",
			"售价",
		}
		sheetList             []*excel.Obj2ExcelSheetConfig
		downloadURL, fileName string
	)
	storeDetail, err := dao.GetStoreDetail(db, storeID, vendorID, "")
	if err != nil || storeDetail == nil {
		return err
	}
	if partner.IsMultiStore(vendorID) {
		// handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IPurchasePlatformStoreSkuHandler)
		// skuBareInfoList, _ := handler.GetStoreSkusBareInfo(ctx, storeDetail.VendorOrgCode, nil, storeID, storeDetail.VendorStoreID, nil)
		return fmt.Errorf("暂不支持京东！")
	} else {
		handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreStoreSkuHandler)
		skuList, _ := handler.GetStoreSkusFullInfo(ctx, nil, storeID, storeDetail.VendorStoreID, nil)
		for _, sku := range skuList {
			if sku.UPC != "" {
				skus := &SpecialtyStoreSkus{
					Upc:     sku.UPC,
					SkuName: sku.Name,
					Unit:    sku.Unit,
					Weight:  sku.SkuList[0].Weight,
					Price:   sku.SkuList[0].VendorPrice,
				}
				specialtyStoreSkus = append(specialtyStoreSkus, skus)
			}
		}
	}
	excelConf := &excel.Obj2ExcelSheetConfig{
		Title:       "sheet1",
		Data:        specialtyStoreSkus,
		CaptionList: excelTitle,
	}
	sheetList = append(sheetList, excelConf)
	if excelConf != nil {
		downloadURL, fileName, err = jxutils.UploadExeclAndPushMsg(sheetList, "平台门店标品")
	} else {
		baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess is nil!")
	}
	if err != nil {
		baseapi.SugarLogger.Errorf("WriteToExcel:upload %s , %s failed error:%v", fileName, err)
	} else {
		noticeMsg := fmt.Sprintf("[详情点我]%s/billshow/?normal=true&path=%s  \n", globals.BackstageHost, downloadURL)
		ddmsg.SendUserMessage(dingdingapi.MsgTyeText, ctx.GetUserID(), "异步任务完成", noticeMsg)
		baseapi.SugarLogger.Debug("WriteToExcel: dataSuccess downloadURL: [%v]", downloadURL)
	}
	return err
}

// CopyMtToJd 复制美团到京东
func CopyMtToJd(ctx *jxcontext.Context, mtStoreID, mtOrgCode, jdStoreID, jdOrgCode string) (err error) {
	//type funcType func(mtID, parentID string, catInfo []*mtwmapi.RetailCategoryInfo)
	var (
		//mtAccessToken = ""
		//db           = dao.GetDB()
		catMap = make(map[string]string)
		//LoopCatChild funcType
		handler, _ = partner.GetPurchasePlatformFromVendorID(model.VendorIDJD).(partner.IPurchasePlatformStoreSkuHandler)
	)

	codes, _ := dao.GetVendorOrgCode(dao.GetDB(), model.VendorIDJD, jdOrgCode, "platform")
	if len(codes) == 0 {
		return nil
	}
	code := codes[0]
	jd := jdapi.New(code.Token, code.AppKey, code.AppSecret)
	//获取token
	mtStore, err := dao.GetStoreDetailByVendorStoreID(dao.GetDB(), mtStoreID, model.VendorIDMTWM, mtOrgCode)
	if err != nil {
		return err
	}
	var mtapi *mtwmapi.API
	if mtStore.VendorOrgCode == globals.Mtwm2Code {
		mtapi = mtwmapi.New(beego.AppConfig.DefaultString("mtwmAppID2", ""), beego.AppConfig.DefaultString("mtwmSecret2", ""), beego.AppConfig.DefaultString("mtwmCallbackURL2", ""), "")
		mtapi.SetToken(mtStore.MtwmToken)
	} else {
		mtapi = partner.CurAPIManager.GetAPI(model.VendorIDMTWM, mtOrgCode).(*mtwmapi.API)
	}
	//LoopCatChild = func(mtID, parentID string, catInfo []*mtwmapi.RetailCategoryInfo) {
	//	if len(catInfo) > 0 {
	//		for j := len(catInfo) - 1; j > -1; j-- {
	//			result, _ := jd.AddShopCategory(utils.Str2Int64(parentID), catInfo[j].Name, catInfo[j].Level, catInfo[j].Sequence, "")
	//			if result != "" {
	//				catMap[mtID] = result
	//			}
	//			LoopCatChild(catInfo[j].Code, result, catInfo[j].Children)
	//		}
	//	}
	//}
	task := tasksch.NewParallelTask("美团到京东", tasksch.NewParallelConfig().SetIsContinueWhenError(true).SetParallelCount(1), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			step := batchItemList[0].(int)
			switch step {
			case 0:
				jdCatList, _ := jd.QueryCategoriesByOrgCode()
				catList, _ := mtapi.RetailCatList(mtStoreID)
				for _, jdc := range jdCatList {
					for _, mtc := range catList {
						if jdc.Name == mtc.Name {
							catMap[mtc.Name] = utils.Int64ToStr(jdc.Id)
							break
						}
						if len(mtc.Children) != 0 {
							for _, mtcl := range mtc.Children {
								if mtcl.Name == jdc.Name {
									catMap[mtcl.Name] = utils.Int64ToStr(jdc.Id)
								}
							}
						}
					}
				}

				////建分类 ,账号存在分类,不在创建分类
				////jdCatList, _ := jd.QueryCategoriesByOrgCode()
				////if len(jdCatList) == 0 {
				////catList, _ = mtapi.RetailCatList(mtStoreID)
				//
				//for i := len(catList) - 1; i > -1; i-- {
				//	catID, _ := jd.AddShopCategory(0, catList[i].Name, catList[i].Level, catList[i].Sequence, "")
				//	if catID != "" {
				//		if catList[i].Code != "" {
				//			catMap[catList[i].Code] = catID
				//		} else {
				//			catMap[catList[i].Name] = catID
				//		}
				//	}
				//	//LoopCatChild(catList[i].Code, catID, catList[i].Children)
				//	//美团好像只能建两级分类？
				//	for j := len(catList[i].Children) - 1; j > -1; j-- {
				//		catID2, _ := jd.AddShopCategory(utils.Str2Int64(catID), catList[i].Children[j].Name, catList[i].Children[j].Level, catList[i].Children[j].Sequence, "")
				//		if catID2 != "" {
				//			if catList[i].Children[j].Code != "" {
				//				catMap[catList[i].Children[j].Code] = catID2
				//			} else {
				//				catMap[catList[i].Children[j].Name] = catID2
				//			}
				//		}
				//	}
				//}

				globals.SugarLogger.Debugf("--------catMapcatMap := %s", utils.Format4Output(catMap, false))
				//}
			case 1:
				//建商品
				var i = 0
				skuList := make([]*mtwmapi.AppFood, 0, 0)
				for {
					foodList, err := mtapi.RetailListAll(mtStoreID, i)
					if err != nil {
						globals.SugarLogger.Debugf("美团商品价格和上下架状态获取平台商品异常 :%v", err)
						break
					}
					skuList = append(skuList, foodList...)
					if len(foodList) < 100 {
						break
					}
					i++
				}

				var searchAfterSkuId string = ""
				var jdSkus = make(map[string]int, 0)
				for i2 := 0; i2 < 85; i2++ {
					idList, afterSkuId, err := jd.AuerySkuInfoList(int64(i), 20, searchAfterSkuId)
					if err != nil {
						globals.SugarLogger.Debugf("--------err:%v", err)
						break
					}
					time.Sleep(time.Second * 1)
					searchAfterSkuId = utils.Int64ToStr(afterSkuId)
					for _, ifd := range idList {
						jdSkus[ifd] = 1
					}
					if len(idList) < 20 {
						break
					}
				}
				globals.SugarLogger.Debugf("--------jdSkus := %d", len(jdSkus))

				task2 := tasksch.NewParallelTask("美团到京东, 建商品", tasksch.NewParallelConfig().SetIsContinueWhenError(true).SetParallelCount(1), ctx,
					func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
						time.Sleep(1 * time.Second)
						mtSku := batchItemList[0].(*mtwmapi.AppFood)
						if _, ok := jdSkus[mtSku.AppFoodCode]; !ok {
							//catMaps, _ := dao.GetMtJdCategoryMap(db, utils.Int2Str(mtSku.TagID), "")
							//if len(catMaps) == 0 {
							//results, _ := dao.GetVendorCategoriesWithMap(db, model.VendorIDMTWM, mtSku.TagID)
							//	results2, _ := dao.GetVendorCategoriesWithMap(db, model.VendorIDMTWM, utils.Str2Int(results[0].ParentID))
							//	results3, _ := dao.GetVendorCategoriesWithMap(db, model.VendorIDMTWM, utils.Str2Int(results2[0].ParentID))
							//	//return retVal, fmt.Errorf("该商品美团分类还未映射到京东，商品名：[%v]，美团分类：[%v] ->[%v] ->[%v]。", mtSku.Name, results3[0].Name, results2[0].Name, results[0].Name)
							//}
							param := &jdapi.NewAddSkuParam{
								TraceId:        ctx.GetTrackInfo(),
								OutSkuId:       mtSku.AppFoodCode,
								ShopCategories: []int64{}, // 商家分类
								CategoryId:     0,         // 到家分类
								BrandId:        0,
								SkuName:        mtSku.Name,
								SkuPrice:       int64(jxutils.StandardPrice2Int(mtSku.Price)),
								Weight:         math.Round(float64(jxutils.IntWeight2Float(utils.Str2Int(mtSku.SkuList[0].Weight)))*1000) / 1000,
								FixedStatus:    1,
								IsSale:         true,
								Upc:            mtSku.SkuList[0].Upc,
								Images:         mtSku.PictureList,
							}
							brandList, err := jd.GetSkuCategoryBySkuName(mtSku.Name)
							if brandList == nil || err != nil {
								param.BrandId = 35247
							} else {
								param.BrandId = int64(brandList.BrandId)
								param.CategoryId = int64(brandList.CategoryId)
							}

							if param.OutSkuId == "" {
								param.OutSkuId = fmt.Sprintf("%d_%d%d", time.Now().Unix(), param.SkuPrice, len(mtSku.Name))
							}

							if param.CategoryId == 0 {
								vendorCategoryId, _ := handler.GetSkuCategoryIdByName(jdOrgCode, strings.Join(mtSku.PictureList, "|"))
								param.CategoryId = utils.Str2Int64(vendorCategoryId)
								//if vendorCategoryId != "" && utils.Str2Int64(vendorCategoryId) <= 25048 {
								//	param.CategoryID = utils.Str2Int64(vendorCategoryId) // 其他水果
								//} else if utils.Str2Int64(vendorCategoryId) > 25048 { // 只有水果,後面刪除
								//	param.CategoryID = 22410 // 其他水果
								//}
							}

							//if mtSku.SecondaryCategoryCode != "" {
							//	param.ShopCategories = append(param.ShopCategories, utils.Str2Int64WithDefault(catMap[mtSku.SecondaryCategoryCode], 33313305))
							//} else if mtSku.SecondaryCategoryName != "" {
							//	param.ShopCategories = append(param.ShopCategories, utils.Str2Int64(catMap[mtSku.SecondaryCategoryName]))
							//} else if mtSku.CategoryCode != "" {
							//	param.ShopCategories = append(param.ShopCategories, utils.Str2Int64(catMap[mtSku.CategoryCode]))
							//} else if mtSku.CategoryName != "" {
							//	param.ShopCategories = append(param.ShopCategories, utils.Str2Int64(catMap[mtSku.CategoryName]))
							//} else {
							//	param.ShopCategories = append(param.ShopCategories, 33313305) // 暂无分类
							//}

							if mtSku.SecondaryCategoryName != "" {
								param.ShopCategories = append(param.ShopCategories, utils.Str2Int64WithDefault(catMap[mtSku.SecondaryCategoryName], 33313305))
							} else if mtSku.CategoryName != "" {
								param.ShopCategories = append(param.ShopCategories, utils.Str2Int64WithDefault(catMap[mtSku.CategoryName], 33313305))
							} else {
								param.ShopCategories = append(param.ShopCategories, 33313305) // 暂无分类
							}

							if mtSku.PictureContents != "" {
								param.ProductDesc = fmt.Sprintf(`<img src="%s"  alt="一张图片" />`, mtSku.PictureContents)
							}
							if param.Upc == "" {
								productInfo, _ := jd.GetJdUpcCodeByName(param.SkuName, "", 1, 10)
								if len(productInfo) != 0 {
									param.Upc = productInfo[0].UpcCode
								}
							}

							skuID, err := jd.NewAddSku(param)
							if skuID == "0" || err != nil {
								globals.SugarLogger.Debugf("Add sku : %s", utils.Format4Output(param, false))
							}
							time.Sleep(1 * time.Second)
							if skuID != "" {
								err1 := jd.UpdateCurrentQty(ctx.GetTrackInfo(), jdStoreID, utils.Str2Int64(skuID), utils.Str2Int(mtSku.SkuList[0].Stock))
								if err1 != nil {
									globals.SugarLogger.Debugf("--------err r1 := %v", err1)
								}
								_, err2 := jd.UpdateVendibility(ctx.GetTrackInfo(), []*jdapi.QueryStockRequest{
									&jdapi.QueryStockRequest{
										StationNo: jdStoreID,
										SkuId:     utils.Str2Int64(skuID),
										DoSale:    mtSku.IsSoldOut,
									},
								})
								if err2 != nil {
									globals.SugarLogger.Debugf("--------err r1 := %v", err2)
								}
							}
						}
						return retVal, err
					}, skuList)
				tasksch.HandleTask(task2, task, true).Run()
				_, err = task2.GetResult(0)
				if err != nil {
					task.SetNoticeMsg(err.Error())
				}
			}
			return retVal, err
		}, []int{0, 1})
	tasksch.HandleTask(task, nil, true).Run()
	task.GetID()
	return err
}

// UpdateMtCatToJd 修改美团分类到京东
func UpdateMtCatToJd(ctx *jxcontext.Context, mtCatID, jdCatID string) (err error) {
	var (
		db = dao.GetDB()
	)
	list, _ := dao.GetMtJdCategoryMap(db, mtCatID, jdCatID)
	if len(list) > 0 {
		return
	}
	list2, _ := dao.GetMtJdCategoryMap(db, mtCatID, "")
	if len(list2) > 0 {
		if list2[0].JdID == jdCatID {
			return
		} else {
			list2[0].JdID = jdCatID
			dao.UpdateEntity(db, list2[0], "JdID")
		}
	} else {
		catMap := &model.MtJdCategoryMap{
			MtID: mtCatID,
			JdID: jdCatID,
		}
		dao.CreateEntity(db, catMap)
	}
	return err
}

func SyncTiktokMainIdToLocal(ctx *jxcontext.Context, param *tiktok_store.MainSku) error {
	//codes, err := dao.GetVendorOrgCode(dao.GetDB(), model.VendorIDDD, "", "platform")
	//if err != nil {
	//	return err
	//}
	//db := dao.GetDB()
	//for _, code := range codes {
	//	result, err := tiktok_store.QueryAllMainSkuList(code.VendorOrgCode, param)
	//	if err != nil {
	//		continue
	//	}
	//
	//	// 删除当前时间段数据
	//	jxutils.CallMsgHandlerAsync(func() {
	//		if err := DeleteThingByData(ctx, db, code.VendorOrgCode, utils.Timestamp2Time(param.StartTime), utils.Timestamp2Time(param.EndTime)); err != nil {
	//			globals.SugarLogger.Error("删除需要同步的数据 := %s", err.Error())
	//		}
	//		if err := OnCreateThing2Tiktok(ctx, db, code.VendorOrgCode, result, model.ThingTypeSku, model.SyncFlagNewMask, false); err != nil {
	//			globals.SugarLogger.Error("初始他抖音平台主订单已经子订单id信息错误:= %s", err.Error())
	//		}
	//	}, "")
	//}
	return nil
}

//批量更新美团商品进货价
func BatchSetRestockingPrice(ctx *jxcontext.Context, preData map[string][]mtwmapi.SpuData) error {
	var params []*mtwmapi.SpuData
	for k, v := range preData {
		if storeID, err := dao.GetCodeAndIDByMeiTuan(k, model.VendorIDMTWM); err != nil {
			return errors.New(fmt.Sprintf("获取京西本地门店ID失败:%v 请重试", err))
		} else {
			for _, i := range v {
				param := &mtwmapi.SpuData{
					AppSpuCode:    i.AppSpuCode,
					SkuID:         i.SkuID,
					PurchasePrice: i.PurchasePrice,
				}
				params = append(params, param)
			}
			if err := mtwm.BatchSetRestockingPrice(ctx, storeID, k, params); err != nil {
				return err
			}
		}
	}
	return nil
}

// BatchSetMTBoxPrice 批量修改美团包装费为0
func BatchSetMTBoxPrice(ctx *jxcontext.Context, storeList []int) error {
	return mtwm.UpdateBoxPrice(ctx, dao.GetDB(), storeList)
}
