markdown渲染服务的完成

This commit is contained in:
moon 2025-07-29 09:00:56 +08:00
parent 590d0076bd
commit 36c070df1d
6 changed files with 310 additions and 4 deletions

View File

@ -19,12 +19,12 @@
</component>
<component name="JavacSettings">
<option name="ADDITIONAL_OPTIONS_OVERRIDE">
<module name="briChat" options="-parameters" />
<module name="briChat" options="-parameters -Xlint:unchecked" />
<module name="common" options="-parameters" />
<module name="eureka-server" options="-parameters" />
<module name="eureka-server" options="-parameters -Xlint:unchecked" />
<module name="face-proc" options="-parameters" />
<module name="infrastructure" options="-parameters" />
<module name="zuul-server" options="-parameters" />
<module name="infrastructure" options="-parameters -Xlint:unchecked" />
<module name="zuul-server" options="-parameters -Xlint:unchecked" />
</option>
</component>
</project>

View File

@ -0,0 +1,34 @@
package com.bjtds.brichat.controller;
import com.bjtds.brichat.service.MarkdownService;
import com.bjtds.brichat.util.ResultUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
@Api(tags="Markdown标记")
@RestController
@Slf4j
@CrossOrigin(value ="*",maxAge = 3600)
@RequestMapping("/markdown")
public class MarkdownController {
@Autowired
private MarkdownService markdownService;
@ApiOperation("Markdown标记")
@PostMapping("/render")
public ResultUtils replaceMarkdown(@RequestBody String content) throws IOException{
markdownService.saveItem(content) ;
markdownService.processMarkdownFiles(content);
return ResultUtils.success("success");
}
@ApiOperation("获取文件名")
@GetMapping("/getFileName")
public ResultUtils getFileName(@RequestParam String FilePath){
return ResultUtils.success(markdownService.getFileName(FilePath));
}
}

View File

@ -0,0 +1,10 @@
package com.bjtds.brichat.service;
import java.io.IOException;
import java.util.List;
public interface MarkdownService {
void saveItem(String content) throws IOException;
void processMarkdownFiles(String content) throws IOException;
List<String> getFileName(String filePath);
}

View File

@ -0,0 +1,65 @@
package com.bjtds.brichat.service.impl;
import com.bjtds.brichat.service.MarkdownService;
import com.bjtds.brichat.util.FuzzyMatcher;
import com.itextpdf.text.pdf.parser.clipper.Path;
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;
import java.io.File;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class MarkdownServiceImpl implements MarkdownService {
private static final String ITEM_FILE="F:\\project\\bjtdsweb\\ai-manus\\chat-server\\file\\item\\item.md";
private static final String INPUT_DIR="F:\\project\\bjtdsweb\\ai-manus\\chat-server\\file\\input" ;
private static final String OUTPUT_DIR="F:\\project\\bjtdsweb\\ai-manus\\chat-server\\file\\output" ;
@Override
public void saveItem(String content) throws IOException {
Files.write(Paths.get(ITEM_FILE),(content+"\n").getBytes(StandardCharsets.UTF_8), StandardOpenOption.CREATE,StandardOpenOption.APPEND);
}
@Override
public void processMarkdownFiles(String content) throws IOException {
List<String> files = Files.list(Paths.get(INPUT_DIR))
.filter(f->f.toString().endsWith(".md"))
.map(Path->Path.toString())
.collect(Collectors.toList());
int count = 0;
for (String file : files) {
String result = FuzzyMatcher.fuzzyMarkdownProcess(file, ITEM_FILE);
if (!"no".equals(result)) {
Files.write(Paths.get(OUTPUT_DIR + "\\" + "change" + (count++) + ".md"), result.getBytes(StandardCharsets.UTF_8));
}
}
}
@Override
public List<String> getFileName(String filePath) {
List<String> fileNames = new ArrayList<>();
File folder = new File(filePath);
if (!folder.exists() || !folder.isDirectory()) {
return fileNames;
}
File[] files = folder.listFiles();
if (files != null) {
for (File file : files) {
if (file.isFile()) {
fileNames.add(file.getName());
}
}
}
return fileNames;
}
}

View File

@ -0,0 +1,102 @@
package com.bjtds.brichat.util;
import org.simmetrics.StringMetric;
import org.simmetrics.metrics.StringMetrics;
import java.nio.file.Files;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
public class FuzzyMatcher {
private static final StringMetric fuzzyMetric = StringMetrics.jaroWinkler();
private static final int DEFAULT_THRESHOLD = 84;
public static String fuzzyMarkdownProcess(String sourcePath, String targetPath, int threshold) throws IOException {
String source = String.join("\n", Files.readAllLines(Paths.get(sourcePath)));
String target = String.join("\n", Files.readAllLines(Paths.get(targetPath)));
List<String> sourceBlocks = MarkdownUtils.splitMarkdownBlocks(source);
List<String> targetBlocks = MarkdownUtils.splitMarkdownBlocks(target);
boolean hasReplacement = false; // 标记是否发生替换
for (int i = 0; i < targetBlocks.size(); i++) {
ReplacementResult result = fuzzyReplaceWithProcessing(sourceBlocks, targetBlocks.get(i), threshold);
if (result.replaced) {
hasReplacement = true;
sourceBlocks = result.updatedBlocks;
}
}
if (!hasReplacement) {
return "no"; // 没有替换返回 no
}
return String.join("\n\n", sourceBlocks);
}
public static String fuzzyMarkdownProcess(String sourcePath, String targetPath) throws IOException {
return fuzzyMarkdownProcess(sourcePath, targetPath, DEFAULT_THRESHOLD);
}
// 用类封装返回值包含更新后的块和是否替换的标记
private static class ReplacementResult {
List<String> updatedBlocks;
boolean replaced;
ReplacementResult(List<String> updatedBlocks, boolean replaced) {
this.updatedBlocks = updatedBlocks;
this.replaced = replaced;
}
}
private static ReplacementResult fuzzyReplaceWithProcessing(List<String> sourceBlocks, String targetBlock, int threshold) {
int bestIndex = -1;
int bestScore = -1;
for (int i = 0; i < sourceBlocks.size(); i++) {
String source = normalize(sourceBlocks.get(i));
String target = normalize(targetBlock);
float score = fuzzyMetric.compare(source, target);
int percentScore = Math.round(score * 100);
if (percentScore > bestScore) {
bestScore = percentScore;
bestIndex = i;
}
}
if (bestScore < threshold || bestIndex == -1) {
return new ReplacementResult(sourceBlocks, false);
}
String replacement;
if (MarkdownUtils.isMarkdownTable(targetBlock)) {
replacement = MarkdownUtils.markdownTableToHtml(targetBlock);
} else if (MarkdownUtils.isMarkdownHeading(targetBlock)) {
replacement = MarkdownUtils.addEqualsToHeading(targetBlock);
} else {
replacement = "==" + targetBlock.trim() + "==";
}
List<String> updated = new ArrayList<>(sourceBlocks);
updated.set(bestIndex, replacement);
return new ReplacementResult(updated, true);
}
private static String normalize(String text) {
return text
.replaceAll("\\|\\s*-+\\s*\\|", "")
.replaceAll("[\\r\\n]+", " ")
.replaceAll("\\s+", " ")
.replaceAll("[`*_#>\\[\\]<>]", "")
.replaceAll("<[^>]+>", "")
.trim()
.toLowerCase();
}
}

View File

@ -0,0 +1,95 @@
package com.bjtds.brichat.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class MarkdownUtils {
/**
* Markdown 文本按段落块分割连续空行视为分隔
*/
public static List<String> splitMarkdownBlocks(String markdown) {
return Arrays.stream(markdown.split("\\n{2,}"))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
}
/**
* 表格判断逻辑只要一行中包含|就视为表格块
*/
public static boolean isMarkdownTable(String block) {
return Arrays.stream(block.split("\n")).anyMatch(line -> line.contains("|"));
}
/**
* 判断是否为 Markdown 标题 '#' 开头
*/
public static boolean isMarkdownHeading(String block) {
return block.trim().startsWith("#");
}
/**
* Markdown 标题转换为等号形式
* 示例 "# 标题" -> "标题\n====="
*/
public static String addEqualsToHeading(String block) {
String line = block.trim().replaceAll("^#+", "").trim();
return line + "\n" + new String(new char[line.length()]).replace('\0', '=');
}
/**
* Markdown 表格转换为 HTML 表格简单处理
*/
public static String markdownTableToHtml(String block) {
List<String> lines = Arrays.stream(block.split("\n"))
.map(String::trim)
.filter(s -> !s.isEmpty())
.collect(Collectors.toList());
List<String[]> rows = new ArrayList<>();
for (String line : lines) {
String[] cells = Arrays.stream(line.split("\\|"))
.map(String::trim)
.filter(s -> !s.isEmpty())
.toArray(String[]::new);
if (cells.length > 0) {
rows.add(cells);
}
}
if (rows.isEmpty()) return "<table></table>";
StringBuilder html = new StringBuilder();
// 加入前置分割线
// 设置表格背景色为淡黄色
html.append("<table class=\"custom - table\"style=\"background-color:#ffffcc; border-collapse: collapse; width: 100%;\">\n");
// 表头
html.append(" <thead><tr>");
for (String header : rows.get(0)) {
html.append("<th style=\"border:1px solid #999;padding:4px;\">")
.append(header).append("</th>");
}
html.append("</tr></thead>\n");
// 表体
html.append(" <tbody>\n");
for (int i = 1; i < rows.size(); i++) {
html.append(" <tr>");
for (String cell : rows.get(i)) {
html.append("<td style=\"border:1px solid #999;padding:4px;\">")
.append(cell).append("</td>");
}
html.append("</tr>\n");
}
html.append(" </tbody>\n</table>");
return html.toString();
}
}