go-common/app/interface/bbq/app-bbq/dao/sv.go
2019-04-22 18:49:16 +08:00

387 lines
11 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 dao
import (
"context"
"encoding/json"
"fmt"
"math/rand"
"net/url"
"sort"
"strconv"
"strings"
"time"
"go-common/app/interface/bbq/app-bbq/api/http/v1"
"go-common/app/interface/bbq/app-bbq/model"
"go-common/app/interface/bbq/app-bbq/model/grpc"
rec "go-common/app/service/bbq/recsys/api/grpc/v1"
video "go-common/app/service/bbq/video/api/grpc/v1"
"go-common/library/database/sql"
"go-common/library/log"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/metadata"
xgrpc "google.golang.org/grpc"
)
const (
_defaultPlatform = "html5"
_playBcNum = 1
_queryList = "select `avid`, `cid`, `svid`, `title`, `mid`, `content`, `pubtime`,`duration`,`tid`,`sub_tid`,`ctime`,`cover_url`,`cover_width`,`cover_height`,`state` from video where svid in (%s)"
_queryRand = "select `svid` from video where state in (4,5) order by mtime desc limit 400;"
_queryStatisticsList = "select `svid`, `play`, `subtitles`, `like`, `share`, `report` from video_statistics where svid in (%s)"
_querySvPlay = "select `svid`,`path`,`resolution_retio`,`code_rate`,`video_code`,`file_size`,`duration` from %s where svid in (%s) and is_deleted = 0 order by code_rate desc"
)
// GetList 获取列表按照db排序按page返回用于推荐的降级
func (d *Dao) GetList(c context.Context, pageSize int64) (result []int64, err error) {
rows, err := d.db.Query(c, _queryRand)
if err != nil {
log.Error("Query(%s) error(%v)", _queryRand, err)
return
}
defer rows.Close()
var list []int64
for rows.Next() {
sv := new(model.SvInfo)
if err = rows.Scan(&sv.SVID); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
if tag, ok := d.c.Tmap[strconv.FormatInt(sv.TID, 10)]; ok {
sv.Tag = tag
}
list = append(list, sv.SVID)
}
size := len(list)
if size > int(pageSize) {
size = int(pageSize)
}
rand.Seed(time.Now().Unix())
sort.Slice(list, func(i, j int) bool {
return rand.Float32() > 0.5
})
result = list[:size]
return
}
// AttentionRecList 关注页为空时的推荐
func (d *Dao) AttentionRecList(ctx context.Context, size int64, mid int64, buvid string) (svIDs []int64, err error) {
svIDs, err = d.abstractRawRecList(ctx, d.recsysClient.UpsRecService, size, mid, buvid)
log.V(1).Infow(ctx, "log", "get ups rec service", "method", "UpsRecService")
return
}
// RawRecList 获取推荐列表
func (d *Dao) RawRecList(ctx context.Context, size int64, mid int64, buvid string) (svIDs []int64, err error) {
svIDs, err = d.abstractRawRecList(ctx, d.recsysClient.RecService, size, mid, buvid)
log.V(1).Infow(ctx, "log", "get ups rec service", "method", "RecService")
return
}
type recsysFunc func(ctx context.Context, in *rec.RecsysRequest, opts ...xgrpc.CallOption) (*rec.RecsysResponse, error)
// abstractRawRecList 由于访问recsys的方法都是同样的请求&回包,同时过程也一样,因此
func (d *Dao) abstractRawRecList(ctx context.Context, f recsysFunc, size int64, mid int64, buvid string) (svIDs []int64, err error) {
var (
res *rec.RecsysResponse
)
req := &rec.RecsysRequest{
MID: mid,
BUVID: buvid,
Limit: int32(size),
}
if tmp, ok := ctx.(*bm.Context).Get("BBQBase"); ok && tmp != nil {
switch tmp.(type) {
case *v1.Base:
base := tmp.(*v1.Base)
req.App = base.App
req.AppVersion = base.Version
}
}
if tmp, ok := ctx.(*bm.Context).Get("QueryID"); ok && tmp != nil {
switch tmp.(type) {
case string:
req.QueryID = tmp.(string)
}
}
log.Info("Rec请求 params: [%v]", req)
//res, err = d.recsysClient.RecService(ctx, req)
res, err = f(ctx, req)
debug := ctx.(*bm.Context).Request.Header.Get("debug")
if err != nil {
log.Errorv(ctx,
log.KV("log", fmt.Sprintf("d.recsysClient.RecService err [%v]", err)),
)
// 降级(推荐服务已挂)
svIDs = d.GetRandSvList(int(size))
return
} else if len(res.List) == 0 || debug == "1" {
log.Warnv(ctx,
log.KV("log", fmt.Sprintf("d.recsysClient.RecService return empty [%v]", res.List)),
)
// 降级(推荐接口返回空)
svIDs = d.GetRandSvList(int(size))
return
} else {
num := len(res.List)
if int64(num) != size {
log.Warnv(ctx,
log.KV("log", fmt.Sprintf("d.recsysClient.RecService return num[%d] not match size[%d]", num, size)),
)
}
for n, sv := range res.List {
if int64(n) > size {
break
}
svIDs = append(svIDs, sv.Svid)
}
}
return
}
// GetVideoDetail 从数据库video中获取svid相应的信息
func (d *Dao) GetVideoDetail(ctx context.Context, svIDs []int64) (list []*model.SvInfo, retIDs []int64, err error) {
list = make([]*model.SvInfo, 0)
num := len(svIDs)
if num == 0 {
return
}
var IDsStr string
for i, id := range svIDs {
if i < num-1 {
IDsStr += strconv.FormatInt(id, 10) + ","
} else {
IDsStr += strconv.FormatInt(id, 10)
}
}
query := fmt.Sprintf(_queryList, IDsStr)
rows, err := d.db.Query(ctx, query)
if err != nil {
log.Error("Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
sv := new(model.SvInfo)
if err = rows.Scan(&sv.AVID, &sv.CID, &sv.SVID, &sv.Title, &sv.MID, &sv.Content, &sv.Pubtime, &sv.Duration, &sv.TID, &sv.SubTID, &sv.Ctime, &sv.CoverURL, &sv.CoverWidth, &sv.CoverHeight, &sv.State); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
if tag, ok := d.c.Tmap[strconv.FormatInt(sv.TID, 10)]; ok {
sv.Tag = tag
}
retIDs = append(retIDs, sv.SVID)
list = append(list, sv)
}
return
}
// RawVideos 从数据库批量获取视频信息
func (d *Dao) RawVideos(ctx context.Context, svIDs []int64) (res map[int64]*model.SvInfo, err error) {
res = make(map[int64]*model.SvInfo)
num := len(svIDs)
if num == 0 {
return
}
var IDsStr string
for i, id := range svIDs {
if i < num-1 {
IDsStr += strconv.FormatInt(id, 10) + ","
} else {
IDsStr += strconv.FormatInt(id, 10)
}
}
query := fmt.Sprintf(_queryList, IDsStr)
rows, err := d.db.Query(ctx, query)
if err != nil {
log.Error("Query error(%v)", err)
return
}
defer rows.Close()
for rows.Next() {
sv := new(model.SvInfo)
if err = rows.Scan(&sv.AVID, &sv.CID, &sv.SVID, &sv.Title, &sv.MID, &sv.Content, &sv.Pubtime, &sv.Duration, &sv.TID, &sv.SubTID, &sv.Ctime, &sv.CoverURL, &sv.CoverWidth, &sv.CoverHeight, &sv.State); err != nil {
log.Error("row.Scan() error(%v)", err)
return
}
res[sv.SVID] = sv
}
return
}
// RawVideoStatistic get video statistics
func (d *Dao) RawVideoStatistic(c context.Context, svids []int64) (res map[int64]*model.SvStInfo, err error) {
const maxIDNum = 20
var (
idStr string
)
res = make(map[int64]*model.SvStInfo)
if len(svids) > maxIDNum {
svids = svids[:maxIDNum]
}
l := len(svids)
for k, svid := range svids {
if k < l-1 {
idStr += strconv.FormatInt(svid, 10) + ","
} else {
idStr += strconv.FormatInt(svid, 10)
}
res[svid] = &model.SvStInfo{}
}
rows, err := d.db.Query(c, fmt.Sprintf(_queryStatisticsList, idStr))
if err != nil {
log.Error("query error(%s)", err.Error())
return
}
defer rows.Close()
for rows.Next() {
ssv := new(model.SvStInfo)
if err = rows.Scan(&ssv.SVID, &ssv.Play, &ssv.Subtitles, &ssv.Like, &ssv.Share, &ssv.Report); err != nil {
log.Error("RawVideoStatistic rows.Scan() error(%v)", err)
return
}
res[ssv.SVID] = ssv
}
cmtCount, _ := d.ReplyCounts(c, svids, model.DefaultCmType)
for id, cmt := range cmtCount {
if _, ok := res[id]; ok {
res[id].Reply = cmt.Count
}
}
return
}
// RawPlayURLs 批量获取cid playurl
func (d *Dao) RawPlayURLs(c context.Context, cids []int64, qn int64, plat string) (res map[int64]*v1.CVideo, err error) {
res = make(map[int64]*v1.CVideo)
var cs string
// transfer cid array to string
l := len(cids)
for i := 0; i < l; i++ {
if i != l-1 {
cs += strconv.FormatInt(cids[i], 10) + ","
} else {
cs += strconv.FormatInt(cids[i], 10)
}
}
ip := metadata.String(c, metadata.RemoteIP)
params := url.Values{}
params.Set("cid", cs)
params.Set("qn", strconv.FormatInt(qn, 10))
params.Set("platform", plat)
var ret struct {
Code int `json:"code"`
Data map[string]map[string]*v1.CVideo `json:"data"`
}
err = d.httpClient.Get(c, d.c.URLs["bvc_batch"], ip, params, &ret)
if err != nil || ret.Data["cids"] == nil {
log.Error("http Get err %v", err)
return
}
for id, v := range ret.Data["cids"] {
var cid int64
cid, err = strconv.ParseInt(id, 10, 64)
if err != nil {
log.Error("strconv.ParseInt err %v", err)
}
if _, ok := res[cid]; !ok {
res[cid] = new(v1.CVideo)
}
res[cid] = v
}
return
}
// RelPlayURLs 相对地址批量获取playurl
func (d *Dao) RelPlayURLs(c context.Context, addrs []string) (res map[string]*grpc.VideoKeyItem, err error) {
res = make(map[string]*grpc.VideoKeyItem)
req := &grpc.RequestMsg{
Keys: addrs,
Backup: uint32(_playBcNum),
Platform: _defaultPlatform,
UIP: metadata.String(c, metadata.RemoteIP),
}
_str, _ := json.Marshal(req)
log.V(5).Infov(c, log.KV("log", fmt.Sprintf("bvc play req (%s)", string(_str))))
r, err := d.bvcPlayClient.ProtobufPlayurl(c, req)
_str, _ = json.Marshal(r)
if err != nil {
log.Error("bvc play err[%v] ret[%s]", err, string(_str))
return
}
log.V(5).Infov(c, log.KV("log", fmt.Sprintf("bvc play ret (%s)", string(_str))))
res = r.Data
return
}
//RawSVBvcKey 批量获取playurl相对地址
func (d *Dao) RawSVBvcKey(c context.Context, svids []int64) (res map[int64][]*model.SVBvcKey, err error) {
var (
tb map[string][]string
rows *sql.Rows
)
res = make(map[int64][]*model.SVBvcKey)
tb = make(map[string][]string)
tName := "video_bvc_%02d"
for _, v := range svids {
if v <= 0 {
continue
}
tbName := fmt.Sprintf(tName, v%100)
tb[tbName] = append(tb[tbName], strconv.FormatInt(v, 10))
}
for k, v := range tb {
query := fmt.Sprintf(_querySvPlay, k, strings.Join(v, ","))
if rows, err = d.db.Query(c, query); err != nil {
log.Errorv(c, log.KV("log", "RawSVBvcKey query sql"), log.KV("err", err))
continue
}
for rows.Next() {
tmp := model.SVBvcKey{}
if err = rows.Scan(&tmp.SVID, &tmp.Path, &tmp.ResolutionRetio, &tmp.CodeRate, &tmp.VideoCode, &tmp.FileSize, &tmp.Duration); err != nil {
log.Errorv(c, log.KV("log", "RawSVBvcKey scan"), log.KV("err", err))
continue
}
res[tmp.SVID] = append(res[tmp.SVID], &tmp)
}
}
return
}
// RelRecList 相关推荐列表
func (d *Dao) RelRecList(ctx context.Context, req *rec.RecsysRequest) (svIDs []int64, err error) {
log.V(1).Infov(ctx, log.KV("log", fmt.Sprintf("RelatedRecService req [%+v]", req)))
res, err := d.recsysClient.RelatedRecService(ctx, req)
if err != nil {
log.Errorv(ctx,
log.KV("log", fmt.Sprintf("RelatedRecService err [%v]", err)),
)
return
}
num := len(res.List)
if int32(num) != req.Limit {
log.Errorv(ctx,
log.KV("log", fmt.Sprintf("RelatedRecService ret num[%d] not match req size[%d]", num, req.Limit)),
)
}
for n, sv := range res.List {
if int32(n) > req.Limit {
break
}
svIDs = append(svIDs, sv.Svid)
}
log.V(1).Infov(ctx, log.KV("log", fmt.Sprintf("RelatedRecService svid [%+v]", svIDs)))
return
}
// SvDel 视频删除
func (d *Dao) SvDel(c context.Context, in *video.VideoDeleteRequest) (interface{}, error) {
return d.videoClient.VideoDelete(c, in)
}