1
This commit is contained in:
20
doc/腾讯文档操作日志表.sql
Normal file
20
doc/腾讯文档操作日志表.sql
Normal file
@@ -0,0 +1,20 @@
|
||||
-- 腾讯文档操作日志表
|
||||
CREATE TABLE `tencent_doc_operation_log` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`file_id` varchar(100) DEFAULT NULL COMMENT '文档ID',
|
||||
`sheet_id` varchar(100) DEFAULT NULL COMMENT '工作表ID',
|
||||
`operation_type` varchar(50) DEFAULT NULL COMMENT '操作类型:WRITE_SINGLE(单个写入)、BATCH_SYNC(批量同步)',
|
||||
`order_no` varchar(100) DEFAULT NULL COMMENT '订单单号',
|
||||
`target_row` int(11) DEFAULT NULL COMMENT '目标行号',
|
||||
`logistics_link` varchar(500) DEFAULT NULL COMMENT '写入的物流链接',
|
||||
`operation_status` varchar(20) DEFAULT NULL COMMENT '操作状态:SUCCESS(成功)、FAILED(失败)、SKIPPED(跳过)',
|
||||
`error_message` text COMMENT '错误信息',
|
||||
`operator` varchar(100) DEFAULT NULL COMMENT '操作人',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_order_no` (`order_no`),
|
||||
KEY `idx_create_time` (`create_time`),
|
||||
KEY `idx_file_sheet` (`file_id`, `sheet_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='腾讯文档操作日志表';
|
||||
|
||||
130
doc/腾讯文档物流链接填充-严格模式说明.md
Normal file
130
doc/腾讯文档物流链接填充-严格模式说明.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# 腾讯文档物流链接填充 - 严格模式
|
||||
|
||||
## 🔒 核心安全机制
|
||||
|
||||
### 1. **分布式锁**
|
||||
- 使用Redis分布式锁,防止并发写入
|
||||
- 锁的粒度:`文档ID:工作表ID:订单单号`
|
||||
- 锁超时时间:30秒
|
||||
- 确保同一订单同一时刻只能有一个请求处理
|
||||
|
||||
### 2. **操作日志记录**
|
||||
- 所有操作都会记录到数据库表 `tencent_doc_operation_log`
|
||||
- 记录内容包括:
|
||||
- 文档ID、工作表ID
|
||||
- 操作类型(WRITE_SINGLE / BATCH_SYNC)
|
||||
- 订单单号、目标行号
|
||||
- 物流链接
|
||||
- 操作状态(SUCCESS / FAILED / SKIPPED)
|
||||
- 错误信息
|
||||
- 操作人、操作时间
|
||||
|
||||
### 3. **写入前验证**
|
||||
在写入之前,会进行以下验证:
|
||||
1. **再次读取目标行** - 防止行数据在查找和写入之间发生变化
|
||||
2. **验证单号匹配** - 确保单号仍然在预期的行
|
||||
3. **验证物流列为空** - 如果已有物流链接,则拒绝写入,防止覆盖
|
||||
|
||||
### 4. **录单不再自动触发**
|
||||
- **旧行为**:录单时如果分销标识是 `H-TF`,自动写入腾讯文档
|
||||
- **新行为**:录单时不再自动写入,必须通过订单列表手动触发
|
||||
- **原因**:防止并发写入和数据覆盖,需要人工确认
|
||||
|
||||
## 📋 操作流程
|
||||
|
||||
### 单个订单填充物流链接
|
||||
1. 在订单列表找到目标订单
|
||||
2. 点击"推送物流"按钮(或类似按钮)
|
||||
3. 系统会:
|
||||
- 获取分布式锁
|
||||
- 读取表头识别列位置
|
||||
- 查找订单单号所在行
|
||||
- 验证单号和物流列
|
||||
- 写入物流链接
|
||||
- 记录操作日志
|
||||
- 释放锁
|
||||
|
||||
### 批量同步物流链接
|
||||
1. 点击"批量同步"按钮
|
||||
2. 系统会自动:
|
||||
- 读取表格数据
|
||||
- 根据单号查询订单系统
|
||||
- 逐个写入(每个都有锁保护)
|
||||
- 记录所有操作日志
|
||||
|
||||
## 🛡️ 安全保障
|
||||
|
||||
### 防止数据覆盖
|
||||
- ✅ 分布式锁防止并发写入
|
||||
- ✅ 写入前验证单号匹配
|
||||
- ✅ 写入前检查物流列是否为空
|
||||
- ✅ 如果物流列已有值,拒绝写入并提示
|
||||
|
||||
### 操作可追溯
|
||||
- ✅ 所有操作都记录到数据库
|
||||
- ✅ 记录操作人、操作时间
|
||||
- ✅ 记录成功/失败/跳过状态
|
||||
- ✅ 记录错误原因
|
||||
|
||||
## 📊 数据库表结构
|
||||
|
||||
```sql
|
||||
CREATE TABLE `tencent_doc_operation_log` (
|
||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||
`file_id` varchar(100) DEFAULT NULL COMMENT '文档ID',
|
||||
`sheet_id` varchar(100) DEFAULT NULL COMMENT '工作表ID',
|
||||
`operation_type` varchar(50) DEFAULT NULL COMMENT '操作类型',
|
||||
`order_no` varchar(100) DEFAULT NULL COMMENT '订单单号',
|
||||
`target_row` int(11) DEFAULT NULL COMMENT '目标行号',
|
||||
`logistics_link` varchar(500) DEFAULT NULL COMMENT '写入的物流链接',
|
||||
`operation_status` varchar(20) DEFAULT NULL COMMENT '操作状态',
|
||||
`error_message` text COMMENT '错误信息',
|
||||
`operator` varchar(100) DEFAULT NULL COMMENT '操作人',
|
||||
`create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_order_no` (`order_no`),
|
||||
KEY `idx_create_time` (`create_time`),
|
||||
KEY `idx_file_sheet` (`file_id`, `sheet_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='腾讯文档操作日志表';
|
||||
```
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
1. **必须先执行SQL** - 请先执行 `doc/腾讯文档操作日志表.sql` 创建日志表
|
||||
2. **Redis必须可用** - 分布式锁依赖Redis
|
||||
3. **手动触发** - 录单后需要手动点击按钮推送到腾讯文档
|
||||
4. **物流列非空则跳过** - 如果物流列已有值,会拒绝写入并提示
|
||||
|
||||
## 🔍 日志查询示例
|
||||
|
||||
### 查询某个订单的操作历史
|
||||
```sql
|
||||
SELECT * FROM tencent_doc_operation_log
|
||||
WHERE order_no = 'JY2025110329041'
|
||||
ORDER BY create_time DESC;
|
||||
```
|
||||
|
||||
### 查询失败的操作
|
||||
```sql
|
||||
SELECT * FROM tencent_doc_operation_log
|
||||
WHERE operation_status = 'FAILED'
|
||||
ORDER BY create_time DESC
|
||||
LIMIT 100;
|
||||
```
|
||||
|
||||
### 查询被跳过的操作(物流已存在)
|
||||
```sql
|
||||
SELECT * FROM tencent_doc_operation_log
|
||||
WHERE operation_status = 'SKIPPED'
|
||||
AND error_message LIKE '%物流链接列已有值%'
|
||||
ORDER BY create_time DESC;
|
||||
```
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如遇到问题,请检查:
|
||||
1. 操作日志表 `tencent_doc_operation_log`
|
||||
2. 应用日志中的 `TencentDocController` 相关日志
|
||||
3. Redis是否正常运行
|
||||
|
||||
@@ -45,9 +45,15 @@ public class TencentDocController extends BaseController {
|
||||
@Autowired
|
||||
private com.ruoyi.jarvis.config.TencentDocConfig tencentDocConfig;
|
||||
|
||||
@Autowired
|
||||
private com.ruoyi.jarvis.mapper.TencentDocOperationLogMapper operationLogMapper;
|
||||
|
||||
/** Redis key前缀,用于存储上次处理的最大行数 */
|
||||
private static final String LAST_PROCESSED_ROW_KEY_PREFIX = "tendoc:last_row:";
|
||||
|
||||
/** Redis key前缀,用于分布式锁 */
|
||||
private static final String TENCENT_DOC_LOCK_KEY = "tendoc:write:lock:";
|
||||
|
||||
/**
|
||||
* 测试回调接口是否可访问(用于调试)
|
||||
*/
|
||||
@@ -452,9 +458,14 @@ public class TencentDocController extends BaseController {
|
||||
*/
|
||||
@PostMapping("/fillSingleLogistics")
|
||||
public AjaxResult fillSingleLogistics(@RequestBody Map<String, Object> params) {
|
||||
String thirdPartyOrderNo = null;
|
||||
String fileId = null;
|
||||
String sheetId = null;
|
||||
String lockKey = null;
|
||||
|
||||
try {
|
||||
// 1. 获取参数
|
||||
String thirdPartyOrderNo = (String) params.get("thirdPartyOrderNo");
|
||||
thirdPartyOrderNo = (String) params.get("thirdPartyOrderNo");
|
||||
String logisticsLink = (String) params.get("logisticsLink");
|
||||
|
||||
if (thirdPartyOrderNo == null || thirdPartyOrderNo.isEmpty()) {
|
||||
@@ -469,13 +480,15 @@ public class TencentDocController extends BaseController {
|
||||
try {
|
||||
accessToken = tencentDocTokenService.getValidAccessToken();
|
||||
} catch (Exception e) {
|
||||
logOperation(null, null, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "访问令牌无效: " + e.getMessage());
|
||||
return AjaxResult.error("访问令牌无效,请先完成授权");
|
||||
}
|
||||
|
||||
// 3. 从配置中读取文档信息
|
||||
final String CONFIG_KEY_PREFIX = "tencent:doc:auto:config:";
|
||||
String fileId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "fileId");
|
||||
String sheetId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "sheetId");
|
||||
fileId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "fileId");
|
||||
sheetId = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "sheetId");
|
||||
Integer headerRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "headerRow");
|
||||
Integer configStartRow = redisCache.getCacheObject(CONFIG_KEY_PREFIX + "startRow");
|
||||
|
||||
@@ -493,21 +506,40 @@ public class TencentDocController extends BaseController {
|
||||
}
|
||||
|
||||
if (fileId == null || fileId.isEmpty() || sheetId == null || sheetId.isEmpty()) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "文档配置不完整");
|
||||
return AjaxResult.error("文档配置不完整,请先完成配置");
|
||||
}
|
||||
|
||||
// 4. 读取表头(从配置的 headerRow 读取,与 startRow 独立)
|
||||
// 4. 获取分布式锁(针对该订单单号+文档+工作表)
|
||||
lockKey = TENCENT_DOC_LOCK_KEY + fileId + ":" + sheetId + ":" + thirdPartyOrderNo;
|
||||
boolean lockAcquired = redisCache.setCacheObject(lockKey, "locked", 30, TimeUnit.SECONDS);
|
||||
|
||||
if (!lockAcquired) {
|
||||
log.warn("获取锁失败,该订单正在被其他请求处理:{}", thirdPartyOrderNo);
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "获取分布式锁失败,该订单正在处理中");
|
||||
return AjaxResult.error("该订单正在处理中,请稍后再试");
|
||||
}
|
||||
|
||||
log.info("✓ 获取分布式锁成功 - 单号: {}, lockKey: {}", thirdPartyOrderNo, lockKey);
|
||||
|
||||
// 5. 读取表头(从配置的 headerRow 读取,与 startRow 独立)
|
||||
String headerRange = String.format("A%d:Z%d", headerRow, headerRow);
|
||||
|
||||
JSONObject headerData = tencentDocService.readSheetData(accessToken, fileId, sheetId, headerRange);
|
||||
if (headerData == null || !headerData.containsKey("gridData")) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "无法读取表头数据");
|
||||
return AjaxResult.error("无法读取表头数据");
|
||||
}
|
||||
|
||||
// 5. 解析表头,找到"单号"和"物流单号"列
|
||||
// 6. 解析表头,找到"单号"和"物流单号"列
|
||||
JSONObject gridData = headerData.getJSONObject("gridData");
|
||||
JSONArray rows = gridData.getJSONArray("rows");
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "表头数据为空");
|
||||
return AjaxResult.error("表头数据为空");
|
||||
}
|
||||
|
||||
@@ -532,16 +564,20 @@ public class TencentDocController extends BaseController {
|
||||
}
|
||||
|
||||
if (orderNoColumn == -1 || logisticsColumn == -1) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "未找到'单号'或'物流'列");
|
||||
return AjaxResult.error("未找到'单号'或'物流'列,请检查表头配置");
|
||||
}
|
||||
|
||||
log.info("表头解析完成 - 单号列: {}, 物流列: {}", orderNoColumn, logisticsColumn);
|
||||
|
||||
// 6. 读取数据区域,查找指定单号
|
||||
// 7. 读取数据区域,查找指定单号
|
||||
String dataRange = String.format("A%d:Z%d", configStartRow, configStartRow + 999);
|
||||
JSONObject data = tencentDocService.readSheetData(accessToken, fileId, sheetId, dataRange);
|
||||
|
||||
if (data == null || !data.containsKey("gridData")) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "无法读取数据区域");
|
||||
return AjaxResult.error("无法读取数据区域");
|
||||
}
|
||||
|
||||
@@ -549,10 +585,12 @@ public class TencentDocController extends BaseController {
|
||||
JSONArray dataRows = dataGridData.getJSONArray("rows");
|
||||
|
||||
if (dataRows == null || dataRows.isEmpty()) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "数据区域为空");
|
||||
return AjaxResult.error("数据区域为空");
|
||||
}
|
||||
|
||||
// 7. 查找匹配的单号
|
||||
// 8. 查找匹配的单号
|
||||
int targetRow = -1;
|
||||
for (int i = 0; i < dataRows.size(); i++) {
|
||||
JSONObject row = dataRows.getJSONObject(i);
|
||||
@@ -571,23 +609,85 @@ public class TencentDocController extends BaseController {
|
||||
}
|
||||
|
||||
if (targetRow == -1) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, logisticsLink,
|
||||
"FAILED", "未找到单号");
|
||||
return AjaxResult.error("未找到单号: " + thirdPartyOrderNo);
|
||||
}
|
||||
|
||||
log.info("找到单号 {} 在第 {} 行", thirdPartyOrderNo, targetRow);
|
||||
|
||||
// 8. 获取用户信息(获取 openId)
|
||||
// 9. 再次读取该行数据,验证单号和物流链接列是否为空(防止并发覆盖)
|
||||
String verifyRange = String.format("A%d:Z%d", targetRow, targetRow);
|
||||
JSONObject verifyData = tencentDocService.readSheetData(accessToken, fileId, sheetId, verifyRange);
|
||||
|
||||
if (verifyData == null || !verifyData.containsKey("gridData")) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, targetRow, logisticsLink,
|
||||
"FAILED", "验证读取失败");
|
||||
return AjaxResult.error("验证读取失败");
|
||||
}
|
||||
|
||||
JSONObject verifyGridData = verifyData.getJSONObject("gridData");
|
||||
JSONArray verifyRows = verifyGridData.getJSONArray("rows");
|
||||
|
||||
if (verifyRows == null || verifyRows.isEmpty()) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, targetRow, logisticsLink,
|
||||
"FAILED", "验证行数据为空");
|
||||
return AjaxResult.error("验证行数据为空");
|
||||
}
|
||||
|
||||
JSONObject verifyRowData = verifyRows.getJSONObject(0);
|
||||
JSONArray verifyCells = verifyRowData.getJSONArray("values");
|
||||
|
||||
// 验证单号是否仍然匹配
|
||||
if (verifyCells == null || verifyCells.size() <= orderNoColumn) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, targetRow, logisticsLink,
|
||||
"SKIPPED", "验证时单号列为空,行已变化");
|
||||
return AjaxResult.error("验证失败:行数据已变化,单号列为空");
|
||||
}
|
||||
|
||||
JSONObject verifyOrderNoCell = verifyCells.getJSONObject(orderNoColumn);
|
||||
String verifyOrderNo = null;
|
||||
if (verifyOrderNoCell.containsKey("cellValue")) {
|
||||
verifyOrderNo = verifyOrderNoCell.getJSONObject("cellValue").getString("text");
|
||||
}
|
||||
|
||||
if (!thirdPartyOrderNo.equals(verifyOrderNo)) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, targetRow, logisticsLink,
|
||||
"SKIPPED", String.format("验证失败:单号不匹配,期望 %s,实际 %s", thirdPartyOrderNo, verifyOrderNo));
|
||||
return AjaxResult.error("验证失败:单号不匹配,行数据已变化");
|
||||
}
|
||||
|
||||
// 验证物流链接列是否为空
|
||||
if (verifyCells.size() > logisticsColumn) {
|
||||
JSONObject logisticsCell = verifyCells.getJSONObject(logisticsColumn);
|
||||
if (logisticsCell.containsKey("cellValue")) {
|
||||
String existingLogistics = logisticsCell.getJSONObject("cellValue").getString("text");
|
||||
if (existingLogistics != null && !existingLogistics.trim().isEmpty()) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, targetRow, logisticsLink,
|
||||
"SKIPPED", String.format("物流链接列已有值:%s", existingLogistics));
|
||||
return AjaxResult.error(String.format("该订单物流链接已存在:%s", existingLogistics));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log.info("✓ 验证通过 - 单号匹配且物流列为空,可以写入");
|
||||
|
||||
// 10. 获取用户信息(获取 openId)
|
||||
JSONObject userInfo = com.ruoyi.jarvis.util.TencentDocApiUtil.getUserInfo(accessToken);
|
||||
JSONObject userData = userInfo.getJSONObject("data");
|
||||
if (userData == null) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, targetRow, logisticsLink,
|
||||
"FAILED", "无法获取用户数据");
|
||||
return AjaxResult.error("无法获取用户数据");
|
||||
}
|
||||
String openId = userData.getString("openID");
|
||||
if (openId == null || openId.isEmpty()) {
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, targetRow, logisticsLink,
|
||||
"FAILED", "无法获取Open-Id");
|
||||
return AjaxResult.error("无法获取Open-Id");
|
||||
}
|
||||
|
||||
// 9. 写入物流链接
|
||||
// 11. 写入物流链接
|
||||
com.ruoyi.jarvis.util.TencentDocApiUtil.writeSheetData(
|
||||
accessToken,
|
||||
tencentDocConfig.getAppId(),
|
||||
@@ -599,6 +699,12 @@ public class TencentDocController extends BaseController {
|
||||
tencentDocConfig.getApiBaseUrl()
|
||||
);
|
||||
|
||||
// 12. 记录成功日志
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, targetRow, logisticsLink,
|
||||
"SUCCESS", null);
|
||||
|
||||
log.info("✓ 物流链接写入成功 - 单号: {}, 行: {}, 链接: {}", thirdPartyOrderNo, targetRow, logisticsLink);
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
result.put("thirdPartyOrderNo", thirdPartyOrderNo);
|
||||
result.put("logisticsLink", logisticsLink);
|
||||
@@ -609,7 +715,42 @@ public class TencentDocController extends BaseController {
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("填充物流链接失败", e);
|
||||
logOperation(fileId, sheetId, "WRITE_SINGLE", thirdPartyOrderNo, null, null,
|
||||
"FAILED", "异常: " + e.getMessage());
|
||||
return AjaxResult.error("填充物流链接失败: " + e.getMessage());
|
||||
} finally {
|
||||
// 释放分布式锁
|
||||
if (lockKey != null) {
|
||||
try {
|
||||
redisCache.deleteObject(lockKey);
|
||||
log.info("✓ 释放分布式锁 - lockKey: {}", lockKey);
|
||||
} catch (Exception e) {
|
||||
log.error("释放分布式锁失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录操作日志到数据库
|
||||
*/
|
||||
private void logOperation(String fileId, String sheetId, String operationType,
|
||||
String orderNo, Integer targetRow, String logisticsLink,
|
||||
String status, String errorMessage) {
|
||||
try {
|
||||
com.ruoyi.jarvis.domain.TencentDocOperationLog log = new com.ruoyi.jarvis.domain.TencentDocOperationLog();
|
||||
log.setFileId(fileId);
|
||||
log.setSheetId(sheetId);
|
||||
log.setOperationType(operationType);
|
||||
log.setOrderNo(orderNo);
|
||||
log.setTargetRow(targetRow);
|
||||
log.setLogisticsLink(logisticsLink);
|
||||
log.setOperationStatus(status);
|
||||
log.setErrorMessage(errorMessage);
|
||||
log.setOperator(getUsername()); // 从BaseController继承的方法
|
||||
operationLogMapper.insertLog(log);
|
||||
} catch (Exception e) {
|
||||
TencentDocController.log.error("记录操作日志失败", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.ruoyi.jarvis.domain;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 腾讯文档操作日志对象 tencent_doc_operation_log
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TencentDocOperationLog extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 主键ID */
|
||||
private Long id;
|
||||
|
||||
/** 文档ID */
|
||||
private String fileId;
|
||||
|
||||
/** 工作表ID */
|
||||
private String sheetId;
|
||||
|
||||
/** 操作类型 */
|
||||
private String operationType;
|
||||
|
||||
/** 订单单号 */
|
||||
private String orderNo;
|
||||
|
||||
/** 目标行号 */
|
||||
private Integer targetRow;
|
||||
|
||||
/** 写入的物流链接 */
|
||||
private String logisticsLink;
|
||||
|
||||
/** 操作状态 */
|
||||
private String operationStatus;
|
||||
|
||||
/** 错误信息 */
|
||||
private String errorMessage;
|
||||
|
||||
/** 操作人 */
|
||||
private String operator;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.ruoyi.jarvis.mapper;
|
||||
|
||||
import com.ruoyi.jarvis.domain.TencentDocOperationLog;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 腾讯文档操作日志Mapper接口
|
||||
*
|
||||
* @author system
|
||||
*/
|
||||
@Mapper
|
||||
public interface TencentDocOperationLogMapper {
|
||||
/**
|
||||
* 插入操作日志
|
||||
*
|
||||
* @param log 操作日志
|
||||
* @return 结果
|
||||
*/
|
||||
int insertLog(TencentDocOperationLog log);
|
||||
}
|
||||
|
||||
@@ -1233,10 +1233,11 @@ private String handleTF(String input) {
|
||||
jdOrderService.insertJDOrder(order);
|
||||
}
|
||||
|
||||
// 如果分销标识是 H-TF,自动写入腾讯文档
|
||||
if ("H-TF".equals(order.getDistributionMark())) {
|
||||
asyncWriteToTencentDoc(order);
|
||||
}
|
||||
// 注意:H-TF订单不再自动写入腾讯文档,需通过订单列表手动触发
|
||||
// 原因:防止并发写入和数据覆盖,需要人工确认
|
||||
// if ("H-TF".equals(order.getDistributionMark())) {
|
||||
// asyncWriteToTencentDoc(order);
|
||||
// }
|
||||
|
||||
// 返回完整的表单格式,使用原始输入保留完整物流链接
|
||||
return formatOrderForm(order, originalInput);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?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.TencentDocOperationLogMapper">
|
||||
|
||||
<resultMap type="com.ruoyi.jarvis.domain.TencentDocOperationLog" id="TencentDocOperationLogResult">
|
||||
<id property="id" column="id" />
|
||||
<result property="fileId" column="file_id" />
|
||||
<result property="sheetId" column="sheet_id" />
|
||||
<result property="operationType" column="operation_type" />
|
||||
<result property="orderNo" column="order_no" />
|
||||
<result property="targetRow" column="target_row" />
|
||||
<result property="logisticsLink" column="logistics_link" />
|
||||
<result property="operationStatus" column="operation_status" />
|
||||
<result property="errorMessage" column="error_message" />
|
||||
<result property="operator" column="operator" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="remark" column="remark" />
|
||||
</resultMap>
|
||||
|
||||
<insert id="insertLog" parameterType="com.ruoyi.jarvis.domain.TencentDocOperationLog">
|
||||
insert into tencent_doc_operation_log
|
||||
<trim prefix="(" suffix=")" suffixOverrides=",">
|
||||
<if test="fileId != null">file_id,</if>
|
||||
<if test="sheetId != null">sheet_id,</if>
|
||||
<if test="operationType != null">operation_type,</if>
|
||||
<if test="orderNo != null">order_no,</if>
|
||||
<if test="targetRow != null">target_row,</if>
|
||||
<if test="logisticsLink != null">logistics_link,</if>
|
||||
<if test="operationStatus != null">operation_status,</if>
|
||||
<if test="errorMessage != null">error_message,</if>
|
||||
<if test="operator != null">operator,</if>
|
||||
<if test="remark != null">remark,</if>
|
||||
create_time
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="fileId != null">#{fileId},</if>
|
||||
<if test="sheetId != null">#{sheetId},</if>
|
||||
<if test="operationType != null">#{operationType},</if>
|
||||
<if test="orderNo != null">#{orderNo},</if>
|
||||
<if test="targetRow != null">#{targetRow},</if>
|
||||
<if test="logisticsLink != null">#{logisticsLink},</if>
|
||||
<if test="operationStatus != null">#{operationStatus},</if>
|
||||
<if test="errorMessage != null">#{errorMessage},</if>
|
||||
<if test="operator != null">#{operator},</if>
|
||||
<if test="remark != null">#{remark},</if>
|
||||
now()
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
</mapper>
|
||||
|
||||
Reference in New Issue
Block a user