123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- package services
- import (
- "fmt"
- "fpdxfeed/config"
- "fpdxfeed/database"
- "fpdxfeed/helpers"
- "fpdxfeed/models"
- "github.com/gomodule/redigo/redis"
- "github.com/jinzhu/gorm"
- "log"
- "math"
- "strconv"
- "sync"
- "time"
- )
- const (
- FPDXGZH = "gh_b598cb7474d8"
- FPDXXCX = "gh_01c089b58dda"
- QQXCX = "1109365561"
- CALCPARAMS = "msy-feed-params"
- TablePartner = "kdgx_partner_charge_partner"
- TableUser = "kdgx_partner_charge_user"
- TableAuthKey = "kdgx_user_auth_key"
- TableNoticeManage = "kdgx_miniprogram_notice_manage"
- TableOpenid = "kddx_user_openid"
- TableCardState = "fpdx_card_state_list"
- TableMiniprogramFormid = "kdgx_charge_fpdx_miniprogram_formid"
- TableQQMiniForms = "kdgx_qqmini_forms"
- TableUserSystag = "kdgx_fpdx_user_systag"
- TableFeedLog = "kdgx_fpdx_feed_log"
- )
- var (
- wg sync.WaitGroup
- pt1 float64
- pz float64
- pg float64
- pp float64
- pd float64
- pt3 float64
- count int
- t0 int
- )
- type Service struct {
- config *config.Config // 配置文件
- conns *database.Conns // 链接
- debug int // 模式
- goLimit int // 整理卡片进程的go程粒度
- sleep time.Duration // 休眠时长
- n0 int // 时间衰减初始分
- dt1 float64 // 最近更新加分衰变率1
- dt2 float64 // 赞数衰变率2*z(x)
- dt3 float64 // 发布时间加分衰变率3
- dt4 float64 // 最近上线加分衰变率4
- }
- func NewService(conf *config.Config, conns *database.Conns, debug int, goLimit int, sleep time.Duration, n0 int, dt1 float64, dt2 float64, dt3 float64, dt4 float64) (service *Service, err error) {
- if 2 == debug {
- conns.KdDB = conns.KdDB.Debug()
- conns.DataDb = conns.DataDb.Debug()
- }
- service = &Service{
- config: conf,
- conns: conns,
- debug: debug,
- goLimit: goLimit,
- sleep: sleep,
- n0: n0,
- dt1: dt1,
- dt2: dt2,
- dt3: dt3,
- dt4: dt4,
- }
- return
- }
- func (s *Service) Run() {
- s.execTask()
- }
- // 计算所有卡片基础分
- func (s *Service) execTask() {
- t0 = int(time.Now().Unix())
- val, err := redis.StringMap(s.conns.Redis.Do("hgetall", CALCPARAMS))
- if err != nil {
- // 异常退出
- log.Fatal(fmt.Errorf("获取计算参数错误:%v", err))
- }
- pt1, err := strconv.ParseFloat(val["pt1"], 64)
- if err != nil {
- pt1 = 0.2
- }
- pz, err := strconv.ParseFloat(val["pz"], 64)
- if err != nil {
- pz = 0.5
- }
- pg, err := strconv.ParseFloat(val["pg"], 64)
- if err != nil {
- pg = 0.5
- }
- pp, err := strconv.ParseFloat(val["pp"], 64)
- if err != nil {
- pp = 0.15
- }
- pd, err := strconv.ParseFloat(val["pd"], 64)
- if err != nil {
- pd = 0.1
- }
- pt3, err := strconv.ParseFloat(val["pt3"], 64)
- if err != nil {
- pt3 = 0.2
- }
- s.conns.KdDB.Table(TablePartner).Where("is_sell=?", 1).Count(&count)
- if s.debug > 0 {
- fmt.Printf("calc params: N0:%v, DT1:%f, DT2:%f, DT3:%f, DT4:%f, pt1:%v, pz:%v, pg:%v, pp:%v, pd:%v, pt3:%v\n", s.n0, s.dt1, s.dt2, s.dt3, s.dt4, pt1, pz, pg, pp, pd, pt3)
- fmt.Printf("partner total:%v\n", count)
- }
- for k := 0; k < count; {
- wg.Add(1)
- go s.calc(k, count)
- k += s.goLimit
- }
- wg.Wait()
- }
- // 卡片是否可通达
- func (s *Service) canNotice(uid int, partnerId int) bool {
- t := int(time.Now().Unix())
- var authkeys []models.AuthKey
- authErr := s.conns.KdDB.Table(TableAuthKey).Find(&authkeys, "uid=? and auth_type in (?,?,?)", uid, "kdgx_unionid", FPDXXCX, QQXCX).Error
- if authErr == gorm.ErrRecordNotFound {
- return false
- }
- authkey := make(map[string]string)
- for _, auth := range authkeys {
- authkey[auth.AuthType] = auth.AuthKey
- }
- if kdgxUnionid, ok := authkey["kdgx_unionid"]; ok {
- // 分配对象公众号
- var openid models.Openid
- publicErr := s.conns.KdDB.Table(TableOpenid).Where("unionid=? and public_id = ? and subscribe = ?", kdgxUnionid, FPDXGZH, 1).Find(&openid).Error
- if publicErr == nil {
- s.conns.DataDb.Table(TableCardState).Where("partner_id=?", partnerId).Updates(map[string]interface{}{"push_fpdx": 1})
- return true
- } else {
- s.conns.DataDb.Table(TableCardState).Where("partner_id=?", partnerId).Updates(map[string]interface{}{"push_fpdx": 0})
- }
- }
- // 微信小程序
- if kdgxWxxcx, ok := authkey[FPDXXCX]; ok {
- var formid models.MiniprogramFormid
- formErr := s.conns.KdDB.Table(TableMiniprogramFormid).Where("openid=? and public_id=? and state=? and created_at > ?", kdgxWxxcx, FPDXXCX, 0, t-86000*7).Find(&formid).Error
- if formErr == nil {
- s.conns.DataDb.Table(TableCardState).Where("partner_id=?", partnerId).Updates(map[string]interface{}{"is_mp_push": 1})
- return true
- }
- }
- // qq小程序
- if kdgxQQxcx, ok := authkey[QQXCX]; ok {
- var qqformid models.QQFormid
- qqformErr := s.conns.KdDB.Table(TableQQMiniForms).Where("openid=? and appid=? and send_at=? and created_at > ?", kdgxQQxcx, QQXCX, 0, t-86000*7).Find(&qqformid).Error
- if qqformErr == nil {
- s.conns.DataDb.Table(TableCardState).Where("partner_id=?", partnerId).Updates(map[string]interface{}{"is_mp_push": 1})
- return true
- } else {
- s.conns.DataDb.Table(TableCardState).Where("partner_id=?", partnerId).Updates(map[string]interface{}{"is_mp_push": 0})
- }
- }
- return false
- }
- func (s *Service) appNotice(user *models.User) bool {
- t := int(time.Now().Unix() - 86400 * 30)
- if user.LoginAt > t && (user.LoginAppPlatform == "ios" || user.LoginAppPlatform == "android") {
- return true
- } else {
- return false
- }
- }
- func (s *Service) calc(k, cnt int) {
- defer wg.Done()
- t := int(time.Now().Unix())
- // 获取需要提取的数据段
- limit := s.goLimit
- if k+s.goLimit > cnt {
- limit = cnt - k
- }
- var partners []models.Partner
- s.conns.KdDB.Table(TablePartner).Where("is_sell=1").Offset(k).Limit(limit).Find(&partners)
- for _, v := range partners {
- if s.debug > 0 {
- fmt.Printf(fmt.Sprintf("partner_id:%d\n", v.Id))
- }
- var user models.User
- s.conns.KdDB.Table(TableUser).Where("uid=?", v.Uid).Find(&user)
- if !s.canNotice(v.Uid, v.Id) && !s.appNotice(&user) {
- /* 通知不可达 */
- // 权重分数清空 + 上报日志
- s.conns.KdDB.Table(TablePartner).Where("id=?", v.Id).Updates(map[string]interface{}{"base_score": -1, "score": -1})
- s.conns.DataDb.Table(TableCardState).Where("partner_id=?", v.Id).Updates(map[string]interface{}{"base_score": -1})
- continue
- }
- // 最近更新时间
- t1 := float64((t - v.UpdateAt) / 3600)
- if v.UpdateAt == 0 {
- t1 = float64((t - v.CreatedAt) / 3600)
- }
- // 点赞数
- z := v.Praises + 1
- // 曝光量
- b := v.FeedCnt + 2
- if b < z {
- b = z * 2
- }
- // 是否有配音 v.Voice
- p := 60
- if v.Voice.String != "" && !v.Voice.Valid {
- p = 100
- }
- // 是否有多张图片
- d := 50
- if v.PhotoSrc.String != "" && !v.PhotoSrc.Valid {
- d += 10
- }
- if v.Photo1.String != "" && !v.Photo1.Valid {
- d += 10
- }
- if v.Photo2.String != "" && !v.Photo2.Valid {
- d += 10
- }
- if v.Photo3.String != "" && !v.Photo3.Valid {
- d += 10
- }
- if v.Photo4.String != "" && !v.Photo4.Valid {
- d += 10
- }
- // 最近上线
- t2 := float64((t - user.LoginAt) / 3600)
- if 720 <= t2 && 2160 > t2 {
- t2 = 100
- } else {
- if 2160 < t2 {
- // 长时间不登录 => 置为上架不推荐 + 上报日志
- s.conns.KdDB.Table(TablePartner).Where("id=? and is_push_feed=?", v.Id, 1).Updates(map[string]interface{}{"is_push_feed": 0, "long_no_login_tag": 1})
- s.conns.DataDb.Table(TableCardState).Where("partner_id=?", v.Id).Updates(map[string]interface{}{"is_push_feed": 0})
- }
- }
- // 发布时间
- t3 := float64((t - v.UploadAt) / 3600)
- if v.UploadAt == 0 {
- t3 = float64((t - v.CreatedAt) / 3600)
- }
- // 最近更新时间
- score1 := float64(100) * math.Exp(float64(s.dt1*t1)) * pt1
- // 点赞/曝光
- score2 := float64(z/b) * 100 * pz
- // 获取近三天曝光量
- var viserCnt int
- s.conns.KdDB.Table(TableFeedLog).Where("created_at > ? and partner_id=?", t-86400*3, v.Id).Count(&viserCnt)
- // 曝光量同步到数据库
- s.conns.KdDB.Table(TablePartner).Where("id=?", v.Id).Updates(map[string]interface{}{"last_three_day_feed": viserCnt})
- // 正态分布=NORMSDIST((近3天曝光数/100))*2*100*近三天曝光数占比
- score3 := helpers.Normsdist(float64(viserCnt/100)) * 200 * pg
- // 声音
- score4 := float64(p) * pp
- // 照片
- score5 := float64(d) * pd
- // 发布时间
- score6 := 100 * math.Exp(float64(s.dt3*t3)) * pt3
- // 基础分
- baseScore := int((score1 + score2 + score3 + score4 + score5 + score6) * math.Exp(float64(s.dt4*t2)))
- if s.debug > 0 {
- fmt.Printf("t1=%v\tscore1=%v\n", t1, score1)
- fmt.Printf("z=%v;b=%v;score2=%v\n", z, b, score2)
- fmt.Printf("vistor=%v;score3=%v\n", viserCnt, score3)
- fmt.Printf("score4=%v\n", score4)
- fmt.Printf("score5=%v\n", score5)
- fmt.Printf("t3=%v;score6=%v\n", t3, score6)
- fmt.Printf("t2=%v;baseScore=%v\n", t2, baseScore)
- }
- // 人气值
- popularity := 0
- switch {
- case 0 <= baseScore && baseScore < 40:
- popularity = 0 + int(100/40*(baseScore-0))
- case 40 <= baseScore && baseScore < 50:
- popularity = 100 + int(100/10)*(baseScore-40)
- case 50 <= baseScore && baseScore < 60:
- popularity = 200 + int(100/10)*(baseScore-50)
- case 60 <= baseScore && baseScore < 70:
- popularity = 200 + int(100/10)*(baseScore-60)
- case 70 <= baseScore && baseScore < 90:
- popularity = 500 + int(300/10)*(baseScore-70)
- case 90 <= baseScore:
- popularity = 800 + int(400/10)*(baseScore-90)
- default:
- popularity = 0
- }
- var usersystag models.UserSystag
- err := s.conns.KdDB.Table(TableUserSystag).Where("uid=?", v.Uid).Find(&usersystag).Error
- if err != nil {
- usersystag.Uid = v.Uid
- usersystag.PopularityShareEndAt = 0
- }
- // 分享加分
- if usersystag.PopularityShareEndAt > t {
- popularity += 100
- }
- // 签到加分
- var userstate models.UserState
- err = s.conns.KdDB.Table("kdgx_fpdx_user_states").Where("uid=? and `key`='popularity_sign_end_at'", v.Uid).Find(&userstate).Error
- if err != nil && err != gorm.ErrRecordNotFound {
- value, _ := strconv.Atoi(userstate.Value)
- if value > t {
- popularity += 100
- }
- }
- // 超级会员加分
- if user.SupvipEndat > t {
- popularity += 100
- }
- score := 0
- switch {
- case 0 <= popularity && popularity < 100:
- score = 0 + int((popularity-0)/((100-0)/(40-0)))
- case 100 <= popularity && popularity < 200:
- score = 40 + int((popularity-100)/((100-0)/(50-40)))
- case 200 <= popularity && popularity < 300:
- score = 50 + int((popularity-200)/((300-200)/(60-50)))
- case 300 <= popularity && popularity < 500:
- score = 60 + int((popularity-300)/(500-300)/(70-60))
- case 500 <= popularity && popularity < 800:
- score = 70 + int((popularity-500)/(800-500)/(90-70))
- case 800 <= popularity && popularity < 1200:
- score = 90 + int((popularity-800)/(1200-800)/(120-90))
- default:
- score = 1
- }
- // 将权重和热度记录写进数据库 + 上报日志
- s.conns.KdDB.Table(TablePartner).Where("id=?", v.Id).Updates(map[string]interface{}{"base_score": baseScore, "score": score})
- s.conns.DataDb.Table(TableCardState).Where("partner_id=?", v.Id).Updates(map[string]interface{}{"base_score": baseScore})
- }
- }
|