package devices import ( "encoding/json" "fmt" "github.com/gin-gonic/gin" "go.uber.org/zap" "io" "os" "path/filepath" "server/dao" "server/global" "server/model/common/response" "server/model/devices" "server/utils/cache" "strconv" "strings" "time" ) type CameraApi struct { } // DeviceEndianHeartbeat 摄像头心跳 func (ca *CameraApi) DeviceEndianHeartbeat(c *gin.Context) { var info devices.CameraEndianHeartbeatRequest if err := c.ShouldBindJSON(&info); err != nil { global.GVA_LOG.Error("DeviceEndianHeartbeat === ", zap.Error(err)) response.FailWithMessage("参数错误", c) return } cache.UpdateDeviceState(info.SerialNum, 1) resp := devices.CameraEndianHeartbeatResponse{ ReturnCode: 0, PushEventPic: true, } response.OkWithData(resp, c) } func (ca *CameraApi) DeviceEndianEvent(c *gin.Context) { // 步骤1:解析 multipart/form-data 表单(包含EventInfo和图片文件) var form devices.CameraEventForm if err := c.ShouldBind(&form); err != nil { global.GVA_LOG.Error("表单解析失败", zap.Error(err)) response.FailWithMessage("表单格式错误:"+err.Error(), c) return } // 步骤2:处理EventInfo.json文件(原有逻辑) file, err := form.EventInfo.Open() if err != nil { global.GVA_LOG.Error("打开EventInfo文件失败", zap.Error(err)) response.FailWithMessage("读取事件文件失败:"+err.Error(), c) return } defer file.Close() fileContent, err := io.ReadAll(file) if err != nil { global.GVA_LOG.Error("读取EventInfo文件内容失败", zap.Error(err)) response.FailWithMessage("解析事件文件失败:"+err.Error(), c) return } var eventReq devices.CameraEndianEventRequest if err := json.Unmarshal(fileContent, &eventReq); err != nil { global.GVA_LOG.Error("JSON解析失败", zap.Error(err), zap.String("content", string(fileContent))) response.FailWithMessage("事件数据格式错误:"+err.Error(), c) return } //if eventReq.EventType != "Alarm" { // global.GVA_LOG.Info("事件处理完成", zap.Any("event", eventReq)) // response.OkWithData(devices.CameraEndianEventResponse{ // ReturnCode: 0, // }, c) // return //} // 步骤3:新增逻辑 - 处理图片文件(form.Files) var savedFiles string // 记录成功保存的图片路径 if form.File != nil { // 遍历所有上传的图片 // 3.1 打开图片文件 imgFile, err := form.File.Open() if err != nil { global.GVA_LOG.Warn("打开图片文件失败", zap.String("filename", form.File.Filename), zap.Error(err)) return // 跳过当前文件,处理下一个 } defer imgFile.Close() // 3.2 读取图片内容 imgBytes, err := io.ReadAll(imgFile) if err != nil { global.GVA_LOG.Warn("读取图片内容失败", zap.String("filename", form.File.Filename), zap.Error(err)) return } // 3.3 简单校验图片类型(可选,根据需求调整) if !isImageFile(form.File.Filename) { global.GVA_LOG.Warn("非图片文件,跳过处理", zap.String("filename", form.File.Filename)) return } // 3.4 保存图片到本地(实际项目可改为上传OSS/云存储) savePath, err := saveImageToLocal(eventReq.SerialNum, form.File.Filename, imgBytes) if err != nil { global.GVA_LOG.Warn("保存图片失败", zap.String("filename", form.File.Filename), zap.Error(err)) } savedFiles = savePath } // 步骤4:返回响应(包含事件信息和图片处理结果) resp := devices.CameraEndianEventResponse{ ReturnCode: 0, } global.GVA_LOG.Info("事件处理完成", zap.Any("event", eventReq), zap.String("saved_files", savedFiles)) response.OkWithData(resp, c) } // 辅助函数:判断是否为图片文件(通过文件名后缀) func isImageFile(filename string) bool { ext := strings.ToLower(filepath.Ext(filename)) switch ext { case ".jpg", ".jpeg", ".png", ".bmp", ".gif": return true default: return false } } // 辅助函数:保存图片到本地目录 func saveImageToLocal(serialNum, filename string, data []byte) (string, error) { // 构建保存路径:./uploads/{设备序列号}/{日期}/filename dateDir := time.Now().Format("20060102") saveDir := filepath.Join("./uploads", serialNum, dateDir) if err := os.MkdirAll(saveDir, 0755); err != nil { return "", fmt.Errorf("创建保存目录失败:%w", err) } // 生成唯一文件名(避免重名) uniqueName := fmt.Sprintf("%s_%s", time.Now().Format("150405"), filename) savePath := filepath.Join(saveDir, uniqueName) // 写入文件 if err := os.WriteFile(savePath, data, 0644); err != nil { return "", fmt.Errorf("写入文件失败:%w", err) } return savePath, nil } //--------------------------------------------------------------------------------------------------------------------- func (ca *CameraApi) QueryAllCameras(c *gin.Context) { cameras, err := cameraService.QueryAllCameras() if err != nil { global.GVA_LOG.Error("查询失败", zap.Error(err)) response.FailWithMessage("查询失败", c) return } response.OkWithData(cameras, c) } func (ca *CameraApi) QueryCameraList(c *gin.Context) { var info devices.SearchCamera if err := c.ShouldBind(&info); err != nil { global.GVA_LOG.Error("参数错误", zap.Error(err)) response.FailWithMessage("参数错误", c) return } list, total, err := cameraService.QueryCameraList(info) if err != nil { global.GVA_LOG.Error("查询失败", zap.Error(err)) response.FailWithMessage("查询失败", c) return } response.OkWithDetailed(response.PageResult{ List: list, Total: total, Page: info.Page, PageSize: info.PageSize, }, "获取成功", c) } func (ca *CameraApi) CreateCamera(c *gin.Context) { var camera dao.Camera if err := c.ShouldBind(&camera); err != nil { global.GVA_LOG.Error("参数错误", zap.Error(err)) response.FailWithMessage("参数错误", c) return } if err := cameraService.CreateCamera(camera); err != nil { global.GVA_LOG.Error("新增失败", zap.Error(err)) response.FailWithMessage("新增失败", c) return } response.OkWithMessage("新增成功", c) } func (ca *CameraApi) UpdateCamera(c *gin.Context) { var camera dao.Camera if err := c.ShouldBind(&camera); err != nil { global.GVA_LOG.Error("参数错误", zap.Error(err)) response.FailWithMessage("参数错误", c) return } if err := cameraService.UpdateCamera(camera); err != nil { global.GVA_LOG.Error("更新失败", zap.Error(err)) response.FailWithMessage("更新失败", c) return } response.OkWithMessage("更新成功", c) } func (ca *CameraApi) DeleteCamera(c *gin.Context) { id, err := strconv.Atoi(c.Query("id")) if err != nil { response.FailWithMessage("参数错误", c) return } if err := cameraService.DeleteCamera(id); err != nil { global.GVA_LOG.Error("删除失败", zap.Error(err)) response.FailWithMessage("删除失败", c) return } response.OkWithMessage("删除成功", c) }