33 changed files with 1444 additions and 50 deletions
-
35pom.xml
-
200src/main/java/com/example/demo/Util/ExcelUploadUtil.java
-
227src/main/java/com/example/demo/Util/ExecutionContextUtil.java
-
173src/main/java/com/example/demo/Util/FeiShuAlertUtil.java
-
37src/main/java/com/example/demo/Util/RedisLockUtil.java
-
31src/main/java/com/example/demo/Util/RedisUtil.java
-
18src/main/java/com/example/demo/config/EnvConfig.java
-
4src/main/java/com/example/demo/config/RedisConfig.java
-
28src/main/java/com/example/demo/controller/ExportController.java
-
31src/main/java/com/example/demo/controller/GoldDetailController.java
-
4src/main/java/com/example/demo/controller/PermissionController.java
-
44src/main/java/com/example/demo/domain/DTO/GoldDetailDTO.java
-
28src/main/java/com/example/demo/domain/export/Goldmingxi.java
-
28src/main/java/com/example/demo/domain/vo/AiEmotionExportRecordVO.java
-
25src/main/java/com/example/demo/domain/vo/ExecutionContext.java
-
26src/main/java/com/example/demo/domain/vo/ExportVo.java
-
25src/main/java/com/example/demo/mapper/AiEmotionMapper.java
-
19src/main/java/com/example/demo/mapper/ExportMapper.java
-
15src/main/java/com/example/demo/mapper/GoldDetailMapper.java
-
19src/main/java/com/example/demo/service/AiEmotionService.java
-
16src/main/java/com/example/demo/service/ExportExcelService.java
-
7src/main/java/com/example/demo/service/GoldDetailService.java
-
1src/main/java/com/example/demo/service/PermissionService.java
-
35src/main/java/com/example/demo/serviceImpl/AiEmotionServiceImpl.java
-
253src/main/java/com/example/demo/serviceImpl/ExportExcelServiceImpl.java
-
69src/main/java/com/example/demo/serviceImpl/GoldDetailServiceImpl.java
-
5src/main/java/com/example/demo/serviceImpl/PermissionServiceImpl.java
-
14src/main/resources/application.yml
-
17src/main/resources/mapper/AiEmotionMapper.xml
-
18src/main/resources/mapper/ExportMapper.xml
-
15src/main/resources/mapper/GoldDetailMapper.xml
-
19src/main/resources/mapper/PermissionMapper.xml
-
8src/main/resources/mapper/UrlMapper.xml
@ -0,0 +1,200 @@ |
|||
package com.example.demo.Util; |
|||
|
|||
import org.slf4j.Logger; |
|||
import org.slf4j.LoggerFactory; |
|||
import org.springframework.core.io.FileSystemResource; |
|||
import org.springframework.http.*; |
|||
import org.springframework.http.client.SimpleClientHttpRequestFactory; |
|||
import org.springframework.http.converter.StringHttpMessageConverter; |
|||
import org.springframework.util.LinkedMultiValueMap; |
|||
import org.springframework.util.MultiValueMap; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.nio.charset.Charset; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* Excel文件上传工具类 |
|||
*/ |
|||
public class ExcelUploadUtil { |
|||
private static final Logger logger = LoggerFactory.getLogger(ExcelUploadUtil.class); |
|||
|
|||
// 默认配置 |
|||
private static final int DEFAULT_CONNECT_TIMEOUT = 30000; // 30秒 |
|||
private static final int DEFAULT_READ_TIMEOUT = 60000; // 60秒 |
|||
|
|||
private final RestTemplate restTemplate; |
|||
private final String uploadUrl; |
|||
private final Map<String, String> defaultHeaders; |
|||
private final Map<String, String> defaultParams; |
|||
|
|||
/** |
|||
* 构造方法 |
|||
* |
|||
* @param uploadUrl 上传接口URL |
|||
*/ |
|||
public ExcelUploadUtil(String uploadUrl) { |
|||
this(uploadUrl, new HashMap<>(), new HashMap<>()); |
|||
} |
|||
|
|||
/** |
|||
* 构造方法 |
|||
* |
|||
* @param uploadUrl 上传接口URL |
|||
* @param defaultHeaders 默认请求头 |
|||
* @param defaultParams 默认请求参数 |
|||
*/ |
|||
public ExcelUploadUtil(String uploadUrl, Map<String, String> defaultHeaders, Map<String, String> defaultParams) { |
|||
this.uploadUrl = uploadUrl; |
|||
this.defaultHeaders = new HashMap<>(defaultHeaders); |
|||
this.defaultParams = new HashMap<>(defaultParams); |
|||
this.restTemplate = createRestTemplate(DEFAULT_CONNECT_TIMEOUT, DEFAULT_READ_TIMEOUT); |
|||
} |
|||
|
|||
/** |
|||
* 创建RestTemplate (Spring Boot 1.x 兼容版本) |
|||
*/ |
|||
private RestTemplate createRestTemplate(int connectTimeout, int readTimeout) { |
|||
RestTemplate restTemplate = new RestTemplate(); |
|||
|
|||
// 添加字符串消息转换器 (Spring 1.x 使用Charset而不是StandardCharsets) |
|||
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8"))); |
|||
|
|||
// 设置超时 (Spring 1.x 方式) |
|||
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); |
|||
factory.setConnectTimeout(connectTimeout); |
|||
factory.setReadTimeout(readTimeout); |
|||
restTemplate.setRequestFactory(factory); |
|||
|
|||
return restTemplate; |
|||
} |
|||
|
|||
/** |
|||
* 上传Excel文件 |
|||
* |
|||
* @param excelFile Excel文件 |
|||
* @param targetDir 目标目录 |
|||
* @return 上传结果 |
|||
* @throws IOException 文件操作异常 |
|||
* @throws UploadException 上传异常 |
|||
*/ |
|||
public String uploadExcel(File excelFile, String targetDir) throws IOException, UploadException { |
|||
return uploadExcel(excelFile, targetDir, new HashMap<>(), new HashMap<>()); |
|||
} |
|||
|
|||
/** |
|||
* 上传Excel文件(带自定义参数) |
|||
* |
|||
* @param excelFile Excel文件 |
|||
* @param targetDir 目标目录 |
|||
* @param customHeaders 自定义请求头 |
|||
* @param customParams 自定义请求参数 |
|||
* @return 上传结果 |
|||
* @throws IOException 文件操作异常 |
|||
* @throws UploadException 上传异常 |
|||
*/ |
|||
public String uploadExcel(File excelFile, String targetDir, |
|||
Map<String, String> customHeaders, |
|||
Map<String, String> customParams) throws IOException, UploadException { |
|||
// 验证文件 |
|||
validateFile(excelFile); |
|||
|
|||
try { |
|||
// 准备请求 |
|||
HttpEntity<MultiValueMap<String, Object>> requestEntity = prepareRequest(excelFile, targetDir, customHeaders, customParams); |
|||
|
|||
// 执行上传 |
|||
ResponseEntity<String> response = restTemplate.exchange( |
|||
uploadUrl, |
|||
HttpMethod.POST, |
|||
requestEntity, |
|||
String.class |
|||
); |
|||
|
|||
// 处理响应 |
|||
return handleResponse(response, excelFile.getName()); |
|||
} catch (Exception e) { |
|||
logger.error("Excel文件上传失败: {}", excelFile.getAbsolutePath(), e); |
|||
throw new UploadException("文件上传失败: " + e.getMessage(), e); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 验证文件 |
|||
*/ |
|||
private void validateFile(File file) throws IOException { |
|||
if (file == null) { |
|||
throw new IOException("文件不能为null"); |
|||
} |
|||
if (!file.exists()) { |
|||
throw new IOException("文件不存在: " + file.getAbsolutePath()); |
|||
} |
|||
if (!file.isFile()) { |
|||
throw new IOException("不是有效的文件: " + file.getAbsolutePath()); |
|||
} |
|||
if (file.length() == 0) { |
|||
throw new IOException("文件内容为空: " + file.getAbsolutePath()); |
|||
} |
|||
if (!file.getName().toLowerCase().endsWith(".xlsx") && |
|||
!file.getName().toLowerCase().endsWith(".xls")) { |
|||
throw new IOException("仅支持Excel文件(.xlsx, .xls)"); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 准备请求 |
|||
*/ |
|||
private HttpEntity<MultiValueMap<String, Object>> prepareRequest(File file, String targetDir, |
|||
Map<String, String> customHeaders, |
|||
Map<String, String> customParams) { |
|||
// 设置请求头 |
|||
HttpHeaders headers = new HttpHeaders(); |
|||
headers.setContentType(MediaType.MULTIPART_FORM_DATA); |
|||
|
|||
// 添加默认和自定义请求头 |
|||
defaultHeaders.forEach(headers::set); |
|||
customHeaders.forEach(headers::set); |
|||
|
|||
// 准备请求体 |
|||
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(); |
|||
body.add("file", new FileSystemResource(file)); |
|||
body.add("dir", targetDir); |
|||
|
|||
// 添加默认和自定义参数 |
|||
defaultParams.forEach(body::add); |
|||
customParams.forEach(body::add); |
|||
|
|||
return new HttpEntity<>(body, headers); |
|||
} |
|||
|
|||
/** |
|||
* 处理响应 |
|||
*/ |
|||
private String handleResponse(ResponseEntity<String> response, String filename) throws UploadException { |
|||
if (response.getStatusCode() == HttpStatus.OK) { |
|||
logger.info("文件上传成功: {}", filename); |
|||
return response.getBody(); |
|||
} else { |
|||
String errorMsg = String.format("上传接口返回错误状态码: %d, 响应: %s", |
|||
response.getStatusCodeValue(), response.getBody()); |
|||
logger.error(errorMsg); |
|||
throw new UploadException(errorMsg); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 自定义上传异常 |
|||
*/ |
|||
public static class UploadException extends Exception { |
|||
public UploadException(String message) { |
|||
super(message); |
|||
} |
|||
|
|||
public UploadException(String message, Throwable cause) { |
|||
super(message, cause); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,227 @@ |
|||
package com.example.demo.Util; |
|||
|
|||
|
|||
import com.example.demo.domain.vo.ExecutionContext; |
|||
import jakarta.servlet.http.HttpServletRequest; |
|||
import org.springframework.web.context.request.RequestContextHolder; |
|||
import org.springframework.web.context.request.ServletRequestAttributes; |
|||
|
|||
|
|||
import java.io.BufferedReader; |
|||
import java.io.File; |
|||
import java.io.IOException; |
|||
import java.lang.management.ManagementFactory; |
|||
import java.util.*; |
|||
import java.util.stream.Collectors; |
|||
|
|||
public class ExecutionContextUtil { |
|||
|
|||
/** |
|||
* 获取当前执行环境信息 |
|||
* @param request 如果是Web请求,传入HttpServletRequest |
|||
* @return 执行环境信息对象 |
|||
*/ |
|||
/** |
|||
* 从Spring上下文获取当前HttpServletRequest |
|||
*/ |
|||
public static ExecutionContext getExecutionContext() { |
|||
ExecutionContext context = new ExecutionContext(); |
|||
context.setExecutionTime(new Date()); |
|||
|
|||
HttpServletRequest request = getCurrentHttpRequest(); |
|||
|
|||
if (isWebEnvironment(request)) { |
|||
// Web API 环境 |
|||
context.setExecutionType("API"); |
|||
context.setApiUrl(getRealRequestUrl(request)); |
|||
context.setRequestParams(getRequestParams(request)); |
|||
context.setToken(getRequestToken(request)); |
|||
context.setMethod(request.getMethod()); |
|||
} else { |
|||
// 脚本环境 |
|||
context.setExecutionType("SCRIPT"); |
|||
context.setScriptFile(getMainClassFile()); |
|||
} |
|||
|
|||
return context; |
|||
} |
|||
|
|||
private static HttpServletRequest getCurrentHttpRequest() { |
|||
try { |
|||
return ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); |
|||
} catch (IllegalStateException e) { |
|||
// 不在Web请求上下文中 |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
private static boolean isWebEnvironment(HttpServletRequest request) { |
|||
return request != null; |
|||
} |
|||
|
|||
private static String getRealRequestUrl(HttpServletRequest request) { |
|||
// 1. 获取协议(优先从代理头获取) |
|||
String protocol = getHeaderWithFallback(request, |
|||
Arrays.asList("X-Forwarded-Proto", "X-Forwarded-Protocol"), |
|||
request.getScheme() |
|||
); |
|||
|
|||
// 2. 获取真实域名(优先从代理头获取原始域名) |
|||
String domain = getHeaderWithFallback(request, |
|||
Arrays.asList( |
|||
"X-Original-Host", // 一些代理服务器设置的原始终端 |
|||
"X-Real-Host", // 另一个可能的原始主机头 |
|||
"X-Forwarded-Host", // 转发的主机头 |
|||
"Host" // 最后回退到常规主机头 |
|||
), |
|||
request.getServerName() |
|||
); |
|||
|
|||
// 3. 获取端口(智能处理默认端口) |
|||
Integer port = getRealPort(request, protocol); |
|||
|
|||
// 4. 获取原始路径(包括QueryString) |
|||
String path = getOriginalUri(request); |
|||
|
|||
// 组装完整URL |
|||
return String.format("%s://%s:%s%s", |
|||
protocol, |
|||
domain, |
|||
port, |
|||
path |
|||
); |
|||
} |
|||
|
|||
// 辅助方法:带fallback的header获取 |
|||
// 方法1:保持强类型(推荐) |
|||
private static String getHeaderWithFallback( |
|||
HttpServletRequest request, |
|||
List<String> headerNames, // 明确要求String列表 |
|||
String defaultValue |
|||
) { |
|||
return headerNames.stream() |
|||
.map(request::getHeader) |
|||
.filter(Objects::nonNull) |
|||
.findFirst() |
|||
.orElse(defaultValue); |
|||
} |
|||
|
|||
// 获取真实端口(处理代理情况) |
|||
private static int getRealPort(HttpServletRequest request, String protocol) { |
|||
// 优先从代理头获取 |
|||
String forwardedPort = request.getHeader("X-Forwarded-Port"); |
|||
if (forwardedPort != null) { |
|||
return Integer.parseInt(forwardedPort); |
|||
} |
|||
|
|||
// 其次从请求获取 |
|||
int port = request.getServerPort(); |
|||
|
|||
// 处理反向代理场景 |
|||
if (port == 80 && "https".equals(protocol)) { |
|||
return 443; |
|||
} |
|||
if (port == 443 && "http".equals(protocol)) { |
|||
return 80; |
|||
} |
|||
return port; |
|||
} |
|||
|
|||
// 获取原始URI(包含QueryString) |
|||
private static String getOriginalUri(HttpServletRequest request) { |
|||
// 优先从代理头获取原始URI |
|||
String originalUri = request.getHeader("X-Original-URI"); |
|||
if (originalUri != null) { |
|||
return originalUri; |
|||
} |
|||
|
|||
// 默认从request获取 |
|||
String queryString = request.getQueryString(); |
|||
return request.getRequestURI() + |
|||
(queryString != null ? "?" + queryString : ""); |
|||
} |
|||
|
|||
private static String getRequestParams(HttpServletRequest request) { |
|||
try { |
|||
// 1. 优先读取Query String(无需缓存) |
|||
String queryString = request.getQueryString(); |
|||
if (queryString != null) return queryString; |
|||
|
|||
// 2. 检查表单参数(GET/POST都适用) |
|||
Map<String, String[]> params = request.getParameterMap(); |
|||
if (!params.isEmpty()) return formatParams(params); |
|||
|
|||
// 3. 只有明确是JSON请求时才尝试读取body |
|||
if (isJsonRequest(request)) { |
|||
return readJsonBodyOnDemand(request); |
|||
} |
|||
|
|||
return "{}"; |
|||
} catch (Exception e) { |
|||
return "{\"error\":\"failed to read params\"}"; |
|||
} |
|||
} |
|||
|
|||
private static String readJsonBodyOnDemand(HttpServletRequest request) throws IOException { |
|||
// 关键点:直接读取原始InputStream(不缓存) |
|||
try (BufferedReader reader = request.getReader()) { |
|||
String body = reader.lines().collect(Collectors.joining()); |
|||
return body.isEmpty() ? "{}" : body; |
|||
} |
|||
} |
|||
|
|||
|
|||
private static boolean isJsonRequest(HttpServletRequest request) { |
|||
String contentType = request.getContentType(); |
|||
return contentType != null && contentType.contains("application/json"); |
|||
} |
|||
|
|||
|
|||
private static String formatParams(Map<String, String[]> params) { |
|||
// 优化后的参数格式化方法 |
|||
return params.entrySet().stream() |
|||
.map(entry -> { |
|||
String key = escapeJson(entry.getKey()); |
|||
String[] values = entry.getValue(); |
|||
if (values.length == 1) { |
|||
return "\"" + key + "\":\"" + escapeJson(values[0]) + "\""; |
|||
} |
|||
return "\"" + key + "\":[" + |
|||
Arrays.stream(values) |
|||
.map(v -> "\"" + escapeJson(v) + "\"") |
|||
.collect(Collectors.joining(",")) + |
|||
"]"; |
|||
}) |
|||
.collect(Collectors.joining(",", "{", "}")); |
|||
} |
|||
|
|||
private static String escapeJson(String raw) { |
|||
return raw.replace("\\", "\\\\") |
|||
.replace("\"", "\\\"") |
|||
.replace("\n", "\\n"); |
|||
} |
|||
|
|||
private static String getRequestToken(HttpServletRequest request) { |
|||
String token = request.getHeader("Authorization"); |
|||
if (token == null) { |
|||
token = request.getHeader("token"); |
|||
} |
|||
return token; |
|||
} |
|||
|
|||
private static String getMainClassFile() { |
|||
try { |
|||
// 获取主类名 |
|||
String mainClass = ManagementFactory.getRuntimeMXBean().getSystemProperties().get("sun.java.command"); |
|||
if (mainClass != null) { |
|||
// 简单处理,提取主类名 |
|||
String className = mainClass.split(" ")[0]; |
|||
// 转换为文件路径 |
|||
return className.replace('.', File.separatorChar) + ".java"; |
|||
} |
|||
} catch (Exception e) { |
|||
e.printStackTrace(); |
|||
} |
|||
return "UnknownScript"; |
|||
} |
|||
} |
@ -0,0 +1,173 @@ |
|||
package com.example.demo.Util; |
|||
|
|||
import com.alibaba.fastjson.JSON; |
|||
import com.alibaba.fastjson.JSONObject; |
|||
import com.example.demo.domain.vo.ExecutionContext; |
|||
import com.example.demo.config.EnvConfig; |
|||
import org.apache.http.HttpResponse; |
|||
import org.apache.http.HttpStatus; |
|||
import org.apache.http.client.methods.HttpPost; |
|||
import org.apache.http.entity.StringEntity; |
|||
import org.apache.http.impl.client.CloseableHttpClient; |
|||
import org.apache.http.impl.client.HttpClients; |
|||
import org.apache.http.util.EntityUtils; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.io.IOException; |
|||
import java.text.SimpleDateFormat; |
|||
import java.util.*; |
|||
|
|||
/** |
|||
* 飞书报警信息发送工具类 |
|||
*/ |
|||
@Component |
|||
public class FeiShuAlertUtil { |
|||
|
|||
private static final String FEISHU_WEBHOOK_URL_PROD = "https://open.feishu.cn/open-apis/bot/v2/hook/1a515b19-b64f-46b7-9486-35842b9539fe"; |
|||
private static final String FEISHU_WEBHOOK_URL_TEST = "https://open.feishu.cn/open-apis/bot/v2/hook/384c78aa-8df1-498b-9c47-04e890ed9877"; |
|||
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
|||
|
|||
static { |
|||
DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); |
|||
} |
|||
|
|||
/** |
|||
* 发送报警信息到飞书群(紧凑单行格式) |
|||
* |
|||
* @param context 请求接口 |
|||
* @param errorFile 错误文件 |
|||
* @param errorLine 错误行数 |
|||
* @param errorMsg 错误信息 |
|||
* @param params 脚本执行时需要手动传参,如果时API请求则无需传,自动获取参数 |
|||
* @return 是否发送成功 |
|||
*/ |
|||
public static boolean sendAlertMessage(ExecutionContext context, |
|||
String errorFile, int errorLine, |
|||
String errorMsg, String params) { |
|||
JSONObject message = new JSONObject(); |
|||
message.put("msg_type", "post"); |
|||
|
|||
JSONObject content = new JSONObject(); |
|||
JSONObject post = new JSONObject(); |
|||
JSONObject zhCn = new JSONObject(); |
|||
String title = "⚠️ 系统异常报警 ⚠️"; |
|||
zhCn.put("title", title); |
|||
|
|||
List<List<JSONObject>> contentList = new ArrayList<>(); |
|||
List<JSONObject> elements = new ArrayList<>(); |
|||
|
|||
StringBuilder contentBuilder = new StringBuilder(); |
|||
contentBuilder.append("------------------------------\n\n"); |
|||
|
|||
if ("API".equals(context.getExecutionType())) { |
|||
contentBuilder.append("**执行类型**: API请求\n\n"); |
|||
contentBuilder.append("**请求接口**: ").append(context.getApiUrl()).append("\n\n"); |
|||
contentBuilder.append("**请求参数**: ").append(params).append("\n\n"); |
|||
contentBuilder.append("**请求方法**: ").append(context.getMethod()).append("\n\n"); |
|||
if (context.getToken() != null) { |
|||
contentBuilder.append("**请求Token**: ") |
|||
.append(context.getToken().length() > 10 ? |
|||
context.getToken().substring(0, 10) + "..." : |
|||
context.getToken()) |
|||
.append("\n\n"); |
|||
} |
|||
} else { |
|||
contentBuilder.append("**执行类型**: 脚本执行\n\n"); |
|||
contentBuilder.append("**脚本文件**: ").append(context.getScriptFile()).append("\n\n"); |
|||
contentBuilder.append("**请求参数**: ").append(params).append("\n\n"); |
|||
} |
|||
|
|||
contentBuilder.append("**错误位置**: ").append(errorFile).append(":").append(errorLine).append("\n\n"); |
|||
contentBuilder.append("**错误信息**: ").append(errorMsg).append("\n\n"); |
|||
contentBuilder.append("**执行时间**: ").append(formatDate(context.getExecutionTime())).append("\n\n"); |
|||
contentBuilder.append("**报警时间**: ").append(formatDate(new Date())).append("\n\n"); |
|||
contentBuilder.append("------------------------------"); |
|||
|
|||
addContentElement(elements, "text", contentBuilder.toString()); |
|||
|
|||
contentList.add(elements); |
|||
zhCn.put("content", contentList); |
|||
post.put("zh_cn", zhCn); |
|||
content.put("post", post); |
|||
message.put("content", content); |
|||
|
|||
return sendMessage(message); |
|||
} |
|||
|
|||
private static String formatDate(Date date) { |
|||
// 实现日期格式化方法 |
|||
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date); |
|||
} |
|||
|
|||
private static void addContentElement(List<JSONObject> elements, String tag, String text) { |
|||
JSONObject element = new JSONObject(); |
|||
element.put("tag", tag); |
|||
element.put("text", text); |
|||
elements.add(element); |
|||
} |
|||
|
|||
private static boolean sendMessage(JSONObject message) { |
|||
try (CloseableHttpClient httpClient = HttpClients.createDefault()) { |
|||
String FEISHU_WEBHOOK_URL; |
|||
String environment = EnvConfig.ENV; |
|||
System.out.println("当前环境变量:" + environment); |
|||
if (Objects.equals(environment, "unknown") || environment.equals("dev")) { |
|||
FEISHU_WEBHOOK_URL = FEISHU_WEBHOOK_URL_TEST; |
|||
} else { |
|||
FEISHU_WEBHOOK_URL = FEISHU_WEBHOOK_URL_PROD; |
|||
} |
|||
HttpPost httpPost = new HttpPost(FEISHU_WEBHOOK_URL); |
|||
httpPost.addHeader("Content-Type", "application/json; charset=utf-8"); |
|||
|
|||
StringEntity entity = new StringEntity(message.toJSONString(), "UTF-8"); |
|||
httpPost.setEntity(entity); |
|||
|
|||
HttpResponse response = httpClient.execute(httpPost); |
|||
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { |
|||
String result = EntityUtils.toString(response.getEntity()); |
|||
JSONObject obj = JSON.parseObject(result); |
|||
return obj.getInteger("code") == 0; |
|||
} |
|||
} catch (IOException e) { |
|||
System.out.println("发送飞书异常" + e.getMessage()); |
|||
e.printStackTrace(); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 发送普通信息到飞书群 |
|||
* |
|||
* @param title 消息标题 |
|||
* @param content 消息内容 |
|||
* @return 是否发送成功 |
|||
*/ |
|||
public static boolean sendNormalMessage(String title, String content) { |
|||
JSONObject message = new JSONObject(); |
|||
message.put("msg_type", "post"); |
|||
|
|||
JSONObject messageContent = new JSONObject(); |
|||
JSONObject post = new JSONObject(); |
|||
JSONObject zhCn = new JSONObject(); |
|||
zhCn.put("title", title); |
|||
|
|||
List<List<JSONObject>> contentList = new ArrayList<>(); |
|||
List<JSONObject> elements = new ArrayList<>(); |
|||
|
|||
StringBuilder contentBuilder = new StringBuilder(); |
|||
contentBuilder.append("------------------------------\n\n"); |
|||
contentBuilder.append(content).append("\n\n"); |
|||
contentBuilder.append("**发送时间**: ").append(formatDate(new Date())).append("\n\n"); |
|||
contentBuilder.append("------------------------------"); |
|||
|
|||
addContentElement(elements, "text", contentBuilder.toString()); |
|||
|
|||
contentList.add(elements); |
|||
zhCn.put("content", contentList); |
|||
post.put("zh_cn", zhCn); |
|||
messageContent.put("post", post); |
|||
message.put("content", messageContent); |
|||
|
|||
return sendMessage(message); |
|||
} |
|||
} |
@ -0,0 +1,37 @@ |
|||
package com.example.demo.Util; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.data.redis.core.StringRedisTemplate; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Component |
|||
public class RedisLockUtil { |
|||
|
|||
@Autowired |
|||
private StringRedisTemplate redisTemplate; |
|||
|
|||
/** |
|||
* 尝试获取分布式锁 |
|||
* @param lockKey 锁的 Key |
|||
* @param requestId 请求 ID(可用 UUID) |
|||
* @param expireTime 锁的过期时间(毫秒) |
|||
* @return 是否获取成功 |
|||
*/ |
|||
public boolean tryLock(String lockKey, String requestId, long expireTime) { |
|||
return redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.MILLISECONDS); |
|||
} |
|||
|
|||
/** |
|||
* 释放分布式锁 |
|||
* @param lockKey 锁的 Key |
|||
* @param requestId 请求 ID |
|||
*/ |
|||
public void unlock(String lockKey, String requestId) { |
|||
String currentValue = redisTemplate.opsForValue().get(lockKey); |
|||
if (requestId.equals(currentValue)) { |
|||
redisTemplate.delete(lockKey); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,18 @@ |
|||
package com.example.demo.config; |
|||
|
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName EnvConfig |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-29 13:55 |
|||
* @Version 1.0 |
|||
**/ |
|||
@Configuration |
|||
public class EnvConfig { |
|||
public static final String ENV = "dev"; |
|||
public static final String ENV_PROD = "prod"; |
|||
public static final String ENV_TEST = "test"; |
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.example.demo.controller; |
|||
|
|||
import com.example.demo.domain.entity.Export; |
|||
import com.example.demo.domain.vo.Result; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName ExportController |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-28 15:22 |
|||
* @Version 1.0 |
|||
**/ |
|||
@RestController |
|||
@RequestMapping("/export") |
|||
@RequiredArgsConstructor |
|||
@Slf4j |
|||
@CrossOrigin |
|||
public class ExportController { |
|||
@PostMapping("/export") |
|||
public Result export(@RequestBody Export export){ |
|||
return null; |
|||
} |
|||
} |
@ -0,0 +1,44 @@ |
|||
package com.example.demo.domain.DTO; |
|||
|
|||
import jakarta.validation.constraints.NotNull; |
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName AiEmotionExportDTO |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-30 15:06 |
|||
* @Version 1.0 |
|||
**/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class GoldDetailDTO { |
|||
private String token; |
|||
private String url = ""; |
|||
private String fileName = ""; |
|||
private Integer sort = 0; |
|||
private String field = ""; |
|||
private Integer jwcode; |
|||
private Integer type = 0; //类型 |
|||
private Integer state = 0; //状态 |
|||
private String text = ""; //关键词搜索 |
|||
private Integer dataNum = 0; |
|||
private String deptid = ""; |
|||
|
|||
@NotNull(message = "page不能为空") |
|||
private Integer page = 1; |
|||
@NotNull(message = "pageSize不能为空") |
|||
private Integer pageSize = 20; |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return String.format( |
|||
"AiEmotionExport(jwcode=%d, type=%d, state=%d, dataNum=%d)", |
|||
jwcode, type, state, dataNum |
|||
); |
|||
} |
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.example.demo.domain.export; |
|||
|
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName goldmingxi |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-29 17:37 |
|||
* @Version 1.0 |
|||
**/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
public class Goldmingxi { |
|||
private String name; // 名称 |
|||
private Integer jwcode; // 精网号 |
|||
private String market; // 所属地区 |
|||
private String payPlatform; // 支付平台 |
|||
private Integer type; // 类型 |
|||
private Integer sumGold; // 总金币 |
|||
private Integer permentGold; //永久金币 |
|||
private Integer freeJune; // 免费金币六月到期 |
|||
private Integer freeDecember; // 免费金币七月到期 |
|||
private Integer taskGold; // 任务金币 |
|||
private String adminName; //提交人 |
|||
} |
@ -0,0 +1,28 @@ |
|||
package com.example.demo.domain.vo; |
|||
|
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName AiEmotionExportRecordVO |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-29 14:59 |
|||
* @Version 1.0 |
|||
**/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
public class AiEmotionExportRecordVO { |
|||
private static final long serialVersionUID = 1L; |
|||
private String token; |
|||
private Long id; |
|||
private Long jwcode; |
|||
private String fileName; |
|||
private String url; |
|||
private Integer state; |
|||
private Date createTime; |
|||
private Date updateTime; |
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.example.demo.domain.vo; |
|||
|
|||
import lombok.AllArgsConstructor; |
|||
import lombok.Data; |
|||
|
|||
import java.util.Date; |
|||
|
|||
@Data |
|||
@AllArgsConstructor |
|||
public class ExecutionContext { |
|||
// getters 和 setters |
|||
private String executionType; // "API" 或 "SCRIPT" |
|||
private String apiUrl; |
|||
private String requestParams; |
|||
private String token; |
|||
private String scriptFile; |
|||
private Date executionTime; |
|||
private String method; |
|||
|
|||
// 构造方法 |
|||
public ExecutionContext() { |
|||
this.executionTime = new Date(); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.example.demo.domain.vo; |
|||
|
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
|
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName ExportVo |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-29 17:24 |
|||
* @Version 1.0 |
|||
**/ |
|||
@Data |
|||
@NoArgsConstructor |
|||
|
|||
public class ExportVo { |
|||
private String token; |
|||
private Long id; |
|||
private String fileName; |
|||
private String url; |
|||
private Integer state; |
|||
|
|||
} |
@ -0,0 +1,25 @@ |
|||
package com.example.demo.mapper; |
|||
|
|||
import com.example.demo.domain.vo.AiEmotionExportRecordVO; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
import org.apache.ibatis.annotations.Param; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName AiEmotionMapper |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-29 16:48 |
|||
* @Version 1.0 |
|||
**/ |
|||
@Mapper |
|||
public interface AiEmotionMapper { |
|||
void updateStatus( |
|||
@Param("recordId") Long recordId, |
|||
@Param("state") Integer state, |
|||
@Param("url") String url, |
|||
@Param("reason") String reason, |
|||
@Param("dataNum") Integer dataNum |
|||
); |
|||
AiEmotionExportRecordVO getRecordById(Long recordId); |
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.example.demo.mapper; |
|||
|
|||
import com.example.demo.domain.vo.ExportVo; |
|||
import org.apache.ibatis.annotations.Mapper; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName ExportMapper |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-29 17:28 |
|||
* @Version 1.0 |
|||
**/ |
|||
@Mapper |
|||
public interface ExportMapper { |
|||
ExportVo getExportData(Integer id); |
|||
ExportVo updateExportData(Long recordId, Integer state, String url, String reason, Integer dataNum); |
|||
|
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.example.demo.service; |
|||
|
|||
import com.example.demo.domain.vo.AiEmotionExportRecordVO; |
|||
import com.example.demo.domain.vo.ExportVo; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName AiEmotionService |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-29 14:27 |
|||
* @Version 1.0 |
|||
**/ |
|||
public interface AiEmotionService { |
|||
|
|||
ExportVo updateStatus(Long recordId, int i, String s, String s1, int i1); |
|||
AiEmotionExportRecordVO getRecordById(Long id) throws Exception; |
|||
|
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.example.demo.service; |
|||
|
|||
import com.example.demo.domain.vo.AiEmotionExportRecordVO; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName exportService |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-28 15:39 |
|||
* @Version 1.0 |
|||
**/ |
|||
public interface ExportExcelService { |
|||
Exception handleExcelExportData(String message) throws Exception; |
|||
|
|||
} |
@ -0,0 +1,35 @@ |
|||
package com.example.demo.serviceImpl; |
|||
|
|||
import com.example.demo.domain.vo.AiEmotionExportRecordVO; |
|||
import com.example.demo.domain.vo.ExportVo; |
|||
import com.example.demo.mapper.AiEmotionMapper; |
|||
import com.example.demo.mapper.ExportMapper; |
|||
import com.example.demo.service.AiEmotionService; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
/** |
|||
* @program: GOLD |
|||
* @ClassName AiEmotionServiceImpl |
|||
* @description: |
|||
* @author: huangqizhen |
|||
* @create: 2025−06-29 14:46 |
|||
* @Version 1.0 |
|||
**/ |
|||
@Service |
|||
public class AiEmotionServiceImpl implements AiEmotionService { |
|||
@Autowired |
|||
private ExportMapper exportMapper; |
|||
@Autowired |
|||
private AiEmotionMapper aiEmotionMapper; |
|||
|
|||
@Override |
|||
public ExportVo updateStatus(Long recordId, int i, String s, String s1, int i1) { |
|||
return exportMapper.updateExportData(recordId, i, s, s1, i1); |
|||
} |
|||
|
|||
@Override |
|||
public AiEmotionExportRecordVO getRecordById(Long id) throws Exception { |
|||
return aiEmotionMapper.getRecordById(id); |
|||
} |
|||
} |
@ -0,0 +1,253 @@ |
|||
package com.example.demo.serviceImpl; |
|||
|
|||
import cn.hutool.log.AbstractLog; |
|||
import com.alibaba.excel.EasyExcel; |
|||
import com.alibaba.excel.ExcelWriter; |
|||
import com.alibaba.excel.write.metadata.WriteSheet; |
|||
import com.example.demo.Util.ExcelUploadUtil; |
|||
import com.example.demo.controller.GoldDetailController; |
|||
import com.example.demo.domain.export.Goldmingxi; |
|||
import com.example.demo.domain.vo.*; |
|||
|
|||
import com.example.demo.service.ExportExcelService; |
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
|
|||
|
|||
|
|||
import com.example.demo.service.AiEmotionService; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.StringUtils; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.transaction.annotation.Transactional; |
|||
|
|||
import java.io.*; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class ExportExcelServiceImpl implements ExportExcelService { |
|||
|
|||
private static final ObjectMapper objectMapper = new ObjectMapper(); |
|||
|
|||
//注入AiEmotionService |
|||
@Autowired |
|||
private AiEmotionService aiEmotionService; |
|||
//注入GoldDetailController |
|||
@Autowired |
|||
private GoldDetailController goldDetailController; |
|||
|
|||
// 每页查询的数据量 |
|||
private static final int PAGE_SIZE = 1000; |
|||
|
|||
|
|||
@Transactional |
|||
@Override |
|||
public Exception handleExcelExportData(String message) throws Exception { |
|||
System.out.println("明细导出excel数据开始执行:" + message); |
|||
long startTime = System.currentTimeMillis(); |
|||
Long recordId = null; |
|||
String fileName = null; |
|||
File tempFile = null; |
|||
OutputStream outputStream = null; |
|||
ExcelWriter excelWriter = null; |
|||
|
|||
try { |
|||
// 1. 解析JSON任务 |
|||
JsonNode rootNode = objectMapper.readTree(message); |
|||
// 2. 获取基本参数 |
|||
recordId = rootNode.path("recordId").asLong(); |
|||
JsonNode requestDataNode = rootNode.path("requestData"); |
|||
// 3. 验证导出记录 |
|||
AiEmotionExportRecordVO record = validateExportRecord(recordId); |
|||
if (record == null) return null; |
|||
//4. 更新状态为处理中 |
|||
aiEmotionService.updateStatus(recordId, 1, "", "", 0); |
|||
// 5. 准备Excel文件 |
|||
fileName = record.getFileName(); |
|||
// 初始化临时文件(保存到本地临时目录) |
|||
tempFile = File.createTempFile("export_", ".xlsx"); |
|||
outputStream = new FileOutputStream(tempFile); // 使用文件输出流 |
|||
// 从JSON中提取单个值 |
|||
String text = requestDataNode.has("text") ? requestDataNode.get("text").asText() : null; |
|||
Integer sort = requestDataNode.has("sort") ? requestDataNode.get("sort").asInt() : null; |
|||
String field = requestDataNode.has("field") ? requestDataNode.get("field").asText() : null; |
|||
String deptId = requestDataNode.has("deptId") ? requestDataNode.get("deptId").asText() : null; |
|||
|
|||
try { |
|||
// 6. 初始化Excel写入器(指向本地文件流) |
|||
excelWriter = initExcelWriter(outputStream, "user"); |
|||
WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build(); |
|||
// 7. 分页查询并写入数据 |
|||
Page page = new Page(); |
|||
page.setPageNum(1); |
|||
page.setPageSize(1000); |
|||
Integer totalCount = 0; |
|||
boolean hasMore = true; |
|||
while (hasMore) { |
|||
Result pageResult = goldDetailController.getGoldDetail(page); |
|||
Integer code = pageResult.getCode(); |
|||
Object data = pageResult.getData(); |
|||
if (code == 200) { |
|||
Map<String, Object> rawData = (Map<String, Object>) data; |
|||
Long total = (Long) rawData.get("total"); |
|||
List<Map<String, Object>> list = (List<Map<String, Object>>) rawData.get("list"); |
|||
// 检查是否还有数据 |
|||
if (list == null || list.isEmpty()) { |
|||
hasMore = false; |
|||
} else { |
|||
// 写入数据(注意:finish()应在所有数据写入后调用) |
|||
excelWriter.write(list, writeSheet); |
|||
page.setPageNum(page.getPageNum() + 1); |
|||
totalCount += list.size(); |
|||
log.info("导出进度 recordId: {}, 已处理: {}条", recordId, totalCount); |
|||
// 检查是否还有更多数据 |
|||
hasMore = totalCount < total; |
|||
} |
|||
} else { |
|||
hasMore = false; |
|||
log.error("获取数据失败,状态码: {}", code); |
|||
} |
|||
} |
|||
// 7. 完成Excel写入(所有数据写入后关闭写入器) |
|||
if (excelWriter != null) { |
|||
excelWriter.finish(); |
|||
} |
|||
if (outputStream != null) { |
|||
outputStream.flush(); // 确保所有数据写入 |
|||
outputStream.close(); // 关闭文件流 |
|||
} |
|||
// 检查文件是否存在且不为空 |
|||
if (tempFile != null && tempFile.exists() && tempFile.length() > 0) { |
|||
// 8. 上传到OSS(读取本地临时文件) |
|||
// 获取接口的基础 URL |
|||
String uploadUrl = "http://39.101.133.168:8828/hljw/api/aws/upload"; |
|||
try { |
|||
// 1. 创建上传工具实例 |
|||
ExcelUploadUtil uploadUtil = new ExcelUploadUtil(uploadUrl); |
|||
|
|||
// 2. 准备要上传的文件 |
|||
File excelFile = new File(tempFile.toURI()); |
|||
try { |
|||
// 3. 执行上传 |
|||
String result = uploadUtil.uploadExcel(excelFile, "export/excel/"); |
|||
// 1. 解析JSON任务 |
|||
JsonNode uploadResult = objectMapper.readTree(result); |
|||
long code = uploadResult.path("code").asLong(); |
|||
String url = String.valueOf(uploadResult.path("data")); |
|||
url = url.replace("\"", ""); |
|||
if (code == 1) { |
|||
// 3. 验证导出记录decodecode |
|||
aiEmotionService.updateStatus(recordId, 2, url, "", totalCount); |
|||
} else { |
|||
//更新失败 |
|||
aiEmotionService.updateStatus(recordId, 3, "", url, 0); |
|||
} |
|||
} catch (Exception e) { |
|||
//更新失败 |
|||
aiEmotionService.updateStatus(recordId, 3, "", StringUtils.substring(e.getMessage(), 0, 500), 0); |
|||
throw new Exception("文件上传云端失败1", e); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("上传文件失败 recordId: {}, 文件名: {}", recordId, fileName, e); |
|||
//更新状态为失败 |
|||
if (recordId != null) { |
|||
aiEmotionService.updateStatus(recordId, 3, "", StringUtils.substring(e.getMessage(), 0, 500), 0); |
|||
} |
|||
throw new Exception("文件上传云端失败2", e); |
|||
} |
|||
} else { |
|||
throw new Exception("导出的Excel文件不存在或为空"); |
|||
} |
|||
|
|||
} catch (Exception e) { |
|||
System.out.println("导出异常" + e.getMessage()); |
|||
log.error("导出任务处理失败 recordId: {}", recordId, e); |
|||
// 更新状态为失败 |
|||
if (recordId != null) { |
|||
aiEmotionService.updateStatus(recordId, 3, "", StringUtils.substring(e.getMessage(), 0, 500), 0); |
|||
} |
|||
throw new Exception("导出异常", e); |
|||
} finally { |
|||
// 确保资源被关闭 |
|||
try { |
|||
if (excelWriter != null) { |
|||
excelWriter.finish(); |
|||
} |
|||
if (outputStream != null) { |
|||
outputStream.close(); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("关闭资源失败", e); |
|||
throw new Exception("excel文件关闭资源失败", e); |
|||
} |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("导出任务处理失败 recordId: {}", recordId, e); |
|||
// 更新状态为失败 |
|||
if (recordId != null) { |
|||
aiEmotionService.updateStatus(recordId, 3, "", StringUtils.substring(e.getMessage(), 0, 500), 0); |
|||
} |
|||
System.out.println("<导出失败>" + e.getMessage()); |
|||
throw new Exception("导出任务处理失败", e); |
|||
} finally { |
|||
// 清理临时文件 |
|||
if (tempFile != null && tempFile.exists()) { |
|||
try { |
|||
if (tempFile.delete()) { |
|||
log.info("临时文件已删除: {}", tempFile.getAbsolutePath()); |
|||
} else { |
|||
log.warn("无法删除临时文件: {}", tempFile.getAbsolutePath()); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("删除临时文件失败", e.getMessage()); |
|||
throw new Exception("删除临时文件失败", e); |
|||
} |
|||
} |
|||
long endTime = System.currentTimeMillis(); |
|||
log.info("导出任务完成,耗时: {}毫秒", (endTime - startTime)); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 验证导出记录 |
|||
*/ |
|||
private AiEmotionExportRecordVO validateExportRecord(Long recordId) throws Exception { |
|||
AiEmotionExportRecordVO record = aiEmotionService.getRecordById(recordId); |
|||
AbstractLog log = null; |
|||
if (record == null) { |
|||
log.error("导出记录不存在 recordId: {}", recordId); |
|||
return null; |
|||
} |
|||
|
|||
// 检查是否已经处理过 |
|||
if (record.getState() != 0) { |
|||
log.warn("导出记录已处理 recordId: {}, status: {}", recordId, record.getState()); |
|||
return null; |
|||
} |
|||
return record; |
|||
} |
|||
|
|||
/** |
|||
* 初始化excel文件 |
|||
* @param os |
|||
* @param exportType |
|||
* @return |
|||
*/ |
|||
private ExcelWriter initExcelWriter(OutputStream os, String exportType) { |
|||
switch (exportType) { |
|||
case "user": |
|||
return EasyExcel.write(os, Goldmingxi.class) |
|||
.inMemory(Boolean.TRUE) |
|||
.build(); |
|||
default: |
|||
throw new IllegalArgumentException("不支持的导出类型: " + exportType); |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,17 @@ |
|||
<?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.example.demo.mapper.AiEmotionMapper"> |
|||
<update id="updateStatus"> |
|||
UPDATE admin_export_record |
|||
<set> |
|||
<if test="state != null">state = #{state},</if> |
|||
<if test="url != null and url != ''">url = #{url},</if> |
|||
<if test="reason != null and reason != ''">reason = #{reason},</if> |
|||
<if test="dataNum != null and dataNum != ''">data_num = #{dataNum},</if> |
|||
</set> |
|||
WHERE id = #{recordId} |
|||
</update> |
|||
<select id="getRecordById" resultType="com.example.demo.domain.vo.AiEmotionExportRecordVO"> |
|||
SELECT id, file_name, state, url FROM admin_export_record WHERE id = #{recordId} |
|||
</select> |
|||
</mapper> |
@ -0,0 +1,18 @@ |
|||
<?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.example.demo.mapper.ExportMapper"> |
|||
<update id="updateExportData"> |
|||
UPDATE export |
|||
<set> |
|||
<if test="state != null">state = #{state},</if> |
|||
<if test="url != null and url != ''">url = #{url},</if> |
|||
<if test="reason != null and reason != ''">reason = #{reason},</if> |
|||
<if test="dataNum != null and dataNum != ''">data_num = #{dataNum},</if> |
|||
</set> |
|||
WHERE id = #{recordId} |
|||
</update> |
|||
|
|||
<select id="getExportData" resultType="com.example.demo.domain.vo.ExportVo"> |
|||
select id,file_name,url,state from export where id=#{recordId} |
|||
</select> |
|||
</mapper> |
@ -0,0 +1,8 @@ |
|||
<?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.example.demo.mapper.UrlMapper"> |
|||
|
|||
<select id="selectBaseUrl" resultType="java.lang.String"> |
|||
select value from env where `key` = #{key} |
|||
</select> |
|||
</mapper> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue