Files
jx-callback/business/jxstore/cms/store.go
gazebo 637f22a887 存储所有评价
3至4星评价发送中评提醒消息
2019-10-12 15:01:49 +08:00

1993 lines
72 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 (
"errors"
"fmt"
"math"
"sort"
"strconv"
"strings"
"time"
"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/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/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"` // 非京东运营人姓名
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"` // 开户行名称
StoreMaps []map[string]interface{} `orm:"-"`
CourierMaps []map[string]interface{} `orm:"-"`
OrderCount int `json:"orderCount"`
}
type StoresInfo struct {
TotalCount int `json:"totalCount"`
MapCenterLng float64 `json:"mapCenterLng"`
MapCenterLat float64 `json:"mapCenterLat"`
Stores []*StoreExt `json:"stores"`
}
type JxBadCommentsExt struct {
legacymodel.JxBadComments
StoreName string `json:"storeName"`
CityName string `json:"cityName"`
VendorOrderID2 string `orm:"column(vendor_order_id2);size(48);index" json:"vendorOrderID2"`
}
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,
}
WatchVendorStoreTimeList = []string{
"8:00:00",
"10:00:00",
"11:00:00",
"15:00:00",
"20:00:00",
}
)
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 = ?
`
sqlFromParams = []interface{}{
utils.DefaultTimeValue,
model.ConfigTypeBank,
// 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]int
if err = utils.UnmarshalUseNumber([]byte(mapCondsStr), &vendorStoreConds); err != nil {
return "", nil, "", nil, err
}
sqlVendorStoreCond := ""
for vendor, cond := range vendorStoreConds {
tableAlias := tableName + vendor
if cond != 0 {
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 = ?"
sqlFromParams = append(sqlFromParams, vendor, utils.DefaultTimeValue)
if cond == 1 {
sqlVendorStoreCond += " " + mapCond + " " + tableAlias + ".id IS NOT NULL"
} else {
sqlVendorStoreCond += " " + mapCond + " " + tableAlias + ".id IS 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.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)
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))
) > 0`
prefix := autils.NewRole("", 0).GetFullName()
sqlWhereParams = append(sqlWhereParams, keyword+"%", autils.NewStoreBossRole(-1).GetFullName(), prefix, prefix, prefix) // 必须要前缀,不然不能用过些会很慢
}
sqlWhere += " OR t1.id = ? OR t1.city_code = ? OR t1.district_code = ?"
sqlWhereParams = append(sqlWhereParams, 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("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["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) (err error) {
storeMapList, err := dao.GetStoresMapList(db, nil, storeIDs, 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 := make(map[int][]*model.StoreMap)
for _, v := range storeMapList {
storeMapMap[v.StoreID] = append(storeMapMap[v.StoreID], v)
}
storeCourierMap := make(map[int][]*model.StoreCourierMap)
for _, v := range storeCourierList {
storeCourierMap[v.StoreID] = append(storeCourierMap[v.StoreID], v)
}
for _, v := range storesInfo.Stores {
for _, v2 := range storeMapMap[v.ID] {
v.StoreMaps = append(v.StoreMaps, utils.Struct2FlatMap(v2))
}
for _, v2 := range storeCourierMap[v.ID] {
v.CourierMaps = append(v.CourierMaps, utils.Struct2FlatMap(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) {
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,
province.code province_code,
province.name province_name,
city.name city_name,
district.name district_name
` + 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)
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, vendorStoreID string, vendorID int) (retVal *StoreExt, err error) {
if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil {
result, err2 := handler.ReadStore(ctx, 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
}
valid := dao.StrictMakeMapByStructObject(payload, store, 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)
}
}
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))
} else {
status = store.Status
}
if status != model.StoreStatusClosed && status != model.StoreStatusHaveRest {
valid["autoEnableAt"] = nil
} else {
if valid["autoEnableAt"] != nil {
status = model.StoreStatusHaveRest
valid["autoEnableAt"] = utils.Time2Date(utils.Str2Time(utils.Interface2String(valid["autoEnableAt"])))
} else {
status = model.StoreStatusClosed
}
valid["status"] = status
}
}
if valid["deliveryRange"] != nil {
valid["deliveryRange"] = strings.Trim(valid["deliveryRange"].(string), ";")
}
// 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 {
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)
}
}
} else {
globals.SugarLogger.Debugf("UpdateStore track:%s, store:%s", ctx.GetTrackInfo(), utils.Format4Output(store, true))
}
return num, err
}
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 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); err != nil {
return 0, err
}
}
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 {
UpdateOrCreateCourierStores(ctx, store.ID, false, false, false)
TryAddStoreBossRole4StoreByMobile(ctx, storeExt.ID, []string{storeExt.Tel1, storeExt.Tel2})
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)
}
func AddStoreVendorMap(ctx *jxcontext.Context, db *dao.DaoDB, storeID, vendorID 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()
if handler := CurVendorSync.GetStoreHandler(vendorID); handler != nil {
store, err2 := handler.ReadStore(ctx, storeMap.VendorStoreID)
if err = err2; err == nil || storeMap.IsSync == 0 {
dao.WrapAddIDCULDEntity(storeMap, userName)
storeMap.StoreID = storeID
storeMap.VendorID = vendorID
if store != nil {
storeMap.DeliveryType = store.DeliveryType
storeMap.Status = store.Status
}
err = nil
storeMap.SyncStatus = model.SyncFlagModifiedMask | model.SyncFlagStoreName | model.SyncFlagStoreAddress // 新增绑定门店是修改的概念
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 {
outStoreMap = storeMap
if store != nil {
_, err = CurVendorSync.SyncStore(ctx, db, storeMap.VendorID, storeID, false, userName)
}
}
if err != nil {
dao.Rollback(db)
} else {
dao.Commit(db)
}
}
} else {
err = ErrCanNotFindVendor
}
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.VendorStoreID, utils.Str2Int64WithDefault(storeMap.VendorStoreID, 0))
}
num, err = dao.DeleteEntityLogically(db, storeMap, map[string]interface{}{
model.FieldStatus: model.StoreStatusDisabled,
}, userName, nil)
}
return num, err
}
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("非京东平台要求必须自动拣货")
}
}
storeHandler := CurVendorSync.GetStoreHandler(vendorID)
if storeHandler == nil {
return 0, ErrCanNotFindVendor
}
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
}
syncStatus := model.SyncFlagModifiedMask
valid := dao.StrictMakeMapByStructObject(payload, storeMap, userName)
if valid["status"] != nil {
syncStatus |= model.SyncFlagStoreStatus
}
if valid["pricePercentagePack"] != nil {
if pricePercentagePack := utils.Interface2String(valid["pricePercentagePack"]); pricePercentagePack != "" {
_, err2 := dao.QueryConfigs(db, pricePercentagePack, model.ConfigTypePricePack, "")
if err = err2; err != nil {
return 0, err
}
}
}
if vendorStoreID := utils.Interface2String(valid["vendorStoreID"]); vendorStoreID != "" {
vendorStoreInfo, err2 := storeHandler.ReadStore(ctx, 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() {
dao.Rollback(db)
}()
if valid["status"] != nil { // 对于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 && num > 0 {
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 {
return 0, err
}
}
dao.Commit(db)
if valid["status"] != nil {
_, 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
}
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, 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 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)
if offset < 0 {
offset = 0
}
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
}
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,
})
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
}
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)
}
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
}
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, 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)
}
excelConf := &excel.Obj2ExcelSheetConfig{
Title: "饿百门店情况导出",
Data: healthInfoList2,
CaptionList: []string{
"real_shop_id",
"merchant_id",
"merchant_name",
"hours",
"sku_num",
"target_jiedan",
"is_healthy",
"bad_order_rate",
},
}
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) {
corporationInfo, err = api.JdAPI.GetCorporationInfo("", 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, 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 storeMap.VendorID != model.VendorIDWSC {
if handler := partner.GetPurchasePlatformFromVendorID(storeMap.VendorID); handler != nil {
store, err2 := handler.ReadStore(ctx, 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, 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.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} {
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, "", "", 0, -1)
for _, v := range userList {
userMap[v.GetID()] = 1
}
}
// 直接电话信息相关人员
for _, mobile := range []string{store.Tel1, store.Tel2, store.MarketManPhone, store.OperatorPhone, store.OperatorPhone2} {
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, "")
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, "")
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
}