package statutils import ( "fmt" "math" ) type Value struct { Value int64 `json:"value" bson:"value"` // Value Count int64 `json:"count" bson:"count"` // Count } func (s *Value) CountRef() *int64 { return &s.Count } func (s *Value) ValueRef() *int64 { return &s.Value } func (s *Value) Add(value int64) { s.Count++ s.Value += value } func (s *Value) Sub(d *Value) *Value { ret := &Value{ Count: s.Count - d.Count, Value: s.Value - d.Value, } return ret } func (s *Value) Plus(d *Value) *Value { ret := &Value{ Count: s.Count + d.Count, Value: s.Value + d.Value, } return ret } func (s *Value) ACC(d *Value) { s.Count += d.Count s.Value += d.Value } func (s *Value) Percentage(den *Value) *Value { ret := &Value{ Count: percentageDiv(s.Count, den.Count), Value: percentageDiv(s.Value, den.Value), } return ret } func (s *Value) Permil(den *Value) *Value { ret := &Value{ Count: permilDiv(s.Count, den.Count), Value: permilDiv(s.Value, den.Value), } return ret } func (s *Value) Mul(v int64) *Value { ret := &Value{ Count: s.Count * v, Value: s.Value * v, } return ret } type Direct struct { Total Value `json:"total" bson:"total"` // Total Natural Value `json:"natural" bson:"natural"` // Natural } func (s *Direct) CountRef() *int64 { return &s.Total.Count } func (s *Direct) Count() int64 { return s.Total.Count } func (s *Direct) ValueRef() *int64 { return &s.Total.Value } func (s *Direct) Value() int64 { return s.Total.Value } func (s *Direct) Add(value int64, device *ClientInfo) { s.Total.Add(value) if device.Natural { s.Natural.Add(value) } } func (s *Direct) Sub(d *Direct) *Direct { ret := &Direct{ Total: *s.Total.Sub(&d.Total), Natural: *s.Natural.Sub(&d.Natural), } return ret } func (s *Direct) Plus(d *Direct) *Direct { ret := &Direct{ Total: *s.Total.Plus(&d.Total), Natural: *s.Natural.Plus(&d.Natural), } return ret } func (s *Direct) ACC(d *Direct) { s.Total.ACC(&d.Total) s.Natural.ACC(&d.Natural) } func (s *Direct) Percentage(den *Direct) *Direct { ret := &Direct{ Total: *s.Total.Percentage(&den.Total), Natural: *s.Total.Percentage(&den.Total), } return ret } func (s *Direct) Permil(den *Direct) *Direct { ret := &Direct{ Total: *s.Total.Permil(&den.Total), Natural: *s.Natural.Permil(&den.Natural), } return ret } func (s *Direct) Mul(v int64) *Direct { ret := &Direct{ Total: *s.Total.Mul(v), Natural: *s.Natural.Mul(v), } return ret } type From struct { Total Value `json:"total" bson:"total"` // Total // ---------------------分类1------------------------ Web Value `json:"web" bson:"web"` // 网站 Share Value `json:"share" bson:"share"` // 分享 Normal Value `json:"normal" bson:"normal"` // 自来 District Value `json:"district" bson:"district"` // 渠道 // ---------------------分类2------------------------ Direct Direct `json:"direct" bson:"direct"` // 直推 Fission Value `json:"fission" bson:"fission"` // 裂变 Deduction Value `json:"deduction" bson:"deduction"` // 扣量 } type StatH5 struct { // 总计 Total From `json:"total" bson:"total"` // Total = Pc + Ios + Android // 分类1 Pc From `json:"h5Pc" bson:"h5Pc"` // H5PC Ios From `json:"h5Ios" bson:"h5Ios"` // H5IOS Android From `json:"h5Android" bson:"h5Android"` // Android // 分类2 Guest From `json:"h5Guest" bson:"h5Guest"` // Guest Formal From `json:"h5Formal" bson:"h5Formal"` // H5正式用户 } func (s *StatH5) Sub(d *StatH5) *StatH5 { ret := &StatH5{ Total: *s.Total.Sub(&d.Total), Pc: *s.Pc.Sub(&d.Pc), Ios: *s.Ios.Sub(&d.Ios), Android: *s.Android.Sub(&d.Android), Guest: *s.Guest.Sub(&d.Guest), Formal: *s.Formal.Sub(&d.Formal), } return ret } func (s *StatH5) Plus(d *StatH5) *StatH5 { ret := &StatH5{ Total: *s.Total.Plus(&d.Total), Pc: *s.Pc.Plus(&d.Pc), Ios: *s.Ios.Plus(&d.Ios), Android: *s.Android.Plus(&d.Android), Guest: *s.Guest.Plus(&d.Guest), Formal: *s.Formal.Plus(&d.Formal), } return ret } func (s *StatH5) ACC(d *StatH5) { s.Total.ACC(&d.Total) s.Pc.ACC(&d.Pc) s.Ios.ACC(&d.Ios) s.Android.ACC(&d.Android) s.Guest.ACC(&d.Guest) s.Formal.ACC(&d.Formal) } func (s *StatH5) Mul(v int64) *StatH5 { ret := &StatH5{ Total: *s.Total.Mul(v), Pc: *s.Pc.Mul(v), Ios: *s.Ios.Mul(v), Android: *s.Android.Mul(v), Guest: *s.Guest.Mul(v), Formal: *s.Formal.Mul(v), } return ret } func (s *StatH5) Add(value int64, device *ClientInfo) { s.Total.Add(value, device) if device.H5Android { s.Android.Add(value, device) } else if device.H5Ios { s.Ios.Add(value, device) } else if device.H5Pc { s.Pc.Add(value, device) } if device.Formal { s.Formal.Add(value, device) } else { s.Guest.Add(value, device) } } func (s *StatH5) CountRef() *int64 { return s.Total.CountRef() } func (s *StatH5) Count() int64 { return s.Total.Count() } func (s *StatH5) ValueRef() *int64 { return s.Total.ValueRef() } func (s *StatH5) Value() int64 { return s.Total.Value() } func (s *StatH5) Percentage(den *StatH5) *StatH5 { ret := &StatH5{ Total: *s.Total.Percentage(&den.Total), Pc: *s.Pc.Percentage(&den.Pc), Ios: *s.Ios.Percentage(&den.Ios), Android: *s.Android.Percentage(&den.Android), Guest: *s.Guest.Percentage(&den.Guest), Formal: *s.Formal.Percentage(&den.Formal), } return ret } func (s *StatH5) Permil(den *StatH5) *StatH5 { ret := &StatH5{ Total: *s.Total.Permil(&den.Total), Pc: *s.Pc.Permil(&den.Pc), Ios: *s.Ios.Permil(&den.Ios), Android: *s.Android.Permil(&den.Android), Guest: *s.Guest.Permil(&den.Guest), Formal: *s.Formal.Permil(&den.Formal), } return ret } func (s *StatH5) Rows(div int64, compare *StatH5) [][]string { if div == 0 { div = 1 } ret := make([][]string, 0) if compare != nil { ret = append(ret, s.Total.Row("H5总计", div, &compare.Total)) ret = append(ret, s.Android.Row("H5安卓", div, &compare.Android)) ret = append(ret, s.Ios.Row("H5苹果", div, &compare.Ios)) ret = append(ret, s.Pc.Row("H5电脑", div, &compare.Pc)) ret = append(ret, s.Guest.Row("H5游客", div, &compare.Guest)) ret = append(ret, s.Formal.Row("H5注册", div, &compare.Formal)) } else { ret = append(ret, s.Total.Row("H5总计", div, nil)) ret = append(ret, s.Android.Row("H5安卓", div, nil)) ret = append(ret, s.Ios.Row("H5苹果", div, nil)) ret = append(ret, s.Pc.Row("H5电脑", div, nil)) ret = append(ret, s.Guest.Row("H5游客", div, nil)) ret = append(ret, s.Formal.Row("H5注册", div, nil)) } return ret } type Stat struct { Total From `json:"total" bson:"total"` // Total Android From `json:"android" bson:"android"` // Android Ios From `json:"ios" bson:"ios"` // IOS Light StatLight `json:"light" bson:"light"` // Light H5 StatH5 `json:"h5" bson:"h5"` // H5 } func (s *Stat) Mul(v int64) *Stat { ret := &Stat{ Total: *s.Total.Mul(v), Android: *s.Android.Mul(v), Ios: *s.Ios.Mul(v), Light: *s.Light.Mul(v), H5: *s.H5.Mul(v), } return ret } func (s *Stat) Percentage(den *Stat) *Stat { ret := &Stat{ Total: *s.Total.Percentage(&den.Total), Android: *s.Android.Percentage(&den.Android), Ios: *s.Ios.Percentage(&den.Ios), Light: *s.Light.Percentage(&den.Light), H5: *s.H5.Percentage(&den.H5), } return ret } func (s *Stat) Permil(den *Stat) *Stat { ret := &Stat{ Total: *s.Total.Permil(&den.Total), Android: *s.Android.Permil(&den.Android), Ios: *s.Ios.Permil(&den.Ios), Light: *s.Light.Permil(&den.Light), H5: *s.H5.Permil(&den.H5), } return ret } func (s *Stat) Sub(d *Stat) *Stat { ret := &Stat{ Total: *s.Total.Sub(&d.Total), Android: *s.Android.Sub(&d.Android), Ios: *s.Ios.Sub(&d.Ios), Light: *s.Light.Sub(&d.Light), H5: *s.H5.Sub(&d.H5), } return ret } func (s *Stat) Plus(d *Stat) *Stat { ret := &Stat{ Total: *s.Total.Plus(&d.Total), Android: *s.Android.Plus(&d.Android), Ios: *s.Ios.Plus(&d.Ios), Light: *s.Light.Plus(&d.Light), H5: *s.H5.Plus(&d.H5), } return ret } func (s *Stat) ACC(d *Stat) { s.Total.ACC(&d.Total) s.Android.ACC(&d.Android) s.Ios.ACC(&d.Ios) s.Light.ACC(&d.Light) s.H5.ACC(&d.H5) } func (s *Stat) Add(value int64, device *ClientInfo) { s.Total.Add(value, device) if device.Android { s.Android.Add(value, device) } else if device.Ios { s.Ios.Add(value, device) } else if device.Light { s.Light.Add(value, device) } else if device.H5 { s.H5.Add(value, device) } } func (s *Stat) CountRef() *int64 { return s.Total.CountRef() } func (s *Stat) Count() int64 { return s.Total.Count() } func (s *Stat) ValueRef() *int64 { return s.Total.ValueRef() } func (s *Stat) Value() int64 { return s.Total.Value() } func (s *Stat) Rows(div int64, compare *Stat) [][]string { if div == 0 { div = 1 } ret := make([][]string, 0) if compare != nil { ret = append(ret, s.Android.Row("安卓", div, &compare.Android)) ret = append(ret, s.Ios.Row("苹果", div, &compare.Ios)) ret = append(ret, s.Light.Rows(div, &compare.Light)...) ret = append(ret, s.H5.Rows(div, &compare.H5)...) ret = append(ret, s.Total.Row("合计", div, &compare.Total)) } else { ret = append(ret, s.Android.Row("安卓", div, nil)) ret = append(ret, s.Ios.Row("苹果", div, nil)) ret = append(ret, s.Light.Rows(div, nil)...) ret = append(ret, s.H5.Rows(div, nil)...) ret = append(ret, s.Total.Row("合计", div, nil)) } return ret } func (s *From) Sub(d *From) *From { ret := &From{ Total: *s.Total.Sub(&d.Total), Web: *s.Web.Sub(&d.Web), Normal: *s.Normal.Sub(&d.Normal), District: *s.District.Sub(&d.District), Share: *s.Share.Sub(&d.Share), Direct: *s.Direct.Sub(&d.Direct), Fission: *s.Fission.Sub(&d.Fission), Deduction: *s.Deduction.Sub(&d.Deduction), } return ret } func (s *From) Plus(d *From) *From { ret := &From{ Total: *s.Total.Plus(&d.Total), Web: *s.Web.Plus(&d.Web), Normal: *s.Normal.Plus(&d.Normal), District: *s.District.Plus(&d.District), Share: *s.Share.Plus(&d.Share), Direct: *s.Direct.Plus(&d.Direct), Fission: *s.Fission.Plus(&d.Fission), Deduction: *s.Deduction.Plus(&d.Deduction), } return ret } func (s *From) ACC(d *From) { s.Total.ACC(&d.Total) s.Web.ACC(&d.Web) s.Normal.ACC(&d.Normal) s.District.ACC(&d.District) s.Share.ACC(&d.Share) s.Direct.ACC(&d.Direct) s.Fission.ACC(&d.Fission) s.Deduction.ACC(&d.Deduction) } func (s *From) Mul(v int64) *From { ret := &From{ Total: *s.Total.Mul(v), Web: *s.Web.Mul(v), Normal: *s.Normal.Mul(v), District: *s.District.Mul(v), Share: *s.Share.Mul(v), Direct: *s.Direct.Mul(v), Fission: *s.Fission.Mul(v), Deduction: *s.Deduction.Mul(v), } return ret } func (s *From) Percentage(den *From) *From { ret := &From{ Total: *s.Total.Percentage(&den.Total), Web: *s.Web.Percentage(&den.Web), Normal: *s.Normal.Percentage(&den.Normal), District: *s.District.Percentage(&den.District), Share: *s.Share.Percentage(&den.Share), Direct: *s.Direct.Percentage(&den.Direct), Fission: *s.Fission.Percentage(&den.Fission), Deduction: *s.Deduction.Percentage(&den.Deduction), } return ret } func (s *From) Permil(den *From) *From { ret := &From{ Total: *s.Total.Permil(&den.Total), Web: *s.Web.Permil(&den.Web), Normal: *s.Normal.Permil(&den.Normal), District: *s.District.Permil(&den.District), Share: *s.Share.Permil(&den.Share), Direct: *s.Direct.Permil(&den.Direct), Fission: *s.Fission.Permil(&den.Fission), Deduction: *s.Deduction.Permil(&den.Deduction), } return ret } func (s *From) Row(title string, div int64, compare *From) []string { if div == 0 { div = 1 } if compare == nil { return []string{ title, fmt.Sprintf("%d", s.Total.Value/div), fmt.Sprintf("%d", s.Web.Value/div), fmt.Sprintf("%d", s.Normal.Value/div), fmt.Sprintf("%d", s.District.Value/div), fmt.Sprintf("%d", s.Share.Value/div), fmt.Sprintf("%d", s.Direct.Total.Value/div), fmt.Sprintf("%d", s.Fission.Value/div), fmt.Sprintf("%d", s.Deduction.Value/div), } } else { diff := s.Sub(compare) rate := diff.Permil(compare) return []string{ title, fmt.Sprintf("%d\n%s", s.Total.Value/div, DisplayPermilToPercentage(rate.Total.Value)), fmt.Sprintf("%d\n%s", s.Web.Value/div, DisplayPermilToPercentage(rate.Web.Value)), fmt.Sprintf("%d\n%s", s.Normal.Value/div, DisplayPermilToPercentage(rate.Normal.Value)), fmt.Sprintf("%d\n%s", s.District.Value/div, DisplayPermilToPercentage(rate.District.Value)), fmt.Sprintf("%d\n%s", s.Share.Value/div, DisplayPermilToPercentage(rate.Share.Value)), fmt.Sprintf("%d\n%s", s.Direct.Total.Value/div, DisplayPermilToPercentage(rate.Direct.Total.Value)), fmt.Sprintf("%d\n%s", s.Fission.Value/div, DisplayPermilToPercentage(rate.Fission.Value)), fmt.Sprintf("%d\n%s", s.Deduction.Value/div, DisplayPermilToPercentage(rate.Deduction.Value)), } } } func (s *From) Header(title string) []string { return []string{title, "总量", "官网", "自来", "渠道", "分享", "直推", "裂变", "扣量"} } func (s *From) Add(value int64, client *ClientInfo) { s.Total.Add(value) // 分类1统计 if client.Web { s.Web.Add(value) } else if client.Share { s.Share.Add(value) } else if client.Normal { s.Normal.Add(value) } else if client.District { s.District.Add(value) } // 分类2统计 if client.Deduction { s.Deduction.Add(value) } else { if client.Direct { s.Direct.Add(value, client) } else { s.Fission.Add(value) } } } func (s *From) CountRef() *int64 { return s.Total.CountRef() } func (s *From) Count() int64 { return s.Total.Count } func (s *From) ValueRef() *int64 { return s.Total.ValueRef() } func (s *From) Value() int64 { return s.Total.Value } func percentageDiv(num, den int64) int64 { if num == 0 { return 0 } else { if den == 0 { return math.MaxInt64 } else { return num * 100 / den } } } func permilDiv(num, den int64) int64 { if num == 0 { return 0 } else { if den == 0 { return math.MaxInt64 } else { return num * 1000 / den } } } func DisplayPermilToPercentage(v int64) string { var ret string var x float64 if v == math.MaxInt64 { s := "##.##" ret = fmt.Sprintf("%s", s) return ret } if v < -99999 { s := "--.--" ret = fmt.Sprintf("%s", s) return ret } if v > 99999 { s := "++.++" ret = fmt.Sprintf("%s", s) return ret } if v < 0 { x = float64(v) / -10.0 s := fmt.Sprintf("-%.1f%%", x) ret = fmt.Sprintf("%s", s) } else if v > 0 { x = float64(v) / 10.0 s := fmt.Sprintf("+%.1f%%", x) ret = fmt.Sprintf("%s", s) } else { s := "00.0%" ret = fmt.Sprintf("%s", s) } return ret } type StatLight struct { // 总计 Total From `json:"total" bson:"total"` // Total = Pc + Ios + Android // 分类 Guest From `json:"guest" bson:"guest"` // Guest Formal From `json:"formal" bson:"formal"` // 正式用户 } func (s *StatLight) Sub(d *StatLight) *StatLight { ret := &StatLight{ Total: *s.Total.Sub(&d.Total), Guest: *s.Guest.Sub(&d.Guest), Formal: *s.Formal.Sub(&d.Formal), } return ret } func (s *StatLight) Plus(d *StatLight) *StatLight { ret := &StatLight{ Total: *s.Total.Plus(&d.Total), Guest: *s.Guest.Plus(&d.Guest), Formal: *s.Formal.Plus(&d.Formal), } return ret } func (s *StatLight) ACC(d *StatLight) { s.Total.ACC(&d.Total) s.Guest.ACC(&d.Guest) s.Formal.ACC(&d.Formal) } func (s *StatLight) Mul(v int64) *StatLight { ret := &StatLight{ Total: *s.Total.Mul(v), Guest: *s.Guest.Mul(v), Formal: *s.Formal.Mul(v), } return ret } func (s *StatLight) Add(value int64, device *ClientInfo) { s.Total.Add(value, device) if device.Formal { s.Formal.Add(value, device) } else { s.Guest.Add(value, device) } } func (s *StatLight) CountRef() *int64 { return s.Total.CountRef() } func (s *StatLight) Count() int64 { return s.Total.Count() } func (s *StatLight) ValueRef() *int64 { return s.Total.ValueRef() } func (s *StatLight) Value() int64 { return s.Total.Value() } func (s *StatLight) Percentage(den *StatLight) *StatLight { ret := &StatLight{ Total: *s.Total.Percentage(&den.Total), Guest: *s.Guest.Percentage(&den.Guest), Formal: *s.Formal.Percentage(&den.Formal), } return ret } func (s *StatLight) Permil(den *StatLight) *StatLight { ret := &StatLight{ Total: *s.Total.Permil(&den.Total), Guest: *s.Guest.Permil(&den.Guest), Formal: *s.Formal.Permil(&den.Formal), } return ret } func (s *StatLight) Rows(div int64, compare *StatLight) [][]string { if div == 0 { div = 1 } ret := make([][]string, 0) if compare != nil { ret = append(ret, s.Total.Row("轻量合计", div, &compare.Total)) ret = append(ret, s.Guest.Row("轻量安装", div, &compare.Guest)) ret = append(ret, s.Formal.Row("轻量注册", div, &compare.Formal)) } else { ret = append(ret, s.Total.Row("轻量合计", div, nil)) ret = append(ret, s.Guest.Row("轻量安装", div, nil)) ret = append(ret, s.Formal.Row("轻量注册", div, nil)) } return ret }