package cms

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"git.rosy.net.cn/baseapi/platformapi/tencent_map"
	"io"
	"math"
	"mime/multipart"
	"sort"
	"strconv"
	"strings"
	"time"

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

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

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

	warehouse_getFences_response "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/api/warehouse_getFences/response"
	doudian_sdk "git.rosy.net.cn/baseapi/platformapi/tiktok_shop/sdk-golang/core"
	"git.rosy.net.cn/baseapi/platformapi/tiktok_shop/tiktok_api"
	"git.rosy.net.cn/jx-callback/business/jxstore/common"
	"git.rosy.net.cn/jx-callback/business/jxstore/event"
	"git.rosy.net.cn/jx-callback/business/partner/purchase/tiktok_store"

	"git.rosy.net.cn/jx-callback/business/auth2"
	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
	"git.rosy.net.cn/jx-callback/business/jxstore/permission"

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

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

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

	"github.com/360EntSecGroup-Skylar/excelize"

	"git.rosy.net.cn/baseapi"
	"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/authz/autils"
	"git.rosy.net.cn/jx-callback/business/jxutils"
	"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/jxcontext"
	"git.rosy.net.cn/jx-callback/business/jxutils/msg"
	"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
	"git.rosy.net.cn/jx-callback/business/model/legacymodel"
	"git.rosy.net.cn/jx-callback/business/partner"
	"git.rosy.net.cn/jx-callback/business/partner/purchase/ebai"
	"git.rosy.net.cn/jx-callback/business/partner/purchase/jd"
	"git.rosy.net.cn/jx-callback/globals"
	"git.rosy.net.cn/jx-callback/globals/api"
)

const (
	GET_BAD_COMMENTS_TYPE   = 0 //获取差评的标志
	GET_ALL_COMMENTS_TYPE   = 1 //获取所有评论的标志
	GET_FIXED_COMMENTS_TYPE = 2 //获取已解决评论的标志
)

const (
	COMMENT_NOT_RESOLVED = 0 //未解决的差评状态
	COMMENT_RESOLVED     = 1 //已解决的差评状态

	JX_BAD_COMMENTS_MAX_LEVEL    = 2 // 差评最大分
	JX_MIDDLE_COMMENTS_MAX_LEVEL = 4 // 中评最大分

	COMMENTS_SCORE_ONE_ORTWO_BEGIN_DELAY_TIME   = 1 * 60 //评论回复一星或二星回复延迟开始时间区间
	COMMENTS_SCORE_ONE_ORTWO_END_DELAY_TIME     = 3 * 60 //评论回复一星或二星回复延迟结束时间区间
	COMMENTS_SCORE_THREE_BEGIN_DELAY_TIME       = 2 * 60 //评论回复三星回复延迟开始时间区间
	COMMENTS_SCORE_THREE_END_DELAY_TIME         = 4 * 60 //评论回复三星回复延迟结束时间区间
	COMMENTS_SCORE_FOUR_ORFIVE_BEGIN_DELAY_TIME = 2 * 60 //评论回复四星或五星回复延迟开始时间区间
	COMMENTS_SCORE_FOUR_ORFIVE_END_DELAY_TIME   = 5 * 60 //评论回复四星或五星回复延迟结束时间区间

	MAX_REAPLY_TIME = 18 * time.Hour
)

type StoreExt struct {
	model.Store

	MarketManName string `orm:"size(8)" json:"marketManName"` // 市场负责人姓名
	OperatorName  string `orm:"size(8)" json:"operatorName"`  // 京东运营人姓名
	OperatorName2 string `orm:"size(8)" json:"operatorName2"` // 美团运营人姓名
	OperatorName3 string `orm:"size(8)" json:"operatorName3"` // 饿百运营人姓名

	FloatLng float64 `json:"lng"`
	FloatLat float64 `json:"lat"`

	ProvinceCode   int    `json:"provinceCode"`
	ProvinceName   string `json:"provinceName"`
	CityName       string `json:"cityName"`
	DistrictName   string `json:"districtName"`
	StoreMapStr    string `json:"-"`
	CourierMapStr  string `json:"-"`
	PayeeBankName  string `json:"payeeBankName"` // 开户行名称
	LinkStoreCount int    `json:"linkStoreCount"`
	BrandName      string `json:"brandName"`
	BrandLogo      string `json:"brandLogo"`
	// StoreMaps     []map[string]interface{} `orm:"-"`
	// CourierMaps   []map[string]interface{} `orm:"-"`
	StoreMaps       []*model.StoreMap        `json:"StoreMaps"`
	CourierMaps     []*model.StoreCourierMap `json:"CourierMaps"`
	BussinessStatus int                      `json:"bussinessStatus"` //上下线状态，-1下线，1上线

	OrderCount int `json:"orderCount"`
}

type StoresInfo struct {
	TotalCount   int         `json:"totalCount"`
	MapCenterLng float64     `json:"mapCenterLng"`
	MapCenterLat float64     `json:"mapCenterLat"`
	Stores       []*StoreExt `json:"stores"`
}

type JxBadCommentsExt struct {
	legacymodel.JxBadComments
	StoreName     string `json:"storeName"`
	CityName      string `json:"cityName"`
	OperatorPhone string `json:"operatorPhone"`
	UserName      string `json:"userName"`

	VendorOrderID2 string `orm:"column(vendor_order_id2);size(48);index" json:"vendorOrderID2"`
}

type VendorStoreExcel struct {
	StoreID         int    `json:"门店ID"`
	VendorStoreName string `json:"门店名"`
	Status          string `json:"营业状态"`
	Tel1            string `json:"电话1"`
	Tel2            string `json:"电话2"`
	CityName        string `json:"城市名"`
	Address         string `json:"地址"`
	MarketManName   string `json:"市场负责人"`
	OperatorName    string `json:"运营负责人"`
	OperatorName2   string `json:"运营负责人2"`
	OperatorName3   string `json:"运营负责人3"`
}

//门店信息
type StoreInfo struct {
	StoreID          int    `json:"storeID"`    //门店ID
	StoreName        string `json:"storeName"`  //门店名字
	OpenTime1        int16  `json:"openTime1"`  // 930就表示9点半，用两个的原因是为了支持中午休息，1与2的时间段不能交叉，为0表示没有
	CloseTime1       int16  `json:"closeTime1"` // 营业时间 1
	OpenTime2        int16  `json:"openTime2"`  // 营业时间2
	CloseTime2       int16  `json:"closeTime2"`
	Status           int    `json:"status"`                         //门店是否营业
	Distance         int    `json:"distance"`                       //店铺与定位 直线距离
	Address          string `json:"address"`                        //门店地址
	BrandID          int    `orm:"column(brand_id)" json:"brandID"` //品牌ID
	BrandName        string `json:"brandName"`                      //品牌名字
	BrandLogo        string `json:"brandLogo"`                      //品牌logo
	StoreWeeklyScore int    `json:"storeWeeklyScore"`               //店铺每周评分
	DeliverySelf     int    `json:"deliverySelf"`                   //自提
}

//门店减免策略
type StoreDeductionInfo struct {
	StoreID                  int `json:"storeID"`                  //门店ID
	DeliveryFeeDeductionSill int `json:"deliveryFeeDeductionSill"` //订单满减金额
	DeliveryFeeDeductionFee  int `json:"deliveryFeeDeductionFee"`  //订单减免金额
}

//门店热销商品
type StoreSkuInfo struct {
	StoreID    int    `json:"storeID"`    //门店ID
	SkuID      int    `json:"skuID"`      //商品ID
	SkuName    string `json:"skuName"`    //商品名字
	SkuStatus  int    `json:"skuStatus"`  //商品是否可售状态 1可售 0不可售
	BestSeller int    `json:"bestSeller"` //畅销品 0不是 1是
	Img        string `json:"img"`        //商品第一张图片
	Price      int    `json:"price"`      // 单位为分，标准价，不为份的就为实际标准价，为份的为每市斤价，实际还要乘质量
	Unit       string `json:"unit"`       //商品售卖单位 份/kg等
}

//首页展示信息
type HomePageInfos struct {
	StoreInfo          StoreInfo            `json:"storeInfo"`
	StoreDeductionInfo []StoreDeductionInfo `json:"StoreDeductionInfo"`
	StoreSkuInfo       []StoreSkuInfo       `json:"StoreSkuInfo"`
}

type JdStoreLevelExt struct {
	Level  string `json:"level`
	PageNo int    `json:"pageNo"`
}

var (
	ErrMissingInput     = errors.New("没有有效的输入参数")
	ErrCanNotFindVendor = errors.New("vendorID参数不合法")
)

var (
	storeKeyPropertyMap = map[string]int{
		"name":              1,
		"cityCode":          1,
		"districtCode":      1,
		"address":           1,
		"tel1":              1,
		"tel2":              1,
		"openTime1":         1,
		"closeTime1":        1,
		"openTime2":         1,
		"closeTime2":        1,
		"lng":               1,
		"lat":               1,
		"deliveryRangeType": 1,
		"deliveryRange":     1,
		"status":            1,
		"promoteInfo":       1,
		"vendorStoreName":   1,
	}

	storeMapKeyPropertyMap = map[string]int{
		"status":               1,
		"freightDeductionPack": 1,
		"vendorStoreName":      1,
		"boxFee":               1,
	}

	WatchVendorStoreTimeList = []string{
		"9:00:00",
		"10:00:00",
		"11:00:00",
		"15:00:00",
		"20:00:00",
	}
	titleListStore = []string{
		"门店ID",
		"门店名",
		"营业状态",
		"电话1",
		"电话2",
		"城市名",
		"地址",
		"市场负责人",
		"运营负责人",
		"运营负责人2",
	}

	earningInfoMap = map[string]string{
		"payeeName":           "收款人姓名",
		"payeeAccountNo":      "收款账号",
		"payeeBankBranchName": "开户支行",
		"payeeBankCode":       "开户行代码",
	}

	marketInfoMap = map[string]string{
		"marketManPhone":     "市场负责人电话",
		"marketManRole":      "市场负责人组（角色，单人）",
		"jxBrandFeeFactor":   "京西品牌费因子",
		"marketAddFeeFactor": "市场附加费因子",
		"payPercentage":      "结算比例",
	}
)

func getStoresSql(ctx *jxcontext.Context, keyword string, params map[string]interface{}, orderTimeFrom, orderTimeTo time.Time) (sql string, sqlParams []interface{}, sqlFrom string, sqlFromParams []interface{}, err error) {
	sqlFrom = `
	FROM store t1
	LEFT JOIN new_config bank ON bank.deleted_at = ? AND bank.type = ? AND bank.key = t1.payee_bank_code
	LEFT JOIN place city ON t1.city_code = city.code AND city.level = 2
	LEFT JOIN place province ON province.code = city.parent_code AND province.level = 1
	LEFT JOIN place district ON t1.district_code = district.code AND district.level = 3
	/*
	LEFT JOIN store_map m1 ON t1.id = m1.store_id AND m1.deleted_at = ?
	LEFT JOIN store_courier_map m2 ON t1.id = m2.store_id AND m2.deleted_at = ?
	*/
	LEFT JOIN user mm ON mm.mobile <> '' AND mm.mobile = t1.market_man_phone AND mm.deleted_at = ?
	LEFT JOIN user om ON om.mobile <> '' AND om.mobile = t1.operator_phone AND om.deleted_at = ?
	LEFT JOIN user om2 ON om2.mobile <> '' AND om2.mobile = t1.operator_phone2 AND om2.deleted_at = ?
	LEFT JOIN user om3 ON om3.mobile <> '' AND om3.mobile = t1.operator_phone3 AND om3.deleted_at = ?
	LEFT JOIN brand br ON br.id = t1.brand_id
	`
	sqlFromParams = []interface{}{
		utils.DefaultTimeValue,
		model.ConfigTypeBank,
		// utils.DefaultTimeValue,
		// utils.DefaultTimeValue,
		utils.DefaultTimeValue,
		utils.DefaultTimeValue,
		utils.DefaultTimeValue,
		utils.DefaultTimeValue,
	}
	sqlWhere := `
	WHERE t1.deleted_at = ?
	`
	sqlWhereParams := []interface{}{
		utils.DefaultTimeValue,
	}
	tableAliasMap := make(map[string]*struct{})
	for mapCondKey, tableName := range map[string]string{
		"vendorStoreCond":  "store_map",
		"courierStoreCond": "store_courier_map",
	} {
		if mapCond := strings.ToUpper(utils.Interface2String(params[mapCondKey])); mapCond == "AND" || mapCond == "OR" {
			mapCondsStr := utils.Interface2String(params[mapCondKey+"s"])
			if mapCondsStr != "" {
				var vendorStoreConds map[string]interface{}
				if err = utils.UnmarshalUseNumber([]byte(mapCondsStr), &vendorStoreConds); err != nil {
					return "", nil, "", nil, err
				}
				sqlVendorStoreCond := ""
				for vendor, cond2 := range vendorStoreConds {
					var cond string
					if condStr, ok := cond2.(string); !ok {
						cond = utils.Int64ToStr(utils.ForceInterface2Int64(cond2))
					} else {
						cond = condStr
					}

					tableAlias := tableName + vendor
					if cond != "0" && cond != "" {
						if sqlVendorStoreCond == "" {
							if mapCond == "AND" {
								sqlVendorStoreCond += " AND ( 1 = 1"
							} else {
								sqlVendorStoreCond += " AND ( 1 = 0"
							}
						}
						sqlFrom += "\nLEFT JOIN " + tableName + " " + tableAlias + " ON " + tableAlias + ".vendor_id = ? AND " +
							tableAlias + ".store_id = t1.id AND " + tableAlias + ".deleted_at = ?"
						//  AND "
						//  +tableAlias + ".is_sync <> 0 "
						sqlFromParams = append(sqlFromParams, vendor, utils.DefaultTimeValue)
						if cond == "1" {
							sqlVendorStoreCond += " " + mapCond + " " + tableAlias + ".id IS NOT NULL"
						} else if cond == "-1" {
							sqlVendorStoreCond += " " + mapCond + " " + tableAlias + ".id IS NULL"
						} else {
							sqlFrom += " AND " + tableAlias + ".vendor_org_code = ?"
							sqlFromParams = append(sqlFromParams, cond)
							sqlVendorStoreCond += " " + mapCond + " " + tableAlias + ".id IS NOT NULL"
						}
						tableAliasMap[tableAlias] = &struct{}{}
					}
				}
				if sqlVendorStoreCond != "" {
					sqlWhere += sqlVendorStoreCond + ")"
				}
			}
		}
	}
	if keyword != "" {
		keywordLike := "%" + keyword + "%"
		sqlWhere += ` AND (t1.name LIKE ? OR t1.tel1 LIKE ? OR t1.tel2 LIKE ? OR t1.operator_phone LIKE ? OR t1.operator_phone2 LIKE ? OR t1.operator_phone3 LIKE ? OR t1.market_man_phone LIKE ?
			OR t1.last_operator LIKE ? OR city.name LIKE ? OR t1.address LIKE ? OR t1.printer_sn LIKE ? OR t1.licence_code LIKE ? OR t1.id_code LIKE ? OR t1.comment LIKE ?`
		sqlWhereParams = append(sqlWhereParams, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike,
			keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike)

		if len(tableAliasMap) > 0 {
			for k, _ := range tableAliasMap {
				sqlWhere += ` OR ` + k + ".vendor_store_name LIKE ?"
				sqlWhereParams = append(sqlWhereParams, keywordLike)
			}
		}

		if keywordInt64, err2 := strconv.ParseInt(keyword, 10, 64); err2 == nil {
			if true { // jxutils.IsLegalMobileNumber(keywordInt64) {
				sqlWhere += ` OR (
					SELECT COUNT(*)
					FROM casbin_rule t11
					JOIN user t12 ON t12.user_id = t11.v0 AND t12.mobile LIKE ?
					WHERE t11.v1 <> '' AND (t11.v1 = CONCAT(?, t1.id) OR t11.v1 = CONCAT(?, t1.market_man_role) OR t11.v1 = CONCAT(?, t1.operator_role) OR t11.v1 = CONCAT(?, t1.operator_role2) OR t11.v1 = CONCAT(?, t1.operator_role3))
				) > 0`
				prefix := autils.NewRole("", 0).GetFullName()
				sqlWhereParams = append(sqlWhereParams, keyword+"%", autils.NewStoreBossRole(-1).GetFullName(), prefix, prefix, prefix, prefix) // 必须要前缀，不然不能用过些会很慢
			}
			sqlWhere += " OR t1.id = ? OR t1.city_code = ? OR t1.district_code = ? OR t1.link_store_id = ?"
			sqlWhereParams = append(sqlWhereParams, keywordInt64, keywordInt64, keywordInt64, keywordInt64)
			if jxutils.GuessVendorIDFromVendorStoreID(keywordInt64) != model.VendorIDUnknown {
				sqlWhere += `
				OR (SELECT COUNT(*) FROM store_map tsm WHERE t1.id = tsm.store_id AND tsm.deleted_at = ? AND tsm.vendor_store_id = ?) > 0
				OR (SELECT COUNT(*) FROM store_courier_map tsm WHERE t1.id = tsm.store_id AND tsm.deleted_at = ? AND tsm.vendor_store_id = ?) > 0
				`
				sqlWhereParams = append(sqlWhereParams, utils.DefaultTimeValue, keyword, utils.DefaultTimeValue, keyword)
			}
		}
		sqlWhere += ")"
	}

	if params["storeID"] != nil || params["storeIDs"] != nil {
		var storeIDs []int
		if params["storeIDs"] != nil {
			if err = jxutils.Strings2Objs(utils.Interface2String(params["storeIDs"]), &storeIDs); err != nil {
				return "", nil, "", nil, err
			}
		}
		if params["storeID"] != nil {
			storeIDs = append(storeIDs, params["storeID"].(int))
		}
		if len(storeIDs) > 0 {
			sqlWhere += " AND t1.id IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
			sqlWhereParams = append(sqlWhereParams, storeIDs)
		}
	}
	if params["startStoreID"] != nil {
		sqlWhere += " AND t1.id >= ?"
		sqlWhereParams = append(sqlWhereParams, params["startStoreID"])
	}
	if params["brandID"] != nil {
		if utils.MustInterface2Int64(params["brandID"]) != 0 {
			sqlWhere += " AND t1.brand_id = ?"
			sqlWhereParams = append(sqlWhereParams, params["brandID"])
		}
	}
	if params["name"] != nil {
		sqlWhere += " AND t1.name LIKE ?"
		sqlWhereParams = append(sqlWhereParams, "%"+params["name"].(string)+"%")
	}
	if params["placeID"] != nil {
		level := 2
		if params["placeLevel"] != nil {
			level = params["placeLevel"].(int)
		}
		if level == 2 {
			sqlWhere += " AND t1.city_code = ?"
		} else {
			sqlWhere += " AND t1.district_code = ?"
		}
		sqlWhereParams = append(sqlWhereParams, params["placeID"].(int))
	}
	if params["address"] != nil {
		sqlWhere += " AND t1.address LIKE ?"
		sqlWhereParams = append(sqlWhereParams, "%"+params["address"].(string)+"%")
	}
	if params["marketManPhone"] != nil {
		sqlWhere += " AND t1.market_man_phone = ?"
		sqlWhereParams = append(sqlWhereParams, params["marketManPhone"].(string))
	}
	if params["jdPhone"] != nil {
		jdPhone := params["jdPhone"].(string)
		sqlWhere += " AND t1.operator_phone = ?"
		if jdPhone == "0" {
			jdPhone = ""
		}
		sqlWhereParams = append(sqlWhereParams, jdPhone)
	}
	if params["mtPhone"] != nil {
		mtPhone := params["mtPhone"].(string)
		if mtPhone == "0" {
			mtPhone = ""
		}
		sqlWhere += " AND t1.operator_phone2 = ?"
		sqlWhereParams = append(sqlWhereParams, mtPhone)
	}
	if params["ebaiPhone"] != nil {
		ebaiPhone := params["ebaiPhone"].(string)
		if ebaiPhone == "0" {
			ebaiPhone = ""
		}
		sqlWhere += " AND t1.operator_phone3 = ?"
		sqlWhereParams = append(sqlWhereParams, ebaiPhone)
	}
	if params["tel"] != nil {
		sqlWhere += " AND (t1.tel1 LIKE ? OR t1.tel2 LIKE ?)"
		sqlWhereParams = append(sqlWhereParams, "%"+params["tel"].(string)+"%")
		sqlWhereParams = append(sqlWhereParams, "%"+params["tel"].(string)+"%")
	}
	if params["earningType"] != nil {
		if params["earningType"].(int) != 0 {
			if params["earningType"].(int) == 1 {
				sqlWhere += " AND t1.pay_percentage >= 50"
			} else {
				sqlWhere += " AND t1.pay_percentage < 50"
			}
		}
	}

	if params["statuss"] != nil {
		var statuss []int
		if err = utils.UnmarshalUseNumber([]byte(params["statuss"].(string)), &statuss); err != nil {
			return "", nil, "", nil, err
		}
		if len(statuss) > 0 {
			sqlWhere += " AND t1.status IN (" + dao.GenQuestionMarks(len(statuss)) + ")"
			sqlWhereParams = append(sqlWhereParams, statuss)
		}
	}
	if params["marketManPhones"] != nil {
		var phones []string
		if err = utils.UnmarshalUseNumber([]byte(params["marketManPhones"].(string)), &phones); err != nil {
			return "", nil, "", nil, err
		}
		if len(phones) > 0 {
			sqlWhere += " AND t1.market_man_phone IN (" + dao.GenQuestionMarks(len(phones)) + ")"
			sqlWhereParams = append(sqlWhereParams, phones)
		}
	}
	if params["storeLevels"] != nil {
		var storeLevels []string
		if err = jxutils.Strings2Objs(utils.Interface2String(params["storeLevels"]), &storeLevels); err != nil {
			return "", nil, "", nil, err
		}
		if len(storeLevels) > 0 {
			sqlWhere += " AND t1.store_level IN (" + dao.GenQuestionMarks(len(storeLevels)) + ")"
			sqlWhereParams = append(sqlWhereParams, storeLevels)
		}
	}

	if params["mapLongitude"] != nil && params["mapLatitude"] != nil && params["mapRadius"] != nil {
		mapLongitude := utils.Str2Float64(params["mapLongitude"].(string))
		mapLatitude := utils.Str2Float64(params["mapLatitude"].(string))
		mapRadius := params["mapRadius"].(int)
		sqlWhere += " AND getDistance(?, ?, CAST(t1.lng AS DECIMAL(15,6))/1000000, CAST(t1.lat AS DECIMAL(15,6))/1000000) * 1000 <= ?"
		sqlWhereParams = append(sqlWhereParams, mapLongitude, mapLatitude, mapRadius)
	}

	sql = sqlFrom + sqlWhere
	sqlParams = append(sqlParams, sqlFromParams...)
	sqlParams = append(sqlParams, sqlWhereParams...)
	return sql, sqlParams, sqlFrom, sqlFromParams, nil
}

func setStoreMapInfo(ctx *jxcontext.Context, db *dao.DaoDB, storesInfo *StoresInfo, storeIDs []int, briefLevel int, isBussinessStatus interface{}) (err error) {
	storeMapList, err := dao.GetStoresMapList(db, nil, storeIDs, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	if err != nil {
		return err
	}
	storeCourierList, err := dao.GetStoreCourierList(db, storeIDs, nil, model.StoreStatusAll, model.StoreAuditStatusAll)
	if err != nil {
		return err
	}
	//isStoreVendorStatus := false
	//if isBussinessStatus != nil {
	//	if isBussinessStatus.(bool) {
	//		isStoreVendorStatus = true
	//	}
	//}
	storeMapMap := dao.StoreMapList2Map(storeMapList)
	storeCourierMap := dao.StoreCourierList2Map(storeCourierList)

	for _, v := range storesInfo.Stores {
		if briefLevel > 0 {
			v.DeliveryRange = ""
			v.IDCardFront = ""
			v.IDCardBack = ""
			v.IDCardHand = ""
			v.IDCardHandBack = ""
			v.Licence = ""
			v.Licence2Image = ""
		}
		for _, v2 := range storeMapMap[v.ID] {
			v.StoreMaps = append(v.StoreMaps, v2)
		}
		for _, v2 := range storeCourierMap[v.ID] {
			v.CourierMaps = append(v.CourierMaps, v2)
		}
	}
	//task := tasksch.NewParallelTask("上下线状态", tasksch.NewParallelConfig().SetParallelCount(4), ctx,
	//	func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
	//		store := batchItemList[0].(*StoreExt)
	//		for _, v2 := range store.StoreMaps {
	//			if isStoreVendorStatus {
	//				if handler := CurVendorSync.GetStoreHandler(v2.VendorID); handler != nil {
	//					if store, err := handler.ReadStore(ctx, v2.VendorOrgCode, v2.VendorStoreID); err == nil && store != nil {
	//						if store.Status != model.StoreStatusDisabled {
	//							v2.BussinessStatus = 1
	//						} else {
	//							v2.BussinessStatus = -1
	//						}
	//					}
	//				}
	//			}
	//		}
	//		return retVal, err
	//	}, storesInfo.Stores)
	//task.Run()
	//task.GetResult(0)
	return nil
}

// todo 门店绑定信息可以考虑以数组形式返回，而不是现在这样
func GetStores(ctx *jxcontext.Context, keyword string, params map[string]interface{}, offset, pageSize int, orderTimeFrom, orderTimeTo time.Time, orderCountFrom, orderCountTo int) (retVal *StoresInfo, err error) {
	briefLevel := int(utils.ForceInterface2Int64(params["briefLevel"]))
	//权限
	if permission.IsRoled(ctx) {
		if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
			var storeIDs2 []int
			if params["storeIDs"] != nil {
				var storeIDs []int
				if err = utils.UnmarshalUseNumber([]byte(params["storeIDs"].(string)), &storeIDs); err == nil {
					for _, v := range storeIDs {
						if storeIDsMap[v] != 0 {
							storeIDs2 = append(storeIDs2, v)
						}
					}
				}
				if len(storeIDs2) == 0 {
					storeIDs2 = append(storeIDs2, -1)
				}
			} else {
				if params["storeID"] != nil && len(storeIDsMap) > 0 {
					if storeIDsMap[int(utils.Interface2Int64WithDefault(params["storeID"], 0))] == 0 && int(utils.Interface2Int64WithDefault(params["storeID"], 0)) != 666666 {
						params["storeID"] = nil
					}
				} else if len(storeIDsMap) > 0 {
					for k, _ := range storeIDsMap {
						storeIDs2 = append(storeIDs2, k)
					}
					roleInfo, _ := GetUserRoleList(ctx, ctx.GetUserID())
					for _, v := range roleInfo {
						storeIDs2 = append(storeIDs2, v.StoreID)
					}
				} else {
					//storeIDs2 = append(storeIDs2, -1)l
					// 商家小程序用户切换门店,如果角色storeIDsMap没有数据则查询传入数据
					storeIDs2 = append(storeIDs2, int(utils.Interface2Int64WithDefault(params["storeID"], 0)))
				}
			}

			if data, err := json.Marshal(storeIDs2); err == nil {
				params["storeIDs"] = string(data)
			}
		}
	}
	sql, sqlParams, _, _, err := getStoresSql(ctx, keyword, params, orderTimeFrom, orderTimeTo)
	if err != nil {
		return nil, err
	}
	sql = `
	SELECT
	SQL_CALC_FOUND_ROWS
	DISTINCT t1.*,
	CAST(t1.lng AS DECIMAL(15,6))/1000000 float_lng,
	CAST(t1.lat AS DECIMAL(15,6))/1000000 float_lat,
	IF(mm.name <> '', mm.name, mm.user_id2) market_man_name,
	bank.value payee_bank_name,
	IF(om.name <> '', om.name, om.user_id2) operator_name,
	IF(om2.name <> '', om2.name, om2.user_id2) operator_name2,
	IF(om3.name <> '', om3.name, om3.user_id2) operator_name3,
	province.code province_code,
	province.name province_name,
	city.name city_name,
	district.name district_name,
	br.name brand_name,
	br.logo brand_logo,
	(SELECT COUNT(*) FROM store t11 WHERE t11.link_store_id = t1.id) link_store_count
	` + sql + `
	ORDER BY t1.id DESC
	LIMIT ? OFFSET ?
	`
	db := dao.GetDB()
	retVal = &StoresInfo{}

	var storeIDs []int
	var storeList []*StoreExt
	offset = jxutils.FormalizePageOffset(offset)
	pageSize = jxutils.FormalizePageSize(pageSize)
	sqlParams = append(sqlParams, pageSize, offset)
	//mapLimit := false
	txDB, _ := dao.Begin(db)

	if err = dao.GetRowsTx(txDB, &storeList, sql, sqlParams...); err == nil {
		retVal.Stores = storeList
		retVal.TotalCount = dao.GetLastTotalRowCount2(db, txDB)
		// 地图区域限制过滤
		//if mapLongitude2, ok := params["mapLongitude"].(string); ok {
		//	var (
		//		mapLatitude, mapLongitude float64
		//		mapRadius                 int
		//	)
		//	mapLimit = true
		//	mapLongitude = utils.Str2Float64(mapLongitude2)
		//	mapLatitude = utils.Str2Float64(params["mapLatitude"].(string))
		//	mapRadius = params["mapRadius"].(int)
		//	for _, v := range storeList {
		//		valid := !mapLimit
		//		if mapLimit {
		//			valid = jxutils.EarthDistance(mapLongitude, mapLatitude, v.FloatLng, v.FloatLat)*1000 <= float64(mapRadius)
		//		}
		//		if valid {
		//			retVal.Stores = append(retVal.Stores, v)
		//		}
		//	}
		//} else {
		//	retVal.Stores = storeList
		//}
		// 订单情况过滤
		//storeList, err = filterStoreByOrderInfo(db, retVal.Stores, orderTimeFrom, orderTimeTo, orderCountFrom, orderCountTo)
		//if err != nil {
		//	return nil, err
		//}

		// 分页
		//retVal.TotalCount = len(storeList)
		//retVal.Stores = nil
		//for i := offset; i < offset+pageSize && i < len(storeList); i++ {
		//	storeIDs = append(storeIDs, storeList[i].ID)
		//	retVal.Stores = append(retVal.Stores, storeList[i])
		//}
		//if len(storeIDs) == 0 {
		//	return retVal, nil
		//}

		// 导出门店地图标信息时，可能会需要转换门店坐标
		needConver2Baidu := int(utils.Interface2Int64WithDefault(params["coordinateType"], 0)) == model.CoordinateTypeBaiDu
		if needConver2Baidu {
			task := tasksch.NewParallelTask("坐标转换", tasksch.NewParallelConfig().SetParallelCount(4).SetBatchSize(tencent_map.MaxConvertCount), ctx,
				func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
					var coords []*tencent_map.Coordinate
					for _, v := range batchItemList {
						store := v.(*StoreExt)
						coords = append(coords, &tencent_map.Coordinate{
							Lng: store.FloatLng,
							Lat: store.FloatLat,
						})
					}
					//coords, err = api.BaiDuNaviAPI.BatchCoordinateConvert(coords, baidunavi.CoordSysGaoDe2Baidu)
					//if err == nil {
					for k, v := range batchItemList {
						store := v.(*StoreExt)
						coord := coords[k]
						store.FloatLng = coord.Lng
						store.FloatLat = coord.Lat
					}
					//}
					return retVal, err
				}, retVal.Stores)
			task.Run()
			task.GetResult(0)
		}
	}
	dao.Commit(db, txDB)
	if len(retVal.Stores) > 0 {
		setStoreMapInfo(ctx, db, retVal, storeIDs, briefLevel, params["isBussinessStatus"])
		retVal.MapCenterLng, retVal.MapCenterLat = getMapCenter(retVal.Stores)
	}
	return retVal, err
}

func filterStoreByOrderInfo(db *dao.DaoDB, inStores []*StoreExt, orderTimeFrom, orderTimeTo time.Time, orderCountFrom, orderCountTo int) (outStores []*StoreExt, err error) {
	if len(inStores) > 0 && !utils.IsTimeZero(orderTimeFrom) {
		storeIDs := make([]int, len(inStores))
		for k, v := range inStores {
			storeIDs[k] = v.ID
		}
		orderSaleList, err2 := dao.GetStoresOrderSaleInfo(dao.GetDB(), storeIDs, orderTimeFrom, orderTimeTo, []int{model.OrderStatusFinished})
		if err = err2; err != nil {
			return nil, err
		}
		storeOrderCountMap := make(map[int]int)
		for _, v := range orderSaleList {
			storeOrderCountMap[v.StoreID] += v.Count
		}

		for _, v := range inStores {
			orderCount := storeOrderCountMap[v.ID]
			if orderCount >= orderCountFrom && orderCount <= orderCountTo {
				v.OrderCount = orderCount
				outStores = append(outStores, v)
			}
		}
	} else {
		outStores = inStores
	}
	return outStores, err
}

func getMapCenter(storeList []*StoreExt) (lng, lat float64) {
	if len(storeList) == 0 {
		return 0, 0
	}

	lngAvg := float64(0)
	latAvg := float64(0)
	storeListLenFloat := float64(len(storeList))
	for _, store := range storeList {
		lngAvg += store.FloatLng
		latAvg += store.FloatLat
	}
	lngAvg = lngAvg / storeListLenFloat
	latAvg = latAvg / storeListLenFloat

	distAvg := float64(0)
	for _, store := range storeList {
		distAvg += math.Sqrt((store.FloatLng-lngAvg)*(store.FloatLng-lngAvg) + (store.FloatLat-latAvg)*(store.FloatLat-latAvg))
	}
	distAvg = (distAvg / storeListLenFloat)
	maxDist := distAvg * 5
	newStoreList := []*StoreExt{}
	for _, store := range storeList {
		dist := math.Sqrt((store.FloatLng-lngAvg)*(store.FloatLng-lngAvg) + (store.FloatLat-latAvg)*(store.FloatLat-latAvg))
		if dist <= maxDist {
			lng += store.FloatLng
			lat += store.FloatLat
			newStoreList = append(newStoreList, store)
		}
	}
	if len(newStoreList) == len(storeList) {
		lng = lng / float64(len(newStoreList))
		lat = lat / float64(len(newStoreList))
		// for _, store := range storeList {
		// 	globals.SugarLogger.Debugf("store:%s, lng:%, lat:%f", store.Name, store.FloatLng, store.FloatLat)
		// }
		return lng, lat
	}
	return getMapCenter(newStoreList)
}

func GetVendorStore(ctx *jxcontext.Context, vendorID int, vendorOrgCode, vendorStoreID string) (retVal *StoreExt, err error) {
	if vendorID == model.VendorIDJDShop && vendorStoreID == model.JdShopMainVendorStoreID {
		return nil, err
	}
	//绑定顺丰专送门店校验
	if vendorID == model.VendorIDSFPS {
		flag := false
		if store, err := dao.GetStoreDetail(dao.GetDB(), utils.Str2Int(vendorStoreID), 0, ""); err == nil {
			for k, _ := range sfps2.SFCityStoreIDs {
				if strings.Contains(store.CityName, k) || store.CityName == k {
					flag = true
				}
			}
		}
		if !flag {
			return nil, errors.New("此门店暂时不在顺丰派送范围，无法绑定")
		}
	}
	if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil {
		result, err2 := handler.ReadStore(ctx, vendorOrgCode, vendorStoreID, "")
		if err = err2; err == nil {
			retVal = &StoreExt{
				Store:    result.Store,
				FloatLng: jxutils.IntCoordinate2Standard(result.Lng),
				FloatLat: jxutils.IntCoordinate2Standard(result.Lat),
			}
			db := dao.GetDB()
			if city, err2 := dao.GetPlaceByCode(db, result.CityCode); err2 == nil {
				retVal.CityName = city.Name
			}
			if district, err2 := dao.GetPlaceByCode(db, result.DistrictCode); err2 == nil {
				retVal.DistrictName = district.Name
			}
			if !jxutils.IsLegalStoreID(retVal.ID) {
				retVal.ID = 0
			}

			retVal.BussinessStatus = result.Status // -2 下线,禁用 ,-1 休息 ,1 营业
			//if result.Status <= model.StoreStatusHaveRest {
			//	retVal.BussinessStatus = -1
			//} else {
			//	retVal.BussinessStatus = 1
			//}
			return retVal, nil
		}
		return nil, err
	} else {
		handler := partner.GetDeliveryPlatformFromVendorID(vendorID).Handler
		if store, err := handler.GetStore(ctx, 0, vendorStoreID); err == nil {
			retVal = &StoreExt{
				Store: store.Store,
				//暂时添加上
				//FloatLng: jxutils.IntCoordinate2Standard(store.Lng),
				//FloatLat: jxutils.IntCoordinate2Standard(store.Lat),
			}
			retVal.Name = store.Name
			retVal.BussinessStatus = store.CourierStatus
			//if retVal.Status == model.StoreStatusDisabled {
			//	retVal.BussinessStatus = -1
			//} else {
			//	retVal.BussinessStatus = 1
			//}
			globals.SugarLogger.Debugf("---------retVal := %s", utils.Format4Output(retVal, false))
			return retVal, err
		}
	}
	return nil, err
}

// QueryPlatformBalance 获取三方配送平台账号余额
func QueryPlatformBalance(vendorID int) (int64, error) {
	handlerInfo := partner.GetDeliveryPlatformFromVendorID(vendorID)
	if handlerInfo.Handler != nil {
		return handlerInfo.Handler.GetPlatformBalance()
	}
	return 0, fmt.Errorf("vendorId 解析异常")
}

// BalanceRecharge 三方配送平台账号余额充值
func BalanceRecharge(vendorID int, param *utils.RechargeBalance) (string, error) {
	handlerInfo := partner.GetDeliveryPlatformFromVendorID(vendorID)
	if handlerInfo.Handler != nil {
		return handlerInfo.Handler.BalanceRecharge(&utils.RechargeBalance{
			Amount:    param.Amount,
			Category:  param.Category,
			NotifyUrl: param.NotifyUrl,
		})
	}
	return "", fmt.Errorf("vendorId 解析异常")
}

func isUpdateStoreNeedSync(valid map[string]interface{}) bool {
	for k := range valid {
		if storeKeyPropertyMap[k] == 1 {
			return true
		}
	}
	return false
}

func checkBankBranch(payeeBankBranchName string) (err error) {
	if payeeBankBranchName != "" {
		if strings.Index(payeeBankBranchName, "支行") == -1 && strings.Index(payeeBankBranchName, "分行") == -1 {
			err = fmt.Errorf("支行信息异常，你可能没有在使用最新的版本，请强制刷新浏览器或联系开发")
		}
	}
	return err
}

func checkCreateStore(store *model.Store) (err error) {
	if store.Lng == 0 || store.Lat == 0 || store.Address == "" {
		err = fmt.Errorf("必须设置地址信息")
	}
	return err
}

func checkStoreDeliveryRange(deliveryRange string) (err error) {
	if deliveryRange != "" {
		points := jxutils.CoordinateStr2Points(deliveryRange)
		for _, point := range points {
			for _, coord := range point {
				if coord == 0 || math.IsNaN(coord) {
					return fmt.Errorf("坐标点数据非法:%s", deliveryRange)
				}
			}
		}
	}
	return err
}

func UpdateStore(ctx *jxcontext.Context, storeID int, payload map[string]interface{}, userName string) (num int64, err error) {
	if err = checkBankBranch(utils.Interface2String(payload["payeeBankBranchName"])); err != nil {
		return 0, err
	}
	db := dao.GetDB()
	store := &model.Store{}
	store.ID = storeID
	if err = dao.GetEntity(db, store); err != nil {
		return 0, err
	}
	var outStore *model.Store
	var beforStore = *store
	if payload["autoEnableAt"] != nil {
		payload["autoEnableAt"] = utils.Time2Date(utils.Str2Time(utils.Interface2String(payload["autoEnableAt"])))
	}
	if payload["smsNotify"] != nil {
		payload["smsNotify"] = 0 //model.NotifyTypeSMS
	}
	valid := dao.StrictMakeMapByStructObject2(payload, store, &outStore, userName)
	if err = checkStoreDeliveryRange(utils.Interface2String(valid["deliveryRange"])); err != nil {
		return 0, err
	}
	if globals.EnableWXAuth2 {
		if err = dao.ValidateRoles(db, utils.Interface2String(valid["marketManRole"]), utils.Interface2String(valid["operatorRole"]), utils.Interface2String(valid["operatorRole2"])); err != nil {
			return 0, err
		}
	}
	if payload["lng"] != nil || payload["lat"] != nil {
		intLng := jxutils.StandardCoordinate2Int(utils.Interface2Float64WithDefault(payload["lng"], 0.0))
		intLat := jxutils.StandardCoordinate2Int(utils.Interface2Float64WithDefault(payload["lat"], 0.0))
		if intLng != 0 && intLng != store.Lng {
			valid["lng"] = intLng
		}
		if intLat != 0 && intLat != store.Lng {
			valid["lat"] = intLat
		}
	}

	if payload["packageSetting"] != nil {
		valid["packageSetting"], _ = payload["packageSetting"].(json.Number).Int64()
	}
	if payload["packageSwitch"] != nil {
		valid["packageSwitch"], _ = payload["packageSwitch"].(json.Number).Int64()
	}
	if payload["pushBill"] != nil {
		valid["pushBill"], _ = payload["pushBill"].(json.Number).Int64()
	}
	if payload["freightMarkup"] != nil {
		valid["freightMarkup"], _ = payload["freightMarkup"].(json.Number).Int64()
	}

	if valid["originalName"] != nil {
		delete(valid, "originalName")
	}
	if valid["printerBindInfo"] != nil {
		delete(valid, "printerBindInfo")
	}
	syncStatus := model.SyncFlagModifiedMask
	if valid["name"] != nil {
		valid["name"] = jxutils.FormalizeName(valid["name"].(string))
		store.Name = valid["name"].(string)
		syncStatus |= model.SyncFlagStoreName
		courierList, _ := dao.GetStoreCourierList2(db, []int{storeID}, nil, 0, nil)
		for _, v := range courierList {
			v.VendorStoreName = store.Name
			dao.UpdateEntity(db, v, "VendorStoreName")
		}
	}
	printerVendorID := int(utils.Interface2Int64WithDefault(valid["printerVendorID"], 0))
	if printerVendorID == 0 {
		printerVendorID = store.PrinterVendorID
	}
	if printerVendorID == model.VendorIDXiaoWM {
		delete(valid, "printerKey")
	}
	// 网络打印机处理
	if valid["printerVendorID"] != nil || valid["printerSN"] != nil || valid["printerKey"] != nil {
		if valid["printerVendorID"] == nil {
			valid["printerVendorID"] = store.PrinterVendorID
		}
		if printerVendorID := int(utils.Interface2Int64WithDefault(valid["printerVendorID"], 0)); printerVendorID > 0 {
			handler := partner.GetPrinterPlatformFromVendorID(printerVendorID)
			if handler == nil {
				return 0, fmt.Errorf("不支持的打印机厂商ID:%d", printerVendorID)
			}
			if valid["printerSN"] == nil {
				valid["printerSN"] = store.PrinterSN
			}
			if valid["printerKey"] == nil {
				valid["printerKey"] = store.PrinterKey
			}
			newID1, newID2, err2 := handler.RegisterPrinter(ctx, valid["printerSN"].(string), valid["printerKey"].(string), store.Name, int64(store.ID))
			if err = err2; err != nil {
				return 0, err
			}
			handler.EmptyPrintList(ctx, newID1, newID2)
			if newID1 != "" {
				valid["printerSN"] = newID1
			}
			if newID2 != "" {
				valid["printerKey"] = newID2
			}
		} else {
			valid["printerVendorID"] = 0
			valid["printerSN"] = ""
			valid["printerKey"] = ""
		}
		valid["printerBindInfo"] = ""
		if handler := partner.GetPrinterPlatformFromVendorID(store.PrinterVendorID); handler != nil {
			handler.UnregisterPrinter(ctx, store.PrinterSN, utils.Int2Str(store.ID))
		}
	}
	if valid["soundPercentage"] != nil && valid["printerSN"] != nil {
		handler := partner.GetPrinterPlatformFromVendorID(printerVendorID)
		if handler == nil {
			return 0, fmt.Errorf("不支持的打印机厂商ID:%d", printerVendorID)
		}
		err = handler.SetSound(ctx, store.PrinterSN, store.PrinterKey, jxutils.TranslateSoundSize(printerVendorID, int(utils.Interface2Int64WithDefault(valid["soundPercentage"], 50))))
		if err != nil {
			return 0, err
		}
	}
	if valid["linkStoreID"] != nil {
		linkStoreID := int(utils.Interface2Int64WithDefault(valid["linkStoreID"], 0))
		linkStoreID, err = dao.GetRealLinkStoreID(db, linkStoreID)
		if err != nil {
			return 0, err
		}
		if err = checkStoreHaveLinkedStore(storeID, linkStoreID); err != nil {
			return 0, err
		}
		valid["linkStoreID"] = linkStoreID
	}

	// globals.SugarLogger.Debug(utils.Format4Output(valid, false))
	for _, v := range []string{
		"lng",
		"lat",
		"cityCode",
		"address",
		"deliveryRange",
	} {
		if valid[v] != nil {
			syncStatus |= model.SyncFlagStoreAddress
			break
		}
	}
	status := 0
	if valid["status"] != nil || valid["autoEnableAt"] != nil {
		if valid["status"] != nil {
			syncStatus |= model.SyncFlagStoreStatus
			status = int(utils.Interface2Int64WithDefault(valid["status"], 0))
			if status == model.StoreStatusDisabled {
				valid["deliveryRangeType"] = model.DeliveryRangeTypeRadius
				valid["deliveryRange"] = "1"
			}
		} else {
			status = store.Status
		}

		if status != model.StoreStatusClosed && status != model.StoreStatusHaveRest {
			valid["autoEnableAt"] = nil
		} else {
			if valid["autoEnableAt"] != nil {
				status = model.StoreStatusHaveRest
			} else {
				status = model.StoreStatusClosed
				valid["autoEnableAt"] = nil
			}
			valid["status"] = status
		}
	}

	if valid["deliveryRange"] != nil {
		valid["deliveryRange"] = strings.Trim(valid["deliveryRange"].(string), ";")
	}

	//时间校验
	if outStore != nil {
		if outStore.OpenTime1 != 0 && outStore.CloseTime1 != 0 {
			if err := ValidateStructPartial(outStore, "OpenTime1", "CloseTime1"); err != nil {
				return 0, errors.New(fmt.Sprintf("门店营业时间1设置不合法！时间范围1 ：[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1))
			}
		}
		if (outStore.OpenTime1 == 0 && outStore.CloseTime1 != 0) || (outStore.OpenTime1 != 0 && outStore.CloseTime1 == 0) {
			return 0, errors.New(fmt.Sprintf("门店营业时间1设置不合法！时间范围1 ：[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1))
		}
		if outStore.OpenTime2 != 0 && outStore.CloseTime2 != 0 {
			if err := ValidateStructPartial(outStore, "OpenTime2", "CloseTime2"); err != nil {
				return 0, errors.New(fmt.Sprintf("门店营业时间2设置不合法！时间范围2 ：[%v] 至 [%v]", outStore.OpenTime2, outStore.CloseTime2))
			}
		}
		if (outStore.OpenTime2 == 0 && outStore.CloseTime2 != 0) || (outStore.OpenTime2 != 0 && outStore.CloseTime2 == 0) {
			return 0, errors.New(fmt.Sprintf("门店营业时间2设置不合法！时间范围2 ：[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1))
		}
		if outStore.OpenTime1 != 0 && outStore.CloseTime1 != 0 && outStore.OpenTime2 != 0 && outStore.CloseTime2 != 0 {
			if outStore.OpenTime2 < outStore.CloseTime1 {
				return 0, errors.New(fmt.Sprintf("门店营业时间设置不合法!第二段营业时间应该在第一段营业时间之后！"))
			}
		}
		if beginAt, endAt := GetTimeMixByInt(outStore.OpenTime1, outStore.CloseTime1, outStore.OpenTime2, outStore.CloseTime2); beginAt != 0 && endAt != 0 {
			return 0, errors.New(fmt.Sprintf("两段门店营业时间不可交叉！时间范围1 ：[%v] 至 [%v], 时间范围2 ：[%v] 至 [%v]", outStore.OpenTime1, outStore.CloseTime1, outStore.OpenTime2, outStore.CloseTime2))
		}
	}

	if valid["promoteInfo"] != nil {
		valid["promoteInfo"] = strings.ReplaceAll(valid["promoteInfo"].(string), "{phone}", store.Tel1)
	}

	for k, _ := range valid {
		if earningInfoMap[k] != "" || marketInfoMap[k] != "" {
			//增加权限校验
			userRole, err := dao.GetUserRole(dao.GetDB(), []string{ctx.GetUserID()}, []int{model.RoleUpdateSettle})
			if err != nil || len(userRole) == 0 {
				return 0, errors.New(fmt.Sprintf("当前用户 [%v] 无权限修改此字段！", ctx.GetUserName()))
			} else {
				menus, _ := dao.GetMenuWithUser(dao.GetDB(), "结算权限", 0, 1, ctx.GetUserID())
				if len(menus) == 0 {
					if marketInfoMap[k] != "" {
						menus2, _ := dao.GetMenuWithUser(dao.GetDB(), "市场信息", 0, 1, ctx.GetUserID())
						if len(menus2) == 0 {
							return 0, errors.New(fmt.Sprintf("当前用户 [%v] 无权限修改此字段！", ctx.GetUserName()))
						}
					} else {
						return 0, errors.New(fmt.Sprintf("当前用户 [%v] 无权限修改此字段！", ctx.GetUserName()))
					}
				}
			}
		}
	}

	if len(valid) > 0 {
		if globals.IsAddEvent {
			mapBefore := refutil.FindMapAndStructMixed(valid, beforStore)
			err = AddEventDetail(db, ctx, model.OperateUpdate, storeID, model.ThingTypeStore, storeID, BuildDiffData(mapBefore), BuildDiffData(valid))
		}
		txDB, _ := dao.Begin(db)
		defer func() {
			dao.Rollback(db, txDB)
		}()

		num, err = dao.UpdateEntityLogically(db, store, valid, userName, nil)
		if err == nil && num == 1 {
			if isUpdateStoreNeedSync(valid) {
				dummy := &model.StoreMap{}
				kv := make(map[string]interface{})
				if valid["status"] != nil {
					//if syncStatus&model.SyncFlagStoreStatus != 0 && status == model.StoreStatusOpened {
					kv[model.FieldStatus] = status
					//}
				}
				_, err2 := dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, dummy, kv, userName, map[string]interface{}{
					model.FieldStoreID: store.ID,
				}, model.FieldSyncStatus, syncStatus)
				if err = err2; err == nil {
					dao.Commit(db, txDB)
					_, err = CurVendorSync.SyncStore(ctx, db, -1, store.ID, false, userName)
					if valid["tel1"] != nil || valid["tel2"] != nil {
						TryAddStoreBossRole4StoreByMobile(ctx, store.ID, []string{utils.Interface2String(valid["tel1"]), utils.Interface2String(valid["tel2"])})
					}
					if syncStatus&model.SyncFlagStoreAddress != 0 || valid["tel1"] != nil || valid["payeeName"] != nil {
						// 修改门店信息(第三方配送平台同步)
						updateCourierStores(ctx, storeID)
					}
				}
			} else {
				dao.Commit(db, txDB)
			}
			notifyStoreOperatorChanged(ctx, store.ID, "京东运营", store.OperatorPhone, valid["operatorPhone"])
			notifyStoreOperatorChanged(ctx, store.ID, "美团运营", store.OperatorPhone2, valid["operatorPhone2"])
			notifyStoreOperatorChanged(ctx, store.ID, "饿百运营", store.OperatorPhone3, valid["operatorPhone3"])
			notifyStoreOperatorChanged(ctx, store.ID, "市场", store.MarketManPhone, valid["marketManPhone"])
			if err == nil {
				if valid["openTime1"] != 0 || valid["closeTime1"] != 0 || valid["openTime2"] != 0 || valid["closeTime2"] != 0 {
					err = CurVendorSync.ChangeStoreSkuSaleStatus(ctx, storeID, true, true)
				}
			}
		}
	} else {
		globals.SugarLogger.Debugf("UpdateStore track:%s, store:%s", ctx.GetTrackInfo(), utils.Format4Output(store, true))
	}

	return num, err
}

//// updateVendorStoreStatusAndDeliveryStatus 当门店被禁用时,将三方平台和三方配送全部关闭
//func updateVendorStoreStatusAndDeliveryStatus(ctx *jxcontext.Context, storeId int) error {
//	var (
//		db = dao.GetDB()
//	)
//	// 获取门店绑定三方平台列表
//	storeMap, err := dao.GetStoresMapList(db, nil, []int{storeId}, nil, 0, 0, "", "", "")
//	if err != nil {
//		return err
//	}
//	// 获取门店绑定的配送凭条
//	storeCouriers, err := dao.GetOpenedStoreCouriersByStoreID(db, storeId, -1)
//	if err != nil {
//		return err
//	}
//
//	work := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
//		var errList []error
//		step := batchItemList[0].(int)
//		switch step {
//		case 0: // 同步修改门店营业撞他
//			// 京东关店,本地店铺状态修改
//			// 美团关店,本地店铺状态修改
//			// 饿百关店,本地店铺状态修改
//			// 抖音关店,本地店铺状态修改
//		case 1: // 同步修改门店配送状态
//			// 美团跑腿配送(没有门店状态修改,直接修改本店)
//			// 蜂鸟配送,关闭(有)
//			// 达达配送,关闭(有)
//			// uu跑腿,关闭(不涉及门店概念,直接本地取消绑定信息)
//		}
//		return errList, nil
//	}
//	tasksch.NewParallelTask(
//		"禁用门店,修改平台门店状态",
//		tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(false),
//		ctx,
//		work,
//		[]int{0, 1},
//	)
//
//}

func notifyStoreOperatorChanged(ctx *jxcontext.Context, storeID int, operatorRoleName, phone string, newPhone interface{}) {
	if phone != "" && newPhone != nil {
		db := dao.GetDB()
		if user, err := dao.GetUserByID(db, "mobile", phone); err == nil {
			curUserName := ""
			if newOperator := utils.Interface2String(newPhone); newOperator != "" {
				if curUser, err := dao.GetUserByID(db, "mobile", newOperator); err == nil {
					curUserName = curUser.GetName()
				}
			}
			ddmsg.SendUserMessage(dingdingapi.MsgTyeText, user.GetID(), fmt.Sprintf("门店%s变更", operatorRoleName), fmt.Sprintf("门店:%d,原%s:%s,变更为:%s，操作人：%s", storeID, operatorRoleName, user.GetName(), curUserName, ctx.GetFullUser().GetName()))
			AddEventDetail(db, ctx, model.OperateUpdate, storeID, model.ThingTypeStore, storeID, BuildDiffData(map[string]interface{}{
				"storeId":        storeID,
				operatorRoleName: user.GetName(),
				"operator":       ctx.GetFullUser().GetName(),
			}), BuildDiffData(map[string]interface{}{
				"storeId":        storeID,
				operatorRoleName: curUserName,
				"operator":       ctx.GetFullUser().GetName(),
			}))
		}
	}
}

func SetStoreStatus(ctx *jxcontext.Context, storeID, status int) (err error) {
	payload := map[string]interface{}{
		"status": status,
	}
	_, err = UpdateStore(ctx, storeID, payload, ctx.GetUserName())
	return err
}

func EnableHaveRestStores(ctx *jxcontext.Context, isAsync, isContinueWhenError bool) (hint string, err error) {
	storeInfo, err := GetStores(ctx, "", map[string]interface{}{
		"statuss": string(utils.MustMarshal([]int{model.StoreStatusHaveRest})),
	}, 0, model.UnlimitedPageSize, utils.DefaultTimeValue, utils.DefaultTimeValue, 0, 0)
	if err != nil {
		return "", err
	}
	if len(storeInfo.Stores) == 0 {
		return "0", nil
	}

	task := tasksch.NewParallelTask("EnableHaveRestStores", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			store := batchItemList[0].(*StoreExt)
			var autoEnableAt time.Time
			if store.AutoEnableAt != nil {
				autoEnableAt = *store.AutoEnableAt
			} else {
				autoEnableAt = utils.Time2Date(time.Now().Add(12 * time.Hour)) // 临时休息，但没有设置恢复营业时间，缺省是第二天
			}
			if time.Now().Sub(autoEnableAt) >= -2*time.Hour {
				err = SetStoreStatus(ctx, store.ID, model.StoreStatusOpened)
			}
			return nil, err
		}, storeInfo.Stores)
	tasksch.ManageTask(task).Run()
	if !isAsync {
		if _, err = task.GetResult(0); err == nil {
			hint = utils.Int2Str(len(storeInfo.Stores))
		}
	} else {
		hint = task.GetID()
	}
	return hint, err
}

func checkStoreHaveLinkedStore(storeID, linkStoreID int) (err error) {
	if linkStoreID != 0 {
		if storeID == linkStoreID {
			err = fmt.Errorf("不能自我关联")
		} else {
			storeList, err2 := dao.GetStoreLinkStores(dao.GetDB(), storeID)
			if err = err2; err == nil {
				if len(storeList) > 0 {
					var storeInfo []string
					for _, v := range storeList {
						storeInfo = append(storeInfo, utils.Int2Str(v.ID))
					}
					err = fmt.Errorf("门店%d已经被其它门店(%s)关联，不能再关联至其它门店", storeID, strings.Join(storeInfo, ","))
				}
			}
		}
	}
	return err
}

// 创建京西门店，同时生成专送门店。
func CreateStore(ctx *jxcontext.Context, storeExt *StoreExt, userName string) (id int, err error) {
	if err = checkBankBranch(storeExt.PayeeBankBranchName); err != nil {
		return 0, err
	}
	store := &storeExt.Store
	if store.ID != 0 && !jxutils.IsLegalStoreID(store.ID) {
		return 0, fmt.Errorf("ID:%d不是合法的京西门店编号", store.ID)
	}
	db := dao.GetDB()
	if globals.EnableWXAuth2 {
		if err = dao.ValidateRoles(db, store.MarketManRole, store.OperatorRole, store.OperatorRole2, store.OperatorRole3); err != nil {
			return 0, err
		}
	}
	realLinkStoreID, err := dao.GetRealLinkStoreID(db, storeExt.LinkStoreID)
	if err != nil {
		return 0, err
	}
	if err = checkStoreHaveLinkedStore(storeExt.ID, realLinkStoreID); err != nil {
		return 0, err
	}
	storeExt.LinkStoreID = realLinkStoreID

	if storeExt.MarketManName == "" {
		storeExt.MarketManName = ctx.GetUserName()
	}
	if storeExt.MarketManPhone == "" {
		storeExt.MarketManPhone, _ = ctx.GetMobileAndUserID()
	}

	existingID := store.ID
	store.Lng = jxutils.StandardCoordinate2Int(storeExt.FloatLng)
	store.Lat = jxutils.StandardCoordinate2Int(storeExt.FloatLat)
	if err = checkCreateStore(&storeExt.Store); err != nil {
		return 0, err
	}
	store.Name = jxutils.FormalizeName(store.Name)
	store.DeliveryRange = strings.Trim(store.DeliveryRange, ";")
	if store.PrinterSN != "" {
		handler := partner.GetPrinterPlatformFromVendorID(store.PrinterVendorID)
		if handler == nil {
			return 0, fmt.Errorf("不支持的打印机厂商ID:%d", store.PrinterVendorID)
		}
		newID1, newID2, err2 := handler.RegisterPrinter(ctx, store.PrinterSN, store.PrinterKey, store.Name, int64(store.ID))
		if err = err2; err != nil {
			return 0, err
		}
		handler.EmptyPrintList(ctx, newID1, newID2)
		if newID1 != "" {
			store.PrinterSN = newID1
		}
		if newID2 != "" {
			store.PrinterKey = newID2
		}
	}
	if store.StoreLevel == "" {
		store.StoreLevel = "C"
	}
	dao.WrapAddIDCULDEntity(store, userName)
	store.ID = existingID
	store.SMSNotify = 0 // 禁止使用订单短信通知
	if err = dao.CreateEntity(db, store); err == nil {
		if globals.IsAddEvent {
			err = AddEventDetail(db, ctx, model.OperateAdd, store.ID, model.ThingTypeStore, store.ID, "", "")
		}
		// 同步门店信息
		UpdateOrCreateCourierStores(ctx, store.ID, false, false, false)
		TryAddStoreBossRole4StoreByMobile(ctx, storeExt.ID, []string{storeExt.Tel1, storeExt.Tel2})
		// InsertStoreCategories(ctx, db, store.ID)
		AddStoreVendorMap(ctx, db, model.VendorIDJX, "", store.ID, &model.StoreMap{
			VendorStoreID:       utils.Int2Str(store.ID),
			AutoPickup:          1,
			DeliveryCompetition: 1,
			PricePercentage:     100,
			IsSync:              1,
			Status:              model.StoreStatusOpened,
			PricePercentagePack: "无",
			VendorID:            model.VendorIDJX,
			DeliverySelf:        1,
		}, false)
		//尝试把平台负责人加到他自己的权限里
		if store.MarketManPhone != "" {
			user, _ := dao.GetUserByID(db, "mobile", store.MarketManPhone)
			if roles, _ := dao.GetRole(db, user.Name, ""); len(roles) > 0 {
				role := roles[0]
				if role.StoreIDs != "" {
					role.StoreIDs = role.StoreIDs + "," + utils.Int2Str(store.ID)
				} else {
					role.StoreIDs = utils.Int2Str(store.ID)
				}
				dao.UpdateEntity(db, role, "StoreIDs")
			} else {
				if roles, _ := dao.GetRole(db, user.Name+"城市", ""); len(roles) > 0 {
					role := roles[0]
					if role.StoreIDs != "" {
						role.StoreIDs = role.StoreIDs + "," + utils.Int2Str(store.ID)
					} else {
						role.StoreIDs = utils.Int2Str(store.ID)
					}
					dao.UpdateEntity(db, role, "StoreIDs")
				}
			}
		}
		return store.ID, err
	}
	return 0, err
}

// 创建京西门店，不在生成专送门店！
func CreateStore2JX(ctx *jxcontext.Context, storeExt *StoreExt, userName string) (id int, err error) {
	if err = checkBankBranch(storeExt.PayeeBankBranchName); err != nil {
		return 0, err
	}
	store := &storeExt.Store
	if store.ID != 0 && !jxutils.IsLegalStoreID(store.ID) {
		return 0, fmt.Errorf("ID:%d不是合法的京西门店编号", store.ID)
	}
	db := dao.GetDB()
	if globals.EnableWXAuth2 {
		if err = dao.ValidateRoles(db, store.MarketManRole, store.OperatorRole, store.OperatorRole2, store.OperatorRole3); err != nil {
			return 0, err
		}
	}
	realLinkStoreID, err := dao.GetRealLinkStoreID(db, storeExt.LinkStoreID)
	if err != nil {
		return 0, err
	}
	if err = checkStoreHaveLinkedStore(storeExt.ID, realLinkStoreID); err != nil {
		return 0, err
	}
	storeExt.LinkStoreID = realLinkStoreID

	if storeExt.MarketManName == "" {
		storeExt.MarketManName = ctx.GetUserName()
	}
	if storeExt.MarketManPhone == "" {
		storeExt.MarketManPhone, _ = ctx.GetMobileAndUserID()
	}

	existingID := store.ID
	store.Lng = jxutils.StandardCoordinate2Int(storeExt.FloatLng)
	store.Lat = jxutils.StandardCoordinate2Int(storeExt.FloatLat)
	if err = checkCreateStore(&storeExt.Store); err != nil {
		return 0, err
	}
	store.Name = jxutils.FormalizeName(store.Name)
	store.DeliveryRange = strings.Trim(store.DeliveryRange, ";")
	if store.PrinterSN != "" {
		handler := partner.GetPrinterPlatformFromVendorID(store.PrinterVendorID)
		if handler == nil {
			return 0, fmt.Errorf("不支持的打印机厂商ID:%d", store.PrinterVendorID)
		}
		newID1, newID2, err2 := handler.RegisterPrinter(ctx, store.PrinterSN, store.PrinterKey, store.Name, int64(store.ID))
		if err = err2; err != nil {
			return 0, err
		}
		handler.EmptyPrintList(ctx, newID1, newID2)
		if newID1 != "" {
			store.PrinterSN = newID1
		}
		if newID2 != "" {
			store.PrinterKey = newID2
		}
	}
	if store.StoreLevel == "" {
		store.StoreLevel = "C"
	}
	if store.Tel1 == "" {
		store.Tel1 = model.DefaultPhone
	}
	if store.IDName == "" {
		store.IDName = model.DefaultName
	}
	if store.IDCode == "" {
		store.IDCode = model.DefaultIdCard
	}
	// 默认无品牌
	store.BrandID = 9

	dao.WrapAddIDCULDEntity(store, userName)
	store.ID = existingID
	if err = dao.CreateEntity(db, store); err == nil {
		if globals.IsAddEvent {
			err = AddEventDetail(db, ctx, model.OperateAdd, store.ID, model.ThingTypeStore, store.ID, "", "")
		}
		// 同步门店信息
		//UpdateOrCreateCourierStores(ctx, store.ID, false, false, false)
		TryAddStoreBossRole4StoreByMobile(ctx, storeExt.ID, []string{storeExt.Tel1, storeExt.Tel2})
		// InsertStoreCategories(ctx, db, store.ID)
		AddStoreVendorMap(ctx, db, model.VendorIDJX, "", store.ID, &model.StoreMap{
			VendorStoreID:       utils.Int2Str(store.ID),
			AutoPickup:          1,
			DeliveryCompetition: 1,
			PricePercentage:     100,
			IsSync:              0,
			Status:              model.StoreStatusOpened,
			PricePercentagePack: "无",
			CreateDeliveryType:  model.YES, // 菜市门店默认门店发单
			VendorID:            model.VendorIDJX,
		}, false)
		//尝试把平台负责人加到他自己的权限里
		if store.MarketManPhone != "" {
			user, _ := dao.GetUserByID(db, "mobile", store.MarketManPhone)
			if roles, _ := dao.GetRole(db, user.Name, ""); len(roles) > 0 {
				role := roles[0]
				if role.StoreIDs != "" {
					role.StoreIDs = role.StoreIDs + "," + utils.Int2Str(store.ID)
				} else {
					role.StoreIDs = utils.Int2Str(store.ID)
				}
				dao.UpdateEntity(db, role, "StoreIDs")
			} else {
				if roles, _ := dao.GetRole(db, user.Name+"城市", ""); len(roles) > 0 {
					role := roles[0]
					if role.StoreIDs != "" {
						role.StoreIDs = role.StoreIDs + "," + utils.Int2Str(store.ID)
					} else {
						role.StoreIDs = utils.Int2Str(store.ID)
					}
					dao.UpdateEntity(db, role, "StoreIDs")
				}
			}
		}
		return store.ID, err
	}
	return 0, err
}

func CreateStoreByUser(ctx *jxcontext.Context, mobile string) (id int, err error) {
	store := &model.Store{}
	db := dao.GetDB()
	store.Lng = jxutils.StandardCoordinate2Int(104.3975)
	store.Lat = jxutils.StandardCoordinate2Int(30.504679)
	store.Address = "成都市简阳市简阳市"
	store.Name = "无名称" + utils.Int64ToStr(time.Now().Unix())
	store.DeliveryRangeType = model.DeliveryRangeTypeRadius
	store.DeliveryRange = "3000"
	store.PayPercentage = 100
	store.Status = model.StoreStatusClosed
	store.OpenTime1 = 700
	store.CloseTime1 = 1900
	store.Tel1 = mobile
	store.CityCode = 510100
	store.DistrictCode = 510185
	store.BrandID = 9
	if store.StoreLevel == "" {
		store.StoreLevel = "C"
	}
	store.MarketManPhone = "18048531223"
	dao.WrapAddIDCULDEntity(store, ctx.GetUserName())
	storeList, _ := dao.GetStoreList(db, nil, nil, nil, nil, []string{mobile}, "")
	if len(storeList) > 0 {
		return 0, fmt.Errorf("此电话已有门店了！[%v]", mobile)
	}
	if err = dao.CreateEntity(db, store); err == nil {
		if globals.IsAddEvent {
			err = AddEventDetail(db, ctx, model.OperateAdd, store.ID, model.ThingTypeStore, store.ID, "", "")
		}
		TryAddStoreBossRole4StoreByMobile(ctx, store.ID, []string{mobile})
		return store.ID, err
	}
	return 0, err
}

func GetStoreVendorMaps(ctx *jxcontext.Context, db *dao.DaoDB, storeID int, vendorID int) (storeMaps []*model.StoreMap, err error) {
	cond := map[string]interface{}{
		model.FieldStoreID: storeID,
	}
	if vendorID != -1 {
		cond[model.FieldVendorID] = vendorID
	}
	if vendorID == model.VendorIDDD {
		if err1 := dao.GetEntitiesByKV(db, &storeMaps, cond, false); err1 == nil && len(storeMaps) > 0 {
			_ = tiktok_store.GetRemoteStoreSth(storeMaps[0].VendorOrgCode, storeMaps[0].VendorStoreID, storeID)
		}
	}
	return storeMaps, dao.GetEntitiesByKV(db, &storeMaps, cond, false)
}

// todo 需要对字段做有效性检查
func AddStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, vendorID int, vendorOrgCode string, storeID int, storeMap *model.StoreMap, isAdd bool) (outStoreMap *model.StoreMap, err error) {
	errList := errlist.ErrList{}
	if storeID == 0 {
		return nil, fmt.Errorf("storeID不能为0")
	}
	if vendorID != model.VendorIDJD && (storeMap.AutoPickup == 0) {
		return nil, fmt.Errorf("非京东平台要求必须自动拣货")
	}
	userName := ctx.GetUserName()
	storeMap.StoreID = storeID
	storeMap.VendorID = vendorID
	storeMap.VendorOrgCode = vendorOrgCode
	storeMap.Status = model.StoreStatusOpened
	storeMap.DeliveryType = model.StoreDeliveryTypeByStore
	storeMap.SyncStatus = 0
	if vendorID != model.VendorIDJX && vendorID != model.VendorIDYB && vendorID != model.VendorIDJDShop {
		if storeMap.VendorOrgCode == "" {
			return nil, fmt.Errorf("必须指定平台分账号信息")
		}
		if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil {
			if vendorOrgCode != globals.Mtwm2Code {
				store, err2 := handler.ReadStore(ctx, vendorOrgCode, storeMap.VendorStoreID, storeMap.VendorStoreName)
				if err = err2; err == nil || storeMap.IsSync == 0 {
					if store != nil {
						storeMap.DeliveryType = store.DeliveryType
						storeMap.Status = store.Status
						storeMap.VendorStoreName = store.Name
					}
					err = nil
				}
			} else {
				//美团商超获取token
				mtapi := apimanager.CurAPIManager.GetAPI(model.VendorIDMTWM, storeMap.VendorOrgCode).(*mtwmapi.API)
				if token, err := mtapi.GetAccessToken2(storeMap.VendorStoreID); err == nil && token != nil {
					storeMap.MtwmToken = token.AccessToken
					storeMap.MtwmRefreshToken = token.RefreshToken
				}
				if err != nil {
					return nil, err
				}
			}
			storeMap.SyncStatus = model.SyncFlagModifiedMask | model.SyncFlagStoreName | model.SyncFlagStoreAddress // 新增绑定门店是修改的概念
		} else {
			err = ErrCanNotFindVendor
		}
		storeDetail, _ := dao.GetStoreDetailByVendorStoreID(db, storeMap.VendorStoreID, storeMap.VendorID, "")
		if storeDetail != nil {
			return nil, fmt.Errorf("此平台门店ID已在京西有绑定，请先解除绑定，平台门店ID :[%v]", storeMap.VendorStoreID)
		}
		if vendorID == model.VendorIDDD { //抖店绑定店铺时 检查并补充创建
			if err2 := tiktok_store.CreateOrUpdateAll(vendorOrgCode, int64(storeMap.StoreID), utils.Str2Int64(storeMap.VendorStoreID), int64(storeMap.DeliveryFeeDeductionFee), int64(storeMap.DeliveryFeeDeductionSill), utils.Str2Int64(storeMap.YbStorePrefix), storeMap.YbAppID); err2 != nil {
				errList.AddErr(err2)
			}
		}
	} else if vendorID == model.VendorIDJX {
		ReCalculateJxPriceLight(db, ctx, storeID)
		//}	else if vendorID == model.VendorIDYB {
		//	err = checkYbParams(db, storeMap, storeID)
		//	if err != nil {
		//		return nil, err
		//	}
	} else if vendorID == model.VendorIDJDShop {
		if storeMap.VendorStoreID == "" {
			storeMap.SyncStatus = model.SyncFlagNewMask //京东商城要去建店
		} else {
			if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil {
				store, err2 := handler.ReadStore(ctx, vendorOrgCode, storeMap.VendorStoreID, storeMap.VendorStoreName)
				if err = err2; err == nil || storeMap.IsSync == 0 {
					if store != nil {
						storeMap.Status = store.Status
					}
					err = nil
					storeMap.SyncStatus = model.SyncFlagModifiedMask | model.SyncFlagStoreName | model.SyncFlagStoreAddress
				} else {
					return nil, err
				}
			} else {
				err = ErrCanNotFindVendor
			}
		}
	}

	storeMapList, err := dao.GetStoresMapList2(db, []int{vendorID}, []int{storeID}, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "", true)
	if len(storeMapList) > 0 {
		_, err = CurVendorSync.SyncStore(ctx, db, storeMap.VendorID, storeID, false, userName)
	} else {
		if err == nil {
			dao.WrapAddIDCULDEntity(storeMap, userName)
			if db == nil {
				db = dao.GetDB()
			}
			txDB, _ := dao.Begin(db)
			defer func() {
				if r := recover(); r != nil {
					dao.Rollback(db, txDB)
					panic(r)
				}
			}()
			if err = dao.CreateEntity(db, storeMap); err == nil {
				dao.Commit(db, txDB)
				outStoreMap = storeMap
			} else {
				dao.Rollback(db, txDB)
			}
		}
	}
	if err != nil {
		return outStoreMap, err
	}
	if vendorID == model.VendorIDJDShop {
		//绑定京东商城后，需要对绑定的门店现有的和模板店相同且可售的商品设置京东商城的门店库存
		//TODO SyncFlagSaleMask对京东商城来说，修改门店商品状态就是修改库存
		if _, err = SetStoreSkuSyncStatus2(db, []int{storeID}, []int{model.VendorIDJDShop}, findSkusBetweenJdsMainStore(db, storeID), model.SyncFlagSaleMask); err == nil {
			// CurVendorSync.SyncStoresSkus(ctx, nil, 0, db, nil, []int{storeID}, nil, false, true, true)
		}
	}
	if globals.IsAddEvent {
		err = AddEventDetail(db, ctx, model.OperateAdd, vendorID, model.ThingTypeStore, storeID, "", `{"VendorID":`+utils.Int2Str(vendorID)+`}`)
	}
	_, err = CurVendorSync.SyncStore(ctx, db, storeMap.VendorID, storeID, false, userName)
	_, err = CurVendorSync.FullSyncStoresSkus(ctx, db, []int{vendorID}, []int{storeID}, true, nil, true, true, isAdd)
	if utils.IsNil(errList) && err == nil {
		return outStoreMap, nil
	} else {
		return outStoreMap, errors.New(fmt.Sprintf("errList=%s,err=%v", utils.Format4Output(errList, true), err))
	}
}

func DeleteStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, userName string) (num int64, err error) {
	if db == nil {
		db = dao.GetDB()
	}
	storeMap := &model.StoreMap{
		StoreID:  storeID,
		VendorID: vendorID,
	}
	storeMap.DeletedAt = utils.DefaultTimeValue
	if err = dao.GetEntity(db, storeMap, model.FieldStoreID, model.FieldVendorID, model.FieldDeletedAt); err == nil {
		if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
			handler.UpdateStoreCustomID(ctx, storeMap.VendorOrgCode, storeMap.VendorStoreID, utils.Str2Int64WithDefault(storeMap.VendorStoreID, 0))
		}
		storeMap.FakeOpenStart = 0
		storeMap.FakeOpenStop = 0
		num, err = dao.DeleteEntityLogically(db, storeMap, map[string]interface{}{
			model.FieldStatus: model.StoreStatusDisabled,
		}, userName, nil)
		if globals.IsAddEvent {
			err = AddEventDetail(db, ctx, model.OperateDelete, vendorID, model.ThingTypeStore, storeID, "", `{"VendorID":`+utils.Int2Str(vendorID)+`}`)
		}
	}
	//删除抖店映射
	//if vendorID == model.VendorIDDD {
	if _, err1 := dao.ExecuteSQL(db, "DELETE FROM freight_template WHERE freight_template.store_id = ? ", storeID); err1 != nil {
		err = fmt.Errorf("%v,%v", err, err1)
	}
	//}
	return num, err
}

func isStoreMapNeedSync(vendorID int, valid map[string]interface{}) bool {
	if vendorID != model.VendorIDJX {
		for k := range valid {
			if storeMapKeyPropertyMap[k] == 1 {
				return true
			}
		}
	}
	return false
}

func UpdateStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, payload map[string]interface{}, userName string) (num int64, err error) {
	if vendorID != model.VendorIDJD {
		if autoPickup, ok := payload["autoPickup"]; ok && autoPickup == 0 {
			return 0, fmt.Errorf("非京东平台要求必须自动拣货")
		}
	}
	var storeHandler partner.IPurchasePlatformHandler
	if vendorID != model.VendorIDJX {
		storeHandler = CurVendorSync.GetStoreHandler(vendorID)
		if storeHandler == nil {
			return 0, ErrCanNotFindVendor
		}
	}
	// 暂时不开放isSync
	if isSync, ok := payload["isSync"].(int); ok && isSync == 0 {
		delete(payload, "isSync")
	}

	delete(payload, "bussinessStatus")

	//var vendorOrgCode = ""
	//if payload["vendorOrgCode"].(string) == "" {
	//	return 0, fmt.Errorf("平台账号不能为空")
	//} else {
	//	vendorOrgCode = payload["vendorOrgCode"].(string)
	//}
	if db == nil {
		db = dao.GetDB()
	}
	storeMap := &model.StoreMap{
		StoreID:  storeID,
		VendorID: vendorID,
		//VendorOrgCode: vendorOrgCode,
	}
	storeMap.DeletedAt = utils.DefaultTimeValue
	if err = dao.GetEntity(db, storeMap, model.FieldStoreID, model.FieldVendorID, model.FieldDeletedAt); err != nil {
		return 0, err
	}

	var beforeStoreMap = *storeMap
	syncStatus := model.SyncFlagModifiedMask
	valid := dao.StrictMakeMapByStructObject(payload, storeMap, userName)
	if valid["status"] != nil {
		syncStatus |= model.SyncFlagStoreStatus
	}
	//修改分类开关需要打同步标志
	if valid["isSysCat"] != nil {
		SetStoreCategorySyncStatus2(db, []int{storeID}, nil, model.SyncFlagModifiedMask)
	}
	if _, ok := valid["vendorStoreName"]; ok {
		// if utf8.RuneCountInString(vendorStoreName) > jdapi.MaxStoreNameLen && vendorID == model.VendorIDJD {
		// 	return 0, fmt.Errorf("门店名称不允许超过13位！")
		// }
		syncStatus |= model.SyncFlagStoreName
	}

	//增加同步到抖店厂商的同步标志
	DDFlag := false
	errList := errlist.ErrList{}
	//deliveryFeeDeductionSill-包邮模板 满x包邮金额   deliveryFeeDeductionFee-打包费 ybStorePrefix-起送价  ybAppID-自动运力设置
	if (valid["deliveryFeeDeductionSill"] != nil || valid["deliveryFeeDeductionFee"] != nil || valid["ybStorePrefix"] != nil || valid["ybAppID"] != nil) && vendorID == model.VendorIDDD {
		DDFlag = true
	} //特殊处理抖店运费模板、打包费
	if DDFlag {
		if err := tiktok_store.SpecialTreat(storeMap.VendorOrgCode, utils.Str2Int64(storeMap.VendorStoreID), int64(storeMap.StoreID), int64(storeMap.DeliveryFeeDeductionFee), int64(storeMap.DeliveryFeeDeductionSill), utils.Str2Int64(storeMap.YbStorePrefix), storeMap.YbAppID); err != nil {
			errList.AddErr(fmt.Errorf("抖店运费模板、起送价、打包费、自动运力设置相关处理错误:%v", err))
		}
	}
	for _, v := range [][]string{
		[]string{
			"pricePercentagePack",
			model.ConfigTypePricePack,
		},
		[]string{
			"freightDeductionPack",
			model.ConfigTypeFreightPack,
		},
	} {
		if valid[v[0]] != nil {
			if value := utils.Interface2String(valid[v[0]]); value != "" {
				_, err2 := dao.QueryConfigs(db, value, v[1], "")
				if err = err2; err != nil {
					return 0, err
				}
			}
		}
	}

	if vendorID != model.VendorIDJX && vendorID != model.VendorIDYB {
		if vendorStoreID := utils.Interface2String(valid["vendorStoreID"]); vendorStoreID != "" {
			vendorStoreInfo, err2 := storeHandler.ReadStore(ctx, storeMap.VendorOrgCode, vendorStoreID, storeMap.VendorStoreName)
			if err = err2; err == nil {
				valid["deliveryType"] = vendorStoreInfo.DeliveryType
			}
			err = nil // todo 忽略读不到DeliveryType的错误
		}
	}

	// 增加门店地址修改同步
	storeDetail, err := dao.GetStoreBaseByVendorStoreID(storeMap.VendorStoreID, storeMap.VendorID)
	address := ""
	if err != nil || storeDetail == nil {
		return 0, fmt.Errorf("基础门店信息异常,请联系管理员")
	}
	if storeDetail != nil && payload["address"] != nil {
		// 添加同步
		valid["address"] = payload["address"].(string)
		syncStatus |= model.SyncFlagStoreName
		address = payload["address"].(string)
		// 这个字段暂存美团门店地址
		if storeMap.VendorID == model.VendorIDMTWM {
			storeMap.YbStorePrefix = address
			dao.UpdateEntity(db, storeMap, "YbStorePrefix")
		}
	}

	if err == nil {
		if len(valid) > 0 {
			txDB, _ := dao.Begin(db)
			defer func() {
				if r := recover(); r != nil {
					dao.Rollback(db, txDB)
					panic(r)
				}
			}()

			// 这里面包含address时会报错,所以删除
			delete(valid, "address")
			// storeMap 不包含address参数
			if isStoreMapNeedSync(vendorID, valid) { // 对于store vendor map，只有Status改变才需要同步到厂商
				num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, storeMap, valid, userName, map[string]interface{}{
					model.FieldStoreID:  storeID,
					model.FieldVendorID: vendorID,
					//"VendorOrgCode":     vendorOrgCode,
				}, model.FieldSyncStatus, syncStatus)
			} else {
				num, err = dao.UpdateEntityLogically(db, storeMap, valid, userName, map[string]interface{}{
					model.FieldStoreID:  storeID,
					model.FieldVendorID: vendorID,
					//"VendorOrgCode":     vendorOrgCode,
				})
			}

			if err != nil {
				dao.Rollback(db, txDB)
				return 0, err
			}
			isSyncStoreMapSku := false
			if num > 0 {
				if globals.IsAddEvent {
					mapBefore := refutil.FindMapAndStructMixed(valid, beforeStoreMap)
					err = AddEventDetail(db, ctx, model.OperateUpdate, vendorID, model.ThingTypeStore, storeID, BuildDiffData(mapBefore), BuildDiffData(valid))
				}
				if vendorID != model.VendorIDJX {
					if valid["pricePercentage"] != nil || valid["pricePercentagePack"] != nil {
						storeSkuBind := &model.StoreSkuBind{}
						if num, err = dao.UpdateEntityLogicallyAndUpdateSyncStatus(db, storeSkuBind, nil, userName, map[string]interface{}{
							model.FieldStoreID: storeID,
						}, dao.GetSyncStatusStructField(model.VendorNames[vendorID]), model.SyncFlagPriceMask); err != nil {
							dao.Rollback(db, txDB)
							return 0, err
						}
						isSyncStoreMapSku = true
					}
				} else {
					if valid["pricePercentage"] != nil || valid["pricePercentagePack"] != nil {
						ReCalculateJxPriceLight(db, ctx, storeID)
					}
				}
			}
			dao.Commit(db, txDB)
			if address != "" {
				valid["address"] = address
			}

			if isUpdateStoreNeedSync(valid) { // 同步修改门店已经配送门店
				_, err = CurVendorSync.SyncStore(ctx, db, vendorID, storeID, false, userName) // address原来的userName 因为storeMap无法保存address,所以美团暂时使用该参数代替
				//updateCourierStores(ctx, storeID)
			}

			// 更新商品
			if isSyncStoreMapSku {
				singleStoreHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreStoreSkuHandler)
				task := tasksch.NewParallelTask("修改门店调价包的时候,同时同步门店商品", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(true), ctx,
					func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
						step := batchItemList[0].(int)
						switch step {
						case 1:
							if singleStoreHandler != nil {
								// 直接同步商品
								err = syncStoreSkuNew(ctx, task, 0, true, vendorID, storeID, "", nil, nil, nil, false, true)
							}
						}
						return retVal, err
					}, []int{1})
				tasksch.HandleTask(task, nil, true).Run()
				_, err = task.GetResult(0)
				return 0, err
			}
		}
	}
	errList.AddErr(err)
	endErr := errList.GetErrListAsOne()
	return num, endErr
}

func DeleteStore(ctx *jxcontext.Context, storeID int) (num int64, err error) {
	db := dao.GetDB()

	store := &model.Store{}
	store.ID = storeID
	if err = dao.GetEntity(db, store); err != nil {
		return 0, err
	}
	if store.Status == model.StoreStatusOpened {
		return 0, fmt.Errorf("删除京西门店前必须将所有门店解绑且门店处于关店状态")
	}

	sql := `
	SELECT (SELECT COUNT(*) FROM store_map t1 WHERE t1.store_id = ? AND t1.deleted_at = ?)
	+ (SELECT COUNT(*) FROM store_courier_map t1 WHERE t1.store_id = ? AND t1.deleted_at = ?) ct
	`
	ct := 0
	if err = dao.GetRow(db, &ct, sql, storeID, utils.DefaultTimeValue, storeID, utils.DefaultTimeValue); err != nil {
		return 0, err
	}
	if ct > 0 {
		return 0, fmt.Errorf("删除京西门店前必须将所有门店解绑且门店处于关店状态")
	}

	txDB, _ := dao.Begin(db)
	defer dao.Rollback(db, txDB)
	now := time.Now()

	for _, tableName := range []string{"store_sku_bind", "store_sku_category_map", "store_op_request"} {
		sql = fmt.Sprintf(`
		UPDATE %s t1
		SET t1.deleted_at = ?,
		t1.updated_at = ?,
		t1.last_operator = ?
		WHERE t1.store_id = ? AND t1.deleted_at = ?
		`, tableName)
		if _, err = dao.ExecuteSQL(db, sql, now, now, ctx.GetUserName(), storeID, utils.DefaultTimeValue); err != nil {
			return 0, err
		}
	}

	if num, err = dao.DeleteEntityLogically(db, store, nil, ctx.GetUserName(), nil); err != nil {
		return 0, err
	}
	if globals.IsAddEvent {
		err = AddEventDetail(db, ctx, model.OperateDelete, storeID, model.ThingTypeStore, storeID, "", "")
	}
	DeleteStoreCategroies(ctx, db, storeID)
	dao.Commit(db, txDB)
	return num, err
	// return 0, errors.New("暂不支持删除京西门店")
}

// 状态是未解决，且初始是2星以下
func TmpGetJxBadCommentsNo(ctx *jxcontext.Context, storeID int) (count int, err error) {
	db := dao.GetDB()
	var ctInfo struct {
		Ct int
	}
	if err = dao.GetRow(db, &ctInfo, `
		SELECT COUNT(*) ct
		FROM jx_bad_comments
		WHERE status = ? AND jxstoreid = ? AND score <= ?
		`, COMMENT_NOT_RESOLVED, utils.Int2Str(storeID), JX_BAD_COMMENTS_MAX_LEVEL); err == nil {
		count = ctInfo.Ct
	}
	return count, err
}

func TmpGetJxBadCommentsByStoreId(ctx *jxcontext.Context, keyword string, storeIDs []int, offset, pageSize, commentType int, fromTime, toTime time.Time, orderFlag, operatorPhone string) (retVal map[string]interface{}, err error) {
	db := dao.GetDB()
	//权限
	//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
	//	}
	//}
	//sql := `
	//SELECT SQL_CALC_FOUND_ROWS
	//t1.*, t2.name store_name, t3.name city_name,
	//t4.vendor_order_id2,
	//CASE t1.order_flag
	//	WHEN 0 THEN t2.operator_phone
	//	WHEN 1 THEN t2.operator_phone2
	//	WHEN 2 THEN t2.operator_phone3
	//	ELSE t2.market_man_phone
	//END as operator_phone,
	//t5.name as user_name
	//FROM jx_bad_comments t1
	//LEFT JOIN store t2 ON t2.id = t1.jxstoreid
	//LEFT JOIN place t3 ON t3.code = t2.city_code
	//LEFT JOIN goods_order t4 ON t4.vendor_order_id = t1.order_id AND t4.vendor_id = t1.order_flag
	//INNER JOIN user t5 ON t5.mobile = operator_phone
	//WHERE 1 = 1
	//`
	sql := `
	SELECT SQL_CALC_FOUND_ROWS
	t1.*, t2.name store_name, t3.name city_name
	FROM jx_bad_comments t1
	LEFT JOIN store t2 ON t2.id = t1.jxstoreid
	LEFT JOIN place t3 ON t3.code = t2.city_code
	WHERE 1 = 1
	`
	sqlParams := []interface{}{}
	if keyword != "" {
		keywordLike := "%" + keyword + "%"
		sql += `
		AND (t1.order_id LIKE ? OR t1.jxstoreid LIKE ? OR t1.userphone LIKE ? OR t1.scorecontent LIKE ? OR t1.vendertags LIKE ? OR t1.updated_scorecontent LIKE ?
		OR t1.updated_vendertags LIKE ? OR t2.name LIKE ?  OR t1.vendor_order_id LIKE ?)`
		sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike)
	}
	if len(storeIDs) > 0 {
		sql += " AND t1.jxstoreid IN (" + dao.GenQuestionMarks(len(storeIDs)) + ")"
		sqlParams = append(sqlParams, storeIDs)
	}
	if commentType != GET_ALL_COMMENTS_TYPE {
		sql += " AND t1.status = ?"
		if commentType == GET_BAD_COMMENTS_TYPE {
			sql += " AND t1.score <= ?"
			sqlParams = append(sqlParams, COMMENT_NOT_RESOLVED, JX_BAD_COMMENTS_MAX_LEVEL)
		} else {
			sqlParams = append(sqlParams, COMMENT_RESOLVED)
		}
	}
	if !utils.IsTimeZero(fromTime) {
		sql += " AND t1.createtime >= ?"
		sqlParams = append(sqlParams, fromTime)
	}
	if !utils.IsTimeZero(toTime) {
		sql += " AND t1.createtime < ?"
		sqlParams = append(sqlParams, toTime)
	}

	if orderFlag != "" {
		sql += " AND t1.order_flag = ?"
		sqlParams = append(sqlParams, orderFlag)
	}
	if operatorPhone != "" {
		sql += " AND (t2.market_man_phone = ? OR t2.operator_phone = ? OR t2.operator_phone2 = ? OR t2.operator_phone3 = ?)"
		sqlParams = append(sqlParams, operatorPhone, operatorPhone, operatorPhone, operatorPhone)
	}
	sql += " ORDER BY t1.createtime DESC"
	pageSize = jxutils.FormalizePageSize(pageSize)
	offset = jxutils.FormalizePageOffset(offset)
	sql += " LIMIT ? OFFSET ?"
	sqlParams = append(sqlParams, pageSize, offset)
	var commentList []*JxBadCommentsExt
	txDB, _ := dao.Begin(db)
	defer func() {
		dao.Rollback(db, txDB)
	}()

	if err = dao.GetRowsTx(txDB, &commentList, sql, sqlParams...); err == nil {
		retVal = map[string]interface{}{
			"total": dao.GetLastTotalRowCount2(db, txDB),
			"list":  commentList,
		}
		dao.Commit(db, txDB)
	}
	return retVal, err
}

type GetStoreCourierMapsResult struct {
	storeCourierMaps *model.StoreCourierMap
	CourierAddress   string `json:"courierAddress"`
	CourierPhone     string `json:"courierPhone"`
}

func GetStoreCourierMaps(ctx *jxcontext.Context, db *dao.DaoDB, storeID int, vendorID int) (storeCourierMaps []*model.StoreCourierMap, err error) {
	cond := map[string]interface{}{
		model.FieldStoreID: storeID,
	}
	if vendorID != -1 {
		cond[model.FieldVendorID] = vendorID
	}
	err = dao.GetEntitiesByKV(db, &storeCourierMaps, cond, false)
	if len(storeCourierMaps) != 0 {
		//同步美团配送与否状态及美团门店是否存在
		for _, v := range storeCourierMaps {
			if v.VendorID != model.VendorIDMTPS {
				continue
			} else {
				SetMTPSStatus(jxcontext.AdminCtx, v.StoreID, v.Status)
				break
			}
		}
	}
	handler := partner.GetDeliveryPlatformFromVendorID(vendorID).Handler
	storeDetail, _ := handler.GetStore(ctx, storeID, utils.Int2Str(storeID))
	for _, v := range storeCourierMaps {
		if v != nil {
			if v.VendorStoreName == "" {
				if storeDetail != nil {
					v.VendorStoreName = storeDetail.Name
				}
			}
		}
	}
	return storeCourierMaps, err
}

func AddStoreCourierMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, storeCourierMap *model.StoreCourierMap) (outStoreCourierMap *model.StoreCourierMap, err error) {
	return addStoreCourierMap(ctx, db, storeID, vendorID, storeCourierMap, true)
}

func addStoreCourierMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, storeCourierMap *model.StoreCourierMap, isNeedUpdateRemote bool) (outStoreCourierMap *model.StoreCourierMap, err error) {
	storeCourierMap.StoreID = storeID
	storeCourierMap.VendorID = vendorID
	storeCourierMap.VendorStatus = storeCourierMap.Status

	if handler := partner.GetDeliveryPlatformFromVendorID(vendorID); handler != nil {
		if db == nil {
			db = dao.GetDB()
		}
		if isNeedUpdateRemote {
			storeDetail, err2 := dao.GetStoreDetail2(db, storeID, "", vendorID)
			if err = err2; err != nil {
				return nil, err
			}
			if storeDetail.VendorStoreID != "" {
				return nil, fmt.Errorf("门店已经绑定了%s, ID:%s, 如需重新绑定, 请先解绑", model.VendorChineseNames[vendorID], storeDetail.VendorStoreID)
			}
			storeDetail.VendorID = vendorID
			storeDetail.VendorStoreID = storeCourierMap.VendorStoreID
			if err = updateCourierStore(ctx, storeDetail); err != nil {
				if vendorID != model.VendorIDMTPS {
					return nil, err
				}
				// 如果是美团配送，强制忽略更新错
				globals.SugarLogger.Infof("addStoreCourierMap storeID:%d, vendorID:%d failed with err:%v", storeID, vendorID, err)
				err = nil
			}
		}
		dao.WrapAddIDCULDEntity(storeCourierMap, ctx.GetUserName())
		if err = dao.CreateEntity(db, storeCourierMap); err != nil {
			return nil, err
		}
		if globals.IsAddEvent {
			err = AddEventDetail(db, ctx, model.OperateAdd, vendorID, model.ThingTypeStore, storeID, "", `{"VendorID":`+utils.Int2Str(vendorID)+`}`)
		}
		outStoreCourierMap = storeCourierMap
	} else {
		err = ErrCanNotFindVendor
	}
	//同步美团配送与否状态及美团门店是否存在
	if outStoreCourierMap.VendorID == model.VendorIDMTPS {
		//SetMTPSStatus(jxcontext.AdminCtx, outStoreCourierMap.StoreID, outStoreCourierMap.Status)
		SetMTPSStatus2(outStoreCourierMap.StoreID)
	}
	return outStoreCourierMap, err
}

func DeleteStoreCourierMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, userName string) (num int64, err error) {
	if db == nil {
		db = dao.GetDB()
	}
	storeCourierMap := &model.StoreCourierMap{}
	num, err = dao.DeleteEntityLogically(db, storeCourierMap, map[string]interface{}{
		model.FieldStatus:       model.StoreStatusDisabled,
		model.FieldVendorStatus: model.StoreStatusDisabled,
	}, userName, map[string]interface{}{
		model.FieldStoreID:  storeID,
		model.FieldVendorID: vendorID,
	})
	if globals.IsAddEvent {
		err = AddEventDetail(db, ctx, model.OperateDelete, vendorID, model.ThingTypeStore, storeID, "", `{"VendorID":`+utils.Int2Str(vendorID)+`}`)
	}
	return num, err
}

func UpdateStoreCourierMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, payload map[string]interface{}, userName string) (num int64, err error) {
	if db == nil {
		db = dao.GetDB()
	}
	storeCourier := &model.StoreCourierMap{
		StoreID:  storeID,
		VendorID: vendorID,
	}
	storeCourier.DeletedAt = utils.DefaultTimeValue
	if err = dao.GetEntity(db, storeCourier, model.FieldStoreID, model.FieldVendorID, model.FieldDeletedAt); err != nil {
		return 0, err
	}
	var beforeStoreCourier = *storeCourier
	delete(payload, "auditStatus") // 不允许直接修改auditStatus的值
	valid := dao.NormalMakeMapByStructObject(payload, storeCourier, userName)
	if len(valid) > 0 {
		if storeCourier.AuditStatus != model.StoreAuditStatusOnline {
			if status := utils.Interface2Int64WithDefault(valid["status"], 0); status == model.StoreStatusOpened {
				// 没有通过审核的禁止改状态为正常
				//return 0, fmt.Errorf("此快递门店还没有通过审核，不启用")
			}
		}
		num, err = dao.UpdateEntityLogically(db, storeCourier, valid, userName, nil)
		if globals.IsAddEvent {
			mapBefore := refutil.FindMapAndStructMixed(valid, beforeStoreCourier)
			err = AddEventDetail(db, ctx, model.OperateUpdate, vendorID, model.ThingTypeStore, storeID, BuildDiffData(mapBefore), BuildDiffData(valid))
		}
	}
	return num, err
}

func updateCourierStore(ctx *jxcontext.Context, storeDetail *dao.StoreDetail2) (err error) {
	if handlerInfo := partner.GetDeliveryPlatformFromVendorID(storeDetail.VendorID); handlerInfo != nil && handlerInfo.Use4CreateWaybill {
		if updateHandler, ok := handlerInfo.Handler.(partner.IDeliveryUpdateStoreHandler); ok {
			err = updateHandler.UpdateStore(ctx, formalizeStore4Courier(storeDetail))
		}
	} else {
		err = fmt.Errorf("配送平台:%s不被支持", model.VendorChineseNames[storeDetail.VendorID])
	}
	return err
}

// 更新门店信息
//VendorIDDada          = 101 // 达达配送
//VendorIDMTPS          = 102 // 美团配送
//VendorIDFengNiao      = 103 // 蜂鸟配送
func updateCourierStores(ctx *jxcontext.Context, storeID int) (err error) {
	db := dao.GetDB()
	errList := errlist.New()
	for k, v := range partner.DeliveryPlatformHandlers {
		if v.Use4CreateWaybill {
			if _, ok := v.Handler.(partner.IDeliveryUpdateStoreHandler); ok {
				storeDetail, err2 := dao.GetStoreDetail2(db, storeID, "", k)
				if err = err2; err2 == nil {
					if storeDetail.VendorStoreID != "" && storeDetail.AuditStatus == model.StoreAuditStatusOnline {
						// 同步更新门店信息
						err = updateCourierStore(ctx, storeDetail)
						if err == nil {
							//美团配送修改门店信息后要待审核
							if k == model.VendorIDMTPS {
								if couriers, err := dao.GetStoreCourierList(db, []int{storeID}, []int{model.VendorIDMTPS}, model.StoreStatusAll, model.StoreAuditStatusAll); len(couriers) > 0 && err == nil {
									couriers[0].AuditStatus = model.StoreAuditStatusUpdated
									dao.UpdateEntity(db, couriers[0], "AuditStatus")
								}
							}
						}
					}
				}
				errList.AddErr(err)
			}
		}
	}
	return errList.GetErrListAsOne()
}

func UpdateStoreName() error {
	var (
		db      = dao.GetDB()
		context = jxcontext.AdminCtx
	)

	sql := ` SELECT * FROM store_courier_map WHERE vendor_id IN (?,?,?,?) AND deleted_at = ?`
	courierList := make([]*model.StoreCourierMap, 0, 0)
	if err := dao.GetRows(db, &courierList, sql, []interface{}{model.VendorIDMTPS, model.VendorIDFengNiao, model.VendorIDDada, model.VendorIDUUPT, utils.DefaultTimeValue}...); err != nil {
		return err
	}

	for i := 0; i < len(courierList); i++ {
		storeDetail, _ := dao.GetStoreDetail2(db, courierList[i].StoreID, courierList[i].VendorStoreID, courierList[i].VendorID)
		formalizeStore4Courier(storeDetail)
		if handlerInfo := partner.GetDeliveryPlatformFromVendorID(storeDetail.VendorID); handlerInfo != nil {
			if updateHandler, _ := handlerInfo.Handler.(partner.IDeliveryUpdateStoreHandler); updateHandler != nil {
				if err := updateHandler.UpdateStore(context, storeDetail); err != nil {
					globals.SugarLogger.Debugf("err := %v,storeID : %s,storeName :%s , vendorId : %d", err, storeDetail.VendorStoreID, storeDetail.Name, storeDetail.VendorID)
				}
			}
		}
	}
	return nil
}

// ====================================第三方店铺创建=============================
func updateOrCreateCourierStore(ctx *jxcontext.Context, storeDetail *dao.StoreDetail2) (isCreated bool, err error) {

	if handlerInfo := partner.GetDeliveryPlatformFromVendorID(storeDetail.VendorID); handlerInfo != nil && handlerInfo.Use4CreateWaybill {
		if storeDetail.DistrictName == "" {
			return false, fmt.Errorf("门店的区码有问题，请检查")
		}
		if storeDetail.CityName == "" {
			return false, fmt.Errorf("门店的城市码有问题，请检查")
		}
		formalizeStore4Courier(storeDetail)
		needUpdate := false
		remoteStoreDetail, err2 := handlerInfo.Handler.GetStore(ctx, 0, storeDetail.VendorStoreID)
		if err = err2; err != nil {
			if handlerInfo.Handler.IsErrStoreNotExist(err) {
				// 本地门店同步第三方平台
				storeDetail.VendorStoreID, storeDetail.AuditStatus, err = handlerInfo.Handler.CreateStore(ctx, storeDetail)
				if err == nil {
					isCreated = true
				} else if handlerInfo.Handler.IsErrStoreExist(err) {
					storeDetail.AuditStatus = model.StoreAuditStatusCreated
					err = nil
				}
			}
		} else {
			storeDetail.CourierStatus = remoteStoreDetail.CourierStatus
			storeDetail.AuditStatus = remoteStoreDetail.AuditStatus
			if storeDetail.VendorID == model.VendorIDFengNiao {
				storeDetail.VendorStoreID = remoteStoreDetail.VendorStoreID
			}
			needUpdate = true
		}
		if err == nil && needUpdate {
			if updateHandler, _ := handlerInfo.Handler.(partner.IDeliveryUpdateStoreHandler); updateHandler != nil {
				err = updateHandler.UpdateStore(ctx, storeDetail)
			} else {
				// err = fmt.Errorf("快递平台%s不支持更新门店信息，请手动处理", model.VendorChineseNames[storeDetail.VendorID])
			}
		}
		if err != nil {
			err = fmt.Errorf("门店ID:%d,门店名:%s,错误描述:%s", storeDetail.Store.ID, storeDetail.Name, err.Error())
		}
	}
	return isCreated, err
}

func UpdateOrCreateCourierStores(ctx *jxcontext.Context, storeID int, isForceUpdate, isAsync, isContinueWhenError bool) (hint string, err error) {
	var storeIDs []int
	if storeID != 0 {
		storeIDs = []int{storeID}
	} else {
		storesInfo, err2 := GetStores(ctx, "", map[string]interface{}{}, 0, -1, utils.DefaultTimeValue, utils.DefaultTimeValue, 0, 0)
		if err = err2; err != nil {
			return "", err
		}
		for _, v := range storesInfo.Stores {
			storeIDs = append(storeIDs, v.ID)
		}
	}

	// 定时任务更新或者创建时修改东西
	task := tasksch.NewParallelTask("UpdateOrCreateCourierStores", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			var resultList []interface{}
			storeID := batchItemList[0].(int)
			errList := errlist.New()
			db := dao.GetDB()
			for vendorID, v := range partner.DeliveryPlatformHandlers {
				if v.Use4CreateWaybill {
					if _, ok := v.Handler.(partner.IDeliveryUpdateStoreHandler); ok {
						storeDetail, err2 := dao.GetStoreDetail2(db, storeID, "", vendorID)
						if err = err2; err2 == nil {
							isNeedAdd := storeDetail.VendorStoreID == ""
							if isForceUpdate || isNeedAdd {
								if isNeedAdd {
									storeDetail.VendorID = vendorID
									storeDetail.VendorStoreID = utils.Int2Str(storeDetail.ID)
								}
								if _, err = updateOrCreateCourierStore(ctx, storeDetail); err == nil && isNeedAdd {
									storeCourier := &model.StoreCourierMap{
										VendorStoreID:   storeDetail.VendorStoreID,
										Status:          model.StoreStatusOpened,
										AuditStatus:     storeDetail.AuditStatus,
										VendorStoreName: storeDetail.Name,
									}
									if storeDetail.AuditStatus != model.StoreAuditStatusOnline {
										storeCourier.Status = model.StoreStatusDisabled
									}
									if _, err = addStoreCourierMap(ctx, db, storeDetail.ID, storeDetail.VendorID, storeCourier, false); err == nil {
										resultList = append(resultList, 1)
									}
								}
							}
						}
						errList.AddErr(err)
					}
				}
			}
			return resultList, errList.GetErrListAsOne()
		}, storeIDs)
	tasksch.HandleTask(task, nil, len(storeIDs) > 1).Run()
	if !isAsync {
		resultList, err2 := task.GetResult(0)
		if err = err2; err == nil {
			hint = utils.Int2Str(len(resultList))
		}
	} else {
		hint = task.ID
	}
	return hint, err
}

func formalizeStore4Courier(storeDetail *dao.StoreDetail2) *dao.StoreDetail2 {
	// 获取品牌名称
	brandInfo, err := dao.GetBrands(dao.GetDB(), "", storeDetail.BrandID, "", false, "")
	if err != nil {
		return nil
	}

	storeDetail.Name = fmt.Sprintf("%s-%s-%s", strings.ReplaceAll(brandInfo[0].Name, " ", ""), strings.ReplaceAll(storeDetail.CityName, " ", ""), strings.ReplaceAll(storeDetail.Name, " ", ""))
	if storeDetail.PayeeName == "" {
		storeDetail.PayeeName = "店主"
	}
	return storeDetail
}

type EbaiStoreHealthy struct {
	RealShopID        int    `json:"门店ID"`
	MerchantID        string `json:"平台门店ID"`
	MerchantName      string `json:"平台门店名"`
	SkuNum            string `json:"日均在架且有库存的商品数"`
	IsSku             string `json:"商品数是否达标"`
	HasPhotoRate      string `json:"有图率"`
	IsYoutu           string `json:"有图率是否达标"`
	Hours             string `json:"日均营业时长"`
	IsYinye           string `json:"营业时长是否达标"`
	UnvalidOrderNum   string `json:"商家原因取消订单数"`
	IsJiedan          string `json:"取消数是否达标"`
	BadOrderRate      string `json:"差评订单率"`
	IsCp              string `json:"差评率是否达标"`
	RestaurantSubsidy string `json:"商家补贴金额(元)"`
	IsButie           string `json:"补贴是否达标"`
	IsHealthy         string `json:"商户是否健康"`
}

func ExportShopsHealthInfo(ctx *jxcontext.Context, vendorIDs, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
	db := dao.GetDB()
	vendorID := model.VendorIDEBAI
	storeMapList, err := dao.GetStoresMapList(db, []int{vendorID}, storeIDs, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "")
	if err != nil {
		return "", err
	}
	storeMap2 := make(map[string]*model.StoreMap)
	for _, v := range storeMapList {
		storeMap2[v.VendorStoreID] = v
	}
	if len(storeMapList) > 0 {
		var healthInfoList []interface{}
		var excelBin []byte
		var excelURL string
		task := tasksch.NewSeqTask(fmt.Sprintf("ExportShopHealthInfo[%s]", model.VendorChineseNames[vendorID]), ctx,
			func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
				switch step {
				case 0:
					subTask := tasksch.NewParallelTask(fmt.Sprintf("ExportShopHealthInfo2[%s]", model.VendorChineseNames[vendorID]), tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx,
						func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
							storeMap := batchItemList[0].(*model.StoreMap)
							healthInfo, err := ebai.CurPurchaseHandler.GetShopHealthInfo(storeMap.VendorStoreID)
							if err == nil {
								retVal = []map[string]interface{}{healthInfo}
							}
							return retVal, err
						}, storeMapList)
					tasksch.AddChild(task, subTask).Run()
					healthInfoList, err = subTask.GetResult(0)
					if isContinueWhenError && err != nil && len(healthInfoList) > 0 {
						err = nil
					}
				case 1:
					var healthInfoList2 []map[string]interface{}
					for _, v := range healthInfoList {
						mapInfo := v.(map[string]interface{})
						mapInfo["real_shop_id"] = storeMap2[utils.Interface2String(mapInfo["merchant_id"])].StoreID
						healthInfoList2 = append(healthInfoList2, mapInfo)
					}
					var healthyList []*EbaiStoreHealthy
					for _, v := range healthInfoList2 {
						healthy := &EbaiStoreHealthy{
							RealShopID:        v["real_shop_id"].(int),
							MerchantID:        v["merchant_id"].(string),
							MerchantName:      v["merchant_name"].(string),
							SkuNum:            v["sku_num"].(string),
							IsSku:             v["is_sku"].(string),
							HasPhotoRate:      v["has_photo_rate"].(string),
							IsYoutu:           v["is_youtu"].(string),
							Hours:             v["hours"].(string),
							IsYinye:           v["is_yinye"].(string),
							UnvalidOrderNum:   v["unvalid_order_num"].(string),
							IsJiedan:          v["is_jiedan"].(string),
							BadOrderRate:      v["bad_order_rate"].(string),
							IsCp:              v["is_cp"].(string),
							RestaurantSubsidy: v["restaurant_subsidy"].(string),
							IsButie:           v["is_butie"].(string),
							IsHealthy:         v["is_healthy"].(string),
						}
						healthyList = append(healthyList, healthy)
					}
					excelConf := &excel.Obj2ExcelSheetConfig{
						Title: "饿百门店情况导出",
						Data:  healthyList,
						CaptionList: []string{
							"门店ID",
							"平台门店ID",
							"平台门店名",
							"日均在架且有库存的商品数",
							"商品数是否达标",
							"有图率",
							"有图率是否达标",
							"日均营业时长",
							"营业时长是否达标",
							"商家原因取消订单数",
							"取消数是否达标",
							"差评订单率",
							"差评率是否达标",
							"商家补贴金额(元)",
							"补贴是否达标",
							"商户是否健康",
						},
					}
					excelBin = excel.Obj2Excel([]*excel.Obj2ExcelSheetConfig{excelConf})
				case 2:
					keyPart := []string{
						ctx.GetUserName(),
						"饿百门店情况",
					}
					keyPart = append(keyPart, time.Now().Format("20060102T150405")+".xlsx")
					key := "export/" + strings.Join(keyPart, "_")
					excelURL, err = jxutils.UploadExportContent(excelBin, key)
					if err == nil {
						task.SetNoticeMsg(excelURL)
					}
					globals.SugarLogger.Debugf("导出饿百门店情况excelURL:%s, err:%v", excelURL, err)
				}
				return nil, err
			}, 3)
		tasksch.HandleTask(task, nil, true).Run()
		if !isAsync {
			_, err = task.GetResult(0)
			if err == nil {
				hint = excelURL
			}
		} else {
			hint = task.GetID()
		}
	}
	return hint, err
}

func GetCorporationInfo(ctx *jxcontext.Context, licenceCode, licenceImg string) (corporationInfo *jdapi.GetOrderDetailByLicenseImg, err error) {
	// 门店ID随便一个合法的京东门店ID就可以
	jdImg, err := api.JdAPI.StoreUploadImgByURL(licenceImg)
	if err != nil {
		return nil, err
	}
	corporationInfo, err = api.JdAPI.GetCorporationInfo2(jdImg)
	// 下面的方法无法使用,找不到出处
	// corporationInfo, err = api.JdAPI.GetCorporationInfo("11734851", licenceCode)
	return corporationInfo, err
}

func GetStoresVendorSnapshot(ctx *jxcontext.Context, parentTask tasksch.ITask, vendorIDs, storeIDs []int) (vendorStoreSnapshotList []*model.VendorStoreSnapshot, err error) {
	db := dao.GetDB()
	storeMapList, err := dao.GetStoresMapList(db, vendorIDs, storeIDs, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	if err != nil {
		return nil, err
	}
	task := tasksch.NewParallelTask("GetStoresVendorSnapshot", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeMap := batchItemList[0].(*model.StoreMap)
			if model.IsVendorRemote(storeMap.VendorID) {
				if handler := partner.GetPurchasePlatformFromVendorID(storeMap.VendorID); handler != nil {
					store, err2 := handler.ReadStore(ctx, storeMap.VendorOrgCode, storeMap.VendorStoreID, storeMap.VendorStoreName)
					if err = err2; err == nil {
						retVal = []interface{}{&model.VendorStoreSnapshot{
							StoreID:       storeMap.StoreID,
							VendorID:      storeMap.VendorID,
							VendorStoreID: storeMap.VendorStoreID,

							Status:     store.Status,
							OpenTime1:  store.OpenTime1,
							CloseTime1: store.CloseTime1,
							OpenTime2:  store.OpenTime2,
							CloseTime2: store.CloseTime2,

							DeliveryType: store.DeliveryType,
							StoreName:    store.OriginalName,
							IsAutoOrder:  store.IsAutoOrder,
						}}
					}
				}
			}
			return retVal, err
		}, storeMapList)
	tasksch.HandleTask(task, parentTask, true).Run()
	resultList, err := task.GetResult(0)
	if len(resultList) > 0 {
		for _, v := range resultList {
			dao.WrapAddIDCULDEntity(v, ctx.GetUserName())
			vendorStoreSnapshotList = append(vendorStoreSnapshotList, v.(*model.VendorStoreSnapshot))
		}
	}
	return vendorStoreSnapshotList, err
}

func getCurrentSnapshotAt(now time.Time) (snapshotAt time.Time) {
	return jxutils.GetLastTimeFromList(now, WatchVendorStoreTimeList)
}

func updateVendorStoreStatusBySnapshot(db *dao.DaoDB, curSnapshotList []*model.VendorStoreSnapshot) (err error) {
	storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	if err != nil {
		return err
	}
	snapshotMap := make(map[int64]*model.VendorStoreSnapshot)
	for _, v := range curSnapshotList {
		snapshotMap[jxutils.Combine2Int(v.StoreID, v.VendorID)] = v
	}
	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 storeMapList {
		if snapshot := snapshotMap[jxutils.Combine2Int(v.StoreID, v.VendorID)]; snapshot != nil &&
			(v.Status != snapshot.Status || v.DeliveryType != snapshot.DeliveryType || v.StoreName != snapshot.StoreName) {
			v.Status = snapshot.Status
			v.DeliveryType = snapshot.DeliveryType
			v.StoreName = snapshot.StoreName
			if _, err = dao.UpdateEntity(db, v, model.FieldStatus, "DeliveryType", "StoreName"); err != nil {
				return err
			}
		}
	}
	dao.Commit(db, txDB)

	utils.CallFuncAsync(func() {
		for _, v := range storeMapList {
			if snapshot := snapshotMap[jxutils.Combine2Int(v.StoreID, v.VendorID)]; snapshot != nil {
				if snapshot.IsAutoOrder == 1 {
					if handler, ok := partner.GetPurchasePlatformFromVendorID(snapshot.VendorID).(partner.IStoreHandler); ok {
						handler.EnableAutoAcceptOrder(jxcontext.AdminCtx, v.VendorOrgCode, v.StoreID, v.VendorStoreID, false)
					}
				}
			}
		}
	})
	return err
}

func SaveStoresVendorSnapshot(db *dao.DaoDB, snapshotAt time.Time, curSnapshotList []*model.VendorStoreSnapshot) (err error) {
	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.DeleteVendorStoreSnapshot(db, snapshotAt.Add(-48*time.Hour)); err != nil {
		return err
	}
	for _, v := range curSnapshotList {
		v.SnapshotAt = snapshotAt

		dao.DeleteEntity(db, v, "VendorStoreID", "VendorID", "SnapshotAt")
		if err = dao.CreateEntity(db, v); err != nil {
			return err
		}
	}
	dao.Commit(db, txDB)
	return err
}

func isVendorStoresStatusNotOk(storeMapList []*model.VendorStoreSnapshot) bool {
	statusMap := make(map[int]int)
	maxStatus := model.StoreStatusClosed
	for _, v := range storeMapList {
		statusMap[v.Status] = 1
		if v.Status > maxStatus {
			maxStatus = v.Status
		}
	}
	return len(statusMap) > 1 && maxStatus == model.StoreStatusOpened
}

func getAllUsers4Store(ctx *jxcontext.Context, db *dao.DaoDB, store *model.Store) (userList []*model.User) {
	storeID := store.ID
	var userIDs []string
	userMap := make(map[string]int)

	// 门店老板
	if roleUserIDList, err := GetRoleUserList(ctx, autils.NewStoreBossRole(storeID)); err == nil && len(roleUserIDList) > 0 {
		userIDs = append(userIDs, roleUserIDList...)
	}

	// 全局相关角色（市场或运营）
	for _, v := range []string{store.MarketManRole, store.OperatorRole, store.OperatorRole2, store.OperatorRole3} {
		if v != "" {
			if roleUserIDList, err := GetRoleUserList(ctx, autils.NewRole(v, 0)); err == nil && len(roleUserIDList) > 0 {
				userIDs = append(userIDs, roleUserIDList...)
			}
		}
	}

	if len(userIDs) > 0 {
		userList, _, _ = dao.GetUsers(db, 0, "", userIDs, nil, nil, 0, -1)
		for _, v := range userList {
			userMap[v.GetID()] = 1
		}
	}

	// 直接电话信息相关人员
	for _, mobile := range []string{store.Tel1, store.Tel2, store.MarketManPhone, store.OperatorPhone, store.OperatorPhone2, store.OperatorPhone3} {
		if mobile != "" {
			if user, err2 := dao.GetUserByID(db, "mobile", mobile); err2 == nil {
				if userMap[user.GetID()] == 0 {
					userMap[user.GetID()] = 1
					userList = append(userList, user)
				}
			}
		}
	}
	return userList
}

type tStoreIDList struct {
	StoreIDList []int
	StoreMap    map[int]*model.Store
}

func (l *tStoreIDList) Len() int {
	return len(l.StoreIDList)
}

func (l *tStoreIDList) Less(i, j int) bool {
	storei := l.StoreMap[l.StoreIDList[i]]
	storej := l.StoreMap[l.StoreIDList[j]]
	if storei.CityCode == storej.CityCode {
		return storei.ID < storej.ID
	}
	return storei.CityCode < storej.CityCode
}

func (l *tStoreIDList) Swap(i, j int) {
	l.StoreIDList[i], l.StoreIDList[j] = l.StoreIDList[j], l.StoreIDList[i]
}

func SendAlarmVendorSnapshot(ctx *jxcontext.Context, parentTask tasksch.ITask, prevSnapshotList, curSnapshotList []*model.VendorStoreSnapshot) (err error) {
	if len(prevSnapshotList) == 0 {
		return nil
	}

	prevSnapshotMap := make(map[string]*model.VendorStoreSnapshot)
	prevSnapshotMap2 := make(map[int]map[int]*model.VendorStoreSnapshot)
	for _, v := range prevSnapshotList {
		prevSnapshotMap[v.GenMapKey()] = v
		if prevSnapshotMap2[v.StoreID] == nil {
			prevSnapshotMap2[v.StoreID] = make(map[int]*model.VendorStoreSnapshot)
		}
		prevSnapshotMap2[v.StoreID][v.VendorID] = v
	}
	curSnapshotMap := make(map[string]*model.VendorStoreSnapshot)
	curSnapshotMap2 := make(map[int]map[int]*model.VendorStoreSnapshot)
	curSnapshotGroupMap := make(map[int][]*model.VendorStoreSnapshot)

	alarmSnapshotMap := make(map[int][]*model.VendorStoreSnapshot)
	txtAlarmSnapshotMap := make(map[int][]*model.VendorStoreSnapshot)
	// 之前是开店当前是关店的，或营业时间缩短的
	for _, v := range curSnapshotList {
		curSnapshotGroupMap[v.StoreID] = append(curSnapshotGroupMap[v.StoreID], v)
		if curSnapshotMap2[v.StoreID] == nil {
			curSnapshotMap2[v.StoreID] = make(map[int]*model.VendorStoreSnapshot)
		}
		curSnapshotMap2[v.StoreID][v.VendorID] = v

		curSnapshotMap[v.GenMapKey()] = v
		prevSnapshot := prevSnapshotMap[v.GenMapKey()]
		if prevSnapshot != nil {
			if (prevSnapshot.Status == model.StoreStatusOpened && v.Status != model.StoreStatusOpened) ||
				v.CompareOperationTime(prevSnapshot) < 0 {
				alarmSnapshotMap[v.StoreID] = append(alarmSnapshotMap[v.StoreID], v)

				txtAlarmSnapshotMap[prevSnapshot.StoreID] = append(txtAlarmSnapshotMap[prevSnapshot.StoreID], prevSnapshot)
			}
		}
	}

	//当前门店，不同平台门店状态不一致的
	for storeID, list := range curSnapshotGroupMap {
		if isVendorStoresStatusNotOk(list) {
			alarmSnapshotMap[storeID] = list
		}
	}

	// 之前有店（且是开店状态），当前无店的
	for _, v := range prevSnapshotList {
		if v.Status == model.StoreStatusOpened && curSnapshotMap[v.GenMapKey()] == nil {
			alarmSnapshotMap[v.StoreID] = append(alarmSnapshotMap[v.StoreID], v)

			txtAlarmSnapshotMap[v.StoreID] = append(txtAlarmSnapshotMap[v.StoreID], v)
		}
	}

	db := dao.GetDB()
	storeDetailMap := make(map[int]*dao.StoreDetail)
	userMap2 := make(map[string]*model.User)
	userMap := make(map[string]map[int]int)
	userMapTxt := make(map[string][]*model.VendorStoreSnapshot)
	for storeID, list := range alarmSnapshotMap {
		storeDetail, _ := dao.GetStoreDetail(db, storeID, list[0].VendorID, "")
		if storeDetail != nil {
			storeDetailMap[storeID] = storeDetail
			userList := getAllUsers4Store(ctx, db, &storeDetail.Store)
			for _, user := range userList {
				userID := user.GetID()
				if userMap[userID] == nil {
					userMap[userID] = make(map[int]int)
					userMap2[userID] = user
				}
				userMap[userID][storeID] = 1
				if txtAlarmSnapshotMap[storeID] != nil {
					userMapTxt[userID] = append(userMapTxt[userID], txtAlarmSnapshotMap[storeID]...)
				}
			}
		}
	}
	var userList []*model.User
	for _, user := range userMap2 {
		userList = append(userList, user)
	}

	if len(userList) > 0 {
		allStores, err := dao.GetStoreList(db, nil, nil, nil, nil, nil, "")
		if err != nil {
			return err
		}
		allStoreMap := make(map[int]*model.Store)
		for _, v := range allStores {
			allStoreMap[v.ID] = v
		}

		const fixTitle = "门店状态变化"
		title := fmt.Sprintf("%s:%s-->%s", fixTitle, utils.Time2Str(prevSnapshotList[0].SnapshotAt), utils.Time2Str(curSnapshotList[0].SnapshotAt))
		task := tasksch.NewParallelTask("SendAlarmVendorSnapshot", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
			func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				user := batchItemList[0].(*model.User)
				var excelURL string
				if user.Type&model.UserTypeOperator != 0 {
					var dataList []map[string]interface{}
					captionList := []string{"京西门店ID", "门店名", "城市"}
					isFirstRow := true

					storeIDList := &tStoreIDList{
						StoreMap: allStoreMap,
					}
					for storeID := range userMap[user.GetID()] {
						storeIDList.StoreIDList = append(storeIDList.StoreIDList, storeID)
					}
					sort.Sort(storeIDList)
					for _, storeID := range storeIDList.StoreIDList {
						prevAlarmMap := prevSnapshotMap2[storeID]
						curAlarmMap := curSnapshotMap2[storeID]
						data := map[string]interface{}{
							"京西门店ID": storeID,
							"门店名":    storeDetailMap[storeID].Store.Name,
							"城市":     storeDetailMap[storeID].CityName,
						}
						for _, vendorID := range []int{model.VendorIDJD, model.VendorIDEBAI, model.VendorIDMTWM} {
							if isFirstRow {
								captionList = append(captionList, model.VendorChineseNames[vendorID]+"ID",
									model.VendorChineseNames[vendorID]+"之前状态", model.VendorChineseNames[vendorID]+"当前状态",
									model.VendorChineseNames[vendorID]+"之前营业时间", model.VendorChineseNames[vendorID]+"当前营业时间")
							}
							if prevAlarmMap != nil {
								data[model.VendorChineseNames[vendorID]+"当前状态"] = ""
								data[model.VendorChineseNames[vendorID]+"当前营业时间"] = ""
								if prevSnapshot := prevAlarmMap[vendorID]; prevSnapshot != nil {
									data[model.VendorChineseNames[vendorID]+"ID"] = prevSnapshot.VendorStoreID
									data[model.VendorChineseNames[vendorID]+"之前状态"] = model.StoreStatusName[prevSnapshot.Status]
									data[model.VendorChineseNames[vendorID]+"之前营业时间"] = jxutils.OperationTimeStr4VendorStore(prevSnapshot)
									if snapshot := curSnapshotMap[prevSnapshot.GenMapKey()]; snapshot != nil {
										data[model.VendorChineseNames[vendorID]+"当前状态"] = model.StoreStatusName[snapshot.Status]
										data[model.VendorChineseNames[vendorID]+"当前营业时间"] = jxutils.OperationTimeStr4VendorStore(snapshot)
									}
								} else {
									data[model.VendorChineseNames[vendorID]+"ID"] = ""
									data[model.VendorChineseNames[vendorID]+"之前状态"] = ""
									data[model.VendorChineseNames[vendorID]+"之前营业时间"] = ""
								}
							} else if curAlarmMap != nil {
								data[model.VendorChineseNames[vendorID]+"之前状态"] = ""
								data[model.VendorChineseNames[vendorID]+"之前营业时间"] = ""
								if curSnapshot := curAlarmMap[vendorID]; curSnapshot != nil {
									data[model.VendorChineseNames[vendorID]+"ID"] = curSnapshot.VendorStoreID
									data[model.VendorChineseNames[vendorID]+"当前状态"] = model.StoreStatusName[curSnapshot.Status]
									data[model.VendorChineseNames[vendorID]+"当前营业时间"] = jxutils.OperationTimeStr4VendorStore(curSnapshot)
								} else {
									data[model.VendorChineseNames[vendorID]+"ID"] = ""
									data[model.VendorChineseNames[vendorID]+"当前状态"] = ""
									data[model.VendorChineseNames[vendorID]+"当前营业时间"] = ""
								}
							}
						}
						dataList = append(dataList, data)

						isFirstRow = false
					}
					excelConf := &excel.Obj2ExcelSheetConfig{
						Title:       fixTitle,
						Data:        dataList,
						CaptionList: captionList,
					}
					excelBin := excel.Obj2Excel([]*excel.Obj2ExcelSheetConfig{excelConf})
					keyPart := []string{
						"store_status",
						user.GetMobile(),
						time.Now().Format("20060102T150405") + ".xlsx",
					}
					key := "export/" + strings.Join(keyPart, "_")
					excelURL, err = jxutils.UploadExportContent(excelBin, key)
					if err != nil {
						globals.SugarLogger.Warnf("SendAlarmVendorSnapshot, %s upload %s failed with error:%v", user.GetName(), key, err)
					}
					var txtAlarm []string

					for _, v := range userMapTxt[user.GetID()] {
						curSnapshot := curSnapshotMap[v.GenMapKey()]
						storeDetail := storeDetailMap[v.StoreID]
						curStoreStatus := "无店"
						curOpTimeStr := "无店"
						if curSnapshot != nil {
							curStoreStatus = model.StoreStatusName[curSnapshot.Status]
							curOpTimeStr = jxutils.OperationTimeStr4VendorStore(curSnapshot)
						}
						txtAlarm = append(txtAlarm, fmt.Sprintf(`## 门店: %s(%d)  
- 城市: %s  
- 平台: %s  
- 平台ID: %s  
- 之前状态: %s  
- 当前状态: %s  
- 之前营业时间: %s  
- 当前营业时间: %s  
`, storeDetail.Store.Name, v.StoreID, storeDetail.CityName, model.VendorChineseNames[v.VendorID], v.VendorStoreID, model.StoreStatusName[v.Status], curStoreStatus, jxutils.OperationTimeStr4VendorStore(v), curOpTimeStr))
					}
					alarmTextStr := "# " + title + "  \n" + fmt.Sprintf("[详情点我](%s/billshow/?normal=true&path=%s)  \n", globals.BackstageHost, excelURL) + strings.Join(txtAlarm, "  \n")
					sendStoreStatusInfo2Mobile(user, dingdingapi.MsgTypeMarkdown, title, alarmTextStr)
				} else if len(userMapTxt[user.GetID()]) > 0 {
					var txtAlarm []string
					for _, v := range userMapTxt[user.GetID()] {
						curSnapshot := curSnapshotMap[v.GenMapKey()]
						// storeDetail := storeDetailMap[v.StoreID]
						curStoreStatus := "下线"
						if curSnapshot != nil {
							curStoreStatus = model.StoreStatusName[curSnapshot.Status]
						}
						txtAlarm = append(txtAlarm, fmt.Sprintf("您的门店:%s,平台:%s,%s了", v.StoreName, model.VendorChineseNames[v.VendorID], curStoreStatus))
					}
					alarmTextStr := strings.Join(txtAlarm, ",\n")
					sendStoreStatusInfo2Mobile(user, dingdingapi.MsgTyeText, title, alarmTextStr)
				}
				return nil, nil
			}, userList)
		tasksch.HandleTask(task, parentTask, true).Run()
		_, err = task.GetResult(0)
	}
	return err
}

func sendStoreStatusInfo2Mobile(user *model.User, msgType, title, txtAlarm string) {
	msg.SendUserMessage(msgType, user, title, txtAlarm)
}

func SaveAndSendAlarmVendorSnapshot(ctx *jxcontext.Context, vendorIDs, storeIDs []int, isAsync bool) (err error) {
	curSnapshotAt := getCurrentSnapshotAt(time.Now())
	prevSnapshotAt := getCurrentSnapshotAt(curSnapshotAt.Add(-1 * time.Second))
	db := dao.GetDB()
	var curSnapshotList, prevSnapshotList []*model.VendorStoreSnapshot
	task := tasksch.NewParallelTask("SaveAndSendAlarmVendorSnapshot", 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:
				curSnapshotList, err = GetStoresVendorSnapshot(ctx, task, vendorIDs, storeIDs)
				if len(curSnapshotList) == 0 {
					task.Cancel()
				} else {
					updateVendorStoreStatusBySnapshot(db, curSnapshotList)
				}
			case 1:
				err = SaveStoresVendorSnapshot(db, curSnapshotAt, curSnapshotList)
			case 2:
				prevSnapshotList, err = dao.GetVendorStoreSnapshot(db, prevSnapshotAt)
			case 3:
				err = SendAlarmVendorSnapshot(ctx, task, prevSnapshotList, curSnapshotList)
			}
			return nil, err
		}, []int{0, 1, 2, 3})
	tasksch.ManageTask(task).Run()
	if !isAsync {
		_, err = task.GetResult(0)
	}
	return err
}

func SyncStoresCourierInfo(ctx *jxcontext.Context, storeIDs []int, isAsync, isContinueWhenError bool) (hint string, err error) {
	db := dao.GetDB()
	storeList2, err := dao.GetStoreList(db, storeIDs, nil, nil, nil, nil, "")
	var storeList []*model.Store
	for _, v := range storeList2 {
		if v.Status != model.StoreStatusDisabled {
			storeList = append(storeList, v)
		}
	}
	if err == nil && len(storeList) > 0 {
		task := tasksch.NewParallelTask(fmt.Sprintf("同步门店快递信息1:%v", storeIDs),
			tasksch.NewParallelConfig().SetParallelCount(2).SetIsContinueWhenError(isContinueWhenError), ctx,
			func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				store := batchItemList[0].(*model.Store)
				subTask := tasksch.NewParallelTask(fmt.Sprintf("同步门店快递信息2:%d", store.ID),
					tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
					func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
						vendorID := batchItemList[0].(int)
						storeDetail2, err := dao.GetStoreDetail2(db, store.ID, "", vendorID)
						if err == nil && storeDetail2.VendorStoreID != "" /*&& storeDetail2.CourierStatus != model.StoreStatusDisabled*/ {
							if handler := partner.GetDeliveryPlatformFromVendorID(vendorID); handler != nil {
								if updateHandler, ok := handler.Handler.(partner.IDeliveryUpdateStoreHandler); ok {
									updateHandler.UpdateStore(ctx, storeDetail2)
								}
								storeCourier, err2 := handler.Handler.GetStore(ctx, store.ID, storeDetail2.VendorStoreID)
								if err = err2; err == nil {
									if storeDetail2.AuditStatus == model.StoreAuditStatusCreated { // 如果已经通过审核，更新本地状态
										partner.CurStoreManager.OnCourierStoreStatusChanged(ctx, storeCourier.VendorStoreID, vendorID, storeCourier.AuditStatus, storeCourier.Comment)
									}

									distance := jxutils.EarthDistance(jxutils.IntCoordinate2Standard(store.Lng), jxutils.IntCoordinate2Standard(store.Lat), jxutils.IntCoordinate2Standard(storeCourier.Lng), jxutils.IntCoordinate2Standard(storeCourier.Lat))
									params := map[string]interface{}{
										"Lng":    storeCourier.Lng,
										"Lat":    storeCourier.Lat,
										"Remark": fmt.Sprintf("%d", int(distance*1000)),
									}
									_, err = dao.UpdateEntityLogically(dao.GetDB(), &model.StoreCourierMap{}, params, ctx.GetUserName(), map[string]interface{}{
										"StoreID":  store.ID,
										"VendorID": vendorID,
									})
									if distance > 0.2 {
										retVal = [][]interface{}{
											[]interface{}{
												store,
												storeDetail2,
											},
										}
									}
								} else if handler.Handler.IsErrStoreNotExist(err) {
									if storeDetail2.AuditStatus == model.StoreAuditStatusCreated {
										err = nil
									} else {
										globals.SugarLogger.Infof("SyncStoresCourierInfo [运营2]门店:%s-%d的%s配送门店%s获取出错:%v", store.Name, store.ID, model.VendorChineseNames[vendorID], storeDetail2.VendorStoreID, err)
									}
								}
							}
						} else if dao.IsNoRowsError(err) {
							err = nil
						}
						return retVal, err
					}, partner.UseableDeliveryVendorIDs)
				tasksch.HandleTask(subTask, task, true).Run()
				retVal, err = subTask.GetResult(0)
				return retVal, err
			}, storeList)
		tasksch.HandleTask(task, nil, true).Run()
		if isAsync {
			hint = task.GetID()
		} else {
			resultList, err2 := task.GetResult(0)
			if err = err2; err == nil {
				hint = utils.Int2Str(len(resultList))
			}
		}
	}
	return hint, err
}

func SyncStoresQualify(ctx *jxcontext.Context, storeIDs []int, vendorID int, isAsync, isContinueWhenError bool) (hint string, err error) {
	if len(storeIDs) > 0 {
		db := dao.GetDB()
		task := tasksch.NewParallelTask(fmt.Sprintf("上传门店资质:%v", storeIDs), tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
			func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				if handler := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IStoreSyncQualifyHandler); handler != nil {
					storeID := batchItemList[0].(int)
					storeDetail, err2 := dao.GetStoreDetail(db, storeID, vendorID, "")
					if err = err2; err == nil {
						if err = handler.SyncQualify(ctx, storeDetail); err == nil {
							retVal = []int{1}
						}
					}
				} else {
					err = fmt.Errorf("平台%s不支持此操作", model.VendorChineseNames[vendorID])
				}
				return retVal, err
			}, storeIDs)
		tasksch.HandleTask(task, nil, true).Run()
		if isAsync {
			hint = task.GetID()
		} else {
			resultList, err2 := task.GetResult(0)
			if err = err2; err == nil {
				hint = utils.Int2Str(len(resultList))
			}
		}
	}
	return hint, err
}

func JdStoreInfoCoordinateRecover(ctx *jxcontext.Context, vendorOrgCode string, files []*multipart.FileHeader) (err error) {
	if len(files) == 0 {
		return errors.New("没有文件上传！")
	}
	fileHeader := files[0]
	file1, err := fileHeader.Open()
	defer file1.Close()

	db := dao.GetDB()
	storeList, err := dao.GetStoresMapList(db, []int{model.VendorIDJD}, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, "", "", "")
	if err == nil {
		var validStoreList []*dao.StoreDetail
		for _, v := range storeList {
			if v.Status != model.StoreStatusDisabled && v.CreatedAt.Sub(utils.Str2Time("2019-10-01")) > 0 {
				storeInfo, err := jd.GetAPI(vendorOrgCode).GetStoreInfoByStationNo2(v.VendorStoreID)
				if err == nil && storeInfo.CreateTime.GoTime().Sub(utils.Str2Time("2019-10-25")) > 0 {
					if storeDetail, err := dao.GetStoreDetail(db, v.StoreID, v.VendorID, ""); err == nil {
						validStoreList = append(validStoreList, storeDetail)
					}
				}
			}
		}
		getStoreList := func(lng, lat, lng2, lat2 int) (vendorStoreIDs []string) {
			for _, v := range validStoreList {
				if v.Lng >= lng && v.Lng <= lng2 && v.Lat >= lat && v.Lat <= lat2 {
					vendorStoreIDs = append(vendorStoreIDs, v.VendorStoreID)
				}
			}
			return vendorStoreIDs
		}
		sheetName := "老格明细"
		file, err2 := excelize.OpenReader(file1)
		if err = err2; err == nil {
			rows, err2 := file.GetRows(sheetName)
			if err = err2; err == nil {
				str2Coords := func(str string) (lng, lat int) {
					list := strings.Split(str, ",")
					if len(list) >= 2 {
						lng, lat = jxutils.StandardCoordinate2Int(utils.Str2Float64WithDefault(list[1], 0)), jxutils.StandardCoordinate2Int(utils.Str2Float64WithDefault(list[0], 0))
					}
					return lng, lat
				}
				for i := 1; i < len(rows); i++ {
					lng, lat := str2Coords(rows[i][8])
					lng2, lat2 := str2Coords(rows[i][7])
					vendorStoreIDs := getStoreList(lng, lat, lng2, lat2)
					countInfo := fmt.Sprintf("京西已拓%d", len(vendorStoreIDs))
					axis, _ := excelize.CoordinatesToCellName(5, i+1)
					file.SetCellStr(sheetName, axis, countInfo)
					axis2, _ := excelize.CoordinatesToCellName(6, i+1)
					file.SetCellStr(sheetName, axis2, strings.Join(vendorStoreIDs, ","))
				}
				filename := ExecuteFileName(fileHeader.Filename)
				buf := bytes.NewBuffer(nil)
				if _, err = io.Copy(buf, file1); err != nil {
					return err
				}
				baseapi.SugarLogger.Debugf("WriteToExcel:save %s success", filename)
				downloadURL, err := jxutils.UploadExportContent(buf.Bytes(), filename)
				if err != nil {
					baseapi.SugarLogger.Errorf("WriteToExcel:upload %s, failed error:%v", filename, err)
				} else {
					if authInfo, err := ctx.GetV2AuthInfo(); err == nil {
						noticeMsg := fmt.Sprintf("path=%s\n", downloadURL)
						ddmsg.SendUserMessage(dingdingapi.MsgTyeText, authInfo.UserID, "导出老格恢复拓店进度成功", noticeMsg)
					}
					baseapi.SugarLogger.Debugf("WriteToExcel:upload %s success, downloadURL:%s", filename, downloadURL)
				}
			}
		}
	}
	return err
}

func ExecuteFileName(filename string) (name string) {
	filePrefix := filename[strings.LastIndex(filename, "."):len(filename)]
	fileRealName := filename[0:strings.LastIndex(filename, ".")]
	name = fileRealName + utils.Int64ToStr(time.Now().Unix()) + filePrefix
	return name
}

func GetVendorStoreInfo(ctx *jxcontext.Context, vendorIDList []int, isAsync, isContinueWhenError bool) (hint string, err error) {
	var (
		storeListJD []VendorStoreExcel
		storeListMT []VendorStoreExcel
		storeListEB []VendorStoreExcel
		storeIDs    []string
	)
	taskSeqFunc := func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
		switch step {
		case 0:
			for _, vendorID := range vendorIDList {
				iStoreHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IStoreHandler)
				if vendorID == model.VendorIDEBAI {
					storeIDs, err = ebai.CurPurchaseHandler.GetShopIDsByPage()
				} else {
					storeIDs, err = iStoreHandler.GetAllStoresVendorID(ctx, "")
				}
				if err != nil {
					return "", err
				}
				taskFunc := func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
					var storeDetail *dao.StoreDetail
					storeID := batchItemList[0].(string)
					if partner.IsMultiStore(vendorID) {
						multiHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.IMultipleStoresHandler)
						storeDetail, err = multiHandler.ReadStore(ctx, "", storeID, "")
						if err != nil {
							return retVal, err
						}
					} else {
						singleHandler, _ := partner.GetPurchasePlatformFromVendorID(vendorID).(partner.ISingleStoreHandler)
						storeDetail, err = singleHandler.ReadStore(ctx, "", storeID, "")
						if err != nil {
							return retVal, err
						}
					}
					db := dao.GetDB()
					storeDetail2, err := dao.GetStoreDetailByVendorStoreID(db, storeDetail.VendorStoreID, vendorID, "")
					if err != nil {
						return "", err
					}
					if storeDetail.Status != model.StoreStatusOpened {
						if storeDetail2 != nil {
							var storeExcel = VendorStoreExcel{
								StoreID:         storeDetail2.ID,
								VendorStoreName: storeDetail.Name,
								Tel1:            storeDetail.Tel1,
								Tel2:            storeDetail.Tel2,
								Address:         storeDetail.Address,
								Status:          StoreStatus2Chinese(storeDetail.Status),
								CityName:        storeDetail.CityName,
								MarketManName:   storeDetail2.MarketManName,
								OperatorName:    storeDetail2.OperatorName,
								OperatorName2:   storeDetail2.OperatorName2,
								OperatorName3:   storeDetail2.OperatorName3,
							}
							retVal = []VendorStoreExcel{storeExcel}
						}
					}
					return retVal, err
				}
				taskParallel := tasksch.NewParallelTask("获取各平台未营业门店", tasksch.NewParallelConfig().SetIsContinueWhenError(isContinueWhenError), ctx, taskFunc, storeIDs)
				tasksch.HandleTask(taskParallel, task, true).Run()
				storeList, _ := taskParallel.GetResult(0)
				for _, v := range storeList {
					if vendorID == model.VendorIDJD {
						storeListJD = append(storeListJD, v.(VendorStoreExcel))
					}
					if vendorID == model.VendorIDEBAI {
						storeListEB = append(storeListEB, v.(VendorStoreExcel))
					}
					if vendorID == model.VendorIDMTWM {
						storeListMT = append(storeListMT, v.(VendorStoreExcel))
					}
				}
			}
		case 1:
			WriteToExcelStore(task, storeListJD, storeListMT, storeListEB)
		}
		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 WriteToExcelStore(task *tasksch.SeqTask, storeListJD, storeListMT, storeListEB []VendorStoreExcel) (err error) {
	var sheetList []*excel.Obj2ExcelSheetConfig
	var downloadURL, fileName string
	if len(storeListJD) > 0 {
		excelConf := &excel.Obj2ExcelSheetConfig{
			Title:       "京东平台",
			Data:        storeListJD,
			CaptionList: titleListStore,
		}
		sheetList = append(sheetList, excelConf)
	}
	if len(storeListMT) > 0 {
		excelConf := &excel.Obj2ExcelSheetConfig{
			Title:       "美团平台",
			Data:        storeListMT,
			CaptionList: titleListStore,
		}
		sheetList = append(sheetList, excelConf)
	}
	if len(storeListEB) > 0 {
		excelConf := &excel.Obj2ExcelSheetConfig{
			Title:       "饿百平台",
			Data:        storeListEB,
			CaptionList: titleListStore,
		}
		sheetList = append(sheetList, excelConf)
	}
	if len(sheetList) == 0 {
		return errors.New("所选平台没有未营业的门店！")
	} else {
		downloadURL, fileName, err = jxutils.UploadExeclAndPushMsg(sheetList, "各平台未营业门店统计")
		if err != nil {
			baseapi.SugarLogger.Errorf("WriteToExcel:upload %s failed error:%v", fileName, err)
		} else {
			noticeMsg := fmt.Sprintf("[详情点我]path=%s \n", downloadURL)
			task.SetNoticeMsg(noticeMsg)
			baseapi.SugarLogger.Debugf("WriteToExcel:upload %s success, downloadURL:%s", fileName, downloadURL)
		}
	}
	return err
}

func StoreStatus2Chinese(status int) (str string) {
	if status == model.StoreStatusOpened {
		return "正常营业"
	} else if status == model.StoreStatusDisabled {
		return "暂停营业"
	} else if status == model.StoreStatusClosed {
		return "休息中"
	} else {
		return "未知的营业状态"
	}
}

func GetStorePriceScore(ctx *jxcontext.Context, storeIDs, vendorIDs []int, fromScore, toScore, sort int, snapDate string, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
	var snapDateParam time.Time
	db := dao.GetDB()
	//权限
	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
		}
	}
	if snapDate != "" {
		snapDateParam = utils.Str2Time(snapDate)
	}
	storePriceScore, totalCount, err := dao.GetStorePriceScore(db, storeIDs, vendorIDs, fromScore, toScore, sort, snapDateParam, offset, pageSize)
	pagedInfo = &model.PagedInfo{
		Data:       storePriceScore,
		TotalCount: totalCount,
	}
	return pagedInfo, err
}

func CreateStorePriceScore(ctx *jxcontext.Context) (err error) {
	db := dao.GetDB()
	var snapshotAt time.Time
	snapshotAt = utils.Time2Date(time.Now().AddDate(0, 0, -1))
	storePriceScoreSnapshot, err := dao.GetStorePriceScoreSnapshot(db, snapshotAt)
	if len(storePriceScoreSnapshot) > 0 {
		txDB, _ := dao.Begin(db)
		defer func() {
			if r := recover(); r != nil || err != nil {
				dao.Rollback(db, txDB)
				if r != nil {
					panic(r)
				}
			}
		}()
		priceReferSnapshotDeleteHis := &model.StorePriceScoreSnapshot{SnapshotAt: snapshotAt.AddDate(0, 0, -7)}
		priceReferSnapshotDelete := &model.StorePriceScoreSnapshot{SnapshotAt: snapshotAt}
		dao.DeleteEntity(db, priceReferSnapshotDeleteHis, "SnapshotAt")
		dao.DeleteEntity(db, priceReferSnapshotDelete, "SnapshotAt")
		for _, v := range storePriceScoreSnapshot {
			dao.WrapAddIDCULDEntity(v, ctx.GetUserName())
			v.SnapshotAt = snapshotAt
			if err = dao.CreateEntity(db, v); err != nil {
				return err
			}
		}
		dao.Commit(db, txDB)
	}
	return err
}

func RefreshJdLevel(ctx *jxcontext.Context) (err error) {
	db := dao.GetDB()
	storeMapList, err := dao.GetStoresMapList(db, []int{model.VendorIDJD}, nil, nil, model.StoreStatusOpened, -1, "", "", "")
	if len(storeMapList) > 0 {
		txDB, _ := dao.Begin(db)
		defer func() {
			if r := recover(); r != nil || err != nil {
				dao.Rollback(db, txDB)
				if r != nil {
					panic(r)
				}
			}
		}()
		task := tasksch.NewParallelTask("更新京东门店等级", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
			func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
				v := batchItemList[0].(*model.StoreMap)
				var (
					pageLimit = 5
					pageNo    = 1
					level     string
				)
				for ; pageNo < pageLimit+1; pageNo++ {
					level, err = jd.GetAPI(v.VendorOrgCode).GetJdStoreLevel(v.VendorOrgCode, v.VendorStoreID, pageNo)
					if err != nil {
						return retVal, err
					}
					if level != "" {
						break
					}
					if pageNo == pageLimit {
						level = ""
					}
				}
				v.JdStoreLevel = level
				_, err = dao.UpdateEntity(db, v, "JdStoreLevel")
				return retVal, err
			}, storeMapList)
		tasksch.HandleTask(task, nil, true).Run()
		_, err = task.GetResult(0)
		dao.Commit(db, txDB)
	}
	return err
}

type tJdStoreInfo struct {
	model.Store
	VendorOrgCode string `orm:"size(32)" json:"vendorOrgCode"` // 同一平台下不同的商户代码，如果只有一个，可以为空

	FreightDeductionPack string `orm:"size(32)" json:"freightDeductionPack"` //

	JdCityCode       int
	JdDistrictCode   int
	JdStoreStatus    int
	VendorStoreID    string `orm:"column(vendor_store_id)"`
	RealLastOperator string
	SyncStatus       int
	VendorStoreName  string
}

func UpdateJdStoreNameAll(ctx *jxcontext.Context) (err error) {
	storeMap := map[int]int{
		100345: 100345,
		100347: 100347,
		100852: 100852,
		101689: 101689,
		102654: 102654,
		102381: 102381,
		102500: 102500,
		102810: 102810,
	}
	db := dao.GetDB()
	userName := ctx.GetUserName()
	for _, v := range storeMap {
		var stores []*tJdStoreInfo
		sql := `
		SELECT
		t1.*, city.jd_code jd_city_code, district.jd_code jd_district_code,
		t2.status jd_store_status, t2.vendor_store_id, IF(t1.updated_at > t2.updated_at, t1.last_operator,
		t2.last_operator) real_last_operator,
		t2.sync_status, t2.freight_deduction_pack, t2.vendor_org_code, t2.vendor_store_name
		FROM store t1
		JOIN store_map t2 ON t1.id = t2.store_id AND t2.vendor_id = ? 
		LEFT JOIN place city ON t1.city_code = city.code
		LEFT JOIN place district ON t1.district_code = district.code
		WHERE t1.id = ?
		ORDER BY t2.updated_at
		`
		if err = dao.GetRows(db, &stores, sql, model.VendorIDJD, v); err == nil {
			for _, store := range stores {
				if store.Status == model.StoreStatusDisabled {
					continue
				}
				a := jd.GetAPI(store.VendorOrgCode)
				storeParams := &jdapi.OpStoreParams{
					StationNo: store.VendorStoreID,
					Operator:  userName,
					Phone:     store.Tel1,
					Mobile:    store.Tel2,
				}
				if store.SyncStatus&model.SyncFlagDeletedMask == 0 {
					storeParams.OutSystemID = utils.Int2Str(int(store.ID))
				} else {
					storeParams.OutSystemID = store.VendorStoreID
				}
				if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreName) != 0 {
					if store.VendorStoreName != "" {
						storeParams.StationName = store.VendorStoreName
					} else {
						storeParams.StationName = jxutils.ComposeStoreName(store.Name, model.VendorIDJD)
					}
					storeParams.StationName = utils.LimitUTF8StringLen(storeParams.StationName, jdapi.MaxStoreNameLen)
				}
				if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagStoreAddress) != 0 {
					storeParams.StationAddress = store.Address
					storeParams.CoordinateType = jdapi.CoordinateTypeAutonavi // 一直用高德
					storeParams.Lng = jxutils.IntCoordinate2Standard(store.Lng)
					storeParams.Lat = jxutils.IntCoordinate2Standard(store.Lat)
					if store.JdCityCode != 0 {
						storeParams.City = store.JdCityCode
					}
					if store.JdDistrictCode != 0 {
						storeParams.County = store.JdDistrictCode
					}
				}
				// if specialDistrictMap[storeParams.County] != 0 {
				// 	storeParams.City = storeParams.County
				// 	storeParams.County = specialDistrictMap[storeParams.County]
				// }
				storeParams.StoreNotice = store.PromoteInfo
				modifyCloseStatus := false
				if store.SyncStatus&(model.SyncFlagNewMask|model.SyncFlagDeletedMask|model.SyncFlagStoreStatus) != 0 {
					modifyCloseStatus = true
					_, storeParams.CloseStatus = jd.JxStoreStatus2JdStatus(jxutils.MergeStoreStatus(store.Status, store.JdStoreStatus))
				}
				// fillOpTimeParams(storeParams, store.GetOpTimeList())
				errList := errlist.New()
				if globals.EnableJdStoreWrite {
					errList.AddErr(a.UpdateStoreInfo4Open2(storeParams, modifyCloseStatus))
				}
				err = errList.GetErrListAsOne()
			}
		}
	}
	return err
}

func DeletePrinterSeq(ctx *jxcontext.Context, storeIDs []int) (err error) {
	db := dao.GetDB()
	for _, v := range storeIDs {
		stores, err := dao.GetStoreList(db, []int{v}, nil, nil, nil, nil, "")
		if err != nil || len(stores) == 0 {
			return err
		}
		vendorID := stores[0].PrinterVendorID
		if vendorID == model.NO {
			return fmt.Errorf("该门店没有绑定打印机，ID：[%v]，名字：[%v]", stores[0].ID, stores[0].Name)
		}
		if vendorID == model.VendorIDXiaoWM {
			return fmt.Errorf("暂不支持该打印机品牌清空打印队列，[%v]", model.VendorChineseNames[model.VendorIDXiaoWM])
		}
		handler := partner.GetPrinterPlatformFromVendorID(vendorID)
		err = handler.EmptyPrintList(ctx, stores[0].PrinterSN, stores[0].PrinterKey)
	}
	return err
}

func checkYbParams(db *dao.DaoDB, storeMap *model.StoreMap, storeID int) (err error) {
	var (
		appID        = storeMap.YbAppID
		appKey       = storeMap.YbAppKey
		yinbaoCookie string
		storeMap2    *model.StoreMap
	)
	if appID == "" {
		return fmt.Errorf("绑定银豹平台必须输入appID！")
	}
	if appKey == "" {
		return fmt.Errorf("绑定银豹平台必须输入appKey！")
	}
	api.YinBaoAPI = yinbaoapi.New(appKey, appID)
	_, err = api.YinBaoAPI.QueryProductByBarcode("")
	if err != nil {
		if errCode, ok := err.(*utils.ErrorWithCode); ok {
			if errCode.Code() == yinbaoapi.AppIDErrCode {
				return fmt.Errorf("请输入正确的银豹appID！，[%v]", appID)
			}
			if errCode.Code() == yinbaoapi.AppKeyErrCode {
				return fmt.Errorf("请输入正确的银豹appKey！，[%v]", appKey)
			}
		}
	}
	storeMaps, err := dao.GetStoresMapList2(db, []int{model.VendorIDYB}, nil, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "", false)
	if len(storeMaps) > 0 {
		for _, v := range storeMaps {
			if v.YbAppID == appID {
				return fmt.Errorf("appID和已有店铺重复！[%v]", v.StoreID)
			}
			if v.YbAppKey == appKey {
				return fmt.Errorf("appKey和已有店铺重复！[%v]", v.StoreID)
			}
		}
	}
	stores, err := dao.GetStoreList(db, []int{storeID}, nil, nil, nil, nil, "")
	if len(stores) > 0 {
		if configs, err := dao.QueryConfigs(dao.GetDB(), "yinbaoCookie", model.ConfigTypeCookie, ""); err == nil {
			yinbaoCookie = configs[0].Value
		}
		api.YinBaoAPI.SetCookie(".POSPALAUTH30220", yinbaoCookie)
		result, err := loadSubStoresByUserIdDDLJson()
		if err != nil {
			return err
		}
		flag := false
		for _, v := range result {
			if v.Company == stores[0].Name {
				storeMap.VendorStoreID = utils.Int2Str(v.ID)
				flag = true
				break
			}
		}
		if !flag {
			return fmt.Errorf("未在平台上找到该门店，请确保京西门店名和银豹门店名相同！")
		}
	}
	sql := "SELECT * FROM store_map WHERE vendor_id = ? and deleted_at = ? ORDER BY yb_store_prefix DESC LIMIT 1"
	sqlParams := []interface{}{model.VendorIDYB, utils.DefaultTimeValue}
	err = dao.GetRow(db, &storeMap2, sql, sqlParams)
	if err == nil {
		prefix := utils.Int64ToStr(utils.Str2Int64(storeMap2.YbStorePrefix) + 1)
		realPrefix := prefix
		for i := 0; i < 3-len(prefix); i++ {
			realPrefix = "0" + realPrefix
		}
		storeMap.YbStorePrefix = realPrefix
	}
	return err
}

func loadSubStoresByUserIdDDLJson() (result []*yinbaoapi.LoadSubStoresByUserIdDDLJsonResult, err error) {
	for {
		result, err = api.YinBaoAPI.LoadSubStoresByUserIdDDLJson()
		if err == nil {
			break
		} else {
			if yinbaoapi.IsErrCookie(err) {
				err = ChangeYbCookie()
				if err != nil {
					break
				}
				result, err = loadSubStoresByUserIdDDLJson()
			} else {
				break
			}
		}
	}
	return result, err
}

func ChangeYbCookie() (err error) {
	cookie, err := api.YinBaoAPI.TryGetCookie()
	if err != nil {
		return err
	}
	api.YinBaoAPI.SetCookie(".POSPALAUTH30220", cookie)
	UpdateConfig(jxcontext.AdminCtx, "yinbaoCookie", model.ConfigTypeCookie, cookie)
	return err
}

func GetStoreCategoryMap(ctx *jxcontext.Context, parentID, level int, storeID int) (storeCatMaps []*model.StoreCategoryMap, err error) {
	db := dao.GetDB()
	storeCatMaps, err = dao.GetStoreCategoryMap(db, parentID, level, storeID, 0)
	if err != nil {
		return nil, err
	}
	//if ctx.GetLoginType() != auth2.AuthTypePassword && ctx.GetLoginType() != weixin.AuthTypeMP && ctx.GetLoginType() != weixin.AuthTypeMini && ctx.GetLoginType() != weixin.AuthTypeWxApp && ctx.GetLoginType() != weixin.AuthTypeWxAppCaishi && ctx.GetLoginType() != auth2.AuthTypeMobile {
	//	return storeCatMaps, err
	//}
	//表示没有门店分类
	if len(storeCatMaps) == 0 {
		storeCatMaps, err = dao.GetCategoriesForStore(db, parentID, 0, nil)
	}
	return storeCatMaps, err
}

func GetStoreCategoryMapNoDefault(ctx *jxcontext.Context, parentID, level int, storeID int) (storeCatMaps []*model.StoreCategoryMap, err error) {
	db := dao.GetDB()
	storeCatMaps, err = dao.GetStoreCategoryMap(db, parentID, level, storeID, 0)
	if err != nil {
		return nil, err
	}
	if ctx.GetLoginType() != weixin.AuthTypeMP && ctx.GetLoginType() != weixin.AuthTypeMini && ctx.GetLoginType() != weixin.AuthTypeWxApp && ctx.GetLoginType() != weixin.AuthTypeWxAppCaishi && ctx.GetLoginType() != auth2.AuthTypeMobile {
		return storeCatMaps, err
	}
	return storeCatMaps, err
}

func AddStoreCategoryMap(ctx *jxcontext.Context, storeCategoryMap *model.StoreCategoryMap) (result *model.StoreCategoryMap, err error) {
	var (
		db = dao.GetDB()
	)
	if storeCategoryMap.Level == 1 {
		storeCatMaps, _ := dao.GetStoreCategoryMap(db, -1, 0, storeCategoryMap.StoreID, storeCategoryMap.CategoryID)
		if len(storeCatMaps) > 0 {
			for _, v := range storeCatMaps {
				if storeCategoryMap.StoreCategoryName == v.StoreCategoryName {
					return nil, fmt.Errorf("已存在绑定的京西分类，分类名：[%v]", storeCatMaps[0].StoreCategoryName)
				}
			}
		}
	}
	storeCategoryMap.StoreCategoryName = strings.Trim(storeCategoryMap.StoreCategoryName, " ")
	dao.WrapAddIDCULDEntity(storeCategoryMap, ctx.GetUserName())
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if err = dao.CreateEntity(db, storeCategoryMap); err != nil {
		dao.Rollback(db, txDB)
		return nil, err
	}
	dao.Commit(db, txDB)
	result = storeCategoryMap
	if storeCategoryMap.CategoryID != 0 {
		SetStoreCategorySyncStatus2(db, []int{storeCategoryMap.StoreID}, []int{storeCategoryMap.CategoryID}, model.SyncFlagModifiedMask)
	}
	// _, err = CurVendorSync.SyncCategory(ctx, nil, cat.ID, false, userName)
	return result, err
}

func UpdateStoreCategoryMap(ctx *jxcontext.Context, ID int, storeCategoryMap *model.StoreCategoryMap, isDelete bool) (num int64, err error) {
	var (
		db                = dao.GetDB()
		valid             = make(map[string]interface{})
		storeCategoryMap2 = &model.StoreCategoryMap{}
	)
	storeCategoryMap2.ID = ID
	if err = dao.GetEntity(db, storeCategoryMap2); err != nil {
		return 0, err
	}
	if storeCategoryMap.StoreCategoryName != "" {
		valid["storeCategoryName"] = storeCategoryMap.StoreCategoryName
	}
	if storeCategoryMap.IsHidden < 2 {
		valid["isHidden"] = storeCategoryMap.IsHidden
	}
	if storeCategoryMap.CategoryID != 0 {
		valid["categoryID"] = storeCategoryMap.CategoryID
		if !isDelete {
			storeCatMaps, _ := dao.GetStoreCategoryMap(db, -1, 0, storeCategoryMap2.StoreID, storeCategoryMap.CategoryID)
			if len(storeCatMaps) > 0 {
				for _, v := range storeCatMaps {
					if v.ID != ID {
						return 0, fmt.Errorf("已存在绑定的京西分类，分类名：[%v]", storeCatMaps[0].StoreCategoryName)
					}
				}
			}
		}
	}
	if storeCategoryMap.Level != 0 {
		valid["level"] = storeCategoryMap.Level
		if storeCategoryMap.Level == 2 {
			cat2, _ := dao.GetCategories(db, -1, 0, []int{storeCategoryMap.CategoryID}, false)
			if len(cat2) > 0 {
				if cat2[0].ParentID != storeCategoryMap.ParentID && cat2[0].ParentID != model.NO {
					return 0, fmt.Errorf("此二级分类只能绑定到对应一级分类下！")

				}
			}
		}
	}
	if isDelete {
		valid["deletedAt"] = time.Now()
		valid["updatedAt"] = time.Now()
		valid["lastOperator"] = ctx.GetUserName()
		//如果是1级分类则删除下面的子分类
		var catIDs []int
		if storeCategoryMap2.Level == 1 {
			storeCatMaps, _ := dao.GetStoreCategoryMap(db, storeCategoryMap2.CategoryID, 2, storeCategoryMap2.StoreID, 0)
			if len(storeCatMaps) > 0 {
				for _, v := range storeCatMaps {
					catIDs = append(catIDs, v.CategoryID)
					v.DeletedAt = time.Now()
					v.LastOperator = ctx.GetUserName()
					dao.UpdateEntity(db, v, "DeletedAt", "LastOperator")
				}
			}
		}
		var storeSkus []*model.StoreSkuBind
		sql := `
			SELECT a.*
			FROM store_sku_bind a
			JOIN sku b ON a.sku_id = b.id
			JOIN sku_name c ON c.id = b.name_id
			WHERE a.deleted_at = ? AND b.deleted_at = ? AND c.deleted_at = ?
			AND c.category_id = ? AND a.store_id = ?
			UNION ALL
			SELECT a.*
			FROM store_sku_bind a
			JOIN sku b ON a.sku_id = b.id
			JOIN sku_name c ON c.id = b.name_id
			JOIN sku_category d ON d.id = c.category_id
			JOIN sku_category e ON e.id = d.parent_id
			WHERE a.deleted_at = ? AND b.deleted_at = ? AND c.deleted_at = ?
			AND e.id = ? AND a.store_id = ?
		`
		sqlParams := []interface{}{
			utils.DefaultTimeValue, utils.DefaultTimeValue, utils.DefaultTimeValue,
			storeCategoryMap2.CategoryID, storeCategoryMap2.StoreID,
			utils.DefaultTimeValue, utils.DefaultTimeValue, utils.DefaultTimeValue,
			storeCategoryMap2.CategoryID, storeCategoryMap2.StoreID,
		}
		if err = dao.GetRows(db, &storeSkus, sql, sqlParams); err != nil {
			return 0, err
		}
		if len(storeSkus) > 0 {
			return 0, fmt.Errorf("该分类下或该分类的子分类下有关注的商品，不可删除！分类名：[%v]", storeCategoryMap2.StoreCategoryName)
		}
		catIDs = append(catIDs, storeCategoryMap.CategoryID)
		SetStoreCategorySyncStatus2(db, []int{storeCategoryMap2.StoreID}, catIDs, model.SyncFlagModifiedMask)
	} else {
		SetStoreCategorySyncStatus2(db, []int{storeCategoryMap2.StoreID}, []int{storeCategoryMap.CategoryID, storeCategoryMap2.CategoryID}, model.SyncFlagModifiedMask)
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if num, err = dao.UpdateEntityLogically(db, storeCategoryMap2, valid, ctx.GetUserName(), nil); err != nil {
		dao.Rollback(db, txDB)
		return 0, err
	}
	dao.Commit(db, txDB)
	return num, err
}

func ReorderStoreCategories(ctx *jxcontext.Context, parentID, storeID int, categoryIDs []int) (err error) {
	var (
		storeCatsMap []*model.StoreCategoryMap
	)
	db := dao.GetDB()
	storeCatsMap, err = dao.GetStoreCategoryMap(db, parentID, 0, storeID, 0)
	catsLen := len(storeCatsMap)
	if catsLen != len(categoryIDs) {
		return ErrInputCatsDoesntMatch
	}
	catsMap := make(map[int]*model.StoreCategoryMap, catsLen)
	for _, cat := range storeCatsMap {
		catsMap[cat.CategoryID] = cat
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	for k, v := range categoryIDs {
		if catsMap[v] == nil {
			dao.Rollback(db, txDB)
			return fmt.Errorf("分类:%d不在%d分类下", v, parentID)
		}
		catsMap[v].StoreCategorySeq = k
		catsMap[v].LastOperator = ctx.GetUserName()
		if _, err = dao.UpdateEntity(db, catsMap[v]); err != nil {
			dao.Rollback(db, txDB)
			return err
		}
	}
	dao.Commit(db, txDB)
	SetStoreCategorySyncStatus2(db, nil, categoryIDs, model.SyncFlagModifiedMask)
	if err == nil {
		CurVendorSync.SyncStoresCategory(ctx, db, nil, nil, false, true, true)
	}
	return err
}

func CopyStoreCategories(ctx *jxcontext.Context, fromStoreID int, toStoreIDs, categoryIDs []int, isContinueWhenError, isAsync bool) (hint string, err error) {
	var (
		db = dao.GetDB()
	)
	//权限
	if permission.IsRoled(ctx) {
		if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
			if storeIDsMap[fromStoreID] == 0 {
				return "", fmt.Errorf("抱歉，您无权更改他人店铺分类 [%v]", fromStoreID)
			}
			for _, v := range toStoreIDs {
				if storeIDsMap[v] == 0 {
					return "", fmt.Errorf("抱歉，您无权更改他人店铺分类 [%v]", v)
				}
			}
		}
	}
	storeCatList, err := dao.GetStoreCategoryMap(db, -1, 0, fromStoreID, 0)
	if err != nil {
		return "", err
	}
	if len(storeCatList) == 0 {
		return "", fmt.Errorf("原门店无分类信息！storeID: %v", fromStoreID)
	}
	task := tasksch.NewParallelTask("CopyStoreCategories", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeID := batchItemList[0].(int)
			//证明是要全复制
			if len(categoryIDs) == 0 {
				txDB, _ := dao.Begin(db)
				defer func() {
					if r := recover(); r != nil {
						dao.Rollback(db, txDB)
						panic(r)
					}
				}()
				sql := `
					DELETE FROM store_category_map WHERE store_id = ?
				`
				sqlParams := []interface{}{storeID}
				if _, err = dao.ExecuteSQL(db, sql, sqlParams); err != nil {
					return retVal, err
				}
				sql2 := `
					INSERT INTO store_category_map(created_at,updated_at,last_operator,deleted_at,store_id,category_id,store_category_name,store_category_seq,level,parent_id,is_hidden)
					SELECT ?, ?, ?, ?, ?, category_id, store_category_name, store_category_seq, level, parent_id,is_hidden
					FROM store_category_map 
					WHERE store_id = ? AND deleted_at = ?
				`
				sqlParams2 := []interface{}{
					time.Now(), time.Now(), ctx.GetUserName(), utils.DefaultTimeValue, storeID,
					fromStoreID, utils.DefaultTimeValue,
				}
				if _, err = dao.ExecuteSQL(db, sql2, sqlParams2); err != nil {
					return retVal, err
				}
				dao.Commit(db, txDB)
			} else {
				for _, v := range categoryIDs {
					list, err := dao.GetStoreCategoryMap(db, -1, 0, storeID, v)
					if err != nil {
						return retVal, err
					}
					if len(list) > 0 {
						return retVal, fmt.Errorf("该门店已有重复绑定的京西分类！storeID: %v,categroyID: %v", storeID, v)
					} else {
						txDB, _ := dao.Begin(db)
						defer func() {
							if r := recover(); r != nil {
								dao.Rollback(db, txDB)
								panic(r)
							}
						}()
						sql2 := `
							INSERT INTO store_category_map(created_at,updated_at,last_operator,deleted_at,store_id,category_id,store_category_name,store_category_seq,level,parent_id,is_hidden)
							SELECT ?, ?, ?, ?, ?, category_id, store_category_name, store_category_seq, level, parent_id,is_hidden
							FROM store_category_map 
							WHERE store_id = ? AND deleted_at = ? AND category_id = ?
						`
						sqlParams2 := []interface{}{
							time.Now(), time.Now(), ctx.GetUserName(), utils.DefaultTimeValue, storeID,
							fromStoreID, utils.DefaultTimeValue, v,
						}
						if _, err = dao.ExecuteSQL(db, sql2, sqlParams2); err != nil {
							return retVal, err
						}
						dao.Commit(db, txDB)
					}
				}
			}
			return retVal, err
		}, toStoreIDs)
	tasksch.HandleTask(task, nil, true).Run()
	if isAsync {
		hint = task.GetID()
	} else {
		_, err = task.GetResult(0)
		hint = "1"
	}
	return hint, err
}

func DisabledStoreWithoutVendor(ctx *jxcontext.Context, isContinueWhenError, isAsync bool) (hint string, err error) {
	var (
		db = dao.GetDB()
	)
	stores, err := dao.GetStoreList(db, nil, nil, []int{model.StoreStatusClosed, model.StoreStatusHaveRest, model.StoreStatusOpened}, nil, nil, "")
	task := tasksch.NewParallelTask("DisabledStoreWithoutVendor", tasksch.NewParallelConfig().SetParallelCount(1).SetIsContinueWhenError(isContinueWhenError), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			store := batchItemList[0].(*model.Store)
			storeMaps, err := dao.GetStoresMapList(db, []int{model.VendorIDYB, model.VendorIDJD, model.VendorIDJX, model.VendorIDEBAI, model.VendorIDMTWM, model.VendorIDJDShop}, []int{store.ID}, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
			if len(storeMaps) == 0 {
				store.Status = model.StoreStatusDisabled
				dao.UpdateEntity(db, store, "Status")
			}
			return retVal, err
		}, stores)
	tasksch.HandleTask(task, nil, true).Run()
	if isAsync {
		hint = task.GetID()
	} else {
		_, err = task.GetResult(0)
		hint = "1"
	}
	return hint, err
}

func CleanStoreIsBoughtMatter(ctx *jxcontext.Context) (err error) {
	//每周一凌晨1点清空
	if int(time.Now().Weekday()) != 1 {
		return err
	}
	db := dao.GetDB()
	sql := `
		UPDATE store SET is_bought_matter = ?
	`
	sqlParam := []interface{}{model.NO}
	_, err = dao.ExecuteSQL(db, sql, sqlParam)
	return err
}

func InsertStoreCategories(ctx *jxcontext.Context, db *dao.DaoDB, storeID int) (err error) {
	if db == nil {
		db = dao.GetDB()
	}
	if err = dao.DeleteStoreCategroies2(db, storeID); err == nil {
		err = dao.InsertStoreCategories(db, ctx.GetUserName(), storeID)
	}
	return err
}

func DeleteStoreCategroies(ctx *jxcontext.Context, db *dao.DaoDB, storeID int) (err error) {
	err = dao.DeleteStoreCategroies(db, ctx.GetUserName(), storeID)
	return err
}

func findSkusBetweenJdsMainStore(db *dao.DaoDB, storeID int) (skus []int) {
	var skuMap = make(map[int]int)
	storeSkus1, _ := dao.GetStoresSkusInfo(db, []int{model.JdShopMainStoreID}, nil)
	storeSkus2, _ := dao.GetStoresSkusInfo(db, []int{storeID}, nil)
	for _, v := range storeSkus1 {
		if v.Status == model.SkuStatusNormal {
			skuMap[v.SkuID] = 1
		}
	}
	for _, v := range storeSkus2 {
		if v.Status == model.SkuStatusNormal && skuMap[v.SkuID] != 0 {
			skus = append(skus, v.SkuID)
		}
	}
	return skus
}

func UpdateStorePricePack(ctx *jxcontext.Context, storeID, vendorID int, pricePack, value string) (err error) {
	if err = checkConfig(model.SyncFlagModifiedMask, model.ConfigTypePricePack, pricePack, value); err != nil {
		return err
	}
	db := dao.GetDB()
	storeDetail, _ := dao.GetStoreDetail(db, storeID, vendorID, "")
	if storeDetail.PayPercentage > 50 {
		return fmt.Errorf("目前只允许扣点的门店修改调价包！")
	}

	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	//证明是门店自己的调价包
	if strings.Contains(pricePack, utils.Int2Str(storeID)) {
		obj := dao.PricePercentagePack2Obj(value)
		realValue, _ := json.Marshal(obj)
		// _, err = UpdateConfig(ctx, pricePack, model.ConfigTypePricePack, string(realValue))
		configList, err := dao.QueryConfigs(db, pricePack, model.ConfigTypePricePack, "")
		if err != nil {
			dao.Rollback(db, txDB)
			return err
		}
		if _, err = dao.UpdateEntityLogically(db, configList[0], map[string]interface{}{
			"Value": string(realValue),
		}, ctx.GetUserName(), nil); err != nil {
			dao.Rollback(db, txDB)
			return err
		}
	} else {
		//表示门店要改他原有的调价包
		//1、调价包名字要加上门店编号门店名等
		//2、添加config
		//3、更新storeVendorMap
		//4、同步相关
		pricePack = pricePack + "_" + utils.Int2Str(storeID) + "_" + storeDetail.Name
		if err = AddConfig(ctx, pricePack, model.ConfigTypePricePack, value); err != nil {
			dao.Rollback(db, txDB)
			return err
		}
		storeMapList, err := dao.GetStoresMapList(db, []int{vendorID}, []int{storeID}, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
		if err != nil {
			dao.Rollback(db, txDB)
			return err
		}
		if len(storeMapList) > 0 {
			storeMapList[0].PricePercentagePack = pricePack
			dao.UpdateEntity(db, storeMapList[0], "PricePercentagePack")
		}
	}
	dao.Commit(db, txDB)
	storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, pricePack, "", "")
	if err != nil {
		dao.Rollback(db, txDB)
		return err
	}
	vendorStoreMap := make(map[int][]int)
	for _, v := range storeMapList {
		vendorStoreMap[v.VendorID] = append(vendorStoreMap[v.VendorID], v.StoreID)
	}
	for vendorID, storeIDs := range vendorStoreMap {
		dao.SetStoreSkuSyncStatus(db, vendorID, storeIDs, nil, model.SyncFlagPriceMask)
	}
	return err
}

func GetJdDeliveryArea(ctx *jxcontext.Context, storeIDs []int) (err error) {
	type tmp struct {
		JdID string `json:"jdID"`
		S    int    `json:"s"`
	}
	type SpecialtyStoreSkus struct {
		StoreID   int    `json:"门店ID"`
		StoreName string `json:"门店名"`
		City      string `json:"城市"`
		Area      int    `json:"面积"`
	}
	var (
		ss         []*tmp
		excelTitle = []string{
			"门店ID",
			"门店名",
			"城市",
			"面积",
		}
		sheetList             []*excel.Obj2ExcelSheetConfig
		specialtyStoreSkus    []*SpecialtyStoreSkus
		downloadURL, fileName string
	)
	storeMaps, _ := dao.GetStoresMapList(dao.GetDB(), []int{model.VendorIDJD}, storeIDs, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	for _, v := range storeMaps {
		time.Sleep(time.Second / 3)
		station, err := jd.GetAPI("320406").GetDeliveryRangeByStationNo2(v.VendorStoreID)
		if err != nil {
			continue
		}
		if station.DeliveryRangeType == 2 {
			strs := strings.Split(station.DeliveryRange, ";")
			ss = append(ss, &tmp{
				JdID: v.VendorStoreID,
				S:    utils.Float64TwoInt(math.Ceil((jxutils.ComputeSignedArea(strs[:len(strs)-1])))),
			})
		}
	}
	for i := 0; i < len(ss)-1; i++ {
		for j := 0; j < len(ss)-1-i; j++ {
			if ss[j].S > ss[j+1].S {
				temp := ss[j]
				ss[j] = ss[j+1]
				ss[j+1] = temp
			}
		}
	}
	for _, v := range ss {
		storeDetail, _ := dao.GetStoreDetailByVendorStoreID(dao.GetDB(), v.JdID, model.VendorIDJD, "")
		place, _ := dao.GetPlaceByCode(dao.GetDB(), storeDetail.CityCode)
		specialtyStoreSku := &SpecialtyStoreSkus{
			StoreID:   storeDetail.ID,
			StoreName: storeDetail.Name,
			City:      place.Name,
			Area:      v.S,
		}
		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 UpdateStorePushClient(ctx *jxcontext.Context, storeID int, cID string) (err error) {
	var (
		db = dao.GetDB()
	)
	storePushClients, err := dao.GetStorePushClient(db, storeID, cID)
	if err != nil {
		return err
	}
	if len(storePushClients) == 0 {
		storePushClient := &model.StorePushClient{
			StoreID:  storeID,
			ClientID: cID,
		}
		dao.WrapAddIDCULDEntity(storePushClient, ctx.GetUserName())
		dao.CreateEntity(db, storePushClient)
	}
	return err
}

func CreateStoreAudit(ctx *jxcontext.Context, storeAudit *model.StoreAudit) (err error) {
	var (
		db = dao.GetDB()
	)
	storeAudits, err := dao.GetStoreAudit(db, []int{model.StoreAuditStatusOnline}, storeAudit.UserID, "")
	if len(storeAudits) > 0 {
		return fmt.Errorf("您已申请过入驻，请不要重复申请！")
	}
	if storeAudit.Address == "" {
		return fmt.Errorf("门店地址必填！")
	}
	if storeAudit.Lng == 0 || storeAudit.Lat == 0 {
		lng, lat, _, _, _ := api.TencentMapAPI.GetCoordinateFromAddress(storeAudit.Address, "")
		if lng != 0 && lat != 0 {
			storeAudit.Lng = jxutils.StandardCoordinate2Int(lng)
			storeAudit.Lat = jxutils.StandardCoordinate2Int(lat)
		} else {
			return fmt.Errorf("请填写正确的门店地址！")
		}
	}

	if storeAudit.UserID == "" {
		storeAudit.UserID = ctx.GetUserID()
	}
	dao.WrapAddIDCULDEntity(storeAudit, ctx.GetUserName())
	dao.CreateEntity(db, storeAudit)
	return err
}

func GetStoreAudit(ctx *jxcontext.Context, statuss []int, keyword, applyTimeStart, applyTimeEnd, auditTimeStart, auditTimeEnd string, pageSize, offset int) (pagedInfo *model.PagedInfo, err error) {
	var (
		applyTimeStartp, applyTimeEndp, auditTimeStartp, auditTimeEndp time.Time
		db                                                             = dao.GetDB()
	)
	if applyTimeStart != "" {
		applyTimeStartp = utils.Str2Time(applyTimeStart)
	}
	if applyTimeEnd != "" {
		applyTimeEndp = utils.Str2Time(applyTimeEnd)
	}
	if auditTimeStart != "" {
		auditTimeStartp = utils.Str2Time(auditTimeStart)
	}
	if auditTimeEnd != "" {
		auditTimeEndp = utils.Str2Time(auditTimeEnd)
	}
	pagedInfo, err = dao.GetStoreAuditPage(db, statuss, keyword, applyTimeStartp, applyTimeEndp, auditTimeStartp, auditTimeEndp, pageSize, offset)
	return pagedInfo, err
}

func StoreAudit(ctx *jxcontext.Context, storeAudits []*model.StoreAudit, status int) (hint string, err error) {
	db := dao.GetDB()
	if status != model.StoreAuditStatusCreated && status != model.StoreAuditStatusRejected {
		return "", fmt.Errorf("审核标志不正确！")
	}
	task := tasksch.NewParallelTask("StoreAudit", tasksch.NewParallelConfig().SetParallelCount(5).SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeAudit := batchItemList[0].(*model.StoreAudit)
			storeAudits, err := dao.GetStoreAudit(db, []int{model.StoreAuditStatusOnline}, storeAudit.UserID, "")
			if len(storeAudits) == 0 || err != nil {
				return retVal, fmt.Errorf("未查询到待审核信息！")
			}
			if len(storeAudits) > 1 {
				return retVal, fmt.Errorf("查询到该用户的待审核信息大于1条！userID: [%s]", storeAudit.UserID)
			}
			//审核通过
			if status == model.StoreAuditStatusCreated {
				storeAudits[0].AuditStatus = model.StoreAuditStatusCreated
				//添加门店
				storeExt := &StoreExt{}
				if data, err := json.Marshal(&storeAudits[0]); err == nil {
					utils.UnmarshalUseNumber(data, &storeExt)
				}
				storeExt.ID = 0
				storeExt.Store.StoreLevel = "C"
				storeExt.FloatLng = jxutils.IntCoordinate2Standard(utils.Float64TwoInt(storeExt.FloatLng))
				storeExt.FloatLat = jxutils.IntCoordinate2Standard(utils.Float64TwoInt(storeExt.FloatLat))
				storeExt.Status = model.StoreStatusDisabled
				storeExt.MarketManName = ctx.GetUserName()
				storeExt.MarketManPhone, _ = ctx.GetMobileAndUserID()
				storeID, err := CreateStore2JX(ctx, storeExt, ctx.GetUserName())
				if err != nil {
					return retVal, fmt.Errorf(err.Error())
				}
				err = AddUsers4Role(ctx, autils.NewRole(authz.StoreRoleBoss, storeID), []string{storeAudit.UserID})
				if err != nil {
					return retVal, fmt.Errorf(err.Error())
				}
			} else if status == model.StoreAuditStatusRejected {
				storeAudits[0].AuditStatus = model.StoreAuditStatusRejected
			} else {
				return retVal, fmt.Errorf("审核标志不正确！")
			}
			storeAudits[0].LastOperator = ctx.GetUserName()
			storeAudits[0].Remark = storeAudit.Remark
			_, err = dao.UpdateEntity(db, storeAudits[0], "LastOperator", "AuditStatus", "Remark")
			//是否推送app消息
			if err == nil {

			}
			return retVal, err
		}, storeAudits)
	tasksch.HandleTask(task, nil, true).Run()
	hint = task.GetID()
	return hint, err
}

func GetDiffJxStoreAndMTWMStoreInfo(ctx *jxcontext.Context, storeIDs []int) (err error) {
	type TmpDiffStore struct {
		StoreID      int    `json:"门店ID"`
		StoreName    string `json:"门店名"`
		City         string `json:"城市"`
		Coordinate   string `json:"坐标"`
		Address      string `json:"地址"`
		Phone        string `json:"电话"`
		CoordinateMT string `json:"坐标（美团）"`
		AddressMT    string `json:"地址（美团）"`
		PhoneMT      string `json:"电话（美团）"`
		Comment      string `json:"对比情况"`
	}
	var (
		excelTitle = []string{
			"门店ID",
			"门店名",
			"城市",
			"坐标",
			"地址",
			"电话",
			"坐标（美团）",
			"地址（美团）",
			"电话（美团）",
			"对比情况",
		}
		sheetList             []*excel.Obj2ExcelSheetConfig
		tmpDiffStoreList      []*TmpDiffStore
		downloadURL, fileName string
		db                    = dao.GetDB()
	)
	courierStoreList, _ := dao.GetStoreCourierList(db, storeIDs, []int{model.VendorIDMTPS}, model.StoreStatusAll, model.StoreAuditStatusAll)
	for _, v := range courierStoreList {
		comment := ""
		tmpDiffStore := &TmpDiffStore{
			StoreID: v.StoreID,
		}
		stores, err := dao.GetStoreList(db, []int{v.StoreID}, nil, nil, nil, nil, "")
		store := stores[0]
		place, _ := dao.GetPlaceByCode(db, store.CityCode)
		tmpDiffStore.City = place.Name
		tmpDiffStore.StoreName = store.Name
		tmpDiffStore.Coordinate = utils.Float64ToStr(jxutils.IntCoordinate2Standard(store.Lng)) + "," + utils.Float64ToStr(jxutils.IntCoordinate2Standard(store.Lat))
		tmpDiffStore.Address = store.Address
		tmpDiffStore.Phone = store.Tel1
		shopInfo, err := api.MtpsAPI.ShopQuery(v.VendorStoreID)
		if err != nil {
			tmpDiffStore.Comment += err.Error()
			tmpDiffStoreList = append(tmpDiffStoreList, tmpDiffStore)
			continue
		}
		tmpDiffStore.CoordinateMT = utils.Float64ToStr(jxutils.IntCoordinate2Standard(shopInfo.ShopLng)) + "," + utils.Float64ToStr(jxutils.IntCoordinate2Standard(shopInfo.ShopLat))
		tmpDiffStore.AddressMT = shopInfo.ShopAddress
		tmpDiffStore.PhoneMT = shopInfo.ContactPhone
		if tmpDiffStore.Coordinate != tmpDiffStore.CoordinateMT {
			comment += "坐标不同;"
		}
		if tmpDiffStore.Address != tmpDiffStore.AddressMT {
			comment += "地址不同;"
		}
		if tmpDiffStore.Phone != tmpDiffStore.PhoneMT {
			comment += "电话不同;"
		}
		tmpDiffStore.Comment = comment
		if comment != "" {
			tmpDiffStoreList = append(tmpDiffStoreList, tmpDiffStore)
		}
	}

	excelConf := &excel.Obj2ExcelSheetConfig{
		Title:       "sheet1",
		Data:        tmpDiffStoreList,
		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
}

type GetBrandsResult struct {
	*model.Brand
	Balance int `json:"balance"`
}

func GetBrands(ctx *jxcontext.Context, name string, brandID int, isManage bool) (getBrandsResult []*GetBrandsResult, err error) {
	brands, _ := dao.GetBrands(dao.GetDB(), name, brandID, "", isManage, ctx.GetUserID())
	for _, v := range brands {
		balance, _ := partner.CurStoreAcctManager.GetBrandBalance(v.ID)
		result := &GetBrandsResult{
			Brand:   v,
			Balance: balance,
		}
		getBrandsResult = append(getBrandsResult, result)
	}
	return getBrandsResult, err

}

func AddBrand(ctx *jxcontext.Context, brand *model.Brand) (err error) {
	var (
		db = dao.GetDB()
	)
	if brand.Name == "" {
		return fmt.Errorf("请输入品牌名！")
	}
	brand.Name = utils.TrimBlankChar(brand.Name)
	dao.WrapAddIDCULDEntity(brand, ctx.GetUserName())
	err = dao.CreateEntity(db, brand)
	return err
}

func UpdateBrand(ctx *jxcontext.Context, payload map[string]interface{}, isDel bool) (err error) {
	var (
		db    = dao.GetDB()
		brand = &model.Brand{}
	)
	brand.ID = int(utils.MustInterface2Int64(payload["id"]))
	dao.GetEntity(db, brand)
	if isDel {
		brand.DeletedAt = time.Now()
		brand.LastOperator = ctx.GetUserName()
		dao.UpdateEntity(db, brand, "DeletedAt", "LastOperator")
		//品牌下的用户也删了
		if brandUsers, _ := dao.GetBrandUser(db, brand.ID, ""); len(brandUsers) > 0 {
			for _, v := range brandUsers {
				UpdateBrandUser(ctx, v.BrandID, v.UserID, true)
			}
		}
	} else {
		valid := dao.StrictMakeMapByStructObject(payload, brand, ctx.GetUserName())
		if len(valid) > 0 {
			txDB, _ := dao.Begin(db)
			defer func() {
				if r := recover(); r != nil {
					dao.Rollback(db, txDB)
					panic(r)
				}
				if _, err = dao.UpdateEntityLogically(db, brand, valid, ctx.GetUserName(), nil); err != nil {
					dao.Rollback(db, txDB)
					return
				}
				dao.Commit(db, txDB)
			}()
		}
	}
	return err
}

func CreateVendorStore(ctx *jxcontext.Context, storeID, vendorID int, payload map[string]interface{}) (err error) {
	var (
		db          = dao.GetDB()
		storeDetail *dao.StoreDetail
	)
	if storeDetail, err = dao.GetStoreDetail(db, storeID, vendorID, ""); err != nil {
		return fmt.Errorf("获取门店信息失败，请联系技术部！")
	}
	if payload["vendorOrgCode"] == nil {
		return fmt.Errorf("请选择平台账号！")
	}
	if vendorID == model.VendorIDMTWM {
		if brands, err := dao.GetBrands(db, "", storeDetail.BrandID, "", false, ""); err == nil {
			if len(brands) > 0 {
				if strings.Contains(brands[0].Name, "无品牌") {
					return fmt.Errorf("无品牌店铺不允许创建美团门店！")
				}
			}
		}
	}
	if payload["vendorStoreName"] == nil {
		return fmt.Errorf("请输入平台名！")
	}
	handler := partner.GetPurchasePlatformFromVendorID(vendorID)
	vendorStoreID, err := handler.CreateStore2(db, storeID, ctx.GetUserName(), payload, storeDetail)
	if err != nil {
		return err
	}
	if vendorStoreID != "" && vendorStoreID != "0" && vendorID != model.VendorIDDD { //抖店单独处理
		//AddStoreVendorMap 太复杂了并不满足，还是手动加吧
		storeMap := &model.StoreMap{
			VendorStoreID:       vendorStoreID,
			StoreID:             storeID,
			VendorID:            vendorID,
			VendorOrgCode:       payload["vendorOrgCode"].(string),
			Status:              0, //平台还未审核等
			DeliveryType:        model.StoreDeliveryTypeByStore,
			PricePercentage:     int16(utils.MustInterface2Int64(payload["pricePercentage"])),
			AutoPickup:          1,
			DeliveryCompetition: 1,
			IsSync:              1,
			PricePercentagePack: payload["pricePercentagePack"].(string),
		}
		dao.WrapAddIDCULDEntity(storeMap, ctx.GetUserName())
		err = dao.CreateEntity(db, storeMap)
	}
	return err
}

func GetBrandStore(ctx *jxcontext.Context, name string) (brands []*model.BrandStore, err error) {
	var (
		db = dao.GetDB()
	)
	sql := `
		SELECT * FROM brand_store WHERE deleted_at = ? 
	`
	sqlParams := []interface{}{utils.DefaultTimeValue}
	if name != "" {
		sql += " AND name LIKE ?"
		sqlParams = append(sqlParams, "%"+name+"%")
	}
	err = dao.GetRows(db, &brands, sql, sqlParams)
	return brands, err
}

// RefreshMTWMToken 刷新美团外卖门token
func RefreshMTWMToken(ctx *jxcontext.Context) (err error) {
	var (
		db = dao.GetDB()
	)
	if time.Now().YearDay()%10 != 0 {
		return
	}
	//token是1个月过期，每10天刷一次吧
	storeMaps, _ := dao.GetStoresMapList(db, []int{model.VendorIDMTWM}, nil, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", globals.Mtwm2Code)
	for _, v := range storeMaps {
		if v.MtwmRefreshToken != "" {
			if result, err := api.Mtwm2API.RefreshAccessToken(v.MtwmRefreshToken); err == nil {
				v.MtwmToken = result.AccessToken
				v.MtwmRefreshToken = result.RefreshToken
				dao.UpdateEntity(db, v, "MtwmToken", "MtwmRefreshToken")
			}
		}
	}
	return err
}

// RefreshTiktokShopToken 属性抖店门店tokne
func RefreshTiktokShopToken(ctx *jxcontext.Context) (err error) {
	var (
		db = dao.GetDB()
	)

	codes, _ := dao.GetVendorOrgCode(db, model.VendorIDDD, "", "platform")
	api := api.TiktokStore
	for _, v := range codes {
		if v.Token != "" {
			var access *doudian_sdk.AccessToken
			if err := json.Unmarshal([]byte(v.Token), &access); err != nil {
				globals.SugarLogger.Debugf("定时任务更新抖店店铺token错误:%s,%s", utils.Format4Output(v, false), err.Error())
				continue
			}
			if access.ExpiresIn-time.Now().Unix() > 60*60 {
				continue
			}
			api.SetToken(access.AccessToken)
			api.SetRefreshToken(access.RefreshToken)
			api.SetExpiresIn(access.ExpiresIn)
			api.SetAcctokenObj(access)
			if result, err := api.RefreshToken(); err == nil {
				token, _ := json.Marshal(result)
				v.Token = string(token)
				v.UpdatedAt = time.Now()
				v.StoreBrandName = "定时任务更新"
				dao.UpdateEntity(db, v, "Token", "UpdatedAt", "StoreBrandName")
				if v.VendorOrgCode == "68023619" {
					// 抖音目前只有菜市和百货
					tiktok_store.HttpToGuoYuan(utils.Struct2MapByJson(v), tiktok_store.CaiShiPushGyTagToken, model.ServerTypePet)
				}
			}
		}
	}
	return err
}

func StoreConfirmAct(ctx *jxcontext.Context, status, msgStatusID int) (err error) {
	msgStatus := &model.MessageStatus{}
	msgStatus.ID = msgStatusID
	db := dao.GetDB()
	if err = dao.GetEntity(db, msgStatus); err != nil {
		return err
	}
	if status == model.YES {
		msg := &model.Message{}
		msg.ID = msgStatus.MessageID
		if err = dao.GetEntity(db, msg); err != nil {
			return err
		}
		actMap := &model.MessageActInfo{}
		err = json.Unmarshal([]byte(msg.ActInfo), &actMap)
		if time.Now().Sub(utils.Str2Time(actMap.BeginAt)) > 0 {
			msgStatus.ConfirmStatus = -1
			dao.WrapUpdateULEntity(msgStatus, ctx.GetUserName())
			_, err = dao.UpdateEntity(db, msgStatus, "ConfirmStatus")
			return fmt.Errorf("参与活动时间已过期！")
		}
	}
	msgStatus.ConfirmStatus = status
	dao.WrapUpdateULEntity(msgStatus, ctx.GetUserName())
	_, err = dao.UpdateEntity(db, msgStatus, "ConfirmStatus")
	return err
}

func GetStoreAcctBalance(ctx *jxcontext.Context, storeID int) (storeAcct *model.StoreAcct, err error) {
	var (
		db = dao.GetDB()
	)
	totalIncome, err := dao.GetStoreAcctIncomeTotal(db, storeID, nil, "", utils.ZeroTimeValue, utils.ZeroTimeValue)
	if err != nil {
		return nil, err
	}
	totalExpend, err := dao.GetStoreAcctExpendTotal(db, storeID, nil, "", utils.ZeroTimeValue, utils.ZeroTimeValue)
	if err != nil {
		return nil, err
	}
	return &model.StoreAcct{
		StoreID:        storeID,
		AccountBalance: totalIncome - totalExpend,
	}, err
}

func RefreshStoreBind(ctx *jxcontext.Context) (err error) {
	var (
		db = dao.GetDB()
	)
	stores, err := dao.GetStoreList(db, nil, nil, []int{model.StoreStatusClosed, model.StoreStatusHaveRest, model.StoreStatusOpened}, nil, nil, "")
	task := tasksch.NewParallelTask("RefreshStoreBind", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			store := batchItemList[0].(*model.Store)
			var (
				userIDmap1                         = make(map[string]string)
				userIDmap2                         = make(map[string]string)
				mobileList, userIDsAdd, userIDsDel []string
			)
			// userList := getAllUsers4Store(ctx, db, store)
			// if store.Tel1 != "" {
			// 	mobileList = append(mobileList, store.Tel1)
			// }
			// if store.Tel2 != "" {
			// 	mobileList = append(mobileList, store.Tel2)
			// }
			if store.MarketManPhone != "" {
				mobileList = append(mobileList, store.MarketManPhone)
			}
			if store.OperatorPhone != "" {
				mobileList = append(mobileList, store.OperatorPhone)
			}
			if store.OperatorPhone2 != "" {
				mobileList = append(mobileList, store.OperatorPhone2)
			}
			if store.OperatorPhone3 != "" {
				mobileList = append(mobileList, store.OperatorPhone3)
			}
			mobileList = append(mobileList, "18160030913", "15520595380", "18048531223", "18080188338")
			for _, v := range mobileList {
				if user, err := dao.GetUserByID(db, "mobile", v); err == nil && user != nil {
					userIDmap1[user.UserID] = user.UserID
				}
			}
			nowUserIDs, _ := GetRoleUserList(ctx, autils.NewRole(authz.StoreRoleBoss, store.ID))
			for _, v := range nowUserIDs {
				userIDmap2[v] = v
				if userIDmap1[v] == "" {
					userIDsDel = append(userIDsDel, v)
				}
			}
			for _, v := range userIDmap1 {
				if userIDmap2[v] == "" {
					userIDsAdd = append(userIDsAdd, v)
				}
			}
			AddUsers4Role(ctx, autils.NewRole(authz.StoreRoleBoss, store.ID), userIDsAdd)
			// DeleteUsers4Role(ctx, autils.NewRole(authz.StoreRoleBoss, store.ID), userIDsDel)
			return retVal, err
		}, stores)
	tasksch.HandleTask(task, nil, true).Run()
	task.GetID()
	return err
}

func UpdateVendorStoreBussinessStatus(ctx *jxcontext.Context, storeID int, vendorID []int, status int) (err error) {
	var (
		db      = dao.GetDB()
		errList = errlist.New()
	)
	for _, v := range vendorID {
		storeDetail, err := dao.GetStoreDetail(db, storeID, v, "")
		if err != nil {
			return err
		}
		handler := partner.GetPurchasePlatformFromVendorID(v)
		if err = handler.UpdateStoreLineStatus(ctx, storeDetail.VendorOrgCode, storeID, storeDetail.VendorStoreID, status); err == nil {
			if storeMaps, _ := dao.GetStoresMapList(db, []int{v}, []int{storeID}, nil, model.StoreStatusAll,
				model.StoreIsSyncAll, "", "", ""); len(storeMaps) > 0 {
				storeMaps[0].IsOnline = status
				storeMaps[0].Status = status
				dao.UpdateEntity(db, storeMaps[0], "IsOnline", "Status")
			}
		} else {
			errList.AddErr(fmt.Errorf("平台[%d],门店id[%d],错误:%s", v, storeID, err))
		}
	}

	vendorIDStr := ""
	for _, v := range vendorID {
		vendorIDStr += utils.Int2Str(v)
	}
	AddEventDetail(db, ctx, model.OperateUpdate, storeID, model.ThingTypeStore, storeID, "", utils.Int2Str(status))
	event.AddOperateEvent(ctx, ctx.GetTrackInfo(), fmt.Sprintf("修改门店状态平台[%s],状态[%d]", vendorIDStr, status), "", "", 10, "UpdateStore")
	if errList.GetErrListAsOne() != nil {
		return errList.GetErrListAsOne()
	}
	return nil
}

type JdPage struct {
	ID            int    `orm:"column(id)" json:"id"`
	Name          string `json:"name"`
	MonthSalesTip string `json:"month_sales_tip"`
	ShippingTime  string `json:"shipping_time"`
	Address       string `json:"address"`
	Phone         string `json:"phone"`
	Pj            string `json:"pj"`
	Orgcode       string `json:"orgcode"`
	Wmpoiid       string `json:"wmpoiid"`
	ThirdCategory string `json:"third_category"`
	Set           string `json:"set"`
	Settime       string `json:"settime"`
	Latr          string `json:"latr"`
	Longr         string `json:"longr"`
	Rek           string `json:"rek"`
	BakInfo       string `json:"bak_info"`
	GoodsNumber   int    `json:"goods_number"`
	Lat           string `json:"lat"`
	Lng           string `json:"lng"`
	DistrictName  string `json:"district_name"`
	BrandName     string `json:"brand_name"`
}

type MtPage struct {
	ID            int    `orm:"column(id)" json:"id"`
	Name          string `json:"name"`
	MonthSalesTip string `json:"month_sales_tip"`
	ShippingTime  string `json:"shipping_time"`
	Address       string `json:"address"`
	Phone         string `json:"phone"`
	Pj            string `json:"pj"`
	Wmpoiid       string `json:"wmpoiid"`
	ThirdCategory string `json:"third_category"`
	Set           string `json:"set"`
	Settime       string `json:"settime"`
	Latr          string `json:"latr"`
	Longr         string `json:"longr"`
	Rek           string `json:"rek"`
	CityName      string `json:"city_name"`
	GoodsNumber   int    `json:"goods_number"`
	Lat           string `json:"lat"`
	Lng           string `json:"lng"`
	DistrictName  string `json:"district_name"`
	BrandName     string `json:"brand_name"`
}

type EbaiPage struct {
	LibraryName       string `json:"library_name"`
	EleID             string `orm:"column(ele_id)" json:"ele_id"`
	MonthSales        string `json:"month_sales"`
	FirstOpenTime     string `json:"first_open_time"`
	Wid               string `json:"wid"`
	ShopScore         string `json:"shop_score"`
	StoreID           string `orm:"column(store_id)" json:"store_id"`
	Phone             string `json:"phone"`
	Classif           string `json:"classif"`
	ClassifyParameter string `json:"classify_parameter"`
	Address           string `json:"address"`
	Settime           string `json:"settime"`
	Remark            string `json:"remark"`
	State             int    `json:"state"`
	Lat               string `json:"lat"`
	Lng               string `json:"lng"`
	DistrictName      string `json:"district_name"`
	BrandName         string `json:"brand_name"`
}

func RefreshPageStore() {
	var (
		db    = dao.GetDB()
		jds   []*JdPage
		mts   []*MtPage
		ebais []*EbaiPage
	)
	//刷京东门店表的坐标和区名
	sql := `
		SELECT * FROM jingdong_showd WHERE (lng IS NULL AND lat IS NULL) OR (lng = 0 AND lat = 0) 
	`
	dao.GetRows(db, &jds, sql, nil)
	for _, v := range jds {
		var (
			lng, lat float64
			dis      string
		)
		if (v.Lat == "" && v.Lng == "") || (v.Lat == "0" && v.Lng == "0") {
			if v.Address != "" {
				if v.BakInfo != "" {
					//result, _ := api.AutonaviAPI.GetCoordinateFromAddressAll(v.Address, v.Remark)
					//lng, lat, dis := utils.Float64ToStr(result.Lng), utils.Float64ToStr(result.Lat), result.AdName
					place, _ := dao.GetPlaceByName(db, v.BakInfo, 2, 0)
					/*if result1, _ := api.AutonaviAPI.GetCoordinateFromAddressByPageAll(v.Address, place.Code); result1 != nil {
						if result1.Lng == 0 || result1.Lat == 0 {
							result2, _ := api.AutonaviAPI.GetCoordinateFromAddressByPageAll(v.Address, 0)
							lng, lat, dis = result2.Lng, result2.Lat, result2.AdName
						} else {
							lng, lat, dis = result1.Lng, result1.Lat, result1.AdName
						}
					}*/
					lngTencent, latTencent, _, disName, err := api.TencentMapAPI.GetCoordinateFromAddress(v.Address, utils.Int2Str(place.Code))
					if err != nil {
						globals.SugarLogger.Debugf("RefreshPageStore err := %v", err)
						continue
					}
					lng = lngTencent
					lat = latTencent
					dis = disName
					sql := `
					UPDATE jingdong_showd SET lng = ?, lat = ?, district_name = ? WHERE id = ? 
				`
					sqlparams := []interface{}{
						lng, lat, dis, v.ID,
					}
					dao.ExecuteSQL(db, sql, sqlparams)
				} else {
					lngTencent, latTencent, _, disName, err := api.TencentMapAPI.GetCoordinateFromAddress(v.Address, utils.Int2Str(0))
					if err != nil {
						globals.SugarLogger.Debugf("RefreshPageStore err := %v", err)
						continue
					}
					lng, lat, dis = lngTencent, latTencent, disName
					//result1, _ := api.AutonaviAPI.GetCoordinateFromAddressByPageAll(v.Address, 0)
					//lng, lat, dis = result1.Lng, result1.Lat, result1.AdName
					sql := `
						UPDATE jingdong_showd SET lng = ?, lat = ?, district_name = ? WHERE id = ?
				`
					sqlparams := []interface{}{
						lng, lat, dis, v.ID,
					}
					dao.ExecuteSQL(db, sql, sqlparams)
				}
			}
		}
	}
	//刷美团门店表的坐标和区名
	sql2 := `
		SELECT * FROM meituan_showd WHERE (lng IS NULL AND lat IS NULL) OR (lng = 0 AND lat = 0) 
	`
	dao.GetRows(db, &mts, sql2, nil)
	for _, v := range mts {
		var (
			lng, lat float64
			dis      string
		)
		if (v.Lat == "" && v.Lng == "") || (v.Lat == "0" && v.Lng == "0") {
			if v.Address != "" {
				//if v.CityName != "" {
				//result, _ := api.AutonaviAPI.GetCoordinateFromAddressAll(v.Address, v.Remark)
				//lng, lat, dis := utils.Float64ToStr(result.Lng), utils.Float64ToStr(result.Lat), result.AdName
				place, _ := dao.GetPlaceByName(db, v.CityName, 2, 0)
				if lngTencent, latTencent, _, disName, err := api.TencentMapAPI.GetCoordinateFromAddress(v.Address, utils.Int2Str(place.Code)); err == nil {
					lng, lat, dis = lngTencent, latTencent, disName
				}
				sql := `
					UPDATE meituan_showd SET lng = ?, lat = ?, district_name = ? WHERE id = ? 
				`
				sqlparams := []interface{}{
					lng, lat, dis, v.ID,
				}
				dao.ExecuteSQL(db, sql, sqlparams)
				/*	} else {
						result1, _ := api.AutonaviAPI.GetCoordinateFromAddressByPageAll(v.Address, 0)
						lng, lat, dis = result1.Lng, result1.Lat, result1.AdName
						sql := `
						UPDATE meituan_showd SET lng = ?, lat = ?, district_name = ? WHERE id = ?
					`
						sqlparams := []interface{}{
							lng, lat, dis, v.ID,
						}
						dao.ExecuteSQL(db, sql, sqlparams)
					}*/
			}
		}
	}

	//刷饿百门店表的坐标和区名
	sqlebai := `
		SELECT * FROM elm_showd WHERE (lng IS NULL AND lat IS NULL) OR (lng = 0 AND lat = 0) OR (lng = '' AND lat = '')
	`
	dao.GetRows(db, &ebais, sqlebai, nil)
	for _, v := range ebais {
		var (
			lng, lat float64
			dis      string
		)
		if (v.Lat == "" && v.Lng == "") || (v.Lat == "0" && v.Lng == "0") {
			if v.Address != "" {
				//if v.Remark != "" {
				//result, _ := api.AutonaviAPI.GetCoordinateFromAddressAll(v.Address, v.Remark)
				//lng, lat, dis := utils.Float64ToStr(result.Lng), utils.Float64ToStr(result.Lat), result.AdName
				place, _ := dao.GetPlaceByName(db, v.Remark, 2, 0)
				if lngTencent, latTencent, _, disName, err := api.TencentMapAPI.GetCoordinateFromAddress(v.Address, utils.Int2Str(place.Code)); err == nil {
					lng, lat, dis = lngTencent, latTencent, disName
				}
				sql := `
					UPDATE elm_showd SET lng = ?, lat = ?, district_name = ? WHERE store_id = ? 
				`
				sqlparams := []interface{}{
					lng, lat, dis, v.StoreID,
				}
				dao.ExecuteSQL(db, sql, sqlparams)
				//} else {
				//	result1, _ := api.AutonaviAPI.GetCoordinateFromAddressByPageAll(v.Address, 0)
				//	lng, lat, dis = result1.Lng, result1.Lat, result1.AdName
				//	sql := `
				//	UPDATE elm_showd SET lng = ?, lat = ?, district_name = ? WHERE store_id = ?
				//`
				//	sqlparams := []interface{}{
				//		lng, lat, dis, v.StoreID,
				//	}
				//	dao.ExecuteSQL(db, sql, sqlparams)
				//}
			}
		}
	}
	////商品表合并
	//sql3 := ` TRUNCATE page_sku`
	//dao.ExecuteSQL(db, sql3)
	//sql4 := `
	//	INSERT INTO page_sku (created_at,updated_at,last_operator,vendor_id,vendor_store_id,vendor_sku_id,vendor_sku_name,month_saled,act_price,sale_price,unit)
	//	SELECT NOW(),NOW(),'jxadmin',t1.* FROM (
	//		(
	//			SELECT 0, a.wmpoiid, a.Base, a.name, a.month_saled, ISNULL(a.min_price) *100, a.basic_price*100, ''
	//			FROM jingdong_foodlist a
	//		)
	//		UNION ALL
	//		(
	//			SELECT 1, a.wmpoiid, '', a.name, a.month_saled, 0, a.min_price*100, a.unit
	//			FROM meituan_foodlist a
	//		)
	//	)t1
	//`
	//dao.ExecuteSQL(db, sql4)
	////门店表合并
	//sql5 := ` TRUNCATE page_store`
	//dao.ExecuteSQL(db, sql5)
	//sql6 := `
	//	INSERT INTO page_store (created_at,updated_at,last_operator,vendor_id,vendor_store_id,vendor_store_name,address,city_name,org_code,lng,lat)
	//	SELECT NOW(),NOW(),'jxadmin',t1.* FROM (
	//		(
	//			SELECT 0, a.wmpoiid, a.name, a.address, a.bak_info, a.orgcode, a.lng, a.lat
	//			FROM jingdong_showd a
	//		)
	//		UNION ALL
	//		(
	//			SELECT 1, a.wmpoiid, a.name, a.address, a.city_name, '', a.lng, a.lat
	//			FROM meituan_showd a
	//		)
	//	)t1
	//`
	//dao.ExecuteSQL(db, sql6)

	var (
		jds2      []*JdPage
		mts2      []*MtPage
		splitStr1 = []string{
			"-", "（", "(", "•", "【",
		}
		splitStr2 = []string{
			"-", "（", "(", "•", " ", "【",
		}
		ebai2 []*EbaiPage
	)
	trySplitBrand := func(splitStr []string, name string) (brandName string) {
		for _, v := range splitStr {
			if strings.Index(name, v) != -1 {
				return name[:strings.Index(name, v)]
			} else {
				continue
			}
		}
		return "无"
	}
	sql7 := `
		SELECT * FROM jingdong_showd WHERE brand_name = '' OR brand_name IS NULL OR brand_name = '无' OR brand_name = '0'
	`
	dao.GetRows(db, &jds2, sql7)
	if len(jds2) > 0 {
		for _, v := range jds2 {
			sql := `
					UPDATE jingdong_showd SET brand_name = ? WHERE id = ?
				`
			sqlparams := []interface{}{
				trySplitBrand(splitStr1, v.Name), v.ID,
			}
			dao.ExecuteSQL(db, sql, sqlparams)
		}
	}
	sql8 := `
		SELECT * FROM meituan_showd WHERE brand_name = '' OR brand_name IS NULL OR brand_name = '无' OR brand_name = '0'
	`
	dao.GetRows(db, &mts2, sql8)
	if len(mts2) > 0 {
		for _, v := range mts2 {
			sql := `
					UPDATE meituan_showd SET brand_name = ? WHERE id = ?
				`
			sqlparams := []interface{}{
				trySplitBrand(splitStr2, v.Name), v.ID,
			}
			dao.ExecuteSQL(db, sql, sqlparams)
		}
	}
	sqle := `
		SELECT * FROM elm_showd WHERE brand_name = '' OR brand_name IS NULL OR brand_name = '无' OR brand_name = '0'
	`
	dao.GetRows(db, &ebai2, sqle)
	if len(ebai2) > 0 {
		for _, v := range ebai2 {
			brandName := trySplitBrand(splitStr2, v.LibraryName)
			if brandName == "无" {
				var count = struct {
					Count int
				}{}
				sql := `
					SELECT COUNT(library_name) count,library_name FROM elm_showd WHERE library_name = ? GROUP BY 2 
				`
				sqlParams := []interface{}{v.LibraryName}
				dao.GetRow(db, &count, sql, sqlParams)
				if count.Count > 1 {
					brandName = v.LibraryName
				}
			}
			sql := `
					UPDATE elm_showd SET brand_name = ? WHERE store_id = ?
				`
			sqlparams := []interface{}{
				brandName, v.StoreID,
			}
			dao.ExecuteSQL(db, sql, sqlparams)
		}
	}
	sql9 := ` TRUNCATE page_brand`
	dao.ExecuteSQL(db, sql9)
	sql10 := `
		INSERT INTO page_brand (created_at,updated_at,last_operator,brand_name)
		SELECT NOW(),NOW(),'jxadmin', t1.* FROM (
			(
				SELECT DISTINCT a.brand_name
				FROM jingdong_showd a
			)
			UNION
			(
				SELECT DISTINCT a.brand_name
				FROM meituan_showd a
			)
			UNION
			(
				SELECT DISTINCT a.brand_name
				FROM elm_showd a
			)
		)t1
	`
	dao.ExecuteSQL(db, sql10)
}

func QueryPageStores2(db *dao.DaoDB, pageSize, offset int, keyword, vendorStoreID, brandName string, vendorID, cityCode, districtCode int, tel string,
	minShopScore float32, minRecentOrderNum, minSkuCount int, lng1, lat1, lng2, lat2 float64, cat string) (pagedInfo *model.PagedInfo, err error) {
	//	db.Db = orm.NewOrmUsingDB("c4beta")
	sqlJD := `
		SELECT name, 0 vendor_id, wmpoiid vendor_store_id, orgcode org_code, 1 vendor_status, address, phone tel1, pj shop_score, month_sales_tip recent_order_num,
		bak_info city_name, goods_number sku_count, lat, lng, district_name, brand_name, third_category category, shipping_time
		FROM jingdong_showd
	`
	sqlEbai := `
		SELECT library_name name, 3 vendor_id, store_id vendor_store_id, '' org_code, 1 vendor_status, address, phone tel1, shop_score, month_sales recent_order_num,
		remark city_name, 0 sku_count, lat, lng, district_name, brand_name, classify_parameter category, first_open_time shipping_time
		FROM elm_showd
	`
	sqlMT := `
		SELECT name, 1 vendor_id, wmpoiid vendor_store_id, '' org_code, 1 vendor_status, address, phone tel1, pj shop_score, REPLACE(REPLACE(month_sales_tip,'月售',''),'+','') recent_order_num,
		city_name, goods_number sku_count, lat, lng, district_name, brand_name, IF(rek = '买菜', '菜市', rek) category, shipping_time
		FROM meituan_showd
	`
	sqlGD := `
        SELECT name, 10 vendor_id, wmpoiid vendor_store_id, '' org_code, 1 vendor_status, address, phone tel1, pj shop_score, month_sales_tip recent_order_num,
        city_name, goods_number sku_count, lat, lng, district_name, brand_name, third_category category, shipping_time
        FROM gaode_showd
    `
	//lat, lng,
	//sqlCLB := `
	//   SELECT name ,20 vendor_id, wmpoiid vendor_store_id,'' org_code,1 vendor_status,address, phone tel1,pj shop_score,month_sales_tip recent_order_num,
	//    city_name,goods_number sku_count,lat, lng, district_name, brand_name, third_category  category , shipping_time
	//    FROM clb_showd
	//`
	sql := `
		SELECT SQL_CALC_FOUND_ROWS
				t1.*, t2.code city_code
				FROM (
	`
	if vendorID == -1 {
		sql += sqlJD + ` UNION`
		sql += sqlMT + ` UNION`
		sql += sqlEbai + ` UNION`
		sql += sqlGD
	} else {
		switch vendorID {
		case model.VendorIDJD:
			sql += sqlJD
		case model.VendorIDMTWM:
			sql += sqlMT
		case model.VendorIDEBAI:
			sql += sqlEbai
		case model.VendorIDGD:
			sql += sqlGD
		}
	}
	sql += `
	)t1 
	LEFT JOIN place t2 ON t2.name = t1.city_name AND t2.level = 2
	WHERE 1 = 1
	`
	sqlParams := []interface{}{}
	if vendorStoreID != "" {
		sql += " AND t1.vendor_store_id = ?"
		sqlParams = append(sqlParams, vendorStoreID)
	}
	if cityCode != 0 {
		sql += " AND t2.code = ?"
		sqlParams = append(sqlParams, cityCode)
	}
	if tel != "" {
		sql += " AND t1.tel1 = ?"
		sqlParams = append(sqlParams, tel)
	}
	if brandName != "" {
		sql += " AND t1.brand_name = ?"
		sqlParams = append(sqlParams, brandName)
	}
	if minShopScore > 0 {
		sql += " AND t1.shop_score >= ?"
		sqlParams = append(sqlParams, minShopScore)
	}
	if minRecentOrderNum > 0 {
		sql += " AND t1.recent_order_num >= ?"
		sqlParams = append(sqlParams, minRecentOrderNum)
	}
	if minSkuCount > 0 {
		sql += " AND t1.sku_count >= ?"
		sqlParams = append(sqlParams, minSkuCount)
	}
	if keyword != "" {
		keywordLike := "%" + keyword + "%"
		sql += " AND (t1.name LIKE ? OR t1.tel1 LIKE ? OR t1.org_code LIKE ? OR t1.address LIKE ? OR t2.name LIKE ? "
		sqlParams = append(sqlParams, keywordLike, keywordLike, keywordLike, keywordLike, keywordLike)
		sql += ")"
	}
	if lng1 > 0 {
		sql += " AND t1.lng >= ? AND t1.lat >= ? AND t1.lng <= ? AND t1.lat <= ?"
		sqlParams = append(sqlParams, lng1, lat1, lng2, lat2)
	}
	if cat != "" {
		sql += " AND t1.category = ?"
		sqlParams = append(sqlParams, cat)
	}
	sql += `
		LIMIT ? OFFSET ?
	`
	pageSize = jxutils.FormalizePageSize(pageSize)
	offset = jxutils.FormalizePageOffset(offset)
	sqlParams = append(sqlParams, pageSize, offset)
	var shopList []*dao.PageShopWithPlaceName
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if err = dao.GetRowsTx(txDB, &shopList, sql, sqlParams...); err == nil {
		pagedInfo = &model.PagedInfo{
			TotalCount: dao.GetLastTotalRowCount2(db, txDB),
			Data:       shopList,
		}
		dao.Commit(db, txDB)
		//var newShopList []*dao.PageShopWithPlaceName
		//task := tasksch.NewParallelTask("", tasksch.NewParallelConfig().SetIsContinueWhenError(true), jxcontext.AdminCtx,
		//	func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
		//		v := batchItemList[0].(*dao.PageShopWithPlaceName)
		//		if v.Address != "" && v.CityName != "" {
		//			result, _ := api.AutonaviAPI.GetCoordinateFromAddressAll(v.Address, v.CityName)
		//			lng, lat := result.Lng, result.Lat
		//			v.DistrictName = result.AdName
		//			v.Lng, v.Lat = lng, lat
		//			if lng1 > 0 {
		//				if !(lng >= lng1 && lat >= lat1 && lng <= lng2 && lat <= lat2) {
		//					retVal = []*dao.PageShopWithPlaceName{v}
		//				}
		//			} else {
		//				retVal = []*dao.PageShopWithPlaceName{v}
		//			}
		//			retVal = []*dao.PageShopWithPlaceName{v}
		//		} else {
		//			retVal = []*dao.PageShopWithPlaceName{v}
		//		}
		//		return retVal, err
		//	}, shopList)
		//tasksch.HandleTask(task, nil, true).Run()
		//result, _ := task.GetResult(0)
		//for _, v := range result {
		//	newShopList = append(newShopList, v.(*dao.PageShopWithPlaceName))
		//}
		//pagedInfo.Data = newShopList
	}
	return pagedInfo, err
}

type QueryPageSkusResult struct {
	OrgCode       string `json:"orgCode"`                        //商家ID
	StoreId       string `orm:"column(store_id)" json:"storeId"` //门店ID
	StoreName     string `json:"storeName"`                      //门店名
	SkuId         string `orm:"column(sku_id)" json:"skuId"`     //skuID
	SkuName       string `json:"skuName"`                        //商品名
	Parameter     string `json:"parameter"`                      //规格
	MonthSales    string `json:"monthSales"`                     //月销量
	RealTimePrice string `json:"realTimePrice"`                  //促销价
	BasicPrice    string `json:"basicPrice"`                     // 原价

	VendorID int    `orm:"column(vendor_id)" json:"vendorID"`
	Address  string `json:"address"`
	CityName string `json:"cityName"`
	CityCode int    `json:"cityCode"`
}

func QueryPageSkus(ctx *jxcontext.Context, vendorID int, vendorStoreIDs []string, keyword string, cityCode int, lng, lat, lng1, lat1, lng2, lat2 float64, radius, sortType, offset, pageSize int) (pagedInfo *model.PagedInfo, err error) {
	var (
		db      = dao.GetDB()
		results []*QueryPageSkusResult
	)
	//db.Db = orm.NewOrmUsingDB("c4beta")
	sqlParams := []interface{}{}
	sqlJD := `
		SELECT 0 vendor_id, b.orgcode org_code, a.wmpoiid store_id, b.name store_name, a.Base sku_id, a.name sku_name, a.month_saled month_sales, 
		a.basic_price, b.address, '' parameter, b.bak_info city_name, b.lng, b.lat, getDistance(?, ?, b.lng, b.lat) distance
		FROM jingdong_foodlist a
		LEFT JOIN jingdong_showd b ON a.wmpoiid = b.wmpoiid
		LEFT JOIN place c ON c.name = b.bak_info
		WHERE 1 = 1
	`
	sqlEbai := `

	`
	sqlMT := `
		SELECT 1 vendor_id, '' org_code, a.wmpoiid store_id, b.name store_name, '' sku_id, a.name sku_name, a.month_saled month_sales, 
		a.min_price basic_price, b.address, a.unit parameter, b.city_name, b.lng, b.lat, getDistance(?, ?, b.lng, b.lat) distance
		FROM meituan_foodlist a
		LEFT JOIN meituan_showd b ON a.wmpoiid = b.wmpoiid
		LEFT JOIN place c ON c.name = b.city_name
		WHERE 1 = 1
	`
	sql := `
		SELECT SQL_CALC_FOUND_ROWS
			t1.*	
          FROM (
	`
	if vendorID == -1 {
		//sql += sqlEbai + ` UNION`
		//jd
		sqlParams = append(sqlParams, lng, lat)
		if cityCode > 0 {
			sqlJD += " AND c.code = ?"
			sqlParams = append(sqlParams, cityCode)
		}
		if lng1 > 0 {
			sqlJD += " AND b.lng >= ? AND b.lat >= ? AND b.lng <= ? AND b.lat <= ?"
			sqlParams = append(sqlParams, lng1, lat1, lng2, lat2)
		}
		//mt
		sqlParams = append(sqlParams, lng, lat)
		if cityCode > 0 {
			sqlMT += " AND c.code = ?"
			sqlParams = append(sqlParams, cityCode)
		}
		if lng1 > 0 {
			sqlMT += " AND b.lng >= ? AND b.lat >= ? AND b.lng <= ? AND b.lat <= ?"
			sqlParams = append(sqlParams, lng1, lat1, lng2, lat2)
		}
		sql += sqlJD + ` UNION ALL`
		sql += sqlMT
	} else {
		switch vendorID {
		case model.VendorIDJD:
			sqlParams = append(sqlParams, lng, lat)
			if cityCode > 0 {
				sqlJD += " AND c.code = ?"
				sqlParams = append(sqlParams, cityCode)
			}
			if lng1 > 0 {
				sqlJD += " AND b.lng >= ? AND b.lat >= ? AND b.lng <= ? AND b.lat <= ?"
				sqlParams = append(sqlParams, lng1, lat1, lng2, lat2)
			}
			sql += sqlJD
		case model.VendorIDMTWM:
			sqlParams = append(sqlParams, lng, lat)
			if cityCode > 0 {
				sqlMT += " AND c.code = ?"
				sqlParams = append(sqlParams, cityCode)
			}
			if lng1 > 0 {
				sqlMT += " AND b.lng >= ? AND b.lat >= ? AND b.lng <= ? AND b.lat <= ?"
				sqlParams = append(sqlParams, lng1, lat1, lng2, lat2)
			}
			sql += sqlMT
		case model.VendorIDEBAI:
			sql += sqlEbai
		}
	}
	sql += ` )t1
	WHERE 1 = 1
	`
	if keyword != "" {
		keywordLike := "%" + keyword + "%"
		sql += " AND t1.sku_name LIKE ?"
		sqlParams = append(sqlParams, keywordLike)
	}
	if len(vendorStoreIDs) > 0 {
		sql += " AND t1.store_id IN (" + dao.GenQuestionMarks(len(vendorStoreIDs)) + ")"
		sqlParams = append(sqlParams, vendorStoreIDs)
	}

	//if radius > 0 {
	//	sql += " AND ROUND(t1.distance * 1000) < ?"
	//	sqlParams = append(sqlParams, radius)
	//}
	if sortType != 0 {
		switch math.Abs(float64(sortType)) {
		case 1:
			if sortType < 0 {
				sql += " ORDER BY t1.month_sales DESC"
			} else {
				sql += " ORDER BY t1.month_sales"
			}
		case 2:
			if sortType < 0 {
				sql += " ORDER BY t1.distance DESC"
			} else {
				sql += " ORDER BY t1.distance"
			}
		}
	}
	sql += `
		LIMIT ? OFFSET ?
	`
	sqlParams = append(sqlParams, pageSize, offset)
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if err = dao.GetRowsTx(txDB, &results, sql, sqlParams...); err == nil {
		pagedInfo = &model.PagedInfo{
			TotalCount: dao.GetLastTotalRowCount2(db, txDB),
			Data:       results,
		}
		dao.Commit(db, txDB)
		//if radius > 0 {
		//	var newShopList []*QueryPageSkusResult
		//	task := tasksch.NewParallelTask("", tasksch.NewParallelConfig().SetIsContinueWhenError(true), jxcontext.AdminCtx,
		//		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
		//			v := batchItemList[0].(*QueryPageSkusResult)
		//			if radius > 0 {
		//				result, _ := api.AutonaviAPI.GetCoordinateFromAddressAll(v.Address, v.CityName)
		//				distance := jxutils.EarthDistance(lng, lat, result.Lng, result.Lat)
		//				if utils.Float64TwoInt(math.Round(distance*1000)) < radius {
		//					retVal = []*QueryPageSkusResult{v}
		//				}
		//			} else {
		//				retVal = []*QueryPageSkusResult{v}
		//			}
		//			return retVal, err
		//		}, results)
		//	tasksch.HandleTask(task, nil, true).Run()
		//	result, _ := task.GetResult(0)
		//	for _, v := range result {
		//		newShopList = append(newShopList, v.(*QueryPageSkusResult))
		//	}
		//	pagedInfo.Data = newShopList
		//} else {
		//	pagedInfo.Data = results
		//}
	} else {
		dao.Rollback(db, txDB)
	}
	return pagedInfo, err
}

func GetPageBrands(ctx *jxcontext.Context) (brands []*model.PageBrand, err error) {
	var (
		db = dao.GetDB()
	)
	//db.Db = orm.NewOrmUsingDB("c4beta")
	sql := `
		SELECT * FROM page_brand
	`
	dao.GetRows(db, &brands, sql)
	return brands, err
}

func AddStoreMapAudit(ctx *jxcontext.Context, storeID, vendorID int, vendorOrgCode, vendorStoreID, vendorStoreName, vendorAccount, vendorPasswaord string) (err error) {
	var (
		db            = dao.GetDB()
		storeMapAudit = &model.StoreMapAudit{
			StoreID:         storeID,
			VendorID:        vendorID,
			VendorOrgCode:   vendorOrgCode,
			VendorStoreID:   vendorStoreID,
			VendorStoreName: vendorStoreName,
			VendorAccount:   vendorAccount,
		}
	)
	if vendorPasswaord != "" {
		if configList, err := dao.QueryConfigs(db, "jxKeyIV", model.ConfigTypeSys, ""); err == nil && len(configList) > 0 {
			aesKey, iv := configList[0].Value, configList[0].Value
			if data, err2 := utils.AESCBCEncpryt([]byte(vendorPasswaord), []byte(aesKey), []byte(iv)); err2 == nil {
				vendorPasswaord = base64.StdEncoding.EncodeToString(data)
			}
		}
		storeMapAudit.VendorPasswaord = vendorPasswaord
	}
	dao.WrapAddIDCULDEntity(storeMapAudit, ctx.GetUserName())
	if err = dao.CreateEntity(db, storeMapAudit); err == nil {
		//发消息
		var (
			userIDMap = make(map[string]string)
		)
		store, _ := dao.GetStoreDetail(db, storeID, vendorID, "")
		operatorRoleList := []string{store.OperatorRole, store.OperatorRole2, store.OperatorRole3}
		for _, vv := range operatorRoleList {
			var (
				roleList []*authz.RoleInfo
			)
			if vv != "" {
				roleList = append(roleList, autils.NewRole(vv, 0))
				if userIDMap2, err := GetRolesUserList(jxcontext.AdminCtx, roleList); err == nil {
					for _, w := range userIDMap2 {
						for _, ww := range w {
							userIDMap[ww] = ww
						}
					}
				}
			}
			noticeMsg := fmt.Sprintf("您有负责的商家申请授权，请尽快审核！门店ID :[%d]，门店名：[%s]，申请平台：[%v]", store.ID, store.Name, model.VendorChineseNames[vendorID])
			user2, err := dao.GetUserByID(db, "mobile", store.MarketManPhone)
			if err == nil {
				userIDMap[user2.UserID] = user2.UserID
				for _, v := range userIDMap {
					if api.DingDingAPI.GetToken() != "" {
						ddmsg.SendUserMessage(dingdingapi.MsgTyeText, v, "您有商家申请授权！", noticeMsg)
					}
				}
			}
		}
	}
	return err
}

func AuditStoreMap(ctx *jxcontext.Context, ID int, vendorOrgCode, vendorStoreID string, auditStatus int, comment string) (err error) {
	var (
		db            = dao.GetDB()
		storeMapAudit = &model.StoreMapAudit{}
	)
	storeMapAudit.ID = ID
	err = dao.GetEntity(db, storeMapAudit)
	if err != nil {
		return err
	}
	retVal, err := GetVendorStore(ctx, storeMapAudit.VendorID, vendorOrgCode, vendorStoreID)
	if err != nil {
		return err
	}
	if retVal == nil {
		return fmt.Errorf("未在平台上查询到此门店，请确认平台账号和平台门店ID！")
	}
	if auditStatus == model.StoreMapAuditStatusPass {
		if storeMapAudit.VendorOrgCode == "" && vendorOrgCode == "" {
			return fmt.Errorf("请选择平台账号进行绑定！")
		}
		if vendorStoreID == "" {
			return fmt.Errorf("请输入绑定的平台门店ID！")
		}
		storeMap := &model.StoreMap{
			VendorStoreID:       vendorStoreID,
			IsSync:              0,
			AutoPickup:          1,
			DeliveryCompetition: 1,
			PricePercentage:     100,
		}
		_, err = AddStoreVendorMap(ctx, db, storeMapAudit.VendorID, vendorOrgCode, storeMapAudit.StoreID, storeMap, false)
	} else {
		if comment == "" {
			fmt.Errorf("审核不通过请输入原因！")
		}
	}
	storeMapAudit.AuditStatus = auditStatus
	storeMapAudit.AuditAt = time.Now()
	storeMapAudit.AuditOperator = ctx.GetUserName()
	dao.UpdateEntity(db, storeMapAudit, "AuditStatus", "AuditAt", "AuditOperator")
	return err
}

func GetStoreMapAudit(ctx *jxcontext.Context, storeIDs, vendorIDs, auditStatuss []int, fromTime, toTime string, offset, pageSize int) (page *model.PagedInfo, err error) {
	return dao.GetStoreMapAudit(dao.GetDB(), storeIDs, vendorIDs, auditStatuss, utils.Str2Time(fromTime), utils.Str2Time(toTime), offset, pageSize)
}

func GetBrandUser(ctx *jxcontext.Context, brandID int) (brandUsers []*dao.GetBrandUserResult, err error) {
	return dao.GetBrandUser(dao.GetDB(), brandID, "")
}

func UpdateBrandUser(ctx *jxcontext.Context, brandID int, userID string, isDel bool) (err error) {
	var (
		db = dao.GetDB()
	)
	brandUsers, err := dao.GetBrandUser(db, brandID, userID)
	if err != nil {
		return err
	}
	if !isDel {
		if len(brandUsers) > 0 {
			return fmt.Errorf("此用户已经绑定了该品牌！")
		}
		brandUser := &model.BrandUser{
			BrandID: brandID,
			UserID:  userID,
		}
		dao.WrapAddIDCULDEntity(brandUser, ctx.GetUserName())
		dao.CreateEntity(db, brandUser)
	} else {
		if len(brandUsers) == 0 {
			return fmt.Errorf("此用户没有绑定该品牌！")
		}
		brandUser := brandUsers[0].BrandUser
		brandUser.DeletedAt = time.Now()
		dao.UpdateEntity(db, &brandUser, "DeletedAt")
	}
	return err
}

func UpdateOrCreateCourierStoresByBrand(ctx *jxcontext.Context, brandID, vendorID int) (hint string, err error) {
	var (
		db = dao.GetDB()
	)
	stores, _ := dao.GetStoreList(db, nil, nil, nil, []int{brandID}, nil, "")
	task := tasksch.NewParallelTask("UpdateOrCreateCourierStoresByBrand", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			var resultList []interface{}
			store := batchItemList[0].(*model.Store)
			errList := errlist.New()
			v := partner.GetDeliveryPlatformFromVendorID(vendorID)
			if v.Use4CreateWaybill {
				if _, ok := v.Handler.(partner.IDeliveryUpdateStoreHandler); ok {
					storeDetail, err2 := dao.GetStoreDetail2(db, store.ID, "", vendorID)
					if err = err2; err2 == nil {
						isNeedAdd := storeDetail.VendorStoreID == ""
						if isNeedAdd {
							storeDetail.VendorID = vendorID
							storeDetail.VendorStoreID = utils.Int2Str(storeDetail.ID)
						}
						if _, err = updateOrCreateCourierStore(ctx, storeDetail); err == nil && isNeedAdd {
							storeCourier := &model.StoreCourierMap{
								VendorStoreID:   storeDetail.VendorStoreID,
								Status:          model.StoreStatusOpened,
								AuditStatus:     storeDetail.AuditStatus,
								VendorStoreName: storeDetail.Name,
							}
							if storeDetail.AuditStatus != model.StoreAuditStatusOnline {
								storeCourier.Status = model.StoreStatusDisabled
							}
							if _, err = addStoreCourierMap(ctx, db, storeDetail.ID, storeDetail.VendorID, storeCourier, false); err == nil {
								resultList = append(resultList, 1)
							}
						}
						errList.AddErr(err)
					}
				}
			}
			return resultList, errList.GetErrListAsOne()
		}, stores)
	tasksch.HandleTask(task, nil, true).Run()
	hint = task.ID
	return hint, err
}

//StoreStatusDisabled = -2
//StoreStatusClosed   = -1
//StoreStatusHaveRest = 0
//StoreStatusOpened   = 1
// 定时任务更新门店信息,添加storeStatuss参数,只查询和同步营业的门店 1营业0临时休息-1休息-2禁用
func RefreshStoreIsOnline(ctx *jxcontext.Context) (err error) {
	var (
		db = dao.GetDB()
	)

	stores, _ := dao.GetStoresMapList(db, []int{model.VendorIDMTWM, model.VendorIDJD, model.VendorIDEBAI}, nil, []int{model.StoreStatusOpened, model.StoreStatusHaveRest}, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	task := tasksch.NewParallelTask("RefreshStoreBind", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeMap := batchItemList[0].(*model.StoreMap)
			if handler := CurVendorSync.GetStoreHandler(storeMap.VendorID); handler != nil {
				if store, err := handler.ReadStore(ctx, storeMap.VendorOrgCode, storeMap.VendorStoreID, ""); err == nil && store != nil {
					if store.Status != model.StoreStatusDisabled {
						storeMap.IsOnline = 1
					} else {
						storeMap.IsOnline = -1
						storeMap.Status = model.StoreStatusClosed
					}
					dao.UpdateEntity(db, storeMap, "IsOnline", "Status")
					store := fmt.Sprintf("门店id:%d,门店名称:%s,第三方门店状态:%d,本地门店修改后状态:%d,第三方平台Id(美团,饿了么...):%d", store.Store.ID, store.Store.Name, store.Store.Status, storeMap.IsOnline, storeMap.VendorID)
					event.AddOperateEvent(ctx, ctx.GetTrackInfo(), store, "", "", 10, "UpdateStore")
				}
			}
			return retVal, err
		}, stores)
	tasksch.HandleTask(task, nil, true).Run()
	task.GetID()
	return err
}

func GetBrandCategoryMap(ctx *jxcontext.Context, parentID, level int, brandID int) (brandCatMaps []*model.BrandCategoryMap, err error) {
	db := dao.GetDB()
	brandCatMaps, err = dao.GetBrandCategoryMap(db, parentID, level, brandID, 0)
	if err != nil {
		return nil, err
	}
	if ctx.GetLoginType() != weixin.AuthTypeMP && ctx.GetLoginType() != weixin.AuthTypeMini && ctx.GetLoginType() != weixin.AuthTypeWxApp && ctx.GetLoginType() != weixin.AuthTypeWxAppCaishi && ctx.GetLoginType() != auth2.AuthTypeMobile {
		return brandCatMaps, err
	}
	return brandCatMaps, err
}

func AddBrandCategoryMap(ctx *jxcontext.Context, brandCategoryMap *model.BrandCategoryMap) (result *model.BrandCategoryMap, err error) {
	var (
		db = dao.GetDB()
	)
	if brandCategoryMap.Level != 1 {
		brandCatMaps, _ := dao.GetBrandCategoryMap(db, -1, 0, brandCategoryMap.BrandID, brandCategoryMap.CategoryID)
		if len(brandCatMaps) > 0 {
			return nil, fmt.Errorf("已存在绑定的京西分类，分类名：[%v]", brandCatMaps[0].BrandCategoryName)
		}
	}
	brandCategoryMap.BrandCategoryName = strings.Trim(brandCategoryMap.BrandCategoryName, " ")
	dao.WrapAddIDCULDEntity(brandCategoryMap, ctx.GetUserName())
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if err = dao.CreateEntity(db, brandCategoryMap); err != nil {
		dao.Rollback(db, txDB)
		return nil, err
	}
	dao.Commit(db, txDB)
	result = brandCategoryMap
	if brandCategoryMap.CategoryID != 0 {
		var storeIDList []int
		if stores, _ := dao.GetStoreList(db, nil, nil, nil, []int{brandCategoryMap.BrandID}, nil, ""); len(stores) > 0 {
			for _, v := range stores {
				storeIDList = append(storeIDList, v.ID)
			}
			if len(storeIDList) > 0 {
				SetStoreCategorySyncStatus2(db, storeIDList, []int{brandCategoryMap.CategoryID}, model.SyncFlagModifiedMask)
			}
		}
	}
	// _, err = CurVendorSync.SyncCategory(ctx, nil, cat.ID, false, userName)
	return result, err
}

func UpdateBrandCategoryMap(ctx *jxcontext.Context, ID int, brandCategoryMap *model.BrandCategoryMap, isDelete bool) (num int64, err error) {
	var (
		db                = dao.GetDB()
		valid             = make(map[string]interface{})
		brandCategoryMap2 = &model.BrandCategoryMap{}
		storeIDList       []int
	)
	brandCategoryMap2.ID = ID
	if err = dao.GetEntity(db, brandCategoryMap2); err != nil {
		return 0, err
	}
	if brandCategoryMap.BrandCategoryName != "" {
		valid["brandCategoryName"] = brandCategoryMap.BrandCategoryName
	}
	if brandCategoryMap.CategoryID != 0 {
		valid["categoryID"] = brandCategoryMap.CategoryID
		if !isDelete {
			brandCatMaps, _ := dao.GetBrandCategoryMap(db, -1, 0, brandCategoryMap2.BrandID, brandCategoryMap.CategoryID)
			if len(brandCatMaps) > 0 {
				for _, v := range brandCatMaps {
					if v.ID != ID {
						return 0, fmt.Errorf("已存在绑定的京西分类，分类名：[%v]", brandCatMaps[0].BrandCategoryName)
					}
				}
			}
		}
	}
	if brandCategoryMap.Level != 0 {
		valid["level"] = brandCategoryMap.Level
		if brandCategoryMap.Level == 2 {
			cat2, _ := dao.GetCategories(db, -1, 0, []int{brandCategoryMap.CategoryID}, false)
			if len(cat2) > 0 {
				if cat2[0].ParentID != brandCategoryMap.ParentID {
					return 0, fmt.Errorf("此二级分类只能绑定到对应一级分类下！")

				}
			}
		}
	}
	if stores, _ := dao.GetStoreList(db, nil, nil, nil, []int{brandCategoryMap.BrandID}, nil, ""); len(stores) > 0 {
		for _, v := range stores {
			storeIDList = append(storeIDList, v.ID)
		}
	}
	if isDelete {
		valid["deletedAt"] = time.Now()
		valid["updatedAt"] = time.Now()
		valid["lastOperator"] = ctx.GetUserName()
		//如果是1级分类则删除下面的子分类
		var catIDs []int
		if brandCategoryMap2.Level == 1 {
			brandCatMaps, _ := dao.GetBrandCategoryMap(db, brandCategoryMap2.CategoryID, 2, brandCategoryMap2.BrandID, 0)
			if len(brandCatMaps) > 0 {
				for _, v := range brandCatMaps {
					catIDs = append(catIDs, v.CategoryID)
					v.DeletedAt = time.Now()
					v.LastOperator = ctx.GetUserName()
					dao.UpdateEntity(db, v, "DeletedAt", "LastOperator")
				}
			}
		}
		catIDs = append(catIDs, brandCategoryMap.CategoryID)
		if len(storeIDList) > 0 {
			SetStoreCategorySyncStatus2(db, storeIDList, catIDs, model.SyncFlagModifiedMask)
		}
	} else {
		if len(storeIDList) > 0 {
			SetStoreCategorySyncStatus2(db, storeIDList, []int{brandCategoryMap.CategoryID, brandCategoryMap2.CategoryID}, model.SyncFlagModifiedMask)
		}
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	if num, err = dao.UpdateEntityLogically(db, brandCategoryMap2, valid, ctx.GetUserName(), nil); err != nil {
		dao.Rollback(db, txDB)
		return 0, err
	}
	dao.Commit(db, txDB)
	return num, err
}

func ReorderBrandCategories(ctx *jxcontext.Context, parentID, brandID int, categoryIDs []int) (err error) {
	var (
		brandCatsMap []*model.BrandCategoryMap
		storeIDList  []int
	)
	db := dao.GetDB()
	brandCatsMap, err = dao.GetBrandCategoryMap(db, parentID, 0, brandID, 0)
	catsLen := len(brandCatsMap)
	if catsLen != len(categoryIDs) {
		return ErrInputCatsDoesntMatch
	}
	catsMap := make(map[int]*model.BrandCategoryMap, catsLen)
	for _, cat := range brandCatsMap {
		catsMap[cat.CategoryID] = cat
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	for k, v := range categoryIDs {
		if catsMap[v] == nil {
			dao.Rollback(db, txDB)
			return fmt.Errorf("分类:%d不在%d分类下", v, parentID)
		}
		catsMap[v].BrandCategorySeq = k
		catsMap[v].LastOperator = ctx.GetUserName()
		if _, err = dao.UpdateEntity(db, catsMap[v]); err != nil {
			dao.Rollback(db, txDB)
			return err
		}
	}
	dao.Commit(db, txDB)
	if stores, _ := dao.GetStoreList(db, nil, nil, nil, []int{brandID}, nil, ""); len(stores) > 0 {
		for _, v := range stores {
			storeIDList = append(storeIDList, v.ID)
		}
	}
	if len(storeIDList) > 0 {
		SetStoreCategorySyncStatus2(db, storeIDList, categoryIDs, model.SyncFlagModifiedMask)
	}
	if err == nil {
		CurVendorSync.SyncStoresCategory(ctx, db, nil, storeIDList, false, true, true)
	}
	return err
}

func InsertBrandCategories(ctx *jxcontext.Context, brandID int) (err error) {
	var (
		db = dao.GetDB()
	)
	if err = dao.DeleteBrandCategroies2(db, brandID); err == nil {
		err = dao.InsertBrandCategories(db, ctx.GetUserName(), brandID)
	}
	return err
}

func UpdateStoreTemplate(ctx *jxcontext.Context, storeID int, content, sound string) (err error) {
	var (
		db    = dao.GetDB()
		store = &model.Store{}
	)
	store.ID = storeID
	dao.GetEntity(db, store)
	configs, _ := dao.QueryConfigs(db, "jxPrintTemplate", model.ConfigTypeSys, "")
	sysTemp := configs[0].Value
	if strings.EqualFold(content, sysTemp) {
		store.PrinterTemplate = ""
	} else {
		store.PrinterTemplate = content
	}
	if !strings.EqualFold(sound, store.PrinterSound) {
		store.PrinterSound = sound
	}
	_, err = dao.UpdateEntity(db, store, "PrinterTemplate", "PrinterSound")
	return err
}

//条件过滤 B2B、物料店
func FilterByB2B(locationList []*common.Store4User) (retVal []*common.Store4User, errCode string, err error) {
	//userAuth, err := auth2.GetTokenInfo(token)
	//	//if err != nil {
	//	//	return nil, "", err
	//	//}
	//	//user, total, err := dao.GetUsers(dao.GetDB(), 1, "", []string{userAuth.UserID}, nil, nil, 0, 1)
	//	//if err != nil {
	//	//	return nil, "", err
	//	//}
	// 获取位置附近门店列表
	storeIDs := make([]int, 0, len(locationList))
	for _, v := range locationList {
		storeIDs = append(storeIDs, v.ID)
	}
	// 判断门店是不是b2b门店，如果是，用户必须为系统管理员（门店老板和运营人员）
	store, err := dao.GetStoreList(dao.GetDB(), storeIDs, nil, nil, nil, nil, "")
	if err != nil {
		return nil, "", err
	}

	isMatterStore := false
	for _, v := range storeIDs {
		if v == model.MatterStoreID {
			isMatterStore = true
		}
	}

	result := make([]*common.Store4User, 0, 0)
	for _, v := range store {
		for _, s := range locationList {
			if v.ID == s.ID {
				//if (v.BrandID == model.B2BNumberId || isMatterStore) && user[0].Type == model.YES { // 普通用户进入物料店和b2b店
				if v.BrandID == model.B2BNumberId || isMatterStore { // 普通用户进入物料店和b2b店
					continue
				} else {
					result = append(result, s)
				}
			}
		}
	}

	//if total != model.YES {
	//	return result, "", errors.New("")
	//}
	//if user[0].Type != model.YES {
	//	return locationList, "", nil
	//} else {
	return result, "", err
	//}
}

//首页信息展示
func GetHomePageByLocation(ctx *jxcontext.Context, lng, lat float64, needWalkDistance bool, brandId []int) (interface{}, string, error) {
	//获取门店信息
	var (
		storeID             []int
		storeScore          = 0
		storeInfos          []StoreInfo
		storeDeductionInfo  StoreDeductionInfo
		storeDeductionInfos []StoreDeductionInfo
		storeSkuInfo        StoreSkuInfo
		StoreSkuInfos       []StoreSkuInfo
		homePages           []HomePageInfos
	)
	locationList, err := common.GetStoreListByLocation(ctx, lng, lat, 50000, needWalkDistance, false, brandId)
	if err != nil {
		return nil, "经纬度获取推荐门店失败", err
	}
	//权限过滤门店
	//filterStore, _, err := FilterByB2B(locationList, token)
	filterStore, _, err := FilterByB2B(locationList)
	if err != nil {
		return nil, "权限过滤门店失败", err
	}
	for _, v := range filterStore {
		storeID = append(storeID, v.ID)
	}
	//获取热销商品
	topSkusInfo, err := GetTopSkusByStoreIDs(ctx, storeID)
	if err != nil {
		return nil, "获取热销商品失败", err
	}
	for _, j := range topSkusInfo {
		storeSkuInfo = StoreSkuInfo{
			StoreID:    j.StoreID,
			SkuID:      j.SkuID,
			SkuName:    j.Name,
			SkuStatus:  j.Status,
			BestSeller: j.BestSeller,
			Img:        j.Img,
			Price:      j.Price,
			Unit:       j.Unit,
		}
		StoreSkuInfos = append(StoreSkuInfos, storeSkuInfo)
	}

	for _, v := range filterStore {
		//获取门店品牌信息
		brandInfos, err := dao.GetStoreBrandInfos(v.ID)
		if err != nil {
			return nil, "获取门店品牌信息失败", err
		}
		//获取门店每周评分
		if scores, err := GetWeeklyStoreScore(v.ID, 0); err == nil && len(scores) > 0 {
			storeScore = scores[0].TotalScore
		}

		storeInfo := StoreInfo{
			StoreID:          v.ID,
			StoreName:        v.Name,
			OpenTime1:        v.OpenTime1,
			CloseTime1:       v.CloseTime1,
			OpenTime2:        v.OpenTime2,
			CloseTime2:       v.CloseTime2,
			Status:           v.Status,
			Distance:         v.Distance,
			Address:          v.Address,
			BrandName:        brandInfos.Name,
			BrandLogo:        brandInfos.Logo,
			StoreWeeklyScore: storeScore,
			DeliverySelf:     v.DeliverySelf,
		}
		storeInfos = append(storeInfos, storeInfo)
		//获取门店减免策略
		storePolicy, err := GetStoreVendorMaps(ctx, dao.GetDB(), v.ID, 9)
		if err != nil {
			return nil, "获取门店减免策略失败", err
		}
		for _, k := range storePolicy {
			if k.StoreID == v.ID {
				storeDeductionInfo = StoreDeductionInfo{
					StoreID:                  k.StoreID,
					DeliveryFeeDeductionSill: k.DeliveryFeeDeductionSill,
					DeliveryFeeDeductionFee:  k.DeliveryFeeDeductionFee,
				}
			}
			storeDeductionInfos = append(storeDeductionInfos, storeDeductionInfo)
		}
		//}

	}

	for _, i := range storeInfos {
		homePage := HomePageInfos{}
		homePage.StoreInfo = i
		for _, j := range storeDeductionInfos {
			if i.StoreID == j.StoreID {
				homePage.StoreDeductionInfo = append(homePage.StoreDeductionInfo, j)
			} else {
				continue
			}
		}
		for _, k := range StoreSkuInfos {
			if i.StoreID == k.StoreID {
				homePage.StoreSkuInfo = append(homePage.StoreSkuInfo, k)
			} else {
				continue
			}
		}
		homePages = append(homePages, homePage)
	}
	// todo 明天测试西南交大店首页没商品
	return homePages, "", nil
}

// 抖店定时任务 主动拉取门店审核状态信息并更新
func UpdateStorePoiStatus(ctx *jxcontext.Context) error {
	var (
		db = dao.GetDB()
	)
	storeList, _ := dao.GetStoresMapList(db, []int{model.VendorIDDD}, nil, []int{model.StoreStatusOpened, model.StoreStatusHaveRest}, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "")
	task := tasksch.NewParallelTask("UpdateDDStorePoiAuditInfo", tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
			storeMap := batchItemList[0].(*model.StoreMap)
			if handler := CurVendorSync.GetStoreHandler(storeMap.VendorID); handler != nil {
				if storePoiStatus, err := handler.GetStoreStatus(ctx, storeMap.VendorOrgCode, storeMap.StoreID, storeMap.VendorStoreID); err != nil {
					return nil, err
				} else {
					if storePoiStatus == tiktok_api.PoiAuditStateSuccessBind {
						storeMap.AuditStatus = model.StoreAuditStatusCreated
					} else if storePoiStatus == tiktok_api.PoiAuditStateReject {
						storeMap.AuditStatus = model.StoreAuditStatusRejected
					}
					dao.UpdateEntity(db, storeMap, "AuditStatus")
					store := fmt.Sprintf("门店ID:%d,门店名称:%s,抖店平台审核状态:%d,抖店平台门店ID:%s", storeMap.StoreID, storeMap.StoreName, storeMap.AuditStatus, storeMap.VendorStoreID)
					event.AddOperateEvent(ctx, ctx.GetTrackInfo(), store, "", "", 10, "UpdateStore")
				}
			}
			return retVal, err
		}, storeList)
	tasksch.HandleTask(task, nil, true).Run()
	task.GetID()
	return nil
}

//抖音定时任务
func UpdateStoreRelInformation(ctx *jxcontext.Context) error {
	var (
		tempRelIDs      map[int64]string
		db              = dao.GetDB()
		errList         = errlist.New()
		tempIDs         map[string]map[int64]string
		FreightTemplate = &model.FreightTemplate{}
	)
	if vendorOrgCodes, err := dao.GetVendorOrgCode(db, model.VendorIDDD, "", "platform"); err != nil {
		return fmt.Errorf("获取vendorOrgCodes失败: %v", err)
	} else {
		for _, v := range vendorOrgCodes {
			tempRelIDs, err = tiktok_store.GetAllVendorStoreMap(ctx, v.VendorOrgCode) //平台与京西门店映射关系
			if err != nil {
				return err
			}
			tempIDs[v.VendorOrgCode] = tempRelIDs
		}
		for i, j := range tempIDs {
			for m, n := range j { //m-vendorStoreID n-storeID
				subTask := tasksch.NewSeqTask(fmt.Sprintf("UpdateStoreRelInformation 平台门店(%d) 本地门店(%s):同步本地相关信息", m, n), ctx, func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
					switch step {
					case 0: //(1)仓库以及电子围栏
						if bindWarehouse, err := tiktok_store.GetWarehouseByStore(i, m); err != nil {
							errList.AddErr(fmt.Errorf("同步任务: 获取门店(%s)绑定的仓库以及电子围栏失败:%v", n, err))
						} else if len(bindWarehouse[i][0].OutFenceIds) > 0 {
							for _, s := range bindWarehouse[i][0].OutFenceIds {
								if s == n {
									FreightTemplate.FenceID = s
								}
							}
						} else {
							FreightTemplate.WarehouseID = bindWarehouse[i][0].WarehouseId
						}
					case 1: //(2)运费模板
						if bindFreightIDs, err := tiktok_store.GetStoreFreight(i, m); err != nil {
							globals.SugarLogger.Debugf("同步任务: 获取门店(%s) 绑定运费模板出错:%v", n, err)
							errList.AddErr(fmt.Errorf("同步任务: 获取门店(%s) 绑定运费模板出错:%v", n, err))
						} else {
							FreightTemplate.TemplateID = bindFreightIDs
						}
					case 2: //(3)限售模板
						if bindTradeLimitID, err := tiktok_store.GetStoreSaleLimit(i, m); err != nil {
							globals.SugarLogger.Debugf("同步任务: 获取门店(%s) 限售模板出错:%v", n, err)
							errList.AddErr(fmt.Errorf("同步任务: 获取门店(%s) 限售模板出错:%v", n, err))
						} else {
							FreightTemplate.TradeLimitID = bindTradeLimitID
						}
					}

					return nil, nil
				}, 4)
				tasksch.ManageTask(subTask).Run()
				//time.Sleep(2 * time.Second)
				FreightTemplate.VendorStoreID = utils.Int64ToStr(m)
				FreightTemplate.StoreID = utils.Str2Int(n)
				if err := dao.ReplaceInsertFreight(FreightTemplate); err != nil {
					if err1 := dao.InsertItemFreight(FreightTemplate); err1 != nil {
						globals.SugarLogger.Debug("UpdateStoreRelInformation 同步数据库错误信息:%v", err1)
					}
				}
				//if _, err := dao.UpdateEntity(db, FreightTemplate, "VendorStoreID", "StoreID", "TemplateID", "WarehouseID", "FenceID", "TradeLimitID"); err != nil {
				//	if err1 := dao.CreateEntity(db, FreightTemplate); dao.IsDuplicateError(err1) || err1 != nil {
				//		errList.AddErr(fmt.Errorf("创建操作，同步进数据库错误信息:%v", err1))
				//	} else {
				//		errList.AddErr(fmt.Errorf("更新操作，同步进数据库错误信息:%v", err))
				//	}
				//}
			}
		}
	}
	lastErr := errList.GetErrListAsOne()
	if lastErr != nil {
		globals.SugarLogger.Debugf("定时任务同步抖店门店信息到本地 相关提示:%s", lastErr)
		//return lastErr
	}
	return nil
}

//获取抖店门店建议范围 （即电子围栏）
func GetDDScope(ctx *jxcontext.Context, storeID int, vendorStoreID string) ([]warehouse_getFences_response.FencesItem, error) {
	if storeID == 0 || len(vendorStoreID) == 0 {
		return nil, errors.New("storeID,vendorStoreID 必填")
	}
	if storeMap, err := GetVendorOrgCode(ctx, dao.GetDB(), storeID, model.VendorIDDD, vendorStoreID); err != nil {
		return nil, errors.New(fmt.Sprintf("获取vendorOrgCode失败，请检查数据或重试:%v", err))
	} else {
		if bind, err := tiktok_store.GetWarehouseByStore(storeMap[0].VendorOrgCode, utils.Str2Int64(vendorStoreID)); err != nil {
			return nil, errors.New(fmt.Sprintf("获取门店(%d) 仓库以及电子围栏信息失败:%v", storeID, err))
		} else {
			if len(bind) != 0 && len(bind[vendorStoreID][0].OutFenceIds) != 0 {
				if resp, err := tiktok_store.GetFence(storeMap[0].VendorOrgCode, bind[vendorStoreID][0].OutFenceIds); err != nil {
					return nil, errors.New(fmt.Sprintf("获取门店(%d) 电子围栏失败:%v", storeID, err))
				} else {
					return resp, nil
				}
			}
		}
	}
	return nil, nil
}

//获取 VendorOrgCode
func GetVendorOrgCode(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID int, vendorStoreID string) (storeMap []*model.StoreMap, err error) {
	cond := map[string]interface{}{
		model.FieldStoreID:       storeID,
		model.FieldVendorStoreID: vendorStoreID,
	}
	if vendorID != -1 {
		cond[model.FieldVendorID] = vendorID
	}
	return storeMap, dao.GetEntitiesByKV(db, &storeMap, cond, false)
}

//批量辅助创建电子围栏
func AssistCreateFence(relInfo map[string][]tiktok_store.RelInfo) (string, error) {
	var (
		tempFenceID string
		tStr1       = ""
		tStr2       = ""
		endStr      = ""
		db          = dao.GetDB()
		errList     = errlist.New()
	)
	for k, v := range relInfo {
		for _, i := range v {
			FreightTemplate := &model.FreightTemplate{
				StoreID:       utils.Str2Int(i.StoreID),
				VendorStoreID: i.VendorStoreID,
			}
			tempStoreID := utils.Str2Int64(i.StoreID)
			tempVendorStoreID := utils.Str2Int64(i.VendorStoreID)
			//预处理门店无配送范围情况
			if localStore, err := dao.GetStoreDetail(db, utils.Str2Int(i.StoreID), model.VendorIDDD, k); err == nil {
				if localStore.DeliveryRangeType == tiktok_store.LocalShapeCircular && utils.Str2Int(localStore.DeliveryRange) <= 0 {
					tStr1 += i.StoreID + ","
				} else if localStore.DeliveryRangeType == tiktok_store.LocalShapePolygon {
					tempStr := strings.Split(localStore.DeliveryRange, ";")
					if len(tempStr) < 3 {
						tStr2 += i.StoreID + ","
					}
				}
				endStr = tStr1 + "," + tStr2
				if len(endStr) > 0 {
					globals.SugarLogger.Debugf("京西平台没有配送范围的门店ID=%s", endStr)
				}
			}
			if bindFence, err := tiktok_store.GetWarehouseByStore(k, utils.Str2Int64(i.VendorStoreID)); err == nil && (len(bindFence) > 0 && len(bindFence[i.VendorStoreID][0].OutFenceIds) > 0) {
				tempFenceID = bindFence[i.VendorStoreID][0].OutFenceIds[0]
				if err1 := tiktok_store.UpdateFenceByStore(k, tempFenceID, i.StoreID); err1 != nil {
					errList.AddErr(fmt.Errorf("平台门店(%s),门店(%s)更新电子围栏失败:%v", i.VendorStoreID, i.StoreID, err1))
				} else {
					FreightTemplate.FenceID = tempFenceID
				}
			} else {
				if fenceID, err := tiktok_store.CreateFenceByStore(k, tempStoreID); err != nil {
					if strings.Contains(err.Error(), "该电子围栏已经存在，请使用修改接口") { //兜底处理
						if err1 := tiktok_store.BindFenceByStore(k, tempVendorStoreID, []string{i.StoreID}); err1 == nil {
							if err2 := tiktok_store.UpdateFenceByStore(k, i.StoreID, i.StoreID); err2 != nil {
								errList.AddErr(fmt.Errorf("平台门店(%s),门店(%s)更新电子围栏失败:%v", i.VendorStoreID, i.StoreID, err1))
							} else {
								FreightTemplate.FenceID = i.StoreID
							}
						}
						errList.AddErr(fmt.Errorf("平台门店(%s),门店(%s)创建电子围栏失败:%v", i.VendorStoreID, i.StoreID, err))
					}
				} else if len(fenceID) > 0 {
					if err := tiktok_store.BindFenceByStore(k, tempVendorStoreID, []string{i.StoreID}); err != nil {
						errList.AddErr(fmt.Errorf("平台门店(%s),门店(%s)绑定电子围栏失败:%v", i.VendorStoreID, i.StoreID, err))
					} else {
						FreightTemplate.FenceID = tempFenceID
					}
				}
			}
			if err := dao.ReplaceInsertFreight(FreightTemplate); err != nil {
				if err1 := dao.InsertItemFreight(FreightTemplate); err1 != nil {
					globals.SugarLogger.Debug("同步数据库错误信息:%v", err1)
				}
			}
		}

		//if err := dao.CreateEntity(db, FreightTemplate); dao.IsDuplicateError(err) || err != nil {
		//	if _, err1 := dao.UpdateEntity(db, FreightTemplate, "FenceID"); err1 != nil {
		//		globals.SugarLogger.Debugf("db update storeID,vendorStoreID defeat on :%v", err1)
		//		//errList.AddErr(fmt.Errorf("更新操作，同步进数据库错误信息:%v", err1))
		//	} else {
		//		globals.SugarLogger.Debugf("db create storeID,vendorStoreID defeat on :%v", err1)
		//		//errList.AddErr(fmt.Errorf("创建操作，同步进数据库错误信息:%v", err))
		//	}
		//}
	}
	if errList.GetErrListAsOne() != nil {
		if len(endStr) > 0 {
			return fmt.Sprintf("批量创建电子围栏错误，请根据提示处理:%v,#门店%s的配送范围不合法，请处理后再进行创建#", errList.GetErrListAsOne(), endStr), nil
		} else {
			return fmt.Sprintf("批量创建电子围栏错误，请根据提示处理:%v", errList.GetErrListAsOne()), nil
		}
	}
	return "", nil
}

//批量创建包邮运费模板
func BatchCreateFreeShipTemplate(relInfo map[string][]tiktok_store.RelInfo, shipFee int64) (string, error) {
	errList := errlist.New()
	for k, v := range relInfo {
		for _, i := range v {
			if bindFreightID, err := tiktok_store.GetStoreFreight(k, utils.Str2Int64(i.VendorStoreID)); err != nil || bindFreightID == 0 {
				if err := tiktok_store.CreateBindFreeShipTemplate(k, utils.Str2Int(i.StoreID), shipFee); err != nil {
					errList.AddErr(err)
				}
			} else {
				_ = tiktok_store.ShopBindStoreFreight(k, utils.Str2Int64(i.VendorStoreID), bindFreightID)
				globals.SugarLogger.Debugf("进入更新包邮模板操作")
				if err := tiktok_store.UpdateFreeShipTemplate(k, utils.Str2Int(i.StoreID), shipFee); err != nil {
					errList.AddErr(err)
				}
			}
		}
	}
	if errList.GetErrListAsOne() != nil {
		return fmt.Sprintf("%v", errList.GetErrListAsOne()), nil
	}
	return "", nil
}

//批量配置抖音门店自动呼叫运力
func SetStoreAutoCallRider(vendorOrgCode string, openIDs, closeIDs []int64) (string, error) {
	var errList = errlist.New()
	if len(openIDs) > 0 {
		for _, i := range openIDs {
			if err := tiktok_store.SetStoreAutoCallRider(vendorOrgCode, tiktok_api.AutoCallRiderOpen, i); err != nil {
				errList.AddErr(fmt.Errorf("%d err:%v", i, err))
			}
		}
	}
	if len(closeIDs) > 0 {
		for _, j := range closeIDs {
			if err := tiktok_store.SetStoreAutoCallRider(vendorOrgCode, tiktok_api.AutoCallRiderClose, j); err != nil {
				errList.AddErr(fmt.Errorf("%d err:%v", j, err))
			}
		}
	}
	if errList.GetErrListAsOne() != nil {
		return fmt.Sprintf("部分门店配置自动运力失败:%s", utils.Format4Output(errList.GetErrListAsOne(), false)), nil
	}
	return "", nil
}

// CreateDDWarehouse 创建抖音门店区域仓
func CreateDDWarehouse(vendorOrgCode string, vendorStoreID, storeID int64) error {
	var errList errlist.ErrList
	bind, err := tiktok_store.GetWarehouseByStore(vendorOrgCode, vendorStoreID)
	globals.SugarLogger.Debugf("CreateDDWarehouse bind=%s", utils.Format4Output(bind, false))
	if err == nil && bind[utils.Int64ToStr(vendorStoreID)][0].WarehouseId > 0 {
		return nil
	}
	warehouseID, err1 := tiktok_store.CreateWarehouse(vendorOrgCode, storeID)
	if err1 != nil || warehouseID == 0 {
		errList.AddErr(fmt.Errorf("门店(%d) 创建仓库:%v", storeID, err1))
		//globals.SugarLogger.Debugf("CreateDDWarehouse hint=%v", fmt.Errorf("门店(%d) 创建仓库失败:%v", storeID, err1))

	}
	err1 = tiktok_store.BindStoreWarehouse(vendorOrgCode, utils.Int64ToStr(storeID), vendorStoreID)
	if err1 != nil { //仓库绑定通过自定义外部ID
		errList.AddErr(fmt.Errorf("门店(%d) 绑定仓库:%v", storeID, err1))
	}
	return errList.GetErrListAsOne()
}

type MtRelInfo struct {
	PoiCode string `json:"poi_code"`
	PicUrl  string `json:"pic_url"`
	Address string `json:"address"`
}

//[]{PoiCode:"16594433",PicUrl:"http://p0.meituan.net/business/e23d337029fcf74f5a7bcea4e01dac98219292.jpg"}
//批量获取美团门店logos
func BatchGetMTStoreLogos(vendorOrgCode string, mtStoreIDs []string) (storeLogos []MtRelInfo) {
	for _, v := range mtStoreIDs {
		if storeInfos, err1 := mtwm.GetAPI(vendorOrgCode, 0, "").PoiGet(v); err1 != nil {
			storeLogos = append(storeLogos, MtRelInfo{
				PoiCode: v,
				PicUrl:  " ",
			})
		} else {
			if len(storeInfos.PicURL) != 0 {
				storeLogos = append(storeLogos, MtRelInfo{
					PoiCode: v,
					PicUrl:  storeInfos.PicURL,
				})
			}
		}
	}
	return storeLogos
}

//批量更新美团门店logos
func BatchUpdateMTStoreLogos(vendorOrgCode string, relInfo []MtRelInfo) (hint string) {
	param := map[string]interface{}{}
	if len(relInfo[0].PicUrl) > 0 {
		param["pic_url"] = relInfo[0].PicUrl
	}
	for _, v := range relInfo {
		if len(v.PicUrl) == 0 && len(v.Address) == 0 {
			return "暂无需要更新数据"
		}
		if len(v.Address) > 0 {
			param["address"] = v.Address
		}
		apiObj := mtwm.GetAPI(vendorOrgCode, 0, v.PoiCode)

		remoteStoreInfo, err := apiObj.PoiGet(v.PoiCode)
		if err != nil {
			return fmt.Sprintf("%s:%v\n", v.PoiCode, err)
		}
		// 每个订单的配送费，单位是元。 更新时，如果不传配送费，平台会查询门店当前所有配送范围的配送费取最大的值。如果门店当前没有配送范围或其配送费均为0，则门店配送费取值为0。
		param["shipping_fee"] = remoteStoreInfo.ShippingFee
		if err := apiObj.PoiSave(v.PoiCode, param); err != nil {
			hint += fmt.Sprintf("%s:%v\n", v.PoiCode, err)
		}
	}
	if len(hint) != 0 {
		return hint
	}
	return ""
}

type TaoBindInfo struct {
	StoreID         int    `json:"storeID"`
	VendorStoreID   string `json:"vendorStoreID"`
	VendorStoreName string `json:"vendorStoreName"`
	Status          string `json:"status"` //渠道营业状态
}

var TaoStoreStatus = map[string]int{
	tao_vegetable.StoreOnlineWord:  model.StoreStatusOpened,
	tao_vegetable.StoreOfflineWord: model.StoreStatusClosed,
}

// SingleBindTaoVegetable 单独绑定淘鲜达平台三方映射
func SingleBindTaoVegetable(ctx *jxcontext.Context, bind []TaoBindInfo, vendorOrgCode string) (error, string) {
	var (
		db        *dao.DaoDB
		errList   errlist.ErrList
		errIDName = make([]string, 0)
		storeMaps = make([]*model.StoreMap, 0)
	)
	if len(bind) == 0 {
		return fmt.Errorf("绑定门店数据为空，请检查"), ""
	}
	for _, v := range bind {
		if len(v.VendorStoreID) == 0 || len(v.VendorStoreName) == 0 {
			errIDName = append(errIDName, v.VendorStoreID)
		}
		//else {
		//	if strings.Contains(v.VendorStoreID, "X") {
		//		temp := strings.Split(v.VendorStoreID, "X")
		//		storeID = temp[1]
		//	} else {
		//		return fmt.Errorf("淘鲜达门店ID不合法"), ""
		//	}
		//}
		storeMap := &model.StoreMap{
			StoreID:          v.StoreID,
			VendorID:         model.VendorIDTaoVegetable,
			VendorOrgCode:    vendorOrgCode,
			Status:           TaoStoreStatus[v.Status],
			DeliveryType:     model.StoreDeliveryTypeByStore,
			SyncStatus:       0,
			VendorStoreName:  v.VendorStoreName,
			MtwmToken:        "",
			MtwmRefreshToken: "",
			VendorStoreID:    v.VendorStoreID,
		}
		//跟美团同步 平台调价、调价套餐
		var (
			cond = map[string]interface{}{
				model.FieldStoreID:  v.StoreID,
				model.FieldVendorID: model.VendorIDMTWM,
			}
			tempMaps []*model.StoreMap
		)
		if err := dao.GetEntitiesByKV(db, &tempMaps, cond, false); err == nil && len(tempMaps) > 0 {
			storeMap.PricePercentage = tempMaps[0].PricePercentage
			storeMap.PricePercentagePack = tempMaps[0].PricePercentagePack
		}
		storeMaps = append(storeMaps, storeMap)
	}

	userName := ctx.GetUserName()
	for _, k := range storeMaps {
		dao.WrapAddIDCULDEntity(k, userName)
		if db == nil {
			db = dao.GetDB()
		}
		txDB, _ := dao.Begin(db)
		defer func() {
			if r := recover(); r != nil {
				dao.Rollback(db, txDB)
				panic(r)
			}
		}()

		if err := dao.CreateEntity(db, k); err == nil {
			errList.AddErr(fmt.Errorf("%s:%v", k.VendorStoreID, err))
			dao.Commit(db, txDB)
		} else {
			dao.Rollback(db, txDB)
		}
	}
	if len(errIDName) > 0 {
		errList.AddErr(fmt.Errorf("门店%s Id/名字不合法", errIDName))
	}
	if errList.GetErrListAsOne() != nil {
		return nil, fmt.Sprintf("批量绑定淘鲜达门店错误：%v", errList.GetErrListAsOne())
	}
	return nil, ""
}

// ReplaceInsertFreight 操作 freight_template
func ReplaceInsertFreight(storeID, templateID, warehouseID, tradeLimitID int, vendorStoreID, fenceID string) error {
	return dao.ReplaceInsertFreight2(storeID, templateID, warehouseID, tradeLimitID, vendorStoreID, fenceID)
}

//辅助函数
func String2ArrayInt64(data string) (retVal []int64) {
	temp := strings.Split(data, ",")
	for _, v := range temp {
		retVal = append(retVal, utils.Str2Int64(v))
	}
	return retVal
}

func String2ArrayString(data string) (retVal []string) {
	temp := strings.Split(data, ",")
	for _, v := range temp {
		retVal = append(retVal, v)
	}
	return retVal
}

func GetVendorStoreBind(vendorStoreId string, vendorId int) (int, error) {
	return dao.GetCodeAndIDByMeiTuan(vendorStoreId, vendorId)
}

func BindJxPrintToStore(storeId int64, printSn, printKey string) error {
	return dao.BindJXPrintToStore(storeId, printSn, printKey)
}

var (
	storeScoreFieldName = []string{
		model.FieldStoreOpenTime,
		model.FieldSaleSkuCount,
		model.FieldAveragePickupTime,
		model.FieldBadCommentOrder,
		model.FieldUnfinishOrder,
		model.FieldAbsentGoodsOrder,
		model.FieldPromotionSku,
		model.FieldFullVendor,
		model.FieldStoreRange,
		model.FieldSaleSkuPrice,
	}
)

func GetWeeklyStoreScore(storeID, weekIndexParam int) (outWeeklyStoreScoreDataList []*model.WeeklyStoreScore, err error) {
	db := dao.GetDB()
	storeScoreList, err := dao.GetWeeklyStoreScoreList(db, storeID, 1)
	if err == nil && len(storeScoreList) > 0 {
		weeklyStoreScoreDataList := []*model.WeeklyStoreScore{}
		weekDataList := SplitToSingleWeekDataList(storeScoreList)
		for weekIndex, weekData := range weekDataList {
			weeklyData := &model.WeeklyStoreScore{}
			weeklyData.ID = weekIndex
			weeklyData.ItemTotalScore = 10
			weeklyData.StoreID = storeID
			weeklyStoreScoreDataList = append(weeklyStoreScoreDataList, weeklyData)
			weekDataCount := len(weekData)
			for dayIndex, dayData := range weekData {
				for _, fieldName := range storeScoreFieldName {
					srcFieldValue := refutil.GetObjFieldByName(dayData, fieldName).(int)
					destFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
					refutil.SetObjFieldByName(weeklyData, fieldName, destFieldValue+srcFieldValue)
				}
				if weekDataCount == 1 {
					weeklyData.BeginTime = dayData.ScoreDate
					weeklyData.EndTime = dayData.ScoreDate
				} else {
					if dayIndex == 0 {
						weeklyData.EndTime = dayData.ScoreDate
					} else if dayIndex == weekDataCount-1 {
						weeklyData.BeginTime = dayData.ScoreDate
					}
				}
				weeklyData.StoreName = dayData.StoreName
			}
			for _, fieldName := range storeScoreFieldName {
				destFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
				refutil.SetObjFieldByName(weeklyData, fieldName, int(math.Round(float64(destFieldValue)/float64(weekDataCount))))
			}
			for _, fieldName := range storeScoreFieldName {
				srcFieldValue := refutil.GetObjFieldByName(weeklyData, fieldName).(int)
				destFieldValue := refutil.GetObjFieldByName(weeklyData, model.FieldTotalScore).(int)
				refutil.SetObjFieldByName(weeklyData, model.FieldTotalScore, destFieldValue+srcFieldValue)
			}
			weeklyData.Level = GetStoreScoreLevel(weeklyData.TotalScore)
		}
		if weekIndexParam == -1 {
			outWeeklyStoreScoreDataList = weeklyStoreScoreDataList
		} else {
			//fmt.Println("testss", utils.Format4Output(weeklyStoreScoreDataList, false))
			outWeeklyStoreScoreDataList = []*model.WeeklyStoreScore{weeklyStoreScoreDataList[weekIndexParam]}
		}
	}

	return outWeeklyStoreScoreDataList, err
}
func SplitToSingleWeekDataList(storeScoreList []*model.StoreScoreEx) (weekDataList [][]*model.StoreScoreEx) {
	singleWeekData := []*model.StoreScoreEx{}
	weekIndex := 0
	for _, value := range storeScoreList {
		if weekIndex == 0 {
			weekIndex = Time2Week(value.ScoreDate)
		}
		if weekIndex == Time2Week(value.ScoreDate) {
			singleWeekData = append(singleWeekData, value)
		} else {
			weekDataList = append(weekDataList, singleWeekData)
			singleWeekData = []*model.StoreScoreEx{}
			weekIndex = 0
			singleWeekData = append(singleWeekData, value)
		}
	}
	if len(singleWeekData) > 0 {
		weekDataList = append(weekDataList, singleWeekData)
	}

	return weekDataList
}
func GetStoreScoreLevel(score int) int {
	level := 0
	if score >= 90 {
		level = 1
	} else if score >= 80 {
		level = 2
	} else if score >= 70 {
		level = 3
	}

	return level
}

func Time2Week(t time.Time) int {
	yearDay := t.YearDay()
	yearFirstDay := t.AddDate(0, 0, -yearDay+1)
	firstDayInWeek := int(yearFirstDay.Weekday())

	firstWeekDays := 1
	if firstDayInWeek != 0 {
		firstWeekDays = 7 - firstDayInWeek + 1
	}
	var week int
	if yearDay <= firstWeekDays {
		week = 1
	} else {
		tempWeek := (float64(yearDay) - float64(firstWeekDays)) / float64(7)
		week = int(math.Ceil(tempWeek)) + 1
	}

	return week
}

// GetStoreSettlementList 根据门店统计结算信息
func GetStoreSettlementList(storeId []int, start, end time.Time, vendorId []int, size, offset int, rank string) (*model.PagedInfo, error) {
	return dao.StatisticsSettlement(dao.GetDB(), storeId, start, end, vendorId, size, offset, rank)
}

// GetCityCodeSettlementList 根据城市统计结算
func GetCityCodeSettlementList(cityCode []string, start, end time.Time, vendorId []int, size, offset int, rank string) (*model.PagedInfo, error) {
	return dao.StatisticsSettlementByCity(dao.GetDB(), cityCode, start, end, vendorId, size, offset, rank)
}

func DeleteOrderUploadImg() error {
	var (
		db   = dao.GetDB()
		data = make([]*model.GoodsOrderImg, 0, 0)
	)
	if err := dao.GetRows(db, &data, `SELECT * FROM goods_order_img WHERE created_at <= ? `, time.Now().AddDate(0, 0, -7)); err != nil {
		return err
	}

	for _, v := range data {
		var imgList []string
		if err := jxutils.Strings2Objs(v.OriginalData, &imgList); err != nil {
			return err
		}
		for _, il := range imgList {
			jxutils.DeleteExportContent(strings.Split(il, "/")[3])
		}
		dao.DeleteEntity(db, v, "ID")
	}

	return nil
}
