Bläddra i källkod

定期更新代码

wucan 1 år sedan
förälder
incheckning
3a255d6945

+ 300 - 0
controller/v1/gameAction.go

@@ -0,0 +1,300 @@
+package v1
+
+import (
+	"designs/app/common/request"
+	"designs/app/common/response"
+	"designs/global"
+	"designs/model"
+	"designs/utils"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/pkg/errors"
+)
+
+func UserActionList(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Gid       string `form:"gid" binding:"required"`
+		Pf        string `form:"pf" binding:""`
+		StartTime string `form:"startTime" binding:"required"`
+		EndTime   string `form:"endTime" binding:"required"`
+	}{})
+
+	//查询出所有的事件
+	var actionList []model.GameAction
+	err := global.App.DB.Table("game_action").
+		Where("gid", form.Gid).
+		Select("id", "actionId", "actionName").
+		Order("id desc").Scan(&actionList).Error
+	if err != nil {
+		response.Fail(c, 1003, err.Error())
+		return
+	}
+	//查询出时间段内的活跃用户,登录次数
+	var userLogin []model.UserLogin
+	err = global.App.DB.Table("user_login").
+		Where("gid", form.Gid).
+		Where("loginTime", ">=", form.StartTime).
+		Where("loginTime", "<=", form.EndTime).
+		Select("userId", "loginTime").
+		Scan(&userLogin).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+	userLoginCount := len(userLogin)
+
+	//查询出时间段内事件触发数量,以及触发人的ID
+	var userAction []model.UserAction
+	err = global.App.DB.Table("user_action").
+		Where("gid", form.Gid).
+		Where("createdAt", ">=", form.StartTime).
+		Where("createdAt", "<=", form.EndTime).
+		Select("id", "actionId", "userId", "createdAt").
+		Scan(&userAction).Error
+	if err != nil {
+		response.Fail(c, 1002, err.Error())
+		return
+	}
+	//计算事件的触发总数和触发用户数
+	actionSumMap := make(map[string]int)
+	actionUserSumMap := make(map[string][]int)
+	for _, action := range userAction {
+		actionSumMap[action.ActionId]++
+		actionUserSumMap[action.ActionId] = append(actionUserSumMap[action.ActionId], action.UserId)
+	}
+
+	//根据事件触发和活跃用户数量进行比对得出其他数据
+	activeUser := make(map[int]bool)
+	var activeUserSlice []int
+	for _, users := range userLogin {
+		activeUser[users.UserId] = true
+	}
+	for k := range activeUser {
+		activeUserSlice = append(activeUserSlice, k)
+	}
+
+	type responses struct {
+		ActionId        string  `json:"actionId"`
+		ActionName      string  `json:"actionName"`
+		ActionCount     int     `json:"actionCount"`
+		ActionUserCount int     `json:"actionUserCount"`
+		ActiveUserRate  float64 `json:"activeUserRate"`
+		LoginActiveRate float64 `json:"loginActiveRate"`
+	}
+
+	var res []responses
+
+	for _, v := range actionList {
+		var ActiveUserRate float64
+		var LoginActiveRate float64
+		if userLoginCount > 0 {
+			ActiveUserRate = float64(actionSumMap[v.ActionId] / userLoginCount)
+			LoginActiveRate = float64(len(actionUserSumMap[v.ActionId]) / userLoginCount)
+		}
+
+		res = append(res, responses{
+			ActionId:        v.ActionId,
+			ActionName:      v.ActionName,
+			ActionCount:     actionSumMap[v.ActionId],
+			ActionUserCount: len(actionUserSumMap[v.ActionId]),
+			ActiveUserRate:  ActiveUserRate,
+			LoginActiveRate: LoginActiveRate,
+		})
+	}
+
+	response.Success(c, gin.H{
+		"data":  res,
+		"count": len(res),
+	})
+}
+
+func UserActionDetail(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Id        int    `form:"id" binding:"required"`
+		StartTime string `form:"startTime" binding:"required"`
+		EndTime   string `form:"endTime" binding:"required"`
+	}{})
+
+	//查询启动次数
+	var action model.GameAction
+	err := global.App.DB.Table("game_action").
+		Where("id", form.Id).Find(&action).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+	var userAction []struct {
+		UserId    int    `json:"userId" gorm:"not null;column:userId;"`
+		CreatedAt string `json:"createdAt" gorm:"not null;column:createdAt;"`
+	}
+	err = global.App.DB.Table("user_action").
+		Where("gid", action.Gid).
+		Where("actionId", action.ActionId).
+		Select("userId", "DATE_FORMAT(createdAt, '%Y-%m-%d') as createdAt").
+		Scan(&userAction).Error
+	if err != nil {
+		response.Fail(c, 1002, err.Error())
+		return
+	}
+
+	//查询出时间段内的活跃用户,登录次数
+	var userLogin []struct {
+		UserId    int    `json:"userId" gorm:"not null;column:userId;"`
+		CreatedAt string `json:"createdAt" gorm:"not null;column:createdAt;"`
+	}
+	err = global.App.DB.Table("user_login").
+		Where("gid", action.Gid).
+		Where("loginTime", ">=", form.StartTime).
+		Where("loginTime", "<=", form.EndTime).
+		Select("userId", "DATE_FORMAT(loginTime, '%Y-%m-%d') as createdAt").
+		Scan(&userLogin).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+	//userLoginCount := len(userLogin)
+
+	activeCount := make(map[string]int)
+	activeCountUser := make(map[string]map[int]bool)
+
+	//根据日期进行分组
+	for _, v := range userAction {
+		activeCount[v.CreatedAt]++
+
+		if activeCountUser[v.CreatedAt] == nil {
+			activeCountUser[v.CreatedAt] = make(map[int]bool)
+		}
+		activeCountUser[v.CreatedAt][v.UserId] = true
+
+	}
+
+	days := utils.GetTimeDayDateFormat(form.StartTime, form.EndTime)
+
+	type responses struct {
+		Date            string  `json:"date"`
+		ActiveCount     int     `json:"activeCount"`
+		ActiveUserCount int     `json:"activeUserCount"`
+		ActiveCountRate float64 `json:"activeCountRate"`
+		ActiveCountUser float64 `json:"activeCountUser"`
+	}
+	var res []responses
+	//输出格式
+	for _, v := range days {
+		res = append(res, responses{
+			Date:            v,
+			ActiveCount:     activeCount[v],
+			ActiveUserCount: len(activeCountUser[v]),
+			ActiveCountRate: float64(activeCount[v] / 1),
+			ActiveCountUser: float64(len(activeCountUser[v]) / 1),
+		})
+	}
+
+	response.Success(c, gin.H{
+		"data": res,
+	})
+}
+
+func UserActionDetailDistribution(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Id        int    `form:"id" binding:"required"`
+		StartTime string `form:"startTime" binding:"required"`
+		EndTime   string `form:"endTime" binding:"required"`
+		Type      int    `form:"type" binding:"required"`
+	}{})
+
+	var action model.GameAction
+	err := global.App.DB.Table("game_action").
+		Where("id", form.Id).Find(&action).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+
+	res := make(map[string]interface{})
+	if form.Type == 1 {
+		var userAction []struct {
+			UserId    int    `json:"userId" gorm:"not null;column:userId;"`
+			CreatedAt string `json:"createdAt" gorm:"not null;column:createdAt;"`
+		}
+		err = global.App.DB.Table("user_action").
+			Where("gid", action.Gid).
+			Where("actionId", action.ActionId).
+			Select("userId", "DATE_FORMAT(createdAt, '%Y-%m-%d') as createdAt").
+			Scan(&userAction).Error
+		if err != nil {
+			response.Fail(c, 1002, err.Error())
+			return
+		}
+
+		activeCount := make(map[string]int)
+		for _, v := range userAction {
+			activeCount[v.CreatedAt]++
+		}
+
+		days := utils.GetTimeDayDateFormat(form.StartTime, form.EndTime)
+		for _, v := range days {
+			res[v] = activeCount[v]
+		}
+		fmt.Print(len(userAction) / len(days))
+
+		response.Success(c, gin.H{"data": res, "avg": fmt.Sprintf("%.2f", float64(len(userAction))/float64(len(days)))})
+	} else if form.Type == 2 {
+		var userAction []struct {
+			UserId    int    `json:"userId" gorm:"not null;column:userId;"`
+			CreatedAt string `json:"createdAt" gorm:"not null;column:createdAt;"`
+		}
+		err = global.App.DB.Table("user_action").
+			Where("gid", action.Gid).
+			Where("actionId", action.ActionId).
+			Select("userId", "DATE_FORMAT(createdAt, '%Y-%m-%d') as createdAt").
+			Scan(&userAction).Error
+		if err != nil {
+			response.Fail(c, 1002, err.Error())
+			return
+		}
+
+		activeCount := make(map[string]map[int]bool)
+		for _, v := range userAction {
+			if activeCount[v.CreatedAt] == nil {
+				activeCount[v.CreatedAt] = make(map[int]bool)
+			}
+			activeCount[v.CreatedAt][v.UserId] = true
+		}
+
+		days := utils.GetTimeDayDateFormat(form.StartTime, form.EndTime)
+		for _, v := range days {
+			res[v] = len(activeCount[v])
+		}
+
+		response.Success(c, gin.H{"data": res, "avg": fmt.Sprintf("%.2f", float64(len(userAction))/float64(len(days)))})
+	} else if form.Type == 3 {
+		var userAction []struct {
+			UserId    int    `json:"userId" gorm:"not null;column:userId;"`
+			CreatedAt string `json:"createdAt" gorm:"not null;column:createdAt;"`
+		}
+		err = global.App.DB.Table("user_action").
+			Where("gid", action.Gid).
+			Where("actionId", action.ActionId).
+			Select("userId", "DATE_FORMAT(createdAt, '%Y-%m-%d') as createdAt").
+			Scan(&userAction).Error
+		if err != nil {
+			response.Fail(c, 1002, err.Error())
+			return
+		}
+
+		activeCount := make(map[string]map[int]bool)
+		for _, v := range userAction {
+			if activeCount[v.CreatedAt] == nil {
+				activeCount[v.CreatedAt] = make(map[int]bool)
+			}
+			activeCount[v.CreatedAt][v.UserId] = true
+		}
+		//查询活跃用户
+
+	} else if form.Type == 4 {
+
+	} else {
+		response.Fail(c, 1003, errors.New("type 错误"))
+		return
+	}
+}

+ 275 - 1
controller/v1/gameConfig.go

@@ -3,10 +3,15 @@ package v1
 import (
 	"context"
 	"designs/app/common/request"
+	"designs/app/common/response"
 	"designs/config"
 	"designs/global"
-	"designs/response"
+	"designs/model"
+	"designs/utils"
 	"github.com/gin-gonic/gin"
+	"github.com/go-playground/validator/v10"
+	"strconv"
+	"time"
 )
 
 /* 添加游戏配置 */
@@ -60,3 +65,272 @@ func GetGidConfig(c *gin.Context) {
 		"data": gameData,
 	})
 }
+
+type ActionOption struct {
+	OptionName   string `json:"optionName" form:"optionName" binding:"required"`
+	OptionId     string `json:"optionId" form:"optionId" binding:"required"`
+	OptionType   string `json:"optionType" form:"optionType" binding:"required"`
+	OptionStatus int    `json:"optionStatus" form:"optionStatus" binding:""`
+}
+
+// 设置游戏的打点
+func SetGameAction(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Gid        string         `form:"gid" json:"gid" binding:"required"`
+		ActionId   string         `form:"actionId" json:"actionId" binding:"required"`
+		ActionName string         `form:"actionName" json:"actionName" binding:"required"`
+		Remark     string         `form:"remark" json:"remark" binding:""`
+		Status     int            `form:"status" json:"status" binding:""`
+		Options    []ActionOption `form:"options" json:"options" binding:""`
+	}{})
+
+	//验证一下option 内的参数
+	validate := validator.New()
+	for _, v := range form.Options {
+		err := validate.Struct(&v)
+		if err != nil {
+			response.Fail(c, 1003, err.Error())
+			return
+		}
+	}
+
+	//存入数据库
+	now := time.Now()
+	err := global.App.DB.Transaction(func(tx *utils.WtDB) error {
+		gameAction := model.GameAction{
+			Gid:        form.Gid,
+			ActionId:   form.ActionId,
+			ActionName: form.ActionName,
+			Remark:     form.Remark,
+			Status:     form.Status,
+			CreatedAt:  model.XTime{Time: now},
+			UpdatedAt:  model.XTime{Time: now},
+		}
+		err := tx.Table("game_action").Save(&gameAction).Error
+		if err != nil {
+			return err
+		}
+
+		for _, option := range form.Options {
+			gameActionOption := model.GameActionOption{
+				OptionName: option.OptionName,
+				OptionId:   option.OptionId,
+				ActionId:   gameAction.ID,
+				OptionType: option.OptionType,
+				Status:     option.OptionStatus,
+				CreatedAt:  model.XTime{Time: now},
+				UpdatedAt:  model.XTime{Time: now},
+			}
+			err = tx.Table("game_action_option").Save(&gameActionOption).Error
+			if err != nil {
+				return err
+			}
+		}
+
+		return nil
+	})
+	if err != nil {
+		response.Fail(c, 1002, err.Error())
+		return
+	}
+
+	response.Success(c, gin.H{})
+}
+
+// 更新游戏的打点
+func UpdateGameAction(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Gid        string `form:"gid" json:"gid" binding:"required"`
+		ActionId   string `form:"actionId" json:"actionId" binding:"required"`
+		ActionName string `form:"actionName" json:"actionName" binding:"required"`
+		Remark     string `form:"remark" json:"remark" binding:""`
+		Status     int    `form:"status" json:"status" binding:""`
+	}{})
+
+	err := global.App.DB.Table("game_action").
+		Where("gid", form.Gid).
+		Where("actionId", form.ActionId).
+		Updates(map[string]interface{}{
+			"status":     form.Status,
+			"remark":     form.Remark,
+			"updatedAt":  time.Now(),
+			"actionName": form.ActionName,
+		}).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+
+	response.Success(c, gin.H{})
+}
+
+// 更新事件中的选项
+func UpdateGameActionOption(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Id         int    `form:"id" json:"id" binding:"required"`
+		OptionName string `json:"optionName" form:"optionName" binding:"required"`
+		OptionId   string `json:"optionId" form:"optionId" binding:"required"`
+		OptionType string `json:"optionType" form:"optionType" binding:"required"`
+		Status     int    `form:"status" json:"status" binding:""`
+	}{})
+
+	err := global.App.DB.Table("game_action_option").Where("id", form.Id).Updates(map[string]interface{}{
+		"optionName": form.OptionName,
+		"optionId":   form.OptionId,
+		"optionType": form.OptionType,
+		"status":     form.Status,
+		"updatedAt":  time.Now(),
+	}).Error
+	if err != nil {
+		response.Fail(c, 1003, err.Error())
+		return
+	}
+
+	response.Success(c, gin.H{})
+}
+
+// 新增事件中的选项
+func AddGameActionOption(c *gin.Context) {
+	form := request.Check(c, &struct {
+		ActionId   int    `form:"actionId" json:"actionId" binding:"required"`
+		OptionName string `json:"optionName" form:"optionName" binding:"required"`
+		OptionId   string `json:"optionId" form:"optionId" binding:"required"`
+		OptionType string `json:"optionType" form:"optionType" binding:"required"`
+		Status     int    `json:"status" form:"status" binding:""`
+	}{})
+
+	now := time.Now()
+	gameActionOption := model.GameActionOption{
+		OptionName: form.OptionName,
+		OptionId:   form.OptionId,
+		ActionId:   form.ActionId,
+		OptionType: form.OptionType,
+		Status:     form.Status,
+		CreatedAt:  model.XTime{Time: now},
+		UpdatedAt:  model.XTime{Time: now},
+	}
+	err := global.App.DB.Table("game_action_option").Save(&gameActionOption).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+
+	response.Success(c, gin.H{})
+}
+
+// 删除事件中的选项
+func DeleteGameActionOption(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Id int `form:"id" json:"id" binding:"required"`
+	}{})
+
+	var d interface{}
+	err := global.App.DB.Table("game_action_option").Where("id", form.Id).Delete(d).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+
+	response.Success(c, gin.H{})
+}
+
+// 列表 事件列表
+func GameActionList(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Offset int    `form:"offset" json:"offset" binding:""`
+		Limit  int    `form:"limit" json:"limit" binding:"required"`
+		Search string `form:"search" json:"search" binding:""`
+		Status string `form:"status" json:"status" binding:""`
+		Order  string `form:"order" json:"order" binding:""`
+		Gid    string `form:"gid" json:"gid" binding:"required"`
+	}{})
+
+	query := global.App.DB.Table("game_action").Where("gid", form.Gid)
+	if form.Search != "" {
+		query = query.WhereRaw(global.App.DB.Where("actionId", "like", "%"+form.Search+"%").Where("actionName", "like", "%"+form.Search+"%").SubQuery())
+	}
+	if form.Status != "" {
+		status, _ := strconv.Atoi(form.Status)
+		query = query.Where("status", status)
+	}
+	if form.Order != "" {
+		query = query.Order("id " + form.Order)
+	}
+	var count int64
+	err := query.Count(&count).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+
+	var actionList []model.GameAction
+	err = query.Offset(form.Offset).Limit(form.Limit).Scan(&actionList).Error
+	if err != nil {
+		response.Fail(c, 1002, err.Error())
+		return
+	}
+	response.Success(c, gin.H{
+		"data":  actionList,
+		"count": count,
+	})
+}
+
+// 事件详情
+func GameActionDetail(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Id int `form:"id" json:"id" binding:"required"`
+	}{})
+
+	var action model.GameAction
+	err := global.App.DB.Table("game_action").Where("id", form.Id).First(&action).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+	//var optionList []model.GameActionOption
+	//err = global.App.DB.Table("game_action_option").Where("actionId", action.ActionId).Scan(&optionList).Error
+
+	response.Success(c, gin.H{
+		"data": action,
+	})
+}
+
+// 事件 选项 列表
+func GameActionOptionList(c *gin.Context) {
+	form := request.Check(c, &struct {
+		Offset   int    `form:"offset" json:"offset" binding:""`
+		Limit    int    `form:"limit" json:"limit" binding:"required"`
+		Search   string `form:"search" json:"search" binding:""`
+		Status   string `form:"status" json:"status" binding:""`
+		Order    string `form:"order" json:"order" binding:""`
+		ActionId int    `form:"actionId" json:"actionId" binding:"required"`
+	}{})
+	query := global.App.DB.Table("game_action_option").Where("actionId", form.ActionId)
+	if form.Search != "" {
+		query = query.WhereRaw(global.App.DB.Where("optionId", "like", "%"+form.Search+"%").Where("optionName", "like", "%"+form.Search+"%").SubQuery())
+	}
+	if form.Status != "" {
+		status, _ := strconv.Atoi(form.Status)
+		query = query.Where("status", status)
+	}
+	if form.Order != "" {
+		query = query.Order("id " + form.Order)
+	}
+	var count int64
+	err := query.Count(&count).Error
+	if err != nil {
+		response.Fail(c, 1001, err.Error())
+		return
+	}
+	var optionList []model.GameActionOption
+	err = query.Offset(form.Offset).Limit(form.Limit).Scan(&optionList).Error
+	if err != nil {
+		response.Fail(c, 1002, err.Error())
+		return
+	}
+
+	response.Success(c, gin.H{
+		"data":  optionList,
+		"count": count,
+	})
+}

+ 2 - 1
controller/v1/userBehavior.go

@@ -450,9 +450,10 @@ func RemainDataBydDay(c *gin.Context) {
 		Pf        string `form:"pf" json:"pf" binding:"required"`
 		StartTime string `form:"startTime" json:"startTime" binding:"required"`
 		EndTime   string `form:"endTime" json:"endTime" binding:"required"`
+		Type      int    `form:"type" json:"type" binding:"required"`
 	}{})
 
-	data, err := service.RemainDataBydDay(form.Pf, form.Gid, form.StartTime, form.EndTime)
+	data, err := service.RemainDataBydDay(form.Type, form.Pf, form.Gid, form.StartTime, form.EndTime)
 	if err != nil {
 		response.Fail(c, 1001, err.Error())
 		return

+ 67 - 1
model/user.go

@@ -1,6 +1,10 @@
 package model
 
-import "time"
+import (
+	"database/sql/driver"
+	"fmt"
+	"time"
+)
 
 /* code 结构体 */
 type CodeData struct {
@@ -44,3 +48,65 @@ type UserOnline struct {
 	UserId  int       `json:"userId" gorm:"not null;column:userId;"`
 	LogTime time.Time `json:"logTime" gorm:"column:logTime;"`
 }
+
+// 1. 创建 time.Time 类型的副本 XTime;
+type XTime struct {
+	time.Time
+}
+
+// 2. 为 Xtime 重写 MarshaJSON 方法,在此方法中实现自定义格式的转换;
+func (t XTime) MarshalJSON() ([]byte, error) {
+	output := fmt.Sprintf("\"%s\"", t.Format("2006-01-02 15:04:05"))
+	return []byte(output), nil
+}
+
+// 3. 为 Xtime 实现 Value 方法,写入数据库时会调用该方法将自定义时间类型转换并写入数据库;
+func (t XTime) Value() (driver.Value, error) {
+	var zeroTime time.Time
+	if t.Time.UnixNano() == zeroTime.UnixNano() {
+		return nil, nil
+	}
+	return t.Time, nil
+}
+
+// 4. 为 Xtime 实现 Scan 方法,读取数据库时会调用该方法将时间数据转换成自定义时间类型;
+func (t *XTime) Scan(v interface{}) error {
+	value, ok := v.(time.Time)
+	if ok {
+		*t = XTime{Time: value}
+		return nil
+	}
+	return fmt.Errorf("can not convert %v to timestamp", v)
+}
+
+type GameAction struct {
+	ID         int    `json:"id" gorm:"not null;"`
+	Gid        string `json:"gid" gorm:"not null;"`
+	ActionId   string `json:"actionId" gorm:"not null;column:actionId;"`
+	ActionName string `json:"actionName" gorm:"not null;column:actionName;"`
+	Status     int    `json:"status" gorm:"not null;column:status;"`
+	Remark     string `json:"remark" gorm:"not null;column:remark;"`
+	CreatedAt  XTime  `json:"createdAt" gorm:"column:createdAt;type:date;"`
+	UpdatedAt  XTime  `json:"updatedAt" gorm:"column:updatedAt;type:date;"`
+}
+
+type GameActionOption struct {
+	ID         int    `json:"id" gorm:"not null;"`
+	ActionId   int    `json:"actionId" gorm:"not null;column:actionId;"`
+	OptionId   string `json:"optionId" gorm:"not null;column:optionId;"`
+	OptionName string `json:"optionName" gorm:"not null;column:optionName;"`
+	OptionType string `json:"optionType" gorm:"not null;column:optionType;"`
+	Status     int    `json:"status" gorm:"not null;column:status;"`
+	CreatedAt  XTime  `json:"createdAt" gorm:"column:createdAt;"`
+	UpdatedAt  XTime  `json:"updatedAt" gorm:"column:updatedAt;"`
+}
+
+type UserAction struct {
+	ID        int    `json:"id" gorm:"not null;"`
+	Pf        string `json:"pf" gorm:"not null;"`
+	Gid       string `json:"gid" gorm:"not null;"`
+	ActionId  string `json:"actionId" gorm:"not null;column:actionId;"`
+	UserId    int    `json:"userId" gorm:"not null;column:userId;"`
+	CreatedAt XTime  `json:"createdAt" gorm:"column:createdAt;type:date;"`
+	Data      string `json:"data" gorm:"not null;column:data;"`
+}

+ 15 - 0
route/api.go

@@ -45,5 +45,20 @@ func SetApiGroupRoutes(router *gin.RouterGroup) {
 		GroupV1.POST("/user/dataTradesDetail", v1.DataTradesDetail)
 		GroupV1.POST("/user/remainDataBydDay", v1.RemainDataBydDay)
 
+		//游戏自定义事件管理
+		GroupV1.POST("/user/setGameAction", v1.SetGameAction)
+		GroupV1.POST("/user/updateGameAction", v1.UpdateGameAction)
+		GroupV1.POST("/user/updateGameActionOption", v1.UpdateGameActionOption)
+		GroupV1.POST("/user/addGameActionOption", v1.AddGameActionOption)
+		GroupV1.POST("/user/deleteGameActionOption", v1.DeleteGameActionOption)
+		GroupV1.POST("/user/gameActionList", v1.GameActionList)
+		GroupV1.POST("/user/gameActionDetail", v1.GameActionDetail)
+		GroupV1.POST("/user/gameActionOptionList", v1.GameActionOptionList)
+
+		//游戏自定义事件统计
+		GroupV1.POST("/user/userActionDetail", v1.UserActionDetail)
+		GroupV1.POST("/user/userActionList", v1.UserActionList)
+		GroupV1.POST("/user/userActionDetailDistribution", v1.UserActionDetailDistribution)
+
 	}
 }

+ 8 - 3
service/UserOnlineService.go

@@ -113,6 +113,12 @@ func calculateUserOnlineTime(logData []logData) int64 {
 	var isStart bool
 	var onlineTimeTotal int64
 	for k, v := range logData {
+		logTime := v.LogTime.Unix()
+		//如果跟上一次的记录隔的时间超过6分钟,就认为是之前断线了,属于无效数据
+		if k > 0 && logData[k-1].LogTime.Unix()+60*6 < logTime {
+			isStart = false
+			continue
+		}
 
 		if v.Type == 1 && isStart == false {
 			isStart = true
@@ -121,8 +127,7 @@ func calculateUserOnlineTime(logData []logData) int64 {
 		}
 
 		if v.Type == 1 && isStart == true {
-
-			logTime := v.LogTime.Unix()
+			//logTime := v.LogTime.Unix()
 			onlineTimeTotal = onlineTimeTotal + (logTime - lastLog)
 			lastLog = logTime
 			//如果下一次的心跳,间隔时间大于6分钟了,就可以认为,这次就算是结束了
@@ -133,7 +138,7 @@ func calculateUserOnlineTime(logData []logData) int64 {
 		}
 
 		if v.Type == 2 && isStart == true {
-			logTime := v.LogTime.Unix()
+			//logTime := v.LogTime.Unix()
 			onlineTimeTotal = onlineTimeTotal + (logTime - lastLog)
 
 			isStart = false

+ 1 - 0
service/actionService.go

@@ -0,0 +1 @@
+package service

+ 56 - 23
service/remainData.go

@@ -4,45 +4,78 @@ import (
 	"designs/global"
 	"designs/model"
 	"designs/utils"
+	"github.com/pkg/errors"
+	"time"
 )
 
-func RemainDataBydDay(pf string, gid string, startTime string, endTime string) (map[string]map[string]interface{}, error) {
-	//先计算出这个时间内 ,每天的注册用户数量
-	var users []model.User
+func RemainDataBydDay(types int, pf string, gid string, startTime string, endTime string) (map[string]map[string]interface{}, error) {
+	var err error
 
-	err := global.App.DB.Table("user").
-		Where("pf", pf).Where("gid", gid).
-		Where("createdAt", ">=", startTime).
-		Where("createdAt", "<=", endTime).
-		Scan(&users).Error
-	if err != nil {
-		return nil, err
-	}
+	//对于传过来的endTime ,做一个处理
+	t, _ := time.Parse("2006-01-02", endTime)
+	endTimeData := t.AddDate(0, 0, 1).Format("2006-01-02")
+
+	UsersBydDay := utils.GetTimeDayDate(startTime, endTime)     //用户分别是在哪天注册的
+	UserLoginBydDay := utils.GetTimeDayDate(startTime, endTime) //用户分别是在哪天注册的
 	var UsersId []int
-	UsersBydDay := utils.GetTimeDayDate(startTime, endTime) //用户分别是在哪天注册的
-	UserLoginBydDay := UsersBydDay
-	for _, user := range users {
-		UsersId = append(UsersId, user.UserId)
-		UsersBydDay[user.CreatedAt.Format("2006-01-02")] = append(UsersBydDay[user.CreatedAt.Format("2006-01-02")], user.UserId)
+	userIdMap := make(map[int]bool)
+	if types == 1 {
+		//先计算出这个时间内 ,每天的注册用户数量
+		var users []model.User
+		err = global.App.DB.Table("user").
+			Where("pf", pf).Where("gid", gid).
+			Where("createdAt", ">=", startTime).
+			Where("createdAt", "<=", endTimeData).
+			Scan(&users).Error
+		if err != nil {
+			return nil, err
+		}
+
+		for _, user := range users {
+			userIdMap[user.UserId] = true
+			UsersBydDay[user.CreatedAt.Format("2006-01-02")] = append(UsersBydDay[user.CreatedAt.Format("2006-01-02")], user.UserId)
+
+		}
+		for user := range userIdMap {
+			UsersId = append(UsersId, user)
+		}
+		users = nil //用完后清空内存
+	} else if types == 2 {
+		var users []model.UserLogin
+		err = global.App.DB.Table("user_login").
+			Where("pf", pf).Where("gid", gid).
+			Where("loginTime", ">=", startTime).
+			Where("loginTime", "<=", endTimeData).
+			Group("userId").
+			Scan(&users).Error
+		if err != nil {
+			return nil, err
+		}
+		for _, user := range users {
+			UsersId = append(UsersId, user.UserId)
+			UsersBydDay[user.LoginTime.Format("2006-01-02")] = append(UsersBydDay[user.LoginTime.Format("2006-01-02")], user.UserId)
+		}
+
+	} else {
+		return nil, errors.New("type 参数错误")
 	}
-	users = nil //用完后清空内存
 
 	//把每天的注册用户进行集合,查出后面所有天数的活跃情况
-	var UserLogin []model.UserLogin
-	err = global.App.DB.Table("user_login").
+	var UserLogin []model.UserOnline
+	err = global.App.DB.Table("user_online").
 		Where("pf", pf).Where("gid", gid).
 		WhereIn("userId", UsersId).
-		Where("loginTime", ">=", startTime).
-		Select("loginTime", "userId").
+		Where("logTime", ">=", startTime).
+		Select("logTime", "userId").
+		Group("date,userId").
 		Scan(&UserLogin).Error
 	if err != nil {
 		return nil, err
 	}
-
 	//对这些数据进行整理
 	for _, v := range UserLogin {
 		//根据天进行分组,得出总共有多少
-		times := v.LoginTime.Format("2006-01-02")
+		times := v.LogTime.Format("2006-01-02")
 		UserLoginBydDay[times] = append(UserLoginBydDay[times], v.UserId)
 	}
 

+ 27 - 7
service/userBehavior.go

@@ -2,6 +2,7 @@ package service
 
 import (
 	"designs/global"
+	"designs/model"
 	"designs/utils"
 	"time"
 )
@@ -103,19 +104,22 @@ func GetActiveTimeDistribution(pf string, gid string) (map[string]int, map[strin
 	today := now.Format("2006-01-02")
 
 	hours := utils.GetDayHour(now)
-	var todayRegister []time.Time
+	var todayRegisterModel []model.UserLogin
 
 	//计算今日曲线
 	err := global.App.DB.Table("user_login").
 		Where("pf", pf).
 		Where("gid", gid).
 		Where("loginTime", ">", today).
-		Pluck("loginTime", &todayRegister).Error
+		Select("userId", "loginTime").
+		Scan(&todayRegisterModel).Error
 	if err != nil {
 		global.App.Log.Error(err.Error())
 		return nil, nil, 0, 0, 0, err
 	}
 
+	todayRegister := distinctUserHour(todayRegisterModel)
+
 	var todayCount int64
 	err = global.App.DB.Table("user_login").
 		Where("pf", pf).
@@ -129,7 +133,9 @@ func GetActiveTimeDistribution(pf string, gid string) (map[string]int, map[strin
 	todayTimeDistribution := getTimeDistribution(todayRegister, hours)
 
 	//计算昨日曲线
-	var yesterdayRegister []time.Time
+	//var yesterdayRegister []time.Time
+	var yesterdayRegisterModel []model.UserLogin
+
 	yesterdayHours := utils.GetDayHour(now.AddDate(0, 0, -1))
 	yesterday := now.AddDate(0, 0, -1).Format("2006-01-02")
 	yesterdayThisTime := now.AddDate(0, 0, -1).Format("2006-01-02 15:04:05")
@@ -138,7 +144,7 @@ func GetActiveTimeDistribution(pf string, gid string) (map[string]int, map[strin
 		Where("gid", gid).
 		Where("loginTime", ">", yesterday).
 		Where("loginTime", "<=", today).
-		Pluck("loginTime", &yesterdayRegister).Error
+		Pluck("loginTime", &yesterdayRegisterModel).Error
 	if err != nil {
 		global.App.Log.Error(err.Error())
 		return nil, nil, 0, 0, 0, err
@@ -166,7 +172,7 @@ func GetActiveTimeDistribution(pf string, gid string) (map[string]int, map[strin
 		global.App.Log.Error(err.Error())
 		return nil, nil, 0, 0, 0, err
 	}
-
+	yesterdayRegister := distinctUserHour(yesterdayRegisterModel)
 	yesterdayTimeDistribution := getTimeDistribution(yesterdayRegister, yesterdayHours)
 	todayTimeDistributionRes := make(map[string]int)
 	for k, v := range todayTimeDistribution {
@@ -312,8 +318,22 @@ func GetLoginDistribution(pf string, gid string, startTime string, endTime strin
 	return todayTimeDistribution, float32(len(activeDays) / len(days)), nil
 }
 
-func GetMouthTrade(pf string, gid string) {
-	//获取当前日期
+// 根据时段对信息进行去重(每个小时只有一条数据是有效的)
+func distinctUserHour(data []model.UserLogin) []time.Time {
+	var result []time.Time
+
+	check := make(map[string][]int)
+
+	for _, v := range data {
+		date := v.LoginTime.Format("15")
+		if !utils.InArray(v.UserId, check[date]) {
+			check[date] = append(check[date], v.UserId)
+
+			result = append(result, v.LoginTime)
+		}
+	}
+
+	return result
 }
 
 // 根据时间求出时段信息

+ 0 - 6
utils/array.go

@@ -55,13 +55,7 @@ func IntersectionRate(a, b []int) float64 {
 	// 计算交集大小
 	intersectionSize := len(intersection)
 
-	// 计算最大切片的大小
 	maxSize := len(a)
-	if len(b) > maxSize {
-		maxSize = len(b)
-	}
-
-	// 计算重合率
 	if maxSize == 0 {
 		return 0.0 // 避免除以零的情况
 	}