Compare commits
2 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
669c79e65d | |
|
|
883963dbe6 |
70
pom.xml
70
pom.xml
|
|
@ -164,41 +164,41 @@
|
|||
<dependency>
|
||||
<groupId>com.github.pagehelper</groupId>
|
||||
<artifactId>pagehelper-spring-boot-starter</artifactId>
|
||||
<version>1.4.2</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.mybatis</groupId>
|
||||
<artifactId>mybatis.spring</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>log4j-slf4j-impl</groupId>
|
||||
<artifactId>org.apache.logging.log4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>log4j-over-slf4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>jul-to-slf4j</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<version>2.1.0</version>
|
||||
<!-- <exclusions>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>org.mybatis</groupId>-->
|
||||
<!-- <artifactId>mybatis</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>org.mybatis</groupId>-->
|
||||
<!-- <artifactId>mybatis.spring</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>log4j-slf4j-impl</groupId>-->
|
||||
<!-- <artifactId>org.apache.logging.log4j</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>org.slf4j</groupId>-->
|
||||
<!-- <artifactId>slf4j-api</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>ch.qos.logback</groupId>-->
|
||||
<!-- <artifactId>logback-classic</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>org.slf4j</groupId>-->
|
||||
<!-- <artifactId>log4j-over-slf4j</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>org.slf4j</groupId>-->
|
||||
<!-- <artifactId>jul-to-slf4j</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- <exclusion>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-logging</artifactId>-->
|
||||
<!-- </exclusion>-->
|
||||
<!-- </exclusions>-->
|
||||
</dependency>
|
||||
|
||||
<!-- Sa-Token 核心依赖 -->
|
||||
|
|
|
|||
|
|
@ -16,16 +16,16 @@ import org.springframework.context.annotation.Configuration;
|
|||
@Configuration
|
||||
public class RedissonConfig {
|
||||
|
||||
@Value("${common.redis.host}")
|
||||
@Value("${spring.redis.host}")
|
||||
private String host;
|
||||
|
||||
@Value("${common.redis.port}")
|
||||
@Value("${spring.redis.port}")
|
||||
private int port;
|
||||
|
||||
@Value("${common.redis.password}")
|
||||
@Value("${spring.redis.password}")
|
||||
private String password;
|
||||
|
||||
@Value("${common.redis.database}")
|
||||
@Value("${spring.redis.database}")
|
||||
private int database;
|
||||
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
|
|
@ -35,12 +35,16 @@ public class RedissonConfig {
|
|||
// 单节点模式
|
||||
config.useSingleServer()
|
||||
.setAddress("redis://" + host + ":" + port)
|
||||
.setPassword(password)
|
||||
//.setPassword(password)
|
||||
.setDatabase(database)
|
||||
.setConnectionMinimumIdleSize(5)
|
||||
.setConnectionPoolSize(20)
|
||||
.setTimeout(10000);
|
||||
|
||||
// 处理密码:如果有密码则设置,否则不设置
|
||||
if (password != null && !password.isEmpty()) {
|
||||
config.useSingleServer().setPassword(password);
|
||||
}
|
||||
return Redisson.create(config);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
package com.kexue.skills.controller;
|
||||
|
||||
import com.kexue.skills.common.CommonResult;
|
||||
import com.kexue.skills.entity.request.SkillRequest;
|
||||
import com.kexue.skills.entity.response.SkillResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 技能生成控制器
|
||||
|
|
@ -13,6 +21,7 @@ import javax.annotation.Resource;
|
|||
* @author 维哥
|
||||
* @since 2026-01-28
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("api/skillGen")
|
||||
@Tag(name = "技能生成 Api")
|
||||
|
|
@ -23,17 +32,65 @@ public class SkillGenController {
|
|||
private com.kexue.skills.service.SkillGenService skillGenService;
|
||||
|
||||
/**
|
||||
* 生成技能
|
||||
* 预生成skill,产生相关的描述信息
|
||||
*
|
||||
* @param request 生成请求
|
||||
* @return 生成结果
|
||||
*/
|
||||
@PostMapping("/generate")
|
||||
@Operation(summary = "生成技能", description = "生成技能")
|
||||
public CommonResult<SkillResponse> generate(@RequestBody com.kexue.skills.entity.request.SkillGenRequest request) {
|
||||
return CommonResult.success(skillGenService.generateSkill(request));
|
||||
@PostMapping("/preGenerate")
|
||||
@Operation(summary = "预生成技能", description = "预生成技能")
|
||||
public CommonResult<SkillResponse> preGenerate(com.kexue.skills.entity.request.SkillGenRequest request,
|
||||
@RequestPart(value = "file", required = false) MultipartFile file) {
|
||||
try {
|
||||
// 如果上传了文件,读取文件内容
|
||||
if (file != null && !file.isEmpty()) {
|
||||
String fileContent = readTextFile(file);
|
||||
|
||||
// 将文件内容设置到请求对象中(需要修改 SkillGenRequest)
|
||||
// request.setFileContent(fileContent);
|
||||
// request.setFileName(file.getOriginalFilename());
|
||||
// request.setFileSize(file.getSize());
|
||||
// request.setFileType(file.getContentType());
|
||||
|
||||
log.info("文件上传成功: {}, 大小: {} bytes, 类型: {}",
|
||||
file.getOriginalFilename(), file.getSize(), file.getContentType());
|
||||
return CommonResult.success(skillGenService.preGenerateSkill(request,fileContent));
|
||||
}
|
||||
|
||||
return CommonResult.success(skillGenService.preGenerateSkill(request,null));
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("处理文件上传失败", e);
|
||||
return CommonResult.failed("文件处理失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文本文件内容为字符串
|
||||
* @param file 上传的文件
|
||||
* @return 文件内容字符串
|
||||
*/
|
||||
private String readTextFile(MultipartFile file) throws IOException {
|
||||
// 使用 Apache Commons IO
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 生成技能
|
||||
*
|
||||
* @param
|
||||
* @return 分析结果
|
||||
*/
|
||||
@PostMapping("/generate")
|
||||
@Operation(summary = "生成技能", description = "生成技能")
|
||||
public CommonResult<SkillResponse> generate(@RequestBody SkillRequest request) {
|
||||
Integer skillId=request.getSkillId();
|
||||
return CommonResult.success(skillGenService.generateSkill(skillId));
|
||||
}
|
||||
/**
|
||||
* 分析技能
|
||||
*
|
||||
|
|
|
|||
|
|
@ -132,6 +132,17 @@ public class CmsContent extends BaseEntity implements Serializable {
|
|||
@Schema(description ="父分类ID")
|
||||
private Long parentCategoryId;
|
||||
|
||||
@Schema(description ="用户上传的文件内容")
|
||||
private String userUpload;
|
||||
|
||||
@Schema(description ="标签列表,逗号分隔")
|
||||
private String tagIds;
|
||||
|
||||
@Schema(description ="技能的文件地址")
|
||||
private String skillPath;
|
||||
|
||||
@Schema(description ="技能文件内容")
|
||||
private String skillContent;
|
||||
// 用于接收前端发送的分类ID数组
|
||||
@JsonProperty("categoryIds")
|
||||
public void setCategoryIdsFromArray(List<Long> categoryIdList) {
|
||||
|
|
|
|||
|
|
@ -44,4 +44,7 @@ public class CmsContentDto extends BaseQueryDto {
|
|||
|
||||
private Long parentCategoryId;
|
||||
|
||||
private String userLoad;
|
||||
|
||||
private String tagIds;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ public class SkillRequest implements Serializable {
|
|||
private double temperature;
|
||||
private int max_tokens;
|
||||
private ResponseFormat response_format;
|
||||
private int skillId;
|
||||
private int contentId;
|
||||
|
||||
public SkillRequest(boolean useDefaultSettings) {
|
||||
if (useDefaultSettings) {
|
||||
|
|
@ -52,7 +54,7 @@ public class SkillRequest implements Serializable {
|
|||
systemMessage.setRole("system");
|
||||
systemMessage.setContent("你是一个专业的AI技能设计助手。请严格按照指定的JSON格式输出,仅包含要求的字段,以中文形式返回。");
|
||||
this.messages.add(systemMessage);
|
||||
|
||||
//获取系统存在的标签
|
||||
Message userMessage = new Message();
|
||||
userMessage.setRole("user");
|
||||
userMessage.setContent("主题:"+ prompt +"。请根据agent skills撰写规范帮我生成这个skill的名称、描述,并从以下标签列表中选择一个或者多个标签:\"软件开发,系统集成,网络工程,云计算,大数据,人工智能,物联网,区块链,信息安全,运维服务,测试认证,IT 咨询,外包服务,电商技术,移动开发,前端开发,后端开发,全栈开发,数据库管理\"。输出json格式,仅输出以上所提到的名称、描述、标签,节点名称分别为name、description、tags,节点内容以中文形式返回。");
|
||||
|
|
@ -65,6 +67,53 @@ public class SkillRequest implements Serializable {
|
|||
this.response_format.setType("json_object");
|
||||
}
|
||||
}
|
||||
// String systemPrompt = """
|
||||
// 请严格按照以下格式输出信息:
|
||||
//
|
||||
// ## 标题 ##
|
||||
// [这里填写标题]
|
||||
//
|
||||
// ## 关键点 ##
|
||||
// - 第一点
|
||||
// - 第二点
|
||||
// - 第三点
|
||||
//
|
||||
// ## 详细说明 ##
|
||||
// [这里填写详细说明]
|
||||
//
|
||||
// ## 建议 ##
|
||||
// [这里填写建议]
|
||||
//
|
||||
// 不要添加任何其他内容,严格按照上述格式输出。
|
||||
// """;
|
||||
|
||||
public SkillRequest(boolean useDefaultSettings, String model, Double temperature, Integer maxTokens,String prompt,String description,String tags,String userUpload) {
|
||||
String systemPrompt2="你是一个专业的AI技能设计助手。请基于用户提供的Skill名称、描述、标签,生成完整的skills.md文档内容,仅输出skills.md本体内容,无需其他额外说明。";
|
||||
if (useDefaultSettings) {
|
||||
this.model = model;
|
||||
this.messages = new ArrayList<>();
|
||||
|
||||
Message systemMessage = new Message();
|
||||
systemMessage.setRole("system");
|
||||
systemMessage.setContent(systemPrompt2);
|
||||
this.messages.add(systemMessage);
|
||||
|
||||
Message userMessage = new Message();
|
||||
userMessage.setRole("user");
|
||||
if(userUpload!=null&&!userUpload.equals("")){
|
||||
userMessage.setContent("请根据以下Skill信息生成skills.md文档内容:Skill名称:"+prompt+"Skill描述:"+description+"Skill标签:"+tags+"。请仅输出skills.md本体内容,无需其他额外说明。内容的生成需要参考如下内容:"+userUpload);
|
||||
}else{
|
||||
userMessage.setContent("请根据以下Skill信息生成skills.md文档内容:Skill名称:"+prompt+"Skill描述:"+description+"Skill标签:"+tags+"。请仅输出skills.md本体内容,无需其他额外说明。");
|
||||
}
|
||||
this.messages.add(userMessage);
|
||||
|
||||
this.temperature = temperature;
|
||||
this.max_tokens = maxTokens;
|
||||
|
||||
// this.response_format = new ResponseFormat();
|
||||
// this.response_format.setType("json_object");
|
||||
}
|
||||
}
|
||||
|
||||
public static SkillRequest createDefault() {
|
||||
return new SkillRequest(true);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.kexue.skills.entity.response;
|
||||
|
||||
import com.kexue.skills.entity.CmsContent;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
|
@ -12,4 +13,6 @@ public class SkillResponse implements Serializable {
|
|||
private String name;
|
||||
private String description;
|
||||
private List<String> tags;
|
||||
private Long skillId;
|
||||
private List<CmsContent> cmsContents;
|
||||
}
|
||||
|
|
@ -13,12 +13,16 @@ import com.kexue.skills.entity.response.SkillResponse;
|
|||
public interface SkillGenService {
|
||||
|
||||
/**
|
||||
* 生成技能
|
||||
* 预生成技能
|
||||
*
|
||||
* @param request 生成请求
|
||||
* @param fileContent 上传的文件内容
|
||||
* @return 生成结果
|
||||
*/
|
||||
SkillResponse generateSkill(SkillGenRequest request);
|
||||
SkillResponse preGenerateSkill(SkillGenRequest request,String fileContent);
|
||||
|
||||
|
||||
SkillResponse generateSkill(Integer skillId);
|
||||
|
||||
/**
|
||||
* 分析技能
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.kexue.skills.service.impl;
|
||||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.github.pagehelper.PageInfo;
|
||||
import com.kexue.skills.entity.CmsContent;
|
||||
|
|
|
|||
|
|
@ -4,16 +4,29 @@ import com.alibaba.fastjson.JSON;
|
|||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.kexue.skills.common.util.HttpUtil;
|
||||
import com.kexue.skills.config.DeepSeekConfig;
|
||||
import com.kexue.skills.entity.CmsContent;
|
||||
import com.kexue.skills.entity.CmsTag;
|
||||
import com.kexue.skills.entity.dto.CmsContentDto;
|
||||
import com.kexue.skills.entity.dto.CmsTagDto;
|
||||
import com.kexue.skills.entity.request.SkillAnalyzeRequest;
|
||||
import com.kexue.skills.entity.request.SkillGenRequest;
|
||||
import com.kexue.skills.entity.request.SkillRequest;
|
||||
import com.kexue.skills.entity.response.SkillResponse;
|
||||
import com.kexue.skills.mapper.CmsContentMapper;
|
||||
import com.kexue.skills.service.CmsContentService;
|
||||
import com.kexue.skills.service.CmsTagService;
|
||||
import com.kexue.skills.service.SkillGenService;
|
||||
import com.kexue.skills.utils.FileManager;
|
||||
import com.kexue.skills.utils.MarkdownProcessor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 技能生成服务实现
|
||||
|
|
@ -28,6 +41,10 @@ public class SkillGenServiceImpl implements SkillGenService {
|
|||
@Autowired
|
||||
private DeepSeekConfig deepSeekConfig;
|
||||
|
||||
@Resource
|
||||
private CmsContentService cmsContentService;
|
||||
@Resource
|
||||
private CmsTagService cmsTagService;
|
||||
/**
|
||||
* 生成技能
|
||||
*
|
||||
|
|
@ -35,8 +52,10 @@ public class SkillGenServiceImpl implements SkillGenService {
|
|||
* @return 生成结果
|
||||
*/
|
||||
@Override
|
||||
public SkillResponse generateSkill(SkillGenRequest request) {
|
||||
log.info("生成技能请求: {}", request);
|
||||
public SkillResponse preGenerateSkill(SkillGenRequest request,String fileContent) {
|
||||
log.info("预生成技能请求: {}", request);
|
||||
// List<CmsTag> tags = cmsTagService.getList(new CmsTagDto());
|
||||
|
||||
String url = deepSeekConfig.getBaseUrl() + "/v1/chat/completions";
|
||||
SkillRequest skillRequest = new SkillRequest(true, deepSeekConfig.getChat().getModel(),
|
||||
deepSeekConfig.getChat().getTemperature(), deepSeekConfig.getChat().getMaxTokens(),request.getPrompt());
|
||||
|
|
@ -61,9 +80,57 @@ public class SkillGenServiceImpl implements SkillGenService {
|
|||
SkillResponse skillResponse = new SkillResponse();
|
||||
skillResponse.setName(skillJson.getString("name"));
|
||||
skillResponse.setDescription(skillJson.getString("description"));
|
||||
skillResponse.setTags(skillJson.getJSONArray("tags").toJavaList(String.class));
|
||||
|
||||
final List<String> tags = skillJson.getJSONArray("tags").toJavaList(String.class);
|
||||
//tags需要解析出id
|
||||
// List<Long> targetTagIds = new ArrayList<>();
|
||||
// List<CmsContent> resultContents =new ArrayList<>();
|
||||
// for (String tag : tags) {
|
||||
// CmsTagDto tagDto = new CmsTagDto();
|
||||
// tagDto.setTagName(tag);
|
||||
// List<CmsTag> list = cmsTagService.getList(tagDto);
|
||||
// if (list != null && !list.isEmpty()) {
|
||||
// targetTagIds.add(list.get(0).getTagId());
|
||||
// }
|
||||
// }
|
||||
skillResponse.setTags(tags);
|
||||
log.info("解析技能响应: {}", skillResponse);
|
||||
//skill需要记录到数据库?
|
||||
CmsContent cmsContent = new CmsContent();
|
||||
cmsContent.setTitle(skillJson.getString("name"));
|
||||
cmsContent.setSummary(skillJson.getString("description"));
|
||||
cmsContent.setContentType(1);
|
||||
cmsContent.setContent(skillJson.getString("content"));
|
||||
cmsContent.setUserUpload(fileContent);
|
||||
if(!tags.isEmpty()){
|
||||
String result=tags.stream()
|
||||
//.map(String::valueOf)
|
||||
.collect(Collectors.joining(","));
|
||||
cmsContent.setTagIds(result);
|
||||
}else{
|
||||
cmsContent.setTagIds("通用技能,");
|
||||
}
|
||||
//记录到数据库
|
||||
final CmsContent insert = cmsContentService.insert(cmsContent);
|
||||
//根据tags在去匹配skill,作为推荐,推荐已经发布的skill
|
||||
List<CmsContent> resultContents =new ArrayList<>();
|
||||
CmsContentDto cmsContentDto = new CmsContentDto();
|
||||
cmsContentDto.setPublishStatus(2);
|
||||
final List<CmsContent> list = cmsContentService.getList(cmsContentDto);
|
||||
for (CmsContent c:list) {
|
||||
List<String> currentTags = Arrays.stream(c.getTagIds().split(","))
|
||||
//.map(Long::valueOf)
|
||||
.toList();
|
||||
if(tags.stream().anyMatch(currentTags::contains)) {
|
||||
//存在交集,需要被推荐
|
||||
resultContents.add(c);
|
||||
}
|
||||
}
|
||||
if(resultContents.isEmpty()&& !list.isEmpty()) {
|
||||
//如果没匹配到,就先给第一个
|
||||
resultContents.add(list.get(0));
|
||||
}
|
||||
skillResponse.setSkillId(insert.getContentId());
|
||||
skillResponse.setCmsContents(resultContents);
|
||||
return skillResponse;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
|
@ -73,6 +140,63 @@ public class SkillGenServiceImpl implements SkillGenService {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SkillResponse generateSkill(Integer skillId) {
|
||||
//根据skillId获取用户预创建时的信息,以此来生成skill文档
|
||||
final CmsContent cmsContent = cmsContentService.queryById(Long.valueOf(skillId));
|
||||
String name = cmsContent.getTitle();
|
||||
String description = cmsContent.getSummary();
|
||||
String userUpload = cmsContent.getUserUpload();
|
||||
String tags=cmsContent.getTagIds();
|
||||
|
||||
|
||||
log.info("生成技能请求: {}", skillId);
|
||||
String url = deepSeekConfig.getBaseUrl() + "/v1/chat/completions";
|
||||
SkillRequest skillRequest = new SkillRequest(true, deepSeekConfig.getChat().getModel(),
|
||||
deepSeekConfig.getChat().getTemperature(), deepSeekConfig.getChat().getMaxTokens(),name,description,tags,userUpload);
|
||||
try {
|
||||
// 发送HTTP请求到deepseek API
|
||||
String deepseekResponse = HttpUtil.sendPostRequest(url, skillRequest, deepSeekConfig.getApiKey(), null);
|
||||
log.info("Deepseek API响应: {}", deepseekResponse);
|
||||
|
||||
// 解析deepseek返回结果
|
||||
JSONObject responseJson = JSON.parseObject(deepseekResponse);
|
||||
List<JSONObject> choices = responseJson.getJSONArray("choices").toJavaList(JSONObject.class);
|
||||
//
|
||||
if (choices != null && !choices.isEmpty()) {
|
||||
// 获取最新的choice(这里是第一个,因为只有一个)
|
||||
JSONObject latestChoice = choices.get(0);
|
||||
JSONObject message = latestChoice.getJSONObject("message");
|
||||
String content = message.getString("content");
|
||||
log.info("输出结果:{}",content);
|
||||
FileManager manager = new FileManager();
|
||||
FileManager.FileInfo fileInfo = manager.processAndWrite(content, name);
|
||||
String storedFilePath = "";
|
||||
if (fileInfo != null) {
|
||||
log.info("文件信息: " + fileInfo);
|
||||
// 可以将文件路径存储到数据库或其他地方
|
||||
storedFilePath = fileInfo.getFilePath();
|
||||
log.info("存储的文件路径: " + storedFilePath);
|
||||
}
|
||||
CmsContent c = new CmsContent();
|
||||
c.setSkillPath(storedFilePath);
|
||||
c.setSkillContent(content);
|
||||
c.setContentId(Long.valueOf(skillId));
|
||||
cmsContentService.update(c);
|
||||
// 解析content中的JSON
|
||||
SkillResponse skillResponse = new SkillResponse();
|
||||
skillResponse.setDescription(MarkdownProcessor.unescapeString(content));
|
||||
skillResponse.setSkillId(Long.valueOf(skillId));
|
||||
return skillResponse;
|
||||
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("调用Deepseek API失败: {}", e.getMessage(), e);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
package com.kexue.skills.utils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.*;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
public class FileManager {
|
||||
private String outputDirectory;
|
||||
private String fileExtension;
|
||||
|
||||
public FileManager() {
|
||||
this.outputDirectory = "skill_output";
|
||||
this.fileExtension = ".md";
|
||||
}
|
||||
|
||||
public FileManager(String outputDirectory, String fileExtension) {
|
||||
this.outputDirectory = outputDirectory;
|
||||
this.fileExtension = fileExtension;
|
||||
}
|
||||
|
||||
// 处理内容并写入文件
|
||||
public FileInfo processAndWrite(String content, String customFileName) {
|
||||
try {
|
||||
// 生成文件名
|
||||
String fileName = generateFileName(customFileName);
|
||||
|
||||
// 写入文件
|
||||
String filePath = writeToFile(content, fileName);
|
||||
|
||||
// 返回文件信息
|
||||
return new FileInfo(filePath, fileName);
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println("写入文件失败: " + e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> parseTagIds(String tagIds) {
|
||||
if (tagIds == null || tagIds.trim().isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
return Arrays.stream(tagIds.split(","))
|
||||
.map(String::trim)
|
||||
.filter(tag -> !tag.isEmpty())
|
||||
.toList();
|
||||
}
|
||||
|
||||
private String generateFileName(String customName) {
|
||||
if (customName != null && !customName.trim().isEmpty()) {
|
||||
return customName.endsWith(fileExtension) ? customName : customName + fileExtension;
|
||||
}
|
||||
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
|
||||
String timestamp = LocalDateTime.now().format(formatter);
|
||||
return "skills_" + timestamp + fileExtension;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private String writeToFile(String content, String fileName) throws IOException {
|
||||
// 创建输出目录
|
||||
Path dirPath = Paths.get(outputDirectory);
|
||||
if (!Files.exists(dirPath)) {
|
||||
Files.createDirectories(dirPath);
|
||||
}
|
||||
|
||||
// 构建完整文件路径
|
||||
Path filePath = dirPath.resolve(fileName);
|
||||
|
||||
// 写入文件
|
||||
Files.writeString(
|
||||
filePath,
|
||||
content,
|
||||
StandardOpenOption.CREATE,
|
||||
StandardOpenOption.TRUNCATE_EXISTING,
|
||||
StandardOpenOption.WRITE
|
||||
);
|
||||
|
||||
return filePath.toAbsolutePath().toString();
|
||||
}
|
||||
|
||||
// 文件信息类
|
||||
public static class FileInfo {
|
||||
private final String filePath;
|
||||
private final String fileName;
|
||||
|
||||
public FileInfo(String filePath, String fileName) {
|
||||
this.filePath = filePath;
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
// getters
|
||||
public String getFilePath() { return filePath; }
|
||||
public String getFileName() { return fileName; }
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("文件: %s, 路径: %s",
|
||||
fileName, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package com.kexue.skills.utils;
|
||||
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
|
||||
public class MarkdownProcessor {
|
||||
|
||||
public static String unescapeString(String escapedString) {
|
||||
// 将 \n 转换为真正的换行符
|
||||
return escapedString.replace("\\n", "\n")
|
||||
.replace("\\t", "\t")
|
||||
.replace("\\\"", "\"");
|
||||
}
|
||||
|
||||
public static String convertToMarkdown(String escapedString) {
|
||||
// 使用Apache Commons Text库
|
||||
return StringEscapeUtils.unescapeJava(escapedString);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,10 +14,10 @@ spring:
|
|||
maximum-pool-size: 30
|
||||
# Redis配置,引用公共配置
|
||||
redis:
|
||||
host: ${common.redis.host}
|
||||
port: ${common.redis.port}
|
||||
password: ${common.redis.password}
|
||||
database: ${common.redis.database}
|
||||
host: 192.168.153.100
|
||||
port: 6379
|
||||
password:
|
||||
database: 1
|
||||
timeout: 10000
|
||||
lettuce:
|
||||
pool:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<configuration scan="true" scanPeriod="60 seconds">
|
||||
|
||||
<!-- 定义日志文件的存储路径 -->
|
||||
<property name="LOG_HOME" value="/data/service/logs/yuelong-portal" />
|
||||
<property name="LOG_HOME" value="./logs" />
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
|
|
@ -13,10 +13,10 @@
|
|||
|
||||
<!-- 每日滚动文件输出 -->
|
||||
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>${LOG_HOME}/hyxp-portal.log</file>
|
||||
<file>${LOG_HOME}/skill.log</file>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<!-- 每天生成一个新的日志文件 -->
|
||||
<fileNamePattern>${LOG_HOME}/hyxp-portal.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<fileNamePattern>${LOG_HOME}/skill.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<!-- 保留 30 天的历史日志文件 -->
|
||||
<maxHistory>30</maxHistory>
|
||||
</rollingPolicy>
|
||||
|
|
|
|||
|
|
@ -118,7 +118,7 @@
|
|||
select
|
||||
content_id, title, subtitle, parent_category_id, content_type, category_ids, summary, content, cover_image, author_id, author_name,
|
||||
reviewer_id, reviewer_name, audit_status, audit_comment, publish_status, publish_time,
|
||||
view_count, like_count, comment_count, sort, is_paid, price, required_points, support_points_pay, is_official, share_count, file_url, icon, background, create_time, update_time, create_by, update_by, delete_flag
|
||||
view_count, like_count, comment_count, sort, is_paid, price, required_points, support_points_pay, is_official, share_count, file_url, icon, background, create_time, update_time, create_by, update_by, delete_flag,user_upload,tag_ids
|
||||
from cms_content
|
||||
<where>
|
||||
<if test="contentId != null">
|
||||
|
|
@ -161,6 +161,12 @@
|
|||
<if test="parentCategoryId != null">
|
||||
and parent_category_id = #{parentCategoryId}
|
||||
</if>
|
||||
<if test="userLoad != null">
|
||||
and userLoad = #{userLoad}
|
||||
</if>
|
||||
<if test="tagIds != null">
|
||||
and tagIds = #{tagIds}
|
||||
</if>
|
||||
</where>
|
||||
order by sort asc, create_time desc
|
||||
</select>
|
||||
|
|
@ -169,10 +175,12 @@
|
|||
<insert id="insert" keyProperty="contentId" useGeneratedKeys="true">
|
||||
insert into cms_content(title, subtitle, parent_category_id, content_type, category_ids, summary, content, cover_image, author_id, author_name,
|
||||
reviewer_id, reviewer_name, audit_status, audit_comment, publish_status, publish_time,
|
||||
view_count, like_count, comment_count, sort, is_paid, price, required_points, support_points_pay, is_official, share_count, file_url, icon, background, create_time, update_time, create_by, update_by, delete_flag)
|
||||
view_count, like_count, comment_count, sort, is_paid, price, required_points, support_points_pay, is_official, share_count, file_url, icon, background, create_time, update_time, create_by, update_by, delete_flag,user_upload,tag_ids)
|
||||
values (#{title}, #{subtitle}, #{parentCategoryId}, #{contentType}, #{categoryIds}, #{summary}, #{content}, #{coverImage}, #{authorId}, #{authorName},
|
||||
#{reviewerId}, #{reviewerName}, #{auditStatus}, #{auditComment}, #{publishStatus}, #{publishTime},
|
||||
#{viewCount}, #{likeCount}, #{commentCount}, #{sort}, #{isPaid}, #{price}, #{requiredPoints}, #{supportPointsPay}, #{isOfficial}, #{shareCount}, #{fileUrl}, #{icon}, #{background}, #{createTime}, #{updateTime}, #{createBy}, #{updateBy}, #{deleteFlag})
|
||||
#{viewCount}, #{likeCount}, #{commentCount}, #{sort}, #{isPaid}, #{price}, #{requiredPoints}, #{supportPointsPay}, #{isOfficial}, #{shareCount}, #{fileUrl}, #{icon}, #{background}, #{createTime}, #{updateTime}, #{createBy}, #{updateBy}, #{deleteFlag},
|
||||
#{userUpload},#{tagIds} )
|
||||
|
||||
</insert>
|
||||
|
||||
<!--通过主键修改数据-->
|
||||
|
|
@ -272,6 +280,12 @@
|
|||
<if test="updateBy != null">
|
||||
update_by = #{updateBy},
|
||||
</if>
|
||||
<if test="skillPath != null">
|
||||
skill_path = #{skillPath},
|
||||
</if>
|
||||
<if test="skillContent != null">
|
||||
skill_content = #{skillContent},
|
||||
</if>
|
||||
</set>
|
||||
where content_id = #{contentId}
|
||||
</update>
|
||||
|
|
|
|||
Loading…
Reference in New Issue