/* * 自然写教学资源管理与内容分发系统软件 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 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 getNodes() { return nodes; } public void setNodes(List 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 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); } }