package cms import ( "bytes" "encoding/json" "errors" "fmt" "io" "math" "mime/multipart" "sort" "strconv" "strings" "time" "unicode/utf8" "git.rosy.net.cn/jx-callback/business/jxstore/permission" "git.rosy.net.cn/jx-callback/business/authz" "git.rosy.net.cn/baseapi/platformapi/yinbaoapi" "git.rosy.net.cn/jx-callback/globals/refutil" "github.com/360EntSecGroup-Skylar/excelize" "git.rosy.net.cn/baseapi" "git.rosy.net.cn/jx-callback/business/authz/autils" "git.rosy.net.cn/jx-callback/business/jxcallback/orderman" "git.rosy.net.cn/baseapi/platformapi/autonavi" "git.rosy.net.cn/baseapi/platformapi/baidunavi" "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/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 //获取已解决评论的标志 ) 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"` 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"` 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 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, } 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", } roleMap = map[string]string{ // "marketManPhone": "市场负责人电话", // "marketManRole": "市场负责人组(角色,单人)", "jxBrandFeeFactor": "京西品牌费因子", "marketAddFeeFactor": "市场附加费因子", "payeeName": "收款人姓名", "payeeAccountNo": "收款账号", "payeeBankBranchName": "开户支行", "payeeBankCode": "开户行代码", "payPercentage": "支付比例", } roleMoblieMap = map[string]string{ "17380734342": "17380734342", //漆云的手机 ,用于判断updatestore的权限 "18328080405": "18328080405", //肖娜娜的手机 "13350726500": "13350726500", //谭翔心 "15928865396": "15928865396", //何佳梦 "18048531223": "18048531223", //石老板 "18982250714": "18982250714", //赵敏夫 "13708196093": "13708196093", //顾子航 "15680070110": "15680070110", //李荣维 "15208271238": "15208271238", } marketManPhoneRoleMap = map[string]string{ "marketManPhone": "市场负责人电话", } marketManPhoneRoleMoblieMap = map[string]string{ "13684045763": "13684045763", "18160030913": "18160030913", "18048531223": "18048531223", "18328080405": "18328080405", "17380734342": "17380734342", "15208271238": "15208271238", "18583684218": "18583684218", "18982250714": "18982250714", "13708196093": "13708196093", } ) 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, } 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" } } } 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 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["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 = 100" } 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) } } 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) (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 } 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.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) } } 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"])) sql, sqlParams, _, _, err := getStoresSql(ctx, keyword, params, orderTimeFrom, orderTimeTo) if err != nil { return nil, err } sql = ` SELECT 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 ` db := dao.GetDB() retVal = &StoresInfo{} var storeIDs []int var storeList []*StoreExt offset = jxutils.FormalizePageOffset(offset) pageSize = jxutils.FormalizePageSize(pageSize) mapLimit := false // globals.SugarLogger.Debug(sql) // globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false)) if err = dao.GetRows(db, &storeList, sql, sqlParams...); err == nil { // 地图区域限制过滤 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 } //权限 if permission.IsRoled(ctx) { if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil { var storeList2 []*StoreExt for _, v := range retVal.Stores { if storeIDsMap[v.ID] != 0 { storeList2 = append(storeList2, v) } } retVal.Stores = nil retVal.Stores = storeList2 } } // 订单情况过滤 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(autonavi.MaxConvertCount), ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) { var coords []*baidunavi.Coordinate for _, v := range batchItemList { store := v.(*StoreExt) coords = append(coords, &baidunavi.Coordinate{ Lng: store.FloatLng, Lat: store.FloatLat, }) } coords, err = api.BaiDuNaviAPI.BatchCoordinateConvert(coords, baidunavi.CoordSysGCJ02, baidunavi.CoordSysBaiDu) 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) } } else { return nil, err } if len(retVal.Stores) > 0 { setStoreMapInfo(ctx, db, retVal, storeIDs, briefLevel) 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) { globals.SugarLogger.Debugf("getMapCenter len(storeList):%d", len(storeList)) 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:%f, lat:%f", store.Name, store.FloatLng, store.FloatLat) // } // globals.SugarLogger.Debugf("lng:%f, lat:%f", lng, lat) 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 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 } return retVal, nil } return nil, err } return nil, ErrCanNotFindVendor } 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) { globals.SugarLogger.Debugf("UpdateStore storeID:%d, payload:%s", storeID, utils.Format4Output(payload, true)) 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"]))) } 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 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 } // globals.SugarLogger.Debug(utils.Format4Output(valid, false)) 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) 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, store.PrinterKey) } } 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(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 roleMap[k] != "" { if authInfo, err := ctx.GetV2AuthInfo(); err == nil { if roleMoblieMap[authInfo.Mobile] == "" { return 0, errors.New(fmt.Sprintf("当前用户 [%v] 无权限修改 [%v] 字段!", authInfo.Name, roleMap[k])) } } } if marketManPhoneRoleMap[k] != "" { if authInfo, err := ctx.GetV2AuthInfo(); err == nil { if marketManPhoneRoleMoblieMap[authInfo.Mobile] == "" { return 0, errors.New(fmt.Sprintf("当前用户 [%v] 无权限修改 [%v] 字段!", authInfo.Name, roleMap[k])) } } } } // districtCode := 0 // if valid["districtCode"] != nil { // districtCode = int(utils.MustInterface2Int64(valid["districtCode"])) // } // if districtCode == 0 && store.DistrictCode == 0 { // if lng == 0 { // lng = jxutils.IntCoordinate2Standard(store.Lng) // lat = jxutils.IntCoordinate2Standard(store.Lat) // } // valid["districtCode"] = api.AutonaviAPI.GetCoordinateDistrictCode(lng, lat) // } globals.SugarLogger.Debugf("UpdateStore track:%s, storeID:%d, valid:%s", ctx.GetTrackInfo(), storeID, utils.Format4Output(valid, true)) 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)) } dao.Begin(db) defer func() { dao.Rollback(db) }() if num, err = dao.UpdateEntityLogically(db, store, valid, userName, nil); 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) globals.SugarLogger.Debugf("UpdateStore track:%s, before call SyncStore", ctx.GetTrackInfo()) _, 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) } notifyStoreOperatorChanged(store.ID, "京东运营", store.OperatorPhone, valid["operatorPhone"]) notifyStoreOperatorChanged(store.ID, "美团运营", store.OperatorPhone2, valid["operatorPhone2"]) notifyStoreOperatorChanged(store.ID, "饿百运营", store.OperatorPhone3, valid["operatorPhone3"]) notifyStoreOperatorChanged(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 } func notifyStoreOperatorChanged(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", storeID, operatorRoleName, user.GetName(), curUserName)) } } } 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) { globals.SugarLogger.Debugf("CreateStore storeExt:%s", utils.Format4Output(storeExt, false)) 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 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) 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 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: "京西100-100", VendorID: model.VendorIDJX, }) 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 } 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) (outStoreMap *model.StoreMap, err error) { 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 { store, err2 := handler.ReadStore(ctx, vendorOrgCode, storeMap.VendorStoreID) if err = err2; err == nil || storeMap.IsSync == 0 { if store != nil { storeMap.DeliveryType = store.DeliveryType storeMap.Status = store.Status } err = nil 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) } } 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 { storeMap.VendorOrgCode = "1" if storeMap.VendorStoreID == "" { storeMap.SyncStatus = model.SyncFlagNewMask //京东商城要去建店 } else { if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil { store, err2 := handler.ReadStore(ctx, vendorOrgCode, storeMap.VendorStoreID) 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() } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = dao.CreateEntity(db, storeMap); err == nil { if vendorID == model.VendorIDJDShop { storeMap2 := storeMap storeMap2.VendorOrgCode = "2" dao.CreateEntity(db, storeMap2) } dao.Commit(db) outStoreMap = storeMap _, err = CurVendorSync.SyncStore(ctx, db, storeMap.VendorID, storeID, false, userName) } else { dao.Rollback(db) } } } 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) } } else if vendorID == model.VendorIDMTWM { //如果是绑的美团超市,要去更新token if outStoreMap.VendorOrgCode == globals.Mtwm2Code { // mtapi := apimanager.CurAPIManager.GetAPI(model.VendorIDMTWM, outStoreMap.VendorOrgCode).(*mtwmapi.API) // if result, err := mtapi.GetOAuthCode(outStoreMap.VendorStoreID); result != nil && err == nil { // if token, err := mtapi.GetAccessToken(result.(string)); err == nil && token != nil { // outStoreMap.MtwmToken = token.(string) // dao.UpdateEntity(db, outStoreMap, "MtwmToken") // } // } } } if globals.IsAddEvent { err = AddEventDetail(db, ctx, model.OperateAdd, vendorID, model.ThingTypeStore, storeID, "", `{"VendorID":`+utils.Int2Str(vendorID)+`}`) } _, err = CurVendorSync.FullSyncStoresSkus(ctx, db, []int{vendorID}, []int{storeID}, true, nil, true, true) return outStoreMap, 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)+`}`) } } 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") } 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 { 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 vendorStoreName, ok := valid["vendorStoreName"].(string); ok { if utf8.RuneCountInString(vendorStoreName) > jdapi.MaxStoreNameLen && vendorID == model.VendorIDJD { return 0, fmt.Errorf("门店名称不允许超过13位!") } syncStatus |= model.SyncFlagStoreName } 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) if err = err2; err == nil { valid["deliveryType"] = vendorStoreInfo.DeliveryType } err = nil // todo 忽略读不到DeliveryType的错误 } } if err == nil { // globals.SugarLogger.Debug(utils.Format4Output(valid, false)) if len(valid) > 0 { dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() 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, }, model.FieldSyncStatus, syncStatus) } else { num, err = dao.UpdateEntityLogically(db, storeMap, valid, userName, map[string]interface{}{ model.FieldStoreID: storeID, model.FieldVendorID: vendorID, }) } if err != nil { dao.Rollback(db) return 0, err } 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) return 0, err } } } else { if valid["pricePercentage"] != nil || valid["pricePercentagePack"] != nil { ReCalculateJxPriceLight(db, ctx, storeID) } } } dao.Commit(db) if isStoreMapNeedSync(vendorID, valid) { _, err = CurVendorSync.SyncStore(ctx, db, vendorID, storeID, false, userName) } } } return num, err } 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 } globals.SugarLogger.Debugf("DeleteStore storeID:%d, ct=%d", storeID, ct) if ct > 0 { return 0, fmt.Errorf("删除京西门店前必须将所有门店解绑且门店处于关店状态") } dao.Begin(db) defer dao.Rollback(db) 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) 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 <= ? `, orderman.COMMENT_NOT_RESOLVED, utils.Int2Str(storeID), orderman.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) (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) } } } 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 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 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 ?)` sqlParams = append(sqlParams, 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, orderman.COMMENT_NOT_RESOLVED, orderman.JX_BAD_COMMENTS_MAX_LEVEL) } else { sqlParams = append(sqlParams, orderman.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) } 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 dao.Begin(db) defer func() { dao.Rollback(db) }() // globals.SugarLogger.Debug(sql) // globals.SugarLogger.Debug(utils.Format4Output(sqlParams, false)) if err = dao.GetRows(db, &commentList, sql, sqlParams...); err == nil { retVal = map[string]interface{}{ "total": dao.GetLastTotalRowCount(db), "list": commentList, } dao.Commit(db) } 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) globals.SugarLogger.Debug("之前的storeCourierMaps") globals.SugarLogger.Debug(utils.Format4Output(storeCourierMaps, false)) if len(storeCourierMaps) != 0 { //同步美团配送与否状态及美团门店是否存在 for _, v := range storeCourierMaps { if v.VendorID != model.VendorIDMTPS { continue } else { SetMTPSStatus(jxcontext.AdminCtx, v.StoreID, v.Status) globals.SugarLogger.Debug("之后的storeCourierMaps") globals.SugarLogger.Debug(v) break } } } 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 globals.SugarLogger.Debugf("addStoreCourierMap %s, storeCourierMap:%s, isNeedUpdateRemote:%t", model.VendorChineseNames[vendorID], utils.Format4Output(storeCourierMap, true), isNeedUpdateRemote) 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) // } 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) { globals.SugarLogger.Debugf("updateCourierStore %s, storeID:%d, vendorStoreID:%s", model.VendorChineseNames[storeDetail.VendorID], storeDetail.ID, storeDetail.VendorStoreID) 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 } func updateCourierStores(ctx *jxcontext.Context, storeID int) (err error) { globals.SugarLogger.Debugf("updateCourierStores storeID:%d", storeID) 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) } } errList.AddErr(err) } } } return errList.GetErrListAsOne() } func updateOrCreateCourierStore(ctx *jxcontext.Context, storeDetail *dao.StoreDetail2) (isCreated bool, err error) { globals.SugarLogger.Debugf("updateOrCreateCourierStore %s, storeID:%d, vendorStoreID:%s", model.VendorChineseNames[storeDetail.VendorID], storeDetail.ID, storeDetail.VendorStoreID) 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 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()) globals.SugarLogger.Debugf("updateOrCreateCourierStore storeID:%d failed with error:%v", storeDetail.ID, err) } } return isCreated, err } func UpdateOrCreateCourierStores(ctx *jxcontext.Context, storeID int, isForceUpdate, isAsync, isContinueWhenError bool) (hint string, err error) { globals.SugarLogger.Debugf("UpdateOrCreateCourierStores storeID:%d", storeID) 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, } 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 { storeDetail.Name = fmt.Sprintf("%s-%s-%s", model.ShopChineseNames[model.VendorIDJD], storeDetail.CityName, 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 string) (corporationInfo *jdapi.CorporationInfo, err error) { // 门店ID随便一个合法的京东门店ID就可以 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) 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 } dao.Begin(db) defer func() { if r := recover(); r != nil || err != nil { dao.Rollback(db) 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) 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) { dao.Begin(db) defer func() { if r := recover(); r != nil || err != nil { dao.Rollback(db) 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) 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) } 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 { globals.SugarLogger.Infof("SyncStoresCourierInfo [运营2]门店:%s-%d的%s配送门店坐标不一致,京西:%d,%d,平台:%d,%d,距离:%f公里", store.Name, store.ID, model.VendorChineseNames[vendorID], store.Lng, store.Lat, storeCourier.Lng, storeCourier.Lat, distance) 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) } } } 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 { dao.Begin(db) defer func() { if r := recover(); r != nil || err != nil { dao.Rollback(db) 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) globals.SugarLogger.Debugf("CreateStorePriceScore") } 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 { dao.Begin(db) defer func() { if r := recover(); r != nil || err != nil { dao.Rollback(db) 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) } 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 { 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()) // globals.SugarLogger.Debug(utils.Format4Output(storeParams, false)) 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 } 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 { return nil, fmt.Errorf("已存在绑定的京西分类,分类名:[%v]", storeCatMaps[0].StoreCategoryName) } } storeCategoryMap.StoreCategoryName = strings.Trim(storeCategoryMap.StoreCategoryName, " ") dao.WrapAddIDCULDEntity(storeCategoryMap, ctx.GetUserName()) dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if err = dao.CreateEntity(db, storeCategoryMap); err != nil { dao.Rollback(db) return nil, err } dao.Commit(db) 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.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 { 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) } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() if num, err = dao.UpdateEntityLogically(db, storeCategoryMap2, valid, ctx.GetUserName(), nil); err != nil { dao.Rollback(db) return 0, err } dao.Commit(db) 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 } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } }() for k, v := range categoryIDs { if catsMap[v] == nil { dao.Rollback(db) 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) return err } } dao.Commit(db) 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() ) 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 { dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) 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) SELECT ?, ?, ?, ?, ?, category_id, store_category_name, store_category_seq, level, parent_id 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) } 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 { dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) 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) SELECT ?, ?, ?, ?, ?, category_id, store_category_name, store_category_seq, level, parent_id 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) } } } 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}, []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("目前只允许扣点的门店修改调价包!") } dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) 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) return err } if _, err = dao.UpdateEntityLogically(db, configList[0], map[string]interface{}{ "Value": string(realValue), }, ctx.GetUserName(), nil); err != nil { dao.Rollback(db) 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) return err } storeMapList, err := dao.GetStoresMapList(db, []int{vendorID}, []int{storeID}, nil, model.StoreStatusAll, model.StoreIsSyncAll, "", "", "") if err != nil { dao.Rollback(db) return err } if len(storeMapList) > 0 { storeMapList[0].PricePercentagePack = pricePack dao.UpdateEntity(db, storeMapList[0], "PricePercentagePack") } } dao.Commit(db) storeMapList, err := dao.GetStoresMapList(db, nil, nil, nil, model.StoreStatusAll, model.StoreIsSyncYes, pricePack, "", "") if err != nil { dao.Rollback(db) 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.AutonaviAPI.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 storeID, err := CreateStore(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 } func GetBrands(ctx *jxcontext.Context, name string, brandID int) (brands []*model.Brand, err error) { return dao.GetBrands(dao.GetDB(), name, brandID) } 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") } else { valid := dao.StrictMakeMapByStructObject(payload, brand, ctx.GetUserName()) if len(valid) > 0 { dao.Begin(db) defer func() { if r := recover(); r != nil { dao.Rollback(db) panic(r) } if _, err = dao.UpdateEntityLogically(db, brand, valid, ctx.GetUserName(), nil); err != nil { dao.Rollback(db) return } dao.Commit(db) }() } } 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); 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" { //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 = ? AND name LIKE ? ` sqlParams := []interface{}{utils.DefaultTimeValue, "%" + name + "%"} err = dao.GetRows(db, &brands, sql, sqlParams) return brands, err } func RefreshMTWMToken(ctx *jxcontext.Context) (err error) { // var ( // db = dao.GetDB() // ) // if time.Now().Weekday() != time.Sunday { // return // } // //token是1个月过期,每周刷一次吧 // dao.GetStoresMapList(db, []int{model.VendorIDMTWM}, nil, nil, status, isSync, "", "", globals.Mtwm2Code) return err }