Skip to content

rootshk/cms-server

Repository files navigation

CMS Server

基于Spring Boot 3.5.4 + PostgreSQL + Redis + JDK21的内容管理系统服务器

功能特性

核心功能

  1. 定时爬取数据

  2. 数据查询服务

    • 提供丰富的REST API接口
    • 支持分页查询、条件筛选、关键词搜索
    • Redis缓存优化查询性能
    • 支持按类型、演员、导演、年份、地区等多维度查询

技术特性

  • 高性能: 使用Redis缓存提升查询速度
  • 高可靠: 分布式锁防止重复爬取,异常重试机制
  • 易扩展: 模块化设计,便于功能扩展
  • 易监控: 完整的日志记录和健康检查

技术栈

  • 框架: Spring Boot 3.5.4
  • 数据库: PostgreSQL
  • 缓存: Redis
  • JDK版本: 21
  • 构建工具: Maven
  • HTTP客户端: WebFlux WebClient

项目结构

src/main/java/com/hankshen/cms/server/
├── CmsServerApplication.java          # 主启动类
├── config/
│   └── AppConfig.java                 # 应用配置
├── controller/
│   ├── VideoController.java           # 视频查询API
│   └── CrawlerController.java         # 爬虫管理API
├── dto/
│   └── MacCmsResponse.java            # MacCMS API响应DTO
├── entity/
│   └── VideoEntity.java               # 视频实体类
├── exception/
│   └── GlobalExceptionHandler.java    # 全局异常处理
├── repository/
│   └── VideoRepository.java           # 数据访问层
├── scheduler/
│   └── CrawlerScheduler.java          # 定时任务调度
└── service/
    ├── CrawlerService.java             # 爬虫服务
    └── VideoService.java               # 视频查询服务

环境要求

  • JDK 21+
  • PostgreSQL 12+
  • Redis 6+
  • Maven 3.8+

快速开始

1. 环境准备

安装PostgreSQL

# macOS
brew install postgresql
brew services start postgresql

# 创建数据库
psql postgres
CREATE DATABASE cms_db;
CREATE USER postgres WITH PASSWORD 'password';
GRANT ALL PRIVILEGES ON DATABASE cms_db TO postgres;

安装Redis

# macOS
brew install redis
brew services start redis

2. 配置应用

编辑 src/main/resources/application.yml 文件,修改数据库和Redis连接配置:

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/cms_db
    username: postgres
    password: your_password

  data:
    redis:
      host: localhost
      port: 6379
      password: your_redis_password  # 如果有密码

3. 运行应用

# 编译项目
mvn clean compile

# 运行应用
mvn spring-boot:run

应用启动后,访问 http://localhost:8080/api

API文档

通用响应格式

所有API接口都使用统一的响应格式:

{
  "success": true,
  "message": "操作成功",
  "data": {}
}
  • success: 布尔值,表示请求是否成功
  • message: 字符串,响应消息
  • data: 对象,具体的响应数据

视频查询API

1. 获取所有视频(分页)

接口说明: 分页获取所有视频列表,支持按更新时间倒序排列

请求方式: GET

请求URL: /api/videos

请求参数:

  • page (可选): 页码,从0开始,默认为0
  • size (可选): 每页大小,默认为20,最大100

请求示例:

GET /api/videos?page=0&size=20

返回示例:

{
  "success": true,
  "message": "查询成功",
  "data": {
    "content": [
      {
        "id": 1,
        "videoId": 12345,
        "typeId": 1,
        "typeName": "电影",
        "name": "复仇者联盟",
        "subName": "Avengers",
        "nameEn": "The Avengers",
        "state": 1,
        "pic": "https://example.com/poster.jpg",
        "lang": "国语",
        "area": "美国",
        "year": "2012",
        "note": "HD高清",
        "actor": "小罗伯特·唐尼,克里斯·埃文斯",
        "director": "乔斯·韦登",
        "des": "超级英雄集结拯救世界的故事",
        "tag": "动作,科幻,冒险",
        "className": "动作片",
         "vodPlayUrl": "1$https://m3u8.hmrvideo.com/play/55f6b3a441a04f70a13d071ab0e2f17f.m3u8#2$https://m3u8.hmrvideo.com/play/2707ed8fb13f410187ce2a01fc2fe4f0.m3u8",
         "playUrls": [
           {
             "episode": "1",
             "url": "https://m3u8.hmrvideo.com/play/55f6b3a441a04f70a13d071ab0e2f17f.m3u8"
           },
           {
             "episode": "2",
             "url": "https://m3u8.hmrvideo.com/play/2707ed8fb13f410187ce2a01fc2fe4f0.m3u8"
           }
         ],
         "vodDownUrl": "https://example.com/download.mp4",
        "vodPlayFrom": "优酷",
        "vodServer": "server1",
        "vodPlayNote": "高清播放",
        "duration": "143分钟",
        "updateTime": 1640995200,
        "addTime": 1640995200,
        "createdAt": "2024-01-01T10:00:00",
        "updatedAt": "2024-01-01T10:00:00",
        "deleted": false
      }
    ],
    "pageable": {
      "pageNumber": 0,
      "pageSize": 20,
      "sort": {
        "sorted": true,
        "empty": false
      }
    },
    "totalElements": 1000,
    "totalPages": 50,
    "last": false,
    "first": true,
    "numberOfElements": 20,
    "size": 20,
    "number": 0,
    "sort": {
      "sorted": true,
      "empty": false
    },
    "empty": false
  }
}

2. 根据ID获取视频详情

接口说明: 根据视频ID获取单个视频的详细信息

请求方式: GET

请求URL: /api/videos/{videoId}

路径参数:

  • videoId: 视频ID(必填)

请求示例:

GET /api/videos/12345

返回示例:

{
  "success": true,
  "message": "查询成功",
  "data": {
    "id": 1,
    "videoId": 12345,
    "typeId": 1,
    "typeName": "电影",
    "name": "复仇者联盟",
    "subName": "Avengers",
    "nameEn": "The Avengers",
    "state": 1,
    "pic": "https://example.com/poster.jpg",
    "lang": "国语",
    "area": "美国",
    "year": "2012",
    "note": "HD高清",
    "actor": "小罗伯特·唐尼,克里斯·埃文斯",
    "director": "乔斯·韦登",
    "des": "超级英雄集结拯救世界的故事",
    "tag": "动作,科幻,冒险",
     "className": "动作片",
     "vodPlayUrl": "1$https://m3u8.hmrvideo.com/play/55f6b3a441a04f70a13d071ab0e2f17f.m3u8#2$https://m3u8.hmrvideo.com/play/2707ed8fb13f410187ce2a01fc2fe4f0.m3u8",
     "playUrls": [
       {
         "episode": "1",
         "url": "https://m3u8.hmrvideo.com/play/55f6b3a441a04f70a13d071ab0e2f17f.m3u8"
       },
       {
         "episode": "2",
         "url": "https://m3u8.hmrvideo.com/play/2707ed8fb13f410187ce2a01fc2fe4f0.m3u8"
       }
     ],
     "vodDownUrl": "https://example.com/download.mp4",
    "vodPlayFrom": "优酷",
    "vodServer": "server1",
    "vodPlayNote": "高清播放",
    "duration": "143分钟",
    "updateTime": 1640995200,
    "addTime": 1640995200,
    "createdAt": "2024-01-01T10:00:00",
    "updatedAt": "2024-01-01T10:00:00",
    "deleted": false
  }
}

3. 根据类型查询视频

接口说明: 根据视频类型ID分页查询视频列表

请求方式: GET

请求URL: /api/videos/type/{typeId}

路径参数:

  • typeId: 类型ID(必填)

请求参数:

  • page (可选): 页码,从0开始,默认为0
  • size (可选): 每页大小,默认为20

请求示例:

GET /api/videos/type/1?page=0&size=20

返回示例: 同分页查询格式

4. 综合搜索视频

接口说明: 支持按名称、演员、导演、标签等多字段进行关键词搜索

请求方式: GET

请求URL: /api/videos/search

请求参数:

  • keyword: 搜索关键词(必填)
  • page (可选): 页码,从0开始,默认为0
  • size (可选): 每页大小,默认为20

请求示例:

GET /api/videos/search?keyword=复仇者&page=0&size=20

返回示例: 同分页查询格式

5. 根据名称搜索

接口说明: 根据视频名称进行模糊搜索

请求方式: GET

请求URL: /api/videos/search/name

请求参数:

  • name: 视频名称(必填)
  • page (可选): 页码,从0开始,默认为0
  • size (可选): 每页大小,默认为20

请求示例:

GET /api/videos/search/name?name=复仇者联盟&page=0&size=20

返回示例: 同分页查询格式

6. 根据演员搜索

接口说明: 根据演员名称进行模糊搜索

请求方式: GET

请求URL: /api/videos/search/actor

请求参数:

  • actor: 演员名称(必填)
  • page (可选): 页码,从0开始,默认为0
  • size (可选): 每页大小,默认为20

请求示例:

GET /api/videos/search/actor?actor=小罗伯特·唐尼&page=0&size=20

返回示例: 同分页查询格式

7. 根据导演搜索

接口说明: 根据导演名称进行模糊搜索

请求方式: GET

请求URL: /api/videos/search/director

请求参数:

  • director: 导演名称(必填)
  • page (可选): 页码,从0开始,默认为0
  • size (可选): 每页大小,默认为20

请求示例:

GET /api/videos/search/director?director=乔斯·韦登&page=0&size=20

返回示例: 同分页查询格式

8. 根据年份查询

接口说明: 根据年份查询视频列表

请求方式: GET

请求URL: /api/videos/year/{year}

路径参数:

  • year: 年份(必填)

请求参数:

  • page (可选): 页码,从0开始,默认为0
  • size (可选): 每页大小,默认为20

请求示例:

GET /api/videos/year/2012?page=0&size=20

返回示例: 同分页查询格式

9. 根据地区查询

接口说明: 根据地区查询视频列表

请求方式: GET

请求URL: /api/videos/area/{area}

路径参数:

  • area: 地区名称(必填)

请求参数:

  • page (可选): 页码,从0开始,默认为0
  • size (可选): 每页大小,默认为20

请求示例:

GET /api/videos/area/美国?page=0&size=20

返回示例: 同分页查询格式

10. 获取最近更新的视频

接口说明: 获取最近更新的视频列表,按更新时间倒序排列

请求方式: GET

请求URL: /api/videos/recent

请求参数:

  • limit (可选): 数量限制,默认为10,最大50

请求示例:

GET /api/videos/recent?limit=10

返回示例:

{
  "success": true,
  "message": "查询成功",
  "data": [
    {
      "id": 1,
      "videoId": 12345,
      "typeId": 1,
      "typeName": "电影",
      "name": "复仇者联盟",
      "subName": "Avengers",
      "nameEn": "The Avengers",
      "state": 1,
      "pic": "https://example.com/poster.jpg",
      "lang": "国语",
      "area": "美国",
      "year": "2012",
      "note": "HD高清",
      "actor": "小罗伯特·唐尼,克里斯·埃文斯",
      "director": "乔斯·韦登",
      "des": "超级英雄集结拯救世界的故事",
      "tag": "动作,科幻,冒险",
       "className": "动作片",
       "vodPlayUrl": "1$https://m3u8.hmrvideo.com/play/55f6b3a441a04f70a13d071ab0e2f17f.m3u8#2$https://m3u8.hmrvideo.com/play/2707ed8fb13f410187ce2a01fc2fe4f0.m3u8",
       "playUrls": [
         {
           "episode": "1",
           "url": "https://m3u8.hmrvideo.com/play/55f6b3a441a04f70a13d071ab0e2f17f.m3u8"
         },
         {
           "episode": "2",
           "url": "https://m3u8.hmrvideo.com/play/2707ed8fb13f410187ce2a01fc2fe4f0.m3u8"
         }
       ],
       "vodDownUrl": "https://example.com/download.mp4",
      "vodPlayFrom": "优酷",
      "vodServer": "server1",
      "vodPlayNote": "高清播放",
      "duration": "143分钟",
      "updateTime": 1640995200,
      "addTime": 1640995200,
      "createdAt": "2024-01-01T10:00:00",
      "updatedAt": "2024-01-01T10:00:00",
      "deleted": false
    }
  ]
}

11. 获取视频统计信息

接口说明: 获取系统中视频的统计信息

请求方式: GET

请求URL: /api/videos/statistics

请求示例:

GET /api/videos/statistics

返回示例:

{
  "success": true,
  "message": "查询成功",
  "data": {
    "totalVideos": 10000,
    "todayAdded": 50,
    "weekAdded": 300,
    "monthAdded": 1200
  }
}

12. 根据类型ID获取视频数量

接口说明: 获取指定类型的视频总数量

请求方式: GET

请求URL: /api/videos/count/type/{typeId}

路径参数:

  • typeId: 类型ID(必填)

请求示例:

GET /api/videos/count/type/1

返回示例:

{
  "success": true,
  "message": "查询成功",
  "data": 1500
}

13. 检查视频是否存在

接口说明: 检查指定ID的视频是否存在

请求方式: GET

请求URL: /api/videos/exists/{videoId}

路径参数:

  • videoId: 视频ID(必填)

请求示例:

GET /api/videos/exists/12345

返回示例:

{
  "success": true,
  "message": "查询成功",
  "data": true
}

爬虫管理API

1. 手动触发全量爬取

接口说明: 手动触发全量数据爬取,会爬取所有可用的视频数据

请求方式: POST

请求URL: /api/crawler/full

请求示例:

POST /api/crawler/full
Content-Type: application/json

返回示例:

{
  "success": true,
  "message": "全量爬取任务已启动",
  "data": {
    "taskId": "full_crawl_20240101_100000",
    "startTime": "2024-01-01T10:00:00",
    "status": "RUNNING"
  }
}

2. 手动触发增量爬取

接口说明: 手动触发增量数据爬取,只爬取最近更新的视频数据

请求方式: POST

请求URL: /api/crawler/incremental

请求示例:

POST /api/crawler/incremental
Content-Type: application/json

返回示例:

{
  "success": true,
  "message": "增量爬取任务已启动",
  "data": {
    "taskId": "incremental_crawl_20240101_100000",
    "startTime": "2024-01-01T10:00:00",
    "status": "RUNNING"
  }
}

3. 获取爬虫状态

接口说明: 获取当前爬虫任务的运行状态和统计信息

请求方式: GET

请求URL: /api/crawler/status

请求示例:

GET /api/crawler/status

返回示例:

{
  "success": true,
  "message": "查询成功",
  "data": {
    "isRunning": true,
    "currentTask": {
      "taskId": "incremental_crawl_20240101_100000",
      "type": "INCREMENTAL",
      "startTime": "2024-01-01T10:00:00",
      "status": "RUNNING",
      "progress": {
        "currentPage": 5,
        "totalPages": 10,
        "processedCount": 500,
        "newCount": 50,
        "updatedCount": 30,
        "errorCount": 2
      }
    },
    "lastFullCrawl": "2024-01-01T02:00:00",
    "lastIncrementalCrawl": "2024-01-01T09:00:00",
    "totalVideos": 10000
  }
}

4. 获取爬虫配置

接口说明: 获取当前爬虫的配置信息

请求方式: GET

请求URL: /api/crawler/config

请求示例:

GET /api/crawler/config

返回示例:

{
  "success": true,
  "message": "查询成功",
  "data": {
    "maccms": {
      "baseUrl": "https://json02.heimuer.xyz/api.php/provide/vod/",
      "pageSize": 100,
      "requestTimeout": 30000,
      "retryTimes": 3,
      "retryDelay": 5000
    },
    "schedule": {
      "fullCrawlCron": "0 0 2 * * ?",
      "incrementalCrawlCron": "0 0 * * * ?",
      "enabled": true
    },
    "performance": {
      "maxConcurrentRequests": 5,
      "requestDelay": 1000,
      "batchSize": 50
    }
  }
}

错误响应格式

当API请求出现错误时,会返回以下格式的响应:

{
  "success": false,
  "message": "错误描述信息",
  "data": null,
  "error": {
    "code": "ERROR_CODE",
    "details": "详细错误信息"
  }
}

常见错误码

  • INVALID_PARAMETER: 请求参数无效
  • RESOURCE_NOT_FOUND: 资源不存在
  • CRAWLER_ALREADY_RUNNING: 爬虫任务已在运行中
  • CRAWLER_CONFIG_ERROR: 爬虫配置错误
  • DATABASE_ERROR: 数据库操作错误
  • NETWORK_ERROR: 网络请求错误
  • INTERNAL_SERVER_ERROR: 服务器内部错误

字段说明

VideoEntity 字段说明

字段名 类型 说明
id Long 主键ID
videoId Long 视频ID(来自MacCMS)
typeId Integer 视频类型ID
typeName String 视频类型名称
name String 视频名称
subName String 视频子名称
nameEn String 视频英文名
state Integer 视频状态(1:正常,0:禁用)
pic String 视频海报图片URL
lang String 视频语言
area String 视频地区
year String 视频年份
note String 视频备注信息
actor String 视频演员列表(逗号分隔)
director String 视频导演
des String 视频简介描述
tag String 视频标签(逗号分隔)
className String 视频分类名称
vodPlayUrl String 视频播放地址(原始格式:1$url1#2$url2)
playUrls Array 解析后的播放URL列表,包含episode和url字段
vodDownUrl String 视频下载地址
vodPlayFrom String 视频播放来源
vodServer String 视频服务器
vodPlayNote String 视频播放备注
duration String 视频时长
updateTime Long 视频更新时间(时间戳)
addTime Long 视频添加时间(时间戳)
createdAt String 数据创建时间(ISO格式)
updatedAt String 数据更新时间(ISO格式)
deleted Boolean 是否已删除

PlayUrl 对象字段说明

字段名 类型 说明
episode String 集数或标识(如:1、2、3等)
url String 具体的播放URL地址

说明: playUrls 字段是对 vodPlayUrl 字段的解析结果。原始的 vodPlayUrl 格式为:1$url1#2$url2#3$url3,系统会自动解析为 playUrls 数组,方便前端直接使用。

使用建议

  1. 分页查询: 建议使用合适的页面大小(10-50),避免一次性获取过多数据
  2. 缓存策略: 系统已实现Redis缓存,相同查询条件的请求会优先从缓存获取数据
  3. 搜索优化: 使用具体的搜索条件可以获得更精确的结果
  4. 错误处理: 前端应该根据success字段判断请求是否成功,并适当处理错误情况
  5. 爬虫管理: 避免频繁触发爬虫任务,建议检查爬虫状态后再决定是否启动新任务

配置说明

爬虫配置

crawler:
  maccms:
    base-url: https://json02.heimuer.xyz/api.php/provide/vod/
    page-size: 100              # 每页爬取数量
    request-timeout: 30000      # 请求超时时间(毫秒)
    retry-times: 3              # 重试次数
    retry-delay: 5000           # 重试延迟(毫秒)
  schedule:
    # 全量爬取:每天凌晨2点执行
    full-crawl-cron: "0 0 2 * * ?"
    # 增量爬取:每小时执行一次
    incremental-crawl-cron: "0 0 * * * ?"

数据库配置

spring:
  jpa:
    hibernate:
      ddl-auto: update          # 自动创建/更新表结构
    show-sql: true              # 显示SQL语句

Redis配置

spring:
  data:
    redis:
      timeout: 5000ms
      lettuce:
        pool:
          max-active: 20        # 最大连接数
          max-idle: 10          # 最大空闲连接
          min-idle: 5           # 最小空闲连接

定时任务

系统默认配置了两个定时任务:

  1. 全量爬取: 每天凌晨2点执行,爬取所有视频数据
  2. 增量爬取: 每小时执行一次,只爬取最近更新的数据

可以通过修改配置文件中的cron表达式来调整执行频率。

监控和日志

健康检查

访问 http://localhost:8080/api/actuator/health 查看应用健康状态

日志配置

日志文件位置:logs/cms-server.log

可以通过修改 application.yml 中的日志配置来调整日志级别和输出格式。

开发指南

添加新的查询接口

  1. VideoRepository 中添加查询方法
  2. VideoService 中添加业务逻辑
  3. VideoController 中添加REST接口

扩展爬虫功能

  1. 修改 MacCmsResponse DTO以支持新的数据字段
  2. 更新 VideoEntity 实体类
  3. CrawlerService 中添加新的处理逻辑

常见问题

Q: 如何修改爬取频率?

A: 修改 application.yml 中的 crawler.schedule 配置项。

Q: 如何清除缓存?

A: 可以重启Redis服务,或者调用 VideoService.clearCache() 方法。

Q: 数据库表结构如何更新?

A: 修改实体类后,Spring Boot会自动更新表结构(ddl-auto: update)。

Q: 如何查看爬取日志?

A: 查看 logs/cms-server.log 文件,或者控制台输出。

许可证

本项目采用 MIT 许可证。

About

AppleCMS V10 Media SCAN Server

Resources

Stars

Watchers

Forks

Packages

No packages published