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

424 lines
13 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
* 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 {
/** 资源IDUUID */
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);
}
}