package api import ( "ZhenTuLocalPassiveAdapter/config" "ZhenTuLocalPassiveAdapter/logger" "bytes" "context" "encoding/json" "fmt" "io" "net/http" "time" "go.uber.org/zap" ) // VIID Request/Response Structures type DeviceIdObject struct { DeviceID string `json:"DeviceID"` } type RegisterRequest struct { RegisterObject DeviceIdObject `json:"RegisterObject"` } type KeepaliveRequest struct { KeepaliveObject DeviceIdObject `json:"KeepaliveObject"` } type UnRegisterRequest struct { UnRegisterObject DeviceIdObject `json:"UnRegisterObject"` } type VIIDBaseResponse struct { ResponseStatusObject ResponseStatusObject `json:"ResponseStatusObject"` } type ResponseStatusObject struct { ID string `json:"Id"` RequestURL string `json:"RequestURL"` StatusCode string `json:"StatusCode"` StatusString string `json:"StatusString"` LocalTime string `json:"LocalTime"` } type SystemTimeResponse struct { ResponseStatusObject struct { LocalTime string `json:"LocalTime"` // yyyyMMddHHmmss } `json:"ResponseStatusObject"` } // VIID Client Methods func ProxyRequest(ctx context.Context, method, path string, body io.Reader, header http.Header) (*http.Response, error) { url := fmt.Sprintf("%s%s", config.Config.Viid.ServerUrl, path) req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return nil, err } // Copy headers for k, v := range header { req.Header[k] = v } return GetAPIClient().Do(req) } // Upload Proxy Structures // CompressionConfig 压缩配置 type CompressionConfig struct { Enabled bool `json:"enabled"` // 是否启用压缩 Quality int `json:"quality"` // 压缩质量 (1-100) } // ThumbnailConfig 缩略图配置 type ThumbnailConfig struct { Mode string `json:"mode"` // 缩略图模式: "cut"(裁切) 或 "shrink"(缩放) ShrinkRatio float64 `json:"shrinkRatio"` // 缩放/裁切比例 (0-1) Quality int `json:"quality"` // 缩略图压缩质量 (1-100) } // ImageProcessingConfig 图像处理配置 type ImageProcessingConfig struct { SourceCompression *CompressionConfig `json:"sourceCompression,omitempty"` // 原图压缩配置 Thumbnail *ThumbnailConfig `json:"thumbnail,omitempty"` // 缩略图配置 } // GetEffectiveConfig 返回有效的图像处理配置(应用默认值) // 默认值:原图不压缩;缩略图裁切模式,比例0.5,质量75 func (c *ImageProcessingConfig) GetEffectiveConfig() *ImageProcessingConfig { effective := &ImageProcessingConfig{ SourceCompression: &CompressionConfig{ Enabled: false, // 默认不压缩原图 Quality: 100, }, Thumbnail: &ThumbnailConfig{ Mode: "cut", // 默认裁切模式 ShrinkRatio: 0.5, // 默认比例0.5 Quality: 75, // 默认质量75 }, } if c == nil { return effective } // 覆盖原图压缩配置 if c.SourceCompression != nil { effective.SourceCompression = c.SourceCompression } // 覆盖缩略图配置 if c.Thumbnail != nil { effective.Thumbnail = c.Thumbnail } return effective } type UploadConfig struct { TaskID int64 `json:"taskId"` FaceUploadURL string `json:"faceUploadUrl"` ThumbnailUploadURL string `json:"thumbnailUploadUrl"` SourceUploadURL string `json:"sourceUploadUrl"` ExpiresAt time.Time `json:"expiresAt"` ImageProcessing *ImageProcessingConfig `json:"imageProcessing,omitempty"` // 图像处理配置 } type ProxyResponse struct { Code int `json:"code"` Message string `json:"message"` Success bool `json:"success"` Data *UploadConfig `json:"data"` // Data can be UploadConfig or string depending on endpoint } type FacePositionInfo struct { LeftTopX int `json:"leftTopX"` LeftTopY int `json:"leftTopY"` RightBtmX int `json:"rightBtmX"` RightBtmY int `json:"rightBtmY"` ImgWidth int `json:"imgWidth"` ImgHeight int `json:"imgHeight"` ShotTime string `json:"shotTime"` // yyyyMMddHHmmss } type SubmitResultRequest struct { TaskID int64 `json:"taskId"` FacePosition FacePositionInfo `json:"facePosition"` } type SubmitFailureRequest struct { TaskID int64 `json:"taskId"` ErrorCode string `json:"errorCode"` ErrorMessage string `json:"errorMessage"` } // Upload Proxy Methods func GetUploadConfig(ctx context.Context, scenicId int64, deviceNo string) (*UploadConfig, error) { url := fmt.Sprintf("%s/proxy/VIID/upload-config?scenicId=%d&deviceNo=%s", config.Config.Viid.ServerUrl, scenicId, deviceNo) req, err := http.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, err } resp, err := GetAPIClient().Do(req) if err != nil { return nil, err } defer resp.Body.Close() var result ProxyResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, err } if !result.Success { return nil, fmt.Errorf("get upload config failed: %s", result.Message) } return result.Data, nil } func SubmitResult(ctx context.Context, taskID int64, facePos FacePositionInfo) error { url := fmt.Sprintf("%s/proxy/VIID/submit-result", config.Config.Viid.ServerUrl) reqData := SubmitResultRequest{ TaskID: taskID, FacePosition: facePos, } jsonData, _ := json.Marshal(reqData) req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") resp, err := GetAPIClient().Do(req) if err != nil { return err } defer resp.Body.Close() var result struct { Code int `json:"code"` Message string `json:"message"` Success bool `json:"success"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return err } if !result.Success { return fmt.Errorf("submit result failed: %s", result.Message) } return nil } func SubmitFailure(ctx context.Context, taskID int64, errorCode, errorMessage string) error { url := fmt.Sprintf("%s/proxy/VIID/submit-failure", config.Config.Viid.ServerUrl) reqData := SubmitFailureRequest{ TaskID: taskID, ErrorCode: errorCode, ErrorMessage: errorMessage, } jsonData, _ := json.Marshal(reqData) req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(jsonData)) if err != nil { return err } req.Header.Set("Content-Type", "application/json") resp, err := GetAPIClient().Do(req) if err != nil { return err } defer resp.Body.Close() return nil } func UploadFileToOSS(ctx context.Context, uploadUrl string, data []byte, contentType string) error { req, err := http.NewRequestWithContext(ctx, "PUT", uploadUrl, bytes.NewReader(data)) if err != nil { return err } req.Header.Set("Content-Length", fmt.Sprintf("%d", len(data))) req.Header.Set("Content-Type", contentType) resp, err := GetUploadClient().Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { body, _ := io.ReadAll(resp.Body) logger.Error("oss upload failed", zap.String("body", string(body))) return fmt.Errorf("oss upload failed: %d", resp.StatusCode) } return nil }