Files
2026-03-22 15:24:40 +08:00

343 lines
12 KiB
Java
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 自然写教学资源管理与内容分发系统软件 V1.0
* service/AuditService.java - 内容审核服务
*/
package com.writech.resource.service;
import java.util.*;
import java.util.logging.Logger;
/**
* 内容审核服务
*
* 教师上传的资源需经过管理员审核后才能被其他用户检索和使用。
* 审核流程支持:
* - 自动预审(AI内容安全检测)
* - 人工审核(管理员审核通过/驳回/退回修改)
* - 审核记录全程留痕
* - 批量审核
*/
public class AuditService {
private static final Logger logger =
Logger.getLogger(AuditService.class.getName());
// ============================================================
// 审核数据模型
// ============================================================
/** 审核操作类型 */
public enum AuditAction {
APPROVE, // 审核通过
REJECT, // 驳回
RETURN, // 退回修改
WITHDRAW // 上传者撤回
}
/** 审核记录(对应MySQL audit_record表) */
public static class AuditRecord {
private String id;
private String resourceId;
private String resourceName;
private String auditorId; // 审核人ID
private String auditorName; // 审核人姓名
private AuditAction action;
private String comment; // 审核意见
private String preStatus; // 审核前状态
private String postStatus; // 审核后状态
private Date createdAt;
// Getter/Setter
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getResourceId() { return resourceId; }
public void setResourceId(String rid) { this.resourceId = rid; }
public String getResourceName() { return resourceName; }
public void setResourceName(String name) { this.resourceName = name; }
public String getAuditorId() { return auditorId; }
public void setAuditorId(String id) { this.auditorId = id; }
public AuditAction getAction() { return action; }
public void setAction(AuditAction action) { this.action = action; }
public String getComment() { return comment; }
public void setComment(String comment) { this.comment = comment; }
public Date getCreatedAt() { return createdAt; }
}
/** 审核请求 */
public static class AuditRequest {
private String resourceId;
private AuditAction action;
private String comment;
private String auditorId;
public String getResourceId() { return resourceId; }
public void setResourceId(String id) { this.resourceId = id; }
public AuditAction getAction() { return action; }
public void setAction(AuditAction action) { this.action = action; }
public String getComment() { return comment; }
public void setComment(String c) { this.comment = c; }
public String getAuditorId() { return auditorId; }
public void setAuditorId(String id) { this.auditorId = id; }
}
/** 自动预审结果 */
public static class PreAuditResult {
private boolean safe;
private double safeScore; // 安全评分(0-1
private List<String> warnings; // 警告信息
private String category; // 内容分类
public PreAuditResult(boolean safe, double score) {
this.safe = safe;
this.safeScore = score;
this.warnings = new ArrayList<>();
}
public boolean isSafe() { return safe; }
public double getSafeScore() { return safeScore; }
public List<String> getWarnings() { return warnings; }
public void addWarning(String w) { this.warnings.add(w); }
}
// ============================================================
// 审核业务方法
// ============================================================
/**
* 执行审核操作 PUT /api/v1/resource/audit/{id}
*
* @param request 审核请求
* @return 审核结果
*/
public Map<String, Object> performAudit(AuditRequest request) {
logger.info(String.format(
"执行审核: resource=%s, action=%s, auditor=%s",
request.getResourceId(), request.getAction(), request.getAuditorId()
));
// 查询资源当前状态
// ResourceMetadata resource = resourceMapper.selectById(request.getResourceId());
// if (resource == null) {
// return errorResponse(404, "资源不存在");
// }
// 状态机校验:只有PENDING状态可被审核
// if (resource.getAuditStatus() != AuditStatus.PENDING) {
// return errorResponse(400, "当前状态不可审核");
// }
// 创建审核记录
AuditRecord record = new AuditRecord();
record.setId(UUID.randomUUID().toString().replace("-", ""));
record.setResourceId(request.getResourceId());
record.setAuditorId(request.getAuditorId());
record.setAction(request.getAction());
record.setComment(request.getComment());
record.setPreStatus("PENDING");
// 根据审核动作更新资源状态
String newStatus;
switch (request.getAction()) {
case APPROVE:
newStatus = "APPROVED";
// 审核通过后,同步更新Elasticsearch索引状态
// updateEsAuditStatus(request.getResourceId(), "APPROVED");
// 预热CDN缓存(使资源可被终端下载)
// cdnService.preheatResource(request.getResourceId());
break;
case REJECT:
newStatus = "REJECTED";
break;
case RETURN:
newStatus = "PENDING"; // 退回修改后重新提交
break;
default:
newStatus = "PENDING";
}
record.setPostStatus(newStatus);
// 持久化
// auditRecordMapper.insert(record);
// resourceMapper.updateAuditStatus(request.getResourceId(), newStatus);
// 通知上传者审核结果(消息推送)
// notifyUploader(request.getResourceId(), request.getAction(), request.getComment());
Map<String, Object> result = new HashMap<>();
result.put("code", 0);
result.put("message", "审核操作成功");
result.put("data", Map.of(
"resource_id", request.getResourceId(),
"new_status", newStatus,
"audit_record_id", record.getId()
));
return result;
}
/**
* 批量审核
*
* @param resourceIds 资源ID列表
* @param action 审核动作
* @param comment 审核意见
* @param auditorId 审核人
* @return 批量审核结果
*/
public Map<String, Object> batchAudit(
List<String> resourceIds,
AuditAction action,
String comment,
String auditorId
) {
logger.info(String.format(
"批量审核: count=%d, action=%s", resourceIds.size(), action
));
int successCount = 0;
int failCount = 0;
List<String> failedIds = new ArrayList<>();
for (String resourceId : resourceIds) {
try {
AuditRequest request = new AuditRequest();
request.setResourceId(resourceId);
request.setAction(action);
request.setComment(comment);
request.setAuditorId(auditorId);
Map<String, Object> result = performAudit(request);
if ((int) result.get("code") == 0) {
successCount++;
} else {
failCount++;
failedIds.add(resourceId);
}
} catch (Exception e) {
failCount++;
failedIds.add(resourceId);
logger.warning("批量审核失败: resource=" + resourceId + ", error=" + e.getMessage());
}
}
Map<String, Object> result = new HashMap<>();
result.put("code", 0);
result.put("data", Map.of(
"total", resourceIds.size(),
"success", successCount,
"failed", failCount,
"failed_ids", failedIds
));
return result;
}
/**
* AI自动预审
*
* 在人工审核前,自动进行内容安全检测:
* - 文本内容是否包含违禁词
* - 图片是否包含不当内容
* - 文件格式是否合规
* - 文件大小是否超限
*
* @param resourceId 资源ID
* @return 预审结果
*/
public PreAuditResult performPreAudit(String resourceId) {
logger.info("AI预审: resource=" + resourceId);
PreAuditResult result = new PreAuditResult(true, 1.0);
// 1. 文件格式和大小检查
// ResourceMetadata resource = resourceMapper.selectById(resourceId);
// if (resource.getFileSize() > MAX_FILE_SIZE) {
// result = new PreAuditResult(false, 0.0);
// result.addWarning("文件大小超过限制");
// return result;
// }
// 2. 文本内容安全检测(提取PDF/PPT中的文字进行违禁词检查)
// String textContent = extractTextContent(resource.getFileKey());
// ContentSafetyResult textSafety = contentSafetyApi.checkText(textContent);
// if (!textSafety.isSafe()) {
// result.addWarning("文本内容包含敏感词: " + textSafety.getDetails());
// }
// 3. 图片内容安全检测(提取文档中的图片进行AI审核)
// List<byte[]> images = extractImages(resource.getFileKey());
// for (byte[] image : images) {
// ImageSafetyResult imageSafety = contentSafetyApi.checkImage(image);
// if (!imageSafety.isSafe()) {
// result.addWarning("图片内容不合规: " + imageSafety.getCategory());
// }
// }
// 综合评分
if (!result.getWarnings().isEmpty()) {
double penalty = result.getWarnings().size() * 0.2;
double finalScore = Math.max(0.0, 1.0 - penalty);
result = new PreAuditResult(finalScore >= 0.6, finalScore);
}
logger.info(String.format(
"预审完成: resource=%s, safe=%b, score=%.2f",
resourceId, result.isSafe(), result.getSafeScore()
));
return result;
}
/**
* 查询审核记录列表
*
* @param resourceId 资源ID(可选,为空则查所有)
* @param auditorId 审核人ID(可选)
* @param page 页码
* @param pageSize 每页大小
* @return 审核记录列表
*/
public Map<String, Object> queryAuditRecords(
String resourceId,
String auditorId,
int page,
int pageSize
) {
logger.info(String.format(
"查询审核记录: resource=%s, auditor=%s, page=%d",
resourceId, auditorId, page
));
// List<AuditRecord> records = auditRecordMapper.selectByCondition(
// resourceId, auditorId, page, pageSize
// );
// int total = auditRecordMapper.countByCondition(resourceId, auditorId);
Map<String, Object> result = new HashMap<>();
result.put("code", 0);
result.put("data", Map.of(
"total", 0,
"page", page,
"items", new ArrayList<>()
));
return result;
}
/**
* 获取待审核资源数量(仪表盘统计用)
*/
public Map<String, Object> getAuditStats() {
// int pendingCount = resourceMapper.countByStatus("PENDING");
// int approvedToday = auditRecordMapper.countTodayByAction("APPROVE");
// int rejectedToday = auditRecordMapper.countTodayByAction("REJECT");
Map<String, Object> result = new HashMap<>();
result.put("code", 0);
result.put("data", Map.of(
"pending_count", 0,
"approved_today", 0,
"rejected_today", 0
));
return result;
}
}