This commit is contained in:
2025-11-07 14:40:42 +08:00
parent b2b18972d7
commit 2409c8c819
12 changed files with 1086 additions and 5 deletions

View File

@@ -0,0 +1,226 @@
package com.ruoyi.jarvis.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
import java.util.Date;
import java.util.List;
/**
* 腾讯文档批量推送记录
*/
public class TencentDocBatchPushRecord extends BaseEntity {
private static final long serialVersionUID = 1L;
/** 主键ID */
private Long id;
/** 批次ID */
private String batchId;
/** 文件ID */
private String fileId;
/** 工作表ID */
private String sheetId;
/** 推送类型AUTO-自动推送MANUAL-手动推送 */
private String pushType;
/** 触发来源DELAYED_TIMER-延迟定时器USER-用户手动 */
private String triggerSource;
/** 推送开始时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
/** 推送结束时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
/** 推送耗时(毫秒) */
private Long durationMs;
/** 起始行号 */
private Integer startRow;
/** 结束行号 */
private Integer endRow;
/** 总行数 */
private Integer totalRows;
/** 成功数量 */
private Integer successCount;
/** 跳过数量 */
private Integer skipCount;
/** 错误数量 */
private Integer errorCount;
/** 状态RUNNING-执行中SUCCESS-成功PARTIAL-部分成功FAILED-失败 */
private String status;
/** 结果消息 */
private String resultMessage;
/** 错误信息 */
private String errorMessage;
/** 关联的操作日志列表(非数据库字段) */
private List<TencentDocOperationLog> operationLogs;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getBatchId() {
return batchId;
}
public void setBatchId(String batchId) {
this.batchId = batchId;
}
public String getFileId() {
return fileId;
}
public void setFileId(String fileId) {
this.fileId = fileId;
}
public String getSheetId() {
return sheetId;
}
public void setSheetId(String sheetId) {
this.sheetId = sheetId;
}
public String getPushType() {
return pushType;
}
public void setPushType(String pushType) {
this.pushType = pushType;
}
public String getTriggerSource() {
return triggerSource;
}
public void setTriggerSource(String triggerSource) {
this.triggerSource = triggerSource;
}
public Date getStartTime() {
return startTime;
}
public void setStartTime(Date startTime) {
this.startTime = startTime;
}
public Date getEndTime() {
return endTime;
}
public void setEndTime(Date endTime) {
this.endTime = endTime;
}
public Long getDurationMs() {
return durationMs;
}
public void setDurationMs(Long durationMs) {
this.durationMs = durationMs;
}
public Integer getStartRow() {
return startRow;
}
public void setStartRow(Integer startRow) {
this.startRow = startRow;
}
public Integer getEndRow() {
return endRow;
}
public void setEndRow(Integer endRow) {
this.endRow = endRow;
}
public Integer getTotalRows() {
return totalRows;
}
public void setTotalRows(Integer totalRows) {
this.totalRows = totalRows;
}
public Integer getSuccessCount() {
return successCount;
}
public void setSuccessCount(Integer successCount) {
this.successCount = successCount;
}
public Integer getSkipCount() {
return skipCount;
}
public void setSkipCount(Integer skipCount) {
this.skipCount = skipCount;
}
public Integer getErrorCount() {
return errorCount;
}
public void setErrorCount(Integer errorCount) {
this.errorCount = errorCount;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getResultMessage() {
return resultMessage;
}
public void setResultMessage(String resultMessage) {
this.resultMessage = resultMessage;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public List<TencentDocOperationLog> getOperationLogs() {
return operationLogs;
}
public void setOperationLogs(List<TencentDocOperationLog> operationLogs) {
this.operationLogs = operationLogs;
}
}

View File

@@ -17,6 +17,9 @@ public class TencentDocOperationLog extends BaseEntity {
/** 主键ID */
private Long id;
/** 批次ID关联批量推送记录 */
private String batchId;
/** 文档ID */
private String fileId;

View File

@@ -0,0 +1,45 @@
package com.ruoyi.jarvis.mapper;
import com.ruoyi.jarvis.domain.TencentDocBatchPushRecord;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 腾讯文档批量推送记录 Mapper
*/
public interface TencentDocBatchPushRecordMapper {
/**
* 插入批量推送记录
*/
int insertBatchPushRecord(TencentDocBatchPushRecord record);
/**
* 更新批量推送记录
*/
int updateBatchPushRecord(TencentDocBatchPushRecord record);
/**
* 根据批次ID查询
*/
TencentDocBatchPushRecord selectByBatchId(@Param("batchId") String batchId);
/**
* 查询批量推送记录列表
*/
List<TencentDocBatchPushRecord> selectBatchPushRecordList(TencentDocBatchPushRecord record);
/**
* 查询最近的推送记录
*/
List<TencentDocBatchPushRecord> selectRecentRecords(@Param("fileId") String fileId,
@Param("limit") int limit);
/**
* 查询最后一次成功的推送记录
*/
TencentDocBatchPushRecord selectLastSuccessRecord(@Param("fileId") String fileId,
@Param("sheetId") String sheetId);
}

View File

@@ -37,5 +37,13 @@ public interface TencentDocOperationLogMapper {
* @return 操作日志集合
*/
List<TencentDocOperationLog> selectRecentLogs(@Param("fileId") String fileId, @Param("limit") int limit);
/**
* 根据批次ID查询操作日志
*
* @param batchId 批次ID
* @return 操作日志集合
*/
List<TencentDocOperationLog> selectLogsByBatchId(@Param("batchId") String batchId);
}

View File

@@ -0,0 +1,45 @@
package com.ruoyi.jarvis.service;
import com.ruoyi.jarvis.domain.TencentDocBatchPushRecord;
import java.util.List;
import java.util.Map;
/**
* 腾讯文档批量推送记录服务
*/
public interface ITencentDocBatchPushService {
/**
* 创建批量推送记录
*/
String createBatchPushRecord(String fileId, String sheetId, String pushType,
String triggerSource, Integer startRow, Integer endRow);
/**
* 更新批量推送记录
*/
void updateBatchPushRecord(String batchId, String status, Integer successCount,
Integer skipCount, Integer errorCount, String resultMessage, String errorMessage);
/**
* 根据批次ID查询
*/
TencentDocBatchPushRecord getBatchPushRecord(String batchId);
/**
* 查询批量推送记录列表(带操作日志)
*/
List<TencentDocBatchPushRecord> getBatchPushRecordListWithLogs(String fileId, String sheetId, Integer limit);
/**
* 查询最后一次成功的推送记录
*/
TencentDocBatchPushRecord getLastSuccessRecord(String fileId, String sheetId);
/**
* 获取推送状态和倒计时信息
*/
Map<String, Object> getPushStatusAndCountdown();
}

View File

@@ -0,0 +1,159 @@
package com.ruoyi.jarvis.service.impl;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.jarvis.domain.TencentDocBatchPushRecord;
import com.ruoyi.jarvis.domain.TencentDocOperationLog;
import com.ruoyi.jarvis.mapper.TencentDocBatchPushRecordMapper;
import com.ruoyi.jarvis.mapper.TencentDocOperationLogMapper;
import com.ruoyi.jarvis.service.ITencentDocBatchPushService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* 腾讯文档批量推送记录服务实现
*/
@Service
public class TencentDocBatchPushServiceImpl implements ITencentDocBatchPushService {
@Resource
private TencentDocBatchPushRecordMapper batchPushRecordMapper;
@Resource
private TencentDocOperationLogMapper operationLogMapper;
@Resource
private RedisCache redisCache;
private static final String DELAYED_PUSH_TASK_KEY = "tendoc:delayed_push:task_scheduled";
private static final String DELAYED_PUSH_SCHEDULE_TIME_KEY = "tendoc:delayed_push:schedule_time";
@Override
public String createBatchPushRecord(String fileId, String sheetId, String pushType,
String triggerSource, Integer startRow, Integer endRow) {
String batchId = UUID.randomUUID().toString().replace("-", "");
TencentDocBatchPushRecord record = new TencentDocBatchPushRecord();
record.setBatchId(batchId);
record.setFileId(fileId);
record.setSheetId(sheetId);
record.setPushType(pushType);
record.setTriggerSource(triggerSource);
record.setStartTime(new Date());
record.setStartRow(startRow);
record.setEndRow(endRow);
record.setTotalRows(endRow - startRow + 1);
record.setSuccessCount(0);
record.setSkipCount(0);
record.setErrorCount(0);
record.setStatus("RUNNING");
batchPushRecordMapper.insertBatchPushRecord(record);
return batchId;
}
@Override
public void updateBatchPushRecord(String batchId, String status, Integer successCount,
Integer skipCount, Integer errorCount, String resultMessage, String errorMessage) {
TencentDocBatchPushRecord record = new TencentDocBatchPushRecord();
record.setBatchId(batchId);
record.setEndTime(new Date());
record.setStatus(status);
record.setSuccessCount(successCount);
record.setSkipCount(skipCount);
record.setErrorCount(errorCount);
record.setResultMessage(resultMessage);
record.setErrorMessage(errorMessage);
// 计算耗时
TencentDocBatchPushRecord existingRecord = batchPushRecordMapper.selectByBatchId(batchId);
if (existingRecord != null && existingRecord.getStartTime() != null) {
long durationMs = new Date().getTime() - existingRecord.getStartTime().getTime();
record.setDurationMs(durationMs);
}
batchPushRecordMapper.updateBatchPushRecord(record);
}
@Override
public TencentDocBatchPushRecord getBatchPushRecord(String batchId) {
TencentDocBatchPushRecord record = batchPushRecordMapper.selectByBatchId(batchId);
if (record != null) {
// 加载关联的操作日志
List<TencentDocOperationLog> logs = operationLogMapper.selectLogsByBatchId(batchId);
record.setOperationLogs(logs);
}
return record;
}
@Override
public List<TencentDocBatchPushRecord> getBatchPushRecordListWithLogs(String fileId, String sheetId, Integer limit) {
TencentDocBatchPushRecord query = new TencentDocBatchPushRecord();
query.setFileId(fileId);
query.setSheetId(sheetId);
List<TencentDocBatchPushRecord> records = limit != null && limit > 0
? batchPushRecordMapper.selectRecentRecords(fileId, limit)
: batchPushRecordMapper.selectBatchPushRecordList(query);
// 为每条记录加载操作日志
for (TencentDocBatchPushRecord record : records) {
List<TencentDocOperationLog> logs = operationLogMapper.selectLogsByBatchId(record.getBatchId());
record.setOperationLogs(logs);
}
return records;
}
@Override
public TencentDocBatchPushRecord getLastSuccessRecord(String fileId, String sheetId) {
TencentDocBatchPushRecord record = batchPushRecordMapper.selectLastSuccessRecord(fileId, sheetId);
if (record != null) {
List<TencentDocOperationLog> logs = operationLogMapper.selectLogsByBatchId(record.getBatchId());
record.setOperationLogs(logs);
}
return record;
}
@Override
public Map<String, Object> getPushStatusAndCountdown() {
Map<String, Object> result = new HashMap<>();
// 检查是否有定时任务
Boolean isScheduled = redisCache.getCacheObject(DELAYED_PUSH_TASK_KEY);
Long scheduleTime = redisCache.getCacheObject(DELAYED_PUSH_SCHEDULE_TIME_KEY);
result.put("isScheduled", isScheduled != null && isScheduled);
if (isScheduled != null && isScheduled && scheduleTime != null) {
long now = System.currentTimeMillis();
long remainingMs = scheduleTime - now;
if (remainingMs > 0) {
result.put("scheduledTime", new Date(scheduleTime));
result.put("remainingSeconds", remainingMs / 1000);
result.put("remainingMs", remainingMs);
// 格式化倒计时显示
long minutes = remainingMs / 60000;
long seconds = (remainingMs % 60000) / 1000;
result.put("countdownText", String.format("%d分%d秒", minutes, seconds));
} else {
result.put("remainingSeconds", 0);
result.put("remainingMs", 0);
result.put("countdownText", "即将执行");
}
} else {
result.put("scheduledTime", null);
result.put("remainingSeconds", 0);
result.put("remainingMs", 0);
result.put("countdownText", "无定时任务");
}
return result;
}
}

View File

@@ -1,12 +1,16 @@
package com.ruoyi.jarvis.service.impl;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.jarvis.config.TencentDocConfig;
import com.ruoyi.jarvis.service.ITencentDocBatchPushService;
import com.ruoyi.jarvis.service.ITencentDocDelayedPushService;
import com.ruoyi.jarvis.service.ITencentDocTokenService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@@ -33,6 +37,15 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS
@Autowired
private RedisCache redisCache;
@Autowired
private ITencentDocBatchPushService batchPushService;
@Autowired
private TencentDocConfig tencentDocConfig;
@Autowired
private ITencentDocTokenService tokenService;
/**
* 延迟时间(分钟),可通过配置文件修改
@@ -237,15 +250,36 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS
/**
* 执行批量同步
*
* 说明:这里通过HTTP调用本地接口,避免复杂的依赖注入
* 说明:创建批量推送记录,然后通过HTTP调用本地接口
*/
private void executeBatchSync() {
String batchId = null;
try {
log.info("开始执行批量同步...");
// 使用RestTemplate或HttpClient调用本地接口
// 这里简化处理直接发送HTTP请求到本地
java.net.URL url = new java.net.URL("http://localhost:30313/jarvis-api/jarvis/tendoc/fillLogisticsByOrderNo");
// 获取配置信息
String fileId = tencentDocConfig.getFileId();
String sheetId = tencentDocConfig.getSheetId();
Integer startRow = tencentDocConfig.getStartRow();
if (StringUtils.isEmpty(fileId) || StringUtils.isEmpty(sheetId)) {
log.error("腾讯文档配置不完整,无法执行批量同步");
return;
}
// 创建批量推送记录
batchId = batchPushService.createBatchPushRecord(
fileId,
sheetId,
"AUTO",
"DELAYED_TIMER",
startRow,
startRow + 199 // 暂定范围
);
log.info("✓ 创建批量推送记录批次ID: {}", batchId);
// 调用批量同步接口传递批次ID
java.net.URL url = new java.net.URL("http://localhost:30313/jarvis-api/jarvis/tendoc/fillLogisticsByOrderNo?batchId=" + batchId);
java.net.HttpURLConnection conn = (java.net.HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json");
@@ -273,10 +307,24 @@ public class TencentDocDelayedPushServiceImpl implements ITencentDocDelayedPushS
}
} else {
log.error("批量同步调用失败,响应码: {}", responseCode);
// 更新批量推送记录为失败状态
if (batchId != null) {
batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0,
null, "批量同步调用失败,响应码: " + responseCode);
}
}
} catch (Exception e) {
log.error("执行批量同步失败", e);
// 更新批量推送记录为失败状态
if (batchId != null) {
try {
batchPushService.updateBatchPushRecord(batchId, "FAILED", 0, 0, 0,
null, "执行批量同步失败: " + e.getMessage());
} catch (Exception ex) {
log.error("更新批量推送记录失败", ex);
}
}
throw new RuntimeException("执行批量同步失败", e);
}
}

View File

@@ -0,0 +1,100 @@
<?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.jarvis.mapper.TencentDocBatchPushRecordMapper">
<resultMap type="com.ruoyi.jarvis.domain.TencentDocBatchPushRecord" id="BatchPushRecordResult">
<id property="id" column="id" />
<result property="batchId" column="batch_id" />
<result property="fileId" column="file_id" />
<result property="sheetId" column="sheet_id" />
<result property="pushType" column="push_type" />
<result property="triggerSource" column="trigger_source" />
<result property="startTime" column="start_time" />
<result property="endTime" column="end_time" />
<result property="durationMs" column="duration_ms" />
<result property="startRow" column="start_row" />
<result property="endRow" column="end_row" />
<result property="totalRows" column="total_rows" />
<result property="successCount" column="success_count" />
<result property="skipCount" column="skip_count" />
<result property="errorCount" column="error_count" />
<result property="status" column="status" />
<result property="resultMessage" column="result_message" />
<result property="errorMessage" column="error_message" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
</resultMap>
<sql id="selectBatchPushRecordVo">
SELECT id, batch_id, file_id, sheet_id, push_type, trigger_source,
start_time, end_time, duration_ms, start_row, end_row, total_rows,
success_count, skip_count, error_count, status, result_message,
error_message, create_time, update_time
FROM tencent_doc_batch_push_record
</sql>
<insert id="insertBatchPushRecord" parameterType="com.ruoyi.jarvis.domain.TencentDocBatchPushRecord">
INSERT INTO tencent_doc_batch_push_record (
batch_id, file_id, sheet_id, push_type, trigger_source,
start_time, end_time, duration_ms, start_row, end_row, total_rows,
success_count, skip_count, error_count, status, result_message, error_message
) VALUES (
#{batchId}, #{fileId}, #{sheetId}, #{pushType}, #{triggerSource},
#{startTime}, #{endTime}, #{durationMs}, #{startRow}, #{endRow}, #{totalRows},
#{successCount}, #{skipCount}, #{errorCount}, #{status}, #{resultMessage}, #{errorMessage}
)
</insert>
<update id="updateBatchPushRecord" parameterType="com.ruoyi.jarvis.domain.TencentDocBatchPushRecord">
UPDATE tencent_doc_batch_push_record
<set>
<if test="endTime != null">end_time = #{endTime},</if>
<if test="durationMs != null">duration_ms = #{durationMs},</if>
<if test="successCount != null">success_count = #{successCount},</if>
<if test="skipCount != null">skip_count = #{skipCount},</if>
<if test="errorCount != null">error_count = #{errorCount},</if>
<if test="status != null and status != ''">status = #{status},</if>
<if test="resultMessage != null">result_message = #{resultMessage},</if>
<if test="errorMessage != null">error_message = #{errorMessage},</if>
</set>
WHERE batch_id = #{batchId}
</update>
<select id="selectByBatchId" parameterType="String" resultMap="BatchPushRecordResult">
<include refid="selectBatchPushRecordVo"/>
WHERE batch_id = #{batchId}
</select>
<select id="selectBatchPushRecordList" parameterType="com.ruoyi.jarvis.domain.TencentDocBatchPushRecord" resultMap="BatchPushRecordResult">
<include refid="selectBatchPushRecordVo"/>
<where>
<if test="fileId != null and fileId != ''">AND file_id = #{fileId}</if>
<if test="sheetId != null and sheetId != ''">AND sheet_id = #{sheetId}</if>
<if test="status != null and status != ''">AND status = #{status}</if>
<if test="pushType != null and pushType != ''">AND push_type = #{pushType}</if>
</where>
ORDER BY create_time DESC
</select>
<select id="selectRecentRecords" resultMap="BatchPushRecordResult">
<include refid="selectBatchPushRecordVo"/>
<where>
<if test="fileId != null and fileId != ''">AND file_id = #{fileId}</if>
</where>
ORDER BY create_time DESC
LIMIT #{limit}
</select>
<select id="selectLastSuccessRecord" resultMap="BatchPushRecordResult">
<include refid="selectBatchPushRecordVo"/>
WHERE file_id = #{fileId}
AND sheet_id = #{sheetId}
AND status IN ('SUCCESS', 'PARTIAL')
ORDER BY end_time DESC
LIMIT 1
</select>
</mapper>

View File

@@ -6,6 +6,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<resultMap type="com.ruoyi.jarvis.domain.TencentDocOperationLog" id="TencentDocOperationLogResult">
<id property="id" column="id" />
<result property="batchId" column="batch_id" />
<result property="fileId" column="file_id" />
<result property="sheetId" column="sheet_id" />
<result property="operationType" column="operation_type" />
@@ -22,6 +23,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<insert id="insertLog" parameterType="com.ruoyi.jarvis.domain.TencentDocOperationLog">
insert into tencent_doc_operation_log
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="batchId != null">batch_id,</if>
<if test="fileId != null">file_id,</if>
<if test="sheetId != null">sheet_id,</if>
<if test="operationType != null">operation_type,</if>
@@ -35,6 +37,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
create_time
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="batchId != null">#{batchId},</if>
<if test="fileId != null">#{fileId},</if>
<if test="sheetId != null">#{sheetId},</if>
<if test="operationType != null">#{operationType},</if>
@@ -50,7 +53,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</insert>
<sql id="selectLogVo">
select id, file_id, sheet_id, operation_type, order_no, target_row,
select id, batch_id, file_id, sheet_id, operation_type, order_no, target_row,
logistics_link, operation_status, error_message, operator,
create_time, remark
from tencent_doc_operation_log
@@ -89,5 +92,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
limit #{limit}
</select>
<select id="selectLogsByBatchId" resultMap="TencentDocOperationLogResult">
<include refid="selectLogVo"/>
WHERE batch_id = #{batchId}
ORDER BY create_time ASC
</select>
</mapper>