camera_machine_handler.go 8.8 KB

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