Files
jx-callback/business/jxutils/jxutils.go
suyl b94de1d201 aa
2021-07-20 16:52:41 +08:00

721 lines
19 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 (
"crypto/aes"
"crypto/md5"
"encoding/base64"
"fmt"
"io/ioutil"
"math"
"math/rand"
"regexp"
"strings"
"time"
"git.rosy.net.cn/baseapi/utils"
"git.rosy.net.cn/baseapi/utils/routinepool"
"git.rosy.net.cn/jx-callback/business/model"
)
var (
routinePool *routinepool.Pool
skuNamePat *regexp.Regexp
emailPat *regexp.Regexp
orderNoBeginTimestamp int64
resourceTypeMap = map[int][]string{
model.VendorIDQiNiuCloud: []string{
"image.jxc4.com",
},
model.VendorIDJD: []string{
"img30.360buyimg.com",
},
model.VendorIDEBAI: []string{
"image-star.elemecdn.com",
},
}
letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
flowUnitMap = map[string]string{
"KB": "KB",
"MB": "MB",
"GB": "GB",
}
)
const fileExt = ".xlsx"
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.*)?$\s*([(].*[)])?$`)
emailPat = regexp.MustCompile(`[A-Za-z0-9_\-.]+@(?:[A-Za-z0-9_\-]+\.)+[A-Za-z]+`)
orderNoBeginTimestamp = utils.Str2Time("2010-01-01 00:00:00").Unix()
}
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
func GenRand6() (num int) {
return utils.Str2Int(fmt.Sprintf("%06v", rand.New(rand.NewSource(time.Now().UnixNano())).Int31n(1000000)))
}
func GenOrderNo() (orderNo int64) {
var prefix = utils.Str2Int64(time.Now().Format("20060102"))
const randPartNum = 1000
orderNo = time.Now().Unix() - orderNoBeginTimestamp
orderNo = orderNo * randPartNum
md5Bytes := md5.Sum([]byte(utils.GetUUID()))
randPart := 0
for k, v := range md5Bytes {
randPart += int(v) << ((k % 3) * 8)
}
orderNo += int64(randPart % randPartNum)
orderNo += int64(math.Pow10(int(math.Log10(float64(orderNo)))+1)) * prefix
return orderNo
}
// 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))
if dist < 0 {
dist = 0
}
return dist * radius
}
func StandardCoordinate2Int(value float64) int {
return int(math.Round(value * 1000000))
}
func IntCoordinate2Standard(value int) float64 {
return float64(value) / 1000000
}
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 IntPrice2StandardCurrencyString(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 ComposeSkuNameOriginal(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int) (skuName string) {
strBuilder := &strings.Builder{}
if prefix != "" {
strBuilder.WriteString("[")
strBuilder.WriteString(prefix)
strBuilder.WriteString("]")
}
skuName += name
strBuilder.WriteString(name)
if unit == "份" {
strBuilder.WriteString("约")
}
if unit != "" {
strBuilder.WriteString(ComposeSkuSpec(spec_quality, spec_unit))
strBuilder.WriteString("/")
strBuilder.WriteString(unit)
}
if comment != "" {
strBuilder.WriteString("(")
strBuilder.WriteString(comment)
strBuilder.WriteString(")")
}
skuName = strBuilder.String()
if maxLen > 0 {
skuName = utils.LimitUTF8StringLen(skuName, maxLen)
}
return skuName
}
func ComposeSkuNameForJds(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int) (skuName string) {
strBuilder := &strings.Builder{}
if prefix != "" {
strBuilder.WriteString("[")
strBuilder.WriteString(prefix)
strBuilder.WriteString("]")
}
skuName += name
strBuilder.WriteString(name)
if comment != "" {
strBuilder.WriteString(" ")
strBuilder.WriteString(comment)
}
if unit == "份" {
strBuilder.WriteString("约")
}
if unit != "" {
strBuilder.WriteString(ComposeSkuSpec(spec_quality, spec_unit))
}
skuName = strBuilder.String()
if maxLen > 0 {
skuName = utils.LimitUTF8StringLen(skuName, maxLen)
}
return skuName
}
func ComposeSkuName(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time) (skuName string) {
if exPrefix != "" && exPrefixBegin != nil && exPrefixEnd != nil {
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
skuName = exPrefix
}
}
skuName += ComposeSkuNameOriginal(prefix, name, comment, unit, spec_quality, spec_unit, maxLen)
return skuName
}
func ComposeSkuNameSync(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time) (skuName string) {
if exPrefix != "" && exPrefixBegin != nil && exPrefixEnd != nil {
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
skuName = exPrefix
}
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixEnd) > 0 {
skuName = ""
}
}
skuName += ComposeSkuNameOriginal(prefix, name, comment, unit, spec_quality, spec_unit, maxLen)
return skuName
}
func ComposeSkuNameSync2(prefix, name, comment, unit string, spec_quality float32, spec_unit string, maxLen int, exPrefix string, exPrefixBegin, exPrefixEnd *time.Time) (skuName string) {
if exPrefix != "" && exPrefixBegin != nil && exPrefixEnd != nil {
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixBegin) >= 0 && utils.Time2Date(time.Now()).Sub(*exPrefixEnd) <= 0 {
skuName = exPrefix
}
if utils.Time2Date(time.Now().Add(6*time.Hour)).Sub(*exPrefixEnd) > 0 {
skuName = ""
}
}
skuName += ComposeSkuNameForJds(prefix, name, comment, unit, spec_quality, spec_unit, 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]
} else if searchResult[9] != "" {
comment = searchResult[9]
}
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
}
// https://my.oschina.net/hyller/blog/700414
func CalUpcCheckSum(upc12 int64) (checkSum int) {
var sum [2]int
for i := 0; i < 12; i++ {
base := int64(math.Pow10(i))
sum[i%2] += int((upc12 / base) % 10)
}
sum[0] *= 3
return (10 - (sum[0]+sum[1])%10) % 10
}
func IsUpcValid(upc string) bool {
if len(upc) != 13 {
return false
}
upcInt := utils.Str2Int64WithDefault(upc, 0)
checkSum := CalUpcCheckSum(upcInt / 10)
return int(utils.Str2Int64(upc[12:])) == checkSum
}
func GenFakeUPC(skuID int) string {
id := int64(skuID) + 666600000000
return fmt.Sprintf("%012d%d", id, CalUpcCheckSum(id))
}
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 "https://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 TaskResult2Hint(resultList []interface{}) (hint string) {
strList := make([]string, len(resultList))
for k, v := range resultList {
strList[k] = fmt.Sprint(v)
}
hint = strings.Join(strList, ",")
return hint
}
// 这个函数用于将两个整数合并为一单一int64不要用于持久化的场景
func Combine2Int(int1, int2 int) (outInt int64) {
return int64(int1)*100000 + int64(int2)
}
func GetLastTimeFromList(now time.Time, timeList []string) (snapshotAt time.Time) {
dateStr := utils.Time2DateStr(now)
selectTime := utils.Str2Time(utils.Time2DateStr(now.Add(-24*time.Hour)) + " " + timeList[len(timeList)-1])
for _, v := range timeList {
tmpTime := utils.Str2Time(dateStr + " " + v)
if tmpTime.Sub(now) > 0 {
break
}
selectTime = tmpTime
}
return selectTime
}
func GetNextTimeFromList(now time.Time, timeList []string) (snapshotAt time.Time) {
dateStr := utils.Time2DateStr(now)
timeListLen := len(timeList)
selectTime := utils.Str2Time(utils.Time2DateStr(now.Add(24*time.Hour)) + " " + timeList[0])
for k := range timeList {
v := timeList[timeListLen-k-1]
tmpTime := utils.Str2Time(dateStr + " " + v)
if tmpTime.Sub(now) < 0 {
break
}
selectTime = tmpTime
}
return selectTime
}
func OperationTime2StrWithSecond(opTime int16) (str string) {
str = fmt.Sprintf("%02d:%02d:00", opTime/100, opTime%100)
return str
}
func OperationTime2Str(opTime int16) (str string) {
str = fmt.Sprintf("%02d:%02d", opTime/100, opTime%100)
return str
}
func OperationTime2Str2(openTime, closeTime int16) (str string) {
str = fmt.Sprintf("%s-%s", OperationTime2Str(openTime), OperationTime2Str(closeTime))
return str
}
func OperationTime2HourMinuteFormat(time time.Time) (i int16) {
return int16(time.Hour()*100 + time.Minute())
}
func WriteFile(fileName string, binData []byte) error {
err := ioutil.WriteFile(fileName, binData, 0666)
return err
}
func GuessDataResourceVendor(resourceURL string) (vendorID int) {
vendorID = -1
for tmpVendorID, urlList := range resourceTypeMap {
for _, v := range urlList {
if v != "" && strings.Index(resourceURL, "//"+v) >= 0 {
vendorID = tmpVendorID
break
}
}
if vendorID >= 0 {
break
}
}
return vendorID
}
func BatchString2Slice(strs ...string) (strList []string) {
for _, v := range strs {
if v != "" {
strList = append(strList, v)
}
}
return strList
}
func GetShortNameFromURL(strURL string) (shortName string) {
index := strings.Index(strURL, "?")
if index != -1 {
strURL = strURL[:index]
}
index = strings.LastIndex(strURL, "/")
if index != -1 {
shortName = strURL[index+1:]
}
return shortName
}
// 阶梯计算总量
// stageList是一个二维数组第一维要求是从大到小排序的第二维是级别及单位代价
func CalcStageValue(stageList [][]float64, totalVolume float64) (value float64) {
for _, v := range stageList {
if totalVolume > v[0] {
used := math.Ceil(totalVolume - v[0])
value += v[1] * used
totalVolume -= used
}
}
return value
}
func GetOneEmailFromStr(str string) (email string) {
if str != "" {
searchResult := emailPat.FindStringSubmatch(str)
if searchResult != nil && len(searchResult) > 0 {
email = searchResult[0]
}
}
return email
}
// 计算一个坐标点距离一个门店的距离单位为米如果不在有效范围内则返回0
func Point2StoreDistance(lng, lat float64, intStoreLng, intStoreLat int, deliveryRangeType int8, deliveryRange string) (distance int) {
// storeLng := IntCoordinate2Standard(intStoreLng)
// storeLat := IntCoordinate2Standard(intStoreLat)
// if deliveryRangeType == model.DeliveryRangeTypeRadius {
// maxDistance := int(utils.Str2Int64WithDefault(deliveryRange, 0))
// distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
// if distance > maxDistance {
// distance = 0
// }
// } else {
// points := CoordinateStr2Points(deliveryRange)
// if utils.IsPointInPolygon(lng, lat, points) {
// distance = int(EarthDistance(lng, lat, storeLng, storeLat) * 1000)
// }
// }
return distance
}
func TranslateSoundSize(vendorID, soundPercentage int) (soundSize string) {
if vendorID == model.VendorIDYiLianYun || vendorID == model.VendorIDFeiE {
if soundPercentage == 0 {
soundSize = "0"
}
if soundPercentage > 0 && soundPercentage <= 33 {
soundSize = "1"
}
if soundPercentage > 33 && soundPercentage <= 66 {
soundSize = "2"
}
if soundPercentage > 66 && soundPercentage <= 100 {
soundSize = "3"
}
}
return soundSize
}
//ECB,AES模式解密
//目前就京东商城订单手机号解密用
func DecryptDESECB(d, key []byte) string {
data, err := base64.StdEncoding.DecodeString(string(d))
if err != nil {
return ""
}
block, err := aes.NewCipher(key)
if err != nil {
return ""
}
bs := block.BlockSize()
if len(data)%bs != 0 {
return ""
}
out := make([]byte, len(data))
dst := out
for len(data) > 0 {
block.Decrypt(dst, data[:bs])
data = data[bs:]
dst = dst[bs:]
}
out = PKCS5UnPadding(out)
return string(out)
}
func PKCS5UnPadding(origData []byte) []byte {
length := len(origData)
unpadding := int(origData[length-1])
return origData[:(length - unpadding)]
}
func GetIssue() (issue int) {
year, month, _ := time.Now().Date()
return year*100 + int(month)
}
//根据一堆坐标求面积
//有待考证,不过暂时拿来用
func ComputeSignedArea(path []string) (s float64) {
var (
radius = 6371009
len = len(path)
total float64
prev = path[len-1]
)
if len < 3 {
return
}
prevTanLat := math.Tan(((math.Pi/2 - utils.Str2Float64(strings.Split(prev, ",")[1])/180*math.Pi) / 2))
prevLng := utils.Str2Float64(strings.Split(prev, ",")[0]) / 180 * math.Pi
for i := 0; i < len; i++ {
tanLat := math.Tan(((math.Pi/2 - utils.Str2Float64(strings.Split(path[i], ",")[1])/180*math.Pi) / 2))
lng := utils.Str2Float64(strings.Split(path[i], ",")[0]) / 180 * math.Pi
total += polarTriangleArea(tanLat, lng, prevTanLat, prevLng)
prevTanLat = tanLat
prevLng = lng
}
return math.Abs(total * (float64(radius) * float64(radius)))
}
func polarTriangleArea(tan1, lng1, tan2, lng2 float64) (s float64) {
deltaLng := lng1 - lng2
t := tan1 * tan2
return 2 * math.Atan2(t*math.Sin(deltaLng), 1+t*math.Cos(deltaLng))
}
func GenRandomString(l int) string {
str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < l; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return string(result)
}
func GetDayTime() (dayTimeBegin, dayTimeEnd time.Time) {
dayTimeBegin = utils.Str2Time(utils.Time2Str(utils.Time2Date(time.Now())) + " 00:00:00")
dayTimeEnd = dayTimeBegin.AddDate(0, 0, 1)
return dayTimeBegin, dayTimeEnd
}
func GetWeekTime() (weekTimeBegin, weekTimeEnd time.Time) {
offset := int(time.Now().Weekday() - 1)
if offset == -1 {
offset = -6
}
weekTimeBegin = time.Now().AddDate(0, 0, offset)
weekTimeEnd = weekTimeBegin.AddDate(0, 0, 7)
return weekTimeBegin, weekTimeEnd
}
func BuildErr(errs []error) (err error) {
var errStr = strings.Builder{}
for _, v := range errs {
errStr.WriteString(v.Error())
}
return fmt.Errorf(errStr.String())
}
func SplitFlowAndUnit(flowStr string) (flow float64, unit string) {
for _, v := range flowUnitMap {
if strings.Contains(flowStr, v) {
return utils.Str2Float64WithDefault(flowStr[:len(flowStr)-2], 0), flowStr[len(flowStr)-2:]
}
}
return flow, unit
}
func Flow2KB(flow float64, unit string) (flowKB float64) {
if unit == "KB" {
return flow
} else if unit == "MB" {
return flow * 1024
} else if unit == "GB" {
return flow * 1024 * 1024
}
return flowKB
}
func FlowKB2Other(flowKB float64) (flow float64, unit string) {
if flowKB < 1024 {
return flowKB, "KB"
} else {
flowMB := math.Round(flowKB / float64(1024))
if flowMB < 1024 {
return flowMB, "MB"
} else {
flowGB := math.Round(flowMB / float64(1024))
if flowGB < 1024 {
return flowGB, "GB"
}
}
}
return flow, unit
}