package statistics import ( "context" "database/sql" "fmt" "time" ) type DepsClick struct { Conn *sql.DB } type StatisticClick struct { conn *sql.DB } func NewClickStatistic(deps DepsClick) *StatisticClick { return &StatisticClick{ conn: deps.Conn, } } type PipelineAndFormStatistic struct { PipelineStatistic PipeLineStatsResp ContactFormStatistic map[int64]int } type Statistic struct { Count int64 QuestionID int64 } type PipeLineStatsResp map[int64][]Statistic func (s *StatisticClick) GetPipelinesStatistics(ctx context.Context, quizID int64, from uint64, to uint64) (*PipelineAndFormStatistic, error) { pipelines := make(PipeLineStatsResp) query := ` select last_quesion, questions, count() from (select last_quesion, questions, target_quiz from (select last_quesion, questions, target_quiz from view_pipelines_signs join view_respondent_paths on long_sess = session array join questions) as pipelines join mv_answers on questions=questionid and quizid=target_quiz where target_quiz = ? AND event_time BETWEEN ? AND ? ) group by last_quesion, questions; ` rows, err := s.conn.QueryContext(ctx, query, quizID, from, to) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var lastQuestionID int64 var questionID int64 var count int64 if err := rows.Scan(&lastQuestionID, &questionID, &count); err != nil { return nil, err } stat := Statistic{ Count: count, QuestionID: questionID, } pipelines[lastQuestionID] = append(pipelines[lastQuestionID], stat) } if err := rows.Err(); err != nil { return nil, err } contactQuery := ` SELECT questionid, count() FROM mv_quiz_open WHERE quizid = ? AND event_time BETWEEN ? AND ? GROUP BY questionid; ` contactRows, err := s.conn.QueryContext(ctx, contactQuery, quizID, from, to) if err != nil { return nil, err } defer contactRows.Close() contactForms := make(map[int64]int) for contactRows.Next() { var questionID int64 var count int if err := contactRows.Scan(&questionID, &count); err != nil { return nil, err } contactForms[questionID] = count } if err := contactRows.Err(); err != nil { return nil, err } return &PipelineAndFormStatistic{ PipelineStatistic: pipelines, ContactFormStatistic: contactForms, }, nil } func (s *StatisticClick) CalculateCustomerLTV(ctx context.Context, req ChStatDeps) (int64, error) { timeFilter := "" if req.From != 0 { fromTime := time.Unix(req.From, 0).UTC().Format("2006-01-02 15:04:05") timeFilter += fmt.Sprintf(" AND create_time >= '%s'", fromTime) } if req.To != 0 { toTime := time.Unix(req.To, 0).UTC().Format("2006-01-02 15:04:05") timeFilter += fmt.Sprintf(" AND create_time <= '%s'", toTime) } query := fmt.Sprintf(` SELECT avg(lifeTimeInDays) AS averageLTV FROM ( SELECT (dateDiff('day', min(create_time), max(create_time))) AS lifeTimeInDays FROM ltv_view WHERE 1 = 1 %s GROUP BY ctxuserid) `, timeFilter) var result struct{ AverageLTV float64 } if err := s.conn.QueryRowContext(ctx, query).Scan(&result.AverageLTV); err != nil { return 0, err } return int64(result.AverageLTV), nil } type ChStatDeps struct { From int64 To int64 } type QuizLogoStats struct { ID string Regs int Money int64 Quizes []Quiz } type Quiz struct { QuizID string Regs uint64 Money int64 } func (s *StatisticClick) QuizLogoStat(ctx context.Context, req ChStatDeps) ([]QuizLogoStats, error) { timeFilter := "" if req.From != 0 && req.To != 0 { timeFilter = fmt.Sprintf("AND lv.create_time >= toDate(%d) AND lv.create_time <= toDate(%d)", req.From, req.To) } query := fmt.Sprintf(` SELECT sub.partner AS partner, count() AS regs, sum(sub.rowPrice) AS money, groupArray((sub.fromID, sub.regCount, sub.sumPrice)) AS quizes FROM ( SELECT uv.keyfrompartner AS partner, uv.keyfromid AS fromID, count() AS regCount, sum(lv.ctxprice) AS sumPrice, sum(lv.ctxprice) AS rowPrice FROM ltv_view lv JOIN user_regs_view uv ON lv.ctxuserid = uv.ctxuserid WHERE uv.keyfrompartner != '' AND uv.keyfromid != '' %s GROUP BY uv.keyfrompartner, uv.keyfromid ) AS sub GROUP BY sub.partner; `, timeFilter) var results []QuizLogoStats rows, err := s.conn.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() for rows.Next() { var stats QuizLogoStats var quizesData [][]interface{} err := rows.Scan(&stats.ID, &stats.Regs, &stats.Money, &quizesData) if err != nil { return nil, err } var quizes []Quiz for _, q := range quizesData { quiz := Quiz{ QuizID: q[0].(string), Regs: q[1].(uint64), Money: q[2].(int64), } quizes = append(quizes, quiz) } stats.Quizes = quizes results = append(results, stats) } if err := rows.Err(); err != nil { return nil, err } return results, nil } type PromoLtvResp struct { Regs int Money int } func (s *StatisticClick) PromocodeLTV(ctx context.Context, req ChStatDeps) (map[string]PromoLtvResp, error) { timeFilterFirstPromo := "" timeFilterFirstData := "" timeFilterMainQuery := "" if req.From != 0 && req.To != 0 { timeFilterFirstPromo = fmt.Sprintf("AND create_time BETWEEN toDateTime(%d) AND toDateTime(%d)", req.From, req.To) timeFilterFirstData = fmt.Sprintf("AND promo.create_time BETWEEN toDateTime(%d) AND toDateTime(%d)", req.From, req.To) timeFilterMainQuery = fmt.Sprintf("AND ltv.create_time BETWEEN toDateTime(%d) AND toDateTime(%d)", req.From, req.To) } query := fmt.Sprintf(` WITH first_promo AS ( SELECT ctxuserid, min(create_time) AS first_promo_time FROM promo_view WHERE 1=1 %s GROUP BY ctxuserid ), first_data AS ( SELECT promo.ctxid, promo.ctxuserid, promo.create_time AS first_time FROM promo_view AS promo JOIN first_promo AS first ON promo.ctxuserid = first.ctxuserid AND promo.create_time = first.first_promo_time WHERE 1=1 %s ) SELECT promo_data.ctxid AS promoID, count(DISTINCT promo_data.ctxuserid) AS regs, sum(if(ltv.create_time > promo_data.first_time %s, ltv.ctxprice, 0)) AS totalSpent FROM first_data AS promo_data LEFT JOIN ltv_view AS ltv ON promo_data.ctxuserid = ltv.ctxuserid GROUP BY promo_data.ctxid; `, timeFilterFirstPromo, timeFilterFirstData, timeFilterMainQuery) rows, err := s.conn.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() results := make(map[string]PromoLtvResp) for rows.Next() { var promoID string var resp PromoLtvResp if err := rows.Scan(&promoID, &resp.Regs, &resp.Money); err != nil { return nil, err } results[promoID] = resp } if err := rows.Err(); err != nil { return nil, err } return results, nil }