feat: 优化代码生成及文案模板渲染
This commit is contained in:
@@ -103,26 +103,14 @@ public class TtCodeController extends BaseController
|
|||||||
return toAjax(ttCodeService.deleteTtCodeByCodeIds(codeIds));
|
return toAjax(ttCodeService.deleteTtCodeByCodeIds(codeIds));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 转文章
|
|
||||||
*/
|
|
||||||
@PreAuthorize("@ss.hasPermi('office:code:edit')")
|
|
||||||
@Log(title = "源码管理", businessType = BusinessType.UPDATE)
|
|
||||||
@GetMapping("/transToArticle/{codeId}")
|
|
||||||
public AjaxResult transToArticle(@PathVariable Long codeId)
|
|
||||||
{
|
|
||||||
String msg = ttCodeService.transToArticle(codeId);
|
|
||||||
return success(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转文章
|
* 转文章
|
||||||
*/
|
*/
|
||||||
@PreAuthorize("@ss.hasPermi('office:code:edit')")
|
@PreAuthorize("@ss.hasPermi('office:code:edit')")
|
||||||
@Log(title = "转文章(南音)", businessType = BusinessType.UPDATE)
|
@Log(title = "转文章(南音)", businessType = BusinessType.UPDATE)
|
||||||
@GetMapping("/transToArticle1/{codeId}")
|
@GetMapping("/transToArticle1/{codeId}")
|
||||||
public AjaxResult transToArticle1(@PathVariable Long codeId, @RequestParam String coverUrl) {
|
public AjaxResult transToArticle1(@PathVariable Long codeId, @RequestParam Long templateId) {
|
||||||
String msg = ttCodeService.transToArticle1(codeId, coverUrl);
|
String msg = ttCodeService.transToArticle1(codeId, templateId);
|
||||||
return success(msg);
|
return success(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,9 @@ import com.ruoyi.common.annotation.Log;
|
|||||||
import com.ruoyi.common.core.controller.BaseController;
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
import com.ruoyi.common.enums.BusinessType;
|
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.TtCode;
|
||||||
import com.ruoyi.office.domain.TtFile;
|
|
||||||
import com.ruoyi.office.domain.TtCopyTemplate;
|
import com.ruoyi.office.domain.TtCopyTemplate;
|
||||||
|
import com.ruoyi.office.service.CopyTemplateRenderer;
|
||||||
import com.ruoyi.office.service.ITtCodeService;
|
import com.ruoyi.office.service.ITtCodeService;
|
||||||
import com.ruoyi.office.service.ITtCopyTemplateService;
|
import com.ruoyi.office.service.ITtCopyTemplateService;
|
||||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||||
@@ -44,6 +42,9 @@ public class TtCopyTemplateController extends BaseController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private ITtCodeService ttCodeService;
|
private ITtCodeService ttCodeService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private CopyTemplateRenderer copyTemplateRenderer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询文案模板列表
|
* 查询文案模板列表
|
||||||
*/
|
*/
|
||||||
@@ -134,53 +135,6 @@ public class TtCopyTemplateController extends BaseController {
|
|||||||
if (code == null) {
|
if (code == null) {
|
||||||
return error("未找到名称为【" + codeName + "】的源码项目");
|
return error("未找到名称为【" + codeName + "】的源码项目");
|
||||||
}
|
}
|
||||||
// 3. 提取变量
|
return success(copyTemplateRenderer.render(template, code));
|
||||||
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("</p>", "</p>\n");
|
|
||||||
plainDesc = HtmlUtils.htmlToText(codeDesc.trim());
|
|
||||||
}
|
|
||||||
// 5. 生成截图列表
|
|
||||||
String screenshots = "";
|
|
||||||
int idx = 0;
|
|
||||||
if (code.getFileList() != null && !code.getFileList().isEmpty()) {
|
|
||||||
for (TtFile item : code.getFileList()) {
|
|
||||||
screenshots += (++idx) + "." + item.getFileName() + "\n + ")\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("{frontendTechnology}", code.getFrontendTechnology() != null ? code.getFrontendTechnology() : "");
|
|
||||||
content = content.replace("{backendTechnology}", code.getBackendTechnology() != null ? code.getBackendTechnology() : "");
|
|
||||||
content = content.replace("{databaseTechnology}", code.getDatabaseTechnology() != null ? code.getDatabaseTechnology() : "");
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,4 +67,6 @@ public interface TtProjectInfoMapper
|
|||||||
int updateProjectQuarkUrl(@Param("id") Integer id, @Param("projectUrl") String projectUrl);
|
int updateProjectQuarkUrl(@Param("id") Integer id, @Param("projectUrl") String projectUrl);
|
||||||
|
|
||||||
int updateProjectBaiduUrl(@Param("id") Integer id, @Param("projectBaiduUrl") String projectBaiduUrl);
|
int updateProjectBaiduUrl(@Param("id") Integer id, @Param("projectBaiduUrl") String projectBaiduUrl);
|
||||||
|
|
||||||
|
int updateProjectNumIfBlank(@Param("id") Integer id, @Param("projectNum") String projectNum);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
package com.ruoyi.office.service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
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.domain.TtFile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文案模板占位符渲染器。
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class CopyTemplateRenderer
|
||||||
|
{
|
||||||
|
private static final Pattern PROJECT_CODE_PATTERN = Pattern.compile("【(.+?)】");
|
||||||
|
private static final Pattern PROJECT_NAME_PATTERN = Pattern.compile("实现的(.+?)$");
|
||||||
|
|
||||||
|
public String render(TtCopyTemplate template, TtCode code)
|
||||||
|
{
|
||||||
|
String codeName = value(code.getCodeName());
|
||||||
|
String projectCode = extractProjectCode(codeName);
|
||||||
|
String projectName = extractProjectName(codeName);
|
||||||
|
String content = value(template.getTemplateBody());
|
||||||
|
|
||||||
|
content = content.replace("{codeName}", codeName);
|
||||||
|
content = content.replace("{projectCode}", projectCode);
|
||||||
|
content = content.replace("{projectName}", projectName);
|
||||||
|
content = content.replace("{codeDesc}", toPlainDescription(code.getCodeDesc()));
|
||||||
|
content = content.replace("{codeEnvironment}", value(code.getCodeEnvironment()));
|
||||||
|
content = content.replace("{frontendTechnology}", value(code.getFrontendTechnology()));
|
||||||
|
content = content.replace("{backendTechnology}", value(code.getBackendTechnology()));
|
||||||
|
content = content.replace("{databaseTechnology}", value(code.getDatabaseTechnology()));
|
||||||
|
content = content.replace("{codeTechnology}", value(code.getCodeTechnology()));
|
||||||
|
content = content.replace("{diskLink}", value(code.getDiskLink()));
|
||||||
|
content = content.replace("{screenshots}", buildScreenshots(code.getFileList()));
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractProjectCode(String codeName)
|
||||||
|
{
|
||||||
|
Matcher matcher = PROJECT_CODE_PATTERN.matcher(codeName);
|
||||||
|
return matcher.find() ? matcher.group(1) : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String extractProjectName(String codeName)
|
||||||
|
{
|
||||||
|
String projectName = PROJECT_CODE_PATTERN.matcher(codeName).replaceFirst("").trim();
|
||||||
|
if (projectName.startsWith("基于"))
|
||||||
|
{
|
||||||
|
Matcher matcher = PROJECT_NAME_PATTERN.matcher(projectName);
|
||||||
|
if (matcher.find())
|
||||||
|
{
|
||||||
|
return matcher.group(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return projectName;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toPlainDescription(String codeDesc)
|
||||||
|
{
|
||||||
|
if (StringUtils.isEmpty(codeDesc))
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return HtmlUtils.htmlToText(codeDesc.replaceAll("(?i)</p>", "</p>\n").trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildScreenshots(List<TtFile> fileList)
|
||||||
|
{
|
||||||
|
if (fileList == null || fileList.isEmpty())
|
||||||
|
{
|
||||||
|
return "请前往微信小程序:南音源码库。查看项目详情!\n\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder screenshots = new StringBuilder();
|
||||||
|
int index = 0;
|
||||||
|
for (TtFile file : fileList)
|
||||||
|
{
|
||||||
|
screenshots.append(++index)
|
||||||
|
.append(".")
|
||||||
|
.append(value(file.getFileName()))
|
||||||
|
.append("\n
|
||||||
|
.append(value(file.getFileUrl()))
|
||||||
|
.append(")\n\n");
|
||||||
|
}
|
||||||
|
return screenshots.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String value(String value)
|
||||||
|
{
|
||||||
|
return value == null ? "" : value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -67,16 +67,8 @@ public interface ITtCodeService {
|
|||||||
*/
|
*/
|
||||||
public int deleteTtCodeByCodeId(Long codeId);
|
public int deleteTtCodeByCodeId(Long codeId);
|
||||||
|
|
||||||
/**
|
|
||||||
* 转文章
|
|
||||||
*
|
|
||||||
* @param codeId 源码管理主键
|
|
||||||
* @return 结果
|
|
||||||
*/
|
|
||||||
public String transToArticle(Long codeId);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转文章(南音)
|
* 转文章(南音)
|
||||||
*/
|
*/
|
||||||
public String transToArticle1(Long codeId, String coverUrl);
|
public String transToArticle1(Long codeId, Long templateId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,18 @@ import com.ruoyi.app.domain.AppResource;
|
|||||||
import com.ruoyi.app.domain.AppResourceList;
|
import com.ruoyi.app.domain.AppResourceList;
|
||||||
import com.ruoyi.app.mapper.AppBlogArticleMapper;
|
import com.ruoyi.app.mapper.AppBlogArticleMapper;
|
||||||
import com.ruoyi.app.mapper.AppResourceMapper;
|
import com.ruoyi.app.mapper.AppResourceMapper;
|
||||||
|
import com.ruoyi.common.exception.ServiceException;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import com.ruoyi.office.domain.TtArticles;
|
import com.ruoyi.office.domain.TtCopyTemplate;
|
||||||
import com.ruoyi.office.domain.TtFile;
|
import com.ruoyi.office.domain.TtFile;
|
||||||
import com.ruoyi.office.domain.TtProjectInfo;
|
import com.ruoyi.office.domain.TtProjectInfo;
|
||||||
import com.ruoyi.office.mapper.TtArticlesMapper;
|
import com.ruoyi.office.mapper.TtCopyTemplateMapper;
|
||||||
import com.ruoyi.office.mapper.TtFileMapper;
|
import com.ruoyi.office.mapper.TtFileMapper;
|
||||||
import com.ruoyi.office.mapper.TtProjectInfoMapper;
|
import com.ruoyi.office.mapper.TtProjectInfoMapper;
|
||||||
|
import com.ruoyi.office.service.CopyTemplateRenderer;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import com.ruoyi.office.mapper.TtCodeMapper;
|
import com.ruoyi.office.mapper.TtCodeMapper;
|
||||||
import com.ruoyi.office.domain.TtCode;
|
import com.ruoyi.office.domain.TtCode;
|
||||||
import com.ruoyi.office.service.ITtCodeService;
|
import com.ruoyi.office.service.ITtCodeService;
|
||||||
@@ -33,8 +36,6 @@ public class TtCodeServiceImpl implements ITtCodeService {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private TtCodeMapper ttCodeMapper;
|
private TtCodeMapper ttCodeMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private TtArticlesMapper ttArticlesMapper;
|
|
||||||
@Autowired
|
|
||||||
private TtFileMapper fileMapper;
|
private TtFileMapper fileMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private AppBlogArticleMapper appBlogArticleMapper;
|
private AppBlogArticleMapper appBlogArticleMapper;
|
||||||
@@ -42,6 +43,10 @@ public class TtCodeServiceImpl implements ITtCodeService {
|
|||||||
private AppResourceMapper appResourceMapper;
|
private AppResourceMapper appResourceMapper;
|
||||||
@Autowired
|
@Autowired
|
||||||
private TtProjectInfoMapper ttProjectInfoMapper;
|
private TtProjectInfoMapper ttProjectInfoMapper;
|
||||||
|
@Autowired
|
||||||
|
private TtCopyTemplateMapper ttCopyTemplateMapper;
|
||||||
|
@Autowired
|
||||||
|
private CopyTemplateRenderer copyTemplateRenderer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询源码管理
|
* 查询源码管理
|
||||||
@@ -131,105 +136,154 @@ public class TtCodeServiceImpl implements ITtCodeService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String transToArticle(Long codeId) {
|
@Transactional(rollbackFor = Exception.class)
|
||||||
TtCode ttCode = ttCodeMapper.selectTtCodeByCodeId(codeId);
|
public String transToArticle1(Long codeId, Long templateId) {
|
||||||
TtArticles ttArticles = new TtArticles();
|
TtCopyTemplate template = ttCopyTemplateMapper.selectTtCopyTemplateByTemplateId(templateId);
|
||||||
ttArticles.setTitle(ttCode.getCodeName());
|
if (template == null || !"0".equals(template.getStatus())) {
|
||||||
List<TtArticles> list = ttArticlesMapper.selectTtArticlesList(ttArticles);
|
throw new ServiceException("所选文案模板不存在或已停用");
|
||||||
if (!list.isEmpty()) {
|
|
||||||
return "不能重复转文章!";
|
|
||||||
}
|
}
|
||||||
ttArticles.setAuthor("Feast");
|
|
||||||
ttArticles.setCategory(ttCode.getPaymentType());
|
|
||||||
if (ttCode.getPaymentType().equals("2")) {
|
|
||||||
ttArticles.setAttachmentUrl(ttCode.getDiskLink());
|
|
||||||
}
|
|
||||||
StringBuffer content = new StringBuffer();
|
|
||||||
content.append("<p><strong>### 项目描述</strong></p>");
|
|
||||||
content.append(ttCode.getCodeDesc());
|
|
||||||
content.append("<p><strong>### 运行环境</strong></p>");
|
|
||||||
content.append(ttCode.getCodeEnvironment());
|
|
||||||
content.append("<p><strong>### 其他技术</strong></p>");
|
|
||||||
content.append(ttCode.getCodeTechnology());
|
|
||||||
ttArticles.setContent(content.toString());
|
|
||||||
ttArticlesMapper.insertTtArticles(ttArticles);
|
|
||||||
return "源码转文章成功!";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String transToArticle1(Long codeId, String coverUrl) {
|
|
||||||
TtCode ttCode = ttCodeMapper.selectTtCodeByCodeId(codeId);
|
TtCode ttCode = ttCodeMapper.selectTtCodeByCodeId(codeId);
|
||||||
|
if (ttCode == null) {
|
||||||
|
throw new ServiceException("源码项目不存在");
|
||||||
|
}
|
||||||
TtProjectInfo ttProjectInfo = ttProjectInfoMapper.selectTtProjectInfoByName(ttCode.getCodeName());
|
TtProjectInfo ttProjectInfo = ttProjectInfoMapper.selectTtProjectInfoByName(ttCode.getCodeName());
|
||||||
|
if (ttProjectInfo == null) {
|
||||||
|
throw new ServiceException("未找到对应的项目清单信息");
|
||||||
|
}
|
||||||
ttCode.setDiskLink(ttProjectInfo.getProjectBaiduUrl());
|
ttCode.setDiskLink(ttProjectInfo.getProjectBaiduUrl());
|
||||||
ttCode.setPublishFlag("Y");
|
|
||||||
ttCodeMapper.updateTtCode(ttCode);
|
|
||||||
StringBuffer content = new StringBuffer();
|
|
||||||
content.append("<p><strong>### 项目描述</strong></p>");
|
|
||||||
content.append(ttCode.getCodeDesc());
|
|
||||||
content.append("<p><strong>### 运行环境</strong></p>");
|
|
||||||
content.append(ttCode.getCodeEnvironment());
|
|
||||||
content.append("<p><strong>### 其他技术</strong></p>");
|
|
||||||
content.append(ttCode.getCodeTechnology());
|
|
||||||
content.append("<p><strong>### 演示视频</strong></p>");
|
|
||||||
content.append("请移步首页-<strong>视频资源</strong>,搜索<strong>项目编号</strong>查看");
|
|
||||||
TtFile file = new TtFile();
|
TtFile file = new TtFile();
|
||||||
file.setCodeName(ttCode.getCodeName());
|
file.setCodeName(ttCode.getCodeName());
|
||||||
List<TtFile> fileList = fileMapper.selectTtFileList(file);
|
List<TtFile> fileList = fileMapper.selectTtFileList(file);
|
||||||
AppBlogArticle article = new AppBlogArticle();
|
ttCode.setFileList(fileList);
|
||||||
article.setTitle(ttCode.getCodeName());
|
String content = copyTemplateRenderer.render(template, ttCode);
|
||||||
List<AppBlogArticle> list = appBlogArticleMapper.selectAppBlogArticleList(article);
|
String firstImageUrl = getFirstImageUrl(fileList);
|
||||||
if (!list.isEmpty()) {
|
|
||||||
|
AppBlogArticle existingArticle = appBlogArticleMapper.selectAppBlogArticleByTitle(ttCode.getCodeName());
|
||||||
|
if (existingArticle != null) {
|
||||||
|
if (repairIncompleteConversion(existingArticle, ttProjectInfo, content, firstImageUrl)) {
|
||||||
|
markCodePublished(ttCode);
|
||||||
|
return "源码转文章成功,已补全资源链接!";
|
||||||
|
}
|
||||||
return "不能重复转文章!";
|
return "不能重复转文章!";
|
||||||
}
|
}
|
||||||
|
|
||||||
//新增资源
|
//新增资源
|
||||||
AppResource resource = new AppResource();
|
AppResource resource = new AppResource();
|
||||||
resource.setExplain(content.toString());
|
resource.setExplain(content);
|
||||||
resource.setResourceTitle(ttCode.getCodeName());
|
resource.setResourceTitle(ttCode.getCodeName());
|
||||||
if (!fileList.isEmpty()) {
|
if (!StringUtils.isEmpty(firstImageUrl)) {
|
||||||
resource.setShowImg(fileList.get(0).getFileUrl());
|
resource.setShowImg(firstImageUrl);
|
||||||
}
|
}
|
||||||
resource.setResourceType(5L);
|
resource.setResourceType(5L);
|
||||||
resource.setIsShow(0L);
|
resource.setIsShow(0L);
|
||||||
resource.setIsAd(2L);
|
resource.setIsAd(2L);
|
||||||
resource.setAdNumber(100L);
|
resource.setAdNumber(100L);
|
||||||
|
resource.setPriceFen(0);
|
||||||
resource.setCreateTime(new Date());
|
resource.setCreateTime(new Date());
|
||||||
resource.setShowImg(coverUrl);
|
|
||||||
appResourceMapper.insertAppResource(resource);
|
appResourceMapper.insertAppResource(resource);
|
||||||
//新增文章
|
//新增文章
|
||||||
|
AppBlogArticle article = new AppBlogArticle();
|
||||||
|
article.setTitle(ttCode.getCodeName());
|
||||||
article.setArticleType(4L);
|
article.setArticleType(4L);
|
||||||
article.setIsRecommendation(1L);
|
article.setIsRecommendation(1L);
|
||||||
article.setIsShow(1L);
|
article.setIsShow(1L);
|
||||||
article.setIsAd(1L);
|
article.setIsAd(1L);
|
||||||
if (!fileList.isEmpty()) {
|
if (!StringUtils.isEmpty(firstImageUrl)) {
|
||||||
article.setShowImg(fileList.get(0).getFileUrl());
|
article.setShowImg(firstImageUrl);
|
||||||
}
|
}
|
||||||
article.setContentInfo(content.toString());
|
article.setContentInfo(content);
|
||||||
article.setAppResourceId(resource.getId());
|
article.setAppResourceId(resource.getId());
|
||||||
article.setCreateTime(new Date());
|
article.setCreateTime(new Date());
|
||||||
article.setShowImg(coverUrl);
|
|
||||||
appBlogArticleMapper.insertAppBlogArticle(article);
|
appBlogArticleMapper.insertAppBlogArticle(article);
|
||||||
//新增资源网盘链接
|
//新增资源网盘链接
|
||||||
List<AppResourceList> resourceList = new ArrayList<AppResourceList>();
|
List<AppResourceList> resourceList = buildResourceList(ttProjectInfo, resource.getId());
|
||||||
//百度网盘
|
|
||||||
if(!StringUtils.isEmpty(ttProjectInfo.getProjectBaiduUrl())){
|
|
||||||
AppResourceList appResourceList = new AppResourceList();
|
|
||||||
appResourceList.setAppResourceId(resource.getId());
|
|
||||||
appResourceList.setListName("百度网盘");
|
|
||||||
appResourceList.setListUrl(ttProjectInfo.getProjectBaiduUrl());
|
|
||||||
resourceList.add(appResourceList);
|
|
||||||
}
|
|
||||||
//夸克网盘
|
|
||||||
if(!StringUtils.isEmpty(ttProjectInfo.getProjectUrl())){
|
|
||||||
AppResourceList appResourceList1 = new AppResourceList();
|
|
||||||
appResourceList1.setAppResourceId(resource.getId());
|
|
||||||
appResourceList1.setListName("夸克网盘");
|
|
||||||
appResourceList1.setListUrl(ttProjectInfo.getProjectUrl());
|
|
||||||
resourceList.add(appResourceList1);
|
|
||||||
}
|
|
||||||
if (resourceList.size() > 0)
|
if (resourceList.size() > 0)
|
||||||
{
|
{
|
||||||
appResourceMapper.batchAppResourceList(resourceList);
|
appResourceMapper.batchAppResourceList(resourceList);
|
||||||
}
|
}
|
||||||
|
markCodePublished(ttCode);
|
||||||
return "源码转文章成功!";
|
return "源码转文章成功!";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修复旧转换流程异常后已经生成文章、但尚未写入下载链接的半成品数据。
|
||||||
|
*/
|
||||||
|
private boolean repairIncompleteConversion(AppBlogArticle article, TtProjectInfo projectInfo,
|
||||||
|
String content, String firstImageUrl)
|
||||||
|
{
|
||||||
|
if (article.getAppResourceId() == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
AppResource resource = appResourceMapper.selectAppResourceById(article.getAppResourceId());
|
||||||
|
if (resource == null || (resource.getAppResourceListList() != null
|
||||||
|
&& !resource.getAppResourceListList().isEmpty()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AppResourceList> resourceList = buildResourceList(projectInfo, resource.getId());
|
||||||
|
if (resourceList.isEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.setExplain(content);
|
||||||
|
if (!StringUtils.isEmpty(firstImageUrl))
|
||||||
|
{
|
||||||
|
resource.setShowImg(firstImageUrl);
|
||||||
|
}
|
||||||
|
resource.setPriceFen(0);
|
||||||
|
appResourceMapper.updateAppResource(resource);
|
||||||
|
|
||||||
|
article.setContentInfo(content);
|
||||||
|
if (!StringUtils.isEmpty(firstImageUrl))
|
||||||
|
{
|
||||||
|
article.setShowImg(firstImageUrl);
|
||||||
|
}
|
||||||
|
appBlogArticleMapper.updateAppBlogArticle(article);
|
||||||
|
appResourceMapper.batchAppResourceList(resourceList);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getFirstImageUrl(List<TtFile> fileList)
|
||||||
|
{
|
||||||
|
if (fileList == null || fileList.isEmpty())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return fileList.get(0).getFileUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AppResourceList> buildResourceList(TtProjectInfo projectInfo, Long resourceId)
|
||||||
|
{
|
||||||
|
List<AppResourceList> resourceList = new ArrayList<AppResourceList>();
|
||||||
|
addResourceList(resourceList, resourceId, "百度网盘", projectInfo.getProjectBaiduUrl());
|
||||||
|
addResourceList(resourceList, resourceId, "夸克网盘", projectInfo.getProjectUrl());
|
||||||
|
return resourceList;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addResourceList(List<AppResourceList> resourceList, Long resourceId,
|
||||||
|
String name, String url)
|
||||||
|
{
|
||||||
|
if (StringUtils.isEmpty(url))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AppResourceList item = new AppResourceList();
|
||||||
|
item.setAppResourceId(resourceId);
|
||||||
|
item.setListName(name);
|
||||||
|
item.setListUrl(url);
|
||||||
|
item.setPassword("");
|
||||||
|
item.setPriceFen(0);
|
||||||
|
item.setStatus(1);
|
||||||
|
item.setSortOrder(resourceList.size());
|
||||||
|
resourceList.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void markCodePublished(TtCode ttCode)
|
||||||
|
{
|
||||||
|
ttCode.setPublishFlag("Y");
|
||||||
|
ttCodeMapper.updateTtCode(ttCode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import com.ruoyi.office.service.ITtProjectInfoService;
|
|||||||
public class TtProjectInfoServiceImpl implements ITtProjectInfoService
|
public class TtProjectInfoServiceImpl implements ITtProjectInfoService
|
||||||
{
|
{
|
||||||
private static final Pattern PROJECT_NUM_PATTERN =
|
private static final Pattern PROJECT_NUM_PATTERN =
|
||||||
Pattern.compile("^[【\\[]\\s*(S\\d+)\\s*[】\\]]", Pattern.CASE_INSENSITIVE);
|
Pattern.compile("【\\s*([^】]+?)\\s*】");
|
||||||
|
|
||||||
private static final Pattern QUARK_URL_PATTERN =
|
private static final Pattern QUARK_URL_PATTERN =
|
||||||
Pattern.compile("https://pan\\.quark\\.cn/s/[A-Za-z0-9_-]+(?:\\?[^\\s\"'<>,。;]*)?");
|
Pattern.compile("https://pan\\.quark\\.cn/s/[A-Za-z0-9_-]+(?:\\?[^\\s\"'<>,。;]*)?");
|
||||||
@@ -203,6 +203,7 @@ public class TtProjectInfoServiceImpl implements ITtProjectInfoService
|
|||||||
}
|
}
|
||||||
|
|
||||||
TtProjectInfo project = matches.get(0);
|
TtProjectInfo project = matches.get(0);
|
||||||
|
backfillProjectNumIfBlank(project, row.projectName);
|
||||||
String oldLink = NetDiskConstants.DISK_QUARK.equals(normalizedDiskType)
|
String oldLink = NetDiskConstants.DISK_QUARK.equals(normalizedDiskType)
|
||||||
? project.getProjectUrl() : project.getProjectBaiduUrl();
|
? project.getProjectUrl() : project.getProjectBaiduUrl();
|
||||||
if (StringUtils.equals(StringUtils.trimToEmpty(oldLink), row.linkUrl))
|
if (StringUtils.equals(StringUtils.trimToEmpty(oldLink), row.linkUrl))
|
||||||
@@ -390,6 +391,20 @@ public class TtProjectInfoServiceImpl implements ITtProjectInfoService
|
|||||||
return project;
|
return project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void backfillProjectNumIfBlank(TtProjectInfo project, String projectName)
|
||||||
|
{
|
||||||
|
if (StringUtils.isNotBlank(project.getProjectNum()))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String projectNum = extractProjectNum(projectName);
|
||||||
|
if (StringUtils.isNotBlank(projectNum)
|
||||||
|
&& ttProjectInfoMapper.updateProjectNumIfBlank(project.getId(), projectNum) == 1)
|
||||||
|
{
|
||||||
|
project.setProjectNum(projectNum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String extractProjectNum(String projectName)
|
private String extractProjectNum(String projectName)
|
||||||
{
|
{
|
||||||
Matcher matcher = PROJECT_NUM_PATTERN.matcher(projectName);
|
Matcher matcher = PROJECT_NUM_PATTERN.matcher(projectName);
|
||||||
|
|||||||
@@ -125,6 +125,13 @@
|
|||||||
where id = #{id}
|
where id = #{id}
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<update id="updateProjectNumIfBlank">
|
||||||
|
update tt_project_info
|
||||||
|
set project_num = #{projectNum}
|
||||||
|
where id = #{id}
|
||||||
|
and (project_num is null or trim(project_num) = '')
|
||||||
|
</update>
|
||||||
|
|
||||||
<delete id="deleteTtProjectInfoById" parameterType="Integer">
|
<delete id="deleteTtProjectInfoById" parameterType="Integer">
|
||||||
delete from tt_project_info where id = #{id}
|
delete from tt_project_info where id = #{id}
|
||||||
</delete>
|
</delete>
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
package com.ruoyi.office.service;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.ruoyi.office.domain.TtCode;
|
||||||
|
import com.ruoyi.office.domain.TtCopyTemplate;
|
||||||
|
import com.ruoyi.office.domain.TtFile;
|
||||||
|
|
||||||
|
public class CopyTemplateRendererTest
|
||||||
|
{
|
||||||
|
private final CopyTemplateRenderer renderer = new CopyTemplateRenderer();
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void rendersCodeFieldsAndScreenshots()
|
||||||
|
{
|
||||||
|
TtCopyTemplate template = new TtCopyTemplate();
|
||||||
|
template.setTemplateBody("{codeName}|{projectCode}|{projectName}|{codeDesc}|"
|
||||||
|
+ "{codeEnvironment}|{frontendTechnology}|{backendTechnology}|"
|
||||||
|
+ "{databaseTechnology}|{codeTechnology}|{diskLink}|{screenshots}");
|
||||||
|
|
||||||
|
TtCode code = new TtCode();
|
||||||
|
code.setCodeName("【S008】基于SpringBoot实现的爱心众筹系统");
|
||||||
|
code.setCodeDesc("<p>第一段</p><p>第二段</p>");
|
||||||
|
code.setCodeEnvironment("JDK 17");
|
||||||
|
code.setFrontendTechnology("Vue3");
|
||||||
|
code.setBackendTechnology("SpringBoot");
|
||||||
|
code.setDatabaseTechnology("MySQL");
|
||||||
|
code.setCodeTechnology("ECharts");
|
||||||
|
code.setDiskLink("https://example.com/resource");
|
||||||
|
|
||||||
|
TtFile screenshot = new TtFile();
|
||||||
|
screenshot.setFileName("首页");
|
||||||
|
screenshot.setFileUrl("https://example.com/home.png");
|
||||||
|
code.setFileList(Arrays.asList(screenshot));
|
||||||
|
|
||||||
|
String content = renderer.render(template, code);
|
||||||
|
|
||||||
|
Assert.assertEquals("【S008】基于SpringBoot实现的爱心众筹系统|S008|爱心众筹系统|"
|
||||||
|
+ "第一段\n第二段|JDK 17|Vue3|SpringBoot|MySQL|ECharts|"
|
||||||
|
+ "https://example.com/resource|1.首页\n\n\n", content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void usesFallbackWhenThereAreNoScreenshots()
|
||||||
|
{
|
||||||
|
TtCopyTemplate template = new TtCopyTemplate();
|
||||||
|
template.setTemplateBody("{screenshots}");
|
||||||
|
|
||||||
|
String content = renderer.render(template, new TtCode());
|
||||||
|
|
||||||
|
Assert.assertEquals("请前往微信小程序:南音源码库。查看项目详情!\n\n", content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
package com.ruoyi.office.service.impl;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.ruoyi.app.domain.AppBlogArticle;
|
||||||
|
import com.ruoyi.app.domain.AppResource;
|
||||||
|
import com.ruoyi.app.domain.AppResourceList;
|
||||||
|
import com.ruoyi.app.mapper.AppBlogArticleMapper;
|
||||||
|
import com.ruoyi.app.mapper.AppResourceMapper;
|
||||||
|
import com.ruoyi.office.domain.TtCode;
|
||||||
|
import com.ruoyi.office.domain.TtCopyTemplate;
|
||||||
|
import com.ruoyi.office.domain.TtFile;
|
||||||
|
import com.ruoyi.office.domain.TtProjectInfo;
|
||||||
|
import com.ruoyi.office.mapper.TtCodeMapper;
|
||||||
|
import com.ruoyi.office.mapper.TtCopyTemplateMapper;
|
||||||
|
import com.ruoyi.office.mapper.TtFileMapper;
|
||||||
|
import com.ruoyi.office.mapper.TtProjectInfoMapper;
|
||||||
|
import com.ruoyi.office.service.CopyTemplateRenderer;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.never;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
|
public class TtCodeServiceImplTest
|
||||||
|
{
|
||||||
|
@InjectMocks
|
||||||
|
private TtCodeServiceImpl service;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private TtCodeMapper codeMapper;
|
||||||
|
@Mock
|
||||||
|
private TtFileMapper fileMapper;
|
||||||
|
@Mock
|
||||||
|
private AppBlogArticleMapper articleMapper;
|
||||||
|
@Mock
|
||||||
|
private AppResourceMapper resourceMapper;
|
||||||
|
@Mock
|
||||||
|
private TtProjectInfoMapper projectInfoMapper;
|
||||||
|
@Mock
|
||||||
|
private TtCopyTemplateMapper copyTemplateMapper;
|
||||||
|
@Mock
|
||||||
|
private CopyTemplateRenderer templateRenderer;
|
||||||
|
|
||||||
|
private TtCode code;
|
||||||
|
private TtProjectInfo projectInfo;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
TtCopyTemplate template = new TtCopyTemplate();
|
||||||
|
template.setTemplateId(1L);
|
||||||
|
template.setStatus("0");
|
||||||
|
|
||||||
|
code = new TtCode();
|
||||||
|
code.setCodeId(9L);
|
||||||
|
code.setCodeName("【S009】自习室预约选座系统");
|
||||||
|
|
||||||
|
projectInfo = new TtProjectInfo();
|
||||||
|
projectInfo.setProjectBaiduUrl("https://pan.baidu.com/s/example");
|
||||||
|
projectInfo.setProjectUrl("https://pan.quark.cn/s/example");
|
||||||
|
|
||||||
|
TtFile firstImage = new TtFile();
|
||||||
|
firstImage.setFileName("第一张");
|
||||||
|
firstImage.setFileUrl("https://img.example.com/first.png");
|
||||||
|
TtFile secondImage = new TtFile();
|
||||||
|
secondImage.setFileName("第二张");
|
||||||
|
secondImage.setFileUrl("https://img.example.com/second.png");
|
||||||
|
|
||||||
|
when(copyTemplateMapper.selectTtCopyTemplateByTemplateId(1L)).thenReturn(template);
|
||||||
|
when(codeMapper.selectTtCodeByCodeId(9L)).thenReturn(code);
|
||||||
|
when(projectInfoMapper.selectTtProjectInfoByName(code.getCodeName())).thenReturn(projectInfo);
|
||||||
|
when(fileMapper.selectTtFileList(any())).thenReturn(Arrays.asList(firstImage, secondImage));
|
||||||
|
when(templateRenderer.render(template, code)).thenReturn("模板正文");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void fillsRequiredResourceListFields()
|
||||||
|
{
|
||||||
|
when(articleMapper.selectAppBlogArticleByTitle(code.getCodeName())).thenReturn(null);
|
||||||
|
when(resourceMapper.insertAppResource(any())).thenAnswer(invocation -> {
|
||||||
|
AppResource resource = invocation.getArgument(0);
|
||||||
|
resource.setId(100L);
|
||||||
|
return 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
service.transToArticle1(9L, 1L);
|
||||||
|
|
||||||
|
ArgumentCaptor<AppResource> resourceCaptor = ArgumentCaptor.forClass(AppResource.class);
|
||||||
|
verify(resourceMapper).insertAppResource(resourceCaptor.capture());
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), resourceCaptor.getValue().getPriceFen());
|
||||||
|
Assert.assertEquals("https://img.example.com/first.png", resourceCaptor.getValue().getShowImg());
|
||||||
|
|
||||||
|
ArgumentCaptor<AppBlogArticle> articleCaptor = ArgumentCaptor.forClass(AppBlogArticle.class);
|
||||||
|
verify(articleMapper).insertAppBlogArticle(articleCaptor.capture());
|
||||||
|
Assert.assertEquals("https://img.example.com/first.png", articleCaptor.getValue().getShowImg());
|
||||||
|
|
||||||
|
ArgumentCaptor<List> listCaptor = ArgumentCaptor.forClass(List.class);
|
||||||
|
verify(resourceMapper).batchAppResourceList(listCaptor.capture());
|
||||||
|
List<AppResourceList> items = listCaptor.getValue();
|
||||||
|
Assert.assertEquals(2, items.size());
|
||||||
|
assertResourceListDefaults(items.get(0), 0);
|
||||||
|
assertResourceListDefaults(items.get(1), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void repairsArticleLeftByPreviousFailedConversion()
|
||||||
|
{
|
||||||
|
AppBlogArticle existingArticle = new AppBlogArticle();
|
||||||
|
existingArticle.setId(200L);
|
||||||
|
existingArticle.setTitle(code.getCodeName());
|
||||||
|
existingArticle.setAppResourceId(100L);
|
||||||
|
|
||||||
|
AppResource existingResource = new AppResource();
|
||||||
|
existingResource.setId(100L);
|
||||||
|
existingResource.setAppResourceListList(new ArrayList<AppResourceList>());
|
||||||
|
|
||||||
|
when(articleMapper.selectAppBlogArticleByTitle(code.getCodeName())).thenReturn(existingArticle);
|
||||||
|
when(resourceMapper.selectAppResourceById(100L)).thenReturn(existingResource);
|
||||||
|
|
||||||
|
String message = service.transToArticle1(9L, 1L);
|
||||||
|
|
||||||
|
Assert.assertEquals("源码转文章成功,已补全资源链接!", message);
|
||||||
|
Assert.assertEquals("https://img.example.com/first.png", existingResource.getShowImg());
|
||||||
|
Assert.assertEquals("https://img.example.com/first.png", existingArticle.getShowImg());
|
||||||
|
verify(resourceMapper).updateAppResource(existingResource);
|
||||||
|
verify(articleMapper).updateAppBlogArticle(existingArticle);
|
||||||
|
verify(resourceMapper).batchAppResourceList(any());
|
||||||
|
verify(resourceMapper, never()).insertAppResource(any());
|
||||||
|
verify(articleMapper, never()).insertAppBlogArticle(any());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertResourceListDefaults(AppResourceList item, int sortOrder)
|
||||||
|
{
|
||||||
|
Assert.assertEquals(Integer.valueOf(0), item.getPriceFen());
|
||||||
|
Assert.assertEquals(Integer.valueOf(1), item.getStatus());
|
||||||
|
Assert.assertEquals(Integer.valueOf(sortOrder), item.getSortOrder());
|
||||||
|
Assert.assertEquals(Long.valueOf(100L), item.getAppResourceId());
|
||||||
|
Assert.assertEquals("", item.getPassword());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,43 @@ public class TtProjectInfoServiceImplTest
|
|||||||
Assert.assertEquals(1, result.getAddedCount());
|
Assert.assertEquals(1, result.getAddedCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extractsAnyTextInsideFullWidthBracketsAsProjectNumber()
|
||||||
|
{
|
||||||
|
quarkRow.setProjectName("课程项目【 K001 】药店进销存系统");
|
||||||
|
when(parser.parse(file, "QUARK")).thenReturn(Collections.singletonList(quarkRow));
|
||||||
|
when(projectInfoMapper.selectTtProjectInfoList(any(TtProjectInfo.class)))
|
||||||
|
.thenReturn(Collections.emptyList());
|
||||||
|
when(projectInfoMapper.insertTtProjectInfo(any(TtProjectInfo.class))).thenReturn(1);
|
||||||
|
|
||||||
|
service.importProjectLinks(file, "QUARK");
|
||||||
|
|
||||||
|
ArgumentCaptor<TtProjectInfo> captor = ArgumentCaptor.forClass(TtProjectInfo.class);
|
||||||
|
verify(projectInfoMapper).insertTtProjectInfo(captor.capture());
|
||||||
|
Assert.assertEquals("K001", captor.getValue().getProjectNum());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void backfillsProjectNumberForExistingProjectWhenBlank()
|
||||||
|
{
|
||||||
|
quarkRow.setProjectName("【K001】药店进销存系统");
|
||||||
|
TtProjectInfo existing = new TtProjectInfo();
|
||||||
|
existing.setId(1);
|
||||||
|
existing.setProjectName("【K001】药店进销存系统");
|
||||||
|
existing.setProjectUrl("https://pan.quark.cn/s/741bf63df1bb");
|
||||||
|
|
||||||
|
when(parser.parse(file, "QUARK")).thenReturn(Collections.singletonList(quarkRow));
|
||||||
|
when(projectInfoMapper.selectTtProjectInfoList(any(TtProjectInfo.class)))
|
||||||
|
.thenReturn(Collections.singletonList(existing));
|
||||||
|
when(projectInfoMapper.updateProjectNumIfBlank(1, "K001")).thenReturn(1);
|
||||||
|
|
||||||
|
ProjectLinkImportResult result = service.importProjectLinks(file, "QUARK");
|
||||||
|
|
||||||
|
verify(projectInfoMapper).updateProjectNumIfBlank(1, "K001");
|
||||||
|
Assert.assertEquals("K001", existing.getProjectNum());
|
||||||
|
Assert.assertEquals(1, result.getUnchangedCount());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void updatesOnlyBaiduLinkForExistingProjectAndAddsPasscode()
|
public void updatesOnlyBaiduLinkForExistingProjectAndAddsPasscode()
|
||||||
{
|
{
|
||||||
@@ -89,6 +126,7 @@ public class TtProjectInfoServiceImplTest
|
|||||||
|
|
||||||
TtProjectInfo existing = new TtProjectInfo();
|
TtProjectInfo existing = new TtProjectInfo();
|
||||||
existing.setId(31);
|
existing.setId(31);
|
||||||
|
existing.setProjectNum("S031");
|
||||||
existing.setProjectName("【S031】婚纱摄影管理系统");
|
existing.setProjectName("【S031】婚纱摄影管理系统");
|
||||||
existing.setProjectName1("原源码名称");
|
existing.setProjectName1("原源码名称");
|
||||||
existing.setProjectUrl("https://pan.quark.cn/s/old");
|
existing.setProjectUrl("https://pan.quark.cn/s/old");
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ public interface AppBlogArticleMapper
|
|||||||
*/
|
*/
|
||||||
public AppBlogArticle selectAppBlogArticleById(Long id);
|
public AppBlogArticle selectAppBlogArticleById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据完整标题精确查询文章。
|
||||||
|
*
|
||||||
|
* @param title 文章标题
|
||||||
|
* @return 文章
|
||||||
|
*/
|
||||||
|
public AppBlogArticle selectAppBlogArticleByTitle(String title);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询文章列表
|
* 查询文章列表
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -91,6 +91,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
<include refid="selectAppBlogArticleVo"/>
|
<include refid="selectAppBlogArticleVo"/>
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectAppBlogArticleByTitle" parameterType="String" resultMap="AppBlogArticleResult">
|
||||||
|
<include refid="selectAppBlogArticleVo"/>
|
||||||
|
where title = #{title}
|
||||||
|
limit 1
|
||||||
|
</select>
|
||||||
|
|
||||||
<insert id="insertAppBlogArticle" parameterType="AppBlogArticle" useGeneratedKeys="true" keyProperty="id">
|
<insert id="insertAppBlogArticle" parameterType="AppBlogArticle" useGeneratedKeys="true" keyProperty="id">
|
||||||
insert into app_blog_article
|
insert into app_blog_article
|
||||||
@@ -168,4 +174,4 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|||||||
#{id}
|
#{id}
|
||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -43,20 +43,12 @@ export function delCode(codeId) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function transToArticle(codeId) {
|
export function transToArticle1(codeId, templateId) {
|
||||||
return request({
|
|
||||||
url: '/office/code/transToArticle/' + codeId,
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function transToArticle1(codeId, coverUrl) {
|
|
||||||
return request({
|
return request({
|
||||||
url: '/office/code/transToArticle1/' + codeId,
|
url: '/office/code/transToArticle1/' + codeId,
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params: {
|
params: {
|
||||||
coverUrl: coverUrl
|
templateId: templateId
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,14 +115,6 @@
|
|||||||
<!-- <el-table-column label="视频文件" align="center" prop="videoFile" />-->
|
<!-- <el-table-column label="视频文件" align="center" prop="videoFile" />-->
|
||||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="text"
|
|
||||||
icon="el-icon-document-copy"
|
|
||||||
@click="transToArticle(scope.row)"
|
|
||||||
v-hasPermi="['office:code:edit']"
|
|
||||||
>转文章
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="mini"
|
||||||
type="text"
|
type="text"
|
||||||
@@ -262,6 +254,22 @@
|
|||||||
|
|
||||||
<!-- 封面预览对话框 -->
|
<!-- 封面预览对话框 -->
|
||||||
<el-dialog :title="'封面预览'" :visible.sync="coverDialogVisible" width="500px" append-to-body>
|
<el-dialog :title="'封面预览'" :visible.sync="coverDialogVisible" width="500px" append-to-body>
|
||||||
|
<el-form label-width="80px">
|
||||||
|
<el-form-item label="文案模板">
|
||||||
|
<el-select
|
||||||
|
v-model="articleTemplateId"
|
||||||
|
placeholder="请选择文案模板"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="tpl in copyTemplates"
|
||||||
|
:key="tpl.templateId"
|
||||||
|
:label="tpl.templateName"
|
||||||
|
:value="tpl.templateId"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
<div class="cover-preview" style="text-align: center;">
|
<div class="cover-preview" style="text-align: center;">
|
||||||
<img :src="coverImageUrl" style="max-width: 100%; height: auto;" v-if="coverImageUrl"/>
|
<img :src="coverImageUrl" style="max-width: 100%; height: auto;" v-if="coverImageUrl"/>
|
||||||
<div v-else>加载中...</div>
|
<div v-else>加载中...</div>
|
||||||
@@ -276,11 +284,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {listCode, getCode, delCode, addCode, updateCode, transToArticle, transToArticle1} from "@/api/office/code";
|
import {listCode, getCode, delCode, addCode, updateCode, transToArticle1} from "@/api/office/code";
|
||||||
import {listEnabledTemplates} from "@/api/office/copyTemplate";
|
import {listEnabledTemplates} from "@/api/office/copyTemplate";
|
||||||
import {html2Text} from "../../../utils";
|
import {html2Text} from "../../../utils";
|
||||||
import {getToken} from "@/utils/auth";
|
import {getToken} from "@/utils/auth";
|
||||||
import request from '@/utils/request';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "Code",
|
name: "Code",
|
||||||
@@ -337,7 +344,9 @@
|
|||||||
currentFileName: '',
|
currentFileName: '',
|
||||||
currentRow: '',
|
currentRow: '',
|
||||||
// 文案模板列表(动态按钮)
|
// 文案模板列表(动态按钮)
|
||||||
copyTemplates: []
|
copyTemplates: [],
|
||||||
|
// 转文章时使用的文案模板
|
||||||
|
articleTemplateId: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@@ -448,8 +457,21 @@
|
|||||||
loadCopyTemplates() {
|
loadCopyTemplates() {
|
||||||
listEnabledTemplates().then(response => {
|
listEnabledTemplates().then(response => {
|
||||||
this.copyTemplates = response.data || [];
|
this.copyTemplates = response.data || [];
|
||||||
|
this.selectDefaultArticleTemplate();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
selectDefaultArticleTemplate() {
|
||||||
|
if (!this.copyTemplates.length) {
|
||||||
|
this.articleTemplateId = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selectedExists = this.copyTemplates.some(tpl => tpl.templateId === this.articleTemplateId);
|
||||||
|
if (selectedExists) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const defaultTemplate = this.copyTemplates.find(tpl => tpl.templateName === '南音');
|
||||||
|
this.articleTemplateId = (defaultTemplate || this.copyTemplates[0]).templateId;
|
||||||
|
},
|
||||||
cancelDetail() {
|
cancelDetail() {
|
||||||
this.openDetail = false;
|
this.openDetail = false;
|
||||||
this.reset();
|
this.reset();
|
||||||
@@ -507,14 +529,9 @@
|
|||||||
this.title = "查看源码信息";
|
this.title = "查看源码信息";
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
transToArticle(row) {
|
|
||||||
transToArticle(row.codeId).then(response => {
|
|
||||||
console.log(response)
|
|
||||||
this.$modal.msgSuccess(response.msg);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
showCoverDialog(row) {
|
showCoverDialog(row) {
|
||||||
this.currentRow = row
|
this.currentRow = row
|
||||||
|
this.selectDefaultArticleTemplate()
|
||||||
const id = row.codeId || this.ids
|
const id = row.codeId || this.ids
|
||||||
const baseUrl = process.env.VUE_APP_BASE_API
|
const baseUrl = process.env.VUE_APP_BASE_API
|
||||||
fetch(`${baseUrl}/office/project/cover/${id}`, {
|
fetch(`${baseUrl}/office/project/cover/${id}`, {
|
||||||
@@ -564,36 +581,17 @@
|
|||||||
this.$modal.msgSuccess('封面图片已下载')
|
this.$modal.msgSuccess('封面图片已下载')
|
||||||
},
|
},
|
||||||
handleTransToArticle1() {
|
handleTransToArticle1() {
|
||||||
// 将 Blob 转换为 File 对象
|
if (!this.articleTemplateId) {
|
||||||
const file = new File([this.previewBlob], this.currentFileName, { type: 'image/png' });
|
this.$modal.msgError('请选择文案模板');
|
||||||
// 创建 FormData
|
return;
|
||||||
const formData = new FormData();
|
}
|
||||||
formData.append('file', file);
|
transToArticle1(this.currentRow.codeId, this.articleTemplateId).then(response => {
|
||||||
// 上传到七牛云
|
this.$modal.msgSuccess(response.msg);
|
||||||
request({
|
this.getList();
|
||||||
url: '/common/qiniuUpload',
|
|
||||||
method: 'post',
|
|
||||||
data: formData,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'multipart/form-data'
|
|
||||||
}
|
|
||||||
}).then(response => {
|
|
||||||
if (response.code === 200) {
|
|
||||||
// 获取上传后的URL
|
|
||||||
const imageUrl = response.url;
|
|
||||||
// 调用转文章接口
|
|
||||||
transToArticle1(this.currentRow.codeId, imageUrl).then(response => {
|
|
||||||
this.$modal.msgSuccess(response.msg);
|
|
||||||
this.getList();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.$modal.msgError('封面图片上传失败');
|
|
||||||
}
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error('转文章失败:', error);
|
console.error('转文章失败:', error);
|
||||||
this.$modal.msgError('转文章失败');
|
this.$modal.msgError('转文章失败');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.coverDialogVisible = false;
|
this.coverDialogVisible = false;
|
||||||
},
|
},
|
||||||
/** 提交按钮 */
|
/** 提交按钮 */
|
||||||
|
|||||||
Reference in New Issue
Block a user