Files
jx-callback/business/jxutils/jxutils_cms.go
suyl d16d91bf00 aa
2021-07-05 16:45:24 +08:00

590 lines
15 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 (
"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"
"git.rosy.net.cn/jx-callback/globals"
"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 ComposeStoreName(bareName string, vendorID int) (fullName string) {
bareName = TrimDecorationChar(strings.Trim(bareName, "-"))
storeName := globals.StoreName
if vendorID == model.VendorIDJD {
fullName = storeName + "-" + bareName
} else {
if globals.IsMainProductEnv() && model.ShopChineseNames[vendorID] != "" {
storeName = model.ShopChineseNames[vendorID]
}
fullName = storeName + "(" + bareName + ")"
}
return fullName
}
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)
}
// 计算SKU价格unitPrice为一斤的单价specQuality为质量单位为克
func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int {
if skuNameUnit != model.SpecialUnit {
return unitPrice
}
specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
floatPrice := float64(unitPrice) * float64(specQuality2) / float64(model.SpecialSpecQuality)
// if specQuality2 < 250 {
// floatPrice = floatPrice * 110 / 100
// } else if specQuality2 < 500 {
// floatPrice = floatPrice * 105 / 100
// }
if floatPrice <= 1 {
floatPrice = 1
}
return int(math.Round(floatPrice))
}
// 计算SKU标准价格CaculateSkuPrice的逆过程
func CaculateUnitPrice(skuPrice int, specQuality float32, specUnit string, skuNameUnit string) (unitPrice int) {
if skuNameUnit != model.SpecialUnit {
return skuPrice
}
specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
unitPrice = skuPrice * model.SpecialSpecQuality / specQuality2
// if specQuality2 < 250 {
// unitPrice = unitPrice * 100 / 110
// } else if specQuality2 < 500 {
// unitPrice = unitPrice * 100 / 105
// }
if unitPrice <= 1 {
unitPrice = 1
}
return unitPrice
}
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 GetPricePercentage(l model.PricePercentagePack, price int, defPricePercentage int) (pricePercentage, priceAdd int) {
pricePercentage = defPricePercentage
itemLen := len(l)
if itemLen > 0 {
low := 0
high := itemLen - 1
mid := 0
for low <= high {
mid = low + (high-low)/2
if mid < 0 || mid >= itemLen-1 {
break
}
if price >= l[mid].BeginPrice {
if price < l[mid+1].BeginPrice {
break
} else {
low = mid + 1
}
} else {
high = mid - 1
}
}
if mid >= 0 && mid <= itemLen-1 && low <= high {
pricePercentage = l[mid].PricePercentage
priceAdd = l[mid].PriceAdd
}
}
return pricePercentage, priceAdd
}
func GetPricePercentageByVendorPrice(l model.PricePercentagePack, vendorPrice int, defPricePercentage int) (pricePercentage, priceAdd int) {
pricePercentage = defPricePercentage
if len(l) > 0 {
var lastItem *model.PricePercentageItem
for _, v := range l {
if CaculateSkuVendorPrice(v.BeginPrice, v.PricePercentage, v.PriceAdd) > vendorPrice {
break
}
lastItem = v
}
if lastItem != nil {
pricePercentage = lastItem.PricePercentage
priceAdd = lastItem.PriceAdd
}
}
return pricePercentage, priceAdd
}
func CaculatePriceByPricePack(l model.PricePercentagePack, defPricePercentage, price int) (outPrice int) {
pricePercentage, priceAdd := GetPricePercentage(l, price, defPricePercentage)
return CaculateSkuVendorPrice(price, pricePercentage, priceAdd)
}
func CaculateJxPriceByPricePack(l model.PricePercentagePack, defPricePercentage, vendorPrice int) (jxPrice int) {
pricePercentage, priceAdd := GetPricePercentageByVendorPrice(l, vendorPrice, defPricePercentage)
jxPrice = CaculateSkuPriceFromVendor(vendorPrice, pricePercentage, priceAdd)
return jxPrice
}
func ConstrainPayPercentage(payPerCentage int) int {
if payPerCentage <= 50 {
payPerCentage = 70
}
return payPerCentage
}
func IsSkuSpecial(specQuality float32, specUnit string) bool {
return int(specQuality) == model.SpecialSpecQuality && (specUnit == model.SpecialSpecUnit || specUnit == model.SpecialSpecUnit2)
}
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)
}
type SkuList []*model.Sku
func (s SkuList) Len() int {
return len(s)
}
func (s SkuList) Less(i, j int) bool {
if s[i].NameID == s[j].NameID {
if s[i].SpecUnit == s[j].SpecUnit {
return s[i].SpecQuality < s[j].SpecQuality
}
return s[i].SpecUnit < s[j].SpecUnit
}
return s[i].NameID < s[j].NameID
}
func (s SkuList) Swap(i, j int) {
tmp := s[i]
s[i] = s[j]
s[j] = tmp
}
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 { // 京东117330658位
vendorID = model.VendorIDJD
} else if vendorStoreID > 1234567 && vendorStoreID < 9876543 { // 美团外卖 24617137位
vendorID = model.VendorIDMTWM
} else if vendorStoreID > 1234567890 && vendorStoreID < 998765432109 { // 饿百 216700260710位12位
vendorID = model.VendorIDEBAI
} else if false { //vendorStoreID > 123456789 && vendorStoreID < 987654321 { // 微盟微商城 1320910489位
// vendorID = model.VendorIDWSC
} else if vendorStoreID > 123456 && vendorStoreID < 654321 { // 京西门店ID6位
vendorID = model.VendorIDJX
}
return vendorID
}
func GetVendorName(vendorID int) (vendorName string) {
return model.VendorChineseNames[vendorID]
}
func CaculateSkuEarningPrice(shopPrice, salePrice int64, storePayPercentage int) (earningPrice int64) {
//TODO 2021-07-05 菜市和果园一样,取低的
//if beego.BConfig.RunMode == "jxgy" {
earningPrice = salePrice
if salePrice != 0 {
if shopPrice > 0 && shopPrice < earningPrice {
earningPrice = shopPrice
}
}
//} else {
// 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)
}