From eb6339c9a74563d8c1e6ebd76635a16f97eabd82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E9=B9=8F?= Date: Fri, 10 Apr 2026 19:28:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=BC=80=E6=94=BE?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=EF=BC=8C=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=E6=A8=A1=E6=9D=BF=E5=90=8D=E7=A7=B0=E5=92=8C=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E7=94=9F=E6=88=90=E6=96=87=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 HtmlUtils 工具类,支持 HTML 转纯文本 - 新增 selectTtCodeByCodeName 方法,支持按名称查询源码 - 新增 /office/copyTemplate/open/generate 开放接口 无需登录,参数:templateName(模板名称)、codeName(项目名称) 返回:填充占位符后的完整文案内容 Co-Authored-By: Claude Opus 4.6 --- .../com/ruoyi/common/utils/HtmlUtils.java | 60 +++++++++++++++ .../controller/TtCopyTemplateController.java | 76 +++++++++++++++++++ .../com/ruoyi/office/mapper/TtCodeMapper.java | 10 ++- .../ruoyi/office/service/ITtCodeService.java | 8 ++ .../service/impl/TtCodeServiceImpl.java | 17 +++++ .../resources/mapper/office/TtCodeMapper.xml | 5 ++ 6 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/utils/HtmlUtils.java diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/HtmlUtils.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HtmlUtils.java new file mode 100644 index 0000000..88fc1bd --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/HtmlUtils.java @@ -0,0 +1,60 @@ +package com.ruoyi.common.utils; + +import java.util.regex.Pattern; + +/** + * HTML工具类 + * + * @author ruoyi + */ +public class HtmlUtils { + + /** + * 将HTML内容转换为纯文本 + * 移除所有HTML标签,将 等转义字符转换为普通空格,合并多余空白符 + */ + public static String htmlToText(String html) { + if (StringUtils.isEmpty(html)) { + return ""; + } + String text = html; + // 移除script和style标签及其内容 + text = Pattern.compile("]*?>.*?", Pattern.DOTALL | Pattern.CASE_INSENSITIVE).matcher(text).replaceAll(""); + text = Pattern.compile("]*?>.*?", Pattern.DOTALL | Pattern.CASE_INSENSITIVE).matcher(text).replaceAll(""); + // 移除HTML注释 + text = Pattern.compile("", Pattern.DOTALL).matcher(text).replaceAll(""); + // 移除所有HTML标签 + text = Pattern.compile("<[^>]+>").matcher(text).replaceAll(""); + // 转义字符转换 + text = text.replaceAll(" ", " "); + text = text.replaceAll("&", "&"); + text = text.replaceAll("<", "<"); + text = text.replaceAll(">", ">"); + text = text.replaceAll(""", "\""); + text = text.replaceAll("'", "'"); + text = text.replaceAll("'", "'"); + text = text.replaceAll("—", "—"); + text = text.replaceAll("–", "–"); + text = text.replaceAll("…", "…"); + text = text.replaceAll("“", """); + text = text.replaceAll("”", """); + text = text.replaceAll("‘", "'"); + text = text.replaceAll("’", "'"); + text = text.replaceAll("\\r\\n", "\n"); + text = text.replaceAll("\\r", "\n"); + text = text.replaceAll("\\n\\n+", "\n\n"); + // 合并每行首尾空白并移除全空行 + String[] lines = text.split("\n"); + StringBuilder sb = new StringBuilder(); + for (String line : lines) { + String trimmed = line.trim(); + if (trimmed.length() > 0) { + if (sb.length() > 0) { + sb.append("\n"); + } + sb.append(trimmed); + } + } + return sb.toString(); + } +} \ No newline at end of file diff --git a/ruoyi-office/src/main/java/com/ruoyi/office/controller/TtCopyTemplateController.java b/ruoyi-office/src/main/java/com/ruoyi/office/controller/TtCopyTemplateController.java index 7b1001a..7bbab5e 100644 --- a/ruoyi-office/src/main/java/com/ruoyi/office/controller/TtCopyTemplateController.java +++ b/ruoyi-office/src/main/java/com/ruoyi/office/controller/TtCopyTemplateController.java @@ -11,12 +11,18 @@ import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import com.ruoyi.common.annotation.Anonymous; import com.ruoyi.common.annotation.Log; import com.ruoyi.common.core.controller.BaseController; import com.ruoyi.common.core.domain.AjaxResult; import com.ruoyi.common.enums.BusinessType; +import com.ruoyi.common.utils.HtmlUtils; +import com.ruoyi.common.utils.StringUtils; +import com.ruoyi.office.domain.TtCode; import com.ruoyi.office.domain.TtCopyTemplate; +import com.ruoyi.office.service.ITtCodeService; import com.ruoyi.office.service.ITtCopyTemplateService; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.common.core.page.TableDataInfo; @@ -34,6 +40,9 @@ public class TtCopyTemplateController extends BaseController { @Autowired private ITtCopyTemplateService ttCopyTemplateService; + @Autowired + private ITtCodeService ttCodeService; + /** * 查询文案模板列表 */ @@ -103,4 +112,71 @@ public class TtCopyTemplateController extends BaseController { public AjaxResult remove(@PathVariable Long[] templateIds) { return toAjax(ttCopyTemplateService.deleteTtCopyTemplateByTemplateIds(templateIds)); } + + /** + * 开放接口:通过文案模板名称和项目名称生成文案 + * 无需登录,浏览器可直接访问 + */ + @Anonymous + @GetMapping("/open/generate") + public AjaxResult generateByOpen(@RequestParam String templateName, @RequestParam String codeName) { + // 1. 查询模板 + TtCopyTemplate template = ttCopyTemplateService.selectEnabledTemplates().stream() + .filter(t -> templateName.equals(t.getTemplateName())) + .findFirst() + .orElse(null); + if (template == null) { + return error("未找到名称为【" + templateName + "】的启用模板"); + } + // 2. 查询源码 + TtCode code = ttCodeService.selectTtCodeByCodeName(codeName); + if (code == null) { + return error("未找到名称为【" + codeName + "】的源码项目"); + } + // 3. 提取变量 + String fullCodeName = code.getCodeName() != null ? code.getCodeName() : ""; + String projectCode = ""; + String projectName = fullCodeName; + if (fullCodeName.length() > 0) { + java.util.regex.Matcher m = java.util.regex.Pattern.compile("【(.+?)】").matcher(fullCodeName); + if (m.find()) { + projectCode = m.group(1); + } + projectName = fullCodeName.replaceAll("【.+?】", "").trim(); + if (projectName.startsWith("基于")) { + java.util.regex.Matcher nameMatcher = java.util.regex.Pattern.compile("实现的(.+?)$").matcher(projectName); + if (nameMatcher.find()) { + projectName = nameMatcher.group(1); + } + } + } + // 4. 生成纯文本描述 + String plainDesc = ""; + if (StringUtils.isNotEmpty(code.getCodeDesc())) { + String codeDesc = code.getCodeDesc().replaceAll("

", "

\n"); + plainDesc = HtmlUtils.htmlToText(codeDesc.trim()); + } + // 5. 生成截图列表 + String screenshots = ""; + int idx = 0; + if (code.getFileList() != null && !code.getFileList().isEmpty()) { + for (var item : code.getFileList()) { + screenshots += (++idx) + "." + item.getFileName() + "\n![](" + item.getFileUrl() + ")\n\n"; + } + } else { + screenshots = "请前往微信小程序:南音源码库。查看项目详情!\n\n"; + } + // 6. 替换占位符 + String content = template.getTemplateBody(); + content = content.replace("{codeName}", fullCodeName); + content = content.replace("{projectCode}", projectCode); + content = content.replace("{projectName}", projectName); + content = content.replace("{codeDesc}", plainDesc); + content = content.replace("{codeEnvironment}", code.getCodeEnvironment() != null ? code.getCodeEnvironment() : ""); + content = content.replace("{codeTechnology}", code.getCodeTechnology() != null ? code.getCodeTechnology() : ""); + content = content.replace("{diskLink}", code.getDiskLink() != null ? code.getDiskLink() : ""); + content = content.replace("{screenshots}", screenshots); + + return success(content); + } } \ No newline at end of file diff --git a/ruoyi-office/src/main/java/com/ruoyi/office/mapper/TtCodeMapper.java b/ruoyi-office/src/main/java/com/ruoyi/office/mapper/TtCodeMapper.java index 91f59e8..153affb 100644 --- a/ruoyi-office/src/main/java/com/ruoyi/office/mapper/TtCodeMapper.java +++ b/ruoyi-office/src/main/java/com/ruoyi/office/mapper/TtCodeMapper.java @@ -13,12 +13,20 @@ public interface TtCodeMapper { /** * 查询源码管理 - * + * * @param codeId 源码管理主键 * @return 源码管理 */ public TtCode selectTtCodeByCodeId(Long codeId); + /** + * 根据项目名称查询源码管理 + * + * @param codeName 项目名称 + * @return 源码管理 + */ + public TtCode selectTtCodeByCodeName(String codeName); + /** * 查询源码管理列表 * diff --git a/ruoyi-office/src/main/java/com/ruoyi/office/service/ITtCodeService.java b/ruoyi-office/src/main/java/com/ruoyi/office/service/ITtCodeService.java index 76116a0..134f643 100644 --- a/ruoyi-office/src/main/java/com/ruoyi/office/service/ITtCodeService.java +++ b/ruoyi-office/src/main/java/com/ruoyi/office/service/ITtCodeService.java @@ -19,6 +19,14 @@ public interface ITtCodeService { */ public TtCode selectTtCodeByCodeId(Long codeId); + /** + * 根据项目名称查询源码管理 + * + * @param codeName 项目名称 + * @return 源码管理 + */ + public TtCode selectTtCodeByCodeName(String codeName); + /** * 查询源码管理列表 * diff --git a/ruoyi-office/src/main/java/com/ruoyi/office/service/impl/TtCodeServiceImpl.java b/ruoyi-office/src/main/java/com/ruoyi/office/service/impl/TtCodeServiceImpl.java index dcc494a..e603e17 100644 --- a/ruoyi-office/src/main/java/com/ruoyi/office/service/impl/TtCodeServiceImpl.java +++ b/ruoyi-office/src/main/java/com/ruoyi/office/service/impl/TtCodeServiceImpl.java @@ -58,6 +58,23 @@ public class TtCodeServiceImpl implements ITtCodeService { return ttCode; } + /** + * 根据项目名称查询源码管理 + * + * @param codeName 项目名称 + * @return 源码管理 + */ + @Override + public TtCode selectTtCodeByCodeName(String codeName) { + TtCode ttCode = ttCodeMapper.selectTtCodeByCodeName(codeName); + if (ttCode != null) { + TtFile file = new TtFile(); + file.setCodeName(ttCode.getCodeName()); + ttCode.setFileList(fileMapper.selectTtFileList(file)); + } + return ttCode; + } + /** * 查询源码管理列表 * diff --git a/ruoyi-office/src/main/resources/mapper/office/TtCodeMapper.xml b/ruoyi-office/src/main/resources/mapper/office/TtCodeMapper.xml index cf33b0e..a47af60 100644 --- a/ruoyi-office/src/main/resources/mapper/office/TtCodeMapper.xml +++ b/ruoyi-office/src/main/resources/mapper/office/TtCodeMapper.xml @@ -43,6 +43,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" where code_id = #{codeId} + + insert into tt_code