Преглед изворни кода

fix : Websocket图片消息处理

bilingfeng пре 1 година
родитељ
комит
6aab78c6c2

+ 10 - 0
game-module/game-module-manage/pom.xml

@@ -127,6 +127,16 @@
             <artifactId>springfox-swagger-ui</artifactId>
             <version>${swagger2.ui.version}</version>
         </dependency>
+        <dependency>
+            <groupId>commons-fileupload</groupId>
+            <artifactId>commons-fileupload</artifactId>
+            <version>1.3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>2.4</version>
+        </dependency>
     </dependencies>
 
     <build>

+ 1 - 1
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/ManageApplication.java

@@ -23,7 +23,7 @@ public class ManageApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(ManageApplication.class, args);
-        System.out.println("赞象Manage服务启动成功 <Websocket房间消息新增游戏列表返回> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象Manage服务启动成功 <Websocket图片消息处理> ( ´・・)ノ(._.`) \n" +
                 "___  ___  ___   _   _   ___  _____  _____ \n" +
                 "|  \\/  | / _ \\ | \\ | | / _ \\|  __ \\|  ___|\n" +
                 "| .  . |/ /_\\ \\|  \\| |/ /_\\ \\ |  \\/| |__  \n" +

+ 0 - 9
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/IOssService.java

@@ -1,9 +0,0 @@
-package com.zanxiang.game.module.manage.service;
-
-/**
- * @author : lingfeng
- * @time : 2022-07-12
- * @description : 阿里云oss
- */
-public interface IOssService {
-}

+ 25 - 17
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/api/KfWxApiService.java

@@ -3,10 +3,8 @@ package com.zanxiang.game.module.manage.service.api;
 import com.zanxiang.game.module.base.ServerInfo;
 import com.zanxiang.game.module.base.rpc.IWxApiServiceRpc;
 import com.zanxiang.game.module.manage.pojo.dto.GameAppletDTO;
-import com.zanxiang.game.module.manage.pojo.dto.KfUploadTempMediaDTO;
 import com.zanxiang.game.module.manage.service.IGameAppletService;
 import com.zanxiang.module.util.JsonUtil;
-import com.zanxiang.module.util.exception.BaseException;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboReference;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -59,7 +57,13 @@ public class KfWxApiService {
                 .queryParam("access_token", accessToken)
                 .build().toUri();
         // 发送请求
-        String result = restTemplate.postForObject(uri, msgParamMap, String.class);
+        String result;
+        try {
+            result = restTemplate.postForObject(uri, msgParamMap, String.class);
+        } catch (Exception e) {
+            log.error("客服消息发送异常, e : {}", e.getMessage());
+            return Tuples.of(400L, e.getMessage());
+        }
         log.error("客服消息发送结果, result : {}", result);
         Map<String, String> resultMap = JsonUtil.toMap(result, Map.class, String.class);
         return Tuples.of(Long.valueOf(resultMap.get("errcode")), resultMap.get("errmsg"));
@@ -68,7 +72,15 @@ public class KfWxApiService {
     /**
      * 上传临时素材
      */
-    public String mediaUpload(String accessToken, MultipartFile files) {
+    public Tuple2<Long, String> mediaUpload(Long gameId, MultipartFile files) {
+        GameAppletDTO gameAppletDTO = gameAppletService.getByGameId(gameId);
+        if (gameAppletDTO == null) {
+            return Tuples.of(400L, "小程序信息不存在");
+        }
+        //客服消息参数构造
+        log.error("客服消息发送参数, gameId : {}", gameId);
+        //获取接口token
+        String accessToken = wxApiServiceRpc.getAccessToken(gameAppletDTO.getAppId(), gameAppletDTO.getAppSecret());
         URI uri = UriComponentsBuilder.fromHttpUrl("https://api.weixin.qq.com/cgi-bin/media/upload")
                 .queryParam("access_token", accessToken)
                 .queryParam("type", "image")
@@ -85,22 +97,18 @@ public class KfWxApiService {
         map.add("media", formEntity);
         //http请求头
         HttpEntity<LinkedMultiValueMap<String, Object>> httpEntity = new HttpEntity<>(map, httpHeaders);
-        Object result;
+        String result;
         try {
-            result = restTemplate.postForObject(uri, httpEntity, Object.class);
+            result = restTemplate.postForObject(uri, httpEntity, String.class);
         } catch (Exception e) {
             log.error("客服消息上传临时素材异常, e : {}", e.getMessage());
-            throw new BaseException("上传临时素材异常");
+            return Tuples.of(400L, e.getMessage());
         }
-        if (result == null) {
-//            log.error("企微上传临时素材异常, accessToken : {}, type : {}", accessToken, type);
-            throw new BaseException("企微上传临时素材返回结果为空");
-        }
-        KfUploadTempMediaDTO res = JsonUtil.toObj(JsonUtil.toString(result), KfUploadTempMediaDTO.class);
-        if (res.isSuccess()) {
-            return res.getMedia_id();
-        }
-//        log.error("企微上传临时素材异常, accessToken : {}, type : {}, errmsg : {}", accessToken, type, res.getErrmsg());
-        throw new BaseException("企微上传临时素材失败");
+        log.error("客服消息上传临时素材结果, result : {}", result);
+        Map<String, String> resultMap = JsonUtil.toMap(result, Map.class, String.class);
+        //错误码
+        long resultCode = Long.parseLong(resultMap.get("errcode"));
+        //返回
+        return resultCode == 0 ? Tuples.of(resultCode, resultMap.get("media_id")) : Tuples.of(resultCode, resultMap.get("errmsg"));
     }
 }

+ 37 - 2
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/KfAppletMsgServiceImpl.java

@@ -2,6 +2,7 @@ package com.zanxiang.game.module.manage.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.zanxiang.erp.security.util.SecurityUtil;
 import com.zanxiang.game.module.manage.constant.RedisKeyConstant;
 import com.zanxiang.game.module.manage.enums.KfRoomMsgOwnerEnum;
 import com.zanxiang.game.module.manage.enums.KfRoomMsgTypeEnum;
@@ -9,14 +10,20 @@ import com.zanxiang.game.module.manage.enums.KfWebSocketMsgEnum;
 import com.zanxiang.game.module.manage.pojo.dto.KfAppletMsgDTO;
 import com.zanxiang.game.module.manage.pojo.dto.KfWebSocketMsgDTO;
 import com.zanxiang.game.module.manage.service.*;
+import com.zanxiang.game.module.manage.utils.FileUtil;
 import com.zanxiang.game.module.mybatis.entity.*;
+import com.zanxiang.module.oss.service.IOssService;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
+import com.zanxiang.module.util.exception.BaseException;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.IOException;
 import java.time.LocalDateTime;
 import java.util.Collections;
 import java.util.List;
@@ -31,6 +38,12 @@ import java.util.Objects;
 @Service
 public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
 
+    @Value("${media.realm-name}")
+    private String realmName;
+
+    @Autowired
+    private IOssService ossService;
+
     @Autowired
     private RedisTemplate<String, String> redisTemplate;
 
@@ -177,17 +190,39 @@ public class KfAppletMsgServiceImpl implements IKfAppletMsgService {
     }
 
     private KfWebSocketMsgDTO.MsgContentBean getMsgContent(KfAppletMsgDTO kfAppletMsgDTO) {
-        //todo : 这里要考虑图片是否要传到oss上, 用自己的网络地址, 腾讯的图片地址不能保证一直有效
         KfWebSocketMsgDTO.MsgContentBean msgContentBean = new KfWebSocketMsgDTO.MsgContentBean();
         if (Objects.equals(KfRoomMsgTypeEnum.KF_MSG_TYPE_TEXT.getValue(), kfAppletMsgDTO.getMsgType())) {
             msgContentBean.setText(kfAppletMsgDTO.getContent());
         }
         if (Objects.equals(KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue(), kfAppletMsgDTO.getMsgType())) {
-            msgContentBean.setImage(kfAppletMsgDTO.getPicUrl());
+            msgContentBean.setImage(this.mediaConvertOss(kfAppletMsgDTO.getPicUrl()));
         }
         return msgContentBean;
     }
 
+    private String mediaConvertOss(String mediaUrl) {
+        //资源转换
+        MultipartFile multipartFile = FileUtil.urlToMultipartFile(mediaUrl);
+        // 获取上传文件的MIME类型
+        String contentType = multipartFile.getContentType();
+        // 初始化文件格式为默认值
+        String fileFormat = "jpg";
+        // 判断MIME类型不为空并且包含"/"字符
+        if (contentType != null && contentType.contains("/")) {
+            fileFormat = contentType.substring(contentType.lastIndexOf("/") + 1);
+        }
+        //生成唯一文件名
+        String fileName = Long.toString(System.currentTimeMillis(), 36) + SecurityUtil.getUserId() + "." + fileFormat;
+        try {
+            ossService.upload("image/" + fileName, multipartFile.getInputStream());
+        } catch (IOException e) {
+            log.error("文件上传oss异常, mediaUrl : {}, fileFormat : {}, e : {}", mediaUrl, fileFormat, e.getMessage());
+            throw new BaseException("文件上传oss异常");
+        }
+        //oss资源地址
+        return this.realmName + fileName;
+    }
+
     /**
      * 消息发送到redis广播
      */

+ 0 - 16
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/service/impl/OssServiceImpl.java

@@ -1,16 +0,0 @@
-package com.zanxiang.game.module.manage.service.impl;
-
-import com.zanxiang.game.module.manage.service.IOssService;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-
-/**
- * @author : lingfeng
- * @time : 2022-07-12
- * @description : 阿里云oss
- */
-@Slf4j
-@Service
-public class OssServiceImpl implements IOssService {
-
-}

+ 91 - 0
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/utils/FileUtil.java

@@ -0,0 +1,91 @@
+package com.zanxiang.game.module.manage.utils;
+
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.FileItemFactory;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.io.output.ByteArrayOutputStream;
+import org.springframework.web.multipart.MultipartFile;
+import org.springframework.web.multipart.commons.CommonsMultipartFile;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * @author : lingfeng
+ * @time : 2023-02-06
+ * @description : 文件工具类
+ */
+public class FileUtil {
+
+    /**
+     * 网络url转MultipartFile
+     *
+     * @param url : 网络资源url
+     * @return {@link MultipartFile}
+     */
+    public static MultipartFile urlToMultipartFile(String url) {
+        byte[] bytes = downloadResources(url);
+        String name = "mediaFile" + url.substring(url.lastIndexOf("."));
+        return getMultipartFile(name, bytes);
+    }
+
+    /**
+     * 下载资源
+     *
+     * @param url url
+     * @return {@link byte[]}
+     */
+    private static byte[] downloadResources(String url) {
+        URL urlConnection;
+        HttpURLConnection connection = null;
+        try {
+            urlConnection = new URL(url);
+            connection = (HttpURLConnection) urlConnection.openConnection();
+            InputStream in = connection.getInputStream();
+            byte[] buffer = new byte[1024];
+            int len;
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            while ((len = in.read(buffer)) != -1) {
+                out.write(buffer, 0, len);
+            }
+            in.close();
+            out.close();
+            return out.toByteArray();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (connection != null) {
+                connection.disconnect();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 得到多部分文件
+     *
+     * @param name  名字
+     * @param bytes 字节
+     * @return {@link MultipartFile}
+     */
+    private static MultipartFile getMultipartFile(String name, byte[] bytes) {
+        MultipartFile multipartFile = null;
+        ByteArrayInputStream in;
+        try {
+            in = new ByteArrayInputStream(bytes);
+            FileItemFactory factory = new DiskFileItemFactory(16, null);
+            FileItem fileItem = factory.createItem("mainFile", "text/plain", false, name);
+            IOUtils.copy(new ByteArrayInputStream(bytes), fileItem.getOutputStream());
+            multipartFile = new CommonsMultipartFile(fileItem);
+            in.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return multipartFile;
+    }
+
+
+}

+ 11 - 7
game-module/game-module-manage/src/main/java/com/zanxiang/game/module/manage/websocket/KfMsgWebsocketHandler.java

@@ -13,6 +13,7 @@ import com.zanxiang.game.module.manage.service.IKfRoomMsgService;
 import com.zanxiang.game.module.manage.service.IKfRoomService;
 import com.zanxiang.game.module.manage.service.IKfSessionUserService;
 import com.zanxiang.game.module.manage.service.api.KfWxApiService;
+import com.zanxiang.game.module.manage.utils.FileUtil;
 import com.zanxiang.game.module.mybatis.entity.KfRoom;
 import com.zanxiang.game.module.mybatis.entity.KfRoomMsg;
 import com.zanxiang.game.module.mybatis.entity.KfSessionUser;
@@ -187,20 +188,23 @@ public class KfMsgWebsocketHandler implements WebSocketHandler {
         //图片
         if (Objects.equals(msgContent.getMsgType(), KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE)) {
             Map<String, Object> imageMap = new HashMap<>(1);
-            //图片需要上传到腾讯, 转成 media_id
-            imageMap.put("media_id", "media_id");
-
+            //图片上传到腾讯, 转成 media_id
+            Tuple2<Long, String> tuple2 = wxApiService.mediaUpload(param.getGameId(),
+                    FileUtil.urlToMultipartFile(msgContent.getImage()));
+            //素材上传失败, 通知返回通知客户端
+            if (tuple2.getT1() != 0) {
+                this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), tuple2.getT2()));
+                return;
+            }
+            imageMap.put("media_id", tuple2.getT2());
             msgParamMap.put("msgtype", KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue());
             msgParamMap.put(KfRoomMsgTypeEnum.KF_MSG_TYPE_IMAGE.getValue(), imageMap);
-            this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(),
-                    "发图片上传素材的逻辑还没写完, 先发文本, param : " + JsonUtil.toString(param)));
             return;
         }
         //调腾讯接口发送消息
         Tuple2<Long, String> tuple2 = wxApiService.sendCustomMessageApi(param.getGameId(), msgParamMap);
-        Long result = tuple2.getT1();
         //发送失败, 通知返回通知客户端
-        if (result != 0) {
+        if (tuple2.getT1() != 0) {
             this.sendMessage(session, KfWebSocketMsgDTO.fail(param.getWebSocketMsgType(), tuple2.getT2()));
             return;
         }