You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

105 lines
3.6 KiB

package analyze
import (
"fmt"
"sync/atomic"
"time"
"yunyan/utils"
)
// statLoc 统计日界时区,由模块 Init 经 setStatLoc 写入(默认 Asia/Shanghai)。
// 埋点(Report)与同步(sync)两侧共用,保证 0 点切日口径一致;未初始化时回退固定 UTC+8。
var statLoc atomic.Value // *time.Location
func setStatLoc(loc *time.Location) { statLoc.Store(loc) }
func statLocation() *time.Location {
if v := statLoc.Load(); v != nil {
return v.(*time.Location)
}
return time.FixedZone("UTC+8", 8*3600)
}
// dayNumber 把时间转成 YYYYMMDD 形式的统计日(按固定统计时区,跨部署口径一致)。
func dayNumber(t time.Time) uint32 {
return utils.StatDayNumber(t, statLocation())
}
// ============================ Redis 键 ============================
// keyPrefix 形如 {集群标签}:analyze:{应用ID}:{区域},由 setupKeys 写入;
// 多集群 / 多应用 / 多区域共用一套 Redis 时互不串台。
// 用 atomic 保证 setupKeys(启动期) 与 Report(运行期) 间无竞态。
var keyPrefix atomic.Value // string
func prefix() string {
if p := keyPrefix.Load(); p != nil {
return p.(string)
}
return ""
}
// indexKey 活跃统计分区的索引集合:成员为分区标识(见 partition)。
func indexKey() string { return prefix() + ":idx" }
// hashKey / hllKey 都由「分区标识」直接拼出,而不是各自按字段重新拼——
// 这样分区标识升级(加渠道商段)后,老格式成员算出的仍是老 key,
// 切换版本当天残留在 Redis 里的老分区数据照样能被读出、推送、清理,不丢当日统计。
// hashKey 某分区累加计数的哈希。
func hashKey(part string) string {
return prefix() + ":" + part
}
// hllKey 某分区某去重指标的 HyperLogLog。
func hllKey(part, metric string) string {
return prefix() + ":" + part + ":u:" + metric
}
// emptyChannelMark 分区标识里「无渠道归属」的占位符。渠道商短码字符集为
// A-Z2-9(见 comm.ShortCodeCharset),不含 '-',故不会与真实渠道商冲突。
const emptyChannelMark = "-"
// partition 索引集合的成员,标识一个 产品:渠道商:日 分区。
// 无渠道归属(历史设备、未挂设备的应用级指标)用 emptyChannelMark 占位,
// 避免出现连续冒号导致解析歧义。
func partition(productId uint32, channelId string, statDay uint32) string {
if channelId == "" {
channelId = emptyChannelMark
}
return fmt.Sprintf("%d:%s:%d", productId, channelId, statDay)
}
// 统计哈希的字段名,与 stats_global_day 表列名保持一致,便于追溯。
const (
fLoginCount = "login_count"
fNewUserCount = "new_user_count"
fActiveDeviceCount = "active_device_count"
fBindDeviceCount = "bind_device_count"
fQRActiveCount = "qr_active_count"
fOrderCreateCount = "order_create_count"
fOrderPaidCount = "order_paid_count"
fOrderFailedCount = "order_failed_count"
fOrderCreateAmount = "order_create_amount"
fOrderPaidAmount = "order_paid_amount"
fGrantVipDay = "grant_vip_day"
fGrantAiIntegral = "grant_ai_integral"
fGrantTradeSecond = "grant_trade_second"
fGrantMeetSecond = "grant_meet_second"
fAiChatCount = "ai_chat_count"
fAiUpToken = "ai_up_token"
fAiDownToken = "ai_down_token"
fTradeCount = "trade_count"
fTradeSecond = "trade_second"
fTradeWords = "trade_words"
fMeetCount = "meet_count"
fMeetSecond = "meet_second"
)
// 去重指标的 HLL 名(对应 stats_global_day 的 *_user_count 列)。
const (
uLogin = "login"
uActive = "active"
uPay = "pay"
)