Commit 592280b6 by mushishixian

Merge branch 'ysx-bom服务'

parents 6aa298b8 e8b084d6
......@@ -4,6 +4,7 @@ import (
"bom_server/cmd/queue"
"bom_server/configs"
"bom_server/internal/model"
"bom_server/internal/pkg/gredis"
"flag"
)
......@@ -13,5 +14,6 @@ func main() {
flag.Parse()
configs.Setup(path)
model.Setup()
gredis.Setup()
queue.Sync()
}
......@@ -14,16 +14,16 @@ type RecvPro struct {
}
func init() {
//queueExchange := rabbitmq.QueueExchange{
// "bom_match",
// "bom_match",
// "bom",
// "direct",
// "amqp://huntadmin:jy2y2900@192.168.1.237:5672/",
//}
//
//str := `{"bom_id":369,"delivery_type":1,"sort":2}`
//rabbitmq.Send(queueExchange, str)
queueExchange := rabbitmq.QueueExchange{
"bom_match",
"bom_match",
"bom",
"direct",
"amqp://huntadmin:jy2y2900@192.168.1.237:5672/",
}
str := `{"bom_id":408,"delivery_type":1,"sort":2}`
rabbitmq.Send(queueExchange, str)
}
func (t *RecvPro) Consumer(dataByte []byte) (err error) {
......
......@@ -24,8 +24,10 @@ type Database struct {
}
type Redis struct {
Host string `ini:"host"`
Password string `ini:"password"`
WriteHost string `ini:"write_host"`
ReadHost string `ini:"read_host"`
WritePassword string `ini:"write_password"`
ReadPassword string `ini:"read_password"`
MaxIdle int `ini:"max_idle"`
MaxActive int `ini:"max_active"`
IdleTimeout time.Duration `ini:"idle_timeout"`
......@@ -34,6 +36,7 @@ type Redis struct {
type ES struct {
Url string `ini:"url"`
GoodsIndexName string `ini:"goods_index_name"`
AttrIndex string `ini:"attr_index"`
}
type RabbitMQ struct {
......
......@@ -7,7 +7,7 @@ require (
github.com/gin-gonic/gin v1.6.3
github.com/go-ini/ini v1.56.0
github.com/go-kratos/kratos v0.5.0
github.com/go-sql-driver/mysql v1.5.0
github.com/gomodule/redigo v2.0.0+incompatible
github.com/ichunt2019/go-rabbitmq v1.0.1
github.com/imroc/req v0.3.0
github.com/jinzhu/gorm v1.9.12
......
......@@ -129,6 +129,9 @@ github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/gomodule/redigo/redis v0.0.0-do-not-use h1:J7XIp6Kau0WoyT4JtXHT3Ei0gA1KkSc6bc87j9v9WIo=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
......
......@@ -130,3 +130,25 @@ func Assert(input interface{}, needType string) (result interface{}) {
}
return
}
func ToString(a interface{}) string {
if v, p := a.(int); p {
return strconv.Itoa(v)
}
if v, p := a.(int16); p {
return strconv.Itoa(int(v))
}
if v, p := a.(int32); p {
return strconv.Itoa(int(v))
}
if v, p := a.(uint); p {
return strconv.Itoa(int(v))
}
if v, p := a.(float32); p {
return strconv.FormatFloat(float64(v), 'f', -1, 32)
}
if v, p := a.(float64); p {
return strconv.FormatFloat(v, 'f', -1, 32)
}
return "change to String error"
}
package logic
import (
"bom_server/configs"
"bom_server/internal/common"
"bom_server/internal/mapping"
"bom_server/internal/model"
"bom_server/internal/pkg/gredis"
"context"
"fmt"
"github.com/gomodule/redigo/redis"
"github.com/tidwall/gjson"
es "gopkg.in/olivere/elastic.v5"
"regexp"
"strconv"
"strings"
)
//根据参数去匹配商品
func MatchGoodsNameByAttrs(bomItems []model.BomItem) (result []model.BomItem, err error) {
client, err := es.NewClient(es.SetURL(configs.ESSetting.Url))
if err != nil {
return
}
defer client.Stop()
index := configs.ESSetting.AttrIndex
search := client.MultiSearch().Index(index)
searchFlag := false
//先根据参数去构建批量查询条件
for _, item := range bomItems {
//如果有型号,但是型号有可能是参数,所以先去匹配下参数,有的话转成对应的型号
if item.GoodsName != "" {
search = searchAttr(item.GoodsName, item.BrandName, search)
searchFlag = true
}
//如果没有型号,但是有参数(参数有可能是型号),那就去匹配参数
if item.GoodsName == "" && item.Attrs != "" {
search = searchAttr(item.Attrs, item.BrandName, search)
searchFlag = true
}
}
if !searchFlag {
return bomItems, nil
}
res, err := search.Do(context.Background())
if err != nil {
return
}
if len(res.Responses) == 0 {
return
}
//因为是多重查询,所以会有多套结果
for key, responses := range res.Responses {
//有数据进行转换
if responses.Hits != nil {
for _, hit := range responses.Hits.Hits {
res, _ := hit.Source.MarshalJSON()
//还要去判断至少符合特定数量参数匹配的结果,比如最小匹配参数为2,那就是要有两个参数匹配到才正确
bomItems[key].GoodsName = gjson.Get(string(res), "goods_name").String()
//fmt.Println(string(res))
}
}
}
result = bomItems
return
}
func searchAttr(attrOrigin, brandName string, search *es.MultiSearchService) (result *es.MultiSearchService) {
//先去切割参数得到参数列表
attrs := splitAttrs(attrOrigin)
var attrsSlice []string
//fmt.Println(attrs)
//去转换每一个参数,得到去查询es的标准格式
for _, attr := range attrs {
attr = TransformESParam(attr)
attrsSlice = append(attrsSlice, attr)
}
query := getQuery(attrsSlice)
if brandName != "" {
//提取全英文,转成大写
if strings.Contains(brandName, " ") {
//有空格隔开只取第一个
brandName = strings.Split(brandName, " ")[0]
//转成全大写,因为ES存的是大写
brandName = strings.ToUpper(brandName)
//去除特殊符号和中文等等
r1, _ := regexp.Compile(`/[^A-Za-z0-9]+/`)
brandName = r1.ReplaceAllString(brandName, "")
query.Should(es.NewTermQuery("brand_name", brandName))
}
}
source := es.NewSearchSource().Query(query)
source.Sort("brand_sort", true)
source = source.From(0).Size(1)
searchRequest := es.NewSearchRequest().Source(source)
//fmt.Println(searchRequest.Body())
return search.Add(searchRequest)
}
//切割参数
func splitAttrs(attrs string) (result []string) {
result = strings.Split(attrs, " ")
if len(result) > 1 {
return
}
result = strings.Split(attrs, ",")
if len(result) > 1 {
return
}
result = strings.Split(attrs, "|")
if len(result) > 1 {
return
}
result = strings.Split(attrs, ",")
if len(result) > 1 {
return
}
result = strings.Split(attrs, "/")
if len(result) > 1 {
return
}
return
}
//将写法转成正规写法,转换单位,比如有的人喜欢将Ω输成欧姆或者O,所以要统一转成正常的Ω
func changeKeyword(attr string) (result string) {
regulars := mapping.KeywordRegular
for regexpStr, regular := range regulars {
compile, _ := regexp.Compile(regexpStr)
attr = compile.ReplaceAllString(attr, regular)
}
return attr
}
//将属性值转成ES的标准值(例如 : 阻值(欧姆)€3000)
func TransformESParam(attr string) (result string) {
attr = changeKeyword(attr)
unitMapping := mapping.UnitValueMapping
//获取该属性对应的单位
for unit, _ := range unitMapping {
index := strings.Index(attr, unit)
if index > 0 {
attr = string([]rune(attr)[:index+len(unit)-1])
}
//得到截取后的参数,去得到需要用于es查询的参数
attrValue := getAttrValueByAttr(attr)
return attrValue
}
return
}
//根据参数单位或者参数值获取对应的重要属性,组成最后查询的字符串
//±
func getAttrValueByAttr(attr string) (attrValue string) {
//先找出单位
r, _ := regexp.Compile(mapping.GetAttrUnitRegular)
attrUnit := strings.Trim(r.ReplaceAllString(attr, ""), " ")
//如果单位是W,则要进行除法计算
if strings.ToUpper(attrUnit) == "W" {
//提取值,进行计算
attr = strings.TrimRight(attr, "W")
attr = strings.TrimRight(attr, "w")
attrSlice := strings.Split(attr, "/")
if len(attrSlice) == 2 {
var res float64
a, _ := strconv.ParseFloat(attrSlice[0], 64)
b, _ := strconv.ParseFloat(attrSlice[1], 64)
res = a / b
attr = fmt.Sprintf("%.3f", res) + "W"
}
}
//再找出纯数字
var attrNumber float64
numberR, _ := regexp.Compile(mapping.PureNumberRegular)
pureNumber := numberR.FindString(attr)
attrNumber, _ = strconv.ParseFloat(pureNumber, 64)
//找出对应单位需要转换的值
var attrName string
if attrUnit != "" {
for unit, value := range mapping.UnitValueMapping {
//如果不是标准最小单位,则要进行转换
if attrUnit == unit {
value64, _ := strconv.ParseFloat(value, 64)
//还要找到对应的属性,先找到基础单位
var baseUnit string
if value, exist := mapping.UnitBaseMapping[attrUnit]; exist {
baseUnit = value
}
if baseUnit == "" {
return attr
}
//获取重要属性名称
if value, exist := mapping.UnitAttrMapping[baseUnit]; exist {
attrName = value
}
attrNumberStr := common.ToString(attrNumber * value64)
attrValue = attrName + "€" + attrNumberStr
break
}
}
} else {
//再去找没有单位的对应属性
attrName, _ = redis.String(gredis.HGet("sku_map2", attr))
if attrName != "" {
attrValue = attrName + "€" + attr
} else {
attrValue = attr
}
}
return attrValue
}
//获取匹配的条件
func getQuery(attrs []string) (query *es.BoolQuery) {
var subQuery *es.TermQuery
var nestedQuery *es.NestedQuery
query = es.NewBoolQuery()
for _, attr := range attrs {
//还要判断是不是一个单位对应多个属性的
if strings.Contains(attr, "|") {
name := strings.Split(attr, "€")[0]
value := strings.Split(attr, "€")[1]
multiAttr := strings.Split(name, "|")
for _, temp := range multiAttr {
subQuery = es.NewTermQuery("attrs.attr_value", temp+"€"+value)
nestedQuery = es.NewNestedQuery("attrs", subQuery)
query.Should(nestedQuery)
}
} else {
subQuery = es.NewTermQuery("attrs.attr_value", attr)
nestedQuery = es.NewNestedQuery("attrs", subQuery)
query.Should(nestedQuery)
}
query.MinimumNumberShouldMatch(1)
}
return query
}
......@@ -49,20 +49,25 @@ func BatchSaveMatchings(bomId int, matchingList []model.BomItemMatching) (err er
}
}
now := time.Now().Unix()
var itemStatus int
//if matching.IsBuy == 1 && matching.Price != 0 && matching.Stock != 0 {
// itemStatus = 2
// //库存为0或者价格为0,要放到待确认
//
//} else if matching.Price == 0 || matching.Stock == 0 {
// itemStatus = 4
//}
if matching.IsBuy == 1 {
err = model.Db.Table("lie_bom_item_"+tableEnd).Where("bom_item_id = ?", matching.BomItemID).
Updates(map[string]interface{}{"item_status": 2, "update_time": now}).Error
if err != nil {
return
itemStatus = 2
}else{
itemStatus =3
}
} else {
err = model.Db.Table("lie_bom_item_"+tableEnd).Where("bom_item_id = ?", matching.BomItemID).
Updates(map[string]interface{}{"item_status": 3, "update_time": now}).Error
Updates(map[string]interface{}{"item_status": itemStatus, "update_time": now}).Error
if err != nil {
return
}
}
}
return nil
}
......
......@@ -33,7 +33,6 @@ func UpdateGoodsData(goodsMapList []GoodsMap) (err error) {
if len(goodsMapList) != 0 {
bomId = goodsMapList[0].BomId
}
//fmt.Println(goodsIdsStr)
if err != nil {
return
}
......@@ -82,6 +81,7 @@ func GetGoodsInfo(goodsIdsStr string) (goodsList []model.ApiGoods, err error) {
params := req.Param{
"goods_id": goodsIdsStr,
}
//fmt.Println(goodsIdsStr)
resp, err := req.Post(goodsServerUrl+"/synchronization", params)
if err != nil {
return
......
......@@ -32,7 +32,7 @@ func MatchGoods(message model.BomMessage) (err error) {
}
bomItems := bom.BomItems
perGoDealNumber := 40
//开启协程处理搜索.每50个开启一个协程
//开启协程处理搜索
var wg sync.WaitGroup
//判断是否有余数
extraNumber := len(bomItems) % perGoDealNumber
......@@ -76,6 +76,9 @@ func SearchGoods(bomId int, bomItems []model.BomItem, deliveryType, sort int, wg
panic(err)
}
defer client.Stop()
//匹配之前,去遍历bom_item,把没有型号名称但是有参数的bom_item进行型号补充
bomItems, err = MatchGoodsNameByAttrs(bomItems)
//第一次先去精确匹配
goodsMapList, err := getUpdateGoodsData(bomId, bomItems, deliveryType, sort, client, true)
if err != nil {
......@@ -105,6 +108,7 @@ func SearchGoods(bomId int, bomItems []model.BomItem, deliveryType, sort int, wg
if err != nil {
return
}
//然后去处理匹配到的数据
err = UpdateGoodsData(append(goodsMapList, fuzzyGoodsMapList...))
if err != nil {
return
......@@ -178,7 +182,10 @@ func getUpdateGoodsData(bomId int, bomItems []model.BomItem, deliveryType, sort
index = index + ",zhuanmai"
//查完以后去除已经匹配的自营商品,然后去搜索联营的商品
bomItems = removeZiyingMatchBomItem(ziyingGoodsMapList, bomItems)
lianyingGoodsMapList, _ := search(index, bomId, bomItems, deliveryType, sort, client, rawSearch)
var lianyingGoodsMapList []GoodsMap
if len(bomItems) != 0 {
lianyingGoodsMapList, _ = search(index, bomId, bomItems, deliveryType, sort, client, rawSearch)
}
goodsMapList = append(ziyingGoodsMapList, lianyingGoodsMapList...)
} else {
index = index + ",zhuanmai,liexin_ziying"
......@@ -219,11 +226,19 @@ func search(index string, bomId int, bomItems []model.BomItem, deliveryType, sor
continue
}
}
//经过了最前面的参数匹配
//如果型号还是为空,则用参数去匹配,因为有可能参数里面填了型号
//if bom.GoodsName == "" && bom.Attrs == "" {
// continue
//} else if bom.GoodsName == "" && bom.Attrs != "" {
// bom.GoodsName = bom.Attrs
//}
//构建一个goods_name对应的bomItems列表
searchRequest := getSearchParams(index, bom.GoodsName, bom.BrandName, sort, bom.Number, rawSearch)
searchFlag = true
search.Add(searchRequest)
}
//没有搜索条件的话,直接返回空值即可
if !searchFlag {
return nil, err
......@@ -255,7 +270,6 @@ func search(index string, bomId int, bomItems []model.BomItem, deliveryType, sor
goodsMap.BomItemId = bomItems[key].BomItemID
goodsMap.BomId = bomId
goodsMap.DeliveryType = deliveryType
goodsMapList = append(goodsMapList, goodsMap)
break
}
......
package mapping
var KeywordRegular = map[string]string{
//`^(.* )?([\d\.]+)(欧|欧姆|R|r)( .*)?$`: `$1$2Ω$4`,
//`^(.* )?([\d\.]+)(U|u|μ)(.?)( .*)?$`: `$1$2Μ$4$5`,
"COG": "C0G",
"NPO": "NP0",
"华科|华新科技|华新科": "华新",
`(欧姆|欧|O|o|R|r)`: "Ω",
`(Uf|uf|μf|uF|UF)`: "μF",
`(Uh|uh|μh|uH|UH)`: "μH",
`K`: "nF",
`v`: "V",
}
var PureNumberRegular = `(\d+(\.\d+)?)`
var PureLetterRegular = `[a-zA-Z0-9]+`
var GetAttrUnitRegular = `[\d.]|±|\+_|\+-|/|\(.*\)|\+/-|`
//`^[\d\.]+(欧姆|欧|O|o|R|r)$`: "Ω",
//`^[\d\.]+(Uf|uf|μf|uF|UF)$`: "μF",
//`^[\d\.]+(Uh|uh|μh|uH|UH)$`: "μH",
//`^[\d\.]+K$`: "KΩ",
......@@ -2,22 +2,78 @@ package mapping
//属性单位对应属性
var UnitAttrMapping = map[string]string{
"r ": "阻值(欧姆)",
"Ω ": "阻值(欧姆)",
"r": "阻值(欧姆)|直流电阻(内阻)",
"Ω": "阻值(欧姆)|直流电阻(内阻)",
"OHM": "阻值(欧姆)|直流电阻(内阻)",
"mh": "电感",
"F": "容值",
"w": "功率",
"W": "功率",
"Ω/r ": "内阻",
"% ": "精度",
"%": "精度",
"V": "额定电压",
"A": "额定电流",
}
//封装列表
var PackingList = []string{
"0402",
"0603",
//属性对应的基础属性
var UnitBaseMapping = map[string]string{
"μΩ": "Ω",
"mΩ": "Ω",
"Ω": "Ω",
"kΩ": "Ω",
"KΩ": "Ω",
"MΩ": "Ω",
"pF": "F",
"nF": "F",
"μF": "F",
"μf": "F",
"mF": "F",
"F": "F",
"μH": "H",
"mH": "H",
"H": "H",
"V": "V",
"kV": "V",
"mA": "A",
"A": "A",
"W": "W",
"kW": "W",
"KW": "W",
"%": "%",
}
//介质列表
var MediaList = []string{
"X5U",
"X7R",
//根据单位对值的转换,比如1kΩ=>1000Ω
var UnitValueMapping = map[string]string{
"μΩ": "0.000001",
"mΩ": "0.001",
"Ω": "1",
"kΩ": "1000",
"KΩ": "1000",
"MΩ": "1000000",
"pF": "1",
"nF": "1000",
"μF": "1000000",
"mF": "1000000000",
"F": "1000000000000",
"μH": "1",
"mH": "1000",
"H": "1000000",
"V": "1",
"kV": "1000",
"mA": "1",
"A": "1000",
"W": "1",
"kW": "1000",
"%": "0.01",
}
package mapping
//封装列表
var PackingList = []string{
"0402",
"0603",
}
//介质列表
var MediaList = []string{
"X5U",
"X7R",
}
package gredis
import (
"bom_server/configs"
"encoding/json"
"github.com/gomodule/redigo/redis"
"time"
)
var writeConn, readConn *redis.Pool
func Setup() (err error) {
writeHost := configs.RedisSetting.WriteHost
readHost := configs.RedisSetting.ReadHost
writePassword := configs.RedisSetting.WritePassword
readPassword := configs.RedisSetting.ReadPassword
writeConn, err = getConn(writeHost, writePassword)
if err != nil {
return
}
readConn, err = getConn(readHost, readPassword)
if err != nil {
return
}
return nil
}
func getConn(writeHost, password string) (pool *redis.Pool, err error) {
maxIdle := configs.RedisSetting.MaxIdle
maxActive := configs.RedisSetting.MaxActive
pool = &redis.Pool{
MaxIdle: maxIdle,
MaxActive: maxActive,
Dial: func() (redis.Conn, error) {
c, err := redis.Dial("tcp", writeHost)
if err != nil {
return nil, err
}
if password != "" {
if _, err := c.Do("AUTH", password); err != nil {
c.Close()
return nil, err
}
}
return c, err
},
TestOnBorrow: func(c redis.Conn, t time.Time) error {
_, err := c.Do("PING")
return err
},
}
return
}
//最基础的键值操作
func Set(key string, data interface{}) error {
conn := writeConn.Get()
defer conn.Close()
value, err := json.Marshal(data)
if err != nil {
return err
}
_, err = conn.Do("SET", key, value)
if err != nil {
return err
}
return nil
}
func Exists(key string) bool {
conn := readConn.Get()
defer conn.Close()
exists, err := redis.Bool(conn.Do("EXISTS", key))
if err != nil {
return false
}
return exists
}
func Get(key string) (interface{}, error) {
conn := readConn.Get()
defer conn.Close()
reply, err := conn.Do("GET", key)
if err != nil {
return nil, err
}
return reply, nil
}
func Delete(key string) (bool, error) {
conn := writeConn.Get()
defer conn.Close()
return redis.Bool(conn.Do("DEL", key))
}
//哈希操作
func HSet(key string, k interface{}, data interface{}) error {
conn := writeConn.Get()
defer conn.Close()
value, err := json.Marshal(data)
if err != nil {
return err
}
_, err = conn.Do("HSET", key, k, value)
if err != nil {
return err
}
return nil
}
func HGet(key string, k interface{}) (interface{}, error) {
conn := readConn.Get()
defer conn.Close()
reply, err := conn.Do("HGET", key, k)
if err != nil {
return nil, err
}
return reply, nil
}
func HDelete(key string, k interface{}) (bool, error) {
conn := writeConn.Get()
defer conn.Close()
return redis.Bool(conn.Do("HDEL", key, k))
}
package main
import (
"fmt"
"github.com/shopspring/decimal"
)
type abc struct {
Goods_id float64 `json:"goods_id"`
Goods_name string `json:"goods_name"`
}
import "bom_server/internal/logic"
func main() {
//a := `{"goods_id":1.1572417766286e+18,"goods_name":"LM358"}`
//
//aaa := &abc{}
//err := json.Unmarshal([]byte(a),aaa)
//
//if err !=nil{
// fmt.Println(err)
//}
//
//fmt.Println(aaa)
//
//d := json.NewDecoder(bytes.NewReader([]byte(a)))
//d.UseNumber()
//err = d.Decode(&aaa)
//if err != nil {
// // 错误处理
//}
//fmt.Println(aaa)
//fmt.Printf("%f\n", aaa.Goods_id)
numStr := 1.1572417766286e+18
decimalNum := decimal.NewFromFloat(numStr)
//if err != nil {
// return
//}
fmt.Println(decimalNum.String())
logic.ChangeUnit("100kr(1002) ")
//logic.ChangeUnit("10KΩ(1002) ")
//logic.ChangeUnit("±1%")
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment