Просмотр исходного кода

合作平台
1. RequireSign
2. 移除job直接调用

wangyunpeng 1 неделя назад
Родитель
Сommit
d387d00aa1
14 измененных файлов с 190 добавлено и 222 удалено
  1. 15 0
      api-module/src/main/java/com/tzld/piaoquan/api/annotation/RequireSign.java
  2. 95 5
      api-module/src/main/java/com/tzld/piaoquan/api/config/JwtInterceptor.java
  3. 6 12
      api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformCooperateAccountController.java
  4. 5 102
      api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformDatastatController.java
  5. 5 13
      api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformNoticeController.java
  6. 19 65
      api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformPlanController.java
  7. 2 0
      api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformSettingController.java
  8. 6 20
      api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformUploadContentController.java
  9. 2 0
      api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/LoghubController.java
  10. 28 0
      api-module/src/main/java/com/tzld/piaoquan/api/filter/ContentCachingFilter.java
  11. 2 0
      api-module/src/main/java/com/tzld/piaoquan/api/model/vo/contentplatform/AccountLoginVO.java
  12. 5 0
      api-module/src/main/java/com/tzld/piaoquan/api/service/contentplatform/impl/ContentPlatformAccountServiceImpl.java
  13. 0 1
      api-module/src/main/java/com/tzld/piaoquan/api/service/wecom/impl/WeComServiceImpl.java
  14. 0 4
      api-module/src/main/java/com/tzld/piaoquan/api/util/AliOssFileTool.java

+ 15 - 0
api-module/src/main/java/com/tzld/piaoquan/api/annotation/RequireSign.java

@@ -0,0 +1,15 @@
+package com.tzld.piaoquan.api.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * 标记接口需要请求签名校验(防重放)
+ * 前端需携带 X-Timestamp / X-Nonce / X-Sign 头
+ */
+@Target(ElementType.METHOD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RequireSign {
+}

+ 95 - 5
api-module/src/main/java/com/tzld/piaoquan/api/config/JwtInterceptor.java

@@ -3,6 +3,7 @@ package com.tzld.piaoquan.api.config;
 import com.alibaba.fastjson.JSON;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 import com.tzld.piaoquan.api.annotation.JwtIgnore;
+import com.tzld.piaoquan.api.annotation.RequireSign;
 import com.tzld.piaoquan.api.common.enums.ExceptionEnum;
 import com.tzld.piaoquan.api.common.enums.contentplatform.ContentPlatformAccountTypeEnum;
 import com.tzld.piaoquan.api.common.exception.CommonException;
@@ -16,11 +17,17 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.http.HttpMethod;
 import org.springframework.stereotype.Component;
+import org.springframework.util.StreamUtils;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerInterceptor;
 
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Base64;
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.LinkedBlockingQueue;
@@ -65,11 +72,6 @@ public class JwtInterceptor implements HandlerInterceptor {
             return true;
         }
 
-        if ((StringUtils.isNotBlank(request.getRequestURI()) && request.getRequestURI().contains("auth")) ||
-                (Objects.nonNull(request.getRequestURL()) && request.getRequestURL().toString().contains("auth"))
-        ) {
-            return Boolean.TRUE;
-        }
         // 忽略带JwtIgnore注解的请求, 不做后续token认证校验
         if (handler instanceof HandlerMethod) {
             HandlerMethod handlerMethod = (HandlerMethod) handler;
@@ -97,6 +99,16 @@ public class JwtInterceptor implements HandlerInterceptor {
 
         validToken(authHeader);
 
+        // 请求签名校验(防重放),标记了 @RequireSign 的接口需要验签
+        if (handler instanceof HandlerMethod) {
+            HandlerMethod handlerMethod = (HandlerMethod) handler;
+            RequireSign requireSign = handlerMethod.getMethodAnnotation(RequireSign.class);
+            if (requireSign != null) {
+                // todo
+//                validateSign(request, authHeader);
+            }
+        }
+
         // account 管理接口仅允许内部账号访问
         if (uri.startsWith("/contentPlatform/account/")) {
             ContentPlatformAccount loginUser = LoginUserContext.getUser();
@@ -169,6 +181,84 @@ public class JwtInterceptor implements HandlerInterceptor {
         this.internalKey = internalKey;
     }
 
+    /**
+     * 请求签名校验:HMAC-SHA256(method:uri:timestamp:nonce:body, signKey)
+     * - timestamp 必须在 ±5 分钟内
+     * - nonce 全局唯一(Redis setIfAbsent,10 分钟过期)
+     */
+    private void validateSign(HttpServletRequest request, String token) {
+        String timestamp = request.getHeader("X-Timestamp");
+        String nonce = request.getHeader("X-Nonce");
+        String sign = request.getHeader("X-Sign");
+
+        if (StringUtils.isBlank(timestamp) || StringUtils.isBlank(nonce) || StringUtils.isBlank(sign)) {
+            throw new CommonException(ExceptionEnum.PARAM_ERROR.getCode(), "请求缺少签名参数");
+        }
+
+        // 校验时间戳(±5分钟)
+        long requestTime;
+        try {
+            requestTime = Long.parseLong(timestamp);
+        } catch (NumberFormatException e) {
+            throw new CommonException(ExceptionEnum.PARAM_ERROR.getCode(), "时间戳格式错误");
+        }
+        long now = System.currentTimeMillis();
+        if (Math.abs(now - requestTime) > 5 * 60 * 1000) {
+            throw new CommonException(ExceptionEnum.PARAM_ERROR.getCode(), "请求已过期");
+        }
+
+        // 防重放:nonce 只能使用一次
+        String nonceKey = "sign_nonce:" + token + ":" + nonce;
+        boolean success = redisUtils.tryLock(nonceKey, "1", 10 * 60L);
+        if (!success) {
+            log.warn("Replay attack detected - token={}, nonce={}", token, nonce);
+            throw new CommonException(ExceptionEnum.PARAM_ERROR.getCode(), "请求不能重复提交");
+        }
+
+        // 获取 signKey
+        String signKey = redisUtils.getString("sign_key." + token);
+        if (StringUtils.isBlank(signKey)) {
+            throw new CommonException(ExceptionEnum.Not_LOGIN);
+        }
+
+        // 读取 request body
+        String body = getRequestBody(request);
+
+        // 计算预期签名
+        String method = request.getMethod();
+        String uri = request.getServletPath();
+        String payload = method + ":" + uri + ":" + timestamp + ":" + nonce + ":" + body;
+        String expectedSign = hmacSha256(payload, signKey);
+
+        if (!MessageDigest.isEqual(expectedSign.getBytes(StandardCharsets.UTF_8),
+                sign.getBytes(StandardCharsets.UTF_8))) {
+            log.warn("Sign mismatch - uri={}, expected={}, actual={}", uri, expectedSign, sign);
+            throw new CommonException(ExceptionEnum.PARAM_ERROR.getCode(), "签名校验失败");
+        }
+    }
+
+    private String getRequestBody(HttpServletRequest request) {
+        try {
+            byte[] body = StreamUtils.copyToByteArray(request.getInputStream());
+            return new String(body, StandardCharsets.UTF_8);
+        } catch (Exception e) {
+            log.warn("Failed to read request body for sign validation", e);
+            return "";
+        }
+    }
+
+    private String hmacSha256(String data, String key) {
+        try {
+            Mac mac = Mac.getInstance("HmacSHA256");
+            SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
+            mac.init(keySpec);
+            byte[] hash = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
+            return Base64.getEncoder().encodeToString(hash);
+        } catch (Exception e) {
+            throw new RuntimeException("HMAC-SHA256 computation failed", e);
+        }
+    }
+
     private void renewalToken(String token) {
         executor.execute(() -> {
             try {

+ 6 - 12
api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformCooperateAccountController.java

@@ -1,7 +1,6 @@
 package com.tzld.piaoquan.api.controller.contentplatform;
 
-import com.tzld.piaoquan.api.annotation.JwtIgnore;
-import com.tzld.piaoquan.api.job.contentplatform.ContentPlatformGzhAccountJob;
+import com.tzld.piaoquan.api.annotation.RequireSign;
 import com.tzld.piaoquan.api.model.param.IdParam;
 import com.tzld.piaoquan.api.model.param.contentplatform.CooperateAccountListParam;
 import com.tzld.piaoquan.api.model.param.contentplatform.CooperateAccountSaveParam;
@@ -26,17 +25,17 @@ public class ContentPlatformCooperateAccountController {
 
     @Autowired
     ContentPlatformCooperateAccountService cooperateAccountService;
-    @Autowired
-    ContentPlatformGzhAccountJob job;
 
     @ApiOperation(value = "公众号列表")
     @PostMapping("/gzh/list")
+    @RequireSign
     public CommonResponse<Page<CooperateAccountItemVO>> gzhList(@RequestBody CooperateAccountListParam param) {
         return CommonResponse.success(cooperateAccountService.gzhList(param));
     }
 
     @ApiOperation(value = "公众号创建/更新")
     @PostMapping("/gzh/save")
+    @RequireSign
     public CommonResponse<Void> gzhSave(@RequestBody CooperateAccountSaveParam param) {
         cooperateAccountService.gzhSave(param);
         return CommonResponse.success();
@@ -44,12 +43,14 @@ public class ContentPlatformCooperateAccountController {
 
     @ApiOperation(value = "公众号授权二维码获取")
     @PostMapping("/gzh/getAuthQrCode")
+    @RequireSign
     public CommonResponse<GenerateQrcodeVO> getGzhAuthQrCode(@RequestBody GzhAuthResultParam param) {
         return CommonResponse.success(cooperateAccountService.getGzhAuthQrCode(param));
     }
 
     @ApiOperation(value = "公众号授权结果获取")
     @PostMapping("/gzh/getAuthResult")
+    @RequireSign
     public CommonResponse<GzhAuthResultVO> getGzhAuthResult(@RequestBody GzhAuthResultParam param) {
         return CommonResponse.success(cooperateAccountService.getGzhAuthResult(param));
     }
@@ -69,16 +70,9 @@ public class ContentPlatformCooperateAccountController {
 
     @ApiOperation(value = "公众号删除")
     @PostMapping("/gzh/delete")
+    @RequireSign
     public CommonResponse<Void> gzhDelete(@RequestBody IdParam<Long> param) {
         cooperateAccountService.gzhDelete(param.getId());
         return CommonResponse.success();
     }
-
-    @ApiOperation(value = "公众号账号信息同步", hidden = true)
-    @GetMapping("/job/syncGzhAccountInfoJob")
-    @JwtIgnore
-    public CommonResponse<String> syncGzhAccountInfoJob(String id) {
-        job.syncGzhAccountInfoJob(id);
-        return CommonResponse.success();
-    }
 }

+ 5 - 102
api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformDatastatController.java

@@ -1,7 +1,6 @@
 package com.tzld.piaoquan.api.controller.contentplatform;
 
-import com.tzld.piaoquan.api.annotation.JwtIgnore;
-import com.tzld.piaoquan.api.job.contentplatform.ContentPlatformDatastatJob;
+import com.tzld.piaoquan.api.annotation.RequireSign;
 import com.tzld.piaoquan.api.model.param.contentplatform.GzhDatastatListParam;
 import com.tzld.piaoquan.api.model.param.contentplatform.QwDatastatListParam;
 import com.tzld.piaoquan.api.model.vo.contentplatform.GzhDatastatItemVO;
@@ -21,127 +20,31 @@ public class ContentPlatformDatastatController {
     @Autowired
     ContentPlatformDatastatService datastatService;
 
-    @Autowired
-    ContentPlatformDatastatJob job;
-
     @ApiOperation(value = "公众号数据统计")
     @PostMapping("/gzh")
+    @RequireSign
     public CommonResponse<Page<GzhDatastatItemVO>> gzhDatastatList(@RequestBody GzhDatastatListParam param) {
         return CommonResponse.success(datastatService.gzhDatastatList(param));
     }
 
     @ApiOperation(value = "企微数据统计")
     @PostMapping("/qw")
+    @RequireSign
     public CommonResponse<Page<QwDatastatItemVO>> qwDatastatList(@RequestBody QwDatastatListParam param) {
         return CommonResponse.success(datastatService.qwDatastatList(param));
     }
 
     @ApiOperation(value = "公众号数据统计导出")
     @PostMapping("/gzh/export")
+    @RequireSign
     public CommonResponse<String> gzhDatastatExport(@RequestBody GzhDatastatListParam param) {
         return CommonResponse.success(datastatService.gzhDatastatExport(param));
     }
 
     @ApiOperation(value = "企微数据统计导出")
     @PostMapping("/qw/export")
+    @RequireSign
     public CommonResponse<String> qwDatastatExport(@RequestBody QwDatastatListParam param) {
         return CommonResponse.success(datastatService.qwDatastatExport(param));
     }
-
-    @ApiOperation(value = "公众号自动回复分账号数据统计同步任务", hidden = true)
-    @GetMapping("/job/syncGzhDatastat")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformGzhDatastatJob(String dateStr) {
-        job.syncContentPlatformGzhDatastatJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "公众号自动回复分账号分视频数据统计同步任务", hidden = true)
-    @GetMapping("/job/syncGzhVideoDatastat")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformGzhVideoDatastatJob(String dateStr) {
-        job.syncContentPlatformGzhVideoDatastatJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "公众号自动回复总计数据统计同步任务", hidden = true)
-    @GetMapping("/job/syncGzhDatastatTotal")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformGzhDatastatTotalJob(String dateStr) {
-        job.syncContentPlatformGzhDatastatTotalJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "服务号总计数据统计同步任务", hidden = true)
-    @GetMapping("/job/syncFwhDatastatTotal")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformFwhDatastatTotalJob(String dateStr) {
-        job.syncContentPlatformFwhDatastatTotalJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "服务号分账号数据统计同步任务", hidden = true)
-    @GetMapping("/job/syncFwhDatastat")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformFwhDatastatJob(String dateStr) {
-        job.syncContentPlatformFwhDatastatJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "公众号推送总计数据统计同步任务", hidden = true)
-    @GetMapping("/job/syncGzhPushDatastatTotal")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformGzhPushDatastatTotalJob(String dateStr) {
-        job.syncContentPlatformGzhPushDatastatTotalJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "公众号推送分账号数据统计同步任务", hidden = true)
-    @GetMapping("/job/syncGzhPushDatastat")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformGzhPushDatastatJob(String dateStr) {
-        job.syncContentPlatformGzhPushDatastatJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "企微数据统计同步任务", hidden = true)
-    @GetMapping("/job/syncQwDatastat")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformQwDatastatJob(String dateStr) {
-        job.syncContentPlatformQwDatastatJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "企微数据统计总计同步任务", hidden = true)
-    @GetMapping("/job/syncQwDatastatTotal")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformQwDatastatTotalJob(String dateStr) {
-        job.syncContentPlatformQwDatastatTotalJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "企微数据统计自动回复总计同步任务", hidden = true)
-    @GetMapping("/job/syncQwDatastatReplyTotal")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformQwDatastatReplyTotalJob(String dateStr) {
-        job.syncContentPlatformQwDatastatReplyTotalJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "企微数据统计分子渠道同步任务", hidden = true)
-    @GetMapping("/job/syncQwDatastatSubChannel")
-    @JwtIgnore
-    public CommonResponse<String> syncContentPlatformQwDatastatSubChannelJob(String dateStr) {
-        job.syncContentPlatformQwDatastatSubChannelJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "企微合作账号层级类型同步任务", hidden = true)
-    @GetMapping("/job/syncQwHzAccountLayerTypeJob")
-    @JwtIgnore
-    public CommonResponse<String> syncQwHzAccountLayerTypeJob(String dateStr) {
-        job.syncQwHzAccountLayerTypeJob(dateStr);
-        return CommonResponse.success();
-    }
-
 }

+ 5 - 13
api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformNoticeController.java

@@ -1,7 +1,6 @@
 package com.tzld.piaoquan.api.controller.contentplatform;
 
-import com.tzld.piaoquan.api.annotation.JwtIgnore;
-import com.tzld.piaoquan.api.job.contentplatform.ContentPlatformIllegalVideoJob;
+import com.tzld.piaoquan.api.annotation.RequireSign;
 import com.tzld.piaoquan.api.model.param.contentplatform.NoticeListParam;
 import com.tzld.piaoquan.api.model.param.contentplatform.NoticeReadParam;
 import com.tzld.piaoquan.api.model.vo.contentplatform.NoticeItemVO;
@@ -20,23 +19,23 @@ public class ContentPlatformNoticeController {
     @Autowired
     ContentPlatformNoticeService noticeService;
 
-    @Autowired
-    private ContentPlatformIllegalVideoJob job;
-
     @ApiOperation(value = "通知列表")
     @PostMapping("/list")
+    @RequireSign
     public CommonResponse<Page<NoticeItemVO>> noticeList(@RequestBody NoticeListParam param) {
         return CommonResponse.success(noticeService.noticeList(param));
     }
 
     @ApiOperation(value = "获取未读通知数量")
     @PostMapping("/getNotReadCount")
+    @RequireSign
     public CommonResponse<Long> getNotReadCount() {
         return CommonResponse.success(noticeService.getNotReadCount());
     }
 
     @ApiOperation(value = "通知已读")
     @PostMapping("/read")
+    @RequireSign
     public CommonResponse<Void> noticeRead(@RequestBody NoticeReadParam param) {
         noticeService.noticeRead(param);
         return CommonResponse.success();
@@ -44,16 +43,9 @@ public class ContentPlatformNoticeController {
 
     @ApiOperation(value = "一键全部已读")
     @PostMapping("/readAll")
+    @RequireSign
     public CommonResponse<Void> noticeReadAll() {
         noticeService.noticeReadAll();
         return CommonResponse.success();
     }
-
-    @ApiOperation(value = "检查违规视频发送通知", hidden = true)
-    @JwtIgnore
-    @GetMapping("/job/checkContentPlatformIllegalVideoJob")
-    public CommonResponse<Void> checkContentPlatformIllegalVideoJob(String dateStr) {
-        job.checkContentPlatformIllegalVideoJob(dateStr);
-        return CommonResponse.success();
-    }
 }

+ 19 - 65
api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformPlanController.java

@@ -2,10 +2,7 @@ package com.tzld.piaoquan.api.controller.contentplatform;
 
 import com.tzld.piaoquan.api.annotation.JwtIgnore;
 import com.tzld.piaoquan.api.annotation.RateLimit;
-import com.tzld.piaoquan.api.common.enums.ExceptionEnum;
-import com.tzld.piaoquan.api.common.exception.CommonException;
-import com.tzld.piaoquan.api.job.contentplatform.ContentPlatformVideoJob;
-import com.tzld.piaoquan.api.job.contentplatform.ContentPlatformDemandVideoJob;
+import com.tzld.piaoquan.api.annotation.RequireSign;
 import com.tzld.piaoquan.api.model.param.IdParam;
 import com.tzld.piaoquan.api.model.param.contentplatform.*;
 import com.tzld.piaoquan.api.model.vo.contentplatform.GzhPlanItemVO;
@@ -29,20 +26,16 @@ public class ContentPlatformPlanController {
     @Autowired
     ContentPlatformPlanService planService;
 
-    @Autowired
-    private ContentPlatformVideoJob videoJob;
-
-    @Autowired
-    private ContentPlatformDemandVideoJob demandVideoJob;
-
     @ApiOperation(value = "公众号计划列表")
     @PostMapping("/gzh/list")
+    @RequireSign
     public CommonResponse<Page<GzhPlanItemVO>> gzhPlanList(@RequestBody GzhPlanListParam param) {
         return CommonResponse.success(planService.gzhPlanList(param));
     }
 
     @ApiOperation(value = "公众号计划 创建/更新")
     @PostMapping("/gzh/save")
+    @RequireSign
     public CommonResponse<Void> gzhPlanSave(@RequestBody GzhPlanSaveParam param) {
         planService.gzhPlanSave(param);
         return CommonResponse.success();
@@ -50,6 +43,7 @@ public class ContentPlatformPlanController {
 
     @ApiOperation(value = "公众号计划 删除")
     @PostMapping("/gzh/delete")
+    @RequireSign
     public CommonResponse<Void> gzhPlanDelete(@RequestBody IdParam<Long> param) {
         planService.gzhPlanDelete(param.getId());
         return CommonResponse.success();
@@ -57,6 +51,7 @@ public class ContentPlatformPlanController {
 
     @ApiOperation(value = "公众号计划 平台发布开关修改")
     @PostMapping("/gzh/updateStagePublishStatus")
+    @RequireSign
     public CommonResponse<Void> gzhUpdateStagePublishStatus(@RequestBody GzhPlanStagePublishStatusUpdateParam param) {
         planService.gzhUpdateStagePublishStatus(param.getId(), param.getStagePublishStatus());
         return CommonResponse.success();
@@ -65,6 +60,7 @@ public class ContentPlatformPlanController {
     @ApiOperation(value = "发布内容视频列表")
     @PostMapping("/videoContentList")
     @RateLimit(timeWindow = 30, maxRequests = 10, limitByAccount = true, message = "请求过于频繁,请稍后再试")
+    @RequireSign
     public CommonResponse<Page<VideoContentItemVO>> getVideoContentList(@RequestBody VideoContentListParam param) {
         return CommonResponse.success(planService.getVideoContentList(param));
     }
@@ -72,6 +68,7 @@ public class ContentPlatformPlanController {
     @ApiOperation(value = "向量匹配视频列表")
     @PostMapping("/vector/videoContentList")
     @RateLimit(timeWindow = 30, maxRequests = 10, limitByAccount = true, message = "请求过于频繁,请稍后再试")
+    @RequireSign
     public CommonResponse<Page<VideoContentItemVO>> getVectorVideoContentList(@RequestBody VideoContentListParam param) {
         return CommonResponse.success(planService.getVectorVideoContentList(param));
     }
@@ -79,6 +76,7 @@ public class ContentPlatformPlanController {
     @ApiOperation(value = "个人上传发布内容视频列表")
     @PostMapping("/upload/videoContentList")
     @RateLimit(timeWindow = 30, maxRequests = 10, limitByAccount = true, message = "请求过于频繁,请稍后再试")
+    @RequireSign
     public CommonResponse<Page<VideoContentItemVO>> getUploadVideoContentList(@RequestBody VideoContentListParam param) {
         return CommonResponse.success(planService.getUploadVideoContentList(param));
     }
@@ -86,6 +84,7 @@ public class ContentPlatformPlanController {
     @ApiOperation(value = "需求匹配视频列表")
     @PostMapping("/demand/videoContentList")
     @RateLimit(timeWindow = 30, maxRequests = 10, limitByAccount = true, message = "请求过于频繁,请稍后再试")
+    @RequireSign
     public CommonResponse<Page<VideoContentItemVO>> getDemandVideoContentList(@RequestBody VideoContentListParam param) {
         return CommonResponse.success(planService.getDemandVideoContentList(param));
     }
@@ -98,30 +97,35 @@ public class ContentPlatformPlanController {
 
     @ApiOperation(value = "发布内容视频封面截帧列表")
     @PostMapping("/videoContentCoverFrameList")
+    @RequireSign
     public CommonResponse<List<String>> getVideoContentCoverFrameList(@RequestBody VideoContentCoverFrameParam param) {
         return CommonResponse.success(planService.getVideoContentCoverFrameList(param));
     }
 
     @ApiOperation(value = "企微计划列表")
     @PostMapping("/qw/list")
+    @RequireSign
     public CommonResponse<Page<QwPlanItemVO>> qwPlanList(@RequestBody QwPlanListParam param) {
         return CommonResponse.success(planService.qwPlanList(param));
     }
 
     @ApiOperation(value = "企微计划列表导出")
     @PostMapping("/qw/export")
+    @RequireSign
     public CommonResponse<String> qwPlanExport(@RequestBody QwPlanListParam param) {
         return CommonResponse.success(planService.qwPlanExport(param));
     }
 
     @ApiOperation(value = "企微计划 创建/更新")
     @PostMapping("/qw/save")
+    @RequireSign
     public CommonResponse<List<QwPlanItemVO>> qwPlanSave(@RequestBody QwPlanSaveParam param) {
         return CommonResponse.success(planService.qwPlanSave(param));
     }
 
     @ApiOperation(value = "企微计划 删除")
     @PostMapping("/qw/delete")
+    @RequireSign
     public CommonResponse<Void> qwPlanDelete(@RequestBody IdParam<Long> param) {
         planService.qwPlanDelete(param.getId());
         return CommonResponse.success();
@@ -135,24 +139,28 @@ public class ContentPlatformPlanController {
 
     @ApiOperation(value = "小程序计划列表")
     @PostMapping("/xcx/list")
+    @RequireSign
     public CommonResponse<Page<XcxPlanItemVO>> xcxPlanList(@RequestBody XcxPlanListParam param) {
         return CommonResponse.success(planService.xcxPlanList(param));
     }
 
     @ApiOperation(value = "小程序计划列表导出")
     @PostMapping("/xcx/export")
+    @RequireSign
     public CommonResponse<String> xcxPlanExport(@RequestBody XcxPlanListParam param) {
         return CommonResponse.success(planService.xcxPlanExport(param));
     }
 
     @ApiOperation(value = "小程序计划 创建/更新")
     @PostMapping("/xcx/save")
+    @RequireSign
     public CommonResponse<List<XcxPlanItemVO>> xcxPlanSave(@RequestBody XcxPlanSaveParam param) {
         return CommonResponse.success(planService.xcxPlanSave(param));
     }
 
     @ApiOperation(value = "小程序计划 删除")
     @PostMapping("/xcx/delete")
+    @RequireSign
     public CommonResponse<Void> xcxPlanDelete(@RequestBody IdParam<Long> param) {
         planService.xcxPlanDelete(param.getId());
         return CommonResponse.success();
@@ -160,6 +168,7 @@ public class ContentPlatformPlanController {
 
     @ApiOperation(value = "小程序计划 多链接生成(同 video 复制 N 份)")
     @PostMapping("/xcx/multiLink")
+    @RequireSign
     public CommonResponse<List<XcxPlanItemVO>> xcxPlanMultiLink(@RequestBody XcxPlanMultiLinkParam param) {
         return CommonResponse.success(planService.xcxPlanMultiLink(param));
     }
@@ -188,59 +197,4 @@ public class ContentPlatformPlanController {
     public CommonResponse<String> getShareUrlLink(@RequestParam String pageUrl) {
         return CommonResponse.success(planService.getShareUrlLink(pageUrl));
     }
-
-    @ApiOperation(value = "同步最近top1000视频", hidden = true)
-    @JwtIgnore
-    @GetMapping("/job/syncContentPlatformVideoJob")
-    public CommonResponse<Void> syncContentPlatformVideoJob(String dateStr) {
-        videoJob.syncContentPlatformVideoJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "检查视频状态", hidden = true)
-    @JwtIgnore
-    @GetMapping("/job/checkContentPlatformVideoStatusJob")
-    public CommonResponse<Void> checkContentPlatformVideoStatusJob(String dateStr) {
-        videoJob.checkContentPlatformVideoStatusJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "同步最近top1000视频统计数据", hidden = true)
-    @JwtIgnore
-    @GetMapping("/job/syncContentPlatformVideoDatastatJob")
-    public CommonResponse<Void> syncContentPlatformVideoDatastatJob(String dateStr) {
-        videoJob.syncContentPlatformVideoDatastatJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "同步最近top1000视频统计数据-推荐指数", hidden = true)
-    @JwtIgnore
-    @GetMapping("/job/calContentPlatformVideoDatastatAggRecommendRateJob")
-    public CommonResponse<Void> calContentPlatformVideoDatastatAggRecommendRateJob(String dateStr) {
-        videoJob.calContentPlatformVideoDatastatAggRecommendRateJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "公众号推送-检查rootSourceId是否存在", hidden = true)
-    @JwtIgnore
-    @GetMapping("/gzhPush/checkExistRootSourceId")
-    public CommonResponse<Boolean> gzhPushCheckExistRootSourceId(String rootSourceId) {
-        return CommonResponse.success(planService.gzhPushCheckExistRootSourceId(rootSourceId));
-    }
-
-    @ApiOperation(value = "同步需求匹配视频数据", hidden = true)
-    @JwtIgnore
-    @GetMapping("/job/syncContentPlatformDemandVideoJob")
-    public CommonResponse<Void> syncContentPlatformDemandVideoJob(String dateStr) {
-        demandVideoJob.syncContentPlatformDemandVideoJob(dateStr);
-        return CommonResponse.success();
-    }
-
-    @ApiOperation(value = "检查需求匹配视频状态", hidden = true)
-    @JwtIgnore
-    @GetMapping("/job/checkContentPlatformDemandVideoStatusJob")
-    public CommonResponse<Void> checkContentPlatformDemandVideoStatusJob() {
-        demandVideoJob.checkContentPlatformDemandVideoStatusJob(null);
-        return CommonResponse.success();
-    }
 }

+ 2 - 0
api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformSettingController.java

@@ -1,5 +1,6 @@
 package com.tzld.piaoquan.api.controller.contentplatform;
 
+import com.tzld.piaoquan.api.annotation.RequireSign;
 import com.tzld.piaoquan.api.model.param.contentplatform.WxWebLoginParam;
 import com.tzld.piaoquan.api.model.vo.contentplatform.WxBaseUserInfoVO;
 import com.tzld.piaoquan.api.service.contentplatform.ContentPlatformSettingService;
@@ -19,6 +20,7 @@ public class ContentPlatformSettingController {
 
     @ApiOperation(value = "票圈账号绑定")
     @PostMapping(value = "/webLogin")
+    @RequireSign
     public CommonResponse<WxBaseUserInfoVO> wxLoginByWeb(@Validated WxWebLoginParam param) {
         return CommonResponse.success(service.wxLoginByWeb(param));
     }

+ 6 - 20
api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/ContentPlatformUploadContentController.java

@@ -1,7 +1,7 @@
 package com.tzld.piaoquan.api.controller.contentplatform;
 
 import com.tzld.piaoquan.api.annotation.JwtIgnore;
-import com.tzld.piaoquan.api.job.contentplatform.ContentPlatformVideoJob;
+import com.tzld.piaoquan.api.annotation.RequireSign;
 import com.tzld.piaoquan.api.model.param.contentplatform.DeleteVideoParam;
 import com.tzld.piaoquan.api.model.param.contentplatform.PublishVideoParam;
 import com.tzld.piaoquan.api.model.param.contentplatform.UploadVideoListParam;
@@ -21,23 +21,24 @@ public class ContentPlatformUploadContentController {
 
     @Autowired
     ContentPlatformUploadContentService service;
-    @Autowired
-    ContentPlatformVideoJob job;
 
     @ApiOperation(value = "发布视频")
     @PostMapping("/publishVideo")
+    @RequireSign
     public CommonResponse<UploadVideoItemVO> publishVideo(@RequestBody PublishVideoParam param) {
         return CommonResponse.success(service.publishVideo(param));
     }
 
     @ApiOperation(value = "修改视频")
     @PostMapping("/updateVideo")
+    @RequireSign
     public CommonResponse<UploadVideoItemVO> updateVideo(@RequestBody PublishVideoParam param) {
         return CommonResponse.success(service.updateVideo(param));
     }
 
     @ApiOperation(value = "删除视频")
     @PostMapping("/deleteVideo")
+    @RequireSign
     public CommonResponse<Void> deleteVideo(@RequestBody DeleteVideoParam param) {
         service.deleteVideo(param);
         return CommonResponse.success();
@@ -47,30 +48,15 @@ public class ContentPlatformUploadContentController {
     @PostMapping("/callback/videoAudit")
     @JwtIgnore
     public CommonResponse<Void> videoAuditCallBack(@RequestBody VideoAuditCallBackParam param) {
-        service.videoAuditCallBack(param);
+//        service.videoAuditCallBack(param);
         return CommonResponse.success();
     }
 
     @ApiOperation(value = "上传视频列表")
     @PostMapping("/list")
+    @RequireSign
     public CommonResponse<Page<UploadVideoItemVO>> uploadVideoList(@RequestBody UploadVideoListParam param) {
         return CommonResponse.success(service.uploadVideoList(param));
     }
 
-    @JwtIgnore
-    @ApiOperation(value = "同步手动上传视频裂变分", hidden = true)
-    @GetMapping("/job/syncContentPlatformUploadVideoScoreJob")
-    public CommonResponse<Void> syncContentPlatformUploadVideoScoreJob() {
-        job.syncContentPlatformUploadVideoScoreJob(null);
-        return CommonResponse.success();
-    }
-
-    @JwtIgnore
-    @ApiOperation(value = "检查手动上传视频状态", hidden = true)
-    @GetMapping("/job/checkContentPlatformUploadVideoStatusJob")
-    public CommonResponse<Void> checkContentPlatformUploadVideoStatusJob() {
-        job.checkContentPlatformUploadVideoStatusJob(null);
-        return CommonResponse.success();
-    }
-
 }

+ 2 - 0
api-module/src/main/java/com/tzld/piaoquan/api/controller/contentplatform/LoghubController.java

@@ -1,6 +1,7 @@
 package com.tzld.piaoquan.api.controller.contentplatform;
 
 
+import com.tzld.piaoquan.api.annotation.RequireSign;
 import com.tzld.piaoquan.api.model.param.contentplatform.LogUploadParam;
 import com.tzld.piaoquan.api.service.LoghubService;
 import com.tzld.piaoquan.growth.common.common.base.CommonResponse;
@@ -16,6 +17,7 @@ public class LoghubController {
     LoghubService loghubService;
 
     @PostMapping("/upload")
+    @RequireSign
     public CommonResponse<Void> logUpload(@RequestBody LogUploadParam param) {
         loghubService.logUploadByKey(param.getData(), param.getKey());
         return CommonResponse.success();

+ 28 - 0
api-module/src/main/java/com/tzld/piaoquan/api/filter/ContentCachingFilter.java

@@ -0,0 +1,28 @@
+package com.tzld.piaoquan.api.filter;
+
+import org.springframework.core.Ordered;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+import org.springframework.web.util.ContentCachingRequestWrapper;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * 包装请求以支持重复读取 body(签名校验需要)
+ */
+@Component
+@Order(Ordered.HIGHEST_PRECEDENCE)
+public class ContentCachingFilter extends OncePerRequestFilter {
+
+    @Override
+    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
+                                    FilterChain filterChain) throws ServletException, IOException {
+        ContentCachingRequestWrapper wrapper = new ContentCachingRequestWrapper(request);
+        filterChain.doFilter(wrapper, response);
+    }
+}

+ 2 - 0
api-module/src/main/java/com/tzld/piaoquan/api/model/vo/contentplatform/AccountLoginVO.java

@@ -26,6 +26,8 @@ public class AccountLoginVO {
     private Long createTimestamp;
     @ApiModelProperty(value = "token")
     private String token;
+    @ApiModelProperty(value = "请求签名密钥,前端存内存,用于防重放")
+    private String signKey;
     @ApiModelProperty(value = "层级类型:特殊首层/非特殊首层")
     private String layerType;
 

+ 5 - 0
api-module/src/main/java/com/tzld/piaoquan/api/service/contentplatform/impl/ContentPlatformAccountServiceImpl.java

@@ -77,12 +77,17 @@ public class ContentPlatformAccountServiceImpl implements ContentPlatformAccount
             verifyCodeMapper.updateByPrimaryKeySelective(verifyCode);
         }
         String token = UUID.randomUUID().toString().replace("-", "");
+        String signKey = UUID.randomUUID().toString().replace("-", "");
 
         AccountLoginVO result = new AccountLoginVO();
         BeanUtils.copyProperties(account, result);
         result.setToken(token);
+        result.setSignKey(signKey);
 
         saveTokenToRedis(result, account.getToken(), token);
+        // 保存 signKey 到 Redis,与 token 同生命周期(7天)
+        String signKeyRedisKey = "sign_key." + token;
+        redisUtils.setValueWithExpire(signKeyRedisKey, signKey, 7L * 24 * 60 * 60);
         account.setToken(token);
         account.setTokenExpireTimestamp(now + 7 * 24 * 60 * 60 * 1000L);
         accountMapper.updateByPrimaryKeySelective(account);

+ 0 - 1
api-module/src/main/java/com/tzld/piaoquan/api/service/wecom/impl/WeComServiceImpl.java

@@ -57,7 +57,6 @@ public class WeComServiceImpl implements WeComService {
         strategyServiceMap = applicationContext.getBeansOfType(ReplyStrategyService.class);
     }
 
-    //TODO 后续自动回复迁移完成可以下掉
     @Override
     public CommonResponse<List<WeComPushMessageVo>> getPushMessage(WeComPushMessageParam param) {
         if (param == null || StringUtils.isEmpty(param.getCorpId()) || !CORP_ID.equals(param.getCorpId()) || StringUtils.isEmpty(param.getUserId())) {

+ 0 - 4
api-module/src/main/java/com/tzld/piaoquan/api/util/AliOssFileTool.java

@@ -1231,8 +1231,6 @@ public class AliOssFileTool extends AliOssConfig {
             try {
                 ossInternalClient.restoreObject(bucket, srckey);
             } catch (Exception e) {
-                // TODO OSS Auto-generated catch blockvideo-common/src/main/java/com/weiqu/video/common/enums/ExceptionCodeEnum.java
-//				e.printStackTrace();
                 logger.error("视频可能在解冻中");
             }
         } else if (storageClass == StorageClass.ColdArchive) {
@@ -1253,8 +1251,6 @@ public class AliOssFileTool extends AliOssConfig {
         try {
             isRestoreCompleted = objectMetadata.isRestoreCompleted();
         } catch (Exception e) {
-            // TODO OSS Auto-generated catch block
-//			e.printStackTrace();
             logger.error("视频可能在解冻中");
         }