424 lines
13 KiB
Java
424 lines
13 KiB
Java
/*
|
||
* 自然写教学资源管理与内容分发系统软件 V1.0
|
||
* model/Resource.java - 资源数据模型
|
||
* model/DotPattern.java - 点阵码模型
|
||
* model/AuditRecord.java - 审核记录模型
|
||
* config/OssConfig.java - OSS对象存储配置
|
||
* config/ElasticsearchConfig.java - ES配置
|
||
* security/ResourceSecurity.java - 资源安全(防盗链+水印)
|
||
*/
|
||
package com.writech.resource.model;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* 资源数据模型(对应MySQL resource表)
|
||
*/
|
||
public class Resource {
|
||
|
||
/** 资源ID(UUID) */
|
||
private String id;
|
||
|
||
/** 资源名称 */
|
||
private String name;
|
||
|
||
/** 资源描述 */
|
||
private String description;
|
||
|
||
/** 资源类型(COURSEWARE/COPYBOOK/EXAM_PAPER/TEMPLATE/DOT_CODE/VIDEO) */
|
||
private String type;
|
||
|
||
/** 学科 */
|
||
private String subject;
|
||
|
||
/** 适用年级 */
|
||
private String grade;
|
||
|
||
/** 出版社 */
|
||
private String publisher;
|
||
|
||
/** 版本号 */
|
||
private String version;
|
||
|
||
/** 审核状态(PENDING/APPROVED/REJECTED/WITHDRAWN) */
|
||
private String auditStatus;
|
||
|
||
/** OSS文件存储Key */
|
||
private String fileKey;
|
||
|
||
/** 文件大小(字节) */
|
||
private long fileSize;
|
||
|
||
/** MIME类型 */
|
||
private String mimeType;
|
||
|
||
/** 缩略图URL */
|
||
private String thumbnailUrl;
|
||
|
||
/** 上传者ID */
|
||
private String uploaderId;
|
||
|
||
/** 上传者姓名 */
|
||
private String uploaderName;
|
||
|
||
/** 所属学校ID */
|
||
private String schoolId;
|
||
|
||
/** 标签(逗号分隔) */
|
||
private String tags;
|
||
|
||
/** 下载次数 */
|
||
private int downloadCount;
|
||
|
||
/** 使用次数 */
|
||
private int useCount;
|
||
|
||
/** 创建时间 */
|
||
private Date createdAt;
|
||
|
||
/** 更新时间 */
|
||
private Date updatedAt;
|
||
|
||
/** 是否已删除(软删除标记) */
|
||
private boolean deleted;
|
||
|
||
// ============================================================
|
||
// Getter / Setter
|
||
// ============================================================
|
||
|
||
public String getId() { return id; }
|
||
public void setId(String id) { this.id = id; }
|
||
public String getName() { return name; }
|
||
public void setName(String name) { this.name = name; }
|
||
public String getDescription() { return description; }
|
||
public void setDescription(String description) { this.description = description; }
|
||
public String getType() { return type; }
|
||
public void setType(String type) { this.type = type; }
|
||
public String getSubject() { return subject; }
|
||
public void setSubject(String subject) { this.subject = subject; }
|
||
public String getGrade() { return grade; }
|
||
public void setGrade(String grade) { this.grade = grade; }
|
||
public String getPublisher() { return publisher; }
|
||
public void setPublisher(String publisher) { this.publisher = publisher; }
|
||
public String getVersion() { return version; }
|
||
public void setVersion(String version) { this.version = version; }
|
||
public String getAuditStatus() { return auditStatus; }
|
||
public void setAuditStatus(String auditStatus) { this.auditStatus = auditStatus; }
|
||
public String getFileKey() { return fileKey; }
|
||
public void setFileKey(String fileKey) { this.fileKey = fileKey; }
|
||
public long getFileSize() { return fileSize; }
|
||
public void setFileSize(long fileSize) { this.fileSize = fileSize; }
|
||
public String getMimeType() { return mimeType; }
|
||
public void setMimeType(String mimeType) { this.mimeType = mimeType; }
|
||
public String getThumbnailUrl() { return thumbnailUrl; }
|
||
public void setThumbnailUrl(String thumbnailUrl) { this.thumbnailUrl = thumbnailUrl; }
|
||
public String getUploaderId() { return uploaderId; }
|
||
public void setUploaderId(String uploaderId) { this.uploaderId = uploaderId; }
|
||
public String getSchoolId() { return schoolId; }
|
||
public void setSchoolId(String schoolId) { this.schoolId = schoolId; }
|
||
public String getTags() { return tags; }
|
||
public void setTags(String tags) { this.tags = tags; }
|
||
public int getDownloadCount() { return downloadCount; }
|
||
public int getUseCount() { return useCount; }
|
||
public Date getCreatedAt() { return createdAt; }
|
||
public Date getUpdatedAt() { return updatedAt; }
|
||
public boolean isDeleted() { return deleted; }
|
||
public void setDeleted(boolean deleted) { this.deleted = deleted; }
|
||
|
||
@Override
|
||
public String toString() {
|
||
return "Resource{id='" + id + "', name='" + name + "', type='" + type +
|
||
"', subject='" + subject + "', grade='" + grade + "'}";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 点阵码模型(对应MySQL dot_pattern表 + OSS文件)
|
||
*/
|
||
class DotPattern {
|
||
|
||
/** 点阵码ID(全局唯一) */
|
||
private long dotCodeId;
|
||
|
||
/** 关联的资源ID */
|
||
private String resourceId;
|
||
|
||
/** 页面序号 */
|
||
private int pageIndex;
|
||
|
||
/** 区域类型 */
|
||
private String areaType;
|
||
|
||
/** 区域坐标和尺寸(mm) */
|
||
private double areaX;
|
||
private double areaY;
|
||
private double areaWidth;
|
||
private double areaHeight;
|
||
|
||
/** 点阵码图案文件OSS Key */
|
||
private String patternFileKey;
|
||
|
||
/** 生成参数JSON */
|
||
private String generateParams;
|
||
|
||
/** 状态(ACTIVE/REVOKED) */
|
||
private String status;
|
||
|
||
/** 创建时间 */
|
||
private Date createdAt;
|
||
|
||
public long getDotCodeId() { return dotCodeId; }
|
||
public void setDotCodeId(long id) { this.dotCodeId = id; }
|
||
public String getResourceId() { return resourceId; }
|
||
public void setResourceId(String rid) { this.resourceId = rid; }
|
||
public int getPageIndex() { return pageIndex; }
|
||
public void setPageIndex(int idx) { this.pageIndex = idx; }
|
||
public String getAreaType() { return areaType; }
|
||
public void setAreaType(String type) { this.areaType = type; }
|
||
public double getAreaX() { return areaX; }
|
||
public double getAreaY() { return areaY; }
|
||
public double getAreaWidth() { return areaWidth; }
|
||
public double getAreaHeight() { return areaHeight; }
|
||
public String getPatternFileKey() { return patternFileKey; }
|
||
public String getStatus() { return status; }
|
||
public void setStatus(String status) { this.status = status; }
|
||
public Date getCreatedAt() { return createdAt; }
|
||
}
|
||
|
||
/**
|
||
* 审核记录模型(对应MySQL audit_record表)
|
||
*/
|
||
class AuditRecord {
|
||
|
||
/** 记录ID */
|
||
private String id;
|
||
|
||
/** 关联的资源ID */
|
||
private String resourceId;
|
||
|
||
/** 审核人ID */
|
||
private String auditorId;
|
||
|
||
/** 审核人姓名 */
|
||
private String auditorName;
|
||
|
||
/** 审核操作(APPROVE/REJECT/RETURN/WITHDRAW) */
|
||
private String action;
|
||
|
||
/** 审核意见 */
|
||
private String comment;
|
||
|
||
/** 审核前状态 */
|
||
private String preStatus;
|
||
|
||
/** 审核后状态 */
|
||
private String postStatus;
|
||
|
||
/** 审核时间 */
|
||
private Date createdAt;
|
||
|
||
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 getAuditorId() { return auditorId; }
|
||
public void setAuditorId(String aid) { this.auditorId = aid; }
|
||
public String getAction() { return action; }
|
||
public void setAction(String action) { this.action = action; }
|
||
public String getComment() { return comment; }
|
||
public void setComment(String comment) { this.comment = comment; }
|
||
public String getPreStatus() { return preStatus; }
|
||
public String getPostStatus() { return postStatus; }
|
||
public Date getCreatedAt() { return createdAt; }
|
||
}
|
||
|
||
/**
|
||
* OSS对象存储配置
|
||
*
|
||
* 多副本冗余存储(99.99%数据持久性),
|
||
* 支持STS临时凭证直传,生命周期管理。
|
||
*/
|
||
class OssConfig {
|
||
|
||
/** OSS区域端点 */
|
||
private String endpoint;
|
||
|
||
/** 存储桶名称 */
|
||
private String bucketName;
|
||
|
||
/** AccessKey ID */
|
||
private String accessKeyId;
|
||
|
||
/** AccessKey Secret(加密存储) */
|
||
private String accessKeySecret;
|
||
|
||
/** STS角色ARN(用于前端直传临时授权) */
|
||
private String stsRoleArn;
|
||
|
||
/** STS会话名称 */
|
||
private String stsSessionName;
|
||
|
||
/** 资源前缀路径 */
|
||
private String resourcePrefix;
|
||
|
||
/** 缩略图前缀路径 */
|
||
private String thumbnailPrefix;
|
||
|
||
/** 临时文件过期天数 */
|
||
private int tempFileExpireDays;
|
||
|
||
public OssConfig() {
|
||
this.endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
|
||
this.bucketName = "writech-resources";
|
||
this.resourcePrefix = "resources/";
|
||
this.thumbnailPrefix = "thumbnails/";
|
||
this.tempFileExpireDays = 7;
|
||
this.stsSessionName = "writech-upload-session";
|
||
}
|
||
|
||
public String getEndpoint() { return endpoint; }
|
||
public void setEndpoint(String ep) { this.endpoint = ep; }
|
||
public String getBucketName() { return bucketName; }
|
||
public void setBucketName(String name) { this.bucketName = name; }
|
||
public String getAccessKeyId() { return accessKeyId; }
|
||
public void setAccessKeyId(String id) { this.accessKeyId = id; }
|
||
public String getAccessKeySecret() { return accessKeySecret; }
|
||
public void setAccessKeySecret(String secret) { this.accessKeySecret = secret; }
|
||
public String getStsRoleArn() { return stsRoleArn; }
|
||
public void setStsRoleArn(String arn) { this.stsRoleArn = arn; }
|
||
public String getResourcePrefix() { return resourcePrefix; }
|
||
public String getThumbnailPrefix() { return thumbnailPrefix; }
|
||
public int getTempFileExpireDays() { return tempFileExpireDays; }
|
||
}
|
||
|
||
/**
|
||
* Elasticsearch配置
|
||
*
|
||
* ES集群部署,索引按学科/年级分片,
|
||
* 支持IK中文分词器。
|
||
*/
|
||
class ElasticsearchConfig {
|
||
|
||
/** ES集群节点列表 */
|
||
private List<String> nodes;
|
||
|
||
/** 连接超时(毫秒) */
|
||
private int connectTimeout;
|
||
|
||
/** 读取超时(毫秒) */
|
||
private int socketTimeout;
|
||
|
||
/** 索引名称 */
|
||
private String indexName;
|
||
|
||
/** 分片数 */
|
||
private int shards;
|
||
|
||
/** 副本数 */
|
||
private int replicas;
|
||
|
||
/** 用户名(X-Pack安全) */
|
||
private String username;
|
||
|
||
/** 密码 */
|
||
private String password;
|
||
|
||
public ElasticsearchConfig() {
|
||
this.nodes = Arrays.asList("localhost:9200");
|
||
this.connectTimeout = 5000;
|
||
this.socketTimeout = 30000;
|
||
this.indexName = "writech_resources";
|
||
this.shards = 3;
|
||
this.replicas = 1;
|
||
}
|
||
|
||
public List<String> getNodes() { return nodes; }
|
||
public void setNodes(List<String> nodes) { this.nodes = nodes; }
|
||
public int getConnectTimeout() { return connectTimeout; }
|
||
public int getSocketTimeout() { return socketTimeout; }
|
||
public String getIndexName() { return indexName; }
|
||
public int getShards() { return shards; }
|
||
public int getReplicas() { return replicas; }
|
||
public String getUsername() { return username; }
|
||
public void setUsername(String u) { this.username = u; }
|
||
public String getPassword() { return password; }
|
||
public void setPassword(String p) { this.password = p; }
|
||
}
|
||
|
||
/**
|
||
* 资源安全服务
|
||
*
|
||
* 负责:
|
||
* - 防盗链(Referer校验 + 签名URL)
|
||
* - 数字水印(PDF/图片添加学校教师标识水印)
|
||
* - 权限控制(按学校/区域授权)
|
||
* - 点阵码安全(全局唯一分配防冲突)
|
||
*/
|
||
class ResourceSecurity {
|
||
|
||
/** 防盗链Referer白名单 */
|
||
private static final Set<String> REFERER_WHITELIST = new HashSet<>(Arrays.asList(
|
||
"*.writech.com",
|
||
"localhost"
|
||
));
|
||
|
||
/**
|
||
* 校验资源访问权限
|
||
*
|
||
* 规则:
|
||
* - 管理员:可访问本校所有资源
|
||
* - 教师:可访问本校已授权资源
|
||
* - 学生/家长:仅可访问已分配的资源
|
||
*/
|
||
public boolean checkPermission(
|
||
String userId,
|
||
String userRole,
|
||
String userSchoolId,
|
||
String resourceSchoolId
|
||
) {
|
||
// 超级管理员无限制
|
||
if ("super_admin".equals(userRole)) {
|
||
return true;
|
||
}
|
||
|
||
// 校级管理员和教师:必须同校
|
||
if ("admin".equals(userRole) || "teacher".equals(userRole)) {
|
||
return userSchoolId != null && userSchoolId.equals(resourceSchoolId);
|
||
}
|
||
|
||
// 学生/家长:需要额外的资源分配记录校验
|
||
// return resourceAssignmentMapper.isAssigned(userId, resourceId);
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 验证Referer防盗链
|
||
*/
|
||
public boolean validateReferer(String referer) {
|
||
if (referer == null || referer.isEmpty()) {
|
||
return false;
|
||
}
|
||
for (String pattern : REFERER_WHITELIST) {
|
||
if (pattern.startsWith("*.")) {
|
||
String domain = pattern.substring(2);
|
||
if (referer.contains(domain)) return true;
|
||
} else {
|
||
if (referer.contains(pattern)) return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/**
|
||
* 生成水印文本
|
||
* 格式:学校名称 + 教师姓名 + 日期
|
||
*/
|
||
public String generateWatermarkText(
|
||
String schoolName, String teacherName
|
||
) {
|
||
String dateStr = new java.text.SimpleDateFormat("yyyy-MM-dd")
|
||
.format(new Date());
|
||
return String.format("%s %s %s", schoolName, teacherName, dateStr);
|
||
}
|
||
}
|