Browse Source

add(backend) [素材新增]

wangxiao 4 years ago
parent
commit
0e5a064c16

+ 112 - 8
operation-backend/src/main/java/com/idiot/operationbackend/controller/MaterialController.java

@@ -1,10 +1,25 @@
 package com.idiot.operationbackend.controller;
 
-import com.idiot.operationbackend.support.JsonResult;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.idiot.operationbackend.entity.Material;
+import com.idiot.operationbackend.service.facade.MaterialService;
+import com.idiot.operationbackend.service.facade.WeChatService;
+import com.idiot.operationbackend.support.*;
+import com.idiot.operationbackend.util.JwtTokenUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.time.LocalDateTime;
+import java.util.Arrays;
 
 
 /**
@@ -18,27 +33,116 @@ import org.springframework.web.bind.annotation.*;
 public class MaterialController {
 
 
+    private final Logger logger = LoggerFactory.getLogger(MaterialController.class);
+
+    private final String splitStr = ",";
+
+    @Autowired
+    private WeChatService weChatService;
+
+    @Autowired
+    private MaterialService materialService;
 
     @GetMapping("/{accountId}")
     @ApiOperation(value = "查询公众号素材")
-    public ResponseEntity<JsonResult<Object>> materialList (@RequestHeader String token,
-                                                    @PathVariable String accountId,
-                                                    @RequestParam String type){
-        return null;
+    public ResponseEntity<JsonResult<Page<Material>>> materialList (@RequestHeader String token,
+                                                          @PathVariable String accountId,
+                                                          @RequestParam String type,
+                                                          @RequestParam int page){
+        String userId = JwtTokenUtil.getUserId(token);
+        logger.info("用户:{}查询微信公众号{}素材,素材类型:{}-------------start",userId,accountId,type);
+        Page<Material> materialPage = materialService.pageMaterial(accountId,type,page);
+        logger.info("用户:{}查询微信公众号{}素材,素材类型:{}-------------end",userId,accountId,type);
+        return ResponseEntity.ok(JsonResult.success(materialPage));
     }
 
 
     @PostMapping("/{accountId}")
     @ApiOperation(value = "公众号上传素材(其他素材,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb))")
-    public ResponseEntity<JsonResult<Object>> addMaterial (@RequestHeader String token,
+    @ApiParam(value = "type",name = "type",allowableValues = "image,voice,video,thumb")
+    public ResponseEntity<JsonResult<Boolean>> addMaterial ( @RequestHeader String token,
                                                             @PathVariable String accountId,
-                                                            @RequestParam String type){
+                                                            @RequestParam String type,
+                                                            @RequestParam(required = false) String title,
+                                                            @RequestParam(required = false) String description,
+                                                            MultipartFile file){
 
-        return null;
+        String userId = JwtTokenUtil.getUserId(token);
+        long size  = file.getSize();
+        String fileName = file.getName();
+        String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
+        switch (type){
+            case Constants.IMAGE:
+                validImg(size,suffix);
+                break;
+            case Constants.VIDEO:
+                validVideo(size,suffix);
+                break;
+            case Constants.VOICE:
+                validVoice(size,suffix);
+                break;
+            case Constants.THUMB:
+                validThumb(size,suffix);
+                break;
+            default:
+                throw new CustomException(500,"其他类型素材暂不支持上传");
+        }
+        logger.info("用户:{}上传素材到微信公众号{}服务器,素材名称:{},素材类型:{}-------------start",userId,accountId,fileName,type);
+        Material material = new Material();
+        material.setAccountId(accountId);
+        material.setCreateTime(LocalDateTime.now().format(Constants.DATE_TIME_FORMATTER));
+        material.setType(MediaType.nameOf(type));
+        material.setName(title);
+        material.setDescription(description);
+        String wxResult = null;
+        try {
+            WxInputStreamResource inputStreamResource = new WxInputStreamResource(file.getInputStream(),fileName,size);
+            wxResult = weChatService.addMaterial(accountId,type,inputStreamResource,title,description);
+        }catch (IOException e) {
+            logger.error("上传文件发生IO异常:{}",e.getMessage());
+        }
+        boolean addResult = materialService.addMaterial(material,wxResult);
+        logger.info("用户:{}上传素材到微信公众号{}服务器,素材名称:{},素材类型:{}-------------end,结果:{}",userId,accountId,fileName,type,addResult);
+        return ResponseEntity.ok(JsonResult.success(addResult));
     }
 
 
 
+    private void validImg(long size, String suffixName) {
+        if (size > Constants.IMAGE_MAX_SIZE) {
+            throw new CustomException(500,"文件太大,图片文件的大小最大为10M,请重新上传!");
+        }
+        if (!Arrays.asList(Constants.IMAGE_SUPPORTED.split(splitStr)).contains(suffixName)) {
+            throw new CustomException(500,"图片格式不支持,请选择bmp/png/jpeg/jpg/gif的任意一种!");
+        }
+    }
+
+    private void validVoice(long size, String suffixName) {
+        if (size > Constants.VOICE_MAX_SIZE) {
+            throw new CustomException(500,"文件太大,音频文件的大小最大为2M,请重新上传!");
+        }
+        if (!Arrays.asList(Constants.VOICE_SUPPORTED.split(splitStr)).contains(suffixName)) {
+            throw new CustomException(500,"图片格式不支持,请选择mp3/wma/wav/amr的任意一种!");
+        }
+    }
+
+    private void validVideo(long size, String suffixName) {
+        if (size > Constants.VIDEO_MAX_SIZE) {
+            throw new CustomException(500,"文件太大,视频文件的大小最大为10M,请重新上传!");
+        }
+        if (!suffixName.endsWith(Constants.VIDEO_SUPPORTED)) {
+            throw new CustomException(500,"视频格式不支持,请选择mp4格式!");
+        }
+    }
+
+    private void validThumb(long size, String suffixName) {
+        if (size > Constants.THUMB_MAX_SIZE) {
+            throw new CustomException(500,"文件太大,缩略图文件的大小最大为64K,请重新上传!");
+        }
+        if (!suffixName.endsWith(Constants.THUMB_SUPPORTED)) {
+            throw new CustomException(500,"图片格式不支持,请选择JPG格式!");
+        }
+    }
 
 
 }

+ 23 - 0
operation-backend/src/main/java/com/idiot/operationbackend/service/facade/MaterialService.java

@@ -1,5 +1,6 @@
 package com.idiot.operationbackend.service.facade;
 
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.idiot.operationbackend.entity.Material;
 
@@ -8,4 +9,26 @@ import com.idiot.operationbackend.entity.Material;
  * @date Created in 18:40 2020/9/18
  */
 public interface MaterialService extends IService<Material> {
+
+    /**
+     *  添加素材
+     * @author wangxiao
+     * @date 14:38 2020/9/21
+     * @param material material
+     * @param wxJson wxJson
+     * @return boolean
+     */
+    boolean addMaterial(Material material,String wxJson);
+
+
+    /**
+     *  查询素材
+     * @author wangxiao
+     * @date 15:02 2020/9/21
+     * @param accountId accountId
+     * @param type type
+     * @param page page
+     * @return com.baomidou.mybatisplus.extension.plugins.pagination.Page<com.idiot.operationbackend.entity.Material>
+     */
+    Page<Material> pageMaterial(String accountId,String type,int page);
 }

+ 16 - 0
operation-backend/src/main/java/com/idiot/operationbackend/service/facade/WeChatService.java

@@ -2,11 +2,13 @@ package com.idiot.operationbackend.service.facade;
 
 import com.idiot.operationbackend.entity.Account;
 import com.idiot.operationbackend.support.Constants;
+import com.idiot.operationbackend.support.WxInputStreamResource;
 import org.dom4j.Document;
 import org.dom4j.Element;
 import org.dom4j.io.SAXReader;
 import org.springframework.util.StringUtils;
 
+import java.io.File;
 import java.io.InputStream;
 import java.util.HashMap;
 import java.util.List;
@@ -285,6 +287,20 @@ public interface WeChatService {
   boolean upTag(String accountId,int wxId,String name);
 
 
+    /**
+     *  上传其他素材
+     * @author wangxiao
+     * @date 14:20 2020/9/21
+     * @param accountId
+     * @param type
+     * @param resource
+     * @param title
+     * @param introduction
+     * @return java.lang.String
+     */
+   String addMaterial(String accountId, String type, WxInputStreamResource resource, String title, String introduction);
+
+
 
 
 

+ 36 - 0
operation-backend/src/main/java/com/idiot/operationbackend/service/impl/MaterialServiceImpl.java

@@ -1,11 +1,20 @@
 package com.idiot.operationbackend.service.impl;
 
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.idiot.operationbackend.entity.Material;
 import com.idiot.operationbackend.mappers.MaterialMapper;
 import com.idiot.operationbackend.service.facade.MaterialService;
+import com.idiot.operationbackend.support.MediaType;
+import com.sun.xml.internal.ws.api.model.MEP;
 import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.Objects;
 
 /**
  * @author wang xiao
@@ -16,4 +25,31 @@ public class MaterialServiceImpl extends ServiceImpl<MaterialMapper, Material>
         implements MaterialService {
 
 
+    @Override
+    public boolean addMaterial(Material material, String wxJson) {
+        if(Objects.isNull(wxJson)|| Objects.isNull(material)){
+            return false;
+        }
+        JSONObject jsonObject = JSON.parseObject(wxJson);
+        String mediaId = jsonObject.getString("media_id");
+        String wxUrl = jsonObject.getString("url");
+        if (StringUtils.isEmpty(mediaId) || StringUtils.isEmpty(wxUrl)){
+            return false;
+        }
+        material.setMediaId(mediaId);
+        material.setWxUrl(wxUrl);
+        return save(material);
+    }
+
+
+    @Override
+    public Page<Material> pageMaterial(String accountId, String type,int page) {
+        Page<Material> queryPage = new Page<>(page,20);
+        MediaType mediaType = MediaType.nameOf(type);
+        LambdaQueryWrapper<Material> queryWrapper = Wrappers.<Material>lambdaQuery()
+                .eq(Material::getAccountId,accountId)
+                .eq(Material::getType,mediaType)
+                .orderByDesc(Material::getCreateTime);
+        return page(queryPage,queryWrapper);
+    }
 }

+ 34 - 0
operation-backend/src/main/java/com/idiot/operationbackend/service/impl/WeChatServiceImpl.java

@@ -16,12 +16,15 @@ import com.idiot.operationbackend.service.facade.AccountTagService;
 import com.idiot.operationbackend.service.facade.WeChatService;
 import com.idiot.operationbackend.support.Constants;
 import com.idiot.operationbackend.support.CustomException;
+import com.idiot.operationbackend.support.WxInputStreamResource;
 import com.idiot.operationbackend.support.wechat.AesException;
 import com.idiot.operationbackend.support.wechat.WXBizMsgCrypt;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.WritableResource;
 import org.springframework.http.*;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
@@ -29,8 +32,12 @@ import org.springframework.util.LinkedMultiValueMap;
 import org.springframework.util.MultiValueMap;
 import org.springframework.util.StringUtils;
 import org.springframework.web.client.RestTemplate;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.annotation.Resource;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
@@ -580,4 +587,31 @@ public class WeChatServiceImpl implements WeChatService, InitializingBean {
         int errorCode = JSONObject.parseObject(jsonStr).getIntValue("errcode");
         return 0 == errorCode;
     }
+
+
+    @Override
+    public String addMaterial(String accountId, String type, WxInputStreamResource resource,String title, String introduction) {
+        String requestUrl = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=%s&type=%s";
+        String accessToken = getAuthorizerAccessToken(accountId);
+        requestUrl = String.format(requestUrl,accessToken,type);
+        MultiValueMap<String, Object> data = new LinkedMultiValueMap<>();
+        HttpHeaders httpHeaders = new HttpHeaders();
+        httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA);
+        data.add("media", resource);
+        if (Constants.VIDEO.equals(type)) {
+            JSONObject p = new JSONObject();
+            p.put("title", title);
+            p.put("introduction", introduction);
+            data.add("description", p.toString());
+        }
+        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(data,
+                httpHeaders);
+        logger.info("微信上传素材,accountId:{},type:{}----start,时间:{}",accountId,type, LocalDateTime.now().toString());
+        String resultJSON = restTemplate.postForObject(requestUrl, requestEntity, String.class);
+        logger.info("微信上传素材,accountId:{},type:{}----end,时间:{},微信返回结果:{}",accountId,type, LocalDateTime.now().toString(),resultJSON);
+        return resultJSON;
+    }
+
+
+
 }

+ 20 - 0
operation-backend/src/main/java/com/idiot/operationbackend/support/Constants.java

@@ -65,6 +65,26 @@ public class Constants {
 
     public static final int MENU = 5;
 
+
+    public static final long IMAGE_MAX_SIZE = 1024*1024*10;
+    public static final long VOICE_MAX_SIZE = 1024*1024*2;
+    public static final long VIDEO_MAX_SIZE = 1024*1024*10;
+    public static final long THUMB_MAX_SIZE = 64*1024;
+
+    public static final String IMAGE_SUPPORTED= "bmp,png,jpeg,jpg,gif";
+    public static final String VOICE_SUPPORTED= "mp3,wma,wav,amr";
+    public static final String VIDEO_SUPPORTED= "mp4";
+    public static final String THUMB_SUPPORTED= "JPG";
+
+
+    public static final String IMAGE = "image";
+
+    public static final String VOICE = "voice";
+
+    public static final String VIDEO = "video";
+
+    public static final String THUMB = "thumb";
+
     /**
      *  计算 比率
      * @author wangxiao

+ 27 - 2
operation-backend/src/main/java/com/idiot/operationbackend/support/MediaType.java

@@ -20,11 +20,11 @@ public enum MediaType {
     /**
      * 视频
      */
-    VIDEO(1,"video"),
+    VIDEO(2,"video"),
     /**
      * 缩略图
      */
-    THUMB(1,"thumb");
+    THUMB(3,"thumb");
 
 
     @EnumValue
@@ -37,5 +37,30 @@ public enum MediaType {
         this.value = value;
         this.label = label;
     }
+
+    public int getValue() {
+        return value;
+    }
+
+    public void setValue(int value) {
+        this.value = value;
+    }
+
+    public String getLabel() {
+        return label;
+    }
+
+    public void setLabel(String label) {
+        this.label = label;
+    }
+
+    public static MediaType nameOf(String label) {
+        for (MediaType temp :MediaType.values()) {
+            if (label.equals(temp.label)) {
+                return temp;
+            }
+        }
+        return null;
+    }
 }
 

+ 54 - 0
operation-backend/src/main/java/com/idiot/operationbackend/support/WxInputStreamResource.java

@@ -0,0 +1,54 @@
+package com.idiot.operationbackend.support;
+
+import org.springframework.core.io.InputStreamResource;
+
+import java.io.InputStream;
+
+/**
+ * @author wang xiao
+ * @date Created in 14:04 2020/9/21
+ */
+public class WxInputStreamResource extends InputStreamResource {
+
+    private String fileName;
+    private long length;
+
+    public WxInputStreamResource(InputStream inputStream) {
+        super(inputStream);
+    }
+
+    public WxInputStreamResource(InputStream inputStream, String fileName,long length) {
+        super(inputStream);
+        this.length = length;
+        this.fileName = fileName;
+    }
+
+    /**
+     * 覆写父类方法
+     * 如果不重写这个方法,并且文件有一定大小,那么服务端会出现异常
+     * {@code The multi-part request contained parameter data (excluding uploaded files) that exceeded}
+     *
+     * @return
+     */
+    @Override
+    public String getFilename() {
+        return fileName;
+    }
+
+    /**
+     * 覆写父类 contentLength 方法
+     * 因为 {@link org.springframework.core.io.AbstractResource#contentLength()}方法会重新读取一遍文件,
+     * 而上传文件时,restTemplate 会通过这个方法获取大小。然后当真正需要读取内容的时候,发现已经读完,会报如下错误。
+     * <code>
+     * java.lang.IllegalStateException: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times
+     * at org.springframework.core.io.InputStreamResource.getInputStream(InputStreamResource.java:96)
+     * </code>
+     * <p>
+     * @return
+     */
+    @Override
+    public long contentLength() {
+        long estimate = length;
+        return estimate == 0 ? 1 : estimate;
+    }
+}