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,299 @@
# 腾讯文档倒计时和批量推送记录功能说明
## 功能概述
本次更新实现了腾讯文档自动推送的倒计时监控和批量推送记录管理功能,主要包括:
1. **批量推送记录**:记录每次批量推送的详细信息,包括成功数、失败数、耗时等
2. **操作日志关联**每条操作日志都关联到对应的批次ID方便追踪
3. **倒计时监控**:实时显示自动推送倒计时,支持手动触发和取消
4. **推送历史查看**:可查看历史推送记录,展开查看每条记录的详细操作日志
## 数据库变更
### 1. 新建批量推送记录表
```sql
-- 执行SQL文件
source sql/tencent_doc_batch_push_record.sql;
```
主要字段:
- `batch_id`批次IDUUID
- `file_id``sheet_id`文档和工作表ID
- `push_type`推送类型AUTO-自动推送MANUAL-手动推送)
- `trigger_source`触发来源DELAYED_TIMER-延迟定时器USER-用户手动)
- `start_time``end_time`:推送开始和结束时间
- `duration_ms`:推送耗时(毫秒)
- `start_row``end_row`:推送的行范围
- `success_count``skip_count``error_count`:成功、跳过、错误数量
- `status`状态RUNNING-执行中SUCCESS-成功PARTIAL-部分成功FAILED-失败)
### 2. 修改操作日志表
`tencent_doc_operation_log` 表添加 `batch_id` 字段,用于关联批量推送记录。
```sql
ALTER TABLE `tencent_doc_operation_log`
ADD COLUMN `batch_id` varchar(64) DEFAULT NULL COMMENT '批次ID关联批量推送记录' AFTER `id`,
ADD KEY `idx_batch_id` (`batch_id`);
```
## 后端更新
### 1. 新增实体类和Mapper
- **TencentDocBatchPushRecord.java**:批量推送记录实体
- **TencentDocBatchPushRecordMapper.java**批量推送记录Mapper接口
- **TencentDocBatchPushRecordMapper.xml**MyBatis映射文件
### 2. 新增Service层
- **ITencentDocBatchPushService.java**:批量推送服务接口
- **TencentDocBatchPushServiceImpl.java**:批量推送服务实现
主要方法:
- `createBatchPushRecord`:创建批量推送记录
- `updateBatchPushRecord`:更新批量推送记录
- `getBatchPushRecord`:查询单条记录(含操作日志)
- `getBatchPushRecordListWithLogs`:查询记录列表(含操作日志)
- `getLastSuccessRecord`:查询最后一次成功的推送记录
- `getPushStatusAndCountdown`:获取推送状态和倒计时信息
### 3. 修改延迟推送服务
- **TencentDocDelayedPushServiceImpl.java**
- 在执行批量推送前创建批量推送记录
- 调用API时传递批次ID
- 推送失败时更新记录状态
### 4. 新增Controller API
**TencentDocController.java** 新增接口:
| 接口 | 方法 | 说明 |
|------|------|------|
| `/jarvis/tendoc/batchPushRecords` | GET | 获取批量推送记录列表 |
| `/jarvis/tendoc/batchPushRecord/{batchId}` | GET | 获取批量推送记录详情 |
| `/jarvis/tendoc/pushStatus` | GET | 获取推送状态和倒计时信息 |
| `/jarvis/tendoc/triggerPushNow` | POST | 手动触发立即推送 |
| `/jarvis/tendoc/cancelPendingPush` | POST | 取消待推送任务 |
## 前端更新
### 1. API接口封装
**tendoc.js** 新增方法:
```javascript
// 获取批量推送记录列表
getBatchPushRecords(params)
// 获取批量推送记录详情
getBatchPushRecordDetail(batchId)
// 获取推送状态和倒计时信息
getPushStatus()
// 手动触发立即推送
triggerPushNow()
// 取消待推送任务
cancelPendingPush()
```
### 2. 新增推送监控组件
**TencentDocPushMonitor.vue**
功能特性:
- ✅ 实时倒计时显示(分:秒格式)
- ✅ 推送状态监控(等待推送中/无待推送任务)
- ✅ 手动触发立即推送
- ✅ 取消待推送任务
- ✅ 查看推送历史记录
- ✅ 时间轴展示推送记录
- ✅ 展开查看每条记录的操作日志
- ✅ 自动刷新每30秒
- ✅ 倒计时自动更新(每秒)
### 3. 集成到订单列表
**orderList.vue** 更新:
- 新增"推送监控"按钮
- 导入并注册 `TencentDocPushMonitor` 组件
- 添加 `showPushMonitor` 状态控制
## 使用指南
### 1. 打开推送监控
在订单列表页面,点击"推送监控"按钮即可打开监控对话框。
### 2. 查看倒计时
对话框顶部显示当前倒计时状态:
- **有待推送任务**:显示剩余时间(分:秒)
- **无待推送任务**:显示"00:00"
### 3. 手动操作
- **立即推送**:点击后立即执行批量推送,无需等待倒计时结束
- **取消推送**:取消当前待推送任务,倒计时清零
- **刷新状态**:手动刷新当前状态
### 4. 查看推送历史
对话框下方以时间轴形式展示推送记录:
- 绿色:推送成功
- 黄色:部分成功
- 红色:推送失败
- 蓝色:正在执行
点击记录可展开查看详细信息:
- 结果消息
- 错误信息(如果有)
- 操作日志列表(每条订单的详细操作记录)
### 5. 查看操作日志
展开推送记录后,可以看到该批次的所有操作日志,包括:
- 订单号
- 操作类型
- 目标行
- 物流链接
- 操作状态
- 错误信息(如果有)
## 数据流程
### 1. 录单触发
```
用户录单H-TF订单
触发延迟推送服务
设置10分钟倒计时
10分钟内有新录单 → 重置倒计时
10分钟到期 → 执行批量推送
```
### 2. 批量推送流程
```
创建批量推送记录状态RUNNING
调用批量同步API传递batchId
每条订单操作都关联batchId
推送完成后更新批量推送记录
├─ 状态SUCCESS/PARTIAL/FAILED
├─ 成功/跳过/错误数量
├─ 结果消息
└─ 错误信息(如果有)
```
### 3. 前端监控流程
```
打开推送监控对话框
加载推送状态(倒计时)
加载推送记录列表
每秒更新倒计时显示
每30秒自动刷新状态
展开记录查看操作日志
```
## 技术要点
### 1. 倒计时同步
- 后端Redis存储 `scheduledTime`(推送执行时间戳)
- 前端每秒计算 `remainingSeconds = (scheduledTime - now) / 1000`
- 服务端和客户端同步倒计时,避免误差
### 2. 批次ID生成
使用UUID生成唯一批次ID
```java
String batchId = UUID.randomUUID().toString().replace("-", "");
```
### 3. 日志关联
操作日志表添加 `batch_id` 字段,通过此字段关联:
- 一次批量推送 → 一条批量推送记录
- 一次批量推送 → 多条操作日志(每条订单一条)
### 4. 状态管理
批量推送记录的状态转换:
```
RUNNING → SUCCESS (全部成功)
RUNNING → PARTIAL (部分成功)
RUNNING → FAILED (全部失败)
```
### 5. 自动刷新
组件实现两个定时器:
- **countdownTimer**:每秒更新倒计时显示
- **refreshTimer**每30秒刷新状态和记录列表
## 注意事项
1. **数据库迁移**部署前必须执行SQL脚本创建新表和字段
2. **Redis配置**确保Redis正常运行用于存储倒计时信息
3. **时间同步**:确保服务器时间准确,避免倒计时误差
4. **性能考虑**:批量推送记录会持续增长,建议定期清理历史记录
5. **并发控制**:延迟推送服务使用分布式锁,防止并发推送
## 常见问题
### Q1倒计时不准确
**A**:检查服务器时间是否准确,确保服务器时区设置正确。
### Q2推送记录看不到操作日志
**A**:确保 `batch_id` 字段已正确添加到操作日志表,并且在插入日志时传递了 `batchId`
### Q3手动触发推送没反应
**A**
1. 检查后端日志是否有错误
2. 确认腾讯文档配置是否完整
3. 检查网络连接和API权限
### Q4倒计时显示00:00但标记为"等待推送中"
**A**:可能是倒计时刚结束,正在执行推送。刷新状态即可更新。
## 后续优化建议
1. **推送记录分页**:当记录很多时,实现分页加载
2. **日志导出**支持导出推送记录和操作日志为Excel
3. **推送统计**:添加推送成功率、平均耗时等统计图表
4. **告警通知**:推送失败时发送邮件或钉钉通知
5. **历史清理**:实现定时任务自动清理过期记录
6. **性能监控**:记录每次推送的性能指标,优化慢查询
## 版本信息
- **版本**v1.0.0
- **更新日期**2025-11-07
- **开发者**AI Assistant
- **适用系统**若依管理系统RuoYi-Vue
---
如有问题,请查看日志文件或联系技术支持。

View File

@@ -48,6 +48,12 @@ public class TencentDocController extends BaseController {
@Autowired
private com.ruoyi.jarvis.mapper.TencentDocOperationLogMapper operationLogMapper;
@Autowired
private com.ruoyi.jarvis.service.ITencentDocBatchPushService batchPushService;
@Autowired
private com.ruoyi.jarvis.service.ITencentDocDelayedPushService delayedPushService;
/** Redis key前缀用于存储上次处理的最大行数 */
private static final String LAST_PROCESSED_ROW_KEY_PREFIX = "tendoc:last_row:";
@@ -1570,5 +1576,104 @@ public class TencentDocController extends BaseController {
}
return sb.toString();
}
/**
* 获取批量推送记录列表(带操作日志)
*/
@GetMapping("/batchPushRecords")
public AjaxResult getBatchPushRecords(@RequestParam(required = false) String fileId,
@RequestParam(required = false) String sheetId,
@RequestParam(defaultValue = "10") Integer limit) {
try {
// 如果没有指定文件ID使用配置的默认值
if (fileId == null || fileId.trim().isEmpty()) {
fileId = tencentDocConfig.getFileId();
}
if (sheetId == null || sheetId.trim().isEmpty()) {
sheetId = tencentDocConfig.getSheetId();
}
List<com.ruoyi.jarvis.domain.TencentDocBatchPushRecord> records =
batchPushService.getBatchPushRecordListWithLogs(fileId, sheetId, limit);
return AjaxResult.success(records);
} catch (Exception e) {
log.error("查询批量推送记录失败", e);
return AjaxResult.error("查询批量推送记录失败: " + e.getMessage());
}
}
/**
* 获取批量推送记录详情(包含操作日志)
*/
@GetMapping("/batchPushRecord/{batchId}")
public AjaxResult getBatchPushRecordDetail(@PathVariable String batchId) {
try {
com.ruoyi.jarvis.domain.TencentDocBatchPushRecord record =
batchPushService.getBatchPushRecord(batchId);
if (record == null) {
return AjaxResult.error("未找到批次记录");
}
return AjaxResult.success(record);
} catch (Exception e) {
log.error("查询批量推送记录详情失败", e);
return AjaxResult.error("查询批量推送记录详情失败: " + e.getMessage());
}
}
/**
* 获取推送状态和倒计时信息
*/
@GetMapping("/pushStatus")
public AjaxResult getPushStatus() {
try {
Map<String, Object> status = batchPushService.getPushStatusAndCountdown();
// 添加最后一次成功的推送记录信息
String fileId = tencentDocConfig.getFileId();
String sheetId = tencentDocConfig.getSheetId();
if (fileId != null && sheetId != null) {
com.ruoyi.jarvis.domain.TencentDocBatchPushRecord lastSuccess =
batchPushService.getLastSuccessRecord(fileId, sheetId);
status.put("lastSuccessRecord", lastSuccess);
}
return AjaxResult.success(status);
} catch (Exception e) {
log.error("查询推送状态失败", e);
return AjaxResult.error("查询推送状态失败: " + e.getMessage());
}
}
/**
* 手动触发立即推送
*/
@PostMapping("/triggerPushNow")
public AjaxResult triggerPushNow() {
try {
delayedPushService.executePushNow();
return AjaxResult.success("推送已触发");
} catch (Exception e) {
log.error("触发推送失败", e);
return AjaxResult.error("触发推送失败: " + e.getMessage());
}
}
/**
* 取消待推送任务
*/
@PostMapping("/cancelPendingPush")
public AjaxResult cancelPendingPush() {
try {
delayedPushService.cancelPendingPush();
return AjaxResult.success("待推送任务已取消");
} catch (Exception e) {
log.error("取消待推送任务失败", e);
return AjaxResult.error("取消待推送任务失败: " + e.getMessage());
}
}
}

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>

View File

@@ -0,0 +1,34 @@
-- 腾讯文档批量推送记录表
CREATE TABLE IF NOT EXISTS `tencent_doc_batch_push_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`batch_id` varchar(64) NOT NULL COMMENT '批次IDUUID',
`file_id` varchar(100) DEFAULT NULL COMMENT '文件ID',
`sheet_id` varchar(100) DEFAULT NULL COMMENT '工作表ID',
`push_type` varchar(20) DEFAULT 'AUTO' COMMENT '推送类型AUTO-自动推送MANUAL-手动推送',
`trigger_source` varchar(50) DEFAULT NULL COMMENT '触发来源DELAYED_TIMER-延迟定时器USER-用户手动',
`start_time` datetime DEFAULT NULL COMMENT '推送开始时间',
`end_time` datetime DEFAULT NULL COMMENT '推送结束时间',
`duration_ms` bigint(20) DEFAULT NULL COMMENT '推送耗时(毫秒)',
`start_row` int(11) DEFAULT NULL COMMENT '起始行号',
`end_row` int(11) DEFAULT NULL COMMENT '结束行号',
`total_rows` int(11) DEFAULT 0 COMMENT '总行数',
`success_count` int(11) DEFAULT 0 COMMENT '成功数量',
`skip_count` int(11) DEFAULT 0 COMMENT '跳过数量',
`error_count` int(11) DEFAULT 0 COMMENT '错误数量',
`status` varchar(20) DEFAULT 'RUNNING' COMMENT '状态RUNNING-执行中SUCCESS-成功PARTIAL-部分成功FAILED-失败',
`result_message` text COMMENT '结果消息',
`error_message` text COMMENT '错误信息',
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_batch_id` (`batch_id`),
KEY `idx_file_sheet` (`file_id`, `sheet_id`),
KEY `idx_create_time` (`create_time`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='腾讯文档批量推送记录表';
-- 修改操作日志表添加批次ID字段
ALTER TABLE `tencent_doc_operation_log`
ADD COLUMN `batch_id` varchar(64) DEFAULT NULL COMMENT '批次ID关联批量推送记录' AFTER `id`,
ADD KEY `idx_batch_id` (`batch_id`);