package controllers import ( "basic.com/valib/bhomeclient.git" "basic.com/valib/bhomedbapi.git" "basic.com/valib/logger.git" "encoding/json" "fmt" "io" "net/http" "os" "path/filepath" "strconv" "strings" "time" "vamicro/config" "vamicro/search-service/esutil" "vamicro/search-service/models" ) type EsSearchController struct{} // @Security ApiKeyAuth // @Summary 检索 // @Description 信息检索和比对检索 // @Accept json // @Produce json // @Tags es // @Param obj body models.EsSearch true "底库数据" // @Success 200 {string} json "{"code":200, msg:"目录结构数据", success:true}" // @Failure 500 {string} json "{"code":500, msg:"返回错误信息", success:false}" // @Router /data/api-v/es/esSearch [POST] func (sc *EsSearchController) PostEsSearch(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply { var arg models.EsSearch err := c.BindJSON(&arg) if err != nil || arg.Page <= 0 && arg.Size <= 0 { return &bhomeclient.Reply{Success: false, Msg: "参数有误"} } data := findEsData(&arg) return &bhomeclient.Reply{Success: true, Data: data} } func (sc *EsSearchController) PostEsDelete(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply { type DeleteReq struct { Id string `json:"id"` //数据id User string `json:"user"` Password string `json:"password"` } var reqBody DeleteReq if e := c.BindJSON(&reqBody); e != nil { return &bhomeclient.Reply{Success: false, Msg: "参数有误:" + e.Error()} } uApi := bhomedbapi.UserApi{} ok, _ := uApi.Login(reqBody.User, reqBody.Password) if !ok { return &bhomeclient.Reply{Success: false, Msg: "密码不正确"} } var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" || localConf.AlarmPort == 0 { logger.Debug("localConf err") return &bhomeclient.Reply{Success: false, Msg: "es未配置"} } index := config.EsInfo.EsIndex.AiOcean.IndexName url := "http://" + localConf.AlarmIp + ":" + strconv.Itoa(int(localConf.AlarmPort)) + "/" + index + "/_delete_by_query?refresh=true" var reqJson = ` { "query": { "bool": { "filter": [ { "term": { "id": "` + reqBody.Id + `" } } ] } } } ` buf, re := esutil.EsReq("POST", url, []byte(reqJson)) if re != nil { return &bhomeclient.Reply{Success: false, Msg: re.Error()} } var info interface{} json.Unmarshal(buf, &info) out, ok := info.(map[string]interface{}) if !ok { return &bhomeclient.Reply{Success: false, Msg: "删除失败"} } middle, ok := out["deleted"].(float64) batches, ok1 := out["batches"].(float64) if !ok || !ok1 { return &bhomeclient.Reply{Success: false, Msg: "first updated change error"} } if batches == 0 { return &bhomeclient.Reply{Success: false, Msg: "目标数据不存在"} } logger.Debug("删除es数据ret:", string(buf), " middle:", middle, " batches:", batches, " reqBody:", reqBody) time.Sleep(1 * time.Second) return &bhomeclient.Reply{Success: true, Msg: "删除成功"} } type GetVideoUrlReq struct { Id string `json:"id"` } func (sc *EsSearchController) GetVideoUrl(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply { var arg GetVideoUrlReq err := c.BindJSON(&arg) if err != nil || arg.Id == "" { return &bhomeclient.Reply{Success: false, Msg: "参数有误"} } var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" || localConf.AlarmPort == 0 { logger.Debug("localConf err") return &bhomeclient.Reply{Success: false, Msg: "localConf err"} } url := "http://" + localConf.AlarmIp + ":" + strconv.Itoa(int(localConf.AlarmPort)) + "/" + config.EsInfo.EsIndex.AiOcean.IndexName + "/_search" var prama = ` { "query": { "bool": { "filter": [ { "term": { "id": "` + arg.Id + `" } } ] } }, "_source": [ "videoUrl" ] } ` buf, err := esutil.EsReq("POST", url, []byte(prama)) if err != nil { return &bhomeclient.Reply{Success: false, Msg: "es request err"} } sources, err := esutil.Sourcelist(buf) if err != nil { return &bhomeclient.Reply{Success: false, Msg: "es soucelist err"} } var videoUrl string if len(sources) > 0 { videoUrl = sources[0]["videoUrl"].(string) } return &bhomeclient.Reply{Success: true, Data: videoUrl} } // 获取监控等级 func getAlarmLevel(alarmlevel []int32) []string { d := bhomedbapi.DicApi{} res, data := d.FindByType("ALARMLEVEL") if data == nil { logger.Debug("data is nil") } logger.Debug(res) // logger.Debug(data) alarmLevel := make(map[int32]string) tmp := data.(map[string]interface{}) for _, value := range tmp["ALARMLEVEL"].([]interface{}) { // logger.Debug(value.(map[string]interface{})["value"]," ",value.(map[string]interface{})["name"]) val := value.(map[string]interface{})["value"].(string) name := value.(map[string]interface{})["name"].(string) vl, err := strconv.Atoi(val) if err != nil { logger.Debug(err) } alarmLevel[int32(vl)] = name } alarmLevelRes := make([]string, len(alarmlevel)) for i, v := range alarmlevel { if v < 0 { alarmLevelRes[i] = "撤防" } else { alarmLevelRes[i] = alarmLevel[v] } } return alarmLevelRes } func findEsData(searchBody *models.EsSearch) map[string]interface{} { webPage := searchBody.Page webSize := searchBody.Size from := strconv.Itoa((webPage - 1) * webSize) //esFrom := strconv.Itoa(from) //esSize := strconv.Itoa(webSize) size := strconv.Itoa(webSize) //请求索引 index := config.EsInfo.EsIndex.AiOcean.IndexName queryStr := "" queryBody := searchBody.InputValue //检索框 if queryBody != "" { queryStr = "\"must\":[{\"multi_match\":{\"query\":\"" + queryBody + "\",\"fields\":[\"cameraAddr^1.5\",\"taskName^1.5\",\"sdkName^1.5\",\"showLabels^3.0\",\"baseInfo.tableName^1.5\",\"baseInfo.targetName^1.5\",\"baseInfo.labels^1.5\",\"alarmRules.alarmLevel^1.5\",\"linkTag^1.5\",\"linkTagInfo.cameraAddr\",\"linkTagInfo.taskName^1.5\",\"linkTagInfo.sdkName^1.5\",\"linkTagInfo.showLabels^1.5\",\"linkTagInfo.baseInfo.tableName^1.5\",\"linkTagInfo.baseInfo.targetName^1.5\",\"linkTagInfo.baseInfo.labels^1.5\",\"linkTagInfo.alarmRules.alarmLevel^1.5\"]," + "\"type\":\"cross_fields\",\"operator\":\"OR\",\"slop\":0,\"prefix_length\":0,\"max_expansions\":50,\"zero_terms_query\":\"NONE\",\"auto_generate_synonyms_phrase_query\":true,\"fuzzy_transpositions\":true,\"boost\":1}}]," } searchTime := searchBody.SearchTime if searchTime == nil || len(searchTime) < 2 { info := make(map[string]interface{}, 0) info["err"] = "请输入时间范围" return info } gteDate := searchTime[0] lteDate := searchTime[1] //判断数据ID idStr := "" linkTagInfoIdStr := "" //personId := searchBody.Id id := []string{} if id != nil && len(id) > 0 { esId := strings.Replace(strings.Trim(fmt.Sprint(id), "[]"), " ", "\",\"", -1) idStr = "{\"terms\":{\"id\":[\"" + esId + "\"]}}," linkTagInfoIdStr = "{\"terms\":{\"linkTagInfo.id\":[\"" + esId + "\"]}}," } //判断任务ID taskIdStr := "" linkTagInfoTaskIdStr := "" taskId := searchBody.Tasks if taskId != nil && len(taskId) > 0 { esTaskId := strings.Replace(strings.Trim(fmt.Sprint(taskId), "[]"), " ", "\",\"", -1) taskIdStr = "{\"terms\":{\"taskId\":[\"" + esTaskId + "\"]}}," linkTagInfoTaskIdStr = "{\"terms\":{\"linkTagInfo.taskId\":[\"" + esTaskId + "\"]}}," } //判断数据来源 dataSourceStr := "" dataSource := searchBody.DataSource if dataSource != "" { dataSourceStr = "{\"term\":{\"dataSource\":\"" + dataSource + "\"}}," } //判断摄像机ID cameraIdStr := "" linkTagInfoCameraIdStr := "" cameraId := searchBody.TreeNodes if cameraId != nil && len(cameraId) > 0 { esCameraId := strings.Replace(strings.Trim(fmt.Sprint(cameraId), "[]"), " ", "\",\"", -1) cameraIdStr = "{\"terms\":{\"cameraId\":[\"" + esCameraId + "\"]}}," linkTagInfoCameraIdStr = "{\"terms\":{\"linkTagInfo.cameraId\":[\"" + esCameraId + "\"]}}," } //判断库表ID tableId := searchBody.Tabs esTableId := "" esTableIdStr := "" linkTagInfoEsTableIdStr := "" if tableId != nil && len(tableId) > 0 { esTableId = strings.Replace(strings.Trim(fmt.Sprint(tableId), "[]"), " ", "\",\"", -1) esTableIdStr = "{\"terms\":{\"baseInfo.tableId\":[\"" + esTableId + "\"]}}," linkTagInfoEsTableIdStr = "{\"terms\":{\"linkTagInfo.baseInfo.tableId\":[\"" + esTableId + "\"]}}," } //判断收藏状态 isCollectStr := "" linkTagInfoIsCollectStr := "" isCollect := searchBody.Collection if isCollect != "" { if isCollect == "1" { isCollectStr = "{\"term\":{\"isCollect\":true}}," linkTagInfoIsCollectStr = "{\"term\":{\"linkTagInfo.isCollect\":true}}," } else if isCollect == "0" { isCollectStr = "{\"term\":{\"isCollect\":false}}," linkTagInfoIsCollectStr = "{\"term\":{\"linkTagInfo.isCollect\":false}}," } } //判断布防等级id alarmLevelId := searchBody.AlarmLevel alarmLevelStr := "" linkTagInfoAlarmLevelStr := "" if len(alarmLevelId) > 0 { alarmLevelTypes := strings.Replace(strings.Trim(fmt.Sprint(getAlarmLevel(alarmLevelId)), "[]"), " ", "\",\"", -1) alarmLevelStr = "{\"terms\":{\"alarmRules.alarmLevel.raw\":[\"" + alarmLevelTypes + "\"]}}," linkTagInfoAlarmLevelStr = "{\"terms\":{\"linkTagInfo.alarmRules.alarmLevel.raw\":[\"" + alarmLevelTypes + "\"]}}," } //使用es底层机制处理分页 //请求头 serverId := config.Server.AnalyServerId var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" || localConf.AlarmPort == 0 { logger.Debug("localConf err") return nil } url := "http://" + localConf.AlarmIp + ":" + strconv.Itoa(int(localConf.AlarmPort)) + "/" + index + "/_search?search_type=dfs_query_then_fetch" analyServerFilterStr := "" if !searchBody.IsAll { analyServerFilterStr = "{\"term\":{\"analyServerId\":\"" + serverId + "\"}}," } linkTagInfoAnalyServerFilterStr := "{\"term\":{\"linkTagInfo.analyServerId\":\"" + serverId + "\"}}," //请求体 prama := "{\"from\":\"" + from + "\"," + "\"size\":\"" + size + "\"," + "\"query\":{\"bool\":{" + queryStr + "\"should\":[" + "{\"bool\":{\"filter\":[" + dataSourceStr + cameraIdStr + alarmLevelStr + idStr + taskIdStr + isCollectStr + esTableIdStr + analyServerFilterStr + "{\"range\":{\"picDate\":{\"from\":\"" + gteDate + "\",\"to\":\"" + lteDate + "\",\"include_lower\":true,\"include_upper\":true,\"boost\":1}}}" + "]}}," + "{\"bool\":{\"filter\":[" + linkTagInfoCameraIdStr + linkTagInfoAlarmLevelStr + linkTagInfoIdStr + linkTagInfoTaskIdStr + linkTagInfoIsCollectStr + linkTagInfoEsTableIdStr + linkTagInfoAnalyServerFilterStr + "{\"range\":{\"linkTagInfo.picDate\":{\"from\":\"" + gteDate + "\",\"to\":\"" + lteDate + "\",\"include_lower\":true,\"include_upper\":true,\"boost\":1}}}" + "]}}" + "],\"minimum_should_match\":1}}," + "\"sort\":[{\"_score\":{\"order\":\"desc\"}},{\"updateTime\":{\"order\":\"desc\"}}]," + "\"_source\":{\"includes\":[],\"excludes\":[\"*.feature\"]}" + "}" logger.Debug("url: ", url) logger.Debug("findEsData.param:", prama) //数据解析 tokenRes := esutil.GetEsDataReq(url, prama, true) tmpAllDate := esutil.ResponseData(tokenRes) return tmpAllDate } // 报警记录关注和取消关注接口 func (sc *EsSearchController) Collect(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply { type CollectReq struct { Id string `json:"id"` //数据id IsCollect bool `json:"isCollect"` //关注或者取消关注 } var reqBody CollectReq if e := c.BindJSON(&reqBody); e != nil { return &bhomeclient.Reply{Msg: "参数有误:" + e.Error()} } var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" || localConf.AlarmPort == 0 { logger.Debug("localConf err") return &bhomeclient.Reply{Msg: "es未配置"} } index := config.EsInfo.EsIndex.AiOcean.IndexName url := "http://" + localConf.AlarmIp + ":" + strconv.Itoa(int(localConf.AlarmPort)) + "/" + index + "/_update_by_query?refresh=true" sourceStr := "ctx._source.isCollect=" + strconv.FormatBool(reqBody.IsCollect) + "" var reqJson = ` { "script": { "source": "` + sourceStr + `" }, "query": { "term": { "id": "` + reqBody.Id + `" } } } ` buf, re := esutil.EsReq("POST", url, []byte(reqJson)) if re != nil { return &bhomeclient.Reply{Msg: re.Error()} } var info interface{} json.Unmarshal(buf, &info) out, ok := info.(map[string]interface{}) if !ok { return &bhomeclient.Reply{Msg: "更新失败"} } middle, ok := out["updated"].(float64) batches, ok1 := out["batches"].(float64) if !ok || !ok1 { return &bhomeclient.Reply{Msg: "first updated change error"} } if batches == 0 { return &bhomeclient.Reply{Msg: "目标数据不存在"} } logger.Debug("修改es是否关注 ret:", string(buf), " middle:", middle, " batches:", batches, " reqBody:", reqBody) return &bhomeclient.Reply{Success: true, Msg: "更新成功"} } // UpdateStatusReq 请求体结构 type UpdateMisreportReq struct { Id string `json:"id"` // 数据id PicSrcUrl []string `json:"picSrcUrl"` // 照片URL列表 TaskName string `json:"taskName"` // 保存照片的文件名列表 } // @Security ApiKeyAuth // @Summary 修改ES数据中的status字段 // @Description 修改ES数据中的status字段 // @Accept json // @Produce json // @Tags es // @Param obj body models.SignMisreport true "更新请求" // @Success 200 {string} json "{"code":200, msg:"更新成功", success:true}" // @Failure 500 {string} json "{"code":500, msg:"返回错误信息", success:false}" // @Router /data/api-v/es/esUpdateStatus [POST] func (sc *EsSearchController) SignMisreport(h *bhomeclient.WrapperHandler, c *bhomeclient.Request) *bhomeclient.Reply { var reqBody UpdateMisreportReq if e := c.BindJSON(&reqBody); e != nil { return &bhomeclient.Reply{Msg: "参数有误:" + e.Error()} } var sApi bhomedbapi.SysSetApi flag, localConf := sApi.GetServerInfo() if !flag || localConf.AlarmIp == "" || localConf.AlarmPort == 0 { logger.Error("localConf err") return &bhomeclient.Reply{Msg: "es未配置"} } index := config.EsInfo.EsIndex.AiOcean.IndexName // 保存照片到服务器文件夹 saveDir := "/data/misreport_images" //config.Server.MisreportImagesPath // 拼接保存路径 savePath := filepath.Join(saveDir, reqBody.TaskName, "未标注") //logger.Error(savePath) if err := os.MkdirAll(savePath, os.ModePerm); err != nil { logger.Error(err.Error()) return &bhomeclient.Reply{Msg: "创建保存目录失败: " + err.Error()} } // 下载并保存每个照片 for _, photoUrl := range reqBody.PicSrcUrl { if photoUrl == "" { continue } photoUrl = ensureHTTPPrefix(photoUrl) photoResp, err := http.Get(photoUrl) if err != nil { logger.Error("下载照片失败") return &bhomeclient.Reply{Msg: "下载照片失败: " + err.Error()} } defer photoResp.Body.Close() saveFilePath := filepath.Join(savePath, reqBody.Id+".jpg") saveFile, err := os.Create(saveFilePath) if err != nil { logger.Error("创建保存文件失败:", saveFilePath) return &bhomeclient.Reply{Msg: "创建保存文件失败: " + err.Error()} } defer saveFile.Close() if _, err := io.Copy(saveFile, photoResp.Body); err != nil { logger.Error("保存照片失败:", saveFilePath) return &bhomeclient.Reply{Msg: "保存照片失败: " + err.Error()} } } url := "http://" + localConf.AlarmIp + ":" + strconv.Itoa(int(localConf.AlarmPort)) + "/" + index + "/_update_by_query" // 构造更新请求的 JSON var reqJson = ` { "script": { "source": "ctx._source.misreport = params.misreport", "lang": "painless", "params": { "misreport": true } }, "query": { "bool": { "filter": [ { "term": { "id": "` + reqBody.Id + `" } } ] } } }` // 发送请求到 ES buf, re := esutil.EsReq("POST", url, []byte(reqJson)) if re != nil { logger.Error(re.Error()) return &bhomeclient.Reply{Msg: re.Error()} } // 解析返回结果 var info interface{} json.Unmarshal(buf, &info) out, ok := info.(map[string]interface{}) if !ok { logger.Error("更新失败") return &bhomeclient.Reply{Msg: "更新失败"} } // 检查是否成功更新 updated, ok := out["updated"].(float64) if !ok || updated == 0 { return &bhomeclient.Reply{Msg: "目标数据不存在或未更新"} } logger.Debug("更新es数据ret:", string(buf), " misreport:", updated, " reqBody:", reqBody) return &bhomeclient.Reply{Success: true, Msg: "更新成功"} } // 确保 URL 以 http:// 或 https:// 开头 func ensureHTTPPrefix(url string) string { if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") { url = "http://" + url } return url }