Add multi-platform publishing workflow
This commit is contained in:
@@ -57,11 +57,11 @@ public class TtCopyTemplateController extends BaseController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有启用的文案模板(供源码明细页动态按钮使用,无需分页权限)
|
||||
* 按场景查询所有启用的文案模板(无需分页权限)
|
||||
*/
|
||||
@GetMapping("/enabled")
|
||||
public AjaxResult listEnabled() {
|
||||
return success(ttCopyTemplateService.selectEnabledTemplates());
|
||||
public AjaxResult listEnabled(@RequestParam(value = "templateScene", required = false) String templateScene) {
|
||||
return success(ttCopyTemplateService.selectEnabledTemplates(templateScene));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.ruoyi.office.controller;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.CrossOrigin;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
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.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.office.domain.PublishBridgeTask;
|
||||
import com.ruoyi.office.domain.PublishMaterial;
|
||||
import com.ruoyi.office.domain.PublishProjectRow;
|
||||
import com.ruoyi.office.domain.PublishStatusUpdate;
|
||||
import com.ruoyi.office.domain.TtPublishTask;
|
||||
import com.ruoyi.office.service.ITtPublishTaskService;
|
||||
|
||||
/**
|
||||
* 后台发布管理与浏览器扩展桥接接口。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/office/publish")
|
||||
public class TtPublishTaskController extends BaseController
|
||||
{
|
||||
@Autowired
|
||||
private ITtPublishTaskService publishTaskService;
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('office:publish:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(PublishProjectRow query)
|
||||
{
|
||||
startPage();
|
||||
List<PublishProjectRow> list = publishTaskService.selectPublishProjectList(query);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('office:publish:query')")
|
||||
@GetMapping("/material/{codeId}")
|
||||
public AjaxResult material(@PathVariable Long codeId, @RequestParam String platform)
|
||||
{
|
||||
PublishMaterial material = publishTaskService.selectProjectMaterial(codeId, platform);
|
||||
return AjaxResult.success(material);
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('office:publish:add')")
|
||||
@Log(title = "创建平台发布任务", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/task")
|
||||
public AjaxResult createTask(@RequestBody TtPublishTask task)
|
||||
{
|
||||
return AjaxResult.success("发布任务已创建", publishTaskService.createPublishTask(task));
|
||||
}
|
||||
|
||||
@PreAuthorize("@ss.hasPermi('office:publish:add')")
|
||||
@Log(title = "创建多平台发布任务", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/tasks")
|
||||
public AjaxResult createTasks(@RequestBody List<TtPublishTask> tasks)
|
||||
{
|
||||
return AjaxResult.success("多平台发布任务已创建", publishTaskService.createPublishTasks(tasks));
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@CrossOrigin(origins = "*")
|
||||
@GetMapping("/bridge/task/{taskId}")
|
||||
public AjaxResult bridgeTask(@PathVariable String taskId, @RequestParam String token)
|
||||
{
|
||||
PublishBridgeTask task = publishTaskService.selectBridgeTask(taskId, token);
|
||||
return AjaxResult.success(task);
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@CrossOrigin(origins = "*")
|
||||
@PostMapping("/bridge/task/{taskId}/status")
|
||||
public AjaxResult bridgeStatus(@PathVariable String taskId,
|
||||
@RequestParam String token,
|
||||
@RequestBody PublishStatusUpdate update)
|
||||
{
|
||||
return AjaxResult.success(publishTaskService.updateBridgeStatus(taskId, token, update));
|
||||
}
|
||||
|
||||
@Anonymous
|
||||
@CrossOrigin(origins = "*")
|
||||
@GetMapping({
|
||||
"/bridge/task/{taskId}/asset/{kind}",
|
||||
"/bridge/task/{taskId}/asset/{kind}/{index}"
|
||||
})
|
||||
public void bridgeAsset(@PathVariable String taskId,
|
||||
@PathVariable String kind,
|
||||
@PathVariable(required = false) Integer index,
|
||||
@RequestParam String token,
|
||||
HttpServletResponse response) throws IOException
|
||||
{
|
||||
publishTaskService.writeBridgeAsset(taskId, token, kind, index, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.ruoyi.office.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 浏览器扩展可读取的最小发布任务数据,不包含后台身份凭证。
|
||||
*/
|
||||
public class PublishBridgeTask
|
||||
{
|
||||
private String taskId;
|
||||
private String platform;
|
||||
private String title;
|
||||
private String content;
|
||||
private List<String> tags = new ArrayList<>();
|
||||
private boolean hasVideo;
|
||||
private boolean hasCover;
|
||||
private int imageCount;
|
||||
|
||||
public String getTaskId() { return taskId; }
|
||||
public void setTaskId(String taskId) { this.taskId = taskId; }
|
||||
public String getPlatform() { return platform; }
|
||||
public void setPlatform(String platform) { this.platform = platform; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public List<String> getTags() { return tags; }
|
||||
public void setTags(List<String> tags) { this.tags = tags; }
|
||||
public boolean isHasVideo() { return hasVideo; }
|
||||
public void setHasVideo(boolean hasVideo) { this.hasVideo = hasVideo; }
|
||||
public boolean isHasCover() { return hasCover; }
|
||||
public void setHasCover(boolean hasCover) { this.hasCover = hasCover; }
|
||||
public int getImageCount() { return imageCount; }
|
||||
public void setImageCount(int imageCount) { this.imageCount = imageCount; }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.ruoyi.office.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 创建发布任务前返回给管理页面的项目素材。
|
||||
*/
|
||||
public class PublishMaterial
|
||||
{
|
||||
private Long codeId;
|
||||
private String codeName;
|
||||
private String title;
|
||||
private String content;
|
||||
private Long templateId;
|
||||
private String templateName;
|
||||
private String templateGroup;
|
||||
private String tags;
|
||||
private String videoUrl;
|
||||
private String coverUrl;
|
||||
private List<String> imageUrls = new ArrayList<>();
|
||||
|
||||
public Long getCodeId() { return codeId; }
|
||||
public void setCodeId(Long codeId) { this.codeId = codeId; }
|
||||
public String getCodeName() { return codeName; }
|
||||
public void setCodeName(String codeName) { this.codeName = codeName; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public Long getTemplateId() { return templateId; }
|
||||
public void setTemplateId(Long templateId) { this.templateId = templateId; }
|
||||
public String getTemplateName() { return templateName; }
|
||||
public void setTemplateName(String templateName) { this.templateName = templateName; }
|
||||
public String getTemplateGroup() { return templateGroup; }
|
||||
public void setTemplateGroup(String templateGroup) { this.templateGroup = templateGroup; }
|
||||
public String getTags() { return tags; }
|
||||
public void setTags(String tags) { this.tags = tags; }
|
||||
public String getVideoUrl() { return videoUrl; }
|
||||
public void setVideoUrl(String videoUrl) { this.videoUrl = videoUrl; }
|
||||
public String getCoverUrl() { return coverUrl; }
|
||||
public void setCoverUrl(String coverUrl) { this.coverUrl = coverUrl; }
|
||||
public List<String> getImageUrls() { return imageUrls; }
|
||||
public void setImageUrls(List<String> imageUrls) { this.imageUrls = imageUrls; }
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.ruoyi.office.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 发布管理页面中的项目聚合行。
|
||||
*/
|
||||
public class PublishProjectRow extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long codeId;
|
||||
private String codeName;
|
||||
private String xiaohongshuTaskId;
|
||||
private String xiaohongshuStatus;
|
||||
private String douyinTaskId;
|
||||
private String douyinStatus;
|
||||
private String wechatChannelsTaskId;
|
||||
private String wechatChannelsStatus;
|
||||
private String bilibiliTaskId;
|
||||
private String bilibiliStatus;
|
||||
private String xianyuTaskId;
|
||||
private String xianyuStatus;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastPublishTime;
|
||||
|
||||
public Long getCodeId() { return codeId; }
|
||||
public void setCodeId(Long codeId) { this.codeId = codeId; }
|
||||
public String getCodeName() { return codeName; }
|
||||
public void setCodeName(String codeName) { this.codeName = codeName; }
|
||||
public String getXiaohongshuTaskId() { return xiaohongshuTaskId; }
|
||||
public void setXiaohongshuTaskId(String xiaohongshuTaskId) { this.xiaohongshuTaskId = xiaohongshuTaskId; }
|
||||
public String getXiaohongshuStatus() { return xiaohongshuStatus; }
|
||||
public void setXiaohongshuStatus(String xiaohongshuStatus) { this.xiaohongshuStatus = xiaohongshuStatus; }
|
||||
public String getDouyinTaskId() { return douyinTaskId; }
|
||||
public void setDouyinTaskId(String douyinTaskId) { this.douyinTaskId = douyinTaskId; }
|
||||
public String getDouyinStatus() { return douyinStatus; }
|
||||
public void setDouyinStatus(String douyinStatus) { this.douyinStatus = douyinStatus; }
|
||||
public String getWechatChannelsTaskId() { return wechatChannelsTaskId; }
|
||||
public void setWechatChannelsTaskId(String wechatChannelsTaskId) { this.wechatChannelsTaskId = wechatChannelsTaskId; }
|
||||
public String getWechatChannelsStatus() { return wechatChannelsStatus; }
|
||||
public void setWechatChannelsStatus(String wechatChannelsStatus) { this.wechatChannelsStatus = wechatChannelsStatus; }
|
||||
public String getBilibiliTaskId() { return bilibiliTaskId; }
|
||||
public void setBilibiliTaskId(String bilibiliTaskId) { this.bilibiliTaskId = bilibiliTaskId; }
|
||||
public String getBilibiliStatus() { return bilibiliStatus; }
|
||||
public void setBilibiliStatus(String bilibiliStatus) { this.bilibiliStatus = bilibiliStatus; }
|
||||
public String getXianyuTaskId() { return xianyuTaskId; }
|
||||
public void setXianyuTaskId(String xianyuTaskId) { this.xianyuTaskId = xianyuTaskId; }
|
||||
public String getXianyuStatus() { return xianyuStatus; }
|
||||
public void setXianyuStatus(String xianyuStatus) { this.xianyuStatus = xianyuStatus; }
|
||||
public Date getLastPublishTime() { return lastPublishTime; }
|
||||
public void setLastPublishTime(Date lastPublishTime) { this.lastPublishTime = lastPublishTime; }
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.ruoyi.office.domain;
|
||||
|
||||
/** 浏览器扩展发布状态回传。 */
|
||||
public class PublishStatusUpdate
|
||||
{
|
||||
private String status;
|
||||
private String message;
|
||||
private String error;
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getMessage() { return message; }
|
||||
public void setMessage(String message) { this.message = message; }
|
||||
public String getError() { return error; }
|
||||
public void setError(String error) { this.error = error; }
|
||||
}
|
||||
@@ -25,6 +25,10 @@ public class TtCopyTemplate extends BaseEntity {
|
||||
@Excel(name = "模板分组")
|
||||
private String templateGroup;
|
||||
|
||||
/** 模板场景(CODE源码文案 DELIVERY发货文案) */
|
||||
@Excel(name = "模板场景")
|
||||
private String templateScene;
|
||||
|
||||
/** 文案模板内容,支持 {变量} 占位符 */
|
||||
@Excel(name = "模板内容")
|
||||
private String templateBody;
|
||||
@@ -61,6 +65,14 @@ public class TtCopyTemplate extends BaseEntity {
|
||||
return templateGroup;
|
||||
}
|
||||
|
||||
public void setTemplateScene(String templateScene) {
|
||||
this.templateScene = templateScene;
|
||||
}
|
||||
|
||||
public String getTemplateScene() {
|
||||
return templateScene;
|
||||
}
|
||||
|
||||
public void setTemplateBody(String templateBody) {
|
||||
this.templateBody = templateBody;
|
||||
}
|
||||
@@ -91,6 +103,7 @@ public class TtCopyTemplate extends BaseEntity {
|
||||
.append("templateId", getTemplateId())
|
||||
.append("templateName", getTemplateName())
|
||||
.append("templateGroup", getTemplateGroup())
|
||||
.append("templateScene", getTemplateScene())
|
||||
.append("templateBody", getTemplateBody())
|
||||
.append("sortNum", getSortNum())
|
||||
.append("status", getStatus())
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.ruoyi.office.domain;
|
||||
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
|
||||
/**
|
||||
* 多平台发布任务对象 tt_publish_task。
|
||||
*/
|
||||
public class TtPublishTask extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long publishId;
|
||||
private String taskId;
|
||||
private Long codeId;
|
||||
private String codeName;
|
||||
private String platform;
|
||||
private String title;
|
||||
private String content;
|
||||
private String tags;
|
||||
private String videoUrl;
|
||||
private String imageUrls;
|
||||
private String coverUrl;
|
||||
private String status;
|
||||
private String statusMessage;
|
||||
private String lastError;
|
||||
|
||||
private String bridgeToken;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date bridgeExpiresAt;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date startedAt;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date readyAt;
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date finishedAt;
|
||||
|
||||
public Long getPublishId() { return publishId; }
|
||||
public void setPublishId(Long publishId) { this.publishId = publishId; }
|
||||
public String getTaskId() { return taskId; }
|
||||
public void setTaskId(String taskId) { this.taskId = taskId; }
|
||||
public Long getCodeId() { return codeId; }
|
||||
public void setCodeId(Long codeId) { this.codeId = codeId; }
|
||||
public String getCodeName() { return codeName; }
|
||||
public void setCodeName(String codeName) { this.codeName = codeName; }
|
||||
public String getPlatform() { return platform; }
|
||||
public void setPlatform(String platform) { this.platform = platform; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public String getTags() { return tags; }
|
||||
public void setTags(String tags) { this.tags = tags; }
|
||||
public String getVideoUrl() { return videoUrl; }
|
||||
public void setVideoUrl(String videoUrl) { this.videoUrl = videoUrl; }
|
||||
public String getImageUrls() { return imageUrls; }
|
||||
public void setImageUrls(String imageUrls) { this.imageUrls = imageUrls; }
|
||||
public String getCoverUrl() { return coverUrl; }
|
||||
public void setCoverUrl(String coverUrl) { this.coverUrl = coverUrl; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getStatusMessage() { return statusMessage; }
|
||||
public void setStatusMessage(String statusMessage) { this.statusMessage = statusMessage; }
|
||||
public String getLastError() { return lastError; }
|
||||
public void setLastError(String lastError) { this.lastError = lastError; }
|
||||
public String getBridgeToken() { return bridgeToken; }
|
||||
public void setBridgeToken(String bridgeToken) { this.bridgeToken = bridgeToken; }
|
||||
public Date getBridgeExpiresAt() { return bridgeExpiresAt; }
|
||||
public void setBridgeExpiresAt(Date bridgeExpiresAt) { this.bridgeExpiresAt = bridgeExpiresAt; }
|
||||
public Date getStartedAt() { return startedAt; }
|
||||
public void setStartedAt(Date startedAt) { this.startedAt = startedAt; }
|
||||
public Date getReadyAt() { return readyAt; }
|
||||
public void setReadyAt(Date readyAt) { this.readyAt = readyAt; }
|
||||
public Date getFinishedAt() { return finishedAt; }
|
||||
public void setFinishedAt(Date finishedAt) { this.finishedAt = finishedAt; }
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.ruoyi.office.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.office.domain.TtCopyTemplate;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
/**
|
||||
* 文案模板Mapper接口
|
||||
@@ -24,7 +25,7 @@ public interface TtCopyTemplateMapper {
|
||||
/**
|
||||
* 查询所有启用的文案模板(按排序号升序)
|
||||
*/
|
||||
public List<TtCopyTemplate> selectEnabledTemplates();
|
||||
public List<TtCopyTemplate> selectEnabledTemplates(@Param("templateScene") String templateScene);
|
||||
|
||||
/**
|
||||
* 新增文案模板
|
||||
@@ -45,4 +46,4 @@ public interface TtCopyTemplateMapper {
|
||||
* 删除文案模板信息
|
||||
*/
|
||||
public int deleteTtCopyTemplateByTemplateId(Long templateId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.office.mapper;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import com.ruoyi.office.domain.PublishProjectRow;
|
||||
import com.ruoyi.office.domain.TtPublishTask;
|
||||
|
||||
public interface TtPublishTaskMapper
|
||||
{
|
||||
List<PublishProjectRow> selectPublishProjectList(PublishProjectRow query);
|
||||
|
||||
TtPublishTask selectPublishTaskByTaskId(String taskId);
|
||||
|
||||
TtPublishTask selectPublishTaskByBridge(@Param("taskId") String taskId,
|
||||
@Param("bridgeToken") String bridgeToken);
|
||||
|
||||
int insertPublishTask(TtPublishTask task);
|
||||
|
||||
int updatePublishTaskStatus(TtPublishTask task);
|
||||
}
|
||||
@@ -26,6 +26,11 @@ public interface ITtCopyTemplateService {
|
||||
*/
|
||||
public List<TtCopyTemplate> selectEnabledTemplates();
|
||||
|
||||
/**
|
||||
* 按场景查询所有启用的文案模板
|
||||
*/
|
||||
public List<TtCopyTemplate> selectEnabledTemplates(String templateScene);
|
||||
|
||||
/**
|
||||
* 新增文案模板
|
||||
*/
|
||||
@@ -45,4 +50,4 @@ public interface ITtCopyTemplateService {
|
||||
* 删除文案模板信息
|
||||
*/
|
||||
public int deleteTtCopyTemplateByTemplateId(Long templateId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.ruoyi.office.service;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import com.ruoyi.office.domain.PublishBridgeTask;
|
||||
import com.ruoyi.office.domain.PublishMaterial;
|
||||
import com.ruoyi.office.domain.PublishProjectRow;
|
||||
import com.ruoyi.office.domain.PublishStatusUpdate;
|
||||
import com.ruoyi.office.domain.TtPublishTask;
|
||||
|
||||
public interface ITtPublishTaskService
|
||||
{
|
||||
List<PublishProjectRow> selectPublishProjectList(PublishProjectRow query);
|
||||
|
||||
PublishMaterial selectProjectMaterial(Long codeId, String platform);
|
||||
|
||||
TtPublishTask createPublishTask(TtPublishTask task);
|
||||
|
||||
List<TtPublishTask> createPublishTasks(List<TtPublishTask> tasks);
|
||||
|
||||
PublishBridgeTask selectBridgeTask(String taskId, String token);
|
||||
|
||||
TtPublishTask updateBridgeStatus(String taskId, String token, PublishStatusUpdate update);
|
||||
|
||||
void writeBridgeAsset(String taskId, String token, String kind, Integer index,
|
||||
HttpServletResponse response) throws IOException;
|
||||
}
|
||||
@@ -32,7 +32,12 @@ public class TtCopyTemplateServiceImpl implements ITtCopyTemplateService {
|
||||
|
||||
@Override
|
||||
public List<TtCopyTemplate> selectEnabledTemplates() {
|
||||
return ttCopyTemplateMapper.selectEnabledTemplates();
|
||||
return selectEnabledTemplates(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TtCopyTemplate> selectEnabledTemplates(String templateScene) {
|
||||
return ttCopyTemplateMapper.selectEnabledTemplates(templateScene);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,4 +61,4 @@ public class TtCopyTemplateServiceImpl implements ITtCopyTemplateService {
|
||||
public int deleteTtCopyTemplateByTemplateId(Long templateId) {
|
||||
return ttCopyTemplateMapper.deleteTtCopyTemplateByTemplateId(templateId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,524 @@
|
||||
package com.ruoyi.office.service.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.ruoyi.common.config.RuoYiConfig;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.office.domain.PublishBridgeTask;
|
||||
import com.ruoyi.office.domain.PublishMaterial;
|
||||
import com.ruoyi.office.domain.PublishProjectRow;
|
||||
import com.ruoyi.office.domain.PublishStatusUpdate;
|
||||
import com.ruoyi.office.domain.TtCode;
|
||||
import com.ruoyi.office.domain.TtCopyTemplate;
|
||||
import com.ruoyi.office.domain.TtPublishTask;
|
||||
import com.ruoyi.office.mapper.TtPublishTaskMapper;
|
||||
import com.ruoyi.office.service.CopyTemplateRenderer;
|
||||
import com.ruoyi.office.service.ITtCodeService;
|
||||
import com.ruoyi.office.service.ITtCopyTemplateService;
|
||||
import com.ruoyi.office.service.ITtPublishTaskService;
|
||||
|
||||
@Service
|
||||
public class TtPublishTaskServiceImpl implements ITtPublishTaskService
|
||||
{
|
||||
private static final Set<String> PLATFORMS = Collections.unmodifiableSet(
|
||||
new LinkedHashSet<>(Arrays.asList("xiaohongshu", "douyin", "wechat_channels", "bilibili", "xianyu")));
|
||||
|
||||
private static final Set<String> EXTENSION_STATUSES = Collections.unmodifiableSet(
|
||||
new LinkedHashSet<>(Arrays.asList(
|
||||
"OPENING", "LOGIN_REQUIRED", "DOWNLOADING", "UPLOADING",
|
||||
"FILLING", "READY_TO_PUBLISH", "SUCCEEDED", "FAILED", "CANCELLED"
|
||||
)));
|
||||
|
||||
@Autowired
|
||||
private TtPublishTaskMapper publishTaskMapper;
|
||||
|
||||
@Autowired
|
||||
private ITtCodeService codeService;
|
||||
|
||||
@Autowired
|
||||
private ITtCopyTemplateService copyTemplateService;
|
||||
|
||||
@Autowired
|
||||
private CopyTemplateRenderer copyTemplateRenderer;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Override
|
||||
public List<PublishProjectRow> selectPublishProjectList(PublishProjectRow query)
|
||||
{
|
||||
return publishTaskMapper.selectPublishProjectList(query);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublishMaterial selectProjectMaterial(Long codeId, String platform)
|
||||
{
|
||||
String templateGroup = platformTemplateGroup(platform);
|
||||
TtCode code = requireCode(codeId);
|
||||
PublishMaterial material = new PublishMaterial();
|
||||
material.setCodeId(code.getCodeId());
|
||||
material.setCodeName(code.getCodeName());
|
||||
material.setTitle(code.getCodeName());
|
||||
TtCopyTemplate selectedTemplate = selectPublishTemplate(
|
||||
copyTemplateService.selectEnabledTemplates("CODE"), templateGroup);
|
||||
if (selectedTemplate != null)
|
||||
{
|
||||
material.setTemplateId(selectedTemplate.getTemplateId());
|
||||
material.setTemplateName(selectedTemplate.getTemplateName());
|
||||
material.setTemplateGroup(selectedTemplate.getTemplateGroup());
|
||||
material.setContent(copyTemplateRenderer.render(selectedTemplate, code));
|
||||
}
|
||||
return material;
|
||||
}
|
||||
|
||||
private TtCopyTemplate selectPublishTemplate(List<TtCopyTemplate> templates, String targetGroup)
|
||||
{
|
||||
if (templates == null || templates.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
List<TtCopyTemplate> exactMatches = new ArrayList<>();
|
||||
List<TtCopyTemplate> sharedMatches = new ArrayList<>();
|
||||
for (TtCopyTemplate template : templates)
|
||||
{
|
||||
if (template == null || StringUtils.isEmpty(template.getTemplateGroup()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String group = template.getTemplateGroup().trim();
|
||||
if (targetGroup.equals(group))
|
||||
{
|
||||
exactMatches.add(template);
|
||||
}
|
||||
else if (group.contains(targetGroup))
|
||||
{
|
||||
sharedMatches.add(template);
|
||||
}
|
||||
}
|
||||
List<TtCopyTemplate> candidates = exactMatches.isEmpty() ? sharedMatches : exactMatches;
|
||||
if (candidates.isEmpty())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return candidates.get(ThreadLocalRandom.current().nextInt(candidates.size()));
|
||||
}
|
||||
|
||||
private String platformTemplateGroup(String platform)
|
||||
{
|
||||
if ("xiaohongshu".equals(platform)) return "小红书";
|
||||
if ("douyin".equals(platform)) return "抖音";
|
||||
if ("wechat_channels".equals(platform)) return "视频号";
|
||||
if ("bilibili".equals(platform)) return "B站";
|
||||
if ("xianyu".equals(platform)) return "闲鱼";
|
||||
throw new ServiceException("不支持的发布平台");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TtPublishTask createPublishTask(TtPublishTask task)
|
||||
{
|
||||
if (task == null || task.getCodeId() == null)
|
||||
{
|
||||
throw new ServiceException("请选择需要发布的项目");
|
||||
}
|
||||
if (!PLATFORMS.contains(task.getPlatform()))
|
||||
{
|
||||
throw new ServiceException("不支持的发布平台");
|
||||
}
|
||||
TtCode code = requireCode(task.getCodeId());
|
||||
task.setTitle(normalizePlatformTitle(task.getPlatform(), task.getTitle()));
|
||||
task.setContent(trimToNull(task.getContent()));
|
||||
task.setTags(trimToNull(task.getTags()));
|
||||
task.setVideoUrl(trimToNull(task.getVideoUrl()));
|
||||
task.setCoverUrl(trimToNull(task.getCoverUrl()));
|
||||
List<String> images = parseImageUrls(task.getImageUrls());
|
||||
task.setImageUrls(writeJson(images));
|
||||
|
||||
if (StringUtils.isEmpty(task.getTitle()))
|
||||
{
|
||||
throw new ServiceException("发布标题不能为空");
|
||||
}
|
||||
int titleLimit = "xianyu".equals(task.getPlatform()) || "wechat_channels".equals(task.getPlatform()) ? 30
|
||||
: ("bilibili".equals(task.getPlatform()) ? 80 : 100);
|
||||
if (task.getTitle().length() > titleLimit)
|
||||
{
|
||||
throw new ServiceException("发布标题不能超过" + titleLimit + "个字符");
|
||||
}
|
||||
if (task.getContent() != null && task.getContent().length() > 5000)
|
||||
{
|
||||
throw new ServiceException("发布文案不能超过5000个字符");
|
||||
}
|
||||
if ("xiaohongshu".equals(task.getPlatform())
|
||||
&& StringUtils.isEmpty(task.getVideoUrl()) && images.isEmpty())
|
||||
{
|
||||
throw new ServiceException("发布小红书至少需要一个视频或一张图片");
|
||||
}
|
||||
if (("douyin".equals(task.getPlatform()) || "wechat_channels".equals(task.getPlatform())
|
||||
|| "bilibili".equals(task.getPlatform()))
|
||||
&& StringUtils.isEmpty(task.getVideoUrl()))
|
||||
{
|
||||
throw new ServiceException("发布抖音、视频号或B站需要选择视频");
|
||||
}
|
||||
if ("xianyu".equals(task.getPlatform()))
|
||||
{
|
||||
if (images.isEmpty())
|
||||
{
|
||||
throw new ServiceException("发布闲鱼商品至少需要一张商品图片");
|
||||
}
|
||||
}
|
||||
|
||||
String username = SecurityUtils.getUsername();
|
||||
task.setCodeName(code.getCodeName());
|
||||
task.setTaskId("publish_" + UUID.randomUUID().toString().replace("-", ""));
|
||||
task.setBridgeToken(UUID.randomUUID().toString().replace("-", ""));
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.HOUR, 4);
|
||||
task.setBridgeExpiresAt(calendar.getTime());
|
||||
task.setStatus("PENDING");
|
||||
task.setStatusMessage("等待浏览器扩展接收任务");
|
||||
task.setCreateBy(username);
|
||||
task.setUpdateBy(username);
|
||||
publishTaskMapper.insertPublishTask(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public List<TtPublishTask> createPublishTasks(List<TtPublishTask> tasks)
|
||||
{
|
||||
if (tasks == null || tasks.isEmpty())
|
||||
{
|
||||
throw new ServiceException("请至少选择一个发布平台");
|
||||
}
|
||||
if (tasks.size() > PLATFORMS.size())
|
||||
{
|
||||
throw new ServiceException("一次最多创建" + PLATFORMS.size() + "个平台任务");
|
||||
}
|
||||
Set<String> selectedPlatforms = new LinkedHashSet<>();
|
||||
List<TtPublishTask> created = new ArrayList<>();
|
||||
for (TtPublishTask task : tasks)
|
||||
{
|
||||
if (task == null || !selectedPlatforms.add(task.getPlatform()))
|
||||
{
|
||||
throw new ServiceException("多平台发布中存在重复或无效的平台");
|
||||
}
|
||||
created.add(createPublishTask(task));
|
||||
}
|
||||
return created;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PublishBridgeTask selectBridgeTask(String taskId, String token)
|
||||
{
|
||||
TtPublishTask task = requireBridgeTask(taskId, token);
|
||||
List<String> images = parseImageUrls(task.getImageUrls());
|
||||
PublishBridgeTask bridgeTask = new PublishBridgeTask();
|
||||
bridgeTask.setTaskId(task.getTaskId());
|
||||
bridgeTask.setPlatform(task.getPlatform());
|
||||
bridgeTask.setTitle(task.getTitle());
|
||||
bridgeTask.setContent(task.getContent());
|
||||
bridgeTask.setTags(parseTags(task.getTags()));
|
||||
bridgeTask.setHasVideo(StringUtils.isNotEmpty(task.getVideoUrl()));
|
||||
bridgeTask.setHasCover(StringUtils.isNotEmpty(task.getCoverUrl()));
|
||||
bridgeTask.setImageCount(images.size());
|
||||
return bridgeTask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TtPublishTask updateBridgeStatus(String taskId, String token, PublishStatusUpdate update)
|
||||
{
|
||||
TtPublishTask existing = requireBridgeTask(taskId, token);
|
||||
if (update == null || !EXTENSION_STATUSES.contains(update.getStatus()))
|
||||
{
|
||||
throw new ServiceException("扩展回传的发布状态无效");
|
||||
}
|
||||
TtPublishTask task = new TtPublishTask();
|
||||
task.setTaskId(existing.getTaskId());
|
||||
task.setStatus(update.getStatus());
|
||||
task.setStatusMessage(limit(update.getMessage(), 255));
|
||||
task.setLastError(limit(update.getError(), 1000));
|
||||
task.setUpdateBy("browser-extension");
|
||||
Date now = new Date();
|
||||
if ("OPENING".equals(update.getStatus()))
|
||||
{
|
||||
task.setStartedAt(now);
|
||||
}
|
||||
if ("READY_TO_PUBLISH".equals(update.getStatus()))
|
||||
{
|
||||
task.setReadyAt(now);
|
||||
}
|
||||
if ("SUCCEEDED".equals(update.getStatus()) || "FAILED".equals(update.getStatus())
|
||||
|| "CANCELLED".equals(update.getStatus()))
|
||||
{
|
||||
task.setFinishedAt(now);
|
||||
}
|
||||
publishTaskMapper.updatePublishTaskStatus(task);
|
||||
return publishTaskMapper.selectPublishTaskByTaskId(taskId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBridgeAsset(String taskId, String token, String kind, Integer index,
|
||||
HttpServletResponse response) throws IOException
|
||||
{
|
||||
TtPublishTask task = requireBridgeTask(taskId, token);
|
||||
String source = selectAssetSource(task, kind, index);
|
||||
if (StringUtils.isEmpty(source))
|
||||
{
|
||||
throw new ServiceException("发布素材不存在");
|
||||
}
|
||||
|
||||
response.setHeader("Access-Control-Allow-Origin", "*");
|
||||
response.setHeader("Cross-Origin-Resource-Policy", "cross-origin");
|
||||
response.setHeader("Cache-Control", "private, no-store");
|
||||
|
||||
int profileIndex = source.indexOf(Constants.RESOURCE_PREFIX + "/");
|
||||
if (profileIndex >= 0)
|
||||
{
|
||||
String relative = source.substring(profileIndex + Constants.RESOURCE_PREFIX.length());
|
||||
Path profileRoot = Paths.get(RuoYiConfig.getProfile()).toAbsolutePath().normalize();
|
||||
Path assetPath = profileRoot.resolve(relative.replaceFirst("^[\\\\/]+", "")).normalize();
|
||||
if (!assetPath.startsWith(profileRoot) || !Files.isRegularFile(assetPath))
|
||||
{
|
||||
throw new ServiceException("发布素材文件不存在");
|
||||
}
|
||||
writeLocalAsset(assetPath.toFile(), response);
|
||||
return;
|
||||
}
|
||||
writeRemoteAsset(source, response);
|
||||
}
|
||||
|
||||
private TtCode requireCode(Long codeId)
|
||||
{
|
||||
TtCode code = codeService.selectTtCodeByCodeId(codeId);
|
||||
if (code == null)
|
||||
{
|
||||
throw new ServiceException("项目不存在或已被删除");
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
private TtPublishTask requireBridgeTask(String taskId, String token)
|
||||
{
|
||||
if (StringUtils.isEmpty(taskId) || StringUtils.isEmpty(token))
|
||||
{
|
||||
throw new ServiceException("发布任务凭证不能为空");
|
||||
}
|
||||
TtPublishTask task = publishTaskMapper.selectPublishTaskByBridge(taskId, token);
|
||||
if (task == null)
|
||||
{
|
||||
throw new ServiceException("发布任务不存在或任务凭证已过期");
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
private String selectAssetSource(TtPublishTask task, String kind, Integer index)
|
||||
{
|
||||
if ("video".equals(kind))
|
||||
{
|
||||
return task.getVideoUrl();
|
||||
}
|
||||
if ("cover".equals(kind))
|
||||
{
|
||||
return task.getCoverUrl();
|
||||
}
|
||||
if ("image".equals(kind))
|
||||
{
|
||||
List<String> images = parseImageUrls(task.getImageUrls());
|
||||
int selected = index == null ? 0 : index;
|
||||
if (selected >= 0 && selected < images.size())
|
||||
{
|
||||
return images.get(selected);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void writeLocalAsset(File file, HttpServletResponse response) throws IOException
|
||||
{
|
||||
String contentType = Files.probeContentType(file.toPath());
|
||||
response.setContentType(contentType == null ? "application/octet-stream" : contentType);
|
||||
response.setContentLengthLong(file.length());
|
||||
response.setHeader("Content-Disposition", "inline; filename*=UTF-8''" + encode(file.getName()));
|
||||
try (InputStream input = new FileInputStream(file))
|
||||
{
|
||||
copy(input, response.getOutputStream());
|
||||
}
|
||||
}
|
||||
|
||||
private void writeRemoteAsset(String source, HttpServletResponse response) throws IOException
|
||||
{
|
||||
URL url = new URL(source);
|
||||
if (!"http".equalsIgnoreCase(url.getProtocol()) && !"https".equalsIgnoreCase(url.getProtocol()))
|
||||
{
|
||||
throw new ServiceException("不支持的发布素材地址");
|
||||
}
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.setConnectTimeout(10000);
|
||||
connection.setReadTimeout(120000);
|
||||
connection.setInstanceFollowRedirects(true);
|
||||
connection.setRequestProperty("User-Agent", "RuoYi-Publish-Bridge/1.0");
|
||||
int status = connection.getResponseCode();
|
||||
if (status < 200 || status >= 300)
|
||||
{
|
||||
connection.disconnect();
|
||||
throw new ServiceException("读取远程发布素材失败,HTTP " + status);
|
||||
}
|
||||
String contentType = connection.getContentType();
|
||||
response.setContentType(contentType == null ? "application/octet-stream" : contentType);
|
||||
long length = connection.getContentLengthLong();
|
||||
if (length >= 0)
|
||||
{
|
||||
response.setContentLengthLong(length);
|
||||
}
|
||||
String fileName = new File(url.getPath()).getName();
|
||||
response.setHeader("Content-Disposition", "inline; filename*=UTF-8''" + encode(fileName));
|
||||
try (InputStream input = connection.getInputStream())
|
||||
{
|
||||
copy(input, response.getOutputStream());
|
||||
}
|
||||
finally
|
||||
{
|
||||
connection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private void copy(InputStream input, OutputStream output) throws IOException
|
||||
{
|
||||
byte[] buffer = new byte[8192];
|
||||
int length;
|
||||
while ((length = input.read(buffer)) >= 0)
|
||||
{
|
||||
output.write(buffer, 0, length);
|
||||
}
|
||||
output.flush();
|
||||
}
|
||||
|
||||
private String encode(String value) throws IOException
|
||||
{
|
||||
return URLEncoder.encode(StringUtils.defaultIfEmpty(value, "publish-asset"), "UTF-8")
|
||||
.replace("+", "%20");
|
||||
}
|
||||
|
||||
private List<String> parseImageUrls(String value)
|
||||
{
|
||||
if (StringUtils.isEmpty(value))
|
||||
{
|
||||
return new ArrayList<>();
|
||||
}
|
||||
try
|
||||
{
|
||||
if (value.trim().startsWith("["))
|
||||
{
|
||||
List<String> parsed = objectMapper.readValue(value, new TypeReference<List<String>>() { });
|
||||
return cleanUrls(parsed);
|
||||
}
|
||||
}
|
||||
catch (Exception ignored)
|
||||
{
|
||||
// 兼容历史逗号分隔形式。
|
||||
}
|
||||
return cleanUrls(Arrays.asList(value.split(",")));
|
||||
}
|
||||
|
||||
private List<String> cleanUrls(List<String> urls)
|
||||
{
|
||||
LinkedHashSet<String> result = new LinkedHashSet<>();
|
||||
if (urls != null)
|
||||
{
|
||||
for (String url : urls)
|
||||
{
|
||||
String clean = trimToNull(url);
|
||||
if (clean != null)
|
||||
{
|
||||
result.add(clean);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(result);
|
||||
}
|
||||
|
||||
private String writeJson(List<String> urls)
|
||||
{
|
||||
try
|
||||
{
|
||||
return objectMapper.writeValueAsString(urls == null ? Collections.emptyList() : urls);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ServiceException("发布图片数据格式不正确");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> parseTags(String value)
|
||||
{
|
||||
if (StringUtils.isEmpty(value))
|
||||
{
|
||||
return new ArrayList<>();
|
||||
}
|
||||
LinkedHashSet<String> tags = new LinkedHashSet<>();
|
||||
for (String part : value.split("[,,#\\s]+"))
|
||||
{
|
||||
String tag = trimToNull(part);
|
||||
if (tag != null)
|
||||
{
|
||||
tags.add(tag);
|
||||
}
|
||||
}
|
||||
return new ArrayList<>(tags);
|
||||
}
|
||||
|
||||
private String trimToNull(String value)
|
||||
{
|
||||
if (value == null) return null;
|
||||
String trimmed = value.trim();
|
||||
return trimmed.isEmpty() ? null : trimmed;
|
||||
}
|
||||
|
||||
private String normalizePlatformTitle(String platform, String value)
|
||||
{
|
||||
String title = trimToNull(value);
|
||||
if (title == null || !"wechat_channels".equals(platform))
|
||||
{
|
||||
return title;
|
||||
}
|
||||
title = title.replaceAll("[【】\\[\\]()(){}<>]", " ")
|
||||
.replaceAll("[^\\p{IsHan}A-Za-z0-9《》“”‘’\\\"'::++??%%℃,,、。!!\\s]", " ")
|
||||
.replaceAll("\\s+", " ")
|
||||
.trim();
|
||||
return StringUtils.isEmpty(title) ? null : title;
|
||||
}
|
||||
|
||||
private String limit(String value, int maxLength)
|
||||
{
|
||||
String clean = trimToNull(value);
|
||||
if (clean == null || clean.length() <= maxLength) return clean;
|
||||
return clean.substring(0, maxLength);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="templateId" column="template_id" />
|
||||
<result property="templateName" column="template_name" />
|
||||
<result property="templateGroup" column="template_group" />
|
||||
<result property="templateScene" column="template_scene" />
|
||||
<result property="templateBody" column="template_body" />
|
||||
<result property="sortNum" column="sort_num" />
|
||||
<result property="status" column="status" />
|
||||
@@ -16,7 +17,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectTtCopyTemplateVo">
|
||||
select template_id, template_name, template_group, template_body, sort_num, status, create_time, update_time
|
||||
select template_id, template_name, template_group, template_scene, template_body, sort_num, status, create_time, update_time
|
||||
from tt_copy_template
|
||||
</sql>
|
||||
|
||||
@@ -25,6 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<where>
|
||||
<if test="templateName != null and templateName != ''"> and template_name like concat('%', #{templateName}, '%')</if>
|
||||
<if test="templateGroup != null and templateGroup != ''"> and template_group like concat('%', #{templateGroup}, '%')</if>
|
||||
<if test="templateScene != null and templateScene != ''"> and template_scene = #{templateScene}</if>
|
||||
<if test="status != null and status != ''"> and status = #{status}</if>
|
||||
</where>
|
||||
order by sort_num asc, create_time asc
|
||||
@@ -33,6 +35,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<select id="selectEnabledTemplates" resultMap="TtCopyTemplateResult">
|
||||
<include refid="selectTtCopyTemplateVo"/>
|
||||
where status = '0'
|
||||
<if test="templateScene != null and templateScene != ''"> and template_scene = #{templateScene}</if>
|
||||
order by sort_num asc, create_time asc
|
||||
</select>
|
||||
|
||||
@@ -46,6 +49,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="templateName != null and templateName != ''">template_name,</if>
|
||||
<if test="templateGroup != null and templateGroup != ''">template_group,</if>
|
||||
<if test="templateScene != null and templateScene != ''">template_scene,</if>
|
||||
<if test="templateBody != null">template_body,</if>
|
||||
<if test="sortNum != null">sort_num,</if>
|
||||
<if test="status != null and status != ''">status,</if>
|
||||
@@ -55,6 +59,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="templateName != null and templateName != ''">#{templateName},</if>
|
||||
<if test="templateGroup != null and templateGroup != ''">#{templateGroup},</if>
|
||||
<if test="templateScene != null and templateScene != ''">#{templateScene},</if>
|
||||
<if test="templateBody != null">#{templateBody},</if>
|
||||
<if test="sortNum != null">#{sortNum},</if>
|
||||
<if test="status != null and status != ''">#{status},</if>
|
||||
@@ -68,6 +73,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<trim prefix="SET" suffixOverrides=",">
|
||||
<if test="templateName != null and templateName != ''">template_name = #{templateName},</if>
|
||||
<if test="templateGroup != null and templateGroup != ''">template_group = #{templateGroup},</if>
|
||||
<if test="templateScene != null and templateScene != ''">template_scene = #{templateScene},</if>
|
||||
<if test="templateBody != null">template_body = #{templateBody},</if>
|
||||
<if test="sortNum != null">sort_num = #{sortNum},</if>
|
||||
<if test="status != null and status != ''">status = #{status},</if>
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper
|
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.office.mapper.TtPublishTaskMapper">
|
||||
|
||||
<resultMap type="TtPublishTask" id="PublishTaskResult">
|
||||
<id property="publishId" column="publish_id" />
|
||||
<result property="taskId" column="task_id" />
|
||||
<result property="codeId" column="code_id" />
|
||||
<result property="codeName" column="code_name" />
|
||||
<result property="platform" column="platform" />
|
||||
<result property="title" column="title" />
|
||||
<result property="content" column="content" />
|
||||
<result property="tags" column="tags" />
|
||||
<result property="videoUrl" column="video_url" />
|
||||
<result property="imageUrls" column="image_urls" />
|
||||
<result property="coverUrl" column="cover_url" />
|
||||
<result property="status" column="status" />
|
||||
<result property="statusMessage" column="status_message" />
|
||||
<result property="lastError" column="last_error" />
|
||||
<result property="bridgeToken" column="bridge_token" />
|
||||
<result property="bridgeExpiresAt" column="bridge_expires_at" />
|
||||
<result property="startedAt" column="started_at" />
|
||||
<result property="readyAt" column="ready_at" />
|
||||
<result property="finishedAt" column="finished_at" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<resultMap type="PublishProjectRow" id="PublishProjectResult">
|
||||
<id property="codeId" column="code_id" />
|
||||
<result property="codeName" column="code_name" />
|
||||
<result property="xiaohongshuTaskId" column="xiaohongshu_task_id" />
|
||||
<result property="xiaohongshuStatus" column="xiaohongshu_status" />
|
||||
<result property="douyinTaskId" column="douyin_task_id" />
|
||||
<result property="douyinStatus" column="douyin_status" />
|
||||
<result property="wechatChannelsTaskId" column="wechat_channels_task_id" />
|
||||
<result property="wechatChannelsStatus" column="wechat_channels_status" />
|
||||
<result property="bilibiliTaskId" column="bilibili_task_id" />
|
||||
<result property="bilibiliStatus" column="bilibili_status" />
|
||||
<result property="xianyuTaskId" column="xianyu_task_id" />
|
||||
<result property="xianyuStatus" column="xianyu_status" />
|
||||
<result property="lastPublishTime" column="last_publish_time" />
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectPublishTaskVo">
|
||||
select t.publish_id, t.task_id, t.code_id, c.code_name, t.platform,
|
||||
t.title, t.content, t.tags, t.video_url, t.image_urls, t.cover_url,
|
||||
t.status, t.status_message, t.last_error, t.bridge_token,
|
||||
t.bridge_expires_at, t.started_at, t.ready_at, t.finished_at,
|
||||
t.create_by, t.create_time, t.update_by, t.update_time
|
||||
from tt_publish_task t
|
||||
inner join tt_code c on c.code_id = t.code_id
|
||||
</sql>
|
||||
|
||||
<select id="selectPublishProjectList" parameterType="PublishProjectRow" resultMap="PublishProjectResult">
|
||||
select c.code_id,
|
||||
c.code_name,
|
||||
x.task_id as xiaohongshu_task_id,
|
||||
x.status as xiaohongshu_status,
|
||||
d.task_id as douyin_task_id,
|
||||
d.status as douyin_status,
|
||||
w.task_id as wechat_channels_task_id,
|
||||
w.status as wechat_channels_status,
|
||||
b.task_id as bilibili_task_id,
|
||||
b.status as bilibili_status,
|
||||
y.task_id as xianyu_task_id,
|
||||
y.status as xianyu_status,
|
||||
greatest(
|
||||
ifnull(x.update_time, '1970-01-01 00:00:00'),
|
||||
ifnull(d.update_time, '1970-01-01 00:00:00'),
|
||||
ifnull(w.update_time, '1970-01-01 00:00:00'),
|
||||
ifnull(b.update_time, '1970-01-01 00:00:00'),
|
||||
ifnull(y.update_time, '1970-01-01 00:00:00')
|
||||
) as last_publish_time
|
||||
from tt_code c
|
||||
left join tt_publish_task x on x.publish_id = (
|
||||
select max(tx.publish_id) from tt_publish_task tx
|
||||
where tx.code_id = c.code_id and tx.platform = 'xiaohongshu'
|
||||
)
|
||||
left join tt_publish_task d on d.publish_id = (
|
||||
select max(td.publish_id) from tt_publish_task td
|
||||
where td.code_id = c.code_id and td.platform = 'douyin'
|
||||
)
|
||||
left join tt_publish_task w on w.publish_id = (
|
||||
select max(tw.publish_id) from tt_publish_task tw
|
||||
where tw.code_id = c.code_id and tw.platform = 'wechat_channels'
|
||||
)
|
||||
left join tt_publish_task b on b.publish_id = (
|
||||
select max(tb.publish_id) from tt_publish_task tb
|
||||
where tb.code_id = c.code_id and tb.platform = 'bilibili'
|
||||
)
|
||||
left join tt_publish_task y on y.publish_id = (
|
||||
select max(ty.publish_id) from tt_publish_task ty
|
||||
where ty.code_id = c.code_id and ty.platform = 'xianyu'
|
||||
)
|
||||
<where>
|
||||
<if test="codeName != null and codeName != ''">
|
||||
and c.code_name like concat('%', #{codeName}, '%')
|
||||
</if>
|
||||
</where>
|
||||
order by c.code_id desc
|
||||
</select>
|
||||
|
||||
<select id="selectPublishTaskByTaskId" parameterType="String" resultMap="PublishTaskResult">
|
||||
<include refid="selectPublishTaskVo" />
|
||||
where t.task_id = #{taskId}
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<select id="selectPublishTaskByBridge" resultMap="PublishTaskResult">
|
||||
<include refid="selectPublishTaskVo" />
|
||||
where t.task_id = #{taskId}
|
||||
and t.bridge_token = #{bridgeToken}
|
||||
and t.bridge_expires_at > now()
|
||||
limit 1
|
||||
</select>
|
||||
|
||||
<insert id="insertPublishTask" parameterType="TtPublishTask" useGeneratedKeys="true" keyProperty="publishId">
|
||||
insert into tt_publish_task
|
||||
(task_id, code_id, platform, title, content, tags, video_url,
|
||||
image_urls, cover_url, status, status_message, bridge_token,
|
||||
bridge_expires_at, create_by, create_time, update_by, update_time)
|
||||
values
|
||||
(#{taskId}, #{codeId}, #{platform}, #{title}, #{content}, #{tags},
|
||||
#{videoUrl}, #{imageUrls}, #{coverUrl}, #{status}, #{statusMessage},
|
||||
#{bridgeToken}, #{bridgeExpiresAt}, #{createBy}, now(), #{updateBy}, now())
|
||||
</insert>
|
||||
|
||||
<update id="updatePublishTaskStatus" parameterType="TtPublishTask">
|
||||
update tt_publish_task
|
||||
<set>
|
||||
status = #{status},
|
||||
status_message = #{statusMessage},
|
||||
last_error = #{lastError},
|
||||
<if test="startedAt != null">started_at = #{startedAt},</if>
|
||||
<if test="readyAt != null">ready_at = #{readyAt},</if>
|
||||
<if test="finishedAt != null">finished_at = #{finishedAt},</if>
|
||||
update_by = #{updateBy},
|
||||
update_time = now()
|
||||
</set>
|
||||
where task_id = #{taskId}
|
||||
</update>
|
||||
</mapper>
|
||||
Reference in New Issue
Block a user