You've already forked VptPassiveAdapter
feat(config): 更新配置文件并新增自定义时间类型
- 修改 config.yaml 中的 API 地址、存储配置及设备信息 - 新增 Viid 配置项支持新功能模块 - 在 go.mod 和 go.sum 中更新依赖包版本,引入新的第三方库 - 添加 model/custom_time.go 文件实现自定义时间类型的 JSON 序列化与反序列化 - 调整 DTO 结构体以适配新的配置项 - 升级 OTLP 导出器相关依赖并移除旧的标准输出导出器 - 引入 Gin 框架及相关中间件提升服务性能 - 更新 Protobuf 和 gRPC 相关依赖至最新版本 - 增加 zap 日志库和 lumberjack 日志轮转支持 - 添加 sonic、json-iterator 等高性能 JSON 处理库优化数据解析效率 - 引入 testify 断言库增强测试代码可读性 - 更新 sync 包版本提高并发安全性 - 添加 mock 工具支持单元测试模拟对象 - 引入 validator/v10 实现请求参数校验功能 - 更新 crypto、net、text 等标准库依赖确保安全性和兼容性 - 增加 mimetype 库用于文件类型识别 - 引入 quic-go 支持 HTTP/3 协议通信 - 添加 base64x
This commit is contained in:
209
api/viid_client.go
Normal file
209
api/viid_client.go
Normal file
@@ -0,0 +1,209 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"ZhenTuLocalPassiveAdapter/config"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
type UploadConfig struct {
|
||||
TaskID int64 `json:"taskId"`
|
||||
FaceUploadURL string `json:"faceUploadUrl"`
|
||||
ThumbnailUploadURL string `json:"thumbnailUploadUrl"`
|
||||
SourceUploadURL string `json:"sourceUploadUrl"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
}
|
||||
|
||||
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-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 {
|
||||
return fmt.Errorf("oss upload failed: %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
70
api/viid_upload.go
Normal file
70
api/viid_upload.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"ZhenTuLocalPassiveAdapter/logger"
|
||||
"context"
|
||||
"fmt"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
// UploadFaceData implements the coordinated upload flow: Get Config -> Upload -> Notify
|
||||
func UploadFaceData(ctx context.Context, scenicId int64, deviceNo string, faceImg, thumbImg, srcImg []byte, facePos FacePositionInfo) error {
|
||||
// 1. Get Upload Config
|
||||
uploadConfig, err := GetUploadConfig(ctx, scenicId, deviceNo)
|
||||
if err != nil {
|
||||
logger.Error("获取上传配置失败", zap.String("deviceNo", deviceNo), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("获取到VIID任务ID", zap.Int64("taskId", uploadConfig.TaskID))
|
||||
|
||||
// 2. Parallel Upload to OSS
|
||||
g, subCtx := errgroup.WithContext(ctx)
|
||||
|
||||
// Upload Face Image
|
||||
g.Go(func() error {
|
||||
if len(faceImg) > 0 {
|
||||
if err := UploadFileToOSS(subCtx, uploadConfig.FaceUploadURL, faceImg, "image/jpeg"); err != nil {
|
||||
return fmt.Errorf("upload face image failed: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Upload Thumbnail Image
|
||||
g.Go(func() error {
|
||||
if len(thumbImg) > 0 {
|
||||
if err := UploadFileToOSS(subCtx, uploadConfig.ThumbnailUploadURL, thumbImg, "image/jpeg"); err != nil {
|
||||
return fmt.Errorf("upload thumbnail image failed: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
// Upload Source Image
|
||||
g.Go(func() error {
|
||||
if len(srcImg) > 0 {
|
||||
if err := UploadFileToOSS(subCtx, uploadConfig.SourceUploadURL, srcImg, "image/jpeg"); err != nil {
|
||||
return fmt.Errorf("upload source image failed: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
// Report failure
|
||||
SubmitFailure(ctx, uploadConfig.TaskID, "UPLOAD_FAILED", err.Error())
|
||||
logger.Error("文件上传失败", zap.Int64("taskId", uploadConfig.TaskID), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// 3. Submit Result
|
||||
if err := SubmitResult(ctx, uploadConfig.TaskID, facePos); err != nil {
|
||||
logger.Error("提交结果失败", zap.Int64("taskId", uploadConfig.TaskID), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
logger.Info("VIID任务完成", zap.Int64("taskId", uploadConfig.TaskID))
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user