Files
jx-callback/business/jxstore/cms/store.go
2020-05-19 09:08:42 +08:00

3176 lines
115 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package cms
import (
"bytes"
"errors"
"fmt"
"io"
"math"
"mime/multipart"
"sort"
"strconv"
"strings"
"time"
"unicode/utf8"
"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"`
// 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 Store4User struct {
model.ModelIDCULD
OriginalName string `orm:"-" json:"originalName"`
Name string `orm:"size(255)" json:"name"`
OpenTime1 int16 `json:"openTime1"` // 930就表示9点半用两个的原因是为了支持中午休息1与2的时间段不能交叉为0表示没有
CloseTime1 int16 `json:"closeTime1"` // 格式同上
OpenTime2 int16 `json:"openTime2"` // 格式同上
CloseTime2 int16 `json:"closeTime2"` // 格式同上
Status int `json:"status"`
CityCode int `orm:"default(0);null" json:"cityCode"` // todo ?
DistrictCode int `orm:"default(0);null" json:"districtCode"` // todo ?
Address string `orm:"size(255)" json:"address"`
Tel1 string `orm:"size(32);index" json:"tel1"`
Tel2 string `orm:"size(32);index" json:"tel2"`
Lng int `json:"-"` // 乘了10的6次方
Lat int `json:"-"` // 乘了10的6次方
DeliveryRangeType int8 `json:"-"` // 参见相关常量定义
DeliveryRange string `orm:"type(text)" json:"-"` // 如果DeliveryRangeType为DeliveryRangeTypePolygon则为逗号分隔坐标分号分隔的坐标点坐标与Lng和Lat一样都是整数比如 121361504,31189308;121420555,31150238。否则为半径单位为米
FloatLng float64 `json:"lng"`
FloatLat float64 `json:"lat"`
CityName string `json:"cityName"`
DistrictName string `json:"districtName"`
Distance int `json:"distance"`
WalkDistance int `json:"walkDistance"`
}
type Store4UserList []*Store4User
func (x Store4UserList) Len() int {
return len(x)
}
func (x Store4UserList) Less(i, j int) bool {
if x[i].Status != x[j].Status {
return x[i].Status > x[j].Status
}
if x[i].WalkDistance != x[j].WalkDistance {
return x[i].WalkDistance < x[j].WalkDistance
}
return x[i].Distance < x[j].Distance
}
func (x Store4UserList) Swap(i, j int) {
x[i], x[j] = x[j], x[i]
}
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", //赵敏夫
}
marketManPhoneRoleMap = map[string]string{
"marketManPhone": "市场负责人电话",
}
marketManPhoneRoleMoblieMap = map[string]string{
"13684045763": "13684045763",
"18160030913": "18160030913",
"18048531223": "18048531223",
"18328080405": "18328080405",
"17380734342": "17380734342",
"15208271238": "15208271238",
}
)
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 = ?
`
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 ?`
sqlWhereParams = append(sqlWhereParams, 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["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["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)
}
}
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, 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,
(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
}
// 订单情况过滤
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
}
}
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)
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
}
} else if vendorID == model.VendorIDJX {
ReCalculateJxPriceLight(db, ctx, storeID)
} else if vendorID == model.VendorIDYB {
err = checkYbParams(db, storeMap, storeID)
if err != nil {
return nil, err
}
} else if vendorID == model.VendorIDJDShop {
if storeMap.VendorStoreID == "" {
storeMap.SyncStatus = model.SyncFlagNewMask //京东商城要去建店
} else {
if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil {
store, err2 := handler.ReadStore(ctx, vendorOrgCode, storeMap.VendorStoreID)
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 {
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 {
dao.Commit(db)
outStoreMap = storeMap
_, err = CurVendorSync.SyncStore(ctx, db, storeMap.VendorID, storeID, false, userName)
} else {
dao.Rollback(db)
}
}
}
if vendorID == model.VendorIDJDShop {
//绑定京东商城后,需要对绑定的门店现有的和模板店相同且可售的商品设置京东商城的门店库存
//TODO SyncFlagSaleMask对京东商城来说修改门店商品状态就是修改库存
if _, err = SetStoreSkuSyncStatus2(db, nil, []int{model.VendorIDJDShop}, findSkusBetweenJdsMainStore(db, storeID), model.SyncFlagSaleMask); err == nil {
CurVendorSync.SyncStoresSkus(ctx, nil, 0, db, nil, []int{storeID}, nil, false, true, true)
}
}
if globals.IsAddEvent {
err = AddEventDetail(db, ctx, model.OperateAdd, vendorID, model.ThingTypeStore, storeID, "", `{"VendorID":`+utils.Int2Str(vendorID)+`}`)
}
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 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()
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
}
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
}
return storeCourierMaps, dao.GetEntitiesByKV(db, &storeCourierMaps, cond, false)
}
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
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
}
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,
}, 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.StoreIsSyncYes, "")
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, "")
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, "")
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, 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) {
vendorID := model.VendorIDJD
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 GetStoreListByLocation(ctx *jxcontext.Context, lng, lat float64, needWalkDistance bool) (storeList []*Store4User, err error) {
const (
maxRadius = 5000
maxStoreCount4User = 5
)
lng2, _ := jxutils.ConvertDistanceToLogLat(lng, lat, float64(maxRadius), 90)
_, lat2 := jxutils.ConvertDistanceToLogLat(lng, lat, float64(maxRadius), 0)
lng1 := lng - (lng2 - lng)
lat1 := lat - (lat2 - lat)
// globals.SugarLogger.Debugf("%f,%f,%f,%f\n", lng1, lng2, lat1, lat2)
sql := `
SELECT t1.*,
city.name city_name
FROM store t1
JOIN place city ON city.code = t1.city_code
JOIN store_map sm ON sm.store_id = t1.id AND sm.vendor_id = ? AND sm.deleted_at = ? AND sm.status <> ?
WHERE t1.deleted_at = ? AND t1.status <> ? AND t1.lng > ? AND t1.lng < ? AND t1.lat > ? AND t1.lat < ?
AND sm.is_order <> ?
ORDER BY t1.id
`
sqlParams := []interface{}{
model.VendorIDJX, utils.DefaultTimeValue, model.StoreStatusDisabled,
utils.DefaultTimeValue, model.StoreStatusDisabled, jxutils.StandardCoordinate2Int(lng1), jxutils.StandardCoordinate2Int(lng2), jxutils.StandardCoordinate2Int(lat1), jxutils.StandardCoordinate2Int(lat2),
model.YES,
}
var storeList1 []*Store4User
if err = dao.GetRows(dao.GetDB(), &storeList1, sql, sqlParams...); err == nil {
var storeList2 []*Store4User
for _, v := range storeList1 {
distance := jxutils.Point2StoreDistance(lng, lat, v.Lng, v.Lat, v.DeliveryRangeType, v.DeliveryRange)
if distance > 0 || (lng == jxutils.IntCoordinate2Standard(v.Lng) && lat == jxutils.IntCoordinate2Standard(v.Lat)) {
v.Distance = distance
storeList2 = append(storeList2, v)
}
}
// 为了审核用
if len(storeList2) == 0 {
sql2 := `
SELECT t1.*,
city.name city_name
FROM store t1
JOIN place city ON city.code = t1.city_code
WHERE t1.deleted_at = ? AND t1.status <> ? AND t1.id = ?
`
sqlParams2 := []interface{}{
// model.VendorIDJX, utils.DefaultTimeValue, model.StoreStatusDisabled,
utils.DefaultTimeValue,
model.StoreStatusDisabled,
// jxutils.StandardCoordinate2Int(0),
// jxutils.StandardCoordinate2Int(10000),
// jxutils.StandardCoordinate2Int(0),
// jxutils.StandardCoordinate2Int(10000),
// model.YES,
102919, //商城模板店
}
dao.GetRows(dao.GetDB(), &storeList2, sql2, sqlParams2...)
// if len(storeList2) > 1 {
// storeList2 = storeList2[:1]
// }
}
// 如果要求以步行距离来算
if needWalkDistance {
var coordList []*autonavi.Coordinate
for _, v := range storeList2 {
coordList = append(coordList, &autonavi.Coordinate{
Lng: v.FloatLng,
Lat: v.FloatLat,
})
}
if distanceList, err2 := api.AutonaviAPI.BatchWalkingDistance(lng, lat, coordList); err2 == nil {
for k, v := range storeList2 {
v.WalkDistance = int(distanceList[k])
}
} else {
return nil, err2
}
}
sort.Sort(Store4UserList(storeList2))
storeList = storeList2
if len(storeList) > maxStoreCount4User {
storeList = storeList[:maxStoreCount4User]
}
}
return storeList, 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 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, "")
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, "")
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 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, "")
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) {
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)
}
}
fmt.Println("testskus", skus)
return skus
}