feat: 新增适配验证接口,mediabot 可提交站点访问结果,管理员可校验适配是否正确
This commit is contained in:
@@ -33,6 +33,7 @@ public class WebConfig implements WebMvcConfigurer {
|
||||
// HMAC 鉴权拦截器:拦截所有写入端点
|
||||
registry.addInterceptor(hmacAuthInterceptor)
|
||||
.addPathPatterns("/api/v1/configs/*/submit", "/api/v1/sites/*/submit",
|
||||
"/api/v1/sites/*/access-result",
|
||||
"/api/v1/admin/**", "/api/v1/account/**");
|
||||
}
|
||||
}
|
||||
|
||||
26
par-core/src/main/java/com/par/core/dto/AccessResultDTO.java
Normal file
26
par-core/src/main/java/com/par/core/dto/AccessResultDTO.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package com.par.core.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 站点访问结果 DTO
|
||||
*/
|
||||
@Data
|
||||
public class AccessResultDTO {
|
||||
|
||||
private Long id;
|
||||
private String siteId;
|
||||
private String configVersion;
|
||||
private String clientId;
|
||||
private String contentType;
|
||||
private String content;
|
||||
private String url;
|
||||
private Integer statusCode;
|
||||
private Integer responseTimeMs;
|
||||
private Boolean isValid;
|
||||
private String validationNote;
|
||||
private Long submitterId;
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
53
par-core/src/main/java/com/par/core/entity/AccessResult.java
Normal file
53
par-core/src/main/java/com/par/core/entity/AccessResult.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package com.par.core.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.*;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 站点访问结果实体(mediabot 提交访问站点返回的 HTML/JSON 数据)
|
||||
*/
|
||||
@Data
|
||||
@TableName("access_results")
|
||||
public class AccessResult {
|
||||
|
||||
@TableId(type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
@TableField("site_id")
|
||||
private String siteId;
|
||||
|
||||
@TableField("config_version")
|
||||
private String configVersion;
|
||||
|
||||
@TableField("client_id")
|
||||
private String clientId;
|
||||
|
||||
@TableField("content_type")
|
||||
private String contentType;
|
||||
|
||||
@TableField("content")
|
||||
private String content;
|
||||
|
||||
@TableField("url")
|
||||
private String url;
|
||||
|
||||
@TableField("status_code")
|
||||
private Integer statusCode;
|
||||
|
||||
@TableField("response_time_ms")
|
||||
private Integer responseTimeMs;
|
||||
|
||||
@TableField("is_valid")
|
||||
private Boolean isValid;
|
||||
|
||||
@TableField("validation_note")
|
||||
private String validationNote;
|
||||
|
||||
@TableField("submitter_id")
|
||||
private Long submitterId;
|
||||
|
||||
@TableField(value = "created_at", fill = FieldFill.INSERT)
|
||||
private LocalDateTime createdAt;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.par.core.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.par.core.entity.AccessResult;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 站点访问结果 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface AccessResultMapper extends BaseMapper<AccessResult> {
|
||||
|
||||
@Select("SELECT * FROM access_results WHERE site_id = #{siteId} ORDER BY created_at DESC")
|
||||
List<AccessResult> selectBySiteId(@Param("siteId") String siteId);
|
||||
|
||||
@Select("SELECT * FROM access_results ORDER BY created_at DESC")
|
||||
List<AccessResult> selectAll();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.par.core.service;
|
||||
|
||||
import com.par.core.dto.AccessResultDTO;
|
||||
import com.par.core.entity.AccessResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 站点访问结果服务接口
|
||||
*/
|
||||
public interface AccessResultService {
|
||||
|
||||
/**
|
||||
* 提交访问结果(mediabot 调用)
|
||||
*/
|
||||
AccessResult submit(String siteId, String configVersion, String clientId,
|
||||
String contentType, String content, String url,
|
||||
Integer statusCode, Integer responseTimeMs, Long submitterId);
|
||||
|
||||
/**
|
||||
* 获取所有访问结果
|
||||
*/
|
||||
List<AccessResultDTO> listAll();
|
||||
|
||||
/**
|
||||
* 获取指定站点的访问结果
|
||||
*/
|
||||
List<AccessResultDTO> listBySiteId(String siteId);
|
||||
|
||||
/**
|
||||
* 获取单个访问结果详情
|
||||
*/
|
||||
AccessResult getById(Long id);
|
||||
|
||||
/**
|
||||
* 标记验证结果(有效/无效)
|
||||
*/
|
||||
AccessResult validate(Long id, Boolean isValid, String note);
|
||||
|
||||
/**
|
||||
* 实体转 DTO
|
||||
*/
|
||||
AccessResultDTO toDTO(AccessResult entity);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.par.core.service.impl;
|
||||
|
||||
import com.par.core.dto.AccessResultDTO;
|
||||
import com.par.core.entity.AccessResult;
|
||||
import com.par.core.mapper.AccessResultMapper;
|
||||
import com.par.core.service.AccessResultService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class AccessResultServiceImpl implements AccessResultService {
|
||||
|
||||
private final AccessResultMapper mapper;
|
||||
|
||||
@Override
|
||||
public AccessResult submit(String siteId, String configVersion, String clientId,
|
||||
String contentType, String content, String url,
|
||||
Integer statusCode, Integer responseTimeMs, Long submitterId) {
|
||||
AccessResult result = new AccessResult();
|
||||
result.setSiteId(siteId);
|
||||
result.setConfigVersion(configVersion);
|
||||
result.setClientId(clientId);
|
||||
result.setContentType(contentType != null ? contentType : "html");
|
||||
result.setContent(content);
|
||||
result.setUrl(url);
|
||||
result.setStatusCode(statusCode);
|
||||
result.setResponseTimeMs(responseTimeMs);
|
||||
result.setSubmitterId(submitterId != null ? submitterId : 0L);
|
||||
mapper.insert(result);
|
||||
log.info("Access result submitted: siteId={}, version={}, clientId={}, id={}",
|
||||
siteId, configVersion, clientId, result.getId());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessResultDTO> listAll() {
|
||||
return mapper.selectAll().stream().map(this::toDTO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AccessResultDTO> listBySiteId(String siteId) {
|
||||
return mapper.selectBySiteId(siteId).stream().map(this::toDTO).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessResult getById(Long id) {
|
||||
return mapper.selectById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessResult validate(Long id, Boolean isValid, String note) {
|
||||
AccessResult result = mapper.selectById(id);
|
||||
if (result == null) throw new IllegalArgumentException("访问结果不存在: " + id);
|
||||
result.setIsValid(isValid);
|
||||
result.setValidationNote(note);
|
||||
mapper.updateById(result);
|
||||
log.info("Access result validated: id={}, isValid={}", id, isValid);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessResultDTO toDTO(AccessResult entity) {
|
||||
if (entity == null) return null;
|
||||
AccessResultDTO dto = new AccessResultDTO();
|
||||
dto.setId(entity.getId());
|
||||
dto.setSiteId(entity.getSiteId());
|
||||
dto.setConfigVersion(entity.getConfigVersion());
|
||||
dto.setClientId(entity.getClientId());
|
||||
dto.setContentType(entity.getContentType());
|
||||
dto.setContent(entity.getContent());
|
||||
dto.setUrl(entity.getUrl());
|
||||
dto.setStatusCode(entity.getStatusCode());
|
||||
dto.setResponseTimeMs(entity.getResponseTimeMs());
|
||||
dto.setIsValid(entity.getIsValid());
|
||||
dto.setValidationNote(entity.getValidationNote());
|
||||
dto.setSubmitterId(entity.getSubmitterId());
|
||||
dto.setCreatedAt(entity.getCreatedAt());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
-- PAR Flyway V10: 站点访问结果表(mediabot 提交访问站点返回的 HTML/JSON 数据,用于验证适配是否正确)
|
||||
CREATE TABLE access_results (
|
||||
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||
site_id VARCHAR(64) NOT NULL COMMENT '站点ID',
|
||||
config_version VARCHAR(20) COMMENT '使用的配置版本号',
|
||||
client_id VARCHAR(128) COMMENT '提交者标识(mediabot 客户端 ID)',
|
||||
content_type VARCHAR(10) NOT NULL DEFAULT 'html' COMMENT '数据类型:html / json',
|
||||
content MEDIUMTEXT NOT NULL COMMENT '访问站点返回的内容',
|
||||
url VARCHAR(500) COMMENT '访问的站点 URL',
|
||||
status_code INT COMMENT 'HTTP 状态码',
|
||||
response_time_ms INT COMMENT '响应耗时(毫秒)',
|
||||
is_valid TINYINT(1) COMMENT '适配是否有效(null=未验证, 1=有效, 0=无效)',
|
||||
validation_note VARCHAR(500) COMMENT '管理员验证备注',
|
||||
submitter_id BIGINT UNSIGNED COMMENT '提交者账户 ID(PSK 提交时为 0)',
|
||||
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '提交时间',
|
||||
PRIMARY KEY (id),
|
||||
KEY idx_site_id (site_id),
|
||||
KEY idx_created (created_at),
|
||||
KEY idx_valid (is_valid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='站点访问结果表';
|
||||
Reference in New Issue
Block a user