Files
jx-callback/business/jxutils/jxutils.go
2019-04-01 21:00:15 +08:00

456 lines
13 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 jxutils
import (
"fmt"
"math"
"math/rand"
"regexp"
"strings"
"sync"
"time"
"git.rosy.net.cn/baseapi/platformapi/autonavi"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/baseapi/utils/routinepool"
"git.rosy.net.cn/jx-callback/business/model"
"git.rosy.net.cn/jx-callback/globals/api"
)
var (
routinePool *routinepool.Pool
skuNamePat *regexp.Regexp
)
type SyncMapWithTimeout struct {
sync.Map
timers sync.Map
}
func init() {
rand.Seed(time.Now().Unix())
routinePool = routinepool.New(1000, 1000)
// Go regex does not support lookarounds.
// https://stackoverflow.com/questions/38933898/error-parsing-regexp-invalid-or-unsupported-perl-syntax
skuNamePat = regexp.MustCompile(`([\(\[【][^\(\[【\)\]】]*[\)\]】])?(.*?)([(].*[)])?\s*约?([1-9][\d\.]*)(g|G|kg|kG|Kg|KG|l|L|ml|mL|Ml|ML|克)\s*([(].*[)])?\s*(?:\/||)\s*([^\s()]{0,2})\s*([(].*[)])?$`)
}
func (m *SyncMapWithTimeout) StoreWithTimeout(key, value interface{}, timeout time.Duration) {
m.Map.Store(key, value)
m.timers.Store(key, utils.AfterFuncWithRecover(timeout, func() {
m.Delete(key)
}))
}
func (m *SyncMapWithTimeout) Delete(key interface{}) {
m.Map.Delete(key)
if value, ok := m.timers.Load(key); ok {
timer := value.(*time.Timer)
timer.Stop()
}
m.timers.Delete(key)
}
func getJxStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
if order.JxStoreID != 0 {
return order.JxStoreID
}
return order.StoreID
}
// 此函数得到的是order的销售门店京西ID与GetJxStoreIDFromOrder的区别是order.StoreID的解释不同参考其它相关资料
func GetSaleStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
if order.VendorID != model.VendorIDWSC {
return getJxStoreIDFromOrder(order)
}
return order.StoreID
}
// 此函数得到的是order的商品的展示门店京西ID与GetJxStoreIDFromOrder的区别是order.StoreID的解释不同参考其它相关资料
func GetShowStoreIDFromOrder(order *model.GoodsOrder) (retVal int) {
if order.VendorID != model.VendorIDWSC {
return getJxStoreIDFromOrder(order)
}
return order.JxStoreID
}
func SplitUniversalOrderID(universalOrderID string) (orderID string, vendorID int) {
index := strings.Index(universalOrderID, "|")
if index != -1 {
orderID = universalOrderID[:index]
vendorID = int(utils.Str2Int64(universalOrderID[index+1:]))
} else {
if vendorID = GetPossibleVendorIDFromVendorOrderID(universalOrderID); vendorID == model.VendorIDUnknown {
// globals.SugarLogger.Errorf("unkown order type:%v", universalOrderID)
panic(fmt.Sprintf("unkown order type, orderID:%s", universalOrderID))
}
orderID = universalOrderID
}
return orderID, vendorID
}
func GetPossibleVendorIDFromVendorOrderID(vendorOrderID string) (vendorID int) {
vendorID = model.VendorIDUnknown
if vendorOrderIDInt64 := utils.Str2Int64WithDefault(vendorOrderID, 0); vendorOrderIDInt64 > 0 {
orderIDLen := len(vendorOrderID)
// 5287873015048 13
// 15380342248732 14
// 800402581000221 15
// 33437032333978492 17
// 3022716176275221584 19
// 800402581000221 jd order
// 3022716176275221584 elm order
if orderIDLen == len("800402581000221") {
vendorID = model.VendorIDJD
} else if orderIDLen == len("3022716176275221584") {
vendorID = model.VendorIDELM
} else if orderIDLen == len("15380342248732") {
vendorID = model.VendorIDEBAI
} else if orderIDLen == len("33437032333978492") {
// vendorID = model.VendorIDMTWM
vendorID = model.VendorIDEBAI // 饿百零售开放平台订单接口中订单ID“order_id”字段长度将调整为19位和饿了么订单ID“eleme_order_id”字段格式保持一致。
} else if orderIDLen == len("5287873015048") {
vendorID = model.VendorIDWSC
}
}
return vendorID
}
func ComposeUniversalOrderID(orderID string, vendorID int) string {
// return fmt.Sprintf("%s|%d", orderID, vendorID)
return orderID // 当前用长度就能区分先不加上vendorID
}
func GetUniversalOrderIDFromWaybill(bill *model.Waybill) string {
return ComposeUniversalOrderID(bill.VendorOrderID, bill.OrderVendorID)
}
func GetUniversalOrderIDFromOrder(order *model.GoodsOrder) string {
return ComposeUniversalOrderID(order.VendorOrderID, order.VendorID)
}
func GetUniversalOrderIDFromOrderStatus(status *model.OrderStatus) string {
return ComposeUniversalOrderID(status.VendorOrderID, status.VendorID)
}
// distance单位为米
func ConvertDistanceToLogLat(lng, lat, distance, angle float64) (newLng, newLat float64) {
oneDu := 111319.55 // 单位为米
newLng = lng + (distance*math.Sin(angle*math.Pi/180))/(oneDu*math.Cos(lat*math.Pi/180)) //将距离转换成经度的计算公式
newLat = lat + (distance*math.Cos(angle*math.Pi/180))/oneDu //将距离转换成纬度的计算公式
return newLng, newLat
}
// 返回结果单元为公里
func EarthDistance(lng1, lat1, lng2, lat2 float64) float64 {
radius := 6378.137
rad := math.Pi / 180.0
lat1 = lat1 * rad
lng1 = lng1 * rad
lat2 = lat2 * rad
lng2 = lng2 * rad
theta := lng2 - lng1
dist := math.Acos(math.Sin(lat1)*math.Sin(lat2) + math.Cos(lat1)*math.Cos(lat2)*math.Cos(theta))
return dist * radius
}
func StandardCoordinate2Int(value float64) int {
return int(math.Round(value * 1000000))
}
func IntCoordinate2Standard(value int) float64 {
return float64(value) / 1000000
}
func IntCoordinate2MarsStandard(gpsLng, gpsLat int, coordinateType int) (marsLng, marsLat float64, err error) {
marsLng = IntCoordinate2Standard(gpsLng)
marsLat = IntCoordinate2Standard(gpsLat)
coordSys := ""
switch coordinateType {
case model.CoordinateTypeGPS:
coordSys = autonavi.CoordSysGPS
case model.CoordinateTypeMars:
coordSys = autonavi.CoordSysAutonavi
case model.CoordinateTypeBaiDu:
coordSys = autonavi.CoordSysBaidu
case model.CoordinateTypeMapbar:
coordSys = autonavi.CoordSysMapbar
default:
panic(fmt.Sprintf("known coordinate type:%d", coordinateType))
}
return api.AutonaviAPI.CoordinateConvert(marsLng, marsLat, coordSys)
}
func IntPrice2Standard(value int64) float64 {
return float64(value) / 100
}
func StandardPrice2Int(value float64) int64 {
return int64(math.Round(value * 100))
}
func IntPrice2StandardString(value int64) string {
return fmt.Sprintf("%.2f", IntPrice2Standard(value))
}
func CallMsgHandler(handler func(), primaryID string) {
routinePool.CallFun(func() {
handler()
}, primaryID)
}
func CallMsgHandlerAsync(handler func(), primaryID string) {
routinePool.CallFunAsync(func() {
handler()
}, primaryID)
}
func GetNameAndUnitFromSkuName(fullName string) (name string, unit string) {
unit = "份"
index := strings.Index(fullName, "/")
if index >= 0 {
name = fullName[:index]
unitTmp := []rune(fullName[index+1:])
if len(unitTmp) >= 1 {
unit = string(unitTmp[:1])
}
} else {
name = fullName
}
return name, unit
}
func MapValue2Scope(value, fromMin, fromMax, toMin, toMax int64) int64 {
if value < fromMin {
value = fromMin
}
if value > fromMax {
value = fromMax
}
return int64(math.Round(float64(value-fromMin)/float64(fromMax-fromMin)*float64(toMax-toMin) + float64(toMin)))
}
func Errs2Str(sep string, errs ...error) (retVal string) {
if sep == "" {
sep = "\n"
}
for _, err := range errs {
if err != nil {
retVal += err.Error() + sep
}
}
return retVal
}
func IntWeight2Float(weight int) float32 {
return float32(weight) / 1000.0
}
func FloatWeight2Int(weight float32) int {
return int(math.Round(float64(weight * 1000)))
}
func ComposeSkuName(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int) (skuName string) {
if prefix != "" {
skuName = "[" + prefix + "]"
}
skuName += name
if unit == "份" {
skuName += "约"
}
if unit != "" {
skuName += ComposeSkuSpec(spec_quality, spec_unit)
skuName += "/" + unit
}
if comment != "" {
skuName += "(" + comment + ")"
}
if maxLen > 0 {
runeList := []rune(skuName)
if len(runeList) > maxLen {
skuName = string(runeList[:maxLen])
}
}
return skuName
}
func ComposeSpuName(prefix, name string, maxLen int) (spuName string) {
if prefix != "" {
spuName = "[" + prefix + "]"
}
spuName += name
return utils.LimitUTF8StringLen(spuName, maxLen)
}
func ComposeSkuSpec(spec_quality float32, spec_unit string) (spec string) {
if spec_unit != "" {
if math.Round(float64(spec_quality)) == float64(spec_quality) || (spec_unit != "L" && spec_unit != "kg") {
spec = fmt.Sprintf("%d", int(spec_quality))
} else {
spec = strings.TrimRight(fmt.Sprintf("%.2f", spec_quality), "0.")
}
spec += spec_unit
}
return spec
}
// 1商品特殊前缀
// 2商品名字
// 3商品说明1可缺失
// 4质量数字
// 5质量单位
// 6商品说明2可缺失
// 7商品单位
// 8商品说明3可缺失
func SplitSkuName(skuName string) (prefix, name, comment, specUnit, unit string, specQuality float32) {
searchResult := skuNamePat.FindStringSubmatch(skuName)
if searchResult != nil {
if searchResult[3] != "" {
comment = searchResult[3]
} else if searchResult[6] != "" {
comment = searchResult[6]
} else if searchResult[8] != "" {
comment = searchResult[8]
}
comment = TrimDecorationChar(comment)
name = TrimDecorationChar(searchResult[2])
// if comment != "" {
// // if utf8.RuneCountInString(comment) <= 5 {
// // name += "-" + comment
// // comment = ""
// // }
// }
specUnit = strings.ToLower(strings.Replace(searchResult[5], "克", "g", -1))
if specUnit == "l" {
specUnit = "L"
}
if searchResult[7] == "" {
unit = "份"
} else {
unit = searchResult[7]
}
specQuality = float32(utils.Str2Float64(searchResult[4]))
prefix = TrimDecorationChar(searchResult[1])
}
return prefix, name, comment, specUnit, unit, specQuality
}
func MakeValidationMapFromSlice(validValues []string, flag int) map[string]int {
retVal := make(map[string]int)
for _, v := range validValues {
retVal[v] = flag
}
return retVal
}
func ComposeQiniuResURL(key string) string {
return "http://image.jxc4.com/" + key
}
func IsLegalMobileNumber(num int64) bool {
return num >= 13000000000 && num <= 19999999999
}
func TrimDecorationChar(value string) string {
return strings.Trim(value, " \t\n[]()【】()-_——")
}
func BatchStr2Time(strTime ...string) (timeList []time.Time, err error) {
timeList = make([]time.Time, len(strTime))
for k, v := range strTime {
if v == "" {
timeList[k] = utils.DefaultTimeValue
} else {
if timeList[k], err = utils.TryStr2Time(v); err != nil {
return nil, err
}
}
}
return timeList, nil
}
// strAndObjAddPairs必须按字符串1,转换地址1字符串2转换地址2这样的格式传递
func Strings2Objs(strAndObjAddPairs ...interface{}) (err error) {
str := ""
for k, v := range strAndObjAddPairs {
if k%2 == 0 {
str, _ = v.(string)
} else if str != "" {
if err = utils.UnmarshalUseNumber([]byte(str), v); err != nil {
return err
}
}
}
return nil
}
func GetFeieOrderContent(order *model.GoodsOrder, storeTel string) (content string) {
expectedDeliveryTime := order.ExpectedDeliveredTime
if utils.IsTimeZero(expectedDeliveryTime) {
expectedDeliveryTime = order.OrderCreatedAt.Add(1 * time.Hour)
}
orderFmt := `
<CB>京西菜市</CB><BR>
<C>手机买菜上京西</C><BR>
<C>极速到家送惊喜</C>
--------------------------------<BR>
下单时间: %s<BR>
预计送达: %s<BR>
订单编号: %s<BR>
<BR>
<B>%s#%d</B><BR><BR>
<QR>%s</QR>
<BR>
客户: %s<BR>
电话: %s<BR>
地址: %s<BR>
<BR>
客户备注: <BR>
<B>%s</B><BR>
<BR>
<BOLD>实际支付:</BOLD>%.2f<BR>
<BR>
商品明细: <BR>
品名 数量 单价 小计
--------------------------------<BR>`
orderParams := []interface{}{
utils.Time2Str(order.OrderCreatedAt),
utils.Time2Str(expectedDeliveryTime),
order.VendorOrderID,
GetVendorName(order.VendorID),
order.OrderSeq,
order.VendorOrderID,
order.ConsigneeName,
order.ConsigneeMobile,
order.ConsigneeAddress,
order.BuyerComment,
IntPrice2Standard(order.ActualPayPrice),
}
for _, sku := range order.Skus {
orderFmt += `%s<BR>`
orderFmt += `%10d%10.2f%10.2f<BR>`
orderParams = append(orderParams, sku.SkuName, sku.Count, IntPrice2Standard(sku.SalePrice), IntPrice2Standard(sku.SalePrice*int64(sku.Count)))
}
orderFmt += `<BR>
<BOLD>共%d种%d件商品</BOLD>
<BR>
--------------------------------<BR>
<C><L><BOLD>商品质量问题请联系:</BOLD></L><BR></C>
<C><L><BOLD>%s:%s</BOLD></L><BR></C><BR>
<BR>
官方服务热线: 18011516898<BR>
更多信息请关注官方微信: 京西菜市<BR>
<BR>
<BR><BR>
--------------------------------
--------------------------------
<BR><BR>
`
// <QR>http://weixin.qq.com/r/tkkDGzTERmk5rXB49xyk</QR>
orderParams = append(orderParams, order.SkuCount, order.GoodsCount, order.StoreName, storeTel)
return fmt.Sprintf(strings.Replace(orderFmt, "\n", "", -1), orderParams...)
}