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

改造sop发送飞书链接逻辑3

cgp 6 дней назад
Родитель
Сommit
af908b4fb5
1 измененных файлов с 170 добавлено и 153 удалено
  1. 170 153
      fs-service/src/main/java/com/fs/feishu/service/FeiShuService.java

+ 170 - 153
fs-service/src/main/java/com/fs/feishu/service/FeiShuService.java

@@ -1,13 +1,13 @@
 package com.fs.feishu.service;
 
 import cn.hutool.json.JSONUtil;
+import com.fs.feishu.config.FeiShuConfig;
+import com.fs.feishu.model.FeishuFileDTO;
+import com.fs.feishu.model.FeishuLinkParam;
 import com.fs.common.exception.CustomException;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
-import com.fs.feishu.config.FeiShuConfig;
-import com.fs.feishu.model.FeishuFileDTO;
-import com.fs.feishu.model.FeishuLinkParam;
 import com.fs.system.service.ISysConfigService;
 import com.lark.oapi.Client;
 import com.lark.oapi.service.docx.v1.model.*;
@@ -20,27 +20,21 @@ import com.lark.oapi.service.drive.v2.model.PermissionPublic;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
 import javax.annotation.PostConstruct;
 import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.*;
 
-/**
- * 飞书云文档服务
- * 负责生成飞书看课相关的注册文档和课程文档
- */
+import org.springframework.stereotype.Component;
+
 @Component
 @Slf4j
 public class FeiShuService {
 
     @Autowired
     private FeiShuConfig feishuConfig;
-
     @Autowired
     private ISysConfigService configService;
-
     @Autowired
     private FsUserCourseVideoMapper videoMapper;
 
@@ -48,197 +42,220 @@ public class FeiShuService {
 
     @PostConstruct
     public void init() {
-        this.client = Client.newBuilder(feishuConfig.getAppId(), feishuConfig.getAppSecret())
-                .logReqAtDebug(true)
-                .build();
+        this.client = Client.newBuilder(feishuConfig.getAppId(), feishuConfig.getAppSecret()).logReqAtDebug(true).build();
     }
 
-    // ==================== 对外业务方法 ====================
-
     /**
-     * 生成注册授权页飞书文档(第一阶段)
+     * 001 生成飞书初始链接
      */
-    public String getFeishuRegisterLink(Long videoId, Long companyId, Long courseId,
-                                        Long companyUserId, String shortCode) {
-        CourseConfig config = getCourseConfig();
-        FsUserCourseVideo video = getVideoEntity(videoId);
+    public String getFeishuRegisterLink(Long videoId, Long companyId, Long courseId,Long companyUserId,String shortCode) {
+        // 读取配置判断是否启用飞书新跳转
+        String json = configService.selectConfigByKey("course.config");
+        if (StringUtils.isBlank(json)) {
+            throw new CustomException("飞书看课配置为空");
+        }
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+        Boolean enableFeishuNewLink = config.getEnableFeishuNewLink();
+        if (!Boolean.TRUE.equals(enableFeishuNewLink)) {
+            throw new CustomException("未开启飞书看课");
+        }
 
-        // 1. 构建授权URL
-        String authUrl = buildAuthUrl(config, companyId, companyUserId, courseId, videoId, shortCode);
+        // 构建参数
+        FeishuLinkParam param = new FeishuLinkParam();
+        param.setCompanyId(companyId);
+        param.setCompanyUserId(companyUserId);
+        param.setCourseId(courseId);
+        param.setVideoId(videoId);
 
-        // 2. 构造文档内容
-        Block linkBlock = buildTextLinkBlock(authUrl, "观看课程");
+        FsUserCourseVideo userCourseVideo = videoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
+        if (userCourseVideo == null) {
+            throw new CustomException("视频不存在: " + param.getVideoId());
+        }
+
+        try {
+            // 创建云文档
+            String documentId = createDocument(userCourseVideo.getTitle() + "-注册");
+
+            // 拼接授权url
+            String authUrl = buildAuthLink(param.getCompanyId(), param.getCompanyUserId(),
+                    param.getCourseId(), param.getVideoId(),shortCode);
 
-        // 3. 创建文档并返回链接
-        String documentId = createFeishuDocument(video.getTitle() + "-注册", new Block[]{linkBlock});
-        return "https://www.feishu.cn/docx/" + documentId;
+            // 创建关注公众号 text + link block
+            createTextLinkBlock(documentId, authUrl);
+            // 更新文档权限
+            changeDocumentPermissions(documentId);
+
+            // 返回飞书看课链接
+            return "https://www.feishu.cn/docx/" + documentId;
+        } catch (Exception e) {
+            throw new CustomException("创建飞书注册链接失败: " + e.getMessage(), e);
+        }
     }
 
+
+
     /**
-     * 生成课程播放页飞书文档(第二阶段)
+     * 002生成飞书课程链接
      */
     public String getFeishuCourseNewLink(FeishuLinkParam param) {
         if (param == null || param.getCompanyUserId() == null || param.getCompanyId() == null) {
             throw new CustomException("参数不能为空");
         }
 
-        CourseConfig config = getCourseConfig();
-        FsUserCourseVideo video = getVideoEntity(param.getVideoId());
-
-        // 1. 构建课程播放URL(已双重编码)
-        String courseUrl = buildCourseUrl(config, param);
-
-        // 2. 构造文档内容(一个iframe块)
-        Block iframeBlock = buildIframeBlock(courseUrl);
+        FsUserCourseVideo userCourseVideo = videoMapper.selectFsUserCourseVideoByVideoId(param.getVideoId());
+        if (userCourseVideo == null) {
+            throw new CustomException("视频不存在: " + param.getVideoId());
+        }
+        try {
+            // 创建云文档
+            String documentId = createDocument(userCourseVideo.getTitle());
 
-        // 3. 创建文档并返回链接
-        String documentId = createFeishuDocument(video.getTitle(), new Block[]{iframeBlock});
-        return "https://www.feishu.cn/docx/" + documentId;
-    }
+            // 拼接新看课url
+            String url = buildCourseNewLink(param.getCompanyId(), param.getCompanyUserId(), param.getCourseId(), param.getVideoId(),param.getUserId());
 
-    // ==================== 核心文档创建流程 ====================
+            // 创建iframe块
+            createIframeBlock(documentId, url);
 
-    /**
-     * 通用文档创建流程:创建文档 → 添加子块 → 设置权限
-     */
-    private String createFeishuDocument(String title, Block[] children) {
-        try {
-            String documentId = createDocument(title);
-            addDocumentChildren(documentId, children);
+            // 更新文档权限
             changeDocumentPermissions(documentId);
-            return documentId;
+
+            // 返回飞书看课链接
+            return "https://www.feishu.cn/docx/" + documentId;
         } catch (Exception e) {
-            throw new CustomException("创建飞书文档失败: " + e.getMessage(), e);
+            throw new CustomException("创建飞书课程链接失败: " + e.getMessage(), e);
         }
     }
 
-    // ==================== 文档内容块构建 ====================
-
-    private Block buildTextLinkBlock(String url, String text) {
-        return Block.newBuilder()
-                .blockType(2) // 文本块
-                .text(Text.newBuilder()
-                        .elements(new TextElement[]{
-                                TextElement.newBuilder()
-                                        .textRun(TextRun.newBuilder()
-                                                .content(text)
-                                                .textElementStyle(TextElementStyle.newBuilder()
-                                                        .link(Link.newBuilder().url(url).build())
-                                                        .build())
-                                                .build())
-                                        .build()
-                        })
-                        .build())
-                .build();
-    }
-
-    private Block buildIframeBlock(String url) {
-        return Block.newBuilder()
-                .blockType(26) // iframe块
-                .iframe(Iframe.newBuilder()
-                        .component(IframeComponent.newBuilder()
-                                .iframeType(1)
-                                .url(url)
-                                .build())
-                        .build())
-                .build();
-    }
-
-    // ==================== 飞书API操作封装 ====================
 
+    /**
+     * 创建云文档
+     */
     private String createDocument(String title) throws Exception {
         CreateDocumentReq req = CreateDocumentReq.newBuilder()
                 .createDocumentReqBody(CreateDocumentReqBody.newBuilder().title(title).build())
                 .build();
-        return client.docx().v1().document().create(req)
-                .getData().getDocument().getDocumentId();
-    }
+        CreateDocumentResp resp = client.docx().v1().document().create(req);
 
-    private void addDocumentChildren(String documentId, Block[] children) throws Exception {
-        CreateDocumentBlockChildrenReq req = CreateDocumentBlockChildrenReq.newBuilder()
-                .documentId(documentId)
-                .blockId(documentId)
-                .documentRevisionId(-1)
-                .createDocumentBlockChildrenReqBody(
-                        CreateDocumentBlockChildrenReqBody.newBuilder()
-                                .index(0)
-                                .children(children)
-                                .build()
-                )
-                .build();
-        client.docx().v1().documentBlockChildren().create(req);
+        CreateDocumentRespBody data = resp.getData();
+        return data.getDocument().getDocumentId();
     }
 
-    // ==================== URL构建工具 ====================
-
-    private String buildAuthUrl(CourseConfig config, Long companyId, Long companyUserId,
-                                Long courseId, Long videoId, String shortCode) {
-        Map<String, Object> params = new LinkedHashMap<>();
-        params.put("companyUserId", companyUserId);
-        params.put("videoId", videoId);
-        params.put("companyId", companyId);
-        params.put("courseId", courseId);
-        params.put("link", shortCode);
-        String fullUrl = config.getFeishuAuthDomain()
-                + "/feishuCourse/pages_course/wxauth?course=" + encodeJsonParam(params);
-        return encodeUrl(fullUrl);
-    }
-
-    private String buildCourseUrl(CourseConfig config, FeishuLinkParam param) {
-        Map<String, Object> params = new LinkedHashMap<>();
-        params.put("companyUserId", param.getCompanyUserId());
-        params.put("videoId", param.getVideoId());
-        params.put("companyId", param.getCompanyId());
-        params.put("courseId", param.getCourseId());
-        String fullUrl = config.getFeishuCourseDomain()
-                + "/feishuCourse/pages_course/video?course=" + encodeJsonParam(params)
-                + "&userId=" + param.getUserId();
-        return encodeUrl(fullUrl);
-    }
 
     /**
-     * 将参数Map编码为JSON并URL编码(第一次编码)
+     * 拼接看课url(课程参数JSON方式,适配飞书iframe,双重URL编码)
      */
-    private String encodeJsonParam(Map<String, Object> params) {
+    private String buildCourseNewLink(Long companyId, Long companyUserId, Long courseId, Long videoId,Long userId) {
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+
+        // 构造课程参数 JSON
+        Map<String, Object> courseParam = new LinkedHashMap<>();
+        courseParam.put("companyUserId", companyUserId);
+        courseParam.put("videoId", videoId);
+        courseParam.put("companyId", companyId);
+        courseParam.put("courseId", courseId);
+        String courseJson = JSONUtil.toJsonStr(courseParam);
         try {
-            String json = JSONUtil.toJsonStr(params);
-            return URLEncoder.encode(json, "UTF-8");
+            // 第一次编码:对JSON进行URL编码
+            String encodedJson = URLEncoder.encode(courseJson, "UTF-8");
+            // 拼接完整URL(userId与course参数同级)
+            String baseUrl = config.getFeishuCourseDomain();
+            String fullUrl = baseUrl + "/feishuCourse/pages_course/video?course=" + encodedJson + "&userId=" +userId;
+            // 第二次编码:对整个URL进行URL编码(适配飞书iframe创建要求)
+            return URLEncoder.encode(fullUrl, "UTF-8");
         } catch (UnsupportedEncodingException e) {
-            throw new CustomException("参数JSON编码失败", e);
+            throw new CustomException("看课URL拼接失败", e);
         }
     }
 
     /**
-     * 对整个URL进行URL编码(第二次编码,用于iframe)
+     * 拼接授权url(课程参数JSON方式,适配飞书iframe,双重URL编码
      */
-    private String encodeUrl(String url) {
+    private String buildAuthLink(Long companyId, Long companyUserId, Long courseId, Long videoId,String shortCode) {
+        String json = configService.selectConfigByKey("course.config");
+        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
+
+        Map<String, Object> authParam = new LinkedHashMap<>();
+        authParam.put("companyUserId", companyUserId);
+        authParam.put("videoId", videoId);
+        authParam.put("companyId", companyId);
+        authParam.put("courseId", courseId);
+        authParam.put("link", shortCode);
+        String authJson = JSONUtil.toJsonStr(authParam);
+
         try {
-            return URLEncoder.encode(url, "UTF-8");
+            String encodedJson = URLEncoder.encode(authJson, "UTF-8");
+            String baseUrl =config.getFeishuAuthDomain();
+            String fullUrl = baseUrl + "/feishuCourse/pages/wxauth?course=" + encodedJson;
+            return URLEncoder.encode(fullUrl, "UTF-8");
         } catch (UnsupportedEncodingException e) {
-            throw new CustomException("URL编码失败", e);
+            throw new CustomException("授权URL拼接失败", e);
         }
     }
 
-    // ==================== 配置与数据获取 ====================
+    /**
+     * 创建iframe块
+     */
+    private void createIframeBlock(String documentId, String url) throws Exception {
+        CreateDocumentBlockChildrenReq req = CreateDocumentBlockChildrenReq.newBuilder()
+                .documentId(documentId)
+                .blockId(documentId)
+                .documentRevisionId(-1)
+                .createDocumentBlockChildrenReqBody(CreateDocumentBlockChildrenReqBody.newBuilder()
+                        .children(new Block[] {
+                                Block.newBuilder()
+                                        .blockType(26)
+                                        .iframe(Iframe.newBuilder()
+                                                .component(IframeComponent.newBuilder()
+                                                        .iframeType(1)
+                                                        .url(url).build()
+                                                ).build()
+                                        )
+                                        .build()
+                        })
+                        .index(0)
+                        .build())
+                .build();
 
-    private CourseConfig getCourseConfig() {
-        String json = configService.selectConfigByKey("course.config");
-        if (StringUtils.isBlank(json)) {
-            throw new CustomException("飞书看课配置为空");
-        }
-        CourseConfig config = JSONUtil.toBean(json, CourseConfig.class);
-        if (!Boolean.TRUE.equals(config.getEnableFeishuNewLink())) {
-            throw new CustomException("未开启飞书看课");
-        }
-        return config;
+        // 发起请求
+        client.docx().v1().documentBlockChildren().create(req);
     }
 
-    private FsUserCourseVideo getVideoEntity(Long videoId) {
-        FsUserCourseVideo video = videoMapper.selectFsUserCourseVideoByVideoId(videoId);
-        if (video == null) {
-            throw new CustomException("视频不存在: " + videoId);
-        }
-        return video;
+    /**
+     * 创建 text + link block
+     */
+    private void createTextLinkBlock(String documentId, String url) throws Exception {
+
+        CreateDocumentBlockChildrenReq req = CreateDocumentBlockChildrenReq.newBuilder()
+                .documentId(documentId)
+                .blockId(documentId)
+                .documentRevisionId(-1)
+                .createDocumentBlockChildrenReqBody(
+                        CreateDocumentBlockChildrenReqBody.newBuilder()
+                                .index(0)
+                                .children(new Block[] {
+                                        Block.newBuilder()
+                                                .blockType(2)
+                                                .text(Text.newBuilder()
+                                                        .elements(new TextElement[] {
+                                                                TextElement.newBuilder()
+                                                                        .textRun(TextRun.newBuilder()
+                                                                                .content("观看课程")
+                                                                                .textElementStyle(TextElementStyle.newBuilder()
+                                                                                        .link(Link.newBuilder()
+                                                                                                .url(url)
+                                                                                                .build())
+                                                                                        .build())
+                                                                                .build())
+                                                                        .build()
+                                                        })
+                                                        .build())
+                                                .build()
+                                })
+                                .build())
+                .build();
+
+        client.docx().v1().documentBlockChildren().create(req);
     }
 
     /**
@@ -370,7 +387,7 @@ public class FeiShuService {
                 log.error("删除失败: {} | token={} | err={}", file.getName(), file.getToken(), e.getMessage());
             }
         }
-        log.info("飞书看课文档清理完成 | 列表总数={} | 删除成功={}", total, deleted);
+        log.info("飞书文档清理完成 | 列表总数={} | 删除成功={}", total, deleted);
     }
 
 }