package handlers import ( "bytes" "context" "encoding/base64" "encoding/json" "fmt" "github.com/nfnt/resize" "image" "image/jpeg" "image/png" "os" "path/filepath" "strings" ) const ( cameraMachineUrl = "http://127.0.0.1:7074" // GetPhotoRecordUrl 获取照片记录 GetPhotoRecordUrl = "/get_photo_records" // RemoveBackground 移除背景 RemoveBackground = "/remove_background" ) type CameraMachineHandler struct { ctx context.Context handlerRequest *HandlerRequests token string } func NewCameraMachineHandler(ctx context.Context, token string) *CameraMachineHandler { return &CameraMachineHandler{ token: token, ctx: ctx, } } // PhotoRecord 表示单个照片记录 type PhotoRecord struct { ID int `json:"id"` ImagePath string `json:"image_path"` ImageDealMode int `json:"image_deal_mode"` UpdateTime string `json:"update_time"` DeleteTime *string `json:"delete_time"` ActionID int `json:"action_id"` GoodsArtNo string `json:"goods_art_no"` ImageIndex int `json:"image_index"` PhotoCreateTime string `json:"photo_create_time"` CreateTime string `json:"create_time"` } // PhotoRecordItem 表示包含照片记录和动作名称的项目 type PhotoRecordItem struct { PhotoRecord PhotoRecord `json:"PhotoRecord"` ActionName string `json:"action_name"` } // PhotoRecordList 表示单个货号的照片记录列表 type PhotoRecordList struct { GoodsArtNo string `json:"goods_art_no"` ActionTime string `json:"action_time"` Items []string `json:"items"` } // PhotoRecordResponse 表示完整的API响应 type PhotoRecordResponse struct { List []PhotoRecordList `json:"list"` CurrentPage int `json:"current_page"` Size int `json:"size"` TotalCount int `json:"total_count"` TotalPages int `json:"total_pages"` HasPrev bool `json:"has_prev"` HasNext bool `json:"has_next"` } // convertImageToBase64 将图片文件转换为 base64 编码 // compressAndEncodeImage 压缩图片并转换为 base64 编码 func (t *CameraMachineHandler) compressAndEncodeImage(imagePath string, maxWidth uint) (string, error) { // 检查文件是否存在 if _, err := os.Stat(imagePath); os.IsNotExist(err) { return "", fmt.Errorf("图片文件不存在: %s", imagePath) } // 读取图片文件 file, err := os.Open(imagePath) if err != nil { return "", fmt.Errorf("打开图片文件失败: %v", err) } defer file.Close() // 根据文件扩展名确定图片类型 ext := strings.ToLower(filepath.Ext(imagePath)) var img image.Image switch ext { case ".jpg", ".jpeg": img, err = jpeg.Decode(file) case ".png": img, err = png.Decode(file) default: return "", fmt.Errorf("不支持的图片格式: %s", ext) } if err != nil { return "", fmt.Errorf("解码图片失败: %v", err) } // 获取原始图片尺寸 originalBounds := img.Bounds() originalWidth := uint(originalBounds.Dx()) originalHeight := uint(originalBounds.Dy()) // 如果原始宽度小于等于最大宽度,则不需要压缩 var resizedImg image.Image if originalWidth <= maxWidth { resizedImg = img } else { // 按比例计算新高度 newHeight := uint(float64(originalHeight) * float64(maxWidth) / float64(originalWidth)) // 调整图片大小 resizedImg = resize.Resize(maxWidth, newHeight, img, resize.Lanczos3) } // 将压缩后的图片编码为 JPEG 格式 var buf bytes.Buffer err = jpeg.Encode(&buf, resizedImg, &jpeg.Options{Quality: 80}) if err != nil { return "", fmt.Errorf("编码压缩后的图片失败: %v", err) } // 将图片数据转换为 base64 编码 base64Data := base64.StdEncoding.EncodeToString(buf.Bytes()) return base64Data, nil } // GetPhotoList 获取拍照记录列表 func (t *CameraMachineHandler) GetPhotoList(page string) (*PhotoRecordResponse, error) { t.handlerRequest = NewHandlerRequests(t.ctx, t.token, cameraMachineUrl) request, err := t.handlerRequest.MakeGetRequest(GetPhotoRecordUrl + "?page=" + page) if err != nil { return nil, err } // 将返回结果转换为 map 以提取 data 字段 resultMap, ok := request.(map[string]interface{}) if !ok { return nil, fmt.Errorf("无法将响应转换为 map") } // 提取 data 字段 dataValue, exists := resultMap["data"] if !exists { return nil, fmt.Errorf("响应中不存在 data 字段") } // 将 data 字段序列化为 JSON dataJson, err := json.Marshal(dataValue) if err != nil { return nil, fmt.Errorf("序列化 data 字段失败: %v", err) } // 先解析为原始响应结构 var originalResponse struct { List []struct { GoodsArtNo string `json:"goods_art_no"` ActionTime string `json:"action_time"` Items []PhotoRecordItem `json:"items"` } `json:"list"` CurrentPage int `json:"current_page"` Size int `json:"size"` TotalCount int `json:"total_count"` TotalPages int `json:"total_pages"` HasPrev bool `json:"has_prev"` HasNext bool `json:"has_next"` } // 解析为 PhotoRecordResponse 结构 if err := json.Unmarshal(dataJson, &originalResponse); err != nil { return nil, fmt.Errorf("解析原始数据失败: %v", err) } // 创建新的响应结构,将图片路径转换为 base64 response := &PhotoRecordResponse{ CurrentPage: originalResponse.CurrentPage, Size: originalResponse.Size, TotalCount: originalResponse.TotalCount, TotalPages: originalResponse.TotalPages, HasPrev: originalResponse.HasPrev, HasNext: originalResponse.HasNext, } for _, list := range originalResponse.List { var base64Items []string for idx, item := range list.Items { if idx >= 1 { break } // 将图片路径转换为 base64 base64Image, err := t.compressAndEncodeImage(item.PhotoRecord.ImagePath, 400) if err != nil { fmt.Printf("转换图片失败 %s: %v\n", item.PhotoRecord.ImagePath, err) continue } base64Items = append(base64Items, base64Image) } photoRecordList := PhotoRecordList{ GoodsArtNo: list.GoodsArtNo, ActionTime: list.ActionTime, Items: base64Items, } response.List = append(response.List, photoRecordList) } fmt.Printf("数据列表获取成功,共 %d 条记录\n", len(response.List)) return response, nil } // RemoveBackground 批量处理抠图操作 func (t *CameraMachineHandler) RemoveBackground(goodsArtNos []string) (string, error) { t.handlerRequest = NewHandlerRequests(t.ctx, t.token, cameraMachineUrl) postData := map[string]interface{}{} postData["goods_art_nos"] = goodsArtNos postData["token"] = t.token request, err := t.handlerRequest.MakePostRequest(RemoveBackground, postData) if err != nil { return "", err } // 将返回结果转换为 map 以提取 data 字段 resultMap, ok := request.(map[string]interface{}) if !ok { return "", fmt.Errorf("无法将响应转换为 map") } // 提取 data 字段 dataValue, exists := resultMap["data"] if !exists { return "", fmt.Errorf("响应中不存在 data 字段") } // 解析 data 字段为 map dataMap, ok := dataValue.(map[string]interface{}) if !ok { return "", fmt.Errorf("data 字段不是有效的 map 类型") } // 获取 output_folder 值 outputFolder, exists := dataMap["output_folder"] if !exists { return "", fmt.Errorf("data 字段中不存在 output_folder") } outputFolderPath, ok := outputFolder.(string) if !ok { return "", fmt.Errorf("output_folder 不是字符串类型") } fmt.Printf("输出文件夹路径: %s\n", outputFolderPath) return outputFolderPath, nil }