diff --git a/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/ComplianceNotice.java b/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/ComplianceNotice.java new file mode 100644 index 0000000..dcce070 --- /dev/null +++ b/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/ComplianceNotice.java @@ -0,0 +1,27 @@ +// Author: fruit.com.cn + +package com.fruitshorts.framework.compliance; + +/** + * 平台合规声明(法律红线,不可删除或修改)。 + * + *

本软件「水果短剧(FruitShorts)」为正规短剧平台,仅用于承载、分发符合法律法规 + * 与公序良俗的短剧内容。平台严禁且不提供任何用于上传、传播、存储色情、淫秽、露骨或 + * 违法犯罪内容的功能。任何运营方利用本软件从事违法犯罪行为的,其法律责任由运营方独立 + * 承担,与开发者无任何法律关系。 + */ +public final class ComplianceNotice { + + private ComplianceNotice() { + } + + /** 平台定位与合法用途声明。 */ + public static final String PLATFORM_STATEMENT = + "水果短剧(FruitShorts)为正规短剧平台,仅用于合法合规的短剧内容运营," + + "严禁用于任何违法犯罪活动或色情、淫秽、露骨内容的传播。"; + + /** 内容合规红线:用户生成内容发布前必须通过敏感词自动检查。 */ + public static final String CONTENT_RED_LINE = + "所有用户生成内容必须通过敏感词自动检查(BLOCK 级直接拦截)," + + "命中待审词必须进入人工审核,禁止任何违规内容上线。"; +} diff --git a/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/ContentModerationService.java b/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/ContentModerationService.java new file mode 100644 index 0000000..227cf11 --- /dev/null +++ b/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/ContentModerationService.java @@ -0,0 +1,31 @@ +// Author: fruit.com.cn + +package com.fruitshorts.framework.compliance; + +/** + * 图片/视频内容审核抽象(露骨内容、违规画面检测)。 + * 第一阶段默认未接入第三方,接入时新增实现类即可,业务侧不感知。 + */ +public interface ContentModerationService { + + /** 审核结论:通过 / 拦截 / 转人工。 */ + enum Verdict { + PASS, BLOCK, REVIEW + } + + /** + * 审核图片。 + * + * @param imageUrl 图片地址 + * @return 审核结论 + */ + Verdict checkImage(String imageUrl); + + /** + * 审核视频(实现方负责抽帧)。 + * + * @param videoUrl 视频地址 + * @return 审核结论 + */ + Verdict checkVideo(String videoUrl); +} diff --git a/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/DisabledContentModeration.java b/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/DisabledContentModeration.java new file mode 100644 index 0000000..799f17e --- /dev/null +++ b/fruitshorts-framework/src/main/java/com/fruitshorts/framework/compliance/DisabledContentModeration.java @@ -0,0 +1,20 @@ +// Author: fruit.com.cn + +package com.fruitshorts.framework.compliance; + +/** + * 默认内容审核实现:未配置第三方审核服务时一律放行,交由运营方人工审核兜底。 + * 接入第三方(如阿里云内容安全 / Google Cloud Vision SafeSearch)后新增实现类并替换装配。 + */ +public class DisabledContentModeration implements ContentModerationService { + + @Override + public Verdict checkImage(String imageUrl) { + return Verdict.PASS; + } + + @Override + public Verdict checkVideo(String videoUrl) { + return Verdict.PASS; + } +} diff --git a/fruitshorts-module-creator/pom.xml b/fruitshorts-module-creator/pom.xml index 33f5c3a..7d2c35c 100644 --- a/fruitshorts-module-creator/pom.xml +++ b/fruitshorts-module-creator/pom.xml @@ -39,5 +39,10 @@ com.fruitshorts fruitshorts-module-system + + + com.fruitshorts + fruitshorts-module-interaction + diff --git a/fruitshorts-module-creator/src/main/java/com/fruitshorts/creator/service/ContentSubmissionService.java b/fruitshorts-module-creator/src/main/java/com/fruitshorts/creator/service/ContentSubmissionService.java index 9cfea88..593ed01 100644 --- a/fruitshorts-module-creator/src/main/java/com/fruitshorts/creator/service/ContentSubmissionService.java +++ b/fruitshorts-module-creator/src/main/java/com/fruitshorts/creator/service/ContentSubmissionService.java @@ -12,6 +12,9 @@ import com.fruitshorts.content.dal.entity.Drama; import com.fruitshorts.content.enums.ContentStatus; import com.fruitshorts.content.service.DramaService; import com.fruitshorts.content.service.EpisodeService; +import com.fruitshorts.content.vo.EpisodeRespVO; +import com.fruitshorts.interaction.service.SensitiveWordCheck; +import com.fruitshorts.interaction.service.SensitiveWordService; import com.fruitshorts.creator.dal.entity.ContentSubmission; import com.fruitshorts.creator.dal.entity.Creator; import com.fruitshorts.creator.dal.mapper.ContentSubmissionMapper; @@ -52,6 +55,7 @@ public class ContentSubmissionService { private final ContentSubmissionMapper submissionMapper; private final DramaService dramaService; private final EpisodeService episodeService; + private final SensitiveWordService sensitiveWordService; public PageResult page(SubmissionQueryReqVO req) { Page page = new Page<>(req.getSafePageIndex(), req.getSafePageSize()); @@ -90,6 +94,7 @@ public class ContentSubmissionService { if (!Objects.equals(drama.getCreatorId(), creator.getId())) { throw new ServiceException(ErrorCode.FORBIDDEN, "无权访问该作品"); } + checkContentSensitive(drama); if (!Set.of(TYPE_CREATE, TYPE_UPDATE, TYPE_PUBLISH, TYPE_OFFLINE).contains(type)) { throw new ServiceException(ErrorCode.BAD_REQUEST, "未知的提交类型: " + type); } @@ -166,6 +171,30 @@ public class ContentSubmissionService { return submission; } + /** + * 提交审核前的敏感词自动检查:命中禁止词(BLOCK)直接拦截,命中待审词(REVIEW)仍放行交由人工复核。 + * 覆盖标题、副标题、简介、标签与全部分集标题。 + */ + private void checkContentSensitive(Drama drama) { + checkText(drama.getTitle(), "标题"); + checkText(drama.getSubtitle(), "副标题"); + checkText(drama.getSynopsis(), "简介"); + checkText(drama.getTags(), "标签"); + for (EpisodeRespVO ep : episodeService.listByDrama(drama.getId(), null)) { + checkText(ep.getTitle(), "分集标题"); + } + } + + private void checkText(String text, String field) { + if (!StringUtils.hasText(text)) { + return; + } + SensitiveWordCheck check = sensitiveWordService.check(text); + if (check.blocked()) { + throw new ServiceException(ErrorCode.OPERATION_NOT_ALLOWED, field + check.reason()); + } + } + /** * 创作者撤回自己的提交(撤回重提)。 * 只有还没出结果的提交能撤回;撤回后短剧退回草稿,创作者可以继续改。