package jxutils import ( "bytes" "crypto/md5" "encoding/base64" "errors" "fmt" "image" "image/png" "io/ioutil" "math" "net/http" "reflect" "regexp" "strings" "sync" "time" "git.rosy.net.cn/baseapi/platformapi" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/model" "github.com/boombuler/barcode" "github.com/boombuler/barcode/code128" "github.com/boombuler/barcode/qr" ) const ( MaxCodeWidth = 400 MaxCodeHeight = 400 CodeTypeQr = "qr" CodeTypeBar = "bar" ) var ( storeNamePat = regexp.MustCompile(`([^(\[(【)\])】\-\s]*)[(\[(【\-\s]+([^(\[(【)\])】\-]*)[)\])】]*`) mobilePat = regexp.MustCompile(`^1\d{10}$`) ) // 合并得到最终的门店状态 func MergeStoreStatus(status int, vendorStatus int) int { if status < vendorStatus { return status } return vendorStatus } func MergeSkuStatus(skuStatus int, storeSkuStatus int) int { if skuStatus < storeSkuStatus { return skuStatus } return storeSkuStatus } func SplitSlice(list interface{}, batchCount int) (listInList [][]interface{}) { typeInfo := reflect.TypeOf(list) if typeInfo.Kind() != reflect.Slice { panic("list must be slice") } valueInfo := reflect.ValueOf(list) len := valueInfo.Len() if len > 0 { listInListLen := (len-1)/batchCount + 1 listInList = make([][]interface{}, listInListLen) index := 0 for i := 0; i < len; i++ { if i%batchCount == 0 { index = i / batchCount arrLen := len - i if arrLen > batchCount { arrLen = batchCount } listInList[index] = make([]interface{}, arrLen) } listInList[index][i%batchCount] = valueInfo.Index(i).Interface() } } return listInList } func SplitStoreName(fullName, separator, defaultPrefix string) (prefix, bareName string) { names := strings.Split(fullName, separator) if len(names) == 2 { prefix = names[0] bareName = names[1] } else { searchResult := storeNamePat.FindStringSubmatch(fullName) if searchResult != nil { prefix = searchResult[1] bareName = strings.Trim(strings.Trim(searchResult[2], defaultPrefix), separator) } else { prefix = defaultPrefix bareName = strings.Trim(strings.Trim(fullName, defaultPrefix), separator) } } return TrimDecorationChar(prefix), TrimDecorationChar(bareName) } func StrTime2JxOperationTime(strTime string, defValue int16) int16 { if timeValue, err := time.Parse("15:04:05", strTime); err == nil { return int16(timeValue.Hour()*100 + timeValue.Minute()) } return defValue } func JxOperationTime2StrTime(value int16) string { return fmt.Sprintf("%02d:%02d", value/100, value%100) } func JxOperationTime2TimeByDate(value int16, tm time.Time) (outTm time.Time) { return utils.Str2TimeWithDefault(fmt.Sprintf("%s %02d:%02d:00", utils.Time2DateStr(tm), value/100, value%100), utils.DefaultTimeValue) } func GetPolygonFromCircle(lng, lat, distance float64, pointCount int) (points [][2]float64) { points = make([][2]float64, pointCount) for k := range points { angle := float64(k) * 360 / float64(pointCount) points[k][0], points[k][1] = ConvertDistanceToLogLat(lng, lat, float64(distance), angle) } return points } func GetPolygonFromCircleStr(lng, lat, distance float64, pointCount int) string { return CoordinatePoints2Str(GetPolygonFromCircle(lng, lat, distance, pointCount)) } func CoordinatePoints2Str(points [][2]float64) string { points2 := make([]string, len(points)) for k, v := range points { points2[k] = fmt.Sprintf("%.6f,%.6f", v[0], v[1]) } return strings.Join(points2, ";") } func CoordinateStr2Points(pointsStr string) (points [][2]float64) { strPoints := strings.Split(pointsStr, ";") for _, v := range strPoints { strPoint := strings.Split(v, ",") if len(strPoint) >= 2 { points = append(points, [2]float64{utils.Str2Float64(strPoint[0]), utils.Str2Float64(strPoint[1])}) } } return points } func IntMap2List(intMap map[int]int) []int { retVal := make([]int, len(intMap)) index := 0 for k := range intMap { retVal[index] = k index++ } return retVal } func IntList2Map(intList []int) map[int]int { retVal := make(map[int]int) for _, v := range intList { retVal[v] = 1 } return retVal } func Int64Map2List(int64Map map[int64]int) []int64 { retVal := make([]int64, len(int64Map)) index := 0 for k := range int64Map { retVal[index] = k index++ } return retVal } func StringMap2List(stringMap map[string]int) []string { retVal := make([]string, len(stringMap)) index := 0 for k := range stringMap { retVal[index] = k index++ } return retVal } func StringList2Map(strList []string) map[string]int { retVal := make(map[string]int) for _, v := range strList { retVal[v] = 1 } return retVal } func GetSliceLen(list interface{}) int { return reflect.ValueOf(list).Len() } func RegularizeSkuQuality(specQuality float32, specUnit string) (g int) { lowerSpecUnit := strings.ToLower(specUnit) if lowerSpecUnit == "kg" || lowerSpecUnit == "l" { specQuality *= 1000 } return int(specQuality) } func ConstrainPricePercentage(percentage int) int { if percentage < model.MinVendorPricePercentage || percentage > model.MaxVendorPricePercentage { percentage = model.DefVendorPricePercentage } return percentage } func CaculateSkuActVendorPrice(price, percentage, priceAdd int) (vendorPrice int) { percentage = ConstrainPricePercentage(percentage) vendorPrice = int(math.Round(float64(price*percentage)/100)) + priceAdd if vendorPrice < 1 { vendorPrice = 1 } return vendorPrice } func CaculateSkuVendorPrice(price, percentage, priceAdd int) (vendorPrice int) { percentage = ConstrainPricePercentage(percentage) vendorPrice = int(math.Round(float64(price*percentage)/100)) + priceAdd vendorPrice = int(math.Round(float64(vendorPrice)/10) * 10) if vendorPrice < 1 { vendorPrice = 1 } return vendorPrice } func CaculateSkuPriceFromVendor(vendorPrice, percentage, priceAdd int) (price int) { percentage = ConstrainPricePercentage(percentage) price = int(math.Round(float64(vendorPrice-priceAdd) * 100 / float64(percentage))) if price < 1 { price = 1 } return price } func ConstrainPayPercentage(payPerCentage int) int { if payPerCentage <= 50 { payPerCentage = 70 } return payPerCentage } var lastFakeID int64 var lastFakeIDMutex sync.RWMutex // 生成一个不重复的临时ID func genFakeID1() int64 { for { fakeID := time.Now().UnixNano() / 1000 lastFakeIDMutex.RLock() if fakeID == lastFakeID { lastFakeIDMutex.RUnlock() time.Sleep(1 * time.Microsecond) } else { lastFakeIDMutex.RUnlock() lastFakeIDMutex.Lock() defer lastFakeIDMutex.Unlock() lastFakeID = fakeID return fakeID } } } // 这个用于没有打开远程同步时的假同步,生成ID使用 func GenFakeID() int64 { return genFakeID1() * 3 } func IsFakeID(id int64) bool { if IsEmptyID(id) { return true } multiple := id / genFakeID1() return multiple >= 2 && multiple <= 4 } func IsEmptyID(id int64) bool { return id == 0 } func FormalizePageSize(pageSize int) int { if pageSize == 0 { return model.DefPageSize } else if pageSize < 0 { return model.UnlimitedPageSize } return pageSize } func FormalizePageOffset(offset int) int { if offset < 0 { offset = 0 } return offset } func FormalizeName(name string) string { return utils.TrimBlankChar(strings.Replace(strings.Replace(name, "\t", "", -1), "\"", "", -1)) } func Int2OneZero(value int) int { if value != 0 { return 1 } return 0 } func FormalizeMobile(mobile string) string { mobile = TrimDecorationChar(mobile) return strings.Replace(strings.Replace(mobile, "-", ",", -1), "_", ",", -1) } func IsStringLikeMobile(mobile string) bool { return mobilePat.FindStringIndex(mobile) != nil } func IsLegalStoreID(id int) bool { return id >= 100000 && id < 900000 } // 将规格转为重量 func FormatSkuWeight(specQuality float32, specUnit string) int { return RegularizeSkuQuality(specQuality, specUnit) } func DownloadFileByURL(fileURL string) (bodyData []byte, fileMD5 string, err error) { response, err := http.Get(fileURL) if err == nil { defer response.Body.Close() if response.StatusCode == http.StatusOK { if bodyData, err = ioutil.ReadAll(response.Body); err == nil { fileMD5 = fmt.Sprintf("%X", md5.Sum(bodyData)) } } else { err = platformapi.ErrHTTPCodeIsNot200 } } return bodyData, fileMD5, err } ///// func GenPicFileName(suffix string) string { return fmt.Sprintf("image/%x%s", md5.Sum([]byte(utils.GetUUID()+suffix)), suffix) } func GuessVendorIDFromVendorStoreID(vendorStoreID int64) (vendorID int) { vendorID = model.VendorIDUnknown if vendorStoreID > 10040008 && vendorStoreID < 98765432 { // 京东11733065,8位 vendorID = model.VendorIDJD } else if vendorStoreID > 1234567 && vendorStoreID < 9876543 { // 美团外卖 2461713,7位 vendorID = model.VendorIDMTWM } else if vendorStoreID > 1234567890 && vendorStoreID < 998765432109 { // 饿百 2167002607,10位,12位 vendorID = model.VendorIDEBAI } else if false { //vendorStoreID > 123456789 && vendorStoreID < 987654321 { // 微盟微商城 132091048,9位 // vendorID = model.VendorIDWSC } else if vendorStoreID > 123456 && vendorStoreID < 654321 { // 京西门店ID,6位 vendorID = model.VendorIDJX } return vendorID } func GetVendorName(vendorID int) (vendorName string) { return model.VendorChineseNames[vendorID] } func CaculateSkuEarningPrice(shopPrice, salePrice int64, storePayPercentage int) (earningPrice int64) { earningPrice = salePrice if salePrice != 0 { if shopPrice > 0 && shopPrice < earningPrice { earningPrice = shopPrice } } storePayPercentage = ConstrainPayPercentage(storePayPercentage) if storePayPercentage <= 0 { storePayPercentage = model.DefaultEarningPricePercentage } earningPrice = int64(math.Round(float64(earningPrice) * float64(storePayPercentage) / 100)) return earningPrice } func GetImgBase64(img image.Image) (imgBase64 string, err error) { bufWriter := bytes.NewBuffer(nil) png.Encode(bufWriter, img) imgBase64 = "data:image/png;base64," + base64.StdEncoding.EncodeToString(bufWriter.Bytes()) return imgBase64, err } func CreateQrOrBarCode(width, height int, codeType, srcData string) (imgBase64 string, err error) { if width > MaxCodeWidth { width = MaxCodeWidth } if height > MaxCodeHeight { height = MaxCodeHeight } var code barcode.Barcode if codeType == CodeTypeQr { code, err = qr.Encode(srcData, qr.M, qr.Auto) } else if codeType == CodeTypeBar { code, err = code128.Encode(srcData) } else { err = errors.New(fmt.Sprintf("未知编码类型:%s", codeType)) } if err == nil { code, err = barcode.Scale(code, width, height) if err == nil { imgBase64, err = GetImgBase64(code) } } return imgBase64, err } // 高德地图面积计算公式 // https://blog.csdn.net/zdb1314/article/details/80661602 func CalcPolygonAreaAutonavi(points [][2]float64) (area float64) { sJ := float64(6378137) Hq := float64(0.017453292519943295) c := float64(sJ * Hq) d := float64(0) if 3 > len(points) { return 0 } for i := 0; i < len(points)-1; i++ { h := points[i] k := points[i+1] u := h[0] * c * math.Cos(h[1]*Hq) hhh := h[1] * c v := k[0] * c * math.Cos(k[1]*Hq) d = d + (u*k[1]*c - v*hhh) } g1 := points[len(points)-1] point := points[0] eee := g1[0] * c * math.Cos(g1[1]*Hq) g2 := g1[1] * c k := point[0] * c * math.Cos(point[1]*Hq) d += eee*point[1]*c - k*g2 return 0.5 * math.Abs(d) / float64(1000000) }