wkyuer 1 maand geleden
bovenliggende
commit
6b3cd282b7

+ 241 - 0
common/match/match.go

@@ -0,0 +1,241 @@
+package match
+
+import (
+	"context"
+	"fmt"
+	"github.com/gogf/gf/v2/frame/g"
+)
+
+// 定义前缀树节点
+type TrieNode struct {
+	Children map[rune]*TrieNode // 子节点
+	IsEnd    bool               // 是否为单词结尾
+}
+
+// 插入单词到前缀树中
+func (tn *TrieNode) Insert(words ...string) {
+	for _, word := range words {
+		current := tn
+		for _, char := range word {
+			if current.Children == nil {
+				current.Children = make(map[rune]*TrieNode)
+			}
+			node, exists := current.Children[char]
+			if !exists {
+				node = &TrieNode{}
+				current.Children[char] = node
+			}
+			current = node
+		}
+		current.IsEnd = true
+	}
+}
+
+// Remove 删除单词
+func (tn *TrieNode) Remove(words ...string) {
+	if tn == nil {
+		g.Log().Infof(context.Background(), "TrieNode Remove is nil")
+	}
+	for _, word := range words {
+		current := tn
+		parent := tn
+		if tn == nil {
+			return
+		}
+		var lastChar int32
+		for _, char := range word {
+			if current == nil || current.Children == nil {
+				return
+			}
+			node, _ := current.Children[char]
+			parent = current
+			current = node
+			lastChar = char
+		}
+
+		if current != nil && current.IsEnd {
+			current.IsEnd = false
+		}
+		if current != nil && current.Children != nil && len(current.Children) == 0 {
+			delete(parent.Children, lastChar)
+		}
+
+	}
+}
+
+//func (trie *TrieNode) Print() {
+//	g.Dump(trie)
+//}
+
+// 加载数据到前缀树中
+func (trie *TrieNode) LoadDataToTrie(data map[string]int) {
+	for key, value := range data {
+		runes := []rune(key)
+		current := trie
+		for i, char := range runes {
+			isEnd := 0
+			if i == len(runes)-1 {
+				isEnd = value
+			}
+			if current.Children == nil {
+				current.Children = make(map[rune]*TrieNode)
+			}
+			node, exists := current.Children[char]
+			if !exists {
+				node = &TrieNode{}
+				current.Children[char] = node
+			}
+			current = node
+			current.IsEnd = isEnd == 1
+		}
+	}
+}
+
+// 在前缀树中查找单词
+func (trie *TrieNode) FindWordInTrie(word string) bool {
+	current := trie
+	for _, char := range word {
+		node, exists := current.Children[char]
+		if !exists {
+			return false
+		}
+		current = node
+	}
+	return current.IsEnd
+}
+
+// 在文本中查找单词
+func (trie *TrieNode) FindWords(text string) []string {
+	words := map[string]struct{}{}
+	var rData []string
+
+	runes := []rune(text)
+	for i := 0; i < len(runes); i++ {
+		current := trie
+		for j := i; j < len(runes); j++ {
+			char := runes[j]
+			node, exists := current.Children[char]
+			if !exists {
+				break
+			}
+			current = node
+			if current.IsEnd {
+				words[string(runes[i:j+1])] = struct{}{}
+			}
+		}
+	}
+	for word, _ := range words {
+		rData = append(rData, word)
+	}
+	return rData
+}
+
+func (trie *TrieNode) FindOneMaxStr(text string) string {
+	stringArr := trie.FindWords(text)
+	if len(stringArr) == 0 {
+		return "" // 处理空数组的情况
+	}
+
+	longest := stringArr[0]
+	for _, s := range stringArr {
+		if len(s) > len(longest) {
+			longest = s
+		}
+	}
+	return longest
+}
+
+// 在文本中查找单词
+func (trie *TrieNode) FindWordsInText(title, detail string) map[string]string {
+	words := map[string]string{}
+	var nomatchDetail string
+	runes := []rune(fmt.Sprintf("%s %s", detail, title))
+	detailArr := []rune(detail)
+	detailLen := len(detailArr)
+	for i := 0; i < len(runes); i++ {
+		current := trie
+		for j := i; j < len(runes); j++ {
+			char := runes[j]
+			node, exists := current.Children[char]
+			if !exists {
+				break
+			}
+			current = node
+			if current.IsEnd {
+				if _, ok := words[string(runes[i:j+1])]; !ok {
+					if j < detailLen { //匹配正文
+						//fmt.Println(string(runes[i : j+1]))
+						words[string(runes[i:j+1])] = getDetailMatchStr(detailArr, i, j)
+					} else { //匹配到标题
+						if nomatchDetail == "" && detailLen > 0 { //匹配到标题后,正文取开头
+							nomatchDetail = getDetailMatchStr(detailArr, detailLen-1, detailLen)
+						}
+						words[string(runes[i:j+1])] = nomatchDetail
+					}
+				}
+			}
+		}
+	}
+
+	return words
+}
+
+// 取关键词前40后40个字
+var splitRune = map[int32]bool{}
+
+func init() {
+	splitRune = map[int32]bool{}
+	for _, s := range []string{" ", ",", ".", ",", "。", " ", ":", ":", "、", "-"} {
+		splitRune[[]rune(s)[0]] = true
+	}
+}
+
+// getDetailMatchStr 获取正文中匹配到的短句
+func getDetailMatchStr(desc []rune, left, right int) string {
+	s, e, l := 0, 0, len(desc)
+
+	sT := left - 40
+	if sT < 0 {
+		sT = 0
+	}
+
+	eT := sT + 80
+	if eT > l {
+		eT = l
+	}
+
+	for i := 1; i < 20; i++ {
+		if s != 0 && e != 0 {
+			return string(desc[s:e])
+		}
+
+		if s == 0 {
+			//fmt.Printf("[sT]%s-%d   %s-%d\n", string(desc[sT-i]), desc[sT-i], string(desc[sT+i]), desc[sT+i])
+			if sT+i < left && splitRune[desc[sT+i]] {
+				s = sT + i + 1
+			}
+			if sT-i > 0 && splitRune[desc[sT-i]] {
+				s = sT - i + 1
+			}
+		}
+
+		if e == 0 {
+			//fmt.Printf("[eT]%s-%d   %s-%d\n", string(desc[eT-i]), desc[eT-i], string(desc[eT+i]), desc[eT+i])
+			if eT+i < l && splitRune[desc[eT+i]] {
+				e = eT + i
+			}
+			if eT-i > right && splitRune[desc[eT-i]] {
+				e = eT - i
+			}
+		}
+	}
+
+	if s == 0 {
+		s = sT
+	}
+	if e == 0 {
+		e = eT
+	}
+
+	return string(desc[s:e])
+}

+ 43 - 0
urlStatic/clickIterSource/clickIterSource.go

@@ -0,0 +1,43 @@
+package clickIterSource
+
+import (
+	"context"
+	"fmt"
+	"github.com/ClickHouse/clickhouse-go/v2"
+	"github.com/ClickHouse/clickhouse-go/v2/lib/driver"
+	"github.com/gogf/gf/v2/frame/g"
+	"time"
+)
+
+var Ch_analysis driver.Conn
+
+func init() {
+	var (
+		ctx = context.Background()
+		err error
+	)
+	Ch_analysis, err = clickhouse.Open(&clickhouse.Options{
+		Addr:         []string{g.Cfg().MustGet(ctx, "database_ck.address", "").String()},
+		DialTimeout:  10 * time.Second,
+		MaxIdleConns: g.Cfg().MustGet(ctx, "database_ck.maxIdleConns", 5).Int(),
+		MaxOpenConns: g.Cfg().MustGet(ctx, "database_ck.maxOpenConns", 5).Int(),
+		Auth: clickhouse.Auth{
+			Database: g.Cfg().MustGet(ctx, "database_ck.dbName", "").String(),
+			Username: g.Cfg().MustGet(ctx, "database_ck.username", "").String(),
+			Password: g.Cfg().MustGet(ctx, "database_ck.password", "").String(),
+		},
+		Debugf: func(format string, v ...interface{}) {
+			fmt.Printf(format, v)
+		},
+	})
+	if err != nil {
+		g.Log().Panicf(ctx, "初始化数据源异常 %v", err)
+	}
+	if err := Ch_analysis.Ping(ctx); err != nil {
+		if exception, ok := err.(*clickhouse.Exception); ok {
+			g.Log().Panicf(ctx, "clickhouse Ping %s", exception.Message)
+		}
+		g.Log().Panicf(ctx, "初始化数据源异常 %v", err)
+	}
+	g.Log().Infof(ctx, "初始化clickhouse数据源成功")
+}

+ 85 - 0
urlStatic/config.yaml

@@ -0,0 +1,85 @@
+runCron:
+  "# * * * * *"
+
+runOnce:
+  isRun: true
+  startDate: "2025-06-20 00:00:00"
+  endDate: "2025-06-25 10:00:00"
+
+database:
+  default:
+    link: "clickhouse:jytop:pwdTopJy123@tcp(172.20.45.129:19000)/pub_tags?dial_timeout=2000ms&max_execution_time=60"
+    #link: "clickhouse:jianyu_appl:Cli3#fkh4ouSe@tcp(127.0.0.1:9420)/pub_tags?dial_timeout=2000ms&max_execution_time=60"
+    #debug: true
+
+database_ck:
+  address: 172.20.45.129:19000
+  username: jytop
+  password: pwdTopJy123
+  dbName: data_analysis
+  maxIdleConns: 5
+  maxOpenConns: 5
+
+
+match:
+  - name: 超级订阅购买
+    code: buy_page_vip
+    rule:
+      action_id: c_jy_open_page
+      url: ["/swordfish/page_big_pc/free/svip/buy","/jy_mobile/common/order/create/svip","/jy_mobile/order/create/svip"]
+  - name: 大会员购买页
+    code: buy_page_member
+    rule:
+      action_id: c_jy_open_page
+      url: ["/big/pc/page/buy_commit","/jy_mobile/common/order/create/bigmember"]
+  - name: 省份订阅包购买页
+    code: buy_page_member
+    rule:
+      action_id: c_jy_open_page
+      url: ["/jy_mobile/common/order/create/areapack","/jy_mobile/order/create/areapack"]
+  - name: 业主采购分析报告下载包购买页
+    code: buy_page_buyer_report
+    rule:
+      action_id: c_jy_open_page
+      url: ["/swordfish/page_big_pc/order/download-pack/prop","/jy_mobile/order/create/owneranalysis"]
+  - name: 企业中标分析报告下载包购买页
+    code: buy_page_winner_report
+    rule:
+      action_id: c_jy_open_page
+      url: ["/swordfish/page_big_pc/order/download-pack/ent","/jy_mobile/order/create/enterpriseanalysis"]
+  - name: 市场分析定制报告下载包购买
+    code: buy_page_market_report
+    rule:
+      action_id: c_jy_open_page
+      url: ["/swordfish/page_big_pc/order/download-pack/market","/jy_mobile/order/create/marketanalysis" ]
+  - name: 采购单位画像包购买页单
+    code: buy_page_buyer_portrait_package
+    rule:
+      action_id: c_jy_open_page
+      url: ["/jy_mobile/common/order/create/buyerpack","/swordfish/page_big_pc/order/buyer-pack","/jy_mobile/order/create/buyerpack"]
+  - name: 附件下载包购买页单
+    code: buy_page_attach_package
+    rule:
+      action_id: c_jy_open_page
+      url: [ "/jy_mobile/common/order/create/filepack","/swordfish/page_big_pc/order/file-pack","/jy_mobile/order/create/filepack"]
+  - name: 投标企业信用报告购买页
+    code: buy_page_ent_credit_report
+    rule:
+      action_id: c_jy_open_page
+      url: [ "/jy_mobile/order/create/creditreport","/swordfish/page_big_pc/order/credit-report"]
+  - name: 碎片化小程序购买页单
+    code: buy_page_applet_[mini_program_code]
+    rule:
+      mini_program_code: ["wy","clzl","ywsj","hjcg","jjztb","af","gcsjzx","fw","bx","cwsj","zbdl","glzx"]
+      url: [ "pages/order/create/index"]
+  - name: 运营活动
+    code: jy_activities
+    rule:
+      action_id: c_jy_open_page
+      url: [ "202402?id=RVo=","202504?id=RVk=","/jyapp/view/682ed9441d749c1b910c9de4#/","/view/682d9cad1d749c1b910c9b31#/","/view/682da3911d749c1b910c9b71#/"]
+  - name: 下载信用报告
+    code: download_ent_credit_report
+    rule:
+      page_id: article_content
+      action_id: c_jyclick
+      breaker_name: 点击下载

+ 236 - 0
urlStatic/job/job.go

@@ -0,0 +1,236 @@
+package job
+
+import (
+	uuid2 "app.yhyue.com/moapp/jybase/go-xweb/uuid"
+	"context"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/util/gconv"
+	"github.com/gogf/gf/v2/util/gutil"
+	"github.com/pkg/errors"
+	"regexp"
+	"strings"
+	"time"
+	"workTasks/common/match"
+	"workTasks/urlStatic/clickIterSource"
+)
+
+type (
+	UserId   string
+	UUid     string
+	MatchObj struct {
+		urlMapping      map[string][]*matchItem
+		result          map[UUid]map[UserId]int64 //统计计算次数
+		uuidCodeMapping map[UUid]string
+		matchObj        *match.TrieNode
+	}
+	matchItem struct {
+		UUid UUid
+		Name string
+		Code string
+		Rule map[string]interface{}
+	}
+	dbStruct struct {
+		UserId string    `json:"userId"`
+		Code   string    `json:"code"`
+		Num    int64     `json:"num"`
+		Date   time.Time `json:"date"`
+	}
+)
+
+var re = regexp.MustCompile(`\[[^\]]*\]`)
+
+func NewMatchObj(ctx context.Context) *MatchObj {
+	var (
+		urlMapping      = map[string][]*matchItem{}
+		treeMatch       = &match.TrieNode{}
+		uuidCodeMapping = map[UUid]string{}
+		result          = map[UUid]map[UserId]int64{}
+	)
+	for _, mObj := range g.Cfg().MustGet(ctx, "match").Maps() {
+		var (
+			name = gconv.String(mObj["name"])
+			code = gconv.String(mObj["code"])
+			rule = gconv.Map(mObj["rule"])
+
+			urlArr       []string
+			newMatchItem []*matchItem
+		)
+		for _, s := range gconv.Strings(rule["url"]) {
+			urlArr = append(urlArr, s)
+		}
+
+		arr := re.FindAllStringSubmatch(code, -1)
+		if len(arr) > 0 {
+			for _, str := range arr {
+				var (
+					rep    = str[0]
+					key    = rep[1 : len(rep)-1]
+					repVal = gconv.Strings(rule[key])
+				)
+				for _, val := range repVal {
+					ruleItem := gconv.Map(gutil.Copy(rule))
+					ruleItem[key] = val
+					var (
+						uuid      = UUid(uuid2.New())
+						finalCode = strings.ReplaceAll(code, rep, val)
+					)
+
+					uuidCodeMapping[uuid] = finalCode
+					newMatchItem = append(newMatchItem, &matchItem{
+						UUid: uuid,
+						Name: name,
+						Code: strings.ReplaceAll(code, rep, val),
+						Rule: ruleItem,
+					})
+				}
+			}
+		} else {
+			uuid := UUid(uuid2.New())
+			uuidCodeMapping[uuid] = code
+			newMatchItem = append(newMatchItem, &matchItem{
+				UUid: uuid,
+				Name: name,
+				Code: code,
+				Rule: rule,
+			})
+		}
+
+		if len(urlArr) > 0 {
+			for _, uStr := range gconv.Strings(rule["url"]) {
+				if uStr != "" {
+					treeMatch.Insert(uStr)
+				}
+				urlMapping[uStr] = newMatchItem
+			}
+		} else {
+			urlMapping[""] = newMatchItem
+		}
+
+	}
+
+	for uid, _ := range uuidCodeMapping {
+		result[uid] = make(map[UserId]int64)
+	}
+	return &MatchObj{
+		matchObj:        treeMatch,
+		urlMapping:      urlMapping,
+		result:          result,
+		uuidCodeMapping: uuidCodeMapping,
+	}
+}
+
+func (o *MatchObj) Match(ctx context.Context, start, end time.Time) error {
+	rows, err := clickIterSource.Ch_analysis.Query(ctx, "SELECT date,url,user_id,mini_program_code,action_id,breaker_name,page_id FROM dwd_f_personnel_behavior WHERE user_id!='' AND date>=? and date<? ", start.Format(time.DateTime), end.Format(time.DateTime))
+	if err != nil {
+		return errors.Wrap(err, "加载日志数据异常")
+	}
+	defer rows.Close()
+	for rows.Next() {
+		var (
+			date                                                              time.Time
+			user_id, url, mini_program_code, action_id, breaker_name, page_id string
+		)
+		if err := rows.Scan(
+			&date, &url, &user_id, &mini_program_code, &action_id, &breaker_name, &page_id,
+		); err != nil {
+			g.Log().Errorf(ctx, "字段映射异常 %v", err)
+		}
+		for _, str := range append(o.matchObj.FindWords(url), "") {
+			for _, item := range o.urlMapping[str] {
+				var isValid = func() bool {
+					for key, val := range item.Rule {
+						if key == "url" {
+							continue
+						}
+						switch key {
+						case "mini_program_code":
+							if !hasPrefix(mini_program_code, val) {
+								return false
+							}
+						case "action_id":
+							if !compareValue(action_id, val) {
+								return false
+							}
+						case "breaker_name":
+							if !compareValue(breaker_name, val) {
+								return false
+							}
+						case "page_id":
+							if !compareValue(page_id, val) {
+								return false
+							}
+						}
+					}
+					return true
+				}()
+				if isValid {
+					if _, ok := o.result[item.UUid][UserId(user_id)]; ok {
+						o.result[item.UUid][UserId(user_id)]++
+					} else {
+						o.result[item.UUid][UserId(user_id)] = 1
+					}
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func compareValue(val string, setting interface{}) bool {
+	if v, ok := setting.(string); ok {
+		return v == val
+	} else if v, ok := setting.([]string); ok {
+		for _, s := range v {
+			if s == val {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func hasPrefix(val string, setting interface{}) bool {
+	if v, ok := setting.(string); ok {
+		return strings.HasPrefix(val, v)
+	} else if v, ok := setting.([]string); ok {
+		for _, s := range v {
+			return strings.HasPrefix(val, s)
+		}
+	}
+	return false
+}
+
+func (o *MatchObj) Save(ctx context.Context, data time.Time) error {
+	var (
+		r     []*dbStruct
+		total int
+	)
+	for uuid, mData := range o.result {
+		for userId, num := range mData {
+			r = append(r, &dbStruct{
+				UserId: string(userId),
+				Code:   o.uuidCodeMapping[uuid],
+				Num:    num,
+				Date:   data,
+			})
+		}
+		if len(r) >= 2000 {
+			var t = r
+			if _, err := g.DB().Save(ctx, "dwd_d_visit", t, 500); err != nil {
+				g.Log().Errorf(ctx, "save errr %v", err)
+			}
+			total += 500
+			r = []*dbStruct{}
+		}
+	}
+	if len(r) > 0 {
+		if _, err := g.DB().Save(ctx, "dwd_d_visit", r, 500); err != nil {
+			g.Log().Errorf(ctx, "save errr %v", err)
+		}
+		total += len(r)
+	}
+	if total > 0 {
+		g.Log().Infof(ctx, "任务[%s] 共%d条数据", data, total)
+	}
+	return nil
+}

+ 90 - 0
urlStatic/main.go

@@ -0,0 +1,90 @@
+package main
+
+import (
+	"context"
+	_ "github.com/gogf/gf/contrib/drivers/clickhouse/v2"
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/os/gcron"
+	"github.com/gogf/gf/v2/os/gctx"
+	"github.com/gogf/gf/v2/os/gfile"
+	"time"
+	"workTasks/urlStatic/job"
+)
+
+var (
+	runJob = func(ctx context.Context, st, et time.Time) {
+		var (
+			cJob = job.NewMatchObj(ctx)
+		)
+		if matchErr := cJob.Match(ctx, st, et); matchErr != nil {
+			g.Log().Errorf(ctx, "加载[%s-%s]数据异常 %e", st.Format(time.DateTime), et.Format(time.DateTime), matchErr)
+			return
+		}
+		if saveErr := cJob.Save(ctx, et); saveErr != nil {
+			g.Log().Errorf(ctx, "存储[%s-%s]数据异常 %e", st.Format(time.DateTime), et.Format(time.DateTime), saveErr)
+			return
+		}
+		g.Log().Infof(ctx, "任务[%s-%s]完成", st.Format(time.DateTime), et.Format(time.DateTime))
+	}
+	flagFilePath = "last.txt"
+	dataFormat   = "2006-01-02-15"
+)
+
+func main() {
+	mainCtx := context.Background()
+	//执行一次任务
+	if g.Cfg().MustGet(mainCtx, "runOnce.isRun", false).Bool() {
+		g.Log().Infof(mainCtx, "开始执行一次性任务")
+		var (
+			startDateStr = g.Cfg().MustGet(mainCtx, "runOnce.startDate").String()
+			endDateStr   = g.Cfg().MustGet(mainCtx, "runOnce.endDate").String()
+		)
+		st, st_err := time.ParseInLocation(time.DateTime, startDateStr, time.Local)
+		et, et_err := time.ParseInLocation(time.DateTime, endDateStr, time.Local)
+		if st_err != nil || et_err != nil {
+			g.Log().Panicf(mainCtx, "开始执行时间异常%v %v", st_err, et_err)
+		}
+
+		for et.After(st) {
+			var (
+				ctx = gctx.New()
+				ed  = st.Add(time.Hour)
+			)
+			runJob(ctx, st, ed)
+			st = ed
+		}
+		g.Log().Infof(mainCtx, "一次性任务执行完成")
+		return
+	}
+
+	//定时执行
+	_, cronErr := gcron.AddSingleton(mainCtx, g.Cfg().MustGet(mainCtx, "runCron", "# # * * * *").String(), func(ctx context.Context) {
+		g.Log().Infof(mainCtx, "开始执行定时任务")
+		var (
+			dataStr = gfile.GetContents(flagFilePath)
+			now     = time.Now()
+			timeEnd = time.Date(now.Year(), now.Month(), now.Day(), now.Hour(), 0, 0, 0, time.Local)
+		)
+		st, err := time.ParseInLocation(dataFormat, dataStr, time.Local)
+		if err != nil {
+			g.Log().Errorf(ctx, "加载日期异常 %v", err)
+			return
+		}
+		for timeEnd.After(st) {
+			var (
+				ctx = gctx.New()
+				ed  = st.Add(time.Hour)
+			)
+			runJob(ctx, st, ed)
+			st = ed
+		}
+		if err := gfile.PutContents("last.txt", timeEnd.Format(dataFormat)); err != nil {
+			g.Log().Errorf(ctx, "存储时间异常 %v", err)
+		}
+		g.Log().Infof(mainCtx, "定时任务执行完成")
+	})
+	if cronErr != nil {
+		g.Log().Panicf(mainCtx, "创建定时任务异常 %v", cronErr)
+	}
+	select {}
+}

+ 2 - 0
userSign/new.sql

@@ -31,6 +31,8 @@ INSERT INTO pub_tags.dwd_d_tag (id, code, name, bitobj, groupid, created_at, cre
 INSERT INTO pub_tags.dwd_d_tag (id, code, name, bitobj, groupid, created_at, created_by, bit_num) VALUES(2029, 'unBuyVip45Day', '45天加购未购超级订阅',bitmapBuild([toUInt64(0)]) , 2, '2025-03-25 00:00:00', 'wky', 2029);
 INSERT INTO pub_tags.dwd_d_tag (id, code, name, bitobj, groupid, created_at, created_by, bit_num) VALUES(2030, 'visitedVipBuyPage', '近45天浏览超级订阅购买页',bitmapBuild([toUInt64(0)]) , 2, '2025-03-25 00:00:00', 'wky', 2030);
 
+INSERT INTO pub_tags.dwd_d_tag (id, code, name, bitobj, groupid, created_at, created_by, bit_num) VALUES(2031, 'active_1_90', '除了昨天90天活跃用户',bitmapBuild([toUInt64(0)]) , 2, '2025-03-25 00:00:00', 'wky', 2031);
+INSERT INTO pub_tags.dwd_d_tag(id, code, name, bitobj, groupid, created_at, created_by, bit_num) VALUES(2032, 'newUser_90', '近90天注册用户', bitmapBuild([toUInt64(0)]), 2, '2024-12-16 08:00:00.000', 'wky', 2032);
 
 
 

+ 1 - 1
userSign/userAnalysis/analysisLogsAnalysis.go

@@ -60,7 +60,7 @@ func (ua *UserAnalysis) GetAnalysisLogRes(ctx context.Context, runTime time.Time
 // ActiveUserLoad 活跃用户
 func (ua *UserAnalysis) ActiveUserLoad(ctx context.Context, start time.Time, source string, entTime ...time.Time) (map[BaseUserId]bool, error) {
 	var (
-		execSql = "SELECT DISTINCT(baseUserId) as baseUserId FROM user_log_byHour WHERE create_time > ? "
+		execSql = "SELECT DISTINCT(baseUserId) as baseUserId FROM user_log_byHour WHERE create_time >= ? "
 		values  = []interface{}{start.Format(time.DateTime)}
 	)
 	if len(entTime) > 0 {

+ 16 - 3
userSign/userAnalysis/jobMgoUserRange.go

@@ -33,6 +33,11 @@ func (ua *UserAnalysis) GetMgoUserRes(ctx context.Context) ([]*AnalysisRes, erro
 		})
 		//g.Dump(fmt.Sprintf("近第%d天注册用户", i), ua.NewUser[i])
 	}
+	rData = append(rData, &AnalysisRes{
+		Name: "近第90天注册用户",
+		Code: "newUser_90",
+		Data: ua.NewUser[90],
+	})
 	return rData, nil
 }
 
@@ -42,9 +47,10 @@ func (ua *UserAnalysis) mgoUserRange(ctx context.Context) {
 	//个人身份
 	it := sess.DB("qfw").C("user").Find(nil).Select(g.Map{"base_user_id": 1, "s_phone": 1, "s_m_phone": 1, "s_email": 1, "l_registedate": 1, "i_vip_status": 1, "l_vip_endtime": 1, "i_member_status": 1, "s_m_openid": 1, "i_ispush": 1}).Iter()
 	var (
-		index        int64
-		now          = time.Now()
-		newUserLimit = now.AddDate(0, 0, -8).Unix()
+		index          int64
+		now            = time.Now()
+		newUserLimit   = now.AddDate(0, 0, -8).Unix()
+		newUser90Limit = now.AddDate(0, 0, -90).Unix()
 	)
 	for m := make(map[string]interface{}); it.Next(&m); {
 		index++
@@ -80,6 +86,13 @@ func (ua *UserAnalysis) mgoUserRange(ctx context.Context) {
 				}
 			}
 		}
+		if registedate > newUser90Limit {
+			if _, ok := ua.NewUser[90]; ok {
+				ua.NewUser[90][base_user_id] = true
+			} else {
+				ua.NewUser[90] = map[BaseUserId]bool{base_user_id: true}
+			}
+		}
 
 		for _, p := range g.Cfg().MustGet(ctx, "testUserPhone").Strings() {
 			if (p == s_phone || p == s_m_phone) && p != "" {