4 Коммиты 62d13efd58 ... b19c3d15b2

Автор SHA1 Сообщение Дата
  wangbin b19c3d15b2 Merge remote-tracking branch 'origin/master' лет назад: 2
  wangbin 613af337c4 设备异常逻辑更新 лет назад: 2
  wangbin 7fe48aaffd 设备异常逻辑更新 лет назад: 2
  wangbin cdc0a38c58 处理设备信息 лет назад: 2

+ 56 - 0
api/v1/log/loging.go

@@ -605,6 +605,62 @@ func (e *ApiLoging) GetDeviceInfoLog(c *gin.Context) {
 }
 
 // @Tags loging
+// @Summary 设备id异常数据
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce  application/json
+// @Param data body request.GetDeviceStatisticsRequest true "设备id异常数据"
+// @Success 200
+// @Router /loging/getDeviceIdErr [post]
+func (e *ApiLoging) GetDeviceIdErr(c *gin.Context) {
+	var paramsInfo request.GetDeviceIdErrRequest
+	_ = c.ShouldBindJSON(&paramsInfo)
+	list, total, err := ServiceStatisticsLog.GetDeviceIdErr(c, paramsInfo.ScriptDeviceErr, paramsInfo.PageInfo, paramsInfo.OrderKey, paramsInfo.Desc)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     paramsInfo.Page,
+			PageSize: paramsInfo.PageSize,
+		}, "获取成功", c)
+	}
+}
+
+// @Tags loging
+// @Summary 导出Excel
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce  application/octet-stream
+// @Param data body request.GetStatisticsComputerRequest true "导出Excel文件信息"
+// @Success 200
+// @Router /loging/deviceErrRateExcel [post]
+func (e *ApiLoging) DeviceErrRateExcel(c *gin.Context) {
+	var excelInfo request.ExcelDeviceErrRate
+	_ = c.ShouldBindJSON(&excelInfo)
+	paramsInfo := excelInfo.InfoList
+	paramsInfo.PageSize = 1000
+	paramsInfo.Page = 1
+	list, _, err := ServiceStatisticsLog.GetDeviceStatistics(c, paramsInfo.DeviceStatistics, paramsInfo.PageInfo, paramsInfo.OrderKey, paramsInfo.Desc)
+	if err != nil {
+		global.GVA_LOG.Error("获取电脑数据失败!", zap.Error(err))
+		response.FailWithMessage("获取电脑数据失败", c)
+		return
+	}
+	filePath := global.GVA_CONFIG.Excel.Dir + excelInfo.FileName
+	err = ServiceStatisticsLog.DeviceErrRateExcel(list, filePath)
+	if err != nil {
+		global.GVA_LOG.Error("转换Excel失败!", zap.Error(err))
+		response.FailWithMessage("转换Excel失败", c)
+		return
+	}
+	c.Writer.Header().Add("success", "true")
+	c.File(filePath)
+}
+
+// @Tags loging
 // @Summary 设备异常数据接口
 // @Security ApiKeyAuth
 // @accept application/json

+ 36 - 0
model/log/device_log.go

@@ -1,5 +1,7 @@
 package log
 
+import "time"
+
 type DeviceLog struct {
 	Id                 uint   `json:"id"`
 	SimulatorCode      string `json:"simulator_code"`
@@ -20,6 +22,9 @@ type DeviceLog struct {
 	DeviceHex          string `json:"device_hex"`
 	AccountHex         string `json:"account_hex"`
 	LogUuid            string `json:"log_uuid"` //日志UUID
+	ScriptDeviceId     string `json:"script_device_id"`
+	PcCode             string `json:"pc_code"`
+	SimulatorIpCity    string `json:"simulator_ip_city"`
 }
 
 func (DeviceLog) TableName() string {
@@ -48,3 +53,34 @@ type DeviceHex struct {
 	DeviceId           string `json:"device_id"`
 	GameId             int    `json:"game_id"`
 }
+
+type GameAccount struct {
+	Id             uint      `json:"id"`
+	GameId         int       `json:"game_id"`
+	Account        string    `json:"account"`
+	ScriptDeviceId string    `json:"script_device_id"`
+	CreateTime     time.Time `json:"create_time"` // 创建时间
+}
+
+func (GameAccount) TableName() string {
+	return "game_account"
+}
+
+type ScriptDeviceErr struct {
+	Id              uint      `json:"id"`
+	GameId          int       `json:"game_id"`
+	Account         string    `json:"account"`
+	FirstDeviceId   string    `json:"first_device_id"`
+	CurrentDeviceId string    `json:"current_device_id"`
+	CurrentGameId   int       `json:"current_game_id"`
+	CurrentAccount  string    `json:"current_account"`
+	CreateTime      time.Time `json:"create_time"` // 创建时间
+	CreateDate      string    `json:"create_date"`
+	Status          uint      `json:"status"` // 1是同一账号不同设备id,2新增出现同一设备id
+	PcCode          string    `json:"pc_code"`
+	Operator        string    `json:"operator"`
+}
+
+func (ScriptDeviceErr) TableName() string {
+	return "script_device_err"
+}

+ 34 - 10
model/log/device_statistics.go

@@ -1,18 +1,42 @@
 package log
 
 type DeviceStatistics struct {
-	Id                uint    `json:"id"`
-	GameId            int     `json:"game_id"`
-	DeviceErrNum      int     `json:"device_err_num"`
-	DeviceErrRate     float64 `json:"device_err_rate"`
-	AccountErrNum     int     `json:"account_err_num"`
-	AccountErrRate    float64 `json:"account_err_rate"`
-	SimulatorStartNum int     `json:"simulator_start_num"`
-	CreateDate        string  `json:"create_date"`
-	NewComplete       int     `json:"new_complete"`
-	RetainedComplete  int     `json:"retained_complete"`
+	Id                   uint    `json:"id"`
+	GameId               int     `json:"game_id"`
+	DeviceErrNum         int     `json:"device_err_num"`
+	DeviceErrRate        float64 `json:"device_err_rate"`
+	AccountErrNum        int     `json:"account_err_num"`
+	AccountErrRate       float64 `json:"account_err_rate"`
+	SimulatorStartNum    int     `json:"simulator_start_num"`
+	CreateDate           string  `json:"create_date"`
+	NewComplete          int     `json:"new_complete"`
+	RetainedComplete     int     `json:"retained_complete"`
+	NewDeviceIdErr       int     `json:"new_device_id_err"`
+	NewDeviceIdRate      float64 `json:"new_device_id_rate"`
+	RetainedDeviceIdErr  int     `json:"retained_device_id_err"`
+	RetainedDeviceIdRate float64 `json:"retained_device_id_rate"`
+	DefinedErr           int     `json:"defined_err"`
+	UndefinedErr         int     `json:"undefined_err"`
 }
 
 func (DeviceStatistics) TableName() string {
 	return "device_statistics"
 }
+
+type DeviceStatisticsReply struct {
+	Id                   uint    `json:"id"`
+	User                 string  `json:"user"`
+	GameId               int     `json:"game_id"`
+	DeviceErrNum         int     `json:"device_err_num"`
+	DeviceErrRate        float64 `json:"device_err_rate"`
+	AccountErrNum        int     `json:"account_err_num"`
+	AccountErrRate       float64 `json:"account_err_rate"`
+	SimulatorStartNum    int     `json:"simulator_start_num"`
+	CreateDate           string  `json:"create_date"`
+	NewComplete          int     `json:"new_complete"`
+	RetainedComplete     int     `json:"retained_complete"`
+	NewDeviceIdErr       int     `json:"new_device_id_err"`
+	NewDeviceIdRate      float64 `json:"new_device_id_rate"`
+	RetainedDeviceIdErr  int     `json:"retained_device_id_err"`
+	RetainedDeviceIdRate float64 `json:"retained_device_id_rate"`
+}

+ 25 - 0
model/log/err_log.go

@@ -0,0 +1,25 @@
+package log
+
+import (
+	"log-server/global"
+	"time"
+)
+
+type DeviceErrLog struct {
+	Id         uint      `json:"id"`
+	Content    string    `json:"content"`
+	GameId     int       `json:"game_id"`
+	PcCode     string    `json:"pc_code"`
+	CreateDate time.Time `json:"create_date"`
+	CreateTime time.Time `json:"create_time"`
+	Err        string    `json:"err"`
+	Status     uint      `json:"status"`
+}
+
+func (DeviceErrLog) TableName() string {
+	return "device_err_log"
+}
+
+func (m *DeviceErrLog) Create() error {
+	return global.GVA_DB.Create(m).Error
+}

+ 2 - 2
model/log/loging.go

@@ -26,7 +26,7 @@ type Loging struct {
 	Status       int    `json:"status"` // 1成功2失败
 	AccountType  int    `json:"account_type"`
 	Remarks      string `json:"remarks"`
-	TaskType     int    `json:"task_type"`
+	TaskType     int    `json:"task_type"` //新增0 活跃1
 	ScriptType   int    `json:"script_type"`
 	CreateDate   string `json:"create_date"`
 	CreateTime   string `json:"create_time"` // 创建时间
@@ -65,7 +65,7 @@ func (Loging) CreateLogingTable() (err error) {
 	sql += " `operator` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '脚本开发员',"
 	sql += " `create_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',"
 	sql += " `create_date` date DEFAULT NULL COMMENT '创建日期',"
-	sql += " `task_type` tinyint(1) DEFAULT '0' COMMENT '新增1 活跃2',"
+	sql += " `task_type` tinyint(1) DEFAULT '0' COMMENT '新增0 活跃1',"
 	sql += " `script_type` tinyint(1) DEFAULT '0',"
 	sql += " PRIMARY KEY (`id`) USING BTREE,"
 	sql += " KEY `log_uuid` (`log_uuid`) USING BTREE,"

+ 17 - 0
model/log/request/log_statistics.go

@@ -70,6 +70,16 @@ type ExcelInfo struct {
 	} `json:"infoList"`
 }
 
+type ExcelDeviceErrRate struct {
+	FileName string `json:"fileName"` // 文件名
+	InfoList struct {
+		log.DeviceStatistics
+		PageInfo
+		OrderKey string `json:"orderKey"` // 排序
+		Desc     bool   `json:"desc"`     // 排序方式:升序false(默认)|降序true
+	} `json:"infoList"`
+}
+
 //导出ip列表
 type ExcelIpInfo struct {
 	FileName string `json:"fileName"` // 文件名
@@ -113,3 +123,10 @@ type GetDeviceInfoLogRequest struct {
 	OrderKey string `json:"orderKey"` // 排序
 	Desc     bool   `json:"desc"`     // 排序方式:升序false(默认)|降序true
 }
+
+type GetDeviceIdErrRequest struct {
+	log.ScriptDeviceErr
+	PageInfo
+	OrderKey string `json:"orderKey"` // 排序
+	Desc     bool   `json:"desc"`     // 排序方式:升序false(默认)|降序true
+}

+ 3 - 0
model/log/request/loging.go

@@ -31,6 +31,9 @@ type AddLogRequest struct {
 	DeviceMac          string `json:"device_mac"`
 	DeviceNumber       string `json:"device_number"`
 	DeviceIp           string `json:"device_ip"`
+	ScriptDeviceId     string `json:"script_device_id"`
+	Err                string `json:"err"` // 0或不传表示没有异常,其他表示异常,脚本端自定义
+	SimulatorIpCity    string `json:"simulator_ip_city"`
 }
 
 type ReportPointsData struct {

+ 2 - 0
router/log/loging.go

@@ -31,5 +31,7 @@ func (e *LogingRouter) InitLogingRouter(Router *gin.RouterGroup) {
 		excelRouter.POST("getDeviceStatistics", logApi.GetDeviceStatistics)
 		excelRouter.POST("getDeviceInfoLog", logApi.GetDeviceInfoLog)
 		excelRouter.POST("getErrDeviceLog", logApi.GetErrDeviceLog)
+		excelRouter.POST("getDeviceIdErr", logApi.GetDeviceIdErr)
+		excelRouter.POST("deviceErrRateExcel", logApi.DeviceErrRateExcel)
 	}
 }

+ 4 - 7
service/fileManager/download_url.go

@@ -41,10 +41,7 @@ func (s *ServiceDownLoadUrl) UpdateGameVersion() {
 			return
 		}
 		dataJson, _ := simplejson.NewJson(result)
-		code, err := dataJson.Get("code").Int()
-		if err != nil {
-			return
-		}
+		code, _ := dataJson.Get("code").Int()
 		//global.GVA_LOG.Info(strconv.Itoa(code))
 		//msg, _ := dataJson.Get("msg").String()
 		//global.GVA_LOG.Info(msg)
@@ -81,7 +78,7 @@ func (s *ServiceDownLoadUrl) UpdateGameVersion() {
 				flagPackage = true
 				//global.GVA_LOG.Info("package有新版本")
 				if one.PackageUrl != "" {
-					keyWord += "在打包平台有新版本: " + one.PackageUrl + ";<font color=\"warning\">更新大概率为强制更新</font>;"
+					keyWord += "在打包平台有新版本,,游戏链接请找客服,<font color=\"warning\">更新大概率为强制更新</font>;"
 					//keyWord += "在打包平台有新版本: [点击下载](" + one.PackageUrl + ");"
 				}
 			}
@@ -94,10 +91,10 @@ func (s *ServiceDownLoadUrl) UpdateGameVersion() {
 					flagApp = true
 					//global.GVA_LOG.Info("app有新版本")
 					if one.AppUrl != "" {
-						keyWord += "在小绵羊APP有新版本: " + one.AppUrl + ";"
+						keyWord += "在小绵羊APP有新版本,游戏链接请找客服"
 						if one.AppFlag == 1 {
 							global.GVA_LOG.Info("游戏有强更")
-							keyWord += "<font color=\"warning\">更新可能为强制更新</font>;"
+							keyWord += ",<font color=\"warning\">更新可能为强制更新</font>;"
 						}
 					}
 					break

+ 13 - 13
service/log/log_ip.go

@@ -144,14 +144,14 @@ func (s *ServiceIpLog) GetAbnormalIpLogList(api log.AbnormalIpLogRequest, info r
 }
 
 //获取今日异常租机ip(ip播报使用)
-func (s *ServiceIpLog) GetTodayAbnormalIpLogList() (list map[string]string, err error){
+func (s *ServiceIpLog) GetTodayAbnormalIpLogList() (list map[string]string, err error) {
 	var total int64
 	var apiList []log.TodayAbnormalMachineIp
 	pcCodeList := make(map[string]string)
 	usePcCode := make(map[string]string)
 	date := time.Now().Format("2006-01-02")
 	//1个半小时前的时间
-	effectiveTime :=time.Now().Add(-time.Minute * 90).Format("2006-01-02 15:04:05")
+	effectiveTime := time.Now().Add(-time.Minute * 90).Format("2006-01-02 15:04:05")
 
 	db := global.GVA_DB.Table("abnormal_machine_ip as ami")
 	db = db.Select("ami.*, gt.task_name, gt.user")
@@ -175,7 +175,7 @@ func (s *ServiceIpLog) GetTodayAbnormalIpLogList() (list map[string]string, err
 
 	//遍历列表中的租机,查询数据库(租机编号,和本日时间),得到记录集合,通过ip作为map的key,判断是否需要播报
 	//遍历租机编号列表
-	for k,_ := range pcCodeList {
+	for k, _ := range pcCodeList {
 		var ipList []log.IpLogBroadcast
 		ipSet := make(map[string]int)
 		db1 := global.GVA_DB.Model(&log.IpLog{}).Select("ip_log.*, game_task.user")
@@ -197,7 +197,7 @@ func (s *ServiceIpLog) GetTodayAbnormalIpLogList() (list map[string]string, err
 			usePcCode[ipList[0].PcCode] = ipList[0].User
 		}
 	}
-	return usePcCode , err
+	return usePcCode, err
 }
 
 //根据gameId获取ip
@@ -361,7 +361,7 @@ func (s *ServiceIpLog) UpdateAbnormalMachineIp() (err error) {
 		return err
 	}
 	//将异常ip存储至abnormal_machine_ip数据库中
-	for i,_ := range abnormalIpList {
+	for i, _ := range abnormalIpList {
 		var entity log.AbnormalMachineIp
 		ip := abnormalIpList[i].Ip
 		gameId := abnormalIpList[i].GameId
@@ -370,11 +370,11 @@ func (s *ServiceIpLog) UpdateAbnormalMachineIp() (err error) {
 		count := abnormalIpList[i].Count
 
 		//根据count判断,如果count < 9, continue继续做下一条记录的处理
-		if count < 10 {
+		if count < 6 {
 			continue
 		}
 
-		if !errors.Is(global.GVA_DB.Model(&log.AbnormalMachineIp{}).Where("ip = ? and game_id = ? and pc_code = ? and create_date = ?",ip, gameId, pcCode, createDate).First(&entity).Error, gorm.ErrRecordNotFound) {
+		if !errors.Is(global.GVA_DB.Model(&log.AbnormalMachineIp{}).Where("ip = ? and game_id = ? and pc_code = ? and create_date = ?", ip, gameId, pcCode, createDate).First(&entity).Error, gorm.ErrRecordNotFound) {
 			//存在相同的ip、租机、gameid、创建日期记录,那么更新数量即可
 			err = global.GVA_DB.Model(&log.AbnormalMachineIp{}).Where("ip = ? and game_id = ? and pc_code = ? and create_date = ?", ip, gameId, pcCode, createDate).Update("count", count).Error
 			if err != nil {
@@ -400,7 +400,7 @@ func (s *ServiceIpLog) UpdateGameIpList() (err error) {
 	//筛选日期
 	db = db.Where("ip_log.create_date = ?", date)
 	//查找总ip上报次数
-	db = db.Joins("left join" + "(select count(*) as count, game_id, create_date from ip_log where create_date = ? group by game_id, create_date)" + " as late on late.game_id = ip_log.game_id and late.create_date = ip_log.create_date",  date)
+	db = db.Joins("left join"+"(select count(*) as count, game_id, create_date from ip_log where create_date = ? group by game_id, create_date)"+" as late on late.game_id = ip_log.game_id and late.create_date = ip_log.create_date", date)
 
 	db = db.Where("ip_log.status = 2")
 
@@ -425,14 +425,14 @@ func (s *ServiceIpLog) UpdateGameIpList() (err error) {
 		countDistinctIp := float64(apiList[i].CountDistinctIp)
 		successIp := float64(apiList[i].SuccessIp)
 		taskCount := float64(apiList[i].TaskCount)
-		apiList[i].IpRepetitionRate, _ =  strconv.ParseFloat(fmt.Sprintf("%.2f", (countDistinctIp/ successIp) *100), 64)
+		apiList[i].IpRepetitionRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (countDistinctIp/successIp)*100), 64)
 		if taskCount != 0 {
-			apiList[i].AverageIpRepetitionRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (countDistinctIp / taskCount) *100), 64)
+			apiList[i].AverageIpRepetitionRate, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", (countDistinctIp/taskCount)*100), 64)
 		}
 		//查找数据库中是否有这条记录(game_id, create_date)
-		if !errors.Is(global.GVA_DB.Model(&log.GameIpResponse{}).Where("game_id = ? and create_date = ?",apiList[i].GameId, apiList[i].CreateDate).First(&entity).Error, gorm.ErrRecordNotFound) {
+		if !errors.Is(global.GVA_DB.Model(&log.GameIpResponse{}).Where("game_id = ? and create_date = ?", apiList[i].GameId, apiList[i].CreateDate).First(&entity).Error, gorm.ErrRecordNotFound) {
 			//如果有,更新
-			err = global.GVA_DB.Model(&log.GameIpResponse{}).Where("game_id = ? and create_date = ?",apiList[i].GameId, apiList[i].CreateDate).Save(&apiList[i]).Error
+			err = global.GVA_DB.Model(&log.GameIpResponse{}).Where("game_id = ? and create_date = ?", apiList[i].GameId, apiList[i].CreateDate).Save(&apiList[i]).Error
 			if err != nil {
 				return errors.New("更新ip列表失败")
 			}
@@ -445,4 +445,4 @@ func (s *ServiceIpLog) UpdateGameIpList() (err error) {
 		}
 	}
 	return err
-}
+}

+ 3 - 0
service/log/log_list.go

@@ -147,6 +147,9 @@ func (s *ServiceLogList) CreateLog(c context.Context, request request.AddLogRequ
 	if request.Coding == 4301001 || request.Coding == 4501001 {
 		return
 	}
+	if request.Err != "" {
+		return
+	}
 	if err != nil {
 		body, _ := json.Marshal(request)
 		current := time.Now().Format("2006-01-02")

+ 174 - 26
service/log/log_statistics.go

@@ -1365,31 +1365,34 @@ func (s *ServiceStatisticsLog) ComputerSevenStatistics(ctx context.Context, api
 
 // 定时统计设备信息
 func (s *ServiceStatisticsLog) DeviceStatistics() {
-	var devices []log.DeviceLog
 	date := time.Now().Format("2006-01-02")
-	db := global.GVA_DB.Model(&log.DeviceLog{})
-	db.Where("create_date = ?", date)
-	db.Where("is_err = ?", 1)
-	db.Group("game_id")
-	db.Select("game_id")
-	db.Find(&devices)
-	if len(devices) == 0 {
+	db := global.GVA_DB.Model(&task.GameTask{})
+	var apiList []task.GameTask
+	db = db.Where("is_del = ?", -1)
+	db = db.Where("status = ?", 1)
+	db.Order("id desc").Find(&apiList)
+	if len(apiList) == 0 {
 		return
 	}
+	retainedDeviceErr := s.DeviceIdStatistics(date, 1)
+	newDeviceErr := s.DeviceIdStatistics(date, 2)
+	undefinedErr := s.DeviceErrLogStatistics(date, 1)
+	DefinedErr := s.DeviceErrLogStatistics(date, 2)
 	completeData, _ := s.GameTargetComplete.CompleteTaskData(date)
 	ctx := context.Background()
-	for _, game := range devices {
+	for _, game := range apiList {
 		deviceLog := new(log.DeviceStatistics)
-		deviceLog.GameId = game.GameId
-		gameDeviceAccountErrKey := fmt.Sprintf(loging2.GameDeviceAccountErrKey, date, game.GameId)
-		gameDeviceErrKey := fmt.Sprintf(loging2.GameDeviceErrKey, date, game.GameId)
+		gameId := game.TaskId
+		deviceLog.GameId = gameId
+		gameDeviceAccountErrKey := fmt.Sprintf(loging2.GameDeviceAccountErrKey, date, gameId)
+		gameDeviceErrKey := fmt.Sprintf(loging2.GameDeviceErrKey, date, gameId)
 		deviceLog.AccountErrNum, _ = s.GetCacheNum(ctx, gameDeviceAccountErrKey)
 		deviceLog.DeviceErrNum, _ = s.GetCacheNum(ctx, gameDeviceErrKey)
-		n, _ := s.LogicalLog.NodeLogGetNum(ctx, date, game.GameId, "4300000", loging2.OkStatus, 0)
-		r, _ := s.LogicalLog.NodeLogGetNum(ctx, date, game.GameId, "4300000", loging2.OkStatus, 1)
+		n, _ := s.LogicalLog.NodeLogGetNum(ctx, date, gameId, "4300000", loging2.OkStatus, 0)
+		r, _ := s.LogicalLog.NodeLogGetNum(ctx, date, gameId, "4300000", loging2.OkStatus, 1)
 		deviceLog.SimulatorStartNum = n + r
-		deviceLog.NewComplete = completeData[game.GameId].NewComplete
-		deviceLog.RetainedComplete = completeData[game.GameId].RetainedComplete
+		deviceLog.NewComplete = completeData[gameId].NewComplete
+		deviceLog.RetainedComplete = completeData[gameId].RetainedComplete
 		if deviceLog.DeviceErrNum != 0 {
 			deviceLog.DeviceErrRate = utils.Decimal(float64(deviceLog.DeviceErrNum) / float64(deviceLog.RetainedComplete) * 100)
 		}
@@ -1397,41 +1400,138 @@ func (s *ServiceStatisticsLog) DeviceStatistics() {
 			deviceLog.AccountErrRate = utils.Decimal(float64(deviceLog.AccountErrNum) / float64(deviceLog.NewComplete) * 100)
 		}
 		deviceLog.CreateDate = date
-		if !errors.Is(global.GVA_DB.Where("create_date = ?", date).Where("game_id = ?", game.GameId).First(&log.DeviceStatistics{}).Error, gorm.ErrRecordNotFound) {
-			global.GVA_DB.Where("create_date = ?", date).Where("game_id = ?", game.GameId).Omit("create_date,game_id").Updates(deviceLog)
+		if _, ok := retainedDeviceErr[gameId]; ok {
+			if retainedDeviceErr[gameId].Count != 0 {
+				deviceLog.RetainedDeviceIdErr = retainedDeviceErr[gameId].Count
+				deviceLog.RetainedDeviceIdRate = utils.Decimal(float64(deviceLog.RetainedDeviceIdErr) / float64(deviceLog.RetainedComplete) * 100)
+			}
+		}
+
+		if _, ok := newDeviceErr[gameId]; ok {
+			if newDeviceErr[gameId].Count != 0 {
+				deviceLog.NewDeviceIdErr = newDeviceErr[gameId].Count
+				deviceLog.NewDeviceIdRate = utils.Decimal(float64(deviceLog.NewDeviceIdErr) / float64(deviceLog.NewComplete) * 100)
+			}
+		}
+		if _, ok := undefinedErr[gameId]; ok {
+			if undefinedErr[gameId].Count != 0 {
+				deviceLog.UndefinedErr = undefinedErr[gameId].Count
+			}
+		}
+		if _, ok := DefinedErr[gameId]; ok {
+			if DefinedErr[gameId].Count != 0 {
+				deviceLog.DefinedErr = DefinedErr[gameId].Count
+			}
+		}
+
+		if !errors.Is(global.GVA_DB.Where("create_date = ?", date).Where("game_id = ?", gameId).First(&log.DeviceStatistics{}).Error, gorm.ErrRecordNotFound) {
+			global.GVA_DB.Where("create_date = ?", date).Where("game_id = ?", gameId).Omit("create_date,game_id").Updates(&deviceLog)
 			continue
 		}
-		global.GVA_DB.Save(deviceLog)
+		global.GVA_DB.Create(&deviceLog)
+	}
+}
+
+type DeviceErr struct {
+	GameId int `json:"game_id"`
+	Count  int `json:"count"`
+}
+
+func (s *ServiceStatisticsLog) DeviceIdStatistics(date string, status int) map[int]DeviceErr {
+	var devicesErr []DeviceErr
+	db := global.GVA_DB.Model(&log.ScriptDeviceErr{})
+	db.Where("create_date = ?", date)
+	db.Where("status = ?", status)
+	db.Group("current_game_id")
+	db.Select("current_game_id game_id, count(*) count")
+	db.Find(&devicesErr)
+	if len(devicesErr) == 0 {
+		return nil
+	}
+	devicesErrMaps := make(map[int]DeviceErr, len(devicesErr))
+	for _, game := range devicesErr {
+		devicesErrMaps[game.GameId] = game
+	}
+	return devicesErrMaps
+}
+
+func (s *ServiceStatisticsLog) DeviceErrLogStatistics(date string, status int) map[int]DeviceErr {
+	var devicesErr []DeviceErr
+	db := global.GVA_DB.Model(&log.DeviceErrLog{})
+	db.Where("create_date = ?", date)
+	db.Where("status = ?", status)
+	db.Group("game_id")
+	db.Select("game_id, count(*) count")
+	db.Find(&devicesErr)
+	if len(devicesErr) == 0 {
+		return nil
+	}
+	devicesErrMaps := make(map[int]DeviceErr, len(devicesErr))
+	for _, game := range devicesErr {
+		devicesErrMaps[game.GameId] = game
+	}
+	return devicesErrMaps
+}
+
+func (exa *ServiceStatisticsLog) DeviceErrRateExcel(statistics []*log.DeviceStatisticsReply, filePath string) error {
+	excel := excelize.NewFile()
+	excel.SetSheetRow("Sheet1", "A1", &[]string{"游戏ID", "负责人", "留存设备异常", "留存设备id异常", "留存完成目标", "留存异常率", "留存设备id异常率", "新增设备异常", "新增设备id异常", "新增完成目标", "新增异常率", "新增设备id异常率", "统计日期"})
+	for i, statisticsLog := range statistics {
+		axis := fmt.Sprintf("A%d", i+2)
+		excel.SetSheetRow("Sheet1", axis, &[]interface{}{
+			statisticsLog.GameId,
+			statisticsLog.User,
+			statisticsLog.DeviceErrNum,
+			statisticsLog.RetainedDeviceIdErr,
+			statisticsLog.RetainedComplete,
+			statisticsLog.DeviceErrRate,
+			statisticsLog.RetainedDeviceIdRate,
+			statisticsLog.AccountErrNum,
+			statisticsLog.NewDeviceIdErr,
+			statisticsLog.NewComplete,
+			statisticsLog.AccountErrRate,
+			statisticsLog.NewDeviceIdRate,
+			statisticsLog.CreateDate[:10],
+		})
 	}
+	err := excel.SaveAs(filePath)
+	return err
 }
 
-func (s *ServiceStatisticsLog) GetDeviceStatistics(ctx context.Context, api log.DeviceStatistics, info request.PageInfo, order string, desc bool) (statistics []*log.DeviceStatistics, total int64, err error) {
+func (s *ServiceStatisticsLog) GetDeviceStatistics(ctx context.Context, api log.DeviceStatistics, info request.PageInfo, order string, desc bool) (statistics []*log.DeviceStatisticsReply, total int64, err error) {
 	if api.CreateDate == "" {
 		api.CreateDate = time.Now().Format("2006-01-02")
 	}
-	db := global.GVA_DB.Model(&log.DeviceStatistics{})
-	db = db.Where("create_date = ?", api.CreateDate)
+	db := global.GVA_DB.Table("device_statistics ds")
+
+	db = db.Joins("left join game_task gt on gt.task_id = ds.game_id")
+	db = db.Where("ds.create_date = ?", api.CreateDate)
 	if api.GameId != 0 {
-		db = db.Where("game_id = ?", api.GameId)
+		db = db.Where("ds.game_id = ?", api.GameId)
 	}
 	err = db.Count(&total).Error
 	if err != nil {
 		return nil, 0, err
 	}
+	db = db.Select("ds.*,user")
 	limit := info.PageSize
 	offset := info.PageSize * (info.Page - 1)
 	db = db.Limit(limit).Offset(offset)
 	if order != "" {
 		var OrderStr string
-		orderMap := make(map[string]bool, 3)
+		orderMap := make(map[string]bool, 7)
 		orderMap["device_err_num"] = true
 		orderMap["game_id"] = true
 		orderMap["account_err_num"] = true
+		orderMap["device_err_rate"] = true
+		orderMap["account_err_rate"] = true
+		orderMap["retained_device_id_rate"] = true
+		orderMap["new_device_id_rate"] = true
 		if orderMap[order] {
 			if desc {
-				OrderStr = order + " desc"
+				OrderStr = "ds." + order + " desc"
 			} else {
-				OrderStr = order
+				OrderStr = "ds." + order
 			}
 		} else { // didn't matched any order key in `orderMap`
 			global.GVA_LOG.Error("获取失败!", zap.Error(err))
@@ -1494,6 +1594,54 @@ func (s *ServiceStatisticsLog) GetDeviceInfo(ctx context.Context, api log.Device
 	return
 }
 
+func (s *ServiceStatisticsLog) GetDeviceIdErr(ctx context.Context, api log.ScriptDeviceErr, info request.PageInfo, order string, desc bool) (deviceLogs []*log.ScriptDeviceErr, total int64, err error) {
+	if api.CreateDate == "" {
+		api.CreateDate = time.Now().Format("2006-01-02")
+	}
+	db := global.GVA_DB.Model(&log.ScriptDeviceErr{})
+	if api.GameId != 0 {
+		db = db.Where("current_game_id = ?", api.GameId)
+	}
+	if api.Operator != "" {
+		db = db.Where("operator = ?", api.Operator)
+	}
+	if api.Status != 0 {
+		db = db.Where("status = ?", api.Status)
+	}
+	db = db.Where("create_date = ?", api.CreateDate)
+	err = db.Count(&total).Error
+	if err != nil {
+		return nil, 0, err
+	}
+	limit := info.PageSize
+	offset := info.PageSize * (info.Page - 1)
+	db = db.Limit(limit).Offset(offset)
+	if order != "" {
+		var OrderStr string
+		// 设置有效排序key 防止sql注入
+		// 感谢 Tom4t0 提交漏洞信息
+		orderMap := make(map[string]bool, 3)
+		orderMap["game_id"] = true
+		if orderMap[order] {
+			if desc {
+				OrderStr = order + " desc"
+			} else {
+				OrderStr = order
+			}
+		} else { // didn't matched any order key in `orderMap`
+			global.GVA_LOG.Error("获取失败!", zap.Error(err))
+			return deviceLogs, total, err
+		}
+		err = db.Order(OrderStr).Find(&deviceLogs).Error
+	} else {
+		err = db.Order("id").Find(&deviceLogs).Error
+	}
+	for _, statistic := range deviceLogs {
+		statistic.CreateDate = api.CreateDate
+	}
+	return
+}
+
 func (s *ServiceStatisticsLog) GetDeviceContrastInfo(ctx context.Context, api log.DeviceLog) (deviceLogs []log.DeviceLog, err error) {
 	db := global.GVA_DB.Model(&log.DeviceLog{})
 	var device log.DeviceLog

+ 1 - 0
service/log/loging/game_start_log.go

@@ -27,6 +27,7 @@ func (s *GameStartLog) SuccessLog(ctx context.Context, request request.AddLogReq
 	} else {
 		_ = s.logical.SetUuidCodeCache(context.Background(), s.logical.CurrentDate(), s.logical.Request.LogUuid, s.logical.Request.Coding, s.logical.Request.GameId)
 	}
+	go s.logical.CheckDeviceId(s.logical.Request)
 	code := strconv.Itoa(request.Coding)
 	err = s.logical.PartTypeLogSetNum(ctx, s.logical.CurrentDate(), s.logical.Request.GameId, code, OkStatus, s.logical.Request.TaskType)
 	if err != nil {

+ 170 - 5
service/log/loging/logical_log.go

@@ -11,6 +11,7 @@ import (
 	"log-server/global"
 	"log-server/model/log"
 	"log-server/model/log/request"
+	"log-server/model/typeManage"
 	"log-server/service/cache"
 	"log-server/utils"
 	"strconv"
@@ -37,6 +38,7 @@ var (
 	PcReportingLog              = "%s:pc:Reporting:%s"
 	GameDeviceErrKey            = "%s:device:%d"
 	GameDeviceAccountErrKey     = "%s:deviceAccount:%d"
+	GameDeviceUuidKey           = "%s:GameDeviceUuid:%s"
 )
 
 type LogicalLog struct {
@@ -44,6 +46,7 @@ type LogicalLog struct {
 	Request    request.AddLogRequest
 	cache      cache.Cache
 	ScriptType int
+	Person     typeManage.ResponsiblePerson
 }
 
 func (s *LogicalLog) CurrentDate() (current string) {
@@ -892,16 +895,14 @@ func (s *LogicalLog) UpdateIpLogStatus(logUuid string, createDate string) {
 
 // 记录设备信息
 func (s *LogicalLog) AddDeviceLog(request request.AddLogRequest) {
-	if request.DeviceId == "" {
-		return
-	}
+
 	logSC := new(log.DeviceLog)
 	logSC.GameId = request.GameId
 	logSC.LogUuid = request.LogUuid
 	logSC.Account = request.Account
 	logSC.DeviceId = request.DeviceId
 	logSC.DeviceImei = request.DeviceImei
-	logSC.DeviceIp = request.DeviceIp
+	logSC.DeviceIp = request.SimulatorIp
 	logSC.DeviceMac = request.DeviceMac
 	logSC.DeviceManufacturer = request.DeviceManufacturer
 	logSC.DeviceModel = request.DeviceModel
@@ -913,6 +914,8 @@ func (s *LogicalLog) AddDeviceLog(request request.AddLogRequest) {
 	logSC.DeviceHex = s.DeviceHexLog(request)
 	logSC.AccountHex = s.AccountHexLog(request)
 	logSC.CreateDate = time.Now().Format("2006-01-02")
+	logSC.PcCode = request.PcCode
+	logSC.SimulatorIpCity = request.SimulatorIpCity
 	ctx := context.Background()
 	if request.TaskType == 1 {
 		var deviceLog = log.DeviceLog{}
@@ -977,8 +980,170 @@ func (s *LogicalLog) AccountHexLog(request request.AddLogRequest) string {
 
 // 修改设备信息状态
 func (s *LogicalLog) UpdateDeviceLogStatus(logUuid string, createDate string) {
-	err := global.GVA_DB.Table("ip_log").Where("create_date = ?", createDate).Where("log_uuid = ?", logUuid).Update("status", 2).Error
+	err := global.GVA_DB.Table("device_log").Where("create_date = ?", createDate).Where("log_uuid = ?", logUuid).Update("status", 1).Error
+	if err != nil {
+		global.GVA_LOG.Error("update UpdateDeviceLogStatus fail", zap.Error(err))
+	}
+}
+
+// 修改设备信息状态
+func (s *LogicalLog) UpdateDeviceLogScriptId(logUuid string, scriptDeviceId string, createDate string) {
+	err := global.GVA_DB.Table("device_log").Where("create_date = ?", createDate).Where("log_uuid = ?", logUuid).Update("script_device_id", scriptDeviceId).Update("status", 1).Error
 	if err != nil {
 		global.GVA_LOG.Error("create LogScanningCode fail", zap.Error(err))
 	}
 }
+
+func (s *LogicalLog) AddAccount(request request.AddLogRequest) {
+
+	gameAccount := new(log.GameAccount)
+	gameAccount.GameId = request.GameId
+	gameAccount.ScriptDeviceId = request.ScriptDeviceId
+	gameAccount.Account = request.Account
+	err := global.GVA_DB.Omit("create_time").Create(&gameAccount).Error
+	if err != nil {
+		global.GVA_LOG.Error("create AddAccount fail", zap.Error(err))
+	}
+}
+
+func (s *LogicalLog) AddScriptDeviceErr(gameId int, account, firstDeviceId, currentDeviceId string, firstAccount string, firstGameId int, status uint, pcCode string, operator string) {
+	scriptDeviceErr := new(log.ScriptDeviceErr)
+	scriptDeviceErr.GameId = firstGameId
+	scriptDeviceErr.Account = firstAccount
+	scriptDeviceErr.CurrentAccount = account
+	scriptDeviceErr.CurrentGameId = gameId
+	scriptDeviceErr.FirstDeviceId = firstDeviceId
+	scriptDeviceErr.CurrentDeviceId = currentDeviceId
+	scriptDeviceErr.CreateDate = time.Now().Format("2006-01-02")
+	scriptDeviceErr.Status = status
+	scriptDeviceErr.PcCode = pcCode
+	scriptDeviceErr.Operator = operator
+	err := global.GVA_DB.Omit("create_time").Create(&scriptDeviceErr).Error
+	if err != nil {
+		global.GVA_LOG.Error("create AddScriptDeviceErr fail", zap.Error(err))
+	}
+}
+
+func (s *LogicalLog) CheckDeviceId(request request.AddLogRequest) {
+	if request.ScriptDeviceId == "" {
+		// 通知异常逻辑
+		global.GVA_LOG.Error("get CheckDeviceId fail", zap.Error(errors.New("没有获取到设备id")))
+		return
+	}
+	s.UpdateDeviceLogScriptId(request.LogUuid, request.ScriptDeviceId, time.Now().Format("2006-01-02"))
+	global.GVA_LOG.Warn("进入 CheckDeviceId")
+	if request.TaskType == 0 {
+		b, deviceLog := s.CheckDeviceIdErr(request.ScriptDeviceId, request.LogUuid, request.GameId)
+		// 有异常处理
+		if b && len(deviceLog) >= 3 {
+			s.AddScriptDeviceErr(request.GameId, request.Account, request.ScriptDeviceId, request.ScriptDeviceId, deviceLog[0].Account, deviceLog[0].GameId, 2, request.PcCode, request.Operator)
+			//ct := fmt.Sprintf("<font color=\"warning\">%s:%d, 相同设备id</font>", request.PcCode, request.GameId)
+			//s.SendDeviceMsg(ct, request.Operator)
+		}
+		s.AddAccount(request)
+		return
+	}
+	var gameAccount log.GameAccount
+	result := global.GVA_DB.Where("game_id = ?", request.GameId).Where("account = ?", request.Account).First(&gameAccount)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			// 数据不存在,执行创建操作
+			s.AddAccount(request)
+			return
+		} else {
+			// 其他错误
+			global.GVA_LOG.Error("Select GameAccount fail", zap.Error(result.Error))
+			return
+		}
+	}
+	if gameAccount.ScriptDeviceId != request.ScriptDeviceId {
+		s.AddScriptDeviceErr(request.GameId, request.Account, gameAccount.ScriptDeviceId, request.ScriptDeviceId, gameAccount.Account, gameAccount.GameId, 1, request.PcCode, request.Operator)
+		//ct := fmt.Sprintf("<font color=\"warning\">%s:%d, 设备id出现不同</font>", request.PcCode, request.GameId)
+		//s.SendDeviceMsg(ct, request.Operator)
+		gameAccount.ScriptDeviceId = request.ScriptDeviceId
+		global.GVA_DB.Save(&gameAccount)
+	}
+}
+
+func (s *LogicalLog) SendDeviceMsg(content string, operator string) {
+	c := "# 设备异常"
+	c += "\n"
+	ct := content
+	ct = c + ct
+	fmt.Println(ct)
+	mpsPerson, _ := s.Person.GetUserInfoData()
+	if operator != "" {
+		s.SendContent(ct, mpsPerson[operator].Url)
+		url := global.GVA_CONFIG.SendUrl.ComputerSendUrl
+		s.SendContent(ct, url)
+		var sendTextData SendTextMsg
+		sendTextData.MsgType = "text"
+		sendTextData.Text.MentionedMobileList = []string{mpsPerson[operator].MobilePhoneNumber}
+		_, _ = s.SendMsgData(url, sendTextData)
+	}
+}
+
+type SendMsg struct {
+	MsgType  string `json:"msgtype"`
+	Markdown struct {
+		Content string `json:"content"`
+	} `json:"markdown"`
+}
+
+type SendTextMsg struct {
+	MsgType string `json:"msgtype"`
+	Text    struct {
+		MentionedMobileList []string `json:"mentioned_mobile_list"`
+	} `json:"text"`
+}
+
+func (s *LogicalLog) SendContent(content, url string) {
+	var sendMsg SendMsg
+	sendMsg.MsgType = "markdown"
+	sendMsg.Markdown.Content = content
+	_, _ = s.SendMsgData(url, sendMsg)
+}
+
+func (s *LogicalLog) SendMsgData(url string, params interface{}) (result []byte, err error) {
+	result, err = utils.HttpPost(url, params)
+	return
+}
+
+func (s *LogicalLog) CheckDeviceIdErr(scriptDeviceId string, uuid string, gameId int) (bool, []log.GameAccount) {
+	var deviceLog []log.GameAccount
+	// result := global.GVA_DB.Where("create_date <= ?", time.Now().Format("2006-01-02")).Where("create_date >= ?", time.Now().Add(-time.Hour*24).Format("2006-01-02")).Where("game_id = ?", gameId).Where("script_device_id = ?", scriptDeviceId).Find(&deviceLog)
+	result := global.GVA_DB.Where("game_id = ?", gameId).Where("script_device_id = ?", scriptDeviceId).Find(&deviceLog)
+	if result.Error != nil {
+		if result.Error == gorm.ErrRecordNotFound {
+			return false, deviceLog
+		}
+	}
+	return true, deviceLog
+}
+
+func (s *LogicalLog) AddDeviceErrLog(request request.AddLogRequest, st uint) {
+	if request.Err == "" {
+		return
+	}
+	ctx := context.Background()
+	key := fmt.Sprintf(GameDeviceUuidKey, time.Now().Format("2006-01-02"), request.LogUuid)
+	status, _ := s.ExistsKey(ctx, key)
+	if status {
+		return
+	}
+	errLog := new(log.DeviceErrLog)
+	errLog.GameId = request.GameId
+	errLog.CreateDate = time.Now()
+	errLog.CreateTime = time.Now()
+	errLog.Err = request.Err
+	errLog.PcCode = request.PcCode
+	content, _ := json.Marshal(request)
+	errLog.Content = string(content)
+	errLog.Status = st
+	err := errLog.Create()
+	if err != nil {
+		global.GVA_LOG.Error("create AddAccount fail", zap.Error(err))
+		return
+	}
+	global.GVA_REDIS.Set(ctx, key, 1, time.Hour)
+}

+ 6 - 0
service/log/loging/simulator_start_log.go

@@ -15,7 +15,13 @@ func (s *SimulatorStartLog) SuccessLog(ctx context.Context, request request.AddL
 	s.logical.Request = request
 	s.logical.ScriptType = request.ScriptType
 	err = s.logical.DataAdd()
+	s.logical.AddDeviceErrLog(s.logical.Request, 2)
 	if err != nil {
+		if s.logical.Request.SimulatorIp == "" {
+			return
+		}
+		s.logical.Request.Err = s.logical.Request.SimulatorIp[:3]
+		s.logical.AddDeviceErrLog(s.logical.Request, 1)
 		return
 	}
 	code := strconv.Itoa(request.Coding)