camera_machine_handler.go 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. package handlers
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/base64"
  6. "encoding/json"
  7. "fmt"
  8. "github.com/nfnt/resize"
  9. "image"
  10. "image/jpeg"
  11. "image/png"
  12. "os"
  13. "path/filepath"
  14. "strings"
  15. )
  16. const (
  17. cameraMachineUrl = "http://127.0.0.1:7074"
  18. // GetPhotoRecordUrl 获取照片记录
  19. GetPhotoRecordUrl = "/get_photo_records"
  20. // RemoveBackground 移除背景
  21. RemoveBackground = "/remove_background"
  22. )
  23. type CameraMachineHandler struct {
  24. ctx context.Context
  25. handlerRequest *HandlerRequests
  26. token string
  27. }
  28. func NewCameraMachineHandler(ctx context.Context, token string) *CameraMachineHandler {
  29. return &CameraMachineHandler{
  30. token: token,
  31. ctx: ctx,
  32. }
  33. }
  34. // PhotoRecord 表示单个照片记录
  35. type PhotoRecord struct {
  36. ID int `json:"id"`
  37. ImagePath string `json:"image_path"`
  38. ImageDealMode int `json:"image_deal_mode"`
  39. UpdateTime string `json:"update_time"`
  40. DeleteTime *string `json:"delete_time"`
  41. ActionID int `json:"action_id"`
  42. GoodsArtNo string `json:"goods_art_no"`
  43. ImageIndex int `json:"image_index"`
  44. PhotoCreateTime string `json:"photo_create_time"`
  45. CreateTime string `json:"create_time"`
  46. }
  47. // PhotoRecordItem 表示包含照片记录和动作名称的项目
  48. type PhotoRecordItem struct {
  49. PhotoRecord PhotoRecord `json:"PhotoRecord"`
  50. ActionName string `json:"action_name"`
  51. }
  52. // PhotoRecordList 表示单个货号的照片记录列表
  53. type PhotoRecordList struct {
  54. GoodsArtNo string `json:"goods_art_no"`
  55. ActionTime string `json:"action_time"`
  56. Items []string `json:"items"`
  57. }
  58. // PhotoRecordResponse 表示完整的API响应
  59. type PhotoRecordResponse struct {
  60. List []PhotoRecordList `json:"list"`
  61. CurrentPage int `json:"current_page"`
  62. Size int `json:"size"`
  63. TotalCount int `json:"total_count"`
  64. TotalPages int `json:"total_pages"`
  65. HasPrev bool `json:"has_prev"`
  66. HasNext bool `json:"has_next"`
  67. }
  68. // convertImageToBase64 将图片文件转换为 base64 编码
  69. // compressAndEncodeImage 压缩图片并转换为 base64 编码
  70. func (t *CameraMachineHandler) compressAndEncodeImage(imagePath string, maxWidth uint) (string, error) {
  71. // 检查文件是否存在
  72. if _, err := os.Stat(imagePath); os.IsNotExist(err) {
  73. return "", fmt.Errorf("图片文件不存在: %s", imagePath)
  74. }
  75. // 读取图片文件
  76. file, err := os.Open(imagePath)
  77. if err != nil {
  78. return "", fmt.Errorf("打开图片文件失败: %v", err)
  79. }
  80. defer file.Close()
  81. // 根据文件扩展名确定图片类型
  82. ext := strings.ToLower(filepath.Ext(imagePath))
  83. var img image.Image
  84. switch ext {
  85. case ".jpg", ".jpeg":
  86. img, err = jpeg.Decode(file)
  87. case ".png":
  88. img, err = png.Decode(file)
  89. default:
  90. return "", fmt.Errorf("不支持的图片格式: %s", ext)
  91. }
  92. if err != nil {
  93. return "", fmt.Errorf("解码图片失败: %v", err)
  94. }
  95. // 获取原始图片尺寸
  96. originalBounds := img.Bounds()
  97. originalWidth := uint(originalBounds.Dx())
  98. originalHeight := uint(originalBounds.Dy())
  99. // 如果原始宽度小于等于最大宽度,则不需要压缩
  100. var resizedImg image.Image
  101. if originalWidth <= maxWidth {
  102. resizedImg = img
  103. } else {
  104. // 按比例计算新高度
  105. newHeight := uint(float64(originalHeight) * float64(maxWidth) / float64(originalWidth))
  106. // 调整图片大小
  107. resizedImg = resize.Resize(maxWidth, newHeight, img, resize.Lanczos3)
  108. }
  109. // 将压缩后的图片编码为 JPEG 格式
  110. var buf bytes.Buffer
  111. err = jpeg.Encode(&buf, resizedImg, &jpeg.Options{Quality: 80})
  112. if err != nil {
  113. return "", fmt.Errorf("编码压缩后的图片失败: %v", err)
  114. }
  115. // 将图片数据转换为 base64 编码
  116. base64Data := base64.StdEncoding.EncodeToString(buf.Bytes())
  117. return base64Data, nil
  118. }
  119. // GetPhotoList 获取拍照记录列表
  120. func (t *CameraMachineHandler) GetPhotoList(page string) (*PhotoRecordResponse, error) {
  121. t.handlerRequest = NewHandlerRequests(t.ctx, t.token, cameraMachineUrl)
  122. request, err := t.handlerRequest.MakeGetRequest(GetPhotoRecordUrl + "?page=" + page)
  123. if err != nil {
  124. return nil, err
  125. }
  126. // 将返回结果转换为 map 以提取 data 字段
  127. resultMap, ok := request.(map[string]interface{})
  128. if !ok {
  129. return nil, fmt.Errorf("无法将响应转换为 map")
  130. }
  131. // 提取 data 字段
  132. dataValue, exists := resultMap["data"]
  133. if !exists {
  134. return nil, fmt.Errorf("响应中不存在 data 字段")
  135. }
  136. // 将 data 字段序列化为 JSON
  137. dataJson, err := json.Marshal(dataValue)
  138. if err != nil {
  139. return nil, fmt.Errorf("序列化 data 字段失败: %v", err)
  140. }
  141. // 先解析为原始响应结构
  142. var originalResponse struct {
  143. List []struct {
  144. GoodsArtNo string `json:"goods_art_no"`
  145. ActionTime string `json:"action_time"`
  146. Items []PhotoRecordItem `json:"items"`
  147. } `json:"list"`
  148. CurrentPage int `json:"current_page"`
  149. Size int `json:"size"`
  150. TotalCount int `json:"total_count"`
  151. TotalPages int `json:"total_pages"`
  152. HasPrev bool `json:"has_prev"`
  153. HasNext bool `json:"has_next"`
  154. }
  155. // 解析为 PhotoRecordResponse 结构
  156. if err := json.Unmarshal(dataJson, &originalResponse); err != nil {
  157. return nil, fmt.Errorf("解析原始数据失败: %v", err)
  158. }
  159. // 创建新的响应结构,将图片路径转换为 base64
  160. response := &PhotoRecordResponse{
  161. CurrentPage: originalResponse.CurrentPage,
  162. Size: originalResponse.Size,
  163. TotalCount: originalResponse.TotalCount,
  164. TotalPages: originalResponse.TotalPages,
  165. HasPrev: originalResponse.HasPrev,
  166. HasNext: originalResponse.HasNext,
  167. }
  168. for _, list := range originalResponse.List {
  169. var base64Items []string
  170. for idx, item := range list.Items {
  171. if idx >= 1 {
  172. break
  173. }
  174. // 将图片路径转换为 base64
  175. base64Image, err := t.compressAndEncodeImage(item.PhotoRecord.ImagePath, 400)
  176. if err != nil {
  177. fmt.Printf("转换图片失败 %s: %v\n", item.PhotoRecord.ImagePath, err)
  178. continue
  179. }
  180. base64Items = append(base64Items, base64Image)
  181. }
  182. photoRecordList := PhotoRecordList{
  183. GoodsArtNo: list.GoodsArtNo,
  184. ActionTime: list.ActionTime,
  185. Items: base64Items,
  186. }
  187. response.List = append(response.List, photoRecordList)
  188. }
  189. fmt.Printf("数据列表获取成功,共 %d 条记录\n", len(response.List))
  190. return response, nil
  191. }
  192. // RemoveBackground 批量处理抠图操作
  193. func (t *CameraMachineHandler) RemoveBackground(goodsArtNos []string) (string, error) {
  194. t.handlerRequest = NewHandlerRequests(t.ctx, t.token, cameraMachineUrl)
  195. postData := map[string]interface{}{}
  196. postData["goods_art_nos"] = goodsArtNos
  197. postData["token"] = t.token
  198. request, err := t.handlerRequest.MakePostRequest(RemoveBackground, postData)
  199. if err != nil {
  200. return "", err
  201. }
  202. // 将返回结果转换为 map 以提取 data 字段
  203. resultMap, ok := request.(map[string]interface{})
  204. if !ok {
  205. return "", fmt.Errorf("无法将响应转换为 map")
  206. }
  207. // 提取 data 字段
  208. dataValue, exists := resultMap["data"]
  209. if !exists {
  210. return "", fmt.Errorf("响应中不存在 data 字段")
  211. }
  212. // 解析 data 字段为 map
  213. dataMap, ok := dataValue.(map[string]interface{})
  214. if !ok {
  215. return "", fmt.Errorf("data 字段不是有效的 map 类型")
  216. }
  217. // 获取 output_folder 值
  218. outputFolder, exists := dataMap["output_folder"]
  219. if !exists {
  220. return "", fmt.Errorf("data 字段中不存在 output_folder")
  221. }
  222. outputFolderPath, ok := outputFolder.(string)
  223. if !ok {
  224. return "", fmt.Errorf("output_folder 不是字符串类型")
  225. }
  226. fmt.Printf("输出文件夹路径: %s\n", outputFolderPath)
  227. return outputFolderPath, nil
  228. }