|
|
@@ -3,11 +3,16 @@ package com.fs.feishu.service;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.exception.CustomException;
|
|
|
+import com.fs.common.utils.DateUtils;
|
|
|
import com.fs.course.config.CourseConfig;
|
|
|
import com.fs.course.domain.FsCourseLink;
|
|
|
import com.fs.course.domain.FsUserCourseVideo;
|
|
|
import com.fs.course.mapper.FsCourseLinkMapper;
|
|
|
import com.fs.course.mapper.FsUserCourseVideoMapper;
|
|
|
+import com.fs.feishu.domain.FeishuAccount;
|
|
|
+import com.fs.feishu.domain.FeishuLinkError;
|
|
|
+import com.fs.feishu.mapper.FeishuAccountMapper;
|
|
|
+import com.fs.feishu.mapper.FeishuLinkErrorMapper;
|
|
|
import com.fs.feishu.model.FeishuLinkParam;
|
|
|
import com.fs.feishu.util.FeishuErrorCode;
|
|
|
import com.fs.system.service.ISysConfigService;
|
|
|
@@ -20,13 +25,10 @@ import org.springframework.stereotype.Component;
|
|
|
import java.io.UnsupportedEncodingException;
|
|
|
import java.net.URLEncoder;
|
|
|
import java.util.LinkedHashMap;
|
|
|
+import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
-/**
|
|
|
- * 飞书业务编排层:
|
|
|
- * 负责课程链接和注册链接的完整业务流程,包括缓存、URL 构建、调用底层 API 服务等。
|
|
|
- */
|
|
|
@Component
|
|
|
@Slf4j
|
|
|
public class FeiShuService {
|
|
|
@@ -38,66 +40,144 @@ public class FeiShuService {
|
|
|
@Autowired
|
|
|
private FsCourseLinkMapper courseLinkMapper;
|
|
|
@Autowired
|
|
|
+ private FeishuAccountMapper feishuAccountMapper;
|
|
|
+ @Autowired
|
|
|
+ private FeishuLinkErrorMapper feishuLinkErrorMapper;
|
|
|
+ @Autowired
|
|
|
private RedisCache redisCache;
|
|
|
@Autowired
|
|
|
private FeishuClientPool clientPool;
|
|
|
@Autowired
|
|
|
private FeishuDocApiService docApiService;
|
|
|
|
|
|
- /**
|
|
|
- * 001 生成飞书初始链接(注册授权链接)
|
|
|
- * 每次 shortCode 唯一
|
|
|
- */
|
|
|
+ private static final String FEISHU_COMPANY_USER_KEY = "feishu:account_company_user:";
|
|
|
+
|
|
|
+ // ==================== 获取销售个人账号(带缓存) ====================
|
|
|
+
|
|
|
+ public List<FeishuAccount> getFeishuAccountCompanyUser(Long companyId, Long companyUserId) {
|
|
|
+ String cacheKey = FEISHU_COMPANY_USER_KEY + companyId + ":" + companyUserId;
|
|
|
+ String cached = redisCache.getCacheObject(cacheKey);
|
|
|
+ if (StringUtils.isNotBlank(cached)) {
|
|
|
+ try {
|
|
|
+ return JSONUtil.parseArray(cached).toList(FeishuAccount.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("缓存数据解析失败,key: {}, 将清除缓存", cacheKey, e);
|
|
|
+ redisCache.deleteObject(cacheKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ FeishuAccount account = new FeishuAccount();
|
|
|
+ account.setCompanyId(companyId);
|
|
|
+ account.setCompanyUserId(companyUserId);
|
|
|
+ account.setStatus(1);
|
|
|
+ List<FeishuAccount> feishuAccounts = feishuAccountMapper.selectFeishuAccountList(account);
|
|
|
+ if (feishuAccounts.isEmpty()) {
|
|
|
+ redisCache.setCacheObject(cacheKey, "[]", 1, TimeUnit.MINUTES);
|
|
|
+ } else {
|
|
|
+ redisCache.setCacheObject(cacheKey, JSONUtil.toJsonStr(feishuAccounts), 1, TimeUnit.DAYS);
|
|
|
+ }
|
|
|
+ return feishuAccounts;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void clearCompanyUserCache(Long companyId, Long companyUserId) {
|
|
|
+ if (companyId != null && companyUserId != null) {
|
|
|
+ String cacheKey = FEISHU_COMPANY_USER_KEY + companyId + ":" + companyUserId;
|
|
|
+ redisCache.deleteObject(cacheKey);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 生成飞书注册链接 ====================
|
|
|
+
|
|
|
public String getFeishuRegisterLink(Long videoId, Long companyId, Long courseId,
|
|
|
Long companyUserId, String shortLink) {
|
|
|
- // 检查功能开关
|
|
|
CourseConfig config = getCourseConfig();
|
|
|
if (!Boolean.TRUE.equals(config.getEnableFeishuNewLink())) {
|
|
|
throw new CustomException("未开启飞书看课");
|
|
|
}
|
|
|
-
|
|
|
FsUserCourseVideo userCourseVideo = videoMapper.selectFsUserCourseVideoByVideoId(videoId);
|
|
|
if (userCourseVideo == null) {
|
|
|
throw new CustomException("视频不存在: " + videoId);
|
|
|
}
|
|
|
|
|
|
- int maxRetries = clientPool.getAvailableCount();
|
|
|
- for (int i = 0; i < maxRetries; i++) {
|
|
|
+ // 1. 尝试销售个人账号创建文档
|
|
|
+ List<FeishuAccount> personalAccounts = getFeishuAccountCompanyUser(companyId, companyUserId);
|
|
|
+ if (!personalAccounts.isEmpty()) {
|
|
|
+ for (FeishuAccount account : personalAccounts) {
|
|
|
+ if (account.getStatus() != 1) continue;
|
|
|
+ Client client = buildClient(account);
|
|
|
+ try {
|
|
|
+ String docId = docApiService.createDocument(client, userCourseVideo.getTitle() + "-注册");
|
|
|
+ String authUrl = buildAuthLink(companyId, companyUserId, courseId, videoId, shortLink);
|
|
|
+ docApiService.createTextLinkBlock(client, docId, authUrl);
|
|
|
+ docApiService.changeDocumentPermissions(client, docId);
|
|
|
+ return "https://www.feishu.cn/docx/" + docId;
|
|
|
+ } catch (CustomException e) {
|
|
|
+ Integer code = e.getCode();
|
|
|
+ if (code == null) {
|
|
|
+ recordLinkError(videoId, companyId, courseId, companyUserId, shortLink, e.getMessage(), 1);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (FeishuErrorCode.isAccountDisable(code)) {
|
|
|
+ disablePersonalAccount(account, e.getMessage());
|
|
|
+ log.warn("个人账号[{}]被禁用,尝试下一个", account.getAppId());
|
|
|
+ } else if (FeishuErrorCode.isRateLimit(code) || FeishuErrorCode.isInternalRetryable(code)) {
|
|
|
+ log.warn("个人账号[{}]可重试错误,切换下一个", account.getAppId());
|
|
|
+ } else {
|
|
|
+ // 业务错误或其他,记录失败并返回
|
|
|
+ recordLinkError(videoId, companyId, courseId, companyUserId, shortLink, e.getMessage(), 1);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ recordLinkError(videoId, companyId, courseId, companyUserId, shortLink, e.getMessage(), 1);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.warn("所有个人账号失败,降级到公共账号池");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 公共账号池
|
|
|
+ int retries = clientPool.getAvailableCount();
|
|
|
+ for (int i = 0; i < retries; i++) {
|
|
|
Client client = clientPool.getClient();
|
|
|
try {
|
|
|
- String documentId = docApiService.createDocument(client, userCourseVideo.getTitle() + "-注册");
|
|
|
+ String docId = docApiService.createDocument(client, userCourseVideo.getTitle() + "-注册");
|
|
|
String authUrl = buildAuthLink(companyId, companyUserId, courseId, videoId, shortLink);
|
|
|
- docApiService.createTextLinkBlock(client, documentId, authUrl);
|
|
|
- docApiService.changeDocumentPermissions(client, documentId);
|
|
|
- return "https://www.feishu.cn/docx/" + documentId;
|
|
|
+ docApiService.createTextLinkBlock(client, docId, authUrl);
|
|
|
+ docApiService.changeDocumentPermissions(client, docId);
|
|
|
+ return "https://www.feishu.cn/docx/" + docId;
|
|
|
} catch (CustomException e) {
|
|
|
- // 如果是账号禁用异常,继续尝试下一个账号
|
|
|
- if (e.getMessage().contains("不可用") || e.getMessage().contains("已被自动禁用")) {
|
|
|
- log.warn("账号不可用,尝试下一个");
|
|
|
- continue;
|
|
|
+ Integer code = e.getCode();
|
|
|
+ if (code == null) {
|
|
|
+ recordLinkError(videoId, companyId, courseId, companyUserId, shortLink, e.getMessage(), 1);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (FeishuErrorCode.isAccountDisable(code)) {
|
|
|
+ clientPool.disableClient(client, e.getMessage());
|
|
|
+ log.warn("公共账号被禁用,尝试下一个");
|
|
|
+ } else if (FeishuErrorCode.isRateLimit(code) || FeishuErrorCode.isInternalRetryable(code)) {
|
|
|
+ log.warn("公共账号可重试错误,切换下一个");
|
|
|
+ } else {
|
|
|
+ recordLinkError(videoId, companyId, courseId, companyUserId, shortLink, e.getMessage(), 1);
|
|
|
+ return null;
|
|
|
}
|
|
|
- throw e;
|
|
|
} catch (Exception e) {
|
|
|
- throw new CustomException("创建飞书注册链接失败: " + e.getMessage(), e);
|
|
|
+ recordLinkError(videoId, companyId, courseId, companyUserId, shortLink, e.getMessage(), 1);
|
|
|
+ return null;
|
|
|
}
|
|
|
}
|
|
|
- throw new CustomException("所有飞书账号均不可用,请联系管理员");
|
|
|
+ recordLinkError(videoId, companyId, courseId, companyUserId, shortLink, "所有公共账号不可用", 1);
|
|
|
+ return null;
|
|
|
}
|
|
|
- /**
|
|
|
- * 002 生成飞书课程链接
|
|
|
- * 相同参数可能重复,使用 Redis 缓存避免重复创建文档
|
|
|
- */
|
|
|
+
|
|
|
+ // ==================== 生成飞书课程链接 ====================
|
|
|
+
|
|
|
public String getFeishuCourseNewLink(FeishuLinkParam param) {
|
|
|
if (param == null || param.getCompanyUserId() == null || param.getCompanyId() == null) {
|
|
|
throw new CustomException("参数不能为空");
|
|
|
}
|
|
|
-
|
|
|
String cacheKey = String.format("feishu:course_link:%d:%d:%d:%d:%d:%s",
|
|
|
param.getCompanyId(), param.getCompanyUserId(),
|
|
|
param.getCourseId(), param.getVideoId(),
|
|
|
param.getUserId(), param.getLink());
|
|
|
-
|
|
|
- // 缓存命中直接返回
|
|
|
String cached = redisCache.getCacheObject(cacheKey);
|
|
|
if (StringUtils.isNotBlank(cached)) {
|
|
|
return cached;
|
|
|
@@ -107,42 +187,138 @@ public class FeiShuService {
|
|
|
if (userCourseVideo == null) {
|
|
|
throw new CustomException("视频不存在: " + param.getVideoId());
|
|
|
}
|
|
|
- //获取该视频是否有答题
|
|
|
- String questionBankId = userCourseVideo.getQuestionBankId();
|
|
|
- boolean hasQuestion = StringUtils.isNotBlank(questionBankId) && !"null".equals(questionBankId);
|
|
|
- Integer questionFlag = hasQuestion ? 1 : 0;//0:无题 1:有题
|
|
|
- int maxRetries = clientPool.getAvailableCount();
|
|
|
- for (int i = 0; i < maxRetries; i++) {
|
|
|
+ Integer questionFlag = (StringUtils.isNotBlank(userCourseVideo.getQuestionBankId()) &&
|
|
|
+ !"null".equals(userCourseVideo.getQuestionBankId())) ? 1 : 0;
|
|
|
+
|
|
|
+ // 1. 尝试个人账号
|
|
|
+ List<FeishuAccount> personalAccounts = getFeishuAccountCompanyUser(param.getCompanyId(), param.getCompanyUserId());
|
|
|
+ if (!personalAccounts.isEmpty()) {
|
|
|
+ for (FeishuAccount account : personalAccounts) {
|
|
|
+ if (account.getStatus() != 1) continue;
|
|
|
+ Client client = buildClient(account);
|
|
|
+ try {
|
|
|
+ String docId = docApiService.createDocument(client, userCourseVideo.getTitle());
|
|
|
+ String courseUrl = buildCourseNewLink(param.getCompanyId(), param.getCompanyUserId(),
|
|
|
+ param.getCourseId(), param.getVideoId(), param.getUserId(), param.getLink(), questionFlag);
|
|
|
+ docApiService.createIframeBlock(client, docId, courseUrl);
|
|
|
+ docApiService.changeDocumentPermissions(client, docId);
|
|
|
+ String result = "https://www.feishu.cn/docx/" + docId;
|
|
|
+ redisCache.setCacheObject(cacheKey, result, 24, TimeUnit.HOURS);
|
|
|
+ return result;
|
|
|
+ } catch (CustomException e) {
|
|
|
+ Integer code = e.getCode();
|
|
|
+ if (code == null) {
|
|
|
+ recordLinkError(param.getVideoId(), param.getCompanyId(), param.getCourseId(),
|
|
|
+ param.getCompanyUserId(), param.getLink(), e.getMessage(), 2);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (FeishuErrorCode.isAccountDisable(code)) {
|
|
|
+ disablePersonalAccount(account, e.getMessage());
|
|
|
+ log.warn("个人账号[{}]被禁用,尝试下一个", account.getAppId());
|
|
|
+ } else if (FeishuErrorCode.isRateLimit(code) || FeishuErrorCode.isInternalRetryable(code)) {
|
|
|
+ log.warn("个人账号[{}]可重试错误,切换下一个", account.getAppId());
|
|
|
+ } else {
|
|
|
+ recordLinkError(param.getVideoId(), param.getCompanyId(), param.getCourseId(),
|
|
|
+ param.getCompanyUserId(), param.getLink(), e.getMessage(), 2);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ recordLinkError(param.getVideoId(), param.getCompanyId(), param.getCourseId(),
|
|
|
+ param.getCompanyUserId(), param.getLink(), e.getMessage(), 2);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ log.warn("所有个人账号失败,降级到公共账号池");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 公共账号池
|
|
|
+ int retries = clientPool.getAvailableCount();
|
|
|
+ for (int i = 0; i < retries; i++) {
|
|
|
Client client = clientPool.getClient();
|
|
|
try {
|
|
|
- String documentId = docApiService.createDocument(client, userCourseVideo.getTitle());
|
|
|
- String url = buildCourseNewLink(param.getCompanyId(), param.getCompanyUserId(),
|
|
|
- param.getCourseId(), param.getVideoId(), param.getUserId(), param.getLink(),questionFlag);
|
|
|
- docApiService.createIframeBlock(client, documentId, url);
|
|
|
- docApiService.changeDocumentPermissions(client, documentId);
|
|
|
-
|
|
|
- String resultUrl = "https://www.feishu.cn/docx/" + documentId;
|
|
|
- redisCache.setCacheObject(cacheKey, resultUrl, 24, TimeUnit.HOURS);
|
|
|
- return resultUrl;
|
|
|
+ String docId = docApiService.createDocument(client, userCourseVideo.getTitle());
|
|
|
+ String courseUrl = buildCourseNewLink(param.getCompanyId(), param.getCompanyUserId(),
|
|
|
+ param.getCourseId(), param.getVideoId(), param.getUserId(), param.getLink(), questionFlag);
|
|
|
+ docApiService.createIframeBlock(client, docId, courseUrl);
|
|
|
+ docApiService.changeDocumentPermissions(client, docId);
|
|
|
+ String result = "https://www.feishu.cn/docx/" + docId;
|
|
|
+ redisCache.setCacheObject(cacheKey, result, 24, TimeUnit.HOURS);
|
|
|
+ return result;
|
|
|
} catch (CustomException e) {
|
|
|
- if (e.getMessage().contains("不可用") || e.getMessage().contains("已被自动禁用")) {
|
|
|
- log.warn("账号不可用,尝试下一个");
|
|
|
- continue;
|
|
|
+ Integer code = e.getCode();
|
|
|
+ if (code == null) {
|
|
|
+ recordLinkError(param.getVideoId(), param.getCompanyId(), param.getCourseId(),
|
|
|
+ param.getCompanyUserId(), param.getLink(), e.getMessage(), 2);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (FeishuErrorCode.isAccountDisable(code)) {
|
|
|
+ clientPool.disableClient(client, e.getMessage());
|
|
|
+ log.warn("公共账号被禁用,尝试下一个");
|
|
|
+ } else if (FeishuErrorCode.isRateLimit(code) || FeishuErrorCode.isInternalRetryable(code)) {
|
|
|
+ log.warn("公共账号可重试错误,切换下一个");
|
|
|
+ } else {
|
|
|
+ recordLinkError(param.getVideoId(), param.getCompanyId(), param.getCourseId(),
|
|
|
+ param.getCompanyUserId(), param.getLink(), e.getMessage(), 2);
|
|
|
+ return null;
|
|
|
}
|
|
|
- throw e;
|
|
|
} catch (Exception e) {
|
|
|
- throw new CustomException("创建飞书课程链接失败: " + e.getMessage(), e);
|
|
|
+ recordLinkError(param.getVideoId(), param.getCompanyId(), param.getCourseId(),
|
|
|
+ param.getCompanyUserId(), param.getLink(), e.getMessage(), 2);
|
|
|
+ return null;
|
|
|
}
|
|
|
}
|
|
|
- throw new CustomException("所有飞书账号均不可用,请联系管理员");
|
|
|
+ recordLinkError(param.getVideoId(), param.getCompanyId(), param.getCourseId(),
|
|
|
+ param.getCompanyUserId(), param.getLink(), "所有公共账号不可用", 2);
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== 辅助方法 ====================
|
|
|
+
|
|
|
+ private Client buildClient(FeishuAccount account) {
|
|
|
+ return Client.newBuilder(account.getAppId(), account.getAppSecret())
|
|
|
+ .logReqAtDebug(true)
|
|
|
+ .build();
|
|
|
}
|
|
|
|
|
|
- // ==================== URL 构建(纯业务逻辑,无外部调用) ====================
|
|
|
+ private void disablePersonalAccount(FeishuAccount account, String errorMsg) {
|
|
|
+ feishuAccountMapper.updateStatusAndErrorMsg(account.getId(), 0, errorMsg);
|
|
|
+ clearCompanyUserCache(account.getCompanyId(), account.getCompanyUserId());
|
|
|
+ }
|
|
|
+
|
|
|
+ private void recordLinkError(Long videoId, Long companyId, Long courseId,
|
|
|
+ Long companyUserId, String link, String errorInfo, Integer type) {
|
|
|
+ FeishuLinkError error = new FeishuLinkError();
|
|
|
+ error.setVideoId(videoId);
|
|
|
+ error.setCompanyId(companyId);
|
|
|
+ error.setCourseId(courseId);
|
|
|
+ error.setCompanyUserId(companyUserId);
|
|
|
+ error.setLink(link);
|
|
|
+ error.setErrorInfo(errorInfo);
|
|
|
+ error.setCreateTime(DateUtils.getNowDate());
|
|
|
+ error.setType(type);
|
|
|
+ FsCourseLink fsCourseLink = courseLinkMapper.selectFsCourseLinkByLink(link);
|
|
|
+ if (fsCourseLink != null) {
|
|
|
+ error.setQwUserId(fsCourseLink.getQwUserId());
|
|
|
+ error.setQwExternalId(fsCourseLink.getQwExternalId());
|
|
|
+ error.setCorpId(fsCourseLink.getCorpId());
|
|
|
+ }
|
|
|
+ feishuLinkErrorMapper.insertFeishuLinkError(error);
|
|
|
+ log.info("飞书链接创建失败已记录,videoId={}, link={}, error={}", videoId, link, errorInfo);
|
|
|
+ }
|
|
|
+
|
|
|
+ private CourseConfig getCourseConfig() {
|
|
|
+ String json = configService.selectConfigByKey("course.config");
|
|
|
+ if (StringUtils.isBlank(json)) {
|
|
|
+ throw new CustomException("飞书看课配置为空");
|
|
|
+ }
|
|
|
+ return JSONUtil.toBean(json, CourseConfig.class);
|
|
|
+ }
|
|
|
+
|
|
|
+ // ==================== URL构建 ====================
|
|
|
|
|
|
private String buildCourseNewLink(Long companyId, Long companyUserId, Long courseId,
|
|
|
- Long videoId, Long userId, String link,Integer questionFlag) {
|
|
|
+ Long videoId, Long userId, String link, Integer questionFlag) {
|
|
|
CourseConfig config = getCourseConfig();
|
|
|
-
|
|
|
Map<String, Object> courseParam = new LinkedHashMap<>();
|
|
|
courseParam.put("companyUserId", companyUserId);
|
|
|
courseParam.put("videoId", videoId);
|
|
|
@@ -150,7 +326,6 @@ public class FeiShuService {
|
|
|
courseParam.put("courseId", courseId);
|
|
|
courseParam.put("link", link);
|
|
|
courseParam.put("questionFlag", questionFlag);
|
|
|
-
|
|
|
FsCourseLink fsCourseLink = courseLinkMapper.selectFsCourseLinkByLink(link);
|
|
|
if (fsCourseLink == null) {
|
|
|
throw new CustomException("课程链接已过期");
|
|
|
@@ -159,7 +334,6 @@ public class FeiShuService {
|
|
|
courseParam.put("linkType", fsCourseLink.getLinkType());
|
|
|
courseParam.put("qwExternalId", fsCourseLink.getQwExternalId());
|
|
|
courseParam.put("qwUserId", fsCourseLink.getQwUserId());
|
|
|
-
|
|
|
try {
|
|
|
String encodedJson = URLEncoder.encode(JSONUtil.toJsonStr(courseParam), "UTF-8");
|
|
|
String fullUrl = config.getFeishuCourseDomain() +
|
|
|
@@ -173,14 +347,12 @@ public class FeiShuService {
|
|
|
private String buildAuthLink(Long companyId, Long companyUserId, Long courseId,
|
|
|
Long videoId, String shortLink) {
|
|
|
CourseConfig config = getCourseConfig();
|
|
|
-
|
|
|
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", shortLink);
|
|
|
-
|
|
|
try {
|
|
|
String encodedJson = URLEncoder.encode(JSONUtil.toJsonStr(authParam), "UTF-8");
|
|
|
String fullUrl = config.getFeishuAuthDomain() +
|
|
|
@@ -190,15 +362,4 @@ public class FeiShuService {
|
|
|
throw new CustomException("授权URL拼接失败", e);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * 读取课程配置并检查非空
|
|
|
- */
|
|
|
- private CourseConfig getCourseConfig() {
|
|
|
- String json = configService.selectConfigByKey("course.config");
|
|
|
- if (StringUtils.isBlank(json)) {
|
|
|
- throw new CustomException("飞书看课配置为空");
|
|
|
- }
|
|
|
- return JSONUtil.toBean(json, CourseConfig.class);
|
|
|
- }
|
|
|
}
|