refactor: 访问结果内容改用七牛云/本地存储,与 config 文件存储方式一致
This commit is contained in:
@@ -74,15 +74,15 @@ public class AccessResultController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取单个访问结果详情(含完整内容)
|
* 获取单个访问结果详情(含完整内容,从存储读取)
|
||||||
*/
|
*/
|
||||||
@GetMapping("/api/v1/admin/access-results/{id}")
|
@GetMapping("/api/v1/admin/access-results/{id}")
|
||||||
public ApiResponse<AccessResultDTO> getById(@PathVariable("id") Long id) {
|
public ApiResponse<AccessResultDTO> getById(@PathVariable("id") Long id) {
|
||||||
AccessResult result = accessResultService.getById(id);
|
AccessResultDTO dto = accessResultService.getDetailById(id);
|
||||||
if (result == null) {
|
if (dto == null) {
|
||||||
return ApiResponse.error(404, "访问结果不存在");
|
return ApiResponse.error(404, "访问结果不存在");
|
||||||
}
|
}
|
||||||
return ApiResponse.success(accessResultService.toDTO(result));
|
return ApiResponse.success(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public class AccessResultDTO {
|
|||||||
private String configVersion;
|
private String configVersion;
|
||||||
private String clientId;
|
private String clientId;
|
||||||
private String contentType;
|
private String contentType;
|
||||||
|
private String storagePath;
|
||||||
private String content;
|
private String content;
|
||||||
private String url;
|
private String url;
|
||||||
private Integer statusCode;
|
private Integer statusCode;
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ public class AccessResult {
|
|||||||
@TableField("content_type")
|
@TableField("content_type")
|
||||||
private String contentType;
|
private String contentType;
|
||||||
|
|
||||||
@TableField("content")
|
@TableField("storage_path")
|
||||||
private String content;
|
private String storagePath;
|
||||||
|
|
||||||
@TableField("url")
|
@TableField("url")
|
||||||
private String url;
|
private String url;
|
||||||
|
|||||||
@@ -11,26 +11,26 @@ import java.util.List;
|
|||||||
public interface AccessResultService {
|
public interface AccessResultService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交访问结果(mediabot 调用)
|
* 提交访问结果(mediabot 调用),内容存储到七牛云/本地
|
||||||
*/
|
*/
|
||||||
AccessResult submit(String siteId, String configVersion, String clientId,
|
AccessResult submit(String siteId, String configVersion, String clientId,
|
||||||
String contentType, String content, String url,
|
String contentType, String content, String url,
|
||||||
Integer statusCode, Integer responseTimeMs, Long submitterId);
|
Integer statusCode, Integer responseTimeMs, Long submitterId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有访问结果
|
* 获取所有访问结果(不含内容)
|
||||||
*/
|
*/
|
||||||
List<AccessResultDTO> listAll();
|
List<AccessResultDTO> listAll();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取指定站点的访问结果
|
* 获取指定站点的访问结果(不含内容)
|
||||||
*/
|
*/
|
||||||
List<AccessResultDTO> listBySiteId(String siteId);
|
List<AccessResultDTO> listBySiteId(String siteId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取单个访问结果详情
|
* 获取单个访问结果(从存储读取内容)
|
||||||
*/
|
*/
|
||||||
AccessResult getById(Long id);
|
AccessResultDTO getDetailById(Long id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 标记验证结果(有效/无效)
|
* 标记验证结果(有效/无效)
|
||||||
@@ -38,7 +38,7 @@ public interface AccessResultService {
|
|||||||
AccessResult validate(Long id, Boolean isValid, String note);
|
AccessResult validate(Long id, Boolean isValid, String note);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实体转 DTO
|
* 实体转 DTO(不含内容)
|
||||||
*/
|
*/
|
||||||
AccessResultDTO toDTO(AccessResult entity);
|
AccessResultDTO toDTO(AccessResult entity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import com.par.core.dto.AccessResultDTO;
|
|||||||
import com.par.core.entity.AccessResult;
|
import com.par.core.entity.AccessResult;
|
||||||
import com.par.core.mapper.AccessResultMapper;
|
import com.par.core.mapper.AccessResultMapper;
|
||||||
import com.par.core.service.AccessResultService;
|
import com.par.core.service.AccessResultService;
|
||||||
|
import com.par.core.storage.CachedStorageService;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@@ -17,24 +19,32 @@ import java.util.stream.Collectors;
|
|||||||
public class AccessResultServiceImpl implements AccessResultService {
|
public class AccessResultServiceImpl implements AccessResultService {
|
||||||
|
|
||||||
private final AccessResultMapper mapper;
|
private final AccessResultMapper mapper;
|
||||||
|
private final CachedStorageService storageService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccessResult submit(String siteId, String configVersion, String clientId,
|
public AccessResult submit(String siteId, String configVersion, String clientId,
|
||||||
String contentType, String content, String url,
|
String contentType, String content, String url,
|
||||||
Integer statusCode, Integer responseTimeMs, Long submitterId) {
|
Integer statusCode, Integer responseTimeMs, Long submitterId) {
|
||||||
|
String type = (contentType != null && !contentType.isBlank()) ? contentType : "html";
|
||||||
|
String ext = "json".equalsIgnoreCase(type) ? "json" : "html";
|
||||||
|
String storagePath = String.format("access-results/%s/%d_%s.%s",
|
||||||
|
siteId, System.currentTimeMillis(), UUID.randomUUID().toString().substring(0, 8), ext);
|
||||||
|
|
||||||
|
// 存储到抽象存储层(七牛云或本地)
|
||||||
|
storageService.store(storagePath, content);
|
||||||
|
|
||||||
AccessResult result = new AccessResult();
|
AccessResult result = new AccessResult();
|
||||||
result.setSiteId(siteId);
|
result.setSiteId(siteId);
|
||||||
result.setConfigVersion(configVersion);
|
result.setConfigVersion(configVersion);
|
||||||
result.setClientId(clientId);
|
result.setClientId(clientId);
|
||||||
result.setContentType(contentType != null ? contentType : "html");
|
result.setContentType(type);
|
||||||
result.setContent(content);
|
result.setStoragePath(storagePath);
|
||||||
result.setUrl(url);
|
result.setUrl(url);
|
||||||
result.setStatusCode(statusCode);
|
result.setStatusCode(statusCode);
|
||||||
result.setResponseTimeMs(responseTimeMs);
|
result.setResponseTimeMs(responseTimeMs);
|
||||||
result.setSubmitterId(submitterId != null ? submitterId : 0L);
|
result.setSubmitterId(submitterId != null ? submitterId : 0L);
|
||||||
mapper.insert(result);
|
mapper.insert(result);
|
||||||
log.info("Access result submitted: siteId={}, version={}, clientId={}, id={}",
|
log.info("Access result submitted: siteId={}, version={}, clientId={}, id={}", siteId, configVersion, clientId, result.getId());
|
||||||
siteId, configVersion, clientId, result.getId());
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,8 +59,18 @@ public class AccessResultServiceImpl implements AccessResultService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccessResult getById(Long id) {
|
public AccessResultDTO getDetailById(Long id) {
|
||||||
return mapper.selectById(id);
|
AccessResult result = mapper.selectById(id);
|
||||||
|
if (result == null) return null;
|
||||||
|
AccessResultDTO dto = toDTO(result);
|
||||||
|
// 从存储读取内容
|
||||||
|
try {
|
||||||
|
dto.setContent(storageService.read(result.getStoragePath()));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("读取访问结果内容失败 id={} path={}: {}", id, result.getStoragePath(), e.getMessage());
|
||||||
|
dto.setContent("读取内容失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -73,7 +93,7 @@ public class AccessResultServiceImpl implements AccessResultService {
|
|||||||
dto.setConfigVersion(entity.getConfigVersion());
|
dto.setConfigVersion(entity.getConfigVersion());
|
||||||
dto.setClientId(entity.getClientId());
|
dto.setClientId(entity.getClientId());
|
||||||
dto.setContentType(entity.getContentType());
|
dto.setContentType(entity.getContentType());
|
||||||
dto.setContent(entity.getContent());
|
dto.setStoragePath(entity.getStoragePath());
|
||||||
dto.setUrl(entity.getUrl());
|
dto.setUrl(entity.getUrl());
|
||||||
dto.setStatusCode(entity.getStatusCode());
|
dto.setStatusCode(entity.getStatusCode());
|
||||||
dto.setResponseTimeMs(entity.getResponseTimeMs());
|
dto.setResponseTimeMs(entity.getResponseTimeMs());
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ CREATE TABLE access_results (
|
|||||||
config_version VARCHAR(20) COMMENT '使用的配置版本号',
|
config_version VARCHAR(20) COMMENT '使用的配置版本号',
|
||||||
client_id VARCHAR(128) COMMENT '提交者标识(mediabot 客户端 ID)',
|
client_id VARCHAR(128) COMMENT '提交者标识(mediabot 客户端 ID)',
|
||||||
content_type VARCHAR(10) NOT NULL DEFAULT 'html' COMMENT '数据类型:html / json',
|
content_type VARCHAR(10) NOT NULL DEFAULT 'html' COMMENT '数据类型:html / json',
|
||||||
content MEDIUMTEXT NOT NULL COMMENT '访问站点返回的内容',
|
storage_path VARCHAR(500) NOT NULL COMMENT '内容存储路径(七牛云/本地)',
|
||||||
url VARCHAR(500) COMMENT '访问的站点 URL',
|
url VARCHAR(500) COMMENT '访问的站点 URL',
|
||||||
status_code INT COMMENT 'HTTP 状态码',
|
status_code INT COMMENT 'HTTP 状态码',
|
||||||
response_time_ms INT COMMENT '响应耗时(毫秒)',
|
response_time_ms INT COMMENT '响应耗时(毫秒)',
|
||||||
|
|||||||
@@ -157,9 +157,16 @@ const fetchAll = async () => {
|
|||||||
finally { loading.value = false }
|
finally { loading.value = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleView = (row) => {
|
const handleView = async (row) => {
|
||||||
detail.value = { ...row }
|
// 详情需从服务端获取完整内容
|
||||||
detailVisible.value = true
|
detailVisible.value = true
|
||||||
|
try {
|
||||||
|
const r = await api.accessResults.get(row.id)
|
||||||
|
detail.value = r.data || {}
|
||||||
|
} catch {
|
||||||
|
ElMessage.error('获取详情失败')
|
||||||
|
detail.value = { ...row }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleValidate = async (row, isValid) => {
|
const handleValidate = async (row, isValid) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user