package weixinmsg

import (
	"encoding/json"
	"fmt"
	"strings"
	"time"

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

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

	"git.rosy.net.cn/jx-callback/business/auth2/authprovider/weixin"
	"git.rosy.net.cn/jx-callback/business/authz/autils"
	"git.rosy.net.cn/jx-callback/business/jxstore/permission"
	"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
	"git.rosy.net.cn/jx-callback/business/jxutils/netprinter"
	"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"

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

	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/jx-callback/business/jxutils"
	"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/globals"
	"git.rosy.net.cn/jx-callback/globals/api"
	"git.rosy.net.cn/jx-callback/globals/api2"
)

const (
	WX_TO_ORDER_PAGE_URL           = "/#/?jxStoreId="
	WX_TO_STORE_SKU_PAGE_URL       = "/#/shop?jxStoreId="
	WX_TO_SHOW_COMMENTS_DETAIL_URL = "/#/assess-list?jxStoreId=" //展示差评详情的页面
	WX_TO_SHOW_MSG                 = "/#/message-detail?msgID=%d&msgStatusID=%d"

	WX_MINI_TO_ORDER_PAGE_URL     = "pages/order-manager/main"
	WX_MINI_TO_STORE_SKU_PAGE_URL = "pages/goods-manager/main"
	//WX_MINI_TO_SHOW_COMMENTS_DETAIL_URL = "pages/pagesStore/store-comment/main"
	WX_MINI_TO_SHOW_COMMENTS_DETAIL_URL = "subPages/merchantChild/evaluateM/evaluateM"
	//WX_MINI_TO_SHOW_MSG                 = "pages/pagesStore/msg-page/main?msgID=%d&msgStatusID=%d"
	WX_MINI_TO_SHOW_MSG = "subPages/merchantChild/messageDetail/messageDetail?messageID=%d&id=%d"

	//新订单模板消息文字颜色
	WX_NEW_ORDER_TEMPLATE_COLOR          = "#173177"
	WX_HIGHLEVEL_TEMPLATE_COLOR          = "#FF0000" //红色
	WX_HIGHLEVEL_TEMPLATE_COLOR2         = "#333333" //黑色
	WX_TEMPLATE_VENDERCOLOR_JDDJ         = "#47B34F"
	WX_TEMPLATE_VENDERCOLOR_MT           = "#F4A800"
	WX_TEMPLATE_VENDERCOLOR_ELM          = "#0191EA" //蓝色
	WX_TEMPLATE_VENDERCOLOR_BAD_COMMENTS = "#4F4DA0"

	WX_NEWORDER_TEMPLATE_ID2               = "b8-tLyWwAmK-1tEU1eGqp_YAAqQtSzoVDZkHuyUe9lk" //微信新订单推送2
	WX_NEWORDER_TEMPLATE_ID                = "_DtNGwmOeR6TkkTVUblxLIlkV2MAPOX57TkvfdqG6nY" //微信新订单推送
	WX_ADJUSTORDER_TEMPLATE_ID             = "N5rmV2-PDf3opjkv23IdLc76VnFThr_uOKSh5FzT13M" //微信调整订单推送
	WX_MTPS_DELIVERY_GRABDONE_TEMPLATE_ID  = "h4dkON6AgnHz1XmaksEUB_8Bcir4V8MSexUhC149pPE" //微信美团配送员接单推送
	WX_MTPS_DELIVERY_DONE_TEMPLATE_ID      = "YXdCrQAHZlcZX1htYUiarrLmtkmKAjp7rynjwObgODo" //微信美团配送员配送完成推送
	WX_MTPS_UNABLE_DELIVER_TEMPLATE_ID     = "ZFph5Hp7oLlrzVRXbsKIC_StmaBeB9Dlp4tlHeAmUQ8" //微信美团配送配送能力不足推送
	WX_MTPS_DELIVERY_EXCEPTION_TEMPLATE_ID = "RkfOFHgR1N75L4-a6Gv0DljpCsVfOHhLm_vyXh8MR-w" //微信美团配送异常推送
	WX_BAD_COMMENT_PUSH_TEMPLATE_ID        = "NaMEzjctvVPQ9ishTI1dKpp5QSYV2FWcWftSSjDrpN8" //微信中差评消息推送

	WX_DADA_DELIVERY_GRABDONE_TEMPLATE_ID = "h4dkON6AgnHz1XmaksEUB_8Bcir4V8MSexUhC149pPE" //微信达达众包配送员接单推送
	WX_DADA_DELIVERY_DONE_TEMPLATE_ID     = "YXdCrQAHZlcZX1htYUiarrLmtkmKAjp7rynjwObgODo" //微信达达众包配送员配送完成推送

	WX_SALE_BILL_TEMPLATE_ID = "eTUuFZMWH7IsVBfcxNMpmaHYaxRkUaD6zG8wSGJDcic" // 销售账单模板id

	WX_NORMAL_STORE_ACT_MSG_TEMPLATE_ID = "eXsfOMvDOKVSht0Q0v4IsDrx3MBlRCVYwYl7Hi2uuuI" // 正常存储动作消息模板ID
	WX_NORMAL_STORE_MSG_TEMPLATE_ID     = "EUeIJEz2TLUAn4TU2EffOGYLd3dEaYndD_y6Sw9FcSU" // 工作通知
	WX_CHANGE_APPROVED_TEMPLATE_ID      = "gIG2olBZtQbjXmp6doNB_dESu60By5xuXYOGxksLv3Y"
	WX_CHANGE_REJECTED_TEMPLATE_ID      = "OBF4-d5inK95epHcUltpdb1zq9boVp2HESpASVRh1Oo"
	WX_ORDER_APPLY_CANCEL_TEMPLATE_ID   = "e6urTtcm4PL0rgDMG_1qWNOwrE3Qxqcm_dx0kWWCmEI"
	WX_ORDER_ORDER_CANCELED_TEMPLATE_ID = "HXjuSAbIk77Xh18hjgwoxHzbciR9jX3Rn2CpLJz9dZw"
	WX_ORDER_CHANGE_INFO_TEMPLATE_ID    = "cjmEQFEpvcOuBNqQpTfMU5FeBpGRbQh_gWKz-WPW8Ro"

	WX_AFS_ORDER_WAIT4APPROVE_TEMPLATE_ID   = "X29udtANvhX6x1Lyh-T40NGNjRXBbUj5oSBTfDhZAqU"
	WX_AFS_ORDER_STATUS_CHANGED_TEMPLATE_ID = "99T33rrXX0VboO1hljs4x8dDoLiSj3QX_rOikPHIXkg"

	WS_NOTIFY_STORE_STATUS_CHHANGED_TEMPLATE_ID = "Fl0vOnBKTQqRFx3-shGKxdCnxMdQXNeODzgkuwd7oxw"

	WX_STORE_ALERT_TEMPLATE_ID = "0AjzVl1wPl6iO4nFOS4IEsJYSzBymlT37DciIvcCOxE"

	WX_KNOWLEDGE_BASE_TEMPLATE_ID = "1Fc703r13L3KQ6r_jdql2em_YhQ9CyOXTnqtDx-6Hx8" //知识库模板
)

var (
	VendorColors = map[int]string{
		model.VendorIDJD:   WX_TEMPLATE_VENDERCOLOR_JDDJ,
		model.VendorIDMTWM: WX_TEMPLATE_VENDERCOLOR_MT,
		model.VendorIDELM:  WX_TEMPLATE_VENDERCOLOR_ELM,
		model.VendorIDEBAI: WX_TEMPLATE_VENDERCOLOR_ELM,
	}
	testMiniProgramStoreMap = map[int]int{
		101089: 1,
		100888: 1,
		100204: 1,
		100205: 1,
		100147: 1,
	}
	debugOpenIDMap = map[string]int{
		"oYN_usk0AeGc_C6VEZfmFQP5VHMQ": 1, // 周小扬
		"oYN_ust9hXKEvEv0X6Mq6nlAWs_E": 1, // me
		"oYN_usqnpGVQ4xxlao_yybsbYJh4": 1, // 朱丹
	}
)

func GetWeixinOpenIDsFromStoreID(storeID int) (retVal []string) {
	db := dao.GetDB()
	openIDMap := make(map[string]int)
	if globals.EnableWXAuth2 {
		if userIDList, err2 := api2.RoleMan.GetRoleUserList(autils.NewStoreBossRole(storeID)); err2 == nil {
			for _, v := range userIDList {
				if authList, err2 := dao.GetUserBindAuthInfo(db, v, model.AuthBindTypeAuth, []string{weixin.AuthTypeMP}, "", "", nil); err2 == nil {
					for _, v := range authList {
						retVal = append(retVal, v.AuthID)
						openIDMap[v.AuthID] = 1
					}
				}
			}
		}
	}
	retVal = jxutils.StringMap2List(openIDMap)
	if !globals.ReallyCallPlatformAPI {
		// todo，调试，只发给我
		if storeID == 100146 {
			retVal = []string{"oYN_ust9hXKEvEv0X6Mq6nlAWs_E"}
		} else {
			retVal = nil
		}
	}
	return retVal
}

func SendMsgToStore(storeID int, templateID, downloadURL, miniPageURL string, data interface{}) (err error) {
	openIDs := GetWeixinOpenIDsFromStoreID(storeID)
	successCount := 0
	for _, openID := range openIDs {
		realMiniPageURL := miniPageURL
		if err2 := SmartMessageTemplateSend(openID, templateID, downloadURL, realMiniPageURL, data); err2 == nil {
			successCount++
		} else {
			err = err2
		}
	}
	if successCount > 0 {
		err = nil // 只要成功一个都当成成功
	}
	if err != nil {
		globals.SugarLogger.Debugf("SendMsgToStore all failed storeID:%d, templateID:%s, error:%v", storeID, templateID, err)
	}
	return err
}

func SmartMessageTemplateSend(userOpenID, templateID, downloadURL, miniPageURL string, data interface{}) (err error) {
	var miniProgram map[string]interface{}
	if miniPageURL != "" {
		miniProgram = map[string]interface{}{
			"appid":    api.WeixinMiniAPI.GetAppID(),
			"pagepath": miniPageURL,
		}
	}
	if globals.ReallySendWeixinMsg || debugOpenIDMap[userOpenID] == 1 {
		if err = api.WeixinAPI.CBMessageTemplateSend(userOpenID, templateID, downloadURL, miniProgram, data); err != nil {
			globals.SugarLogger.Debugf("SmartMessageTemplateSend openID:%s, templateID:%s, downloadURL:%s, miniProgram:%s, failed with error:%v", userOpenID, templateID, downloadURL, utils.Format4Output(miniProgram, true), err)
		}
	}
	return err
}

func MediaMessageSendGroup(openIDs []string, mediaID string) (err error) {
	return api.WeixinAPI.CBMassSend(openIDs, mediaID)
}

func getOrderDetailBrief(order *model.GoodsOrder) (brief string) {
	sb := new(strings.Builder)
	sb.WriteString(order.Skus[0].SkuName)
	sb.WriteString("等共")
	sb.WriteString(utils.Int2Str(order.Skus[0].Count))
	sb.WriteString("份（")
	sb.WriteString(jxutils.IntPrice2StandardString(order.Skus[0].SalePrice))
	sb.WriteString("元/份）等，点击查看详情。")
	//，预计收入")
	//TODO 2020-07-20 果园和菜市不同
	//TODO 2021-06-15 都不显示钱了
	//var price int64
	//if beego.BConfig.RunMode == "jxgy" {
	//	price = order.NewEarningPrice
	//} else {
	//	if order.EarningType == model.EarningTypePoints {
	//		price = order.ActualPayPrice
	//	} else {
	//		price = order.EarningPrice
	//	}
	//}
	//sb.WriteString(jxutils.IntPrice2StandardString(price))
	//sb.WriteString("元")
	return sb.String()
}

//新订单推送模板2
func NotifyNewOrder(order *model.GoodsOrder) (err error) {
	if order.VendorID == model.VendorIDELM {
		return nil
	}

	if len(order.Skus) == 0 {
		return nil
	}

	if !model.IsOrderSolid(order) {
		globals.SugarLogger.Infof("NotifyNewOrder orderID:%s is not solid", order.VendorOrderID)
		return nil
	}
	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")

	var expectedDeliveredTime = ""
	//if order.VendorID == model.VendorIDMTWM {
	if order.BusinessType == model.BusinessTypeImmediate {
		expectedDeliveredTime = "立即送"
	}
	if order.BusinessType == model.BusinessTypeDingshida {
		expectedDeliveredTime = "预定单"
	}
	//} else {
	//	if utils.Time2Str(order.ExpectedDeliveredTime) == "1970-01-01 00:00:00" || len(utils.Time2Str(order.ExpectedDeliveredTime)) == 0 {
	//		expectedDeliveredTime = "立即送"
	//	} else {
	//		expectedDeliveredTime = "预定单"
	//	}
	//}

	data := map[string]interface{}{
		"keyword1": map[string]interface{}{ //订单来源
			"value": fmt.Sprintf("%s #%d", model.VendorChineseNames[order.VendorID], order.OrderSeq),
			"color": VendorColors[order.VendorID],
		},
		"keyword2": map[string]interface{}{ //商家信息
			"value": storeDetail.Name,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword3": map[string]interface{}{ //顾客信息
			"value": order.ConsigneeName + " " + order.ConsigneeMobile,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword4": map[string]interface{}{ //下单时间及金额
			"value": utils.Time2Str(order.CreatedAt),
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword5": map[string]interface{}{ //配送时间
			"value": expectedDeliveredTime,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
	}
	storeID := jxutils.GetSaleStoreIDFromOrder(order)
	return SendMsgToStore(storeID, WX_NEWORDER_TEMPLATE_ID2, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
}

//func NotifyNewOrder(order *model.GoodsOrder) (err error) {
//	if order.VendorID == model.VendorIDELM {
//		return nil
//	}
//
//	if len(order.Skus) == 0 {
//		return nil
//	}
//
//	if !model.IsOrderSolid(order) {
//		globals.SugarLogger.Infof("NotifyNewOrder orderID:%s is not solid", order.VendorOrderID)
//		return nil
//	}
//	suffix := ""
//	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
//	if err == nil && storeDetail != nil {
//		suffix = storeDetail.CityName + "," + storeDetail.Name
//	}
//	sb := new(strings.Builder)
//	sb.WriteString(suffix + "老板，")
//	sb.WriteString(order.ConsigneeName)
//	sb.WriteString("购买了商品")
//	sb.WriteString(getOrderDetailBrief(order))
//	data := map[string]interface{}{
//		"first": map[string]interface{}{
//			"value": sb.String(),
//			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
//		},
//		"Day": map[string]interface{}{
//			"value": FormatDeliveryTime(order),
//			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
//		},
//		"orderId": map[string]interface{}{
//			"value": order.VendorOrderID,
//			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
//		},
//		"orderType": map[string]interface{}{
//			"value": fmt.Sprintf("%s 第%d号订单", model.VendorChineseNames[order.VendorID], order.OrderSeq),
//			"color": VendorColors[order.VendorID],
//		},
//		"customerName": map[string]interface{}{
//			"value": order.ConsigneeName,
//			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
//		},
//		"customerPhone": map[string]interface{}{
//			"value": order.ConsigneeMobile,
//			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
//		},
//	}
//	storeID := jxutils.GetSaleStoreIDFromOrder(order)
//	//return SendMsgToStore(storeID, WX_NEWORDER_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL+fmt.Sprintf("?jxStoreId=%v", storeID), data)
//	//dataStore := fmt.Sprintf(`{"address":"%s","cityName":"%s","id":%d,"name":"%s","payeeName":"%s","status":%d,"tel1":"%s","tel2":"%s"}`, storeDetail.Address, storeDetail.CityName, storeDetail.ID, storeDetail.Name, storeDetail.PayeeName, storeDetail.Status, storeDetail.Tel1, storeDetail.Tel2)
//	//url := WX_MINI_TO_ORDER_PAGE_URL + fmt.Sprintf("?jxStoreId=%v&data=%v", storeID, dataStore)
//	return SendMsgToStore(storeID, WX_NEWORDER_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
//}

func NotifyWaybillStatus(bill *model.Waybill, order *model.GoodsOrder, isBillAlreadyCandidate bool) (err error) {
	if order.VendorID == model.VendorIDELM {
		return nil
	}

	if !model.IsOrderSolid(order) {
		globals.SugarLogger.Infof("NotifyWaybillStatus orderID:%s is not solid", order.VendorOrderID)
		return nil
	}
	var title string
	suffix := ""
	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
	if err == nil && storeDetail != nil {
		suffix = storeDetail.CityName + "," + storeDetail.Name
	}
	templateID := ""
	remark := ""
	titleColor := ""
	switch bill.Status {
	case model.WaybillStatusAccepted:
		if bill.WaybillVendorID == model.VendorIDMTPS {
			templateID = WX_MTPS_DELIVERY_GRABDONE_TEMPLATE_ID
		} else if bill.WaybillVendorID == model.VendorIDDada {
			templateID = WX_DADA_DELIVERY_GRABDONE_TEMPLATE_ID
		}
		remark = FormatDeliveryTime(order)
		if isBillAlreadyCandidate {
			titleColor = WX_HIGHLEVEL_TEMPLATE_COLOR
			title = fmt.Sprintf("%s 第%d号订单由于之前安排的配送员长时间没有取货，我们已重新安排%s配送员%s负责配送。^_^", model.VendorChineseNames[bill.OrderVendorID], order.OrderSeq, model.VendorChineseNames[bill.WaybillVendorID], bill.CourierName)
		} else {
			titleColor = WX_HIGHLEVEL_TEMPLATE_COLOR2
			title = fmt.Sprintf("%s 第%d号订单长时间无人配送，我们已安排%s配送员%s负责配送。^_^", model.VendorChineseNames[bill.OrderVendorID], order.OrderSeq, model.VendorChineseNames[bill.WaybillVendorID], bill.CourierName)
		}
	case model.WaybillStatusDelivered:
		if bill.WaybillVendorID == model.VendorIDMTPS {
			templateID = WX_MTPS_DELIVERY_DONE_TEMPLATE_ID
		} else if bill.WaybillVendorID == model.VendorIDDada {
			templateID = WX_DADA_DELIVERY_DONE_TEMPLATE_ID
		}
		titleColor = VendorColors[bill.OrderVendorID]
		title = fmt.Sprintf("%s 第%d号订单的配送完成", model.VendorChineseNames[bill.OrderVendorID], order.OrderSeq)
	}
	if templateID != "" {
		data := map[string]interface{}{
			"first": map[string]interface{}{
				"value": suffix + title,
				"color": titleColor,
			},
			"keyword1": map[string]interface{}{
				"value": storeDetail.Name + "," + bill.VendorOrderID,
				"color": WX_NEW_ORDER_TEMPLATE_COLOR,
			},
			"keyword2": map[string]interface{}{
				"value": fmt.Sprintf("%s（%s）", bill.CourierName, model.VendorChineseNames[bill.WaybillVendorID]),
				"color": WX_NEW_ORDER_TEMPLATE_COLOR,
			},
			"keyword3": map[string]interface{}{
				"value": bill.CourierMobile,
				"color": WX_NEW_ORDER_TEMPLATE_COLOR,
			},
			"remark": map[string]interface{}{
				"value": remark,
				"color": WX_NEW_ORDER_TEMPLATE_COLOR,
			},
		}
		//err = SendMsgToStore(jxutils.GetSaleStoreIDFromOrder(order), templateID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeDetail.ID), WX_MINI_TO_ORDER_PAGE_URL+fmt.Sprintf("?jxStoreId=%v", storeDetail.ID), data)
		//dataStore := fmt.Sprintf(`{"address":"%s","cityName":"%s","id":%d,"name":"%s","payeeName":"%s","status":%d,"tel1":"%s","tel2":"%s"}`, storeDetail.Address, storeDetail.CityName, storeDetail.ID, storeDetail.Name, storeDetail.PayeeName, storeDetail.Status, storeDetail.Tel1, storeDetail.Tel2)
		//url := WX_MINI_TO_ORDER_PAGE_URL + fmt.Sprintf("?jxStoreId=%v&data=%v", storeDetail.ID, dataStore)
		err = SendMsgToStore(jxutils.GetSaleStoreIDFromOrder(order), templateID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeDetail.ID), WX_MINI_TO_ORDER_PAGE_URL, data)
		netprinter.NofityOrderMsg(jxcontext.AdminCtx, jxutils.GetSaleStoreIDFromOrder(order), order.VendorOrderID, title)
	}
	return err
}

func NotifyUserApplyCancel(order *model.GoodsOrder, cancelReason string) (err error) {
	if order.VendorID == model.VendorIDELM {
		return nil
	}

	if !model.IsOrderSolid(order) {
		globals.SugarLogger.Infof("NotifyUserApplyCancel orderID:%s is not solid", order.VendorOrderID)
		return nil
	}
	suffix := ""
	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
	if err == nil && storeDetail != nil {
		suffix = storeDetail.CityName + "," + storeDetail.Name
	}
	title := suffix + "老板，您有订单申请取消！请及时联系客户处理！"
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": title,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword1": map[string]interface{}{ // 订单编号
			"value": storeDetail.Name + fmt.Sprintf(",%s#%d号订单, %s", model.VendorChineseNames[order.VendorID], order.OrderSeq, order.VendorOrderID),
			"color": VendorColors[order.VendorID],
		},
		"keyword2": map[string]interface{}{ // 订单日期
			"value": utils.Time2Str(order.OrderCreatedAt),
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword3": map[string]interface{}{ // 订单内容
			"value": cancelReason,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"remark": map[string]interface{}{
			"value": order.ConsigneeMobile,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
	}
	storeID := jxutils.GetSaleStoreIDFromOrder(order)
	//err = SendMsgToStore(storeID, WX_ORDER_APPLY_CANCEL_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL+fmt.Sprintf("?jxStoreId=%v", storeID), data)
	//dataStore := fmt.Sprintf(`{"address":"%s","cityName":"%s","id":%d,"name":"%s","payeeName":"%s","status":%d,"tel1":"%s","tel2":"%s"}`, storeDetail.Address, storeDetail.CityName, storeDetail.ID, storeDetail.Name, storeDetail.PayeeName, storeDetail.Status, storeDetail.Tel1, storeDetail.Tel2)
	//url := WX_MINI_TO_ORDER_PAGE_URL + fmt.Sprintf("?jxStoreId=%v&data=%v", storeDetail.ID, dataStore)
	err = SendMsgToStore(storeID, WX_ORDER_APPLY_CANCEL_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
	netprinter.NofityOrderMsg(jxcontext.AdminCtx, jxutils.GetSaleStoreIDFromOrder(order), order.VendorOrderID, title)
	return err
}

func NotifyOrderChanged(order *model.GoodsOrder) (err error) {
	if order.VendorID == model.VendorIDELM {
		return nil
	}
	suffix := ""
	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
	if err == nil && storeDetail != nil {
		suffix = storeDetail.CityName + "," + storeDetail.Name
	}
	title := fmt.Sprintf("您有订单的信息已被修改")
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": suffix + title,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword1": map[string]interface{}{
			"value": storeDetail.Name + "," + order.VendorOrderID,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword2": map[string]interface{}{
			"value": "用户修改订单",
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword3": map[string]interface{}{
			"value": order.ConsigneeAddress + "，" + order.ConsigneeName + "，" + order.ConsigneeMobile + "，" + order.BuyerComment,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword4": map[string]interface{}{
			"value": utils.Time2Str(order.OrderCreatedAt),
			"color": VendorColors[order.VendorID],
		},
		"remark": map[string]interface{}{
			"value": "请及时处理",
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
	}
	storeID := jxutils.GetSaleStoreIDFromOrder(order)
	//err = SendMsgToStore(storeID, WX_ORDER_CHANGE_INFO_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL+fmt.Sprintf("?jxStoreId=%v", storeID), data)
	//dataStore := fmt.Sprintf(`{"address":"%s","cityName":"%s","id":%d,"name":"%s","payeeName":"%s","status":%d,"tel1":"%s","tel2":"%s"}`, storeDetail.Address, storeDetail.CityName, storeDetail.ID, storeDetail.Name, storeDetail.PayeeName, storeDetail.Status, storeDetail.Tel1, storeDetail.Tel2)
	//url := WX_MINI_TO_ORDER_PAGE_URL + fmt.Sprintf("?jxStoreId=%v&data=%v", storeDetail.ID, dataStore)
	err = SendMsgToStore(storeID, WX_ORDER_CHANGE_INFO_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
	netprinter.NofityOrderMsg(jxcontext.AdminCtx, jxutils.GetSaleStoreIDFromOrder(order), order.VendorOrderID, title)
	return err
}

func NotifyOrderCanceled(order *model.GoodsOrder) (err error) {
	if order.VendorID == model.VendorIDELM {
		return nil
	}

	if !model.IsOrderSolid(order) {
		globals.SugarLogger.Infof("NotifyOrderCanceled orderID:%s is not solid", order.VendorOrderID)
		return nil
	}
	suffix := ""
	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
	if err == nil && storeDetail != nil {
		suffix = storeDetail.CityName + "," + storeDetail.Name
	}
	title := fmt.Sprintf(suffix+"老板，您的订单%s第%d号订单, %s被取消了！", model.VendorChineseNames[order.VendorID], order.OrderSeq, order.VendorOrderID)
	var price int64
	if beego.BConfig.RunMode == model.ServerTypeFruits || beego.BConfig.RunMode == model.ServerTypePet {
		price = order.NewEarningPrice
	} else {
		if order.EarningType == model.EarningTypePoints {
			price = order.ActualPayPrice
		} else {
			price = order.EarningPrice
		}
	}
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": title,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"orderProductPrice": map[string]interface{}{
			"value": jxutils.IntPrice2StandardCurrencyString(price),
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"orderProductName": map[string]interface{}{
			"value": getOrderDetailBrief(order),
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"orderAddress": map[string]interface{}{
			"value": order.ConsigneeAddress,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"orderName": map[string]interface{}{
			"value": storeDetail.Name + "," + fmt.Sprintf("%s第%d号订单, %s", model.VendorChineseNames[order.VendorID], order.OrderSeq, order.VendorOrderID),
			"color": VendorColors[order.VendorID],
		},
		"remark": map[string]interface{}{
			"value": order.ConsigneeMobile,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
	}
	storeID := jxutils.GetSaleStoreIDFromOrder(order)
	//err = SendMsgToStore(storeID, WX_ORDER_ORDER_CANCELED_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL+fmt.Sprintf("?jxStoreId=%v", storeID), data)
	//dataStore := fmt.Sprintf(`{"address":"%s","cityName":"%s","id":%d,"name":"%s","payeeName":"%s","status":%d,"tel1":"%s","tel2":"%s"}`, storeDetail.Address, storeDetail.CityName, storeDetail.ID, storeDetail.Name, storeDetail.PayeeName, storeDetail.Status, storeDetail.Tel1, storeDetail.Tel2)
	//url := WX_MINI_TO_ORDER_PAGE_URL + fmt.Sprintf("?jxStoreId=%v&data=%v", storeDetail.ID, dataStore)
	err = SendMsgToStore(storeID, WX_ORDER_ORDER_CANCELED_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
	netprinter.NofityOrderMsg(jxcontext.AdminCtx, jxutils.GetSaleStoreIDFromOrder(order), order.VendorOrderID, title)
	return err
}

func PushJDBadCommentToWeiXin(comment *legacymodel.JxBadComments, isBadComment bool, order *model.GoodsOrder) (err error) {
	suffix := ""
	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
	if err == nil && storeDetail != nil {
		suffix = storeDetail.CityName + "," + storeDetail.Name
	}
	sb := new(strings.Builder)
	sb.WriteString(fmt.Sprintf(suffix+"你收到了%s客户(", model.VendorChineseNames[int(utils.Str2Int64WithDefault(comment.OrderFlag, 0))]))
	sb.WriteString(comment.Userphone)
	sb.WriteString(")的一条")
	sb.WriteString(utils.Int2Str(comment.Score))
	if isBadComment {
		sb.WriteString("星差评，请商家在1小时内及时联系客户处理!")
	} else {
		sb.WriteString("星中评，如有必要，请商家在1小时内及时联系客户处理!")
	}
	title := sb.String()
	var orderInfo string
	consigneeName := comment.Userphone
	if order != nil {
		orderInfo = fmt.Sprintf("%s第%d号订单, %s", model.VendorChineseNames[int(utils.Str2Int64WithDefault(comment.OrderFlag, 0))], order.OrderSeq, comment.OrderId)
		consigneeName = order.ConsigneeName
	} else {
		orderInfo = fmt.Sprintf("%s订单, %s", model.VendorChineseNames[int(utils.Str2Int64WithDefault(comment.OrderFlag, 0))] /*comment.OrderId*/, "")
	}
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": title,
			"color": WX_HIGHLEVEL_TEMPLATE_COLOR,
		},
		"keyword1": map[string]interface{}{
			"value": storeDetail.Name + "," + genJdCommentRemark(comment),
			"color": WX_TEMPLATE_VENDERCOLOR_BAD_COMMENTS,
		},
		"keyword2": map[string]interface{}{
			"value": comment.Score,
			"color": WX_TEMPLATE_VENDERCOLOR_BAD_COMMENTS,
		},
		"keyword3": map[string]interface{}{
			"value": orderInfo,
			"color": WX_TEMPLATE_VENDERCOLOR_BAD_COMMENTS,
		},
		"keyword4": map[string]interface{}{
			"value": consigneeName,
			"color": WX_TEMPLATE_VENDERCOLOR_BAD_COMMENTS,
		},
		"keyword5": map[string]interface{}{
			"value": comment.Userphone,
			"color": WX_TEMPLATE_VENDERCOLOR_BAD_COMMENTS,
		},
	}
	storeID := int(utils.Str2Int64(comment.Jxstoreid))
	err = SendMsgToStore(storeID, WX_BAD_COMMENT_PUSH_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_SHOW_COMMENTS_DETAIL_URL, storeID), WX_MINI_TO_SHOW_COMMENTS_DETAIL_URL, data)
	netprinter.NofityOrderMsg(jxcontext.AdminCtx, int(utils.Str2Int64(comment.Jxstoreid)), comment.OrderId, title)
	return err
}

func NotifySaleBill(storeID int, title, shopName, fileURL string) (err error) {
	if title == "" {
		title = "当期账单"
	}
	if shopName == "" {
		shopName = globals.StoreName
	}
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": "新的账单上传成功！",
			"color": "#00008B",
		},
		"keyword1": map[string]interface{}{
			"value": title,
		},
		"keyword2": map[string]interface{}{
			"value": "点击查看详情",
			"color": "#00008B",
		},
		"keyword3": map[string]interface{}{
			"value": shopName,
		},
		"keyword4": map[string]interface{}{
			"value": utils.GetCurTimeStr(),
		},
		"remark": map[string]interface{}{
			"value": "手机买菜上京西，极速到家送惊喜",
		},
	}
	return SendMsgToStore(storeID, WX_SALE_BILL_TEMPLATE_ID, fileURL, "", data)
}

func NotifyStoreOpRequestStatus(isAccepted bool, storeID, nameID int, spuName string, originalUnitPrice, unitPrice int, rejectReason string) (err error) {
	templateID := ""
	fileURL := globals.WxBackstageHost + fmt.Sprintf("%s%d", WX_TO_STORE_SKU_PAGE_URL, storeID)
	data := make(map[string]interface{})
	if isAccepted {
		templateID = WX_CHANGE_APPROVED_TEMPLATE_ID
		data = map[string]interface{}{
			"first": map[string]interface{}{
				"value": fmt.Sprintf("%s%s元，修改为%s元，已经通过审核。^_^", spuName, jxutils.IntPrice2StandardString(int64(originalUnitPrice)), jxutils.IntPrice2StandardString(int64(unitPrice))),
				"color": "#333333",
			},
			"keyword1": map[string]interface{}{
				"value": "审核通过",
				"color": "#2E408E",
			},
			"keyword2": map[string]interface{}{
				"value": utils.Time2Str(time.Now()),
				"color": "#2E408E",
			},
			"remark": map[string]interface{}{
				"value": "点击查看详情",
			},
		}
	} else {
		templateID = WX_CHANGE_REJECTED_TEMPLATE_ID
		data = map[string]interface{}{
			"first": map[string]interface{}{
				"value": fmt.Sprintf("您好！抱歉的通知您。%s%s元，修改为%s元，未通过审核。原因：%s", spuName, jxutils.IntPrice2StandardString(int64(originalUnitPrice)), jxutils.IntPrice2StandardString(int64(unitPrice)), rejectReason),
				"color": "#E80000",
			},
			"keyword1": map[string]interface{}{
				"value": "1",
				"color": "#2E408E",
			},
			"keyword2": map[string]interface{}{
				"value": "审核不通过",
				"color": "#2E408E",
			},
			"keyword3": map[string]interface{}{
				"value": utils.Time2Str(time.Now()),
				"color": "#2E408E",
			},
			"keyword4": map[string]interface{}{
				"value": "审核不通过",
				"color": "#2E408E",
			},
			"remark": map[string]interface{}{
				"value": "请您及时到商品管理修改价格，修改后请重新提交。",
			},
		}
	}
	err = SendMsgToStore(storeID, templateID, fileURL, WX_MINI_TO_STORE_SKU_PAGE_URL, data)
	return err
}

func NotifyStoreMessage(storeID, msgID, msgStatusID int, msg *model.Message) (err error) {
	templateID := ""
	fileURL := globals.WxBackstageHost + fmt.Sprintf(WX_TO_SHOW_MSG, msgID, msgStatusID)
	//fileURL := globals.WxBackstageHost + fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID)
	data := make(map[string]interface{})
	switch msg.Type {
	case model.MessageTypeStore:
		templateID = WX_NORMAL_STORE_MSG_TEMPLATE_ID
		data = map[string]interface{}{
			"first": map[string]interface{}{
				"value": msg.Content,
				"color": "#333333",
			},
			"keyword1": map[string]interface{}{
				"value": msg.Title,
				"color": "#2E408E",
			},
			"keyword2": map[string]interface{}{
				"value": utils.GetCurTimeStr(),
				"color": "#2E408E",
			},
			"remark": map[string]interface{}{
				"value": "",
			},
		}
	case model.MessageTypeStoreAct:
		actMap := &model.MessageActInfo{}
		err = json.Unmarshal([]byte(msg.ActInfo), &actMap)
		templateID = WX_NORMAL_STORE_ACT_MSG_TEMPLATE_ID
		data = map[string]interface{}{
			"first": map[string]interface{}{
				"value": "即将开展[" + msg.Title + "]活动，详情点击",
				"color": "#333333",
			},
			"keyword1": map[string]interface{}{
				"value": msg.Title,
				"color": "#2E408E",
			},
			"keyword2": map[string]interface{}{
				"value": actMap.BeginAt + "-" + actMap.EndAt,
				"color": "#2E408E",
			},
			"keyword3": map[string]interface{}{
				"value": model.VendorNames[int(utils.Interface2Float64WithDefault((actMap.VendorID), 0))],
				"color": "#2E408E",
			},
			"remark": map[string]interface{}{
				"value": msg.Content,
			},
		}
	}
	return SendMsgToStore(storeID, templateID, fileURL, fmt.Sprintf(WX_MINI_TO_SHOW_MSG, msgID, msgStatusID), data)
}

func NotifyAfsOrderStatus(afsOrder *model.AfsOrder) (err error) {
	if afsOrder.VendorID == model.VendorIDELM {
		return nil
	}
	suffix := ""
	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromAfsOrder(afsOrder), afsOrder.VendorID, "")
	if err == nil && storeDetail != nil {
		suffix = storeDetail.CityName + "," + storeDetail.Name
	}
	var templateID, comment string
	if afsOrder.Status == model.AfsOrderStatusWait4Approve {
		templateID = WX_AFS_ORDER_WAIT4APPROVE_TEMPLATE_ID
		comment = "您有新售后单，请尽快处理"
	} else if afsOrder.Status == model.AfsOrderStatusWait4ReceiveGoods {
		templateID = WX_AFS_ORDER_STATUS_CHANGED_TEMPLATE_ID
		comment = "商家您好！如顾客商品已成功退回，请点击确认收货"
	} else {
		return err
	}
	order, err := partner.CurOrderManager.LoadOrder(afsOrder.VendorOrderID, afsOrder.VendorID)
	if err != nil {
		return err
	}

	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": suffix + fmt.Sprintf("%s 第%d号订单, %s", model.VendorChineseNames[afsOrder.VendorID], order.OrderSeq, afsOrder.VendorOrderID),
			"color": WX_HIGHLEVEL_TEMPLATE_COLOR2,
		},
		"keyword1": map[string]interface{}{
			"value": storeDetail.Name + "," + fmt.Sprintf("%s 第%d号订单：", model.VendorChineseNames[afsOrder.VendorID], order.OrderSeq) + afsOrder.AfsOrderID,
			"color": WX_TEMPLATE_VENDERCOLOR_JDDJ,
		},
		"keyword2": map[string]interface{}{
			"value": model.OrderStatusName[afsOrder.Status],
			"color": WX_HIGHLEVEL_TEMPLATE_COLOR,
		},
		"keyword3": map[string]interface{}{
			"value": utils.Time2Str(afsOrder.AfsCreatedAt),
			"color": VendorColors[order.VendorID],
		},
		"remark": map[string]interface{}{
			"value": comment,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
	}
	storeID := jxutils.GetSaleStoreIDFromAfsOrder(afsOrder)
	//dataStore := fmt.Sprintf(`{"address":"%s","cityName":"%s","id":%d,"name":"%s","payeeName":"%s","status":%d,"tel1":"%s","tel2":"%s"}`, storeDetail.Address, storeDetail.CityName, storeDetail.ID, storeDetail.Name, storeDetail.PayeeName, storeDetail.Status, storeDetail.Tel1, storeDetail.Tel2)
	//url := WX_MINI_TO_ORDER_PAGE_URL + fmt.Sprintf("?jxStoreId=%v&data=%v", storeDetail.ID, dataStore)
	err = SendMsgToStore(storeID, templateID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
	netprinter.NofityOrderMsg(jxcontext.AdminCtx, jxutils.GetSaleStoreIDFromAfsOrder(afsOrder), afsOrder.VendorOrderID, comment)
	return err
}

func FormatDeliveryTime(order *model.GoodsOrder) string {
	var tmpTime time.Time
	if order.ExpectedDeliveredTime == utils.DefaultTimeValue {
		tmpTime = order.OrderCreatedAt.Add(1 * time.Hour)
	} else {
		tmpTime = order.ExpectedDeliveredTime
	}
	left := tmpTime.Sub(time.Now()) / time.Minute
	leftHours := left / 60
	leftMinutes := left % 60
	return fmt.Sprintf("请于%s前送达（剩余时间%d小时%d分钟）", utils.Time2Str(tmpTime), leftHours, leftMinutes)
}

func genJdCommentRemark(comment *legacymodel.JxBadComments) string {
	sb := new(strings.Builder)
	sb.WriteString("评价标签：")
	sb.WriteString(comment.Vendertags)
	sb.WriteString(" 评价内容：")
	sb.WriteString(comment.Scorecontent)
	return sb.String()
}

func NotifyStoreStatusChanged(openUserID, title, content string) (err error) {
	SmartMessageTemplateSend(openUserID, WS_NOTIFY_STORE_STATUS_CHHANGED_TEMPLATE_ID, "", "", map[string]interface{}{
		"first": map[string]interface{}{
			"value": title,
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword1": map[string]interface{}{
			"value": "",
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword2": map[string]interface{}{
			"value": utils.Time2Str(time.Now()),
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword3": map[string]interface{}{
			"value": content,
			"color": WX_HIGHLEVEL_TEMPLATE_COLOR,
		},
		"keyword4": map[string]interface{}{
			"value": "",
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"remark": map[string]interface{}{
			"value": "",
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
	})
	return err
}

func NotifyStoreAlertMessage(storeID int, storeName, title, content string) (err error) {
	templateID := WX_STORE_ALERT_TEMPLATE_ID
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": "",
			"color": "#333333",
		},
		"keyword1": map[string]interface{}{
			"value": storeName,
			"color": "#2E408E",
		},
		"keyword2": map[string]interface{}{
			"value": utils.GetCurTimeStr(),
			"color": "#2E408E",
		},
		"keyword3": map[string]interface{}{
			"value": content,
			"color": "#2E408E",
		},
		"remark": map[string]interface{}{
			"value": "",
		},
	}
	if false {
		err = SendMsgToStore(storeID, templateID, "", "", data)
	}
	return err
}

func SendStoreMessage(ctx *jxcontext.Context, title, content string, storeIDs []int, imgs []string, actInfo string, messageType int, isAsync, isContinueWhenError bool) (hint string, err error) {
	db := dao.GetDB()
	//权限
	if permission.IsRoled(ctx) {
		if storeIDsMap, err := permission.GetUserStoresResultMap(ctx.GetUserID()); err == nil {
			var storeIDs2 []int
			if len(storeIDs) > 0 {
				for _, v := range storeIDs {
					if storeIDsMap[v] != 0 {
						storeIDs2 = append(storeIDs2, v)
					}
				}
				if len(storeIDs2) == 0 {
					storeIDs2 = append(storeIDs2, -1)
				}
			} else {
				for k, _ := range storeIDsMap {
					storeIDs2 = append(storeIDs2, k)
				}
			}
			storeIDs = nil
			storeIDs = storeIDs2
		}
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	msg := &model.Message{
		Title:   title,
		Content: content,
		Type:    int8(messageType),
		ActInfo: actInfo,
		Imgs:    strings.Join(imgs, ","),
	}
	dao.WrapAddIDCULDEntity(msg, ctx.GetUserName())
	if err = dao.CreateEntity(db, msg); err != nil {
		dao.Rollback(db, txDB)
		return "", err
	}
	msgStatusList := make([]*model.MessageStatus, len(storeIDs))
	for k, storeID := range storeIDs {
		msgStatus := &model.MessageStatus{
			MessageID: msg.ID,
			StoreID:   storeID,
			Status:    model.MessageStatusNew,
		}
		dao.WrapAddIDCULDEntity(msgStatus, ctx.GetUserName())
		if err = dao.CreateEntity(db, msgStatus); err != nil {
			dao.Rollback(db, txDB)
			return "", err
		}
		msgStatusList[k] = msgStatus
	}
	dao.Commit(db, txDB)

	rootTask := tasksch.NewParallelTask("SendStoreMessage", nil, ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
		db := dao.GetDB()
		msgStatus := batchItemList[0].(*model.MessageStatus)
		if err = NotifyStoreMessage(msgStatus.StoreID, msgStatus.MessageID, msgStatus.ID, msg); err == nil {
			msgStatus.Status = model.MessageStatusSendAllSuccess
		} else {
			msgStatus.Status = model.MessageStatusSendAllFailed
		}
		dao.WrapUpdateULEntity(msgStatus, ctx.GetUserName())
		_, err = dao.UpdateEntity(db, msgStatus)
		return nil, err
	}, msgStatusList)
	tasksch.HandleTask(rootTask, nil, len(msgStatusList) > 5).Run()

	if !isAsync {
		_, err = rootTask.GetResult(0)
	} else {
		hint = rootTask.ID
	}
	return "", err
}

func NotifyAdjustOrder(order *model.GoodsOrder) (err error) {
	if order.VendorID == model.VendorIDELM {
		return nil
	}
	if len(order.Skus) == 0 {
		return
	}
	suffix := ""
	storeDetail, err := dao.GetStoreDetail(dao.GetDB(), jxutils.GetSaleStoreIDFromOrder(order), order.VendorID, "")
	if err == nil && storeDetail != nil {
		suffix = storeDetail.CityName + "," + storeDetail.Name
	}
	if !model.IsOrderSolid(order) {
		globals.SugarLogger.Infof("NotifyAdjustOrder orderID:%s is not solid", order.VendorOrderID)
		return nil
	}
	sb := new(strings.Builder)
	sb.WriteString(suffix + "老板，")
	sb.WriteString(order.ConsigneeName)
	sb.WriteString("调整了订单商品，请及时查看！")
	sb.WriteString(getOrderDetailBrief(order))
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": sb.String(),
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword1": map[string]interface{}{
			"value": storeDetail.Name + "," + "调整了订单商品,请点击查看",
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword2": map[string]interface{}{
			"value": utils.Time2Str(order.CreatedAt),
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
		"keyword3": map[string]interface{}{
			"value": model.OrderStatusName[order.Status],
			"color": WX_NEW_ORDER_TEMPLATE_COLOR,
		},
	}
	storeID := jxutils.GetSaleStoreIDFromOrder(order)
	//dataStore := fmt.Sprintf(`{"address":"%s","cityName":"%s","id":%d,"name":"%s","payeeName":"%s","status":%d,"tel1":"%s","tel2":"%s"}`, storeDetail.Address, storeDetail.CityName, storeDetail.ID, storeDetail.Name, storeDetail.PayeeName, storeDetail.Status, storeDetail.Tel1, storeDetail.Tel2)
	//url := WX_MINI_TO_ORDER_PAGE_URL + fmt.Sprintf("?jxStoreId=%v&data=%v", storeDetail.ID, dataStore)
	return SendMsgToStore(storeID, WX_ADJUSTORDER_TEMPLATE_ID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), WX_MINI_TO_ORDER_PAGE_URL, data)
}

func SendStoreMessageKnowledge(ctx *jxcontext.Context, title string, knowIDs, storeIDs []int) (hint string, err error) {
	var (
		db      = dao.GetDB()
		content string
	)
	knows, err := dao.GetKnowledgeDepotNoPage(db, knowIDs)
	for k, v := range knows {
		if k != 0 {
			content += weixinapi.SNSCutLine
		}
		content += v.Content
	}
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	msg := &model.Message{
		Title:   title,
		Content: content,
		Type:    4,
	}
	dao.WrapAddIDCULDEntity(msg, ctx.GetUserName())
	if err = dao.CreateEntity(db, msg); err != nil {
		dao.Rollback(db, txDB)
		return "", err
	}
	msgStatusList := make([]*model.MessageStatus, len(storeIDs))
	for k, storeID := range storeIDs {
		msgStatus := &model.MessageStatus{
			MessageID: msg.ID,
			StoreID:   storeID,
			Status:    model.MessageStatusNew,
		}
		dao.WrapAddIDCULDEntity(msgStatus, ctx.GetUserName())
		if err = dao.CreateEntity(db, msgStatus); err != nil {
			dao.Rollback(db, txDB)
			return "", err
		}
		msgStatusList[k] = msgStatus
	}
	dao.Commit(db, txDB)

	rootTask := tasksch.NewParallelTask("SendStoreMessageKnowledge", nil, ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
		db := dao.GetDB()
		msgStatus := batchItemList[0].(*model.MessageStatus)
		if err = NotifyKnowledge(msgStatus.StoreID, title); err == nil {
			msgStatus.Status = model.MessageStatusSendAllSuccess
		} else {
			msgStatus.Status = model.MessageStatusSendAllFailed
		}
		dao.WrapUpdateULEntity(msgStatus, ctx.GetUserName())
		_, err = dao.UpdateEntity(db, msgStatus)
		return nil, err
	}, msgStatusList)
	tasksch.HandleTask(rootTask, nil, true).Run()
	//if !isAsync {
	//	_, err = rootTask.GetResult(0)
	//} else {
	hint = rootTask.ID
	//}
	return "", err
}

func NotifyKnowledge(storeID int, title string) (err error) {
	templateID := WX_KNOWLEDGE_BASE_TEMPLATE_ID
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": "今日每日一词已更新！",
			"color": "#333333",
		},
		"keyword1": map[string]interface{}{
			"value": time.Now().Format("2006-01-02"),
			"color": "#2E408E",
		},
		"keyword2": map[string]interface{}{
			"value": title,
			"color": "#2E408E",
		},
		"remark": map[string]interface{}{
			"value": "",
		},
	}
	return SendMsgToStore(storeID, templateID, globals.WxBackstageHost+fmt.Sprintf("%s%d", WX_TO_ORDER_PAGE_URL, storeID), "pages/store-center/main", data)
}

func SendUserMessage(ctx *jxcontext.Context, title, content string, userIDs []string, isAsync, isContinueWhenError bool) (hint string, err error) {
	db := dao.GetDB()
	txDB, _ := dao.Begin(db)
	defer func() {
		if r := recover(); r != nil {
			dao.Rollback(db, txDB)
			panic(r)
		}
	}()
	msg := &model.Message{
		Title:   title,
		Content: content,
		Type:    model.MessageTypeUser,
	}
	dao.WrapAddIDCULDEntity(msg, ctx.GetUserName())
	if err = dao.CreateEntity(db, msg); err != nil {
		dao.Rollback(db, txDB)
		return "", err
	}
	msgStatusList := make([]*model.MessageStatus, len(userIDs))
	for k, userID := range userIDs {
		msgStatus := &model.MessageStatus{
			MessageID: msg.ID,
			UserID:    userID,
			Status:    model.MessageStatusNew,
		}
		dao.WrapAddIDCULDEntity(msgStatus, ctx.GetUserName())
		if err = dao.CreateEntity(db, msgStatus); err != nil {
			dao.Rollback(db, txDB)
			return "", err
		}
		msgStatusList[k] = msgStatus
	}
	dao.Commit(db, txDB)

	rootTask := tasksch.NewParallelTask("SendUserMessage", nil, ctx, func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
		db := dao.GetDB()
		msgStatus := batchItemList[0].(*model.MessageStatus)
		if err = NotifyUserMessage(msgStatus.UserID, msg.Title, msg.Content); err == nil {
			msgStatus.Status = model.MessageStatusSendAllSuccess
		} else {
			msgStatus.Status = model.MessageStatusSendAllFailed
		}
		dao.WrapUpdateULEntity(msgStatus, ctx.GetUserName())
		_, err = dao.UpdateEntity(db, msgStatus)
		return nil, err
	}, msgStatusList)
	tasksch.HandleTask(rootTask, nil, len(msgStatusList) > 5).Run()
	if !isAsync {
		_, err = rootTask.GetResult(0)
	} else {
		hint = rootTask.ID
	}
	return "", err
}

func NotifyUserMessage(userID string, title, content string) (err error) {
	templateID := WX_NORMAL_STORE_MSG_TEMPLATE_ID
	data := map[string]interface{}{
		"first": map[string]interface{}{
			"value": content,
			"color": "#333333",
		},
		"keyword1": map[string]interface{}{
			"value": title,
			"color": "#2E408E",
		},
		"keyword2": map[string]interface{}{
			"value": utils.GetCurTimeStr(),
			"color": "#2E408E",
		},
		"remark": map[string]interface{}{
			"value": "",
		},
	}
	return SendMsgToUser(userID, templateID, data)
}

func SendMsgToUser(userID string, templateID string, data interface{}) (err error) {
	authBinds, err := dao.GetUserBindAuthInfo(dao.GetDB(), userID, model.AuthBindTypeAuth, []string{"weixinsns"}, "", "", []string{"wx2bb99eb5d2c9b82c"})
	if err != nil {
		return err
	}
	successCount := 0
	if len(authBinds) == 0 {
		return fmt.Errorf("此用户未找到微信认证方式！userID: %v", userID)
	}
	if err2 := SmartMessageTemplateSend(authBinds[0].AuthID, templateID, "", "", data); err2 == nil {
		successCount++
	} else {
		err = err2
	}
	if successCount > 0 {
		err = nil // 只要成功一个都当成成功
	}
	if err != nil {
		globals.SugarLogger.Debugf("SendMsgToUser all failed userID:%d, templateID:%s, error:%v", userID, templateID, err)
	}
	return err
}
