mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/per-boe/java-servers.git
synced 2025-12-24 18:23:05 +08:00
Compare commits
53 Commits
SZX-1194-2
...
release-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
35a8f6a3f8 | ||
|
|
b867c37f28 | ||
|
|
0b9f95fab8 | ||
|
|
04fea4b530 | ||
|
|
97288c73b4 | ||
|
|
2a07973c81 | ||
|
|
5e9a40330c | ||
|
|
e521b3fbe8 | ||
|
|
1f5e9769f9 | ||
|
|
63d5e09170 | ||
|
|
4bcef7a327 | ||
|
|
255864204e | ||
|
|
34681bab97 | ||
|
|
9852b24983 | ||
|
|
02788430e9 | ||
|
|
b396926e16 | ||
|
|
94939824bd | ||
|
|
8d0ee29cf7 | ||
|
|
e97d589423 | ||
|
|
39d2681d4c | ||
|
|
9b86026473 | ||
|
|
9219b77895 | ||
|
|
e727f266ec | ||
|
|
49e42edf6f | ||
|
|
f4d9847e8b | ||
|
|
a0e1fea805 | ||
|
|
d0c5d2d561 | ||
|
|
951f2b0105 | ||
|
|
aa14fffd6c | ||
|
|
1d7e478aac | ||
|
|
973bbffc3c | ||
|
|
6c3f380bcb | ||
|
|
80b444c188 | ||
|
|
b0a0725373 | ||
|
|
59e42cda4f | ||
|
|
ec6d9100c2 | ||
|
|
562a4cc862 | ||
|
|
ae11017d67 | ||
|
|
fd473445de | ||
|
|
bfdbd4d38e | ||
|
|
640786391d | ||
|
|
71b00372f6 | ||
|
|
2e4b04d1d1 | ||
|
|
bffb85848f | ||
|
|
a9e34e42b9 | ||
|
|
84bd63ec0e | ||
|
|
1aba83a1b8 | ||
|
|
2b1211c83d | ||
|
|
fe3e8df37c | ||
|
|
8d9b360654 | ||
|
|
3379acdd1e | ||
|
|
9a3b394e03 | ||
|
|
7beb046d6d |
@@ -266,6 +266,10 @@
|
||||
<artifactId>xxl-job-core</artifactId>
|
||||
<version>2.3.0</version> <!-- 请根据实际需求选择版本 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.xboe.module.boecase.service.ICaseAiPermissionService;
|
||||
import com.xboe.module.boecase.service.IElasticSearchIndexService;
|
||||
import com.xboe.module.boecase.vo.CaseAiMessageVo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
@@ -46,6 +47,25 @@ public class CaseAiChatApi extends ApiBaseController {
|
||||
@Autowired
|
||||
private IElasticSearchIndexService elasticSearchIndexService;
|
||||
|
||||
/**
|
||||
* 初始化会话
|
||||
* @return 会话id
|
||||
*/
|
||||
@GetMapping("/initChat")
|
||||
public JsonResponse<String> initChat() {
|
||||
try {
|
||||
String conversationId = caseAiChatService.initChat(getCurrent());
|
||||
if (StringUtils.isNotBlank(conversationId)) {
|
||||
return success(conversationId, "初始化会话成功");
|
||||
} else {
|
||||
return success(conversationId, "初始化会话失败");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("初始化会话异常", e);
|
||||
return error("初始化会话失败", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 聊天
|
||||
* @param caseAiChatDto
|
||||
|
||||
@@ -17,6 +17,13 @@ import java.util.List;
|
||||
*/
|
||||
public interface ICaseAiChatService {
|
||||
|
||||
/**
|
||||
* 初始化会话
|
||||
*
|
||||
* @return {@link String } 会话id
|
||||
*/
|
||||
String initChat(CurrentUser currentUser);
|
||||
|
||||
/**
|
||||
* 聊天
|
||||
*
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.xboe.module.boecase.service.impl;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
@@ -39,9 +40,7 @@ import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.poi.ss.usermodel.Row;
|
||||
import org.apache.poi.ss.usermodel.Sheet;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.poi.ss.usermodel.*;
|
||||
import org.apache.poi.ss.util.CellRangeAddress;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -114,6 +113,16 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
|
||||
// 用于存储会话ID与EventSource的映射关系,以便能够中断特定会话
|
||||
private final Map<String, EventSource> conversationEventSourceMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 初始化会话
|
||||
*
|
||||
* @return {@link String } 会话id
|
||||
*/
|
||||
@Override
|
||||
public String initChat(CurrentUser currentUser) {
|
||||
return getOrCreateConversationId(new CaseAiChatDto(), currentUser);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public SseEmitter chat(CaseAiChatDto caseAiChatDto, CurrentUser currentUser) {
|
||||
@@ -387,7 +396,7 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
|
||||
if (StringUtils.isEmpty(conversationId)) {
|
||||
// 新会话,调用创建会话接口
|
||||
String conversationName = "AI案例咨询-" + LocalDateTime.now().toString();
|
||||
CaseAiConversations newConversation = createNewConversation(currentUser.getCode(), conversationName);
|
||||
CaseAiConversations newConversation = SpringUtil.getBean(ICaseAiChatService.class).createNewConversation(currentUser.getCode(), conversationName);
|
||||
return newConversation.getAiConversationId();
|
||||
} else {
|
||||
// 已存在会话,从数据库查询
|
||||
@@ -933,18 +942,89 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
|
||||
|
||||
// 写入Excel文件
|
||||
Workbook workbook = new XSSFWorkbook();
|
||||
Sheet sheet = workbook.createSheet("AI会话数据");
|
||||
// 样式
|
||||
CellStyle headerStyle = workbook.createCellStyle();
|
||||
Font headerFont = workbook.createFont();
|
||||
headerFont.setBold(true);
|
||||
headerFont.setColor(IndexedColors.WHITE.getIndex());
|
||||
headerStyle.setFont(headerFont);
|
||||
headerStyle.setFillForegroundColor(IndexedColors.DARK_BLUE.getIndex());
|
||||
headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
||||
headerStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
headerStyle.setBorderTop(BorderStyle.THIN);
|
||||
headerStyle.setBorderBottom(BorderStyle.THIN);
|
||||
headerStyle.setBorderLeft(BorderStyle.THIN);
|
||||
headerStyle.setBorderRight(BorderStyle.THIN);
|
||||
|
||||
CellStyle dataStyle = workbook.createCellStyle();
|
||||
dataStyle.setBorderTop(BorderStyle.THIN);
|
||||
dataStyle.setBorderBottom(BorderStyle.THIN);
|
||||
dataStyle.setBorderLeft(BorderStyle.THIN);
|
||||
dataStyle.setBorderRight(BorderStyle.THIN);
|
||||
dataStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
dataStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
|
||||
// 增加一个汇总Sheet
|
||||
Sheet summarySheet = workbook.createSheet("汇总");
|
||||
Row summaryHeaderRow = summarySheet.createRow(0);
|
||||
Cell monthHeaderCell = summaryHeaderRow.createCell(0);
|
||||
monthHeaderCell.setCellValue("月份");
|
||||
monthHeaderCell.setCellStyle(headerStyle);
|
||||
Cell sessionCountHeaderCell = summaryHeaderRow.createCell(1);
|
||||
sessionCountHeaderCell.setCellValue("会话总次数");
|
||||
sessionCountHeaderCell.setCellStyle(headerStyle);
|
||||
Cell userCountHeaderCell = summaryHeaderRow.createCell(2);
|
||||
userCountHeaderCell.setCellValue("会话总人数");
|
||||
userCountHeaderCell.setCellStyle(headerStyle);
|
||||
|
||||
Row summaryDataRow = summarySheet.createRow(1);
|
||||
Cell monthDataCell = summaryDataRow.createCell(0);
|
||||
monthDataCell.setCellValue(startTime.format(DateTimeFormatter.ofPattern("yyyy年MM月")));
|
||||
monthDataCell.setCellStyle(dataStyle);
|
||||
Cell sessionCountDataCell = summaryDataRow.createCell(1);
|
||||
sessionCountDataCell.setCellValue(conversations.size());
|
||||
sessionCountDataCell.setCellStyle(dataStyle);
|
||||
Cell userCountDataCell = summaryDataRow.createCell(2);
|
||||
userCountDataCell.setCellValue(conversations.stream().map(CaseAiConversations::getConversationUser).distinct().count());
|
||||
userCountDataCell.setCellStyle(dataStyle);
|
||||
|
||||
// 列宽
|
||||
for (int i = 0; i < 3; i++) {
|
||||
summarySheet.autoSizeColumn(i);
|
||||
summarySheet.setColumnWidth(i, summarySheet.getColumnWidth(i) + 500);
|
||||
}
|
||||
|
||||
Sheet dataSheet = workbook.createSheet("AI会话数据");
|
||||
// 标题行
|
||||
Row headerRow = sheet.createRow(0);
|
||||
headerRow.createCell(0).setCellValue("会话ID");
|
||||
headerRow.createCell(1).setCellValue("会话名称");
|
||||
headerRow.createCell(2).setCellValue("用户");
|
||||
headerRow.createCell(3).setCellValue("提问");
|
||||
headerRow.createCell(4).setCellValue("回答");
|
||||
headerRow.createCell(5).setCellValue("开始时间");
|
||||
headerRow.createCell(6).setCellValue("问答时长(秒)");
|
||||
headerRow.createCell(7).setCellValue("消息状态");
|
||||
headerRow.createCell(8).setCellValue("错误信息");
|
||||
Row dataHeaderRow = dataSheet.createRow(0);
|
||||
Cell conversationIdHeaderCell = dataHeaderRow.createCell(0);
|
||||
conversationIdHeaderCell.setCellValue("会话ID");
|
||||
conversationIdHeaderCell.setCellStyle(headerStyle);
|
||||
Cell conversationNameHeaderCell = dataHeaderRow.createCell(1);
|
||||
conversationNameHeaderCell.setCellValue("会话名称");
|
||||
conversationNameHeaderCell.setCellStyle(headerStyle);
|
||||
Cell userHeaderCell = dataHeaderRow.createCell(2);
|
||||
userHeaderCell.setCellValue("用户ID");
|
||||
userHeaderCell.setCellStyle(headerStyle);
|
||||
Cell queryHeaderCell = dataHeaderRow.createCell(3);
|
||||
queryHeaderCell.setCellValue("提问");
|
||||
queryHeaderCell.setCellStyle(headerStyle);
|
||||
Cell answerHeaderCell = dataHeaderRow.createCell(4);
|
||||
answerHeaderCell.setCellValue("回答");
|
||||
answerHeaderCell.setCellStyle(headerStyle);
|
||||
Cell startTimeHeaderCell = dataHeaderRow.createCell(5);
|
||||
startTimeHeaderCell.setCellValue("开始时间");
|
||||
startTimeHeaderCell.setCellStyle(headerStyle);
|
||||
Cell durationHeaderCell = dataHeaderRow.createCell(6);
|
||||
durationHeaderCell.setCellValue("问答时长(秒)");
|
||||
durationHeaderCell.setCellStyle(headerStyle);
|
||||
Cell statusHeaderCell = dataHeaderRow.createCell(7);
|
||||
statusHeaderCell.setCellValue("消息状态");
|
||||
statusHeaderCell.setCellStyle(headerStyle);
|
||||
Cell errorMsgHeaderCell = dataHeaderRow.createCell(8);
|
||||
errorMsgHeaderCell.setCellValue("错误信息");
|
||||
errorMsgHeaderCell.setCellStyle(headerStyle);
|
||||
|
||||
// 内容行
|
||||
if (!excelDataList.isEmpty()) {
|
||||
@@ -958,48 +1038,78 @@ public class CaseAiChatServiceImpl implements ICaseAiChatService {
|
||||
|
||||
// 遍历每个消息
|
||||
for (CaseAiMessageVo message : messages) {
|
||||
Row row = sheet.createRow(rowNum++);
|
||||
Row row = dataSheet.createRow(rowNum++);
|
||||
// 填充每行数据
|
||||
row.createCell(0).setCellValue(excelData.getConversationId());
|
||||
row.createCell(1).setCellValue(excelData.getConversationName());
|
||||
row.createCell(2).setCellValue(excelData.getUser());
|
||||
row.createCell(3).setCellValue(message.getQuery() != null ? message.getQuery() : "");
|
||||
row.createCell(4).setCellValue(message.getAnswer() != null ? message.getAnswer() : "");
|
||||
Cell conversationIdCell = row.createCell(0);
|
||||
conversationIdCell.setCellValue(excelData.getConversationId());
|
||||
conversationIdCell.setCellStyle(dataStyle);
|
||||
Cell conversationNameCell = row.createCell(1);
|
||||
conversationNameCell.setCellValue(excelData.getConversationName());
|
||||
conversationNameCell.setCellStyle(dataStyle);
|
||||
Cell userCell = row.createCell(2);
|
||||
userCell.setCellValue(excelData.getUser());
|
||||
userCell.setCellStyle(dataStyle);
|
||||
Cell queryCell = row.createCell(3);
|
||||
queryCell.setCellValue(message.getQuery() != null ? message.getQuery() : "");
|
||||
queryCell.setCellStyle(dataStyle);
|
||||
Cell answerCell = row.createCell(4);
|
||||
answerCell.setCellValue(message.getAnswer() != null ? message.getAnswer() : "");
|
||||
answerCell.setCellStyle(dataStyle);
|
||||
Cell startTimeCell = row.createCell(5);
|
||||
LocalDateTime messageStartTime = message.getStartTime();
|
||||
if (messageStartTime != null) {
|
||||
String startTimeStr = messageStartTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
|
||||
row.createCell(5).setCellValue(startTimeStr);
|
||||
startTimeCell.setCellValue(startTimeStr);
|
||||
} else {
|
||||
row.createCell(5).setCellValue("");
|
||||
startTimeCell.setCellValue("");
|
||||
}
|
||||
row.createCell(6).setCellValue(message.getDurationSeconds() != null ? message.getDurationSeconds() : 0);
|
||||
startTimeCell.setCellStyle(dataStyle);
|
||||
Cell durationCell = row.createCell(6);
|
||||
durationCell.setCellValue(message.getDurationSeconds() != null ? message.getDurationSeconds() : 0);
|
||||
durationCell.setCellStyle(dataStyle);
|
||||
Cell statusCell = row.createCell(7);
|
||||
if (message.getStatus() != null) {
|
||||
int status = message.getStatus();
|
||||
CaseAiChatErrCodeEnum errCodeEnum = CaseAiChatErrCodeEnum.getByCode(status);
|
||||
row.createCell(7).setCellValue(errCodeEnum.getLabel());
|
||||
statusCell.setCellValue(errCodeEnum.getLabel());
|
||||
}
|
||||
statusCell.setCellStyle(dataStyle);
|
||||
Cell errorMsgCell = row.createCell(8);
|
||||
if (StringUtils.isNotBlank(message.getErrorMsg())) {
|
||||
row.createCell(8).setCellValue(message.getErrorMsg());
|
||||
errorMsgCell.setCellValue(message.getErrorMsg());
|
||||
}
|
||||
errorMsgCell.setCellStyle(dataStyle);
|
||||
}
|
||||
|
||||
// 合并单元格(会话ID、会话名称、用户三列)
|
||||
// 参数说明:起始行号,结束行号,起始列号,结束列号
|
||||
if (rowNum > startRow + 1) { // 只有当有多行时才合并
|
||||
sheet.addMergedRegion(new CellRangeAddress(startRow, rowNum - 1, 0, 0));
|
||||
sheet.addMergedRegion(new CellRangeAddress(startRow, rowNum - 1, 1, 1));
|
||||
sheet.addMergedRegion(new CellRangeAddress(startRow, rowNum - 1, 2, 2));
|
||||
dataSheet.addMergedRegion(new CellRangeAddress(startRow, rowNum - 1, 0, 0));
|
||||
dataSheet.addMergedRegion(new CellRangeAddress(startRow, rowNum - 1, 1, 1));
|
||||
dataSheet.addMergedRegion(new CellRangeAddress(startRow, rowNum - 1, 2, 2));
|
||||
}
|
||||
} else {
|
||||
// 如果没有消息,则仍然创建一行显示基本信息
|
||||
Row row = sheet.createRow(rowNum++);
|
||||
row.createCell(0).setCellValue(excelData.getConversationId());
|
||||
row.createCell(1).setCellValue(excelData.getConversationName());
|
||||
row.createCell(2).setCellValue(excelData.getUser());
|
||||
Row row = dataSheet.createRow(rowNum++);
|
||||
Cell conversationIdCell = row.createCell(0);
|
||||
conversationIdCell.setCellValue(excelData.getConversationId());
|
||||
conversationIdCell.setCellStyle(dataStyle);
|
||||
Cell conversationNameCell = row.createCell(1);
|
||||
conversationNameCell.setCellValue(excelData.getConversationName());
|
||||
conversationNameCell.setCellStyle(dataStyle);
|
||||
Cell userCell = row.createCell(2);
|
||||
userCell.setCellValue(excelData.getUser());
|
||||
userCell.setCellStyle(dataStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 列宽
|
||||
for (int i = 0; i < 9; i++) {
|
||||
summarySheet.autoSizeColumn(i);
|
||||
summarySheet.setColumnWidth(i, summarySheet.getColumnWidth(i) + 500);
|
||||
}
|
||||
|
||||
return workbook;
|
||||
}
|
||||
|
||||
|
||||
@@ -765,186 +765,191 @@ public class CaseKnowledgeServiceImpl implements ICaseKnowledgeService {
|
||||
}
|
||||
}
|
||||
|
||||
JSONObject requestBody = new JSONObject();
|
||||
String userId = getCurrentUserId();
|
||||
requestBody.put("userId", userId);
|
||||
requestBody.put("kId", caseAiProperties.getCaseKnowledgeId());
|
||||
requestBody.put("parseType", "AUTO");
|
||||
// 2. 检查文件路径
|
||||
if (StringUtil.isBlank(cases.getFilePath())) {
|
||||
log.error("上传案例文档失败,案例文件路径为空,caseId: {}", caseId);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), "上传案例文档失败,案例文件路径为空",
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
return false;
|
||||
}
|
||||
if (StringUtils.equals(cases.getConfidentialityLevel(), "内部")) {
|
||||
JSONObject requestBody = new JSONObject();
|
||||
String userId = getCurrentUserId();
|
||||
requestBody.put("userId", userId);
|
||||
requestBody.put("kId", caseAiProperties.getCaseKnowledgeId());
|
||||
requestBody.put("parseType", "AUTO");
|
||||
// 2. 检查文件路径
|
||||
if (StringUtil.isBlank(cases.getFilePath())) {
|
||||
log.error("上传案例文档失败,案例文件路径为空,caseId: {}", caseId);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), "上传案例文档失败,案例文件路径为空",
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
String savePath = fileUploader.getSavePath();
|
||||
String filePath = savePath + cases.getFilePath();
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
log.error("上传案例文档失败,案例文件不存在,filePath: {}", filePath);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), "上传案例文档失败,案例文件不存在,filePath: " + filePath,
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
return false;
|
||||
}
|
||||
String savePath = fileUploader.getSavePath();
|
||||
String filePath = savePath + cases.getFilePath();
|
||||
File file = new File(filePath);
|
||||
if (!file.exists()) {
|
||||
log.error("上传案例文档失败,案例文件不存在,filePath: {}", filePath);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), "上传案例文档失败,案例文件不存在,filePath: " + filePath,
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. 构建上传参数
|
||||
String fileName = file.getName();
|
||||
// 4. 构建上传参数
|
||||
String fileName = file.getName();
|
||||
|
||||
String fileType = getFileType(fileName);
|
||||
String fileType = getFileType(fileName);
|
||||
|
||||
// 5. 重试逻辑:最多3次机会
|
||||
int uploadMaxRetries = 3;
|
||||
// 5. 重试逻辑:最多3次机会
|
||||
int uploadMaxRetries = 3;
|
||||
|
||||
// 构建multipart/form-data请求体
|
||||
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||
builder.addBinaryBody("file", file);
|
||||
builder.addTextBody("userId", userId, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("kId", caseAiProperties.getCaseKnowledgeId(), ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("fileName", fileName, ContentType.TEXT_PLAIN);
|
||||
requestBody.put("fileName", fileName);
|
||||
builder.addTextBody("fileType", fileType, ContentType.TEXT_PLAIN);
|
||||
requestBody.put("fileType", fileType);
|
||||
builder.addTextBody("parseType", "AUTO", ContentType.TEXT_PLAIN);
|
||||
// 构建multipart/form-data请求体
|
||||
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||
builder.addBinaryBody("file", file);
|
||||
builder.addTextBody("userId", userId, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("kId", caseAiProperties.getCaseKnowledgeId(), ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("fileName", fileName, ContentType.TEXT_PLAIN);
|
||||
requestBody.put("fileName", fileName);
|
||||
builder.addTextBody("fileType", fileType, ContentType.TEXT_PLAIN);
|
||||
requestBody.put("fileType", fileType);
|
||||
builder.addTextBody("parseType", "AUTO", ContentType.TEXT_PLAIN);
|
||||
|
||||
String url = caseAiProperties.getCaseDetailUrlBase() + caseId;
|
||||
requestBody.put("url", url);
|
||||
String downloadUrl = fileUploader.getHttpPath() + cases.getFilePath();
|
||||
requestBody.put("downloadUrl", downloadUrl);
|
||||
builder.addTextBody("url", url, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("downloadUrl", downloadUrl, ContentType.TEXT_PLAIN);
|
||||
// metadata
|
||||
JSONObject fileMetaData = new JSONObject();
|
||||
fileMetaData.put("标题", cases.getTitle());
|
||||
fileMetaData.put("作者", cases.getAuthorName());
|
||||
fileMetaData.put("年份", String.valueOf(cases.getSysCreateTime().getYear()));
|
||||
fileMetaData.put("摘要", cases.getSummary());
|
||||
// 组织领域:orgDomainParent
|
||||
String orgDomainParent = cases.getOrgDomainParent();
|
||||
List<DictItem> orgDomainParentItems = sysDictionaryService.findByKey("org_domain");
|
||||
Optional<DictItem> orgDomainParentItem = orgDomainParentItems.stream()
|
||||
.filter(item -> StringUtils.equals(orgDomainParent, item.getCode()))
|
||||
.findFirst();
|
||||
if (orgDomainParentItem.isPresent()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(orgDomainParentItem.get().getName());
|
||||
if (StringUtils.isNotBlank(cases.getOrgDomainParent2())) {
|
||||
Optional<DictItem> orgDomainParent2Item = orgDomainParentItems.stream()
|
||||
.filter(item -> StringUtils.equals(cases.getOrgDomainParent2(), item.getCode()))
|
||||
.findFirst();
|
||||
orgDomainParent2Item.ifPresent(dictItem -> sb.append(" - ").append(dictItem.getName()));
|
||||
if (StringUtils.isNotBlank(cases.getOrgDomainParent3())) {
|
||||
Optional<DictItem> orgDomainParent3Item = orgDomainParentItems.stream()
|
||||
.filter(item -> StringUtils.equals(cases.getOrgDomainParent3(), item.getCode()))
|
||||
String url = caseAiProperties.getCaseDetailUrlBase() + caseId;
|
||||
requestBody.put("url", url);
|
||||
String downloadUrl = fileUploader.getHttpPath() + cases.getFilePath();
|
||||
requestBody.put("downloadUrl", downloadUrl);
|
||||
builder.addTextBody("url", url, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("downloadUrl", downloadUrl, ContentType.TEXT_PLAIN);
|
||||
// metadata
|
||||
JSONObject fileMetaData = new JSONObject();
|
||||
fileMetaData.put("标题", cases.getTitle());
|
||||
fileMetaData.put("作者", cases.getAuthorName());
|
||||
fileMetaData.put("年份", String.valueOf(cases.getSysCreateTime().getYear()));
|
||||
fileMetaData.put("摘要", cases.getSummary());
|
||||
// 组织领域:orgDomainParent
|
||||
String orgDomainParent = cases.getOrgDomainParent();
|
||||
List<DictItem> orgDomainParentItems = sysDictionaryService.findByKey("org_domain");
|
||||
Optional<DictItem> orgDomainParentItem = orgDomainParentItems.stream()
|
||||
.filter(item -> StringUtils.equals(orgDomainParent, item.getCode()))
|
||||
.findFirst();
|
||||
if (orgDomainParentItem.isPresent()) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(orgDomainParentItem.get().getName());
|
||||
if (StringUtils.isNotBlank(cases.getOrgDomainParent2())) {
|
||||
Optional<DictItem> orgDomainParent2Item = orgDomainParentItems.stream()
|
||||
.filter(item -> StringUtils.equals(cases.getOrgDomainParent2(), item.getCode()))
|
||||
.findFirst();
|
||||
orgDomainParent3Item.ifPresent(dictItem -> sb.append(" - ").append(dictItem.getName()));
|
||||
orgDomainParent2Item.ifPresent(dictItem -> sb.append(" - ").append(dictItem.getName()));
|
||||
if (StringUtils.isNotBlank(cases.getOrgDomainParent3())) {
|
||||
Optional<DictItem> orgDomainParent3Item = orgDomainParentItems.stream()
|
||||
.filter(item -> StringUtils.equals(cases.getOrgDomainParent3(), item.getCode()))
|
||||
.findFirst();
|
||||
orgDomainParent3Item.ifPresent(dictItem -> sb.append(" - ").append(dictItem.getName()));
|
||||
}
|
||||
}
|
||||
fileMetaData.put("组织领域", sb.toString());
|
||||
}
|
||||
// 分类:majorIds
|
||||
List<CasesMajorType> cmtList = casesMajorTypeDao.findList(FieldFilters.eq("caseId", cases.getId()));
|
||||
if (cmtList != null && !cmtList.isEmpty()) {
|
||||
List<String> majorIds = cmtList.stream().map(CasesMajorType::getMajorId).collect(Collectors.toList());
|
||||
|
||||
List<DictItem> majorItems = sysDictionaryService.findByKey("major_type");
|
||||
if (majorItems != null && !majorItems.isEmpty()) {
|
||||
List<String> majorNames = majorItems.stream()
|
||||
.filter(item -> majorIds.contains(item.getCode()))
|
||||
.map(DictItem::getName)
|
||||
.collect(Collectors.toList());
|
||||
fileMetaData.put("组织领域", String.join(", ", majorNames));
|
||||
}
|
||||
}
|
||||
fileMetaData.put("组织领域", sb.toString());
|
||||
}
|
||||
// 分类:majorIds
|
||||
List<CasesMajorType> cmtList = casesMajorTypeDao.findList(FieldFilters.eq("caseId", cases.getId()));
|
||||
if (cmtList != null && !cmtList.isEmpty()) {
|
||||
List<String> majorIds = cmtList.stream().map(CasesMajorType::getMajorId).collect(Collectors.toList());
|
||||
|
||||
List<DictItem> majorItems = sysDictionaryService.findByKey("major_type");
|
||||
if (majorItems != null && !majorItems.isEmpty()) {
|
||||
List<String> majorNames = majorItems.stream()
|
||||
.filter(item -> majorIds.contains(item.getCode()))
|
||||
.map(DictItem::getName)
|
||||
.collect(Collectors.toList());
|
||||
fileMetaData.put("组织领域", String.join(", ", majorNames));
|
||||
ContentType contentType = ContentType.create("text/plain", StandardCharsets.UTF_8);
|
||||
builder.addTextBody("fileMetaData", fileMetaData.toJSONString(), contentType);
|
||||
requestBody.put("fileMetaData", fileMetaData);
|
||||
// 由于接口权限,目前采用不回调,而是通过批处理的方式,处理文件状态
|
||||
if (caseAiProperties.isFileUploadUseCallback()) {
|
||||
builder.addTextBody("callbackUrl", caseAiProperties.getFileUploadCallbackUrl(), ContentType.TEXT_PLAIN);
|
||||
requestBody.put("callbackUrl", caseAiProperties.getFileUploadCallbackUrl());
|
||||
}
|
||||
}
|
||||
|
||||
ContentType contentType = ContentType.create("text/plain", StandardCharsets.UTF_8);
|
||||
builder.addTextBody("fileMetaData", fileMetaData.toJSONString(), contentType);
|
||||
requestBody.put("fileMetaData", fileMetaData);
|
||||
// 由于接口权限,目前采用不回调,而是通过批处理的方式,处理文件状态
|
||||
if (caseAiProperties.isFileUploadUseCallback()) {
|
||||
builder.addTextBody("callbackUrl", caseAiProperties.getFileUploadCallbackUrl(), ContentType.TEXT_PLAIN);
|
||||
requestBody.put("callbackUrl", caseAiProperties.getFileUploadCallbackUrl());
|
||||
}
|
||||
String uploadUrl = caseAiProperties.getBaseUrl() + "/apigateway/knowledge/v1/file/upload";
|
||||
|
||||
String uploadUrl = caseAiProperties.getBaseUrl() + "/apigateway/knowledge/v1/file/upload";
|
||||
// 3. 获取access_token
|
||||
String accessToken = aiAccessTokenService.getAccessToken();
|
||||
if (StringUtil.isBlank(accessToken)) {
|
||||
log.error("上传案例文档失败,获取access_token失败");
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), "上传案例文档失败,获取access_token失败",
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
return false;
|
||||
}
|
||||
for (int attempt = 1; attempt <= uploadMaxRetries; attempt++) {
|
||||
log.info("上传案例文档第{}次尝试,caseId: {}", attempt, caseId);
|
||||
|
||||
// 3. 获取access_token
|
||||
String accessToken = aiAccessTokenService.getAccessToken();
|
||||
if (StringUtil.isBlank(accessToken)) {
|
||||
log.error("上传案例文档失败,获取access_token失败");
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), "上传案例文档失败,获取access_token失败",
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
return false;
|
||||
}
|
||||
for (int attempt = 1; attempt <= uploadMaxRetries; attempt++) {
|
||||
log.info("上传案例文档第{}次尝试,caseId: {}", attempt, caseId);
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(uploadUrl);
|
||||
httpPost.setHeader("X-AI-ApiCode", caseAiProperties.getAiApiCode());
|
||||
httpPost.setHeader("access_token", accessToken);
|
||||
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
HttpPost httpPost = new HttpPost(uploadUrl);
|
||||
httpPost.setHeader("X-AI-ApiCode", caseAiProperties.getAiApiCode());
|
||||
httpPost.setHeader("access_token", accessToken);
|
||||
HttpEntity multipart = builder.build();
|
||||
httpPost.setEntity(multipart);
|
||||
|
||||
HttpEntity multipart = builder.build();
|
||||
httpPost.setEntity(multipart);
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
|
||||
try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
String responseBody = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
|
||||
if (statusCode == 200) {
|
||||
JSONObject result = JSON.parseObject(responseBody);
|
||||
if (result.getBooleanValue("success")) {
|
||||
// 业务处理成功
|
||||
JSONObject data = result.getJSONObject("data");
|
||||
String taskId = data.getString("taskId");
|
||||
|
||||
if (statusCode == 200) {
|
||||
JSONObject result = JSON.parseObject(responseBody);
|
||||
if (result.getBooleanValue("success")) {
|
||||
// 业务处理成功
|
||||
JSONObject data = result.getJSONObject("data");
|
||||
String taskId = data.getString("taskId");
|
||||
|
||||
// 保存成功的CaseDocumentLog记录
|
||||
// 保存成功的CaseDocumentLog记录
|
||||
// saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
// requestBody.toJSONString(), responseBody,
|
||||
// CaseDocumentLogRunStatusEnum.RUNNING.getCode(), null, null, taskId);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), responseBody,
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.SUCCESS.getCode(), CaseDocumentLogCaseStatusEnum.FAILED.getCode(), taskId);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), responseBody,
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.SUCCESS.getCode(), CaseDocumentLogCaseStatusEnum.FAILED.getCode(), taskId);
|
||||
|
||||
log.info("上传案例文档成功,等待文档状态变更. caseId: {}, taskId: {}, 尝试次数: {}", caseId, taskId, attempt);
|
||||
return true;
|
||||
log.info("上传案例文档成功,等待文档状态变更. caseId: {}, taskId: {}, 尝试次数: {}", caseId, taskId, attempt);
|
||||
return true;
|
||||
} else {
|
||||
// 业务处理失败,不重试
|
||||
log.error("上传案例文档业务处理失败,不重试,response: {}", responseBody);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), responseBody,
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.SUCCESS.getCode(), CaseDocumentLogCaseStatusEnum.FAILED.getCode(), null);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 业务处理失败,不重试
|
||||
log.error("上传案例文档业务处理失败,不重试,response: {}", responseBody);
|
||||
// 接口调用失败
|
||||
log.error("上传案例文档接口调用失败,第{}次尝试,status: {}, response: {}", attempt, statusCode, responseBody);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), responseBody,
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.SUCCESS.getCode(), CaseDocumentLogCaseStatusEnum.FAILED.getCode(), null);
|
||||
return false;
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
if (attempt == uploadMaxRetries) {
|
||||
// 最后一次尝试仍然失败
|
||||
return false;
|
||||
}
|
||||
// 继续下一次重试
|
||||
}
|
||||
} else {
|
||||
// 接口调用失败
|
||||
log.error("上传案例文档接口调用失败,第{}次尝试,status: {}, response: {}", attempt, statusCode, responseBody);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), responseBody,
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
if (attempt == uploadMaxRetries) {
|
||||
// 最后一次尝试仍然失败
|
||||
return false;
|
||||
}
|
||||
// 继续下一次重试
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 接口调用异常
|
||||
log.error("上传案例文档接口调用异常,第{}次尝试", attempt, e);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), "接口调用异常: " + e.getMessage(),
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
if (attempt == uploadMaxRetries) {
|
||||
// 最后一次尝试仍然异常
|
||||
log.error("上传案例文档最终失败,已重试{}次", uploadMaxRetries);
|
||||
return false;
|
||||
}
|
||||
// 继续下一次重试
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// 接口调用异常
|
||||
log.error("上传案例文档接口调用异常,第{}次尝试", attempt, e);
|
||||
saveCaseDocumentLog(caseId, cases.getTitle(), CaseDocumentLogOptTypeEnum.UPDATE.getCode(), CaseAiConstants.CASE_DOC_UPLOAD_INTERFACE_NAME,
|
||||
requestBody.toJSONString(), "接口调用异常: " + e.getMessage(),
|
||||
CaseDocumentLogRunStatusEnum.COMPLETED.getCode(), CaseDocumentLogOptStatusEnum.FAILED.getCode(), null, null);
|
||||
if (attempt == uploadMaxRetries) {
|
||||
// 最后一次尝试仍然异常
|
||||
log.error("上传案例文档最终失败,已重试{}次", uploadMaxRetries);
|
||||
return false;
|
||||
}
|
||||
// 继续下一次重试
|
||||
}
|
||||
} else {
|
||||
log.info("非【内部】密级案例,不进行上传");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.xboe.module.course.api;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -7,10 +8,25 @@ import java.util.List;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xboe.api.ThirdApi;
|
||||
import com.xboe.module.course.dto.CourseParam;
|
||||
import com.xboe.module.course.service.*;
|
||||
import com.xboe.module.course.dto.*;
|
||||
import com.xboe.module.course.entity.*;
|
||||
import com.xboe.module.course.utils.HttpUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -29,6 +45,10 @@ import com.xboe.module.course.entity.Course;
|
||||
import com.xboe.module.course.entity.CourseAudit;
|
||||
import com.xboe.module.course.entity.CourseContent;
|
||||
import com.xboe.module.course.entity.CourseHRBPAudit;
|
||||
import com.xboe.module.course.service.ICourseAuditService;
|
||||
import com.xboe.module.course.service.ICourseContentService;
|
||||
import com.xboe.module.course.service.ICourseHRBPAuditService;
|
||||
import com.xboe.module.course.service.ICourseService;
|
||||
import com.xboe.standard.enums.BoedxContentType;
|
||||
import com.xboe.standard.enums.BoedxCourseType;
|
||||
|
||||
@@ -60,6 +80,12 @@ public class CourseAuditApi extends ApiBaseController{
|
||||
@Resource
|
||||
private ICourseTagService tagService;
|
||||
|
||||
@Value("${kjb.aicoreUrl}")
|
||||
private String aicoreUrl;
|
||||
@Value("${kjb.videoUrlPrefix}")
|
||||
private String videoUrlPerfix;
|
||||
|
||||
|
||||
/**
|
||||
* 教师需要审核的课程
|
||||
* @param pager
|
||||
@@ -448,16 +474,84 @@ public class CourseAuditApi extends ApiBaseController{
|
||||
param.setOrgName(dto.getCourse().getOrgName());
|
||||
thirdApi.updateOrSaveCourse(param,token);
|
||||
log.info("---------------在线课同步到讲师管理完毕 -------");
|
||||
//AI视频处理-调用接口
|
||||
//需求调整-课程AI设置放在课程提交审核之前
|
||||
this.sendMessageToKJB(dto);
|
||||
log.info("---------------**审核**AI视频处理-调用接口完毕 -------");
|
||||
return success(true);
|
||||
} catch (Exception e) {
|
||||
log.error("默认管理员提交直接发布处理失败",e);
|
||||
e.printStackTrace();
|
||||
return error("发布课程失败",e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
public void sendMessageToKJB(CourseFullDto dto) throws JsonProcessingException {
|
||||
Course course = dto.getCourse();
|
||||
//log.info("---------------AI视频处理-调用接口 -------");
|
||||
log.info("aiSet:"+course.getAiSet()+",aiAbstract:"+course.getAiAbstract()+",aiDraft:"+course.getAiDraft()+",aiTranslate:"+course.getAiTranslate()+",languageCode:"+course.getLanguageCode());
|
||||
List<CourseTeacher> teachers = dto.getTeachers();
|
||||
StringBuilder teacherNames = new StringBuilder();
|
||||
if(!CollectionUtils.isEmpty(teachers)){
|
||||
for (CourseTeacher teacher : teachers) {
|
||||
teacherNames.append(teacher.getTeacherName()).append(",");
|
||||
}
|
||||
}
|
||||
List<CourseContent> cclist = ccontentService.getByCourseId(course.getId());
|
||||
List<String> languageCode = course.getLanguageCode();
|
||||
String code = String.join(",", languageCode);
|
||||
List<BoeaiCourseDto> courseDtos = new ArrayList<>();
|
||||
BoeaiCourseDto boeaiCourseDto = new BoeaiCourseDto();
|
||||
List<BoeaiVideoResourceDto> videoList = new ArrayList<>();
|
||||
boeaiCourseDto = BoeaiCourseDto.builder()
|
||||
.courseId(course.getId())
|
||||
.title(course.getName())
|
||||
.description(course.getSummary())
|
||||
.instructor(teacherNames.toString())
|
||||
.chapterCount(1) //章节数
|
||||
.languageCode(code) //语言
|
||||
.aiTranslate(course.getAiTranslate())
|
||||
.aiAbstract(course.getAiAbstract())
|
||||
.aiDraft(course.getAiDraft())
|
||||
.aiSet(course.getAiSet())
|
||||
.languageStatus(course.getLanguageStatus())
|
||||
.build() ;
|
||||
if(cclist != null && !cclist.isEmpty()){
|
||||
for (CourseContent cc : cclist) {
|
||||
//筛选视频资源
|
||||
if(cc.getContentType() == 10 ){
|
||||
JSONObject json = JSONObject.parseObject(cc.getContent());
|
||||
if(json == null || json.getString("url") == null) {
|
||||
continue;
|
||||
}
|
||||
String videoUrl = json.getString("url");
|
||||
//String videoUrlPerfix = "https://u-pre.boe.com/upload"; //测试
|
||||
//String videoUrlPerfix = "https://u.boe.com/upload"; //生产
|
||||
String videoType = videoUrl.substring(videoUrl.lastIndexOf(".")+1);
|
||||
videoList.add(BoeaiVideoResourceDto.builder()
|
||||
.courseId(cc.getCourseId())
|
||||
.videoId(cc.getId())
|
||||
.title(cc.getContentName())
|
||||
.originalUrl(videoUrlPerfix+videoUrl)
|
||||
.duration(cc.getDuration())
|
||||
.format(videoType)
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
boeaiCourseDto.setBoeaiVideoResourceReqList(videoList);
|
||||
boeaiCourseDto.setVideoCount(videoList.size());
|
||||
|
||||
courseDtos.add(boeaiCourseDto);
|
||||
BoeaiCourseParamsReq paramReq = new BoeaiCourseParamsReq();
|
||||
paramReq.setBoeaiCourseReqList(courseDtos);
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String message = objectMapper.writeValueAsString(paramReq);
|
||||
String url = aicoreUrl +"/aiVideo/saveCourse";
|
||||
HttpUtils.sendMessage(message,url);
|
||||
}
|
||||
|
||||
|
||||
/***
|
||||
* 审核
|
||||
* @param dto
|
||||
@@ -488,5 +582,4 @@ public class CourseAuditApi extends ApiBaseController{
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ import java.util.stream.Collectors;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.lang.Opt;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.boe.feign.api.serverall.entity.UserData;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
@@ -17,12 +20,15 @@ import com.xboe.core.orm.QueryBuilder;
|
||||
import com.xboe.data.outside.IOutSideDataService;
|
||||
import com.xboe.module.course.entity.CourseTag;
|
||||
import com.xboe.module.course.service.*;
|
||||
import com.xboe.module.course.dto.BoeaiCourseDto;
|
||||
import com.xboe.module.course.utils.HttpUtils;
|
||||
import com.xboe.module.course.vo.TeacherVo;
|
||||
import com.xboe.school.study.entity.StudyCourse;
|
||||
import com.xboe.school.study.service.IStudyCourseService;
|
||||
import com.xboe.system.organization.service.IOrganizationService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -44,7 +50,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
//课程AI设置
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping(value="/xboe/m/course/fulltext")
|
||||
@@ -77,6 +83,9 @@ public class CourseFullTextApi extends ApiBaseController{
|
||||
@Autowired
|
||||
StringRedisTemplate redisTemplate;
|
||||
|
||||
@Value("${kjb.aicoreUrl}")
|
||||
private String aicoreUrl;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@@ -354,6 +363,8 @@ public class CourseFullTextApi extends ApiBaseController{
|
||||
if(c.getSource()==2) {
|
||||
cids.add(c.getId());
|
||||
}
|
||||
log.info("---- KJB 开始获取课程摘要信息 ---");
|
||||
this.getCourseFromKJB(c,new Course());
|
||||
}
|
||||
List<Course> clist=null;
|
||||
if(!cids.isEmpty()) {
|
||||
@@ -451,6 +462,48 @@ public class CourseFullTextApi extends ApiBaseController{
|
||||
|
||||
}
|
||||
|
||||
public void getCourseFromKJB(CourseFullText courseFull,Course course){
|
||||
String courseId = courseFull.getId();
|
||||
log.info("------------KJB---获取课程详情 start ---------;courseId = " + courseId);
|
||||
//String url = env.getProperty("kjb.url") + "/api/course/getCourseFromKJB?courseId=" + courseId;
|
||||
String url = aicoreUrl + "/aiVideo/getCourseList";
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
JSONArray courseIds = new JSONArray();
|
||||
courseIds.add(courseId);
|
||||
jsonObject.put("courseIds", courseIds);
|
||||
String result = HttpUtils.sendMessage(jsonObject.toJSONString(), url);
|
||||
|
||||
//将返回string 转为json对象
|
||||
log.info("---KJB --摘要接口返回结果result: "+result);
|
||||
JSONObject jsonResult = JSONObject.parseObject(result);
|
||||
String data = jsonResult.getString("rows");
|
||||
//json字符串转为实体对象
|
||||
// 直接解析为 List<Course>
|
||||
List<BoeaiCourseDto> boeaiCourseDto = JSON.parseArray(data, BoeaiCourseDto.class);
|
||||
//List<BoeaiCourseDto> aiVideoResourceRsp = Collections.singletonList(JSON.parseObject(data, BoeaiCourseDto.class));
|
||||
if(boeaiCourseDto != null && !boeaiCourseDto.isEmpty()){
|
||||
//log.info("KJB摘要信息:"+boeaiCourseDto.get(0).getSummaryContent());
|
||||
courseFull.setAiAbstract(boeaiCourseDto.get(0).getAiAbstract());
|
||||
courseFull.setSummaryContent(boeaiCourseDto.get(0).getSummaryContent());
|
||||
courseFull.setAiSet(boeaiCourseDto.get(0).getAiSet());
|
||||
courseFull.setAiDraft(boeaiCourseDto.get(0).getAiDraft());
|
||||
courseFull.setAiTranslate(boeaiCourseDto.get(0).getAiTranslate());
|
||||
courseFull.setLanguageStatus(boeaiCourseDto.get(0).getLanguageStatus());
|
||||
courseFull.setLanguageCode(boeaiCourseDto.get(0).getLanguageCode() == null ? new ArrayList<>() :Arrays.asList(boeaiCourseDto.get(0).getLanguageCode().split(",")));
|
||||
|
||||
course.setAiSet(boeaiCourseDto.get(0).getAiSet());
|
||||
course.setAiAbstract(boeaiCourseDto.get(0).getAiAbstract());
|
||||
//course.setSummaryStatus(boeaiCourseDto.get(0).getSummaryStatus());
|
||||
course.setSummaryContent(boeaiCourseDto.get(0).getSummaryContent());
|
||||
course.setAiDraft(boeaiCourseDto.get(0).getAiDraft());
|
||||
course.setAiTranslate(boeaiCourseDto.get(0).getAiTranslate());
|
||||
course.setLanguageStatus(boeaiCourseDto.get(0).getLanguageStatus());
|
||||
course.setLanguageCode(boeaiCourseDto.get(0).getLanguageCode() == null ? new ArrayList<>() :Arrays.asList(boeaiCourseDto.get(0).getLanguageCode().split(",")));
|
||||
}
|
||||
log.info("------------KJB---获取课程详情 end ---------");
|
||||
}
|
||||
|
||||
private void getTeacherStatusByCode(String token, PageList<CourseFullText> coursePageList) {
|
||||
log.info("获取教师信息通过工号 ");
|
||||
List<String> teacherCoeds = new ArrayList<>();
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.xboe.module.course.service.*;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
//import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -93,6 +94,11 @@ public class CourseManageApi extends ApiBaseController{
|
||||
IDataUserSyncService userSyncService;
|
||||
@Resource
|
||||
private ThirdApi thirdApi;
|
||||
@Resource
|
||||
CourseAuditApi courseAuditApi;
|
||||
|
||||
@Resource
|
||||
CourseFullTextApi courseFullTextApi;
|
||||
|
||||
// @PostMapping("/test")
|
||||
// public JsonResponse<PageList<Course>> findTest(Pagination pager,CourseQueryDto dto){
|
||||
@@ -138,6 +144,10 @@ public class CourseManageApi extends ApiBaseController{
|
||||
dto.setOrgIds(ids);
|
||||
dto.setReadIds(userOrgIds.getReadIds());
|
||||
PageList<Course> coursePageList = courseService.findPage(pager.getPageIndex(), pager.getPageSize(),dto);
|
||||
//补充审核人,审核时间字段
|
||||
if(CollectionUtils.isNotEmpty(coursePageList.getList())){
|
||||
fullAuditInfo(coursePageList.getList());
|
||||
}
|
||||
return success(coursePageList);
|
||||
}catch(Exception e) {
|
||||
log.error("管理课程列表查询错误",e);
|
||||
@@ -145,7 +155,45 @@ public class CourseManageApi extends ApiBaseController{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void fullAuditInfo(List<Course> list){
|
||||
List<String> courseIdList = list.stream().map(Course::getId).collect(Collectors.toList());
|
||||
List<CourseHRBPAudit> auditList = hrbpAuditService.listByCourseIds(courseIdList);
|
||||
Map<String,CourseHRBPAudit> map = new HashMap<>();
|
||||
for(CourseHRBPAudit audit : auditList){
|
||||
CourseHRBPAudit r = map.get(audit.getCourseId());
|
||||
//可能存在多个审核记录,取时间最新的那一条
|
||||
if(r == null || r.getAddTime().isBefore(audit.getAddTime())){
|
||||
map.put(audit.getCourseId(),audit);
|
||||
}
|
||||
}
|
||||
|
||||
list.forEach(item ->{
|
||||
CourseHRBPAudit audit = map.get(item.getId());
|
||||
if(audit != null){
|
||||
//公开课审核完成取公开课的审核信息
|
||||
if(StringUtils.isNotEmpty(audit.getLastAname())){
|
||||
item.setAuditUser(audit.getLastAname());
|
||||
item.setAuditTime(audit.getLastTime());
|
||||
}else if(CourseHRBPAudit.FORWARD_TEACHER == audit.getForward()){
|
||||
//转发到教师审核取教师审核信息
|
||||
item.setAuditUser(audit.getToName());
|
||||
}else if (CourseHRBPAudit.FORWARD_TO_FINISH == audit.getForward()){
|
||||
//老师审核完成
|
||||
item.setAuditUser(audit.getAuditUser());
|
||||
if(CourseHRBPAudit.STATUS_NOPASS ==audit.getStatus()){
|
||||
//老师审核不通过时取老师的审核时间
|
||||
item.setAuditTime(audit.getToAuditTime());
|
||||
}
|
||||
}else {
|
||||
item.setAuditUser(audit.getAuditUser());
|
||||
item.setAuditTime(audit.getAuditTime());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@GetMapping("/detail")
|
||||
public JsonResponse<Map<String,Object>> detail(String id){
|
||||
if(StringUtils.isBlank(id)) {
|
||||
@@ -174,6 +222,13 @@ public class CourseManageApi extends ApiBaseController{
|
||||
isPermission = dicts.contains(course.getOrgId());
|
||||
rs.put("dicts",dicts);
|
||||
}
|
||||
//void getCourseFromKJB(CourseFullText courseFull,Course course){
|
||||
log.info("-------课程详情(KJB)查询开始-------");
|
||||
CourseFullText courseFull = new CourseFullText();
|
||||
courseFull.setId(course.getId());
|
||||
courseFullTextApi.getCourseFromKJB(courseFull,course);
|
||||
log.info("-------课程详情(KJB)查询结束-------");
|
||||
|
||||
log.error("-------是否仅内网查看 = " + isPermission);
|
||||
if (StringUtils.isNotBlank(course.getTags()) && course.getTags().matches("[0-9,]+")){
|
||||
List<CourseTag> tagList = tagService.getTagsByIds(course.getTags());
|
||||
@@ -210,7 +265,7 @@ public class CourseManageApi extends ApiBaseController{
|
||||
}
|
||||
return success(rs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 管理员审核列表,教师的审核不在这里,此审核也应该移动CourseAuditApi中去
|
||||
* @param pager
|
||||
@@ -359,6 +414,11 @@ public class CourseManageApi extends ApiBaseController{
|
||||
param.setOrgName(dto.getCourse().getOrgName());
|
||||
thirdApi.updateOrSaveCourse(param,token);
|
||||
log.info("---------------在线课同步到讲师管理完毕 -------");
|
||||
//AI视频处理-调用接口
|
||||
log.info("---------------AI视频处理-调用接口开始 -------");
|
||||
courseAuditApi.sendMessageToKJB(dto);
|
||||
log.info("---------------AI视频处理-调用接口结束 -------");
|
||||
|
||||
return success(dto);
|
||||
} catch (Exception e) {
|
||||
log.error("整体保存课程信息错误",e);
|
||||
@@ -404,7 +464,29 @@ public class CourseManageApi extends ApiBaseController{
|
||||
return error("保存失败",e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/benchAiSet")
|
||||
@AutoLog(module = "课程",action = "批量课程AI设置",info = "")
|
||||
public JsonResponse<CourseFullDto> benchAiSet(@RequestBody CourseFullDto dto){
|
||||
log.info("--------------- 批量课程AI设置 ----------");
|
||||
if(CollectionUtils.isEmpty(dto.getCourseList())) {
|
||||
return badRequest("请选择要设置的课程");
|
||||
}
|
||||
try {
|
||||
CourseFullDto item;
|
||||
for (Course course : dto.getCourseList()) {
|
||||
item = new CourseFullDto();
|
||||
item.setCourse(course);
|
||||
courseAuditApi.sendMessageToKJB(item);
|
||||
}
|
||||
return success(dto);
|
||||
} catch (Exception e) {
|
||||
log.error("批量课程AI设置错误",e);
|
||||
return error("保存失败",e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String checkForSubmit(Course c) {
|
||||
String error=null;
|
||||
if(StringUtils.isBlank(c.getName())) {
|
||||
@@ -632,7 +714,7 @@ public class CourseManageApi extends ApiBaseController{
|
||||
String email = userBasicInfoVoList.get(0).getEmail();
|
||||
log.info("审批获取邮箱 userBasicInfoVoList.get(0).getEmail():{}", email);
|
||||
|
||||
|
||||
|
||||
return email;
|
||||
} catch (Exception e) {
|
||||
log.error("获取用户邮箱错误",e);
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Huang Run
|
||||
* @date 2025年11月04日
|
||||
*/
|
||||
@Data
|
||||
public class AiVideoResourceRsp {
|
||||
//多语言字幕信息
|
||||
List<BoeaiSubtitleRsp> boeaiSubtitleRspList;
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 视频业务ID
|
||||
*/
|
||||
private String videoId;
|
||||
/**
|
||||
* 视频语言编码 如 zh-CN, en-US
|
||||
*/
|
||||
//private String languageCode;
|
||||
/**
|
||||
* 课程ID
|
||||
*/
|
||||
private String courseId;
|
||||
|
||||
/**
|
||||
* 视频标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 原始视频URL
|
||||
*/
|
||||
private String originalUrl;
|
||||
|
||||
/**
|
||||
* 视频时长(秒)
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 视频格式 mp4/avi等
|
||||
*/
|
||||
private String format;
|
||||
|
||||
/**
|
||||
* 状态 0-待处理 1-处理中 2-已完成 3-失败
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 是否已审核 0-否 1-是
|
||||
*/
|
||||
private Integer isReviewed;
|
||||
|
||||
/**
|
||||
* 审核人ID
|
||||
*/
|
||||
private String reviewerId;
|
||||
|
||||
/**
|
||||
* 审核时间
|
||||
*/
|
||||
private Date reviewedAt;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Date createdAt;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Date updatedAt;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import javax.persistence.Transient;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @date 2025年11月18日
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BoeaiCourseDto {
|
||||
List<BoeaiVideoResourceDto> boeaiVideoResourceReqList;
|
||||
|
||||
|
||||
List<String> courseVideoIds;
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 课程ID
|
||||
*/
|
||||
private String courseId;
|
||||
/**
|
||||
* 视频ID
|
||||
*/
|
||||
private String videoId;
|
||||
|
||||
/**
|
||||
* 课程标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 讲师名称
|
||||
*/
|
||||
private String instructor;
|
||||
|
||||
/**
|
||||
* 章节数
|
||||
*/
|
||||
private Integer chapterCount;
|
||||
|
||||
/**
|
||||
* 视频数
|
||||
*/
|
||||
private Integer videoCount;
|
||||
|
||||
/**
|
||||
* 总时长(秒)
|
||||
*/
|
||||
private Integer totalDuration;
|
||||
|
||||
private Date createdAt;
|
||||
|
||||
private Date updatedAt;
|
||||
|
||||
/**
|
||||
* 课程描述
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 视频语言编码 如 zh-CN, en-US
|
||||
*/
|
||||
private String languageCode;
|
||||
|
||||
/**
|
||||
* 0:关闭;1:打开;
|
||||
* */
|
||||
|
||||
private Integer aiSet;
|
||||
/**
|
||||
* 摘要 0:上架;1:下架;
|
||||
* */
|
||||
|
||||
private Integer aiAbstract;
|
||||
/**
|
||||
* 文稿 0:上架;1:下架;
|
||||
* */
|
||||
|
||||
private Integer aiDraft;
|
||||
/**
|
||||
* 翻译 0:上架;1:下架;
|
||||
* */
|
||||
|
||||
private Integer aiTranslate;
|
||||
|
||||
private Integer languageStatus;
|
||||
//摘要
|
||||
private String summaryContent;
|
||||
//摘要状态 0:下架;1:上架
|
||||
private Integer summaryStatus;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Huang Run
|
||||
* @date 2025年11月19日
|
||||
*/
|
||||
@Data
|
||||
public class BoeaiCourseParamsReq implements Serializable {
|
||||
List<BoeaiCourseDto> boeaiCourseReqList;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author
|
||||
* 视频字幕表
|
||||
*/
|
||||
@Data
|
||||
public class BoeaiSubtitleRsp implements Serializable {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 关联视频ID
|
||||
*/
|
||||
private String videoId;
|
||||
|
||||
/**
|
||||
* 字幕语言(如 zh-CN, en-US)
|
||||
*/
|
||||
private String language;
|
||||
|
||||
/**
|
||||
* WebVTT字幕文件URL(OSS地址)
|
||||
*/
|
||||
private String subtitleUrl;
|
||||
|
||||
/**
|
||||
* 是否原始字幕 0-翻译 1-原始
|
||||
*/
|
||||
private Byte isOriginal;
|
||||
|
||||
/**
|
||||
* 源语言/翻译来源(如 zh-CN)
|
||||
*/
|
||||
private String sourceLanguage;
|
||||
|
||||
/**
|
||||
* 翻译引擎(llm/google/deepl)
|
||||
*/
|
||||
private String translateEngine;
|
||||
|
||||
/**
|
||||
* 状态(0-待生成 1-生成中 2-已完成 3-失败)
|
||||
*/
|
||||
private Boolean status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createdAt;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updatedAt;
|
||||
|
||||
/**
|
||||
* 字幕数据(JSON 数组格式,包含时间戳和翻译文本)
|
||||
*/
|
||||
private String subtitleData;
|
||||
|
||||
/**
|
||||
* WebVTT 格式字幕内容
|
||||
*/
|
||||
private String vttContent;
|
||||
|
||||
/**
|
||||
* 错误信息
|
||||
*/
|
||||
private String errorMsg;
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.xboe.module.course.dto;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @date 2025年11月18日
|
||||
*/
|
||||
@Data
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class BoeaiVideoResourceDto {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 视频业务ID
|
||||
*/
|
||||
private String videoId;
|
||||
/**
|
||||
* 课程ID
|
||||
*/
|
||||
private String courseId;
|
||||
|
||||
/**
|
||||
* 视频标题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 原始视频URL
|
||||
*/
|
||||
private String originalUrl;
|
||||
|
||||
/**
|
||||
* 视频时长(秒)
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
private Long fileSize;
|
||||
|
||||
/**
|
||||
* 视频格式 mp4/avi等
|
||||
*/
|
||||
private String format;
|
||||
|
||||
/**
|
||||
* 状态 0-待处理 1-处理中 2-已完成 3-失败
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 是否已审核 0-否 1-是
|
||||
*/
|
||||
private Integer isReviewed;
|
||||
|
||||
/**
|
||||
* 审核人ID
|
||||
*/
|
||||
private String reviewerId;
|
||||
|
||||
/**
|
||||
* 审核时间
|
||||
*/
|
||||
private Date reviewedAt;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Date createdAt;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private Date updatedAt;
|
||||
}
|
||||
@@ -31,6 +31,11 @@ public class CourseFullDto {
|
||||
* 课程的信息
|
||||
*/
|
||||
private Course course;
|
||||
|
||||
/**
|
||||
* 批量课程AI设置课
|
||||
*/
|
||||
private List<Course> courseList;
|
||||
|
||||
/**
|
||||
* 课程资源信息
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.xboe.module.course.entity;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
@@ -216,6 +217,47 @@ public class Course extends BaseEntity {
|
||||
@Column(name = "device",length = 1)
|
||||
private Integer device;
|
||||
|
||||
|
||||
/**
|
||||
* 0:关闭;1:打开;
|
||||
* */
|
||||
// @Column(name = "ai_set",length = 1)
|
||||
@Transient
|
||||
private Integer aiSet;
|
||||
/**
|
||||
* 摘要 0:上架;1:下架;
|
||||
* */
|
||||
//@Column(name = "ai_abstract",length = 1)
|
||||
@Transient
|
||||
private Integer aiAbstract;
|
||||
/**
|
||||
* 文稿 0:上架;1:下架;
|
||||
* */
|
||||
// @Column(name = "ai_draft",length = 1)
|
||||
@Transient
|
||||
private Integer aiDraft;
|
||||
/**
|
||||
* 翻译 0:上架;1:下架;
|
||||
* */
|
||||
//@Column(name = "ai_translate",length = 1)
|
||||
@Transient
|
||||
private Integer aiTranslate;
|
||||
@Transient
|
||||
private Integer languageStatus;
|
||||
/**
|
||||
* 语言 zh-CN,en-US,ja-JP,ko-KR
|
||||
* */
|
||||
//Column(name = "language_code",length = 120)
|
||||
@Transient
|
||||
private List<String> languageCode;
|
||||
|
||||
//摘要
|
||||
@Transient
|
||||
private String summaryContent;
|
||||
//摘要状态 0:下架;1:上架
|
||||
@Transient
|
||||
private Integer summaryStatus;
|
||||
|
||||
/**
|
||||
* 课程状态,多人审核机制,所以这里并没有审核通过与不通过的状态了
|
||||
* 课程状态 1:未提交(草稿);2:已提交;3: 审核未通过,5审核完成
|
||||
@@ -406,6 +448,13 @@ public class Course extends BaseEntity {
|
||||
@Transient
|
||||
private Boolean isTip;
|
||||
|
||||
@Transient
|
||||
private String auditUser;
|
||||
|
||||
@Transient
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private LocalDateTime auditTime;
|
||||
|
||||
public Course(String id,String name,String summary,String coverImg,String sysCreateAid,String sysCreateBy,Integer type,LocalDateTime publishTime){
|
||||
super.setId(id);
|
||||
this.name=name;
|
||||
|
||||
@@ -8,9 +8,12 @@ import javax.persistence.Transient;
|
||||
import com.xboe.core.SysConstant;
|
||||
import com.xboe.core.orm.BaseEntity;
|
||||
|
||||
import com.xboe.module.course.dto.BoeaiSubtitleRsp;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 课程内容表
|
||||
* */
|
||||
@@ -93,6 +96,9 @@ public class CourseContent extends BaseEntity {
|
||||
/**用于学习时的状态显示,非存储字段*/
|
||||
@Transient
|
||||
private Integer status;
|
||||
|
||||
@Transient
|
||||
List<BoeaiSubtitleRsp> boeaiSubtitleRspList;
|
||||
|
||||
public CourseContent() {
|
||||
|
||||
|
||||
@@ -55,6 +55,8 @@ public interface ICourseHRBPAuditService {
|
||||
* @return
|
||||
*/
|
||||
PageList<CourseHRBPAudit> pageList(Integer pageIndex, Integer pageSize,int userType, CourseHRBPAudit info);
|
||||
|
||||
|
||||
|
||||
List<CourseHRBPAudit> listByCourseIds(List<String> courseIdList);
|
||||
|
||||
}
|
||||
|
||||
@@ -263,4 +263,7 @@ public class CourseHRBPAuditServiceImpl implements ICourseHRBPAuditService {
|
||||
return courseHRBPAuditDao.get(id);
|
||||
}
|
||||
|
||||
public List<CourseHRBPAudit> listByCourseIds(List<String> courseIdList){
|
||||
return courseHRBPAuditDao.findList(FieldFilters.in("courseId",courseIdList));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,14 +24,19 @@ import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
//import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.xboe.api.ThirdApi;
|
||||
import com.xboe.core.orm.*;
|
||||
import com.xboe.module.course.dao.*;
|
||||
import com.xboe.module.course.dto.*;
|
||||
import com.xboe.module.course.entity.*;
|
||||
import com.xboe.module.course.dao.*;
|
||||
import com.xboe.module.course.dto.CourseTagRelationDto;
|
||||
import com.xboe.module.course.entity.*;
|
||||
import com.xboe.module.course.service.ICourseTagService;
|
||||
import com.xboe.module.course.utils.HttpUtils;
|
||||
import com.xboe.school.study.dao.StudyCourseDao;
|
||||
import com.xboe.school.study.entity.StudyCourse;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -43,6 +48,7 @@ import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
|
||||
import org.hibernate.mapping.IdGenerator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
@@ -56,9 +62,6 @@ import com.xboe.common.beans.KeyValue;
|
||||
import com.xboe.common.utils.IDGenerator;
|
||||
import com.xboe.common.utils.StringUtil;
|
||||
import com.xboe.core.event.IEventDataSender;
|
||||
import com.xboe.module.course.dto.CourseFullDto;
|
||||
import com.xboe.module.course.dto.CourseQueryDto;
|
||||
import com.xboe.module.course.dto.RankingDto;
|
||||
import com.xboe.module.course.service.ICourseFullTextSearch;
|
||||
import com.xboe.module.course.service.ICourseService;
|
||||
import com.xboe.module.interaction.service.ICourseGradeService;
|
||||
@@ -134,6 +137,9 @@ public class CourseServiceImpl implements ICourseService {
|
||||
@Resource
|
||||
private ModifyLogDao modifyLogDao;
|
||||
|
||||
@Value("${kjb.aicoreUrl}")
|
||||
private String aicoreUrl;
|
||||
|
||||
/**
|
||||
* 生成过滤条件
|
||||
*
|
||||
@@ -460,6 +466,13 @@ public class CourseServiceImpl implements ICourseService {
|
||||
PageList<Course> rs = new PageList<>();
|
||||
rs.setCount(collect.size());
|
||||
rs.setList(paginate);
|
||||
//log.info("78888888888888888888 "+paginate.isEmpty());
|
||||
if (paginate != null && !paginate.isEmpty()) {
|
||||
log.info("-----KJB------ getCourse");
|
||||
for (Course course : paginate) {
|
||||
this.getCourseFromKJB(course);
|
||||
}
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
}
|
||||
@@ -469,9 +482,46 @@ public class CourseServiceImpl implements ICourseService {
|
||||
rs.setCount(listByFilters2.size());
|
||||
rs.setPageSize(pageSize);
|
||||
rs.setList(paginate);
|
||||
|
||||
if (paginate != null && !paginate.isEmpty()) {
|
||||
log.info("-----KJB------ getCourse");
|
||||
for (Course course : paginate) {
|
||||
this.getCourseFromKJB(course);
|
||||
}
|
||||
}
|
||||
return rs;
|
||||
}
|
||||
|
||||
public void getCourseFromKJB(Course course){
|
||||
String courseId = course.getId();
|
||||
log.info("------------KJB---获取课程信息---------: courseId = " + courseId);
|
||||
//String url = env.getProperty("kjb.url") + "/api/course/getCourseFromKJB?courseId=" + courseId;
|
||||
String url = aicoreUrl + "/aiVideo/getCourseList";
|
||||
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
JSONArray courseIds = new JSONArray();
|
||||
courseIds.add(courseId);
|
||||
jsonObject.put("courseIds", courseIds);
|
||||
String result = HttpUtils.sendMessage(jsonObject.toJSONString(), url);
|
||||
log.info("---KJB --getCourseList 接口返回结果result: "+result);
|
||||
JSONObject jsonResult = JSONObject.parseObject(result);
|
||||
String data = jsonResult.getString("rows");
|
||||
|
||||
List<BoeaiCourseDto> boeaiCourseDto = JSON.parseArray(data, BoeaiCourseDto.class);
|
||||
//List<BoeaiCourseDto> aiVideoResourceRsp = Collections.singletonList(JSON.parseObject(data, BoeaiCourseDto.class));
|
||||
if(boeaiCourseDto != null && !boeaiCourseDto.isEmpty()){
|
||||
log.info("KJB摘要信息:"+boeaiCourseDto.get(0).getSummaryContent());
|
||||
course.setSummaryContent(boeaiCourseDto.get(0).getSummaryContent());
|
||||
course.setSummaryStatus(boeaiCourseDto.get(0).getSummaryStatus());
|
||||
course.setAiSet(boeaiCourseDto.get(0).getAiSet());
|
||||
course.setAiAbstract(boeaiCourseDto.get(0).getAiAbstract());
|
||||
course.setAiDraft(boeaiCourseDto.get(0).getAiDraft());
|
||||
course.setAiTranslate(boeaiCourseDto.get(0).getAiTranslate());
|
||||
course.setLanguageStatus(boeaiCourseDto.get(0).getLanguageStatus());
|
||||
course.setLanguageCode(boeaiCourseDto.get(0).getLanguageCode() == null ? new ArrayList<>() :Arrays.asList(boeaiCourseDto.get(0).getLanguageCode().split(",")));
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> getSeache(CourseQueryDto dto) {
|
||||
//需要设置为隐藏的课程id
|
||||
Set<String> list = new HashSet<>();
|
||||
@@ -1114,6 +1164,7 @@ public class CourseServiceImpl implements ICourseService {
|
||||
hrbpAudit.setCourseId(c.getId());
|
||||
hrbpAudit.setAddTime(LocalDateTime.now());
|
||||
hrbpAudit.setAid(full.getAuditUser().getAid());
|
||||
hrbpAudit.setAuditUser(full.getAuditUser().getName());//提前保存审核hrbp的名字,用于显示下一步审核人
|
||||
hrbpAudit.setAuditRemark("");
|
||||
hrbpAudit.setForward(CourseHRBPAudit.FORWARD_NONE);
|
||||
hrbpAudit.setStatus(CourseHRBPAudit.STATUS_NONE);
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.xboe.module.course.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* @author
|
||||
* @date 2025年11月18日
|
||||
*/
|
||||
@Slf4j
|
||||
public class HttpUtils {
|
||||
public static String sendMessage(String message,String url) {
|
||||
//log.info("----------------发送消息开始 -------");
|
||||
log.info("----------------发送消息参数 -------{},url:{}",message,url);
|
||||
// POST 请求
|
||||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
|
||||
// POST 请求
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
//HttpPost httpPost = new HttpPost("https://jsonplaceholder.typicode.com/posts");
|
||||
httpPost.setEntity(new StringEntity(message, StandardCharsets.UTF_8));
|
||||
httpPost.setHeader("Content-Type", "application/json; charset=utf-8");
|
||||
httpPost.setHeader("Accept", "application/json");
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
|
||||
HttpEntity responseEntity = response.getEntity();
|
||||
String responseBody = "";
|
||||
if (responseEntity != null) {
|
||||
responseBody = EntityUtils.toString(responseEntity, StandardCharsets.UTF_8);
|
||||
System.out.println("Response Body: " + responseBody);
|
||||
}
|
||||
log.info("---------------发送消息响应 -------{}",responseBody);
|
||||
return responseBody;
|
||||
// System.out.println("状态码: " + response.getCode());
|
||||
// System.out.println("响应体: " + EntityUtils.toString(response.getEntity()));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("KJB-接口调用失败",e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,9 @@ import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.nacos.shaded.com.google.common.util.concurrent.RateLimiter;
|
||||
import com.boe.feign.api.infrastructure.entity.CommonSearchVo;
|
||||
import com.boe.feign.api.infrastructure.entity.Dict;
|
||||
@@ -16,6 +19,11 @@ import com.xboe.api.ThirdApi;
|
||||
import com.xboe.constants.CacheName;
|
||||
import com.xboe.module.course.entity.*;
|
||||
import com.xboe.module.course.service.ICourseTagService;
|
||||
import com.xboe.module.course.api.CourseFullTextApi;
|
||||
import com.xboe.module.course.dto.AiVideoResourceRsp;
|
||||
import com.xboe.module.course.dto.BoeaiSubtitleRsp;
|
||||
import com.xboe.module.course.dto.CourseFullText;
|
||||
import com.xboe.module.course.utils.HttpUtils;
|
||||
import com.xboe.module.course.vo.TeacherVo;
|
||||
import com.xboe.module.usergroup.service.IUserGroupService;
|
||||
import com.xboe.school.study.dao.StudyCourseDao;
|
||||
@@ -24,6 +32,7 @@ import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@@ -62,7 +71,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
//添加AI课程学习内容
|
||||
/**
|
||||
* 课程学习的内容
|
||||
*/
|
||||
@@ -103,6 +112,12 @@ public class StudyCourseApi extends ApiBaseController{
|
||||
@Resource
|
||||
private ICourseTagService courseTagService;
|
||||
|
||||
@Autowired
|
||||
CourseFullTextApi courseFullTextApi;
|
||||
|
||||
@Value("${kjb.aicoreUrl}")
|
||||
private String aicoreUrl;
|
||||
|
||||
/**
|
||||
* 用于查询课程的学习记录
|
||||
* @param pager
|
||||
@@ -176,8 +191,15 @@ public class StudyCourseApi extends ApiBaseController{
|
||||
String tags = tagList.stream().map(CourseTag::getTagName).collect(Collectors.joining(","));
|
||||
course1.setTags(tags);
|
||||
}
|
||||
|
||||
//获取课程AI详情
|
||||
log.info("---- KJB 获取课程详情 ---");
|
||||
CourseFullText courseFullText = new CourseFullText();
|
||||
courseFullText.setId(cid);
|
||||
courseFullTextApi.getCourseFromKJB(courseFullText,course);
|
||||
rs.put("course",course1);
|
||||
|
||||
//rs.put("course",course);
|
||||
|
||||
List<CourseCrowd> courseCrowdList = courseService.findCrowdByCourseId(cid);
|
||||
if(crowd!=null && crowd) {
|
||||
rs.put("crowds",courseCrowdList);
|
||||
@@ -213,7 +235,14 @@ public class StudyCourseApi extends ApiBaseController{
|
||||
List<CourseContent> cclist=contentService.getByCourseId(cid);
|
||||
List<CourseSection> sectionlist=sectionService.getByCourseId(cid);
|
||||
List<CourseTeacher> teachers=courseService.findTeachersByCourseId(cid);
|
||||
|
||||
|
||||
if(cclist!=null && !cclist.isEmpty()){
|
||||
for (CourseContent cc : cclist) {
|
||||
log.info("根据视频信息查询AI字幕数据:cc = " + cc.toString());
|
||||
this.getVtt( cid , cc);
|
||||
}
|
||||
}
|
||||
|
||||
//获取教师的介绍信息,因为一门课程 的教师不会太多,所以这里简单直接遍历查询,后续再优化
|
||||
for(CourseTeacher ct : teachers) {
|
||||
Teacher t = teacherService.get(ct.getTeacherId());
|
||||
@@ -239,6 +268,7 @@ public class StudyCourseApi extends ApiBaseController{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rs.put("isCrowd",pass);
|
||||
rs.put("contents",cclist);
|
||||
rs.put("sections",sectionlist);
|
||||
@@ -328,6 +358,35 @@ public class StudyCourseApi extends ApiBaseController{
|
||||
return success(rs);
|
||||
}
|
||||
|
||||
public void getVtt(String couirseId, CourseContent cc){
|
||||
log.info("------KJB --------- getVtt couirseId = " + couirseId + " videoId = " + cc.getId());
|
||||
String url = aicoreUrl + "/aiVideo/getVideoVtt";
|
||||
JSONObject message = new JSONObject();
|
||||
message.put("courseId", couirseId);
|
||||
message.put("videoId", cc.getId());
|
||||
String result = HttpUtils.sendMessage(message.toJSONString(), url);
|
||||
if(StringUtils.isBlank(result)){
|
||||
log.error("未获取到AI字幕信息");
|
||||
return;
|
||||
}
|
||||
//将返回string 转为json对象
|
||||
JSONObject jsonObject = JSONObject.parseObject(result);
|
||||
String data = jsonObject.getString("data");
|
||||
//json字符串转为实体对象
|
||||
AiVideoResourceRsp aiVideoResourceRsp = JSON.parseObject(data, AiVideoResourceRsp.class);
|
||||
log.info("aiVideoResourceRsp:"+aiVideoResourceRsp);
|
||||
//循环json对象
|
||||
if(aiVideoResourceRsp != null){
|
||||
cc.setBoeaiSubtitleRspList(aiVideoResourceRsp.getBoeaiSubtitleRspList());
|
||||
// for (int i = 0; i < rows.size(); i++) {
|
||||
// JSONObject row = rows.getJSONObject(i);
|
||||
// //将json对象转为实体对象 CourseContentVtt
|
||||
// BoeaiSubtitleRsp vtt = row.toJavaObject(BoeaiSubtitleRsp.class);
|
||||
// boeaiSubtitleRspList.add(vtt);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在打开课程详细页面的时候,调用此接口,返回学习课程信息,上次学习的位置。<br/>
|
||||
* 如果没有就返回空<br>
|
||||
|
||||
@@ -343,4 +343,8 @@ aop-log-record:
|
||||
password: admin
|
||||
elasticsearch:
|
||||
host: 10.251.88.218
|
||||
port: 9200
|
||||
port: 9200
|
||||
|
||||
kjb:
|
||||
aicoreUrl: http://10.232.28.95:8080
|
||||
videoUrlPrefix: https://u.boe.com/upload
|
||||
@@ -160,6 +160,10 @@ jasypt:
|
||||
boe:
|
||||
domain: http://10.251.186.27
|
||||
|
||||
kjb:
|
||||
aicoreUrl: http://10.251.186.27:8088
|
||||
videoUrlPrefix: https://u-pre.boe.com/upload
|
||||
|
||||
ok:
|
||||
http:
|
||||
connect-timeout: 30
|
||||
|
||||
Reference in New Issue
Block a user