Create & Init Project...

This commit is contained in:
2019-04-22 18:49:16 +08:00
commit fc4fa37393
25440 changed files with 4054998 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
# v1.0.0
1. 上线功能番剧推荐合并入大仓库master分支

View File

@@ -0,0 +1,9 @@
# Owner
changxuanran
liuzhan
# Author
changxuanran
# Reviewer
liuzhan

View File

@@ -0,0 +1,14 @@
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- changxuanran
- liuzhan
labels:
- job
- job/openplatform/open-sug
- openplatform
options:
no_parent_owners: true
reviewers:
- changxuanran
- liuzhan

View File

@@ -0,0 +1,10 @@
#### open-sug-job
##### 项目简介
> 1.开放平台根据番剧推荐商品job
##### 编译环境
> 请只用golang v1.9.x以上版本编译执行。
##### 依赖包
> 1.公共包go-common

View File

@@ -0,0 +1,16 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("@io_bazel_rules_go//proto:def.bzl", "go_proto_library")
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1 @@
syntax = "proto3";

View File

@@ -0,0 +1 @@
# HTTP API文档

View File

@@ -0,0 +1,43 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
)
go_binary(
name = "cmd",
embed = [":go_default_library"],
tags = ["automanaged"],
)
go_library(
name = "go_default_library",
srcs = ["main.go"],
data = ["test.toml"],
importpath = "go-common/app/job/openplatform/open-sug/cmd",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/openplatform/open-sug/conf:go_default_library",
"//app/job/openplatform/open-sug/server/http:go_default_library",
"//app/job/openplatform/open-sug/service:go_default_library",
"//library/log:go_default_library",
"//library/net/trace:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,45 @@
package main
import (
"flag"
"os"
"os/signal"
"syscall"
"time"
"go-common/app/job/openplatform/open-sug/conf"
"go-common/app/job/openplatform/open-sug/server/http"
"go-common/app/job/openplatform/open-sug/service"
"go-common/library/log"
"go-common/library/net/trace"
)
func main() {
flag.Parse()
if err := conf.Init(); err != nil {
panic(err)
}
log.Init(conf.Conf.Log)
defer log.Close()
log.Info("open-sug-job start")
trace.Init(conf.Conf.Tracer)
defer trace.Close()
svc := service.New(conf.Conf)
http.Init(conf.Conf, svc)
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
for {
s := <-c
log.Info("get a signal %s", s.String())
switch s {
case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
svc.Close()
log.Info("open-sug-job exit")
time.Sleep(time.Second)
return
case syscall.SIGHUP:
default:
return
}
}
}

View File

@@ -0,0 +1,177 @@
env = "uat"
sourcePath = "/Users/caocao/Code/go/src/go-common/app/job/openplatform/open-sug/source/"
[redis]
name = "open-sug-job"
proto = "tcp"
addr = "172.16.33.203:6379"
idle = 10
active = 10
dialTimeout = "1s"
readTimeout = "1s"
writeTimeout = "1s"
idleTimeout = "10s"
expire = "1m"
[mallMySQL]
addr = "172.22.34.101:3310"
dsn = "mall:FIb1lia9lK4UufUScJjrxvbkrEZtZD4l@tcp(172.22.34.101:3310)/open_mall?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
queryTimeout = "1000ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[db.mallDB.breaker]
window = "3s"
sleep = "1000ms"
bucket = 10
ratio = 0.5
request = 500
[mallUgcMySQL]
addr = "172.22.34.101:3310"
dsn = "mallugc:FIb1lia9lK4UufUScJjrxvbkrEZtZD4l@tcp(172.22.34.101:3310)/open_mallugc?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
queryTimeout = "1000ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[db.ugcDB.breaker]
window = "3s"
sleep = "1000ms"
bucket = 10
ratio = 0.5
request = 500
[ticketMySQL]
addr = "172.22.34.101:3310"
dsn = "ticket:i9HXWAvzWiqPxMxlfsQ8DRqYydjf3pYa@tcp(172.22.34.101:3310)/open_ticket?timeout=5s&readTimeout=5s&writeTimeout=5s&parseTime=true&loc=Local&charset=utf8,utf8mb4"
active = 5
idle = 2
queryTimeout = "1000ms"
execTimeout = "100ms"
tranTimeout = "200ms"
[db.ticketDB.breaker]
window = "3s"
sleep = "1000ms"
bucket = 10
ratio = 0.5
request = 500
[pgcSub]
key = "39143bf7b888273e"
secret = "0766c43df93a9204d81c46caf2231ee1"
group = "Pgc-OpenTicket-S"
topic = "Pgc-T"
action = "sub"
offset = "old"
buffer = 1024
name = "open-sug-job/ticket"
proto = "tcp"
addr = "172.16.33.158:6205"
idle = 1
active = 1
dialTimeout = "1s"
readTimeout = "60s"
writeTimeout = "1s"
idleTimeout = "10s"
[berserker]
appkey = "7a374e9a8fd3938ac682cc6cb07855e8"
secret = "ad994843be00732bd6e65f9f95887bac"
url = "http://berserker.bilibili.co/avenger/api/53/query"
[elasticSearch]
addr = ["http://172.16.38.126:9201"]
check = "10s"
timeout = "1s"
[elasticSearch.season]
index = "sug_job_season"
type = "sug_job_season"
mapping = """
{
"settings": {
"number_of_shards": 1,
"number_of_replicas": 0,
"analysis": {
"analyzer": {
"fulltext_analyzer": {
"type": "custom",
"tokenizer": "whitespace",
"filter": [
"lowercase",
"type_as_payload"
]
}
}
}
},
"mappings": {
"sug_job_season": {
"_source": {
"enabled": true
},
"properties": {
"id": {
"type": "long",
"store": true
},
"title": {
"type": "text",
"term_vector": "with_positions_offsets_payloads",
"store": true,
"analyzer": "fulltext_analyzer"
},
"alias": {
"type": "text",
"term_vector": "with_positions_offsets_payloads",
"store": true,
"analyzer": "fulltext_analyzer"
},
"alias_search": {
"type": "text",
"term_vector": "with_positions_offsets_payloads",
"store": true,
"analyzer": "fulltext_analyzer"
},
"actors": {
"type": "text",
"term_vector": "with_positions_offsets_payloads",
"store": true,
"analyzer": "fulltext_analyzer"
}
}
}
}
}
"""
[httpClient]
key = "654af11b5df0c9d3"
secret = "a7512b8b243b82f4bdb72cf2824b3f8e"
dial = "500ms"
timeout = "1s"
keepAlive = "60s"
timer = 10
[httpClient.breaker]
window = "3s"
sleep = "100ms"
bucket = 10
ratio = 0.5
request = 100
[bfs]
timeout = "2s"
maxFileSize = 5242880
key = "c387398b08495933"
secret = "18295608cd3b8a5lbbe2b07a509705"
addr = "http://bfs.bilibili.co/bfs/openplatform/"
bucket = "openplatform"

View File

@@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["conf.go"],
importpath = "go-common/app/job/openplatform/open-sug/conf",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//library/cache/redis:go_default_library",
"//library/conf:go_default_library",
"//library/database/sql:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
"//library/net/trace:go_default_library",
"//library/queue/databus:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/BurntSushi/toml:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,119 @@
package conf
import (
"errors"
"flag"
"go-common/library/cache/redis"
"go-common/library/conf"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
"go-common/library/net/trace"
"go-common/library/queue/databus"
xtime "go-common/library/time"
"github.com/BurntSushi/toml"
)
var (
confPath string
client *conf.Client
// Conf config
Conf = &Config{}
)
// Config .
type Config struct {
Log *log.Config
BM *bm.ServerConfig
Verify *verify.Config
Tracer *trace.Config
Redis *redis.Config
MallMySQL *sql.Config
TicketMySQL *sql.Config
MallUgcMySQL *sql.Config
PgcSub *databus.Config
ElasticSearch *ElasticSearch
Env string
SourcePath string
Bfs *Bfs
}
func init() {
flag.StringVar(&confPath, "conf", "", "default config path")
}
// Init init conf
func Init() error {
if confPath != "" {
return local()
}
return remote()
}
func local() (err error) {
_, err = toml.DecodeFile(confPath, &Conf)
return
}
func remote() (err error) {
if client, err = conf.New(); err != nil {
return
}
if err = load(); err != nil {
return
}
go func() {
for range client.Event() {
log.Info("config reload")
if load() != nil {
log.Error("config reload error (%v)", err)
}
}
}()
return
}
func load() (err error) {
var (
s string
ok bool
tmpConf *Config
)
if s, ok = client.Toml2(); !ok {
return errors.New("load config center error")
}
if _, err = toml.Decode(s, &tmpConf); err != nil {
return errors.New("could not decode config")
}
*Conf = *tmpConf
return
}
// ElasticSearch config
type ElasticSearch struct {
Addr []string
Check xtime.Duration
Timeout string
Season EsIndex
}
// EsIndex config
type EsIndex struct {
Index string
Type string
Alias string
Mapping string
}
// Bfs config
type Bfs struct {
Timeout xtime.Duration
MaxFileSize int
Bucket string
Addr string
Key string
Secret string
}

View File

@@ -0,0 +1,50 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"bfs.go",
"dao.go",
"image.go",
"item.go",
"item_mysql.go",
"redis.go",
"season_es.go",
],
importpath = "go-common/app/job/openplatform/open-sug/dao",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/admin/openplatform/sug/model:go_default_library",
"//app/job/openplatform/open-sug/conf:go_default_library",
"//app/job/openplatform/open-sug/model:go_default_library",
"//library/cache/redis:go_default_library",
"//library/database/sql:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//vendor/code.google.com/p/graphics-go/graphics:go_default_library",
"//vendor/github.com/golang/freetype:go_default_library",
"//vendor/golang.org/x/image/font:go_default_library",
"//vendor/golang.org/x/image/math/fixed:go_default_library",
"//vendor/gopkg.in/olivere/elastic.v5:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,72 @@
package dao
import (
"context"
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"fmt"
"hash"
"io"
"net/http"
"strconv"
"time"
"go-common/library/ecode"
"go-common/library/log"
)
const _method = "PUT"
// Upload upload bfs.
func (d *Dao) Upload(c context.Context, fileType string, fileName string, body io.Reader) (location string, err error) {
req, err := http.NewRequest(_method, d.c.Bfs.Addr+fileName, body)
if err != nil {
log.Error("http.NewRequest error (%v) | fileType(%s) body(%v)", err, fileType, body)
return
}
expire := time.Now().Unix()
authorization := authorize(d.c.Bfs.Key, d.c.Bfs.Secret, _method, d.c.Bfs.Bucket, fileName, expire)
req.Header.Set("Host", d.c.Bfs.Addr)
req.Header.Add("Date", fmt.Sprint(expire))
req.Header.Add("Authorization", authorization)
req.Header.Add("Content-Type", fileType)
// timeout
c, cancel := context.WithTimeout(c, time.Duration(d.c.Bfs.Timeout))
req = req.WithContext(c)
defer cancel()
resp, err := d.client.Do(req)
if err != nil {
log.Error("d.Client.Do error(%v) | _url(%s) req(%v)", err, d.c.Bfs.Addr, req)
err = ecode.BfsUploadServiceUnavailable
return
}
if resp.StatusCode != http.StatusOK {
log.Error("Upload http.StatusCode nq http.StatusOK (%d) | url(%s)", resp.StatusCode, d.c.Bfs.Addr)
err = ecode.BfsUploadStatusErr
return
}
header := resp.Header
code := header.Get("Code")
if code != strconv.Itoa(http.StatusOK) {
log.Error("strconv.Itoa err, code(%s) | url(%s)", code, d.c.Bfs.Addr)
err = ecode.BfsUploadCodeErr
return
}
location = header.Get("Location")
return
}
func authorize(key, secret, method, bucket string, fileName string, expire int64) (authorization string) {
var (
content string
mac hash.Hash
signature string
)
content = fmt.Sprintf("%s\n%s\n%s\n%d\n", method, bucket, fileName, expire)
mac = hmac.New(sha1.New, []byte(secret))
mac.Write([]byte(content))
signature = base64.StdEncoding.EncodeToString(mac.Sum(nil))
authorization = fmt.Sprintf("%s:%s:%d", key, signature, expire)
return
}

View File

@@ -0,0 +1,93 @@
package dao
import (
"context"
"net/http"
"time"
"go-common/app/job/openplatform/open-sug/conf"
"go-common/library/cache/redis"
xsql "go-common/library/database/sql"
"go-common/library/log"
"gopkg.in/olivere/elastic.v5"
)
// Dao dao
type Dao struct {
c *conf.Config
redis *redis.Pool
mallDB *xsql.DB
ugcDB *xsql.DB
ticketDB *xsql.DB
client *http.Client
ItemSalesMax map[string]int
ItemSalesMin map[string]int
ItemWishMax map[string]int
ItemWishMin map[string]int
ItemCommentMax map[string]int
ItemCommentMin map[string]int
es *elastic.Client
}
// New init mysql db
func New(c *conf.Config) (dao *Dao) {
var (
err error
es *elastic.Client
)
dao = &Dao{
c: c,
redis: redis.NewPool(c.Redis),
mallDB: xsql.NewMySQL(c.MallMySQL),
ugcDB: xsql.NewMySQL(c.MallUgcMySQL),
ticketDB: xsql.NewMySQL(c.TicketMySQL),
client: &http.Client{Timeout: time.Second * 5},
ItemSalesMax: make(map[string]int),
ItemSalesMin: make(map[string]int),
ItemWishMax: make(map[string]int),
ItemWishMin: make(map[string]int),
ItemCommentMax: make(map[string]int),
ItemCommentMin: make(map[string]int),
}
es, err = elastic.NewClient(
elastic.SetURL(c.ElasticSearch.Addr...),
elastic.SetSniff(false),
elastic.SetHealthcheckInterval(time.Duration(c.ElasticSearch.Check)),
elastic.SetErrorLog(&elog{}),
elastic.SetInfoLog(&ilog{}),
)
if err != nil {
panic(err)
}
dao.es = es
return
}
// Close close the resource.
func (d *Dao) Close() {
d.redis.Close()
d.mallDB.Close()
d.ugcDB.Close()
d.ticketDB.Close()
}
// Ping dao ping
func (d *Dao) Ping(c context.Context) error {
// TODO: if you need use mc,redis, please add
return d.mallDB.Ping(c)
}
type ilog struct{}
type elog struct{}
// Printf printf.
func (l *ilog) Printf(format string, v ...interface{}) {
log.Info(format, v...)
}
// Printf printf.
func (l *elog) Printf(format string, v ...interface{}) {
log.Error(format, v...)
}

View File

@@ -0,0 +1,162 @@
package dao
import (
"bufio"
"bytes"
"context"
"fmt"
"image"
"image/color"
"image/draw"
"image/jpeg"
"image/png"
"io/ioutil"
"net/http"
"os"
"regexp"
"go-common/app/admin/openplatform/sug/model"
"go-common/library/log"
"code.google.com/p/graphics-go/graphics"
"errors"
"github.com/golang/freetype"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
const (
_Width = 1125
_Height = 234
_HeaderLength = 168
)
var (
_RGBA = []int{244, 245, 247, 255}
_HeadArea = []int{36, 33, 204, 201}
_LabelArea = []int{234, 156, 348, 201}
_BriefText = []int{234, 72}
_NameText = []int{234, 129}
_BriefSize = 44
_NameSize = 36
_BriefRGBA = []int{33, 33, 33, 255}
_NameRGBA = []int{153, 153, 153, 255}
_BriefLimit = 1000
_NameLimit = 1000
)
// CreateItemPNG make a pic
func (d *Dao) CreateItemPNG(item model.Item) (location string, err error) {
var r *image.NRGBA64
if r, err = d.makeBoard(item.Img); err != nil {
log.Error("Create picture board error(%v)", err)
return
}
if item.Brief == "" {
item.Brief = item.Name
}
r = d.drawText(r, item.Brief, item.Name)
buf := new(bytes.Buffer)
png.Encode(buf, r)
bufReader := bufio.NewReader(buf)
if location, err = d.Upload(context.TODO(), "image/png", fmt.Sprintf("season_sug_%s/%d.png", d.c.Env, item.ItemsID), bufReader); err != nil {
log.Error("Upload pic png error (%v)", err)
return
}
reg, _ := regexp.CompilePOSIX(`//(.*)+`)
location = reg.FindString(location)
return
}
func (d *Dao) makeBoard(headerURL string) (board *image.NRGBA64, err error) {
var header image.Image
radius, _ := os.Open(d.c.SourcePath + "radius.png")
label, _ := os.Open(d.c.SourcePath + "label.png")
defer radius.Close()
defer label.Close()
resp, err := http.Get("http:" + headerURL)
if err != nil {
log.Error("http get error(%v)(%s)", "http:"+headerURL)
return
}
bs, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Error("ioutil.ReadAll img error(%v)", err)
return
}
imgType := http.DetectContentType(bs)
buf := bytes.NewBuffer(bs)
resp.Body = ioutil.NopCloser(buf)
if err != nil {
log.Error("http download head img error(%v)", err)
return
}
defer resp.Body.Close()
switch imgType {
case "image/png":
header, err = png.Decode(resp.Body)
if err != nil {
log.Error("png picture decode error(%v)", err)
return
}
case "image/jpeg", "image/jpg":
header, err = jpeg.Decode(resp.Body)
if err != nil {
log.Error("jpg picture decode err(%v)", err)
return
}
default:
err = errors.New("invaild picture type")
log.Error("invaild picture type (%s)", headerURL)
return
}
board = image.NewNRGBA64(image.Rect(0, 0, _Width, _Height))
draw.Draw(board, board.Bounds(), image.White, image.ZP, draw.Src)
radiusPNG, _ := png.Decode(radius)
cover := image.NewRGBA64(image.Rect(0, 0, _HeaderLength, _HeaderLength))
border := image.NewNRGBA64(image.Rect(0, 0, _HeaderLength, _HeaderLength))
headBoard := image.NewRGBA64(image.Rect(0, 0, _HeaderLength, _HeaderLength))
draw.Draw(headBoard, headBoard.Bounds(), image.NewUniform(color.NRGBA{uint8(_RGBA[0]), uint8(_RGBA[1]), uint8(_RGBA[2]), uint8(_RGBA[3])}), image.ZP, draw.Over)
graphics.Thumbnail(border, radiusPNG)
graphics.Thumbnail(cover, header)
labelPNG, _ := png.Decode(label)
draw.Draw(headBoard, headBoard.Bounds(), cover, image.ZP, draw.Over)
draw.Draw(headBoard, headBoard.Bounds(), border, image.ZP, draw.Over)
draw.Draw(board, image.Rect(_HeadArea[0], _HeadArea[1], _HeadArea[2], _HeadArea[3]), headBoard, image.ZP, draw.Over)
draw.Draw(board, image.Rect(_LabelArea[0], _LabelArea[1], _LabelArea[2], _LabelArea[3]), labelPNG, image.ZP, draw.Over)
return
}
func (d *Dao) drawText(r *image.NRGBA64, brief string, name string) *image.NRGBA64 {
ptBrief := fixed.P(_BriefText[0], _BriefText[1])
ptName := fixed.P(_NameText[0], _NameText[1])
freetypeC := d.Context(r)
d.Text(freetypeC, brief, &ptBrief, _BriefSize, image.NewUniform(color.NRGBA{uint8(_BriefRGBA[0]), uint8(_BriefRGBA[1]), uint8(_BriefRGBA[2]), uint8(_BriefRGBA[3])}), fixed.I(_BriefLimit))
d.Text(freetypeC, name, &ptName, _NameSize, image.NewUniform(color.NRGBA{uint8(_NameRGBA[0]), uint8(_NameRGBA[1]), uint8(_NameRGBA[2]), uint8(_RGBA[3])}), fixed.I(_NameLimit))
return r
}
// Context make object for write letters
func (d *Dao) Context(r *image.NRGBA64) (c *freetype.Context) {
c = freetype.NewContext()
c.SetClip(r.Bounds())
c.SetDst(r)
c.SetHinting(font.HintingNone)
return
}
// Text write letters
func (d *Dao) Text(c *freetype.Context, s string, pt *fixed.Point26_6, size int, color image.Image, length fixed.Int26_6) (err error) {
c.SetFontSize(float64(size))
c.SetSrc(color)
ttf, _ := ioutil.ReadFile(d.c.SourcePath + "font.ttf")
font, _ := freetype.ParseFont(ttf)
c.SetFont(font)
for _, r := range s {
c.DrawString(string(r), *pt)
pt.X += font.HMetric(fixed.Int26_6(size*64), font.Index(r)).AdvanceWidth
if pt.X > length {
break
}
}
return
}

View File

@@ -0,0 +1,162 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"go-common/app/job/openplatform/open-sug/model"
"go-common/library/database/sql"
"go-common/library/log"
)
const (
_fetchItemList = "SELECT `items_id`,`name`,`ip_right_id`,`brief`,`img` FROM `items` WHERE `status` = 1 AND `sub_status` <> 13 AND `ip_right_id` <> 0 AND `is_lastest_version` = 1 limit %d,%d"
_fetchItem = "SELECT `items_id`,`name`,`ip_right_id`,`brief`,`img` FROM `items` WHERE `status` = 1 AND `sub_status` <> 13 AND `is_lastest_version` = 1 and `items_id` = ?"
_fetchIPRight = "SELECT `name`,`chs_name`,`alias`,`parent_id` FROM `ip_right` WHERE `ip_right_id` = ?"
_fetchParentIPRight = "SELECT `name`,`chs_name`,`alias` FROM `ip_right` WHERE `ip_right_id` = ?"
_getWishCount = "SELECT COUNT(*) FROM ugc_subject_vote_%d WHERE `subject_id` = ? AND `subject_type` = 1 AND DATE(`ctime`) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)"
_getCommentCount = "SELECT COUNT(*) FROM ugc_info_%d WHERE `subject_id` = ? AND `subject_type` = 1 AND DATE(`ctime`) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY)"
_getSaleCount = "select sum(sku_num) from (select order_id,payment_id,payment_time from order_basic where status in (3, 4, 5, 7) and parent_order_id = 0) as t1 join (select order_id,items_id,sku_id, sku_num from order_sku) as t2 on t1.order_id = t2.order_id where items_id = ? AND DATE(`ctime`) >= DATE_SUB(CURDATE(), INTERVAL 7 DAY) group by items_id "
_fetchMatch = "SELECT `season_id`,`items_id`,`sort` FROM `sug_filter` where type = 1 and sort > 1000000000 limit %d,%d"
_insertMatch = "INSERT INTO sug_filter (`items_id`,`season_id`,`season_name`,`sort`,`type`,`items_name`,`head_pic`,`sug_pic`) VALUES(?, ?, ?,?,'1',?,?,?)ON DUPLICATE KEY UPDATE items_name = IF(type=0, items_name,?) ,season_name = IF(type=0, season_name,?) ,sort = IF(sort>1000000000, sort,?),mtime = IF(type=0, mtime,NOW()),head_pic = IF(type=0, head_pic,?),sug_pic = IF(type=0, sug_pic,?)"
_updatePic = "update sug_filter set sug_pic = ?,items_name = ?,head_pic =? where items_id = ? and season_id = ?"
)
//FetchItem query project list from mysql
func (d *Dao) FetchItem(c context.Context) (itemList []*model.Item, err error) {
var (
rows *sql.Rows
start = 0
offset = 10
)
for {
sqlStr := fmt.Sprintf(_fetchItemList, start, offset)
start += offset
if rows, err = d.mallDB.Query(c, sqlStr); err != nil {
log.Error("d._fetchProjectListSQL.Query error(%v)", err)
return
}
i := 0
for rows.Next() {
i++
item := new(model.Item)
var imgList string
if err = rows.Scan(&item.ID, &item.Name, &item.IPRightID, &item.Brief, &imgList); err != nil {
log.Error("row.Scan() error(%v)", err)
continue
}
if item.Brief == "" {
item.Brief = item.Name
item.Name = ""
}
var imgListArr []string
if err = json.Unmarshal([]byte(imgList), &imgListArr); err != nil {
log.Error("get first img err[%s] (%v)", imgList, err)
err = nil
continue
}
item.HeadImg = imgListArr[0]
row := d.mallDB.QueryRow(c, _fetchIPRight, item.IPRightID)
var name, chs_name, alias, parentID, parentName, parentChsName, parentAlias string
if err = row.Scan(&name, &chs_name, &alias, &parentID); err != nil {
err = nil
continue
}
if parentID != "0" {
row := d.mallDB.QueryRow(c, _fetchParentIPRight, parentID)
if err = row.Scan(&parentName, &parentChsName, &parentAlias); err != nil {
log.Error("get parent ip_right_info err (%v)", err)
err = nil
continue
}
}
item.Keywords = name + " " + chs_name + " " + alias + " " + parentName + " " + parentChsName + " " + parentAlias
itemList = append(itemList, item)
}
if i < 10 {
break
}
}
defer rows.Close()
err = rows.Err()
return
}
// GetBind select sug from db
func (d *Dao) GetBind(c context.Context) (sugList []*model.Sug, err error) {
var (
rows *sql.Rows
start = 0
offset = 50
)
for {
sqlStr := fmt.Sprintf(_fetchMatch, start, offset)
start += offset
if rows, err = d.ticketDB.Query(c, sqlStr); err != nil {
log.Error("d._fetchSugListSQL.Query error(%v)", err)
return
}
i := 0
for rows.Next() {
i++
sug := &model.Sug{}
sug.Item = &model.Item{}
if err = rows.Scan(&sug.SeasonID, &sug.Item.ID, &sug.Score); err != nil {
log.Error("row.Scan() error(%v)", err)
continue
}
sugList = append(sugList, sug)
}
if i < 10 {
break
}
}
defer rows.Close()
err = rows.Err()
return
}
// GetItem get item info
func (d *Dao) GetItem(c context.Context, sug *model.Sug) (err error) {
var row *sql.Row
if row = d.mallDB.QueryRow(c, _fetchItem, sug.Item.ID); err != nil {
log.Error("d._fetchProjectListSQL.Query error(%v)", err)
return
}
var imgList string
if err = row.Scan(&sug.Item.ID, &sug.Item.Name, &sug.Item.IPRightID, &sug.Item.Brief, &imgList); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
if sug.Item.Brief == "" {
sug.Item.Brief = sug.Item.Name
sug.Item.Name = ""
}
var imgListArr []string
if err = json.Unmarshal([]byte(imgList), &imgListArr); err != nil {
log.Error("get first img err[%s] (%v)", imgList, err)
err = nil
return
}
sug.Item.HeadImg = imgListArr[0]
row = d.mallDB.QueryRow(c, _fetchIPRight, sug.Item.IPRightID)
var name, chs_name, alias, parentID, parentName, parentChsName, parentAlias string
if err = row.Scan(&name, &chs_name, &alias, &parentID); err != nil {
err = nil
return
}
if parentID != "0" {
row := d.mallDB.QueryRow(c, _fetchParentIPRight, parentID)
if err = row.Scan(&parentName, &parentChsName, &parentAlias); err != nil {
log.Error("get parent ip_right_info err (%v)", err)
err = nil
return
}
}
sug.Item.Keywords = name + " " + chs_name + " " + alias + " " + parentName + " " + parentChsName + " " + parentAlias
return
}

View File

@@ -0,0 +1,58 @@
package dao
import (
"context"
"fmt"
"go-common/app/job/openplatform/open-sug/model"
"go-common/library/log"
)
// WishCount ...
func (d *Dao) WishCount(c context.Context, item *model.Item) (wishCount int, err error) {
row := d.ugcDB.QueryRow(c, fmt.Sprintf(_getWishCount, item.ID%16), item.ID)
if err = row.Scan(&item.WishCount); err != nil {
item.WishCount = 0
}
return item.WishCount, err
}
// CommentCount ...
func (d *Dao) CommentCount(c context.Context, item *model.Item) (commentCount int, err error) {
row := d.ugcDB.QueryRow(c, fmt.Sprintf(_getCommentCount, item.ID%128), item.ID)
if err = row.Scan(&item.CommentCount); err != nil {
item.CommentCount = 0
}
return item.CommentCount, err
}
// SalesCount ...
func (d *Dao) SalesCount(c context.Context, item *model.Item) (salesCount int, err error) {
row := d.ugcDB.QueryRow(c, _getSaleCount, item.ID)
if err = row.Scan(&item.CommentCount); err != nil {
item.CommentCount = 0
}
return item.CommentCount, err
}
// InsertMatch ...
func (d *Dao) InsertMatch(c context.Context, item *model.Item, season model.Score) (affect int64, err error) {
score := int(season.Score * 1000)
res, err := d.ticketDB.Exec(c, _insertMatch, item.ID, season.SeasonID, season.SeasonName, score, item.Name, item.HeadImg, item.SugImg, item.Name, season.SeasonName, score, item.HeadImg, item.SugImg)
if err != nil {
log.Error("(%v)", err)
return
}
affect, err = res.RowsAffected()
return
}
// UpdatePic ...
func (d *Dao) UpdatePic(c context.Context, sug *model.Sug) (affect int64, err error) {
res, err := d.ticketDB.Exec(c, _updatePic, sug.Item.SugImg, sug.Item.Name, sug.Item.HeadImg, sug.Item.ID, sug.SeasonID)
if err != nil {
log.Error("(%v)", err)
return
}
affect, err = res.RowsAffected()
return
}

View File

@@ -0,0 +1,109 @@
package dao
import (
"context"
"fmt"
amodel "go-common/app/admin/openplatform/sug/model"
"go-common/app/job/openplatform/open-sug/model"
"go-common/library/cache/redis"
"strconv"
)
const (
_sugSeason = "SUG:SEASON:%s"
_sugItem = "SUGITEM:%d"
_expire = 93600
)
// SetItem set item info to redis
func (d *Dao) SetItem(c context.Context, item *model.Item) (b bool, err error) {
var location string
picItem := amodel.Item{ItemsID: item.ID, Name: item.Name, Brief: item.Brief, Img: item.HeadImg}
if location, _ = d.CreateItemPNG(picItem); location == "" {
return
}
item.SugImg = location
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("HSET", fmt.Sprintf(_sugItem, item.ID), "items_id", item.ID); err != nil {
return
}
if err = conn.Send("HSET", fmt.Sprintf(_sugItem, item.ID), "name", item.Name); err != nil {
return
}
if err = conn.Send("HSET", fmt.Sprintf(_sugItem, item.ID), "brief", item.Brief); err != nil {
return
}
if err = conn.Send("HSET", fmt.Sprintf(_sugItem, item.ID), "head", item.HeadImg); err != nil {
return
}
if err = conn.Send("HSET", fmt.Sprintf(_sugItem, item.ID), "pic", item.SugImg); err != nil {
return
}
if err = conn.Send("EXPIRE", fmt.Sprintf(_sugItem, item.ID), _expire); err != nil {
return
}
if err = conn.Flush(); err != nil {
return
}
if b, err = redis.Bool(conn.Receive()); err != nil {
return
}
conn.Receive()
return
}
// SetSug ...
func (d *Dao) SetSug(c context.Context, seasonID string, ItemID int64, score float64) (b bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZADD", fmt.Sprintf(_sugSeason, seasonID), score, ItemID); err != nil {
return
}
if err = conn.Send("EXPIRE", fmt.Sprintf(_sugSeason, seasonID), _expire); err != nil {
return
}
if err = conn.Flush(); err != nil {
return
}
if b, err = redis.Bool(conn.Receive()); err != nil {
return
}
conn.Receive()
return
}
// DelSug ...
func (d *Dao) DelSug(c context.Context, seasonID string) (b bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("DEL", fmt.Sprintf(_sugSeason, seasonID)); err != nil {
return
}
if err = conn.Flush(); err != nil {
return
}
if b, err = redis.Bool(conn.Receive()); err != nil {
return
}
conn.Receive()
return
}
// DelSugItem ...
func (d *Dao) DelSugItem(c context.Context, seasonID, itemsID int64) (b bool, err error) {
conn := d.redis.Get(c)
defer conn.Close()
if err = conn.Send("ZREM", fmt.Sprintf(_sugSeason, strconv.FormatInt(seasonID, 10)), itemsID); err != nil {
return
}
if err = conn.Flush(); err != nil {
return
}
if b, err = redis.Bool(conn.Receive()); err != nil {
return
}
conn.Receive()
return
}

View File

@@ -0,0 +1,100 @@
package dao
import (
"context"
"encoding/json"
"fmt"
"go-common/app/job/openplatform/open-sug/model"
"go-common/library/log"
"gopkg.in/olivere/elastic.v5"
)
// SeasonData
func (d *Dao) SeasonData(c context.Context, item *model.Item) (scoreSlice []model.Score, err error) {
var searchResult *elastic.SearchResult
query := elastic.NewMultiMatchQuery(item.Keywords, "title", "alias", "alias_search", "actors^1.25")
searchResult, err = d.es.Search().
Index(fmt.Sprintf("%s_%s", d.c.Env, d.c.ElasticSearch.Season.Index)).
Type(d.c.ElasticSearch.Season.Type).
Query(query).
Timeout(d.c.ElasticSearch.Timeout).
Do(c)
if err != nil {
return
}
if searchResult.TotalHits() > 0 {
wishCount, _ := d.WishCount(c, item)
commentCount, _ := d.CommentCount(c, item)
salesCount, _ := d.SalesCount(c, item)
for _, s := range searchResult.Hits.Hits {
seasonJson, _ := s.Source.MarshalJSON()
season := model.EsSeason{}
json.Unmarshal(seasonJson, &season)
scoreSlice = append(scoreSlice, model.Score{SeasonID: s.Id, Score: *s.Score, SeasonName: season.Title})
switch {
case wishCount > d.ItemWishMax[s.Id]:
d.ItemWishMax[s.Id] = wishCount
case wishCount != 0 && wishCount < d.ItemWishMin[s.Id]:
d.ItemWishMin[s.Id] = wishCount
case d.ItemWishMin[s.Id] == 0:
d.ItemWishMin[s.Id] = wishCount
}
switch {
case commentCount > d.ItemCommentMax[s.Id]:
d.ItemCommentMax[s.Id] = commentCount
case commentCount != 0 && commentCount < d.ItemCommentMin[s.Id]:
d.ItemCommentMin[s.Id] = commentCount
case d.ItemCommentMin[s.Id] == 0:
d.ItemCommentMin[s.Id] = commentCount
}
switch {
case salesCount > d.ItemSalesMax[s.Id]:
d.ItemSalesMax[s.Id] = salesCount
case salesCount != 0 && salesCount < d.ItemSalesMin[s.Id]:
d.ItemSalesMin[s.Id] = salesCount
case d.ItemSalesMin[s.Id] == 0:
d.ItemSalesMin[s.Id] = salesCount
}
}
}
return
}
// Index ...
func (d *Dao) Index(ctx context.Context, index, typ string, id string, data interface{}) {
resp, err := d.es.Index().Index(index).Type(typ).BodyJson(data).Id(id).Do(ctx)
if err != nil {
log.Error("索引写入失败 index(%s) type(%s) error(%v)", index, typ, err)
return
}
if resp.Result != "" {
log.Info("index(%s) type(%s) 创建成功", resp.Index, resp.Type)
} else {
log.Info("index(%s) type(%s) 更新成功", resp.Index, resp.Type)
}
}
// IndexExists ...
func (d *Dao) IndexExists(ctx context.Context, index string) bool {
e, err := d.es.IndexExists(index).Do(ctx)
if err != nil {
log.Error("检查索引是否存在出错 IndexExists(%s) error(%v)", index, err)
}
return e
}
// CreateIndex ...
func (d *Dao) CreateIndex(ctx context.Context, name string, mapping string) bool {
resp, err := d.es.CreateIndex(name).BodyString(mapping).Do(ctx)
if err != nil {
log.Error("创建索引出错 CreateIndex(%s) error(%v)", name, err)
return false
}
if !resp.Acknowledged {
log.Error("创建索引失败 index(%s)", name)
}
return resp.Acknowledged
}

View File

@@ -0,0 +1,33 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"databus.go",
"item.go",
"model.go",
"sug.go",
],
importpath = "go-common/app/job/openplatform/open-sug/model",
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,82 @@
package model
import (
"encoding/json"
"strings"
)
// Message ...
type Message struct {
Action string `json:"action"`
Table string `json:"table"`
New json.RawMessage `json:"new"`
Old json.RawMessage `json:"old"`
}
// Season ...
type Season struct {
ID int `json:"id"`
Title string `json:"title"`
SeasonTitle string `json:"season_title"`
Mode int `json:"mode"`
Type int `json:"type"`
Alias string `json:"alias"`
AliasSearch string `json:"alias_search"`
Brief string `json:"brief"`
Evaluate string `json:"evaluate"`
Actors string `json:"actors"`
Staff string `json:"staff"`
SquareCover string `json:"square_cover"`
Cover string `json:"cover"`
EpCover string `json:"epcover"`
Area int `json:"area"`
Ctime string `json:"ctime"`
Mtime string `json:"mtime"`
}
// EsSeason ...
type EsSeason struct {
ID int `json:"id"`
Title string `json:"title"`
Alias string `json:"alias"`
AliasSearch []string `json:"alias_search"`
Actors []string `json:"actors"`
}
//EsFormat ...
func (s *Season) EsFormat() (es *EsSeason) {
es = &EsSeason{
ID: s.ID,
Title: s.Title,
Alias: s.Alias,
AliasSearch: make([]string, 0),
Actors: make([]string, 0),
}
if s.AliasSearch != "" {
es.AliasSearch = strings.Split(s.AliasSearch, ",")
}
if s.Actors != "" {
for _, act := range strings.Split(s.Actors, "\n") {
if act == "" {
continue
}
act := strings.Split(act, "")
if len(act) > 0 {
es.Actors = append(es.Actors, act[0])
}
}
}
return
}
// FieldDiff 检查指定字段是有变化
func (s *Season) FieldDiff(season *Season) bool {
return s.Title != season.Title ||
s.Alias != season.Alias ||
s.AliasSearch != season.AliasSearch ||
s.Actors != season.Actors ||
s.Mtime != season.Mtime
}

View File

@@ -0,0 +1,15 @@
package model
// Item ...
type Item struct {
ID int64 `json:"id"`
Name string `json:"name"`
IPRightID int `json:"ip_right_id"`
Brief string `json:"brief"`
Keywords string `json:"keywords"`
SalesCount int `json:"sales_count"`
WishCount int `json:"wish_count"`
CommentCount int `json:"comment_count"`
HeadImg string `json:"head_img"`
SugImg string `json:"sug_img"`
}

View File

@@ -0,0 +1 @@
package model

View File

@@ -0,0 +1,15 @@
package model
// Score ...
type Score struct {
SeasonID string
SeasonName string
Score float64
}
// Sug ...
type Sug struct {
Item *Item
SeasonID int64
Score int64
}

View File

@@ -0,0 +1,35 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = ["http.go"],
importpath = "go-common/app/job/openplatform/open-sug/server/http",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/openplatform/open-sug/conf:go_default_library",
"//app/job/openplatform/open-sug/service:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/http/blademaster/middleware/verify:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,53 @@
package http
import (
"net/http"
"go-common/app/job/openplatform/open-sug/conf"
"go-common/app/job/openplatform/open-sug/service"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/http/blademaster/middleware/verify"
)
var (
srv *service.Service
vfy *verify.Verify
)
// Init init
func Init(c *conf.Config, s *service.Service) {
srv = s
vfy = verify.New(c.Verify)
engine := bm.DefaultServer(c.BM)
route(engine)
if err := engine.Start(); err != nil {
log.Error("bm Start error(%v)", err)
panic(err)
}
}
func route(e *bm.Engine) {
e.Ping(ping)
e.Register(register)
g := e.Group("/x/open-sug")
{
g.GET("/start", vfy.Verify, howToStart)
}
}
func ping(c *bm.Context) {
if err := srv.Ping(c); err != nil {
log.Error("ping error(%v)", err)
c.AbortWithStatus(http.StatusServiceUnavailable)
}
}
func register(c *bm.Context) {
c.JSON(map[string]interface{}{}, nil)
}
// example for http request handler
func howToStart(c *bm.Context) {
c.String(0, "Golang 大法好 !!!")
}

View File

@@ -0,0 +1,39 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"databus.go",
"service.go",
"sug.go",
],
importpath = "go-common/app/job/openplatform/open-sug/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/job/openplatform/open-sug/conf:go_default_library",
"//app/job/openplatform/open-sug/dao:go_default_library",
"//app/job/openplatform/open-sug/model:go_default_library",
"//library/log:go_default_library",
"//library/queue/databus:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,87 @@
package service
import (
"context"
"encoding/json"
"fmt"
"strconv"
"go-common/app/job/openplatform/open-sug/conf"
"go-common/app/job/openplatform/open-sug/model"
"go-common/library/log"
)
func (s *Service) pgcConsumePROC() {
var (
msgs = s.pgcSub.Messages()
err error
)
for {
msg, ok := <-msgs
if !ok {
log.Error("pgc databus Consumer exit")
return
}
s.pgcMsgCnt++
msg.Commit()
log.Info("message commit key:%s partition:%d offset:%d", msg.Key, msg.Partition, msg.Offset)
m := &model.Message{}
if err = json.Unmarshal(msg.Value, m); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", msg.Value, err)
continue
}
func(m *model.Message) {
s.wg.Add(1)
//defer s.wg.Done()
go s.subproc()
if m.Table == "t_chn_season2" || m.Table == "t_jp_season2" {
s.seasonUpdate(m.Action, m.New, m.Old)
s.seasonMsgCnt++
}
}(m)
log.Info("pgcConsumeproc table:%s key:%s partition:%d offset:%d", m.Table, msg.Key, msg.Partition, msg.Offset)
}
}
func (s *Service) seasonUpdate(action string, n, o json.RawMessage) {
newSeason := new(model.Season)
if err := json.Unmarshal(n, newSeason); err != nil {
log.Error("json.Unmarshal(%s) error(%v)", n, err)
return
}
oldSeason := new(model.Season)
if err := json.Unmarshal(o, oldSeason); err != nil {
log.Error("json.Unmarshal(%s) (%s)error(%v)", string(o), o, err)
return
}
if action == "insert" || action == "update" {
if newSeason.FieldDiff(oldSeason) {
s.dao.Index(
context.TODO(),
s.envIndex(s.c.ElasticSearch.Season.Index),
s.c.ElasticSearch.Season.Type,
strconv.Itoa(newSeason.ID),
newSeason.EsFormat(),
)
}
}
}
func (s *Service) envIndex(index string) string {
return fmt.Sprintf("%s_%s", s.c.Env, index)
}
func (s *Service) existsOrCreate(c conf.EsIndex) {
if s.dao.IndexExists(context.TODO(), s.envIndex(c.Index)) {
log.Info("索引(%s)已存在", s.envIndex(c.Index))
return
}
if !s.dao.CreateIndex(context.TODO(), s.envIndex(c.Index), c.Mapping) {
panic("创建索引失败")
}
}
func (s *Service) subproc() {
defer s.wg.Done()
}

View File

@@ -0,0 +1,67 @@
package service
import (
"context"
"go-common/app/job/openplatform/open-sug/conf"
"go-common/app/job/openplatform/open-sug/dao"
"go-common/library/queue/databus"
"sync"
"time"
)
// Service struct
type Service struct {
c *conf.Config
dao *dao.Dao
pgcSub *databus.Databus
pgcMsgCnt int64
wg *sync.WaitGroup
seasonMsgCnt int64
}
// New init
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
dao: dao.New(c),
wg: new(sync.WaitGroup),
pgcSub: databus.New(c.PgcSub),
pgcMsgCnt: 0,
seasonMsgCnt: 0,
}
s.existsOrCreate(c.ElasticSearch.Season)
go s.pgcConsumePROC()
go s.fetchData()
return s
}
// Ping Service
func (s *Service) Ping(c context.Context) (err error) {
return s.dao.Ping(c)
}
// Close Service
func (s *Service) Close() {
s.dao.Close()
}
func (s *Service) fetchData() {
for {
s.buildData()
s.Refresh()
s.Filter()
time.Sleep(time.Hour * 24)
}
}
// Refresh ...
func (s *Service) Refresh() {
s.dao.ItemSalesMin = make(map[string]int)
s.dao.ItemSalesMax = make(map[string]int)
s.dao.ItemWishMax = make(map[string]int)
s.dao.ItemWishMin = make(map[string]int)
s.dao.ItemCommentMax = make(map[string]int)
s.dao.ItemCommentMin = make(map[string]int)
}

View File

@@ -0,0 +1,80 @@
package service
import (
"context"
"strconv"
"go-common/app/job/openplatform/open-sug/model"
"go-common/library/log"
)
func (s *Service) buildData() (err error) {
var (
itemList []*model.Item
scoreSlice []model.Score
)
if itemList, err = s.dao.FetchItem(context.TODO()); err != nil {
log.Error("pull project list error (%v)", err)
return
}
for _, item := range itemList {
if scoreSlice, err = s.dao.SeasonData(context.TODO(), item); err != nil {
log.Error("es item match season fail")
continue
}
if len(scoreSlice) > 0 {
if _, err = s.dao.SetItem(context.TODO(), item); err != nil {
log.Error("set item redis error(%v)", err)
continue
}
for _, score := range scoreSlice {
score.Score = score.Score * s.BuildScore(item, score.SeasonID)
if rowNum, _ := s.dao.InsertMatch(context.TODO(), item, score); rowNum > 0 {
if _, err = s.dao.SetSug(context.TODO(), score.SeasonID, item.ID, score.Score); err != nil {
log.Error("redis dao.SetSug error(%v)", err)
continue
}
}
}
}
}
return
}
// BuildScore ...
func (s *Service) BuildScore(item *model.Item, seasonID string) (normalizationScore float64) {
normalizationScore = 0.5*(s.Normalization(item.SalesCount, s.dao.ItemSalesMax[seasonID], s.dao.ItemSalesMin[seasonID])) + 0.2*(s.Normalization(item.CommentCount, s.dao.ItemCommentMax[seasonID], s.dao.ItemCommentMin[seasonID])) + 0.3*(s.Normalization(item.WishCount, s.dao.ItemWishMax[seasonID], s.dao.ItemCommentMin[seasonID]))
return
}
// Normalization ...
func (s *Service) Normalization(x int, max int, min int) (normalizationScore float64) {
if x == 0 {
return 0
}
if float64(max-min) == 0 {
return 1
}
return float64(x-min) / float64(max-min)
}
// Filter ...
func (s *Service) Filter() {
c := context.TODO()
if bindItems, err := s.dao.GetBind(c); err == nil {
for _, buildItem := range bindItems {
s.dao.GetItem(c, buildItem)
log.Info("add filter data items_id(%d) season_id(%d)", buildItem.Item.ID, buildItem.SeasonID)
if _, err = s.dao.SetItem(c, buildItem.Item); err != nil {
log.Error("set item redis error(%v)", err)
continue
}
log.Info("add sug res items_id(%d) season_id(%d)", buildItem.Item.ID, buildItem.SeasonID)
if _, err = s.dao.SetSug(c, strconv.FormatInt(buildItem.SeasonID, 10), buildItem.Item.ID, float64(buildItem.Score)); err != nil {
log.Error("redis dao.SetSug error(%v)", err)
continue
}
s.dao.UpdatePic(c, buildItem)
}
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB