From 19aa6971d5a5943d1226975043e2688a1e6892a4 Mon Sep 17 00:00:00 2001 From: 16337 <1633794139@qq.com> Date: Sat, 28 Feb 2026 10:02:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=88=AA=E5=9B=BE=E5=93=8D=E5=BA=94?= =?UTF-8?q?=E6=94=B9=E4=B8=BAHTTP=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 45 +++ QUICKSTART.md | 308 +++++++++++++++++ scripts/edge_local_sync.py | 36 ++ .../vmp/aiot/controller/AiRoiController.java | 7 + .../aiot/service/IAiScreenshotService.java | 8 + .../service/impl/AiScreenshotServiceImpl.java | 140 ++++++-- src/main/resources/application-dev.yml | 3 + src/main/resources/application-docker.yml | 2 + tools/maven.zip | 72 ++++ 启动指南-Java17.md | 275 +++++++++++++++ 开始使用.md | 324 ++++++++++++++++++ 11 files changed, 1182 insertions(+), 38 deletions(-) create mode 100644 AGENTS.md create mode 100644 QUICKSTART.md create mode 100644 scripts/edge_local_sync.py create mode 100644 tools/maven.zip create mode 100644 启动指南-Java17.md create mode 100644 开始使用.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..ead3b00b4 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,45 @@ +`# Repository Guidelines + +## Project Structure & Module Organization +- `src/main/java`: Spring Boot backend source. +- `src/main/resources`: application config and resources (e.g., `application.yml`, `application-*.yml`). +- `src/test/java`: backend tests (currently minimal; example: `JT1078ServerTest.java`). +- `web/`: Vue 2 frontend app (Element UI, Vue CLI). +- `docker/`: Docker Compose files and environment configuration (`docker/.env`). +- `docs/` and `doc/`: project documentation and media assets. +- `libs/`, `tools/`, `数据库/`: local dependencies, tooling, and database assets. + +## Build, Test, and Development Commands +Backend (local dev): +- `mvn clean package -Dmaven.test.skip=true`: build backend jar quickly. +- `java -jar target/wvp-pro-2.7.4-*.jar --spring.profiles.active=dev`: run with dev profile. +- `mvn -DskipTests=false test`: run backend tests (tests are skipped by default in `pom.xml`). + +Frontend (from `web/`): +- `npm run dev`: local dev server. +- `npm run build:prod`: production build. +- `npm run lint`: lint Vue/JS code. +- `npm run test:unit`: run Jest unit tests. + +Docker (from `docker/`): +- `docker compose build`: build images. +- `docker compose up -d`: start full stack. +- `docker compose logs -f polaris-wvp`: tail backend logs. + +## Coding Style & Naming Conventions +- Java: follow existing Spring Boot style in `src/main/java` (4-space indentation, standard Java naming). +- Vue/JS: follow ESLint rules in `web/` (`npm run lint`). +- Config files: use `application-.yml` naming for Spring profiles. + +## Testing Guidelines +- Backend: tests live under `src/test/java` and typically use `*Test` class suffix. +- Frontend: Jest via `npm run test:unit`. +- There is no enforced coverage gate in the repo; keep tests focused on new behavior. + +## Commit & Pull Request Guidelines +- Commit messages follow Conventional Commit-style prefixes: `feat`, `fix`, `chore`, `refactor`, optionally with scopes (e.g., `feat(aiot): ...`). +- PRs should include: a concise summary, relevant config changes (`application-*.yml`, `docker/.env`), and screenshots for UI changes in `web/`. + +## Configuration Tips +- Local dev settings live in `src/main/resources/application-dev.yml`. +- Docker configuration lives in `docker/.env` (ports, SIP settings, IPs). Update these when changing network topology. diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 000000000..25639502a --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,308 @@ +# WVP-PRO 快速启动指南 + +## 当前状态 + +### ✅ 已启动的服务 +以下 Docker 容器已成功运行: +- **Redis** (docker-polaris-redis-1) - 端口 6379 - 健康运行 +- **MySQL 8** (docker-polaris-mysql-1) - 端口 3306 - 健康运行,数据库已自动初始化 +- **ZLMediaKit** (docker-polaris-media-1) - 流媒体服务 - 运行正常 + - RTMP: 10001 + - RTSP: 10002 + - RTP: 10003 + +### ⚠️ 待解决问题 +- WVP-PRO 应用和 Nginx 前端尚未启动(Docker 镜像拉取失败) + +--- + +## 方案 A:Docker 完整部署(推荐) + +### 前提条件 +需要解决 Docker Hub 网络连接问题。 + +### 步骤 1:配置 Docker 镜像加速 + +#### Windows/Mac (Docker Desktop): +1. 打开 Docker Desktop +2. 进入 Settings > Docker Engine +3. 添加以下配置: +```json +{ + "registry-mirrors": [ + "https://docker.m.daocloud.io", + "https://docker.mirrors.ustc.edu.cn" + ] +} +``` +4. 点击 "Apply & Restart" + +### 步骤 2:拉取所需镜像 +```bash +cd docker + +# 拉取 JDK 镜像(用于构建) +docker pull eclipse-temurin:21-jdk +docker pull eclipse-temurin:21-jre + +# 拉取 Node 镜像(用于前端构建) +docker pull ubuntu:24.04 +docker pull nginx:alpine +``` + +### 步骤 3:构建并启动所有服务 +```bash +# 构建 WVP 和 Nginx +docker compose build + +# 启动所有服务 +docker compose up -d +``` + +### 步骤 4:验证服务状态 +```bash +# 查看所有服务状态 +docker compose ps + +# 查看 WVP 日志 +docker compose logs -f polaris-wvp +``` + +### 步骤 5:访问服务 +- **管理后台**: http://localhost:8080 +- **API 文档**: http://localhost:18978/doc.html +- **默认账号**: admin / admin + +--- + +## 方案 B:本地开发模式 + +适用于需要频繁修改代码的开发场景。 + +### 前提条件 +- JDK 21 ([下载地址](https://adoptium.net/temurin/releases/?version=21)) +- Maven 3.3+ ([下载地址](https://maven.apache.org/download.cgi)) +- Node.js 16+ ([下载地址](https://nodejs.org/)) + +### 步骤 1:保持基础服务运行 +```bash +# 验证基础服务状态 +docker ps --filter "name=polaris-redis|polaris-mysql|polaris-media" + +# 如果服务未运行,启动它们 +cd docker +docker compose up -d polaris-redis polaris-mysql polaris-media +``` + +### 步骤 2:编译前端 +```bash +cd web +npm install --registry=https://registry.npmmirror.com +npm run build:prod +cd .. +``` + +### 步骤 3:配置应用 +编辑 `src/main/resources/application-dev.yml`(已有模板),确认配置: +```yaml +spring: + data: + redis: + host: 127.0.0.1 # 或 localhost + port: 6379 + datasource: + url: jdbc:mysql://127.0.0.1:3306/wvp?... + username: wvp_user + password: wvp_password + +media: + ip: 127.0.0.1 + http-port: 8080 +``` + +### 步骤 4:编译后端 +```bash +mvn clean package -Dmaven.test.skip=true +``` + +### 步骤 5:运行应用 +```bash +# 指定使用 dev 配置 +java -jar target/wvp-pro-2.7.4-*.jar --spring.profiles.active=dev +``` + +### 步骤 6:访问服务 +- **管理后台**: http://localhost:18080 (注意端口) +- **API 文档**: http://localhost:18080/doc.html + +--- + +## 方案 C:混合模式(最快) + +基础服务用 Docker,应用本地运行(无需编译)。 + +### 步骤 1:下载预编译包 +如果网络允许,可以从项目 Release 页面下载预编译的 jar 包。 + +### 步骤 2:使用现有的 Docker 配置运行 +```bash +cd docker/wvp +# 将预编译的 jar 包重命名为 wvp.jar +# 修改 wvp/application-docker.yml 中的数据库配置指向 localhost +``` + +--- + +## 常见问题 + +### 1. Docker 镜像拉取失败 +**问题**: `failed to fetch anonymous token` 或 `EOF` +**解决**: +- 配置国内镜像源(见方案 A 步骤 1) +- 或使用代理 +- 或使用方案 B 本地运行 + +### 2. MySQL 连接失败 +**问题**: `Communications link failure` +**解决**: +```bash +# 检查 MySQL 是否健康 +docker ps | grep mysql + +# 查看 MySQL 日志 +docker logs docker-polaris-mysql-1 + +# 测试连接 +docker exec -it docker-polaris-mysql-1 mysql -u wvp_user -pwvp_password -e "show databases;" +``` + +### 3. 端口冲突 +**问题**: `bind: address already in use` +**解决**: 修改 `docker/.env` 文件中的端口配置: +```env +WebHttp=18080 # 改为其他端口 +SIP_Port=8160 # 改为其他端口 +``` + +### 4. 编译失败 - Java 版本不对 +**问题**: `release version 21 not supported` +**解决**: +```bash +# 检查 Java 版本 +java -version + +# 必须使用 JDK 21,如果不是,请升级 +``` + +--- + +## 服务管理命令 + +### Docker 方式 +```bash +cd docker + +# 启动所有服务 +docker compose up -d + +# 停止所有服务 +docker compose down + +# 重启服务 +docker compose restart polaris-wvp + +# 查看日志 +docker compose logs -f polaris-wvp + +# 查看服务状态 +docker compose ps + +# 清理并重建 +docker compose down -v +docker compose build --no-cache +docker compose up -d +``` + +### 本地运行方式 +```bash +# 停止应用:Ctrl + C + +# 后台运行 +nohup java -jar target/wvp-pro-*.jar & + +# 查看日志 +tail -f nohup.out + +# 停止后台进程 +ps aux | grep wvp-pro +kill +``` + +--- + +## 配置说明 + +### 关键配置项 (docker/.env) +```env +# 流媒体端口 +MediaRtmp=10001 # RTMP 推拉流端口 +MediaRtsp=10002 # RTSP 推拉流端口 +MediaRtp=10003 # RTP 端口 + +# Web 服务端口 +WebHttp=8080 # HTTP 端口 + +# IP 配置(重要!) +Stream_IP=<你的服务器IP> # 流地址中的 IP,客户端需要能访问 +SDP_IP=<你的服务器IP> # SDP 中的 IP,设备需要能访问 +SIP_ShowIP=<你的服务器IP> # SIP 显示的 IP + +# SIP 配置(国标设备接入) +SIP_Port=8160 # SIP 端口 +SIP_Domain=3502000000 # SIP 域(根据实际修改) +SIP_Id=35020000002000000001 # SIP ID +SIP_Password=wvp_sip_password # 设备认证密码 +``` + +### 公网部署注意事项 +1. 将所有 `127.0.0.1` 改为你的**公网 IP** 或**内网 IP**(根据部署场景) +2. 确保防火墙开放所需端口 +3. 建议启用 HTTPS(需要配置证书) + +--- + +## 下一步 + +### 测试设备接入 +1. 配置国标设备(IPC/NVR)的 SIP 服务器信息: + - 服务器 IP: `<你的 SIP_ShowIP>` + - 服务器端口: `8160`(或你配置的 SIP_Port) + - 服务器域: `3502000000`(或你配置的 SIP_Domain) + - 设备 ID: 20位国标编码 + - 密码: `wvp_sip_password`(或你配置的密码) + +2. 设备注册成功后,在 WVP 管理后台可以看到设备列表 + +### 推流测试 +```bash +# 使用 FFmpeg 推流测试 +ffmpeg -re -i test.mp4 -c copy -f rtsp rtsp://localhost:10002/test + +# 播放地址 +# HTTP-FLV: http://localhost:8080/live/test.flv +# WebSocket-FLV: ws://localhost:8080/live/test.flv +# HLS: http://localhost:8080/live/test.m3u8 +``` + +--- + +## 获取帮助 + +- 官方文档: https://doc.wvp-pro.cn +- GitHub: https://github.com/648540858/wvp-GB28181-pro +- Gitee: https://gitee.com/pan648540858/wvp-GB28181-pro +- 问题反馈: 提交 GitHub Issue + +--- + +最后更新: 2026-02-02 diff --git a/scripts/edge_local_sync.py b/scripts/edge_local_sync.py new file mode 100644 index 000000000..e5f46f45b --- /dev/null +++ b/scripts/edge_local_sync.py @@ -0,0 +1,36 @@ +import json +import sys +import urllib.request + + +def main(): + if len(sys.argv) < 3: + print("usage: edge_local_sync.py ") + return 2 + + edge_url = sys.argv[1].rstrip("/") + "/debug/sync" + payload_path = sys.argv[2] + + with open(payload_path, "r", encoding="utf-8") as f: + payload = f.read() + + data = payload.encode("utf-8") + req = urllib.request.Request( + edge_url, + data=data, + headers={"Content-Type": "application/json"}, + method="POST", + ) + + try: + with urllib.request.urlopen(req, timeout=10) as resp: + body = resp.read().decode("utf-8", errors="ignore") + print(body) + return 0 if resp.status == 200 else 1 + except Exception as e: + print(f"error: {e}") + return 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiRoiController.java b/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiRoiController.java index 41902af70..9f51963fa 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiRoiController.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/controller/AiRoiController.java @@ -88,4 +88,11 @@ public class AiRoiController { @RequestParam(defaultValue = "false") boolean force) { return screenshotService.requestScreenshot(cameraCode, force); } + + @Operation(summary = "Edge 截图回调(Edge 主动调用)") + @PostMapping("/snap/callback") + public void snapCallback(@RequestBody Map body) { + String requestId = (String) body.get("request_id"); + screenshotService.handleCallback(requestId, body); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/service/IAiScreenshotService.java b/src/main/java/com/genersoft/iot/vmp/aiot/service/IAiScreenshotService.java index 702e8e37a..566c36801 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/service/IAiScreenshotService.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/service/IAiScreenshotService.java @@ -12,4 +12,12 @@ public interface IAiScreenshotService { * @return {status: "ok"/"error"/"timeout", url: "...", stale: true/false, message: "..."} */ Map requestScreenshot(String cameraCode, boolean force); + + /** + * 处理 Edge 截图回调 + * + * @param requestId 请求ID + * @param data 回调数据 {request_id, camera_code, status, url/message} + */ + void handleCallback(String requestId, Map data); } diff --git a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiScreenshotServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiScreenshotServiceImpl.java index 1c1903c46..ebffc20f8 100644 --- a/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiScreenshotServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/aiot/service/impl/AiScreenshotServiceImpl.java @@ -5,6 +5,7 @@ import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.aiot.service.IAiScreenshotService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.data.redis.connection.stream.MapRecord; import org.springframework.data.redis.connection.stream.RecordId; import org.springframework.data.redis.core.StringRedisTemplate; @@ -15,24 +16,32 @@ import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; import java.util.UUID; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; @Slf4j @Service public class AiScreenshotServiceImpl implements IAiScreenshotService { private static final String SNAP_REQUEST_STREAM = "edge_snap_request"; - private static final String SNAP_RESULT_KEY_PREFIX = "snap:result:"; private static final String SNAP_CACHE_KEY_PREFIX = "snap:cache:"; + private static final String SNAP_RESULT_KEY_PREFIX = "snap:result:"; - /** 轮询间隔 ms */ - private static final long POLL_INTERVAL_MS = 500; - /** 最大等待时间 ms */ - private static final long MAX_WAIT_MS = 15_000; + /** 缓存 TTL(秒) */ + private static final long SNAP_CACHE_TTL = 300; + /** 最大等待时间(秒) */ + private static final long MAX_WAIT_SECONDS = 15; + /** 降级 Redis 结果 TTL(秒) */ + private static final long SNAP_RESULT_TTL = 60; + + /** 等待 Edge 回调的 pending 请求表 */ + private final ConcurrentHashMap>> pendingRequests = new ConcurrentHashMap<>(); @Autowired private StringRedisTemplate stringRedisTemplate; + @Value("${ai.screenshot.callback-url:}") + private String callbackUrl; + @Override public Map requestScreenshot(String cameraCode, boolean force) { Map result = new HashMap<>(); @@ -58,11 +67,18 @@ public class AiScreenshotServiceImpl implements IAiScreenshotService { String requestId = UUID.randomUUID().toString().replace("-", "").substring(0, 12); String cosPath = buildCosPath(cameraCode, requestId); - // 3. XADD 到 Stream + // 3. 创建 CompletableFuture 并注册到 pending 表 + CompletableFuture> future = new CompletableFuture<>(); + pendingRequests.put(requestId, future); + + // 4. XADD 到 Stream(含 callback_url) Map fields = new HashMap<>(); fields.put("request_id", requestId); fields.put("camera_code", cameraCode); fields.put("cos_path", cosPath); + if (callbackUrl != null && !callbackUrl.isEmpty()) { + fields.put("callback_url", callbackUrl); + } try { MapRecord record = MapRecord.create(SNAP_REQUEST_STREAM, fields); @@ -70,59 +86,107 @@ public class AiScreenshotServiceImpl implements IAiScreenshotService { log.info("[AI截图] 发送截图请求: requestId={}, cameraCode={}, streamId={}", requestId, cameraCode, recordId); } catch (Exception e) { log.error("[AI截图] 发送截图请求失败: {}", e.getMessage()); + pendingRequests.remove(requestId); result.put("status", "error"); result.put("message", "发送截图请求失败"); return result; } - // 4. 轮询结果 - String resultKey = SNAP_RESULT_KEY_PREFIX + requestId; - long deadline = System.currentTimeMillis() + MAX_WAIT_MS; - - while (System.currentTimeMillis() < deadline) { - try { - Thread.sleep(POLL_INTERVAL_MS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; + // 5. 等待回调结果 + try { + Map callbackData = future.get(MAX_WAIT_SECONDS, TimeUnit.SECONDS); + String status = (String) callbackData.get("status"); + result.put("status", status); + if ("ok".equals(status)) { + result.put("url", callbackData.get("url")); + } else { + result.put("message", callbackData.get("message")); } - - String resultJson = stringRedisTemplate.opsForValue().get(resultKey); + return result; + } catch (TimeoutException e) { + // 超时 → 降级检查 Redis 结果(Edge 回调失败时可能回退写 Redis) + log.warn("[AI截图] 回调超时,检查 Redis 降级: requestId={}", requestId); + String resultJson = stringRedisTemplate.opsForValue().get(SNAP_RESULT_KEY_PREFIX + requestId); if (resultJson != null) { try { JSONObject res = JSON.parseObject(resultJson); result.put("status", res.getString("status")); if ("ok".equals(res.getString("status"))) { result.put("url", res.getString("url")); + // 降级成功,写入缓存 + writeCache(cameraCode, res.getString("url")); } else { result.put("message", res.getString("message")); } - // 清理结果 key - stringRedisTemplate.delete(resultKey); + stringRedisTemplate.delete(SNAP_RESULT_KEY_PREFIX + requestId); return result; - } catch (Exception e) { - log.warn("[AI截图] 结果解析失败: {}", e.getMessage()); + } catch (Exception ex) { + log.warn("[AI截图] 降级结果解析失败: {}", ex.getMessage()); } } - } - // 5. 超时 → 尝试返回过期缓存 - log.warn("[AI截图] 截图超时: cameraCode={}, requestId={}", cameraCode, requestId); - String staleCache = stringRedisTemplate.opsForValue().get(SNAP_CACHE_KEY_PREFIX + cameraCode); - if (staleCache != null) { - try { - JSONObject cached = JSON.parseObject(staleCache); - result.put("status", "ok"); - result.put("url", cached.getString("url")); - result.put("stale", true); - return result; - } catch (Exception ignored) { + // Redis 降级也没有 → 尝试返回过期缓存 + log.warn("[AI截图] 截图超时: cameraCode={}, requestId={}", cameraCode, requestId); + String staleCache = stringRedisTemplate.opsForValue().get(SNAP_CACHE_KEY_PREFIX + cameraCode); + if (staleCache != null) { + try { + JSONObject cached = JSON.parseObject(staleCache); + result.put("status", "ok"); + result.put("url", cached.getString("url")); + result.put("stale", true); + return result; + } catch (Exception ignored) { + } } + + result.put("status", "timeout"); + result.put("message", "边缘设备响应超时,请确认设备在线"); + return result; + } catch (Exception e) { + log.error("[AI截图] 等待回调异常: {}", e.getMessage()); + result.put("status", "error"); + result.put("message", "截图请求异常"); + return result; + } finally { + pendingRequests.remove(requestId); + } + } + + @Override + public void handleCallback(String requestId, Map data) { + if (requestId == null || requestId.isEmpty()) { + log.warn("[AI截图] 回调缺少 request_id"); + return; } - result.put("status", "timeout"); - result.put("message", "边缘设备响应超时,请确认设备在线"); - return result; + CompletableFuture> future = pendingRequests.get(requestId); + if (future != null) { + future.complete(data); + log.info("[AI截图] 回调完成: requestId={}", requestId); + } else { + log.warn("[AI截图] 回调未找到对应请求(可能已超时): requestId={}", requestId); + } + + // 写入 Redis 缓存(无论 future 是否存在,缓存都应更新) + String cameraCode = (String) data.get("camera_code"); + String status = (String) data.get("status"); + if ("ok".equals(status) && cameraCode != null) { + String url = (String) data.get("url"); + writeCache(cameraCode, url); + } + } + + /** + * 写入截图缓存 + */ + private void writeCache(String cameraCode, String url) { + String cacheKey = SNAP_CACHE_KEY_PREFIX + cameraCode; + String cacheData = JSON.toJSONString(Map.of("url", url, "timestamp", System.currentTimeMillis() / 1000)); + try { + stringRedisTemplate.opsForValue().set(cacheKey, cacheData, SNAP_CACHE_TTL, TimeUnit.SECONDS); + } catch (Exception e) { + log.warn("[AI截图] 写入缓存失败: {}", e.getMessage()); + } } /** diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index f8dc569ed..c2e06d1c2 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -121,6 +121,9 @@ ai: push-timeout: 10000 # 暂未对接时设为false enabled: true + screenshot: + # Edge截图回调地址(WVP外部可访问地址,Edge通过此地址回调截图结果) + callback-url: http://124.222.218.198:18080 mqtt: # MQTT推送开关 enabled: false diff --git a/src/main/resources/application-docker.yml b/src/main/resources/application-docker.yml index 5c565dbb6..4a24fa674 100644 --- a/src/main/resources/application-docker.yml +++ b/src/main/resources/application-docker.yml @@ -84,6 +84,8 @@ ai: url: ${AI_SERVICE_URL:http://localhost:8090} push-timeout: ${AI_PUSH_TIMEOUT:10000} enabled: ${AI_SERVICE_ENABLED:false} + screenshot: + callback-url: ${AI_SCREENSHOT_CALLBACK_URL:} mqtt: enabled: ${AI_MQTT_ENABLED:false} broker: ${AI_MQTT_BROKER:tcp://127.0.0.1:1883} diff --git a/tools/maven.zip b/tools/maven.zip new file mode 100644 index 000000000..fe6f3cb67 --- /dev/null +++ b/tools/maven.zip @@ -0,0 +1,72 @@ + + + + About us + + + + +
+ + + +
+
+

About Us

+

+ 阿里云镜像由阿里巴巴技术保障部基础系统组提供支持。 +

+

+ 覆盖了Debian、Ubuntu、 Fedora、Arch Linux、 CentOS、openSUSE、Scientific Linux、Gentoo 等多个发行版的软件源镜像。 +

+

+ 搭建此开源镜像的目的在于宣传自由软件的价值,提高自由软件社区文化氛围, 推广自由软件在国内应用。 +

+
+
+

镜像设置

+

+ 如果您不了解如何配置 Linux 发行版 / 软件的安装源, 您可以通过首页的文件列表中相应源的 Help 链接寻求帮助 +

+ +

友情提示

+

+ 同步频率为每天一次,每天凌晨2:00-4:00为镜像的同步时间 +

+

+ 若使用阿里云服务器,将源的域名从mirrors.aliyun.com改为mirrors.cloud.aliyuncs.com,不占用公网流量。 +

+

+ 如果需要下载ISO镜像,请直接使用Chrome、Firefox浏览器下载,勿使用P2P下载工具。 +

+

常用链接

+ +

+

+
+
+
+

联系我们

+

ali-yum@alibaba-inc.com

+
+
+ +
+
+ + diff --git a/启动指南-Java17.md b/启动指南-Java17.md new file mode 100644 index 000000000..5fce5e728 --- /dev/null +++ b/启动指南-Java17.md @@ -0,0 +1,275 @@ +# WVP-PRO 启动指南(使用 Java 17) + +## 当前环境 +- ✅ Java 17 已安装 +- ✅ Node.js 已安装 +- ✅ Docker 已安装 +- ✅ 基础服务已启动(Redis、MySQL、ZLMediaKit) +- ✅ pom.xml 已修改为 Java 17 + +## 方案一:使用 PowerShell 编译(推荐) + +### 1. 打开 PowerShell(管理员权限) + +```powershell +cd C:\workspace\wvp-platform +``` + +### 2. 使用 Docker 编译项目 + +```powershell +docker run --rm -v "${PWD}:/app" -w /app eclipse-temurin:17-jdk cmd /c "apt-get update && apt-get install -y maven && mvn clean package -Dmaven.test.skip=true" +``` + +### 3. 检查编译结果 + +```powershell +ls target\wvp-pro*.jar +``` + +### 4. 配置连接参数 + +编辑 `src\main\resources\application-dev.yml`,确保以下配置正确: + +```yaml +spring: + data: + redis: + host: 127.0.0.1 + port: 6379 + datasource: + url: jdbc:mysql://127.0.0.1:3306/wvp?useUnicode=true&characterEncoding=UTF8&rewriteBatchedStatements=true&serverTimezone=PRC&useSSL=false&allowMultiQueries=true&allowPublicKeyRetrieval=true + username: wvp_user + password: wvp_password + +media: + ip: 127.0.0.1 + http-port: 8080 +``` + +### 5. 启动应用 + +```powershell +java -jar target\wvp-pro-2.7.4-*.jar --spring.profiles.active=dev +``` + +--- + +## 方案二:安装 Maven 后编译 + +### 1. 下载 Maven + +访问:https://maven.apache.org/download.cgi +下载:apache-maven-3.9.6-bin.zip + +### 2. 解压并配置环境变量 + +```powershell +# 解压到 C:\Program Files\apache-maven-3.9.6 + +# 添加到系统PATH +setx /M PATH "%PATH%;C:\Program Files\apache-maven-3.9.6\bin" + +# 重启终端验证 +mvn -version +``` + +### 3. 编译项目 + +```powershell +cd C:\workspace\wvp-platform + +# 编译前端 +cd web +npm install --registry=https://registry.npmmirror.com +npm run build:prod +cd .. + +# 编译后端 +mvn clean package -Dmaven.test.skip=true +``` + +### 4. 启动应用 + +```powershell +java -jar target\wvp-pro-2.7.4-*.jar --spring.profiles.active=dev +``` + +--- + +## 方案三:使用已编译的版本(最快) + +如果您之前编译过项目,可以直接运行: + +```powershell +# 找到已编译的 jar 文件 +ls target\*.jar + +# 直接运行 +java -jar target\wvp-pro-2.7.4-.jar --spring.profiles.active=dev +``` + +--- + +## 验证服务 + +### 1. 检查基础服务状态 + +```powershell +docker ps --filter "name=polaris-" +``` + +应该看到: +- docker-polaris-redis-1 (健康运行) +- docker-polaris-mysql-1 (健康运行) +- docker-polaris-media-1 (运行中) + +### 2. 测试数据库连接 + +```powershell +docker exec -it docker-polaris-mysql-1 mysql -u wvp_user -pwvp_password -e "show databases;" +``` + +应该看到 `wvp` 数据库。 + +### 3. 访问应用 + +启动后访问: +- **管理后台**: http://localhost:18080 +- **API 文档**: http://localhost:18080/doc.html +- **默认账号**: admin / admin + +--- + +## 常见问题 + +### 1. 端口冲突 + +如果 18080 端口被占用: + +```yaml +# 修改 src/main/resources/application-dev.yml +server: + port: 28080 # 改为其他端口 +``` + +### 2. 数据库连接失败 + +检查 MySQL 是否健康: + +```powershell +docker logs docker-polaris-mysql-1 +``` + +### 3. Redis 连接失败 + +检查 Redis 状态: + +```powershell +docker exec -it docker-polaris-redis-1 redis-cli ping +``` + +应该返回 `PONG` + +### 4. ZLMediaKit 连接失败 + +检查 ZLMediaKit 日志: + +```powershell +docker logs docker-polaris-media-1 +``` + +--- + +## 功能模块位置 + +### 主码流/子码流切换 +- **控制器**: `src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceQuery.java` +- **服务**: `src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java` +- **API**: `/api/play/{deviceId}/{channelId}/start` (带 streamType 参数) + +### 云台控制(转向、拉近、拉远) +- **控制器**: `src/main/java/com/genersoft/iot/vmp/gb28181/controller/PtzController.java` +- **服务**: `src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PTZServiceImpl.java` +- **命令**: `src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java` +- **API**: + - `/api/front-end/{deviceId}/{channelId}/ptz` - PTZ控制 + - `/api/front-end/{deviceId}/{channelId}/left` - 左转 + - `/api/front-end/{deviceId}/{channelId}/right` - 右转 + - `/api/front-end/{deviceId}/{channelId}/up` - 上转 + - `/api/front-end/{deviceId}/{channelId}/down` - 下转 + - `/api/front-end/{deviceId}/{channelId}/zoom_in` - 拉近 + - `/api/front-end/{deviceId}/{channelId}/zoom_out` - 拉远 + +### 设备接入管理 +- **设备服务**: `src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java` +- **注册处理**: `src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java` +- **心跳处理**: `src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/MessageRequestProcessor.java` +- **SIP层**: `src/main/java/com/genersoft/iot/vmp/gb28181/SipLayer.java` + +--- + +## 停止服务 + +### 停止 WVP 应用 +在运行窗口按 `Ctrl + C` + +### 停止 Docker 服务 +```powershell +cd docker +docker compose down +``` + +### 只停止 WVP,保留基础服务 +```powershell +cd docker +docker compose stop polaris-wvp polaris-nginx +``` + +--- + +## 日志查看 + +### WVP 应用日志 +应用启动后,日志会输出到控制台和 `logs/` 目录 + +### Docker 服务日志 +```powershell +# 查看所有服务日志 +cd docker +docker compose logs -f + +# 查看特定服务日志 +docker compose logs -f polaris-mysql +docker compose logs -f polaris-redis +docker compose logs -f polaris-media +``` + +--- + +## 下一步 + +### 1. 测试设备接入 + +配置国标设备连接到 WVP: +- SIP 服务器: `你的IP地址` +- SIP 端口: `8160` +- SIP 域: `3502000000` +- 设备ID: 20位国标编码 +- 密码: `wvp_sip_password` + +### 2. 查看设备列表 + +访问 http://localhost:18080 后,在"国标设备"菜单查看已接入设备 + +### 3. 测试云台控制 + +在设备通道列表中,点击"云台控制"按钮测试 + +### 4. 测试视频播放 + +在通道列表中点击"播放"按钮,查看视频流 + +--- + +最后更新: 2026-02-02 diff --git a/开始使用.md b/开始使用.md new file mode 100644 index 000000000..f31e0ff6c --- /dev/null +++ b/开始使用.md @@ -0,0 +1,324 @@ +# 快速开始(使用 Java 17) + +## 📋 前置要求 + +✅ 已安装: +- Java 17(已确认) +- Node.js(已确认) +- Docker(已确认) + +⚠️ 需要安装: +- **Maven 3.3+** + +## 🔧 安装 Maven + +### 方法一:手动安装(推荐) + +1. **下载 Maven** + - 访问:https://maven.apache.org/download.cgi + - 下载:`apache-maven-3.9.9-bin.zip` + - 国内镜像:https://mirrors.aliyun.com/apache/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.zip + +2. **解压到指定目录** + ``` + 解压到:C:\Program Files\apache-maven-3.9.9 + ``` + +3. **配置环境变量** + - 打开"系统属性" → "高级" → "环境变量" + - 在"系统变量"中找到 `Path` + - 点击"编辑" → "新建" + - 添加:`C:\Program Files\apache-maven-3.9.9\bin` + - 点击"确定"保存 + +4. **验证安装** + + 打开**新的** CMD 或 PowerShell: + ```cmd + mvn -version + ``` + + 应该显示: + ``` + Apache Maven 3.9.9 + Maven home: C:\Program Files\apache-maven-3.9.9 + Java version: 17.0.x + ``` + +### 方法二:使用 Chocolatey(如果已安装) + +```powershell +# 以管理员身份运行 PowerShell +choco install maven +``` + +### 方法三:使用 Scoop + +```powershell +scoop install maven +``` + +--- + +## 🚀 启动步骤 + +### 第一次使用 + +1. **确保基础服务运行** + ```cmd + cd docker + docker compose up -d polaris-redis polaris-mysql polaris-media + cd .. + ``` + +2. **编译项目** + + 双击运行:`编译.bat` + + 或在 CMD 中: + ```cmd + 编译.bat + ``` + + 脚本会自动: + - ✅ 检查 Java 和 Maven + - ✅ 编译前端(Vue) + - ✅ 编译后端(Spring Boot) + - ✅ 检查基础服务状态 + +3. **启动应用** + + 双击运行:`启动.bat` + + 或在 CMD 中: + ```cmd + 启动.bat + ``` + +4. **访问应用** + - 管理后台:http://localhost:18080 + - API 文档:http://localhost:18080/doc.html + - 默认账号:`admin` / `admin` + +### 后续使用 + +修改代码后,只需: +```cmd +编译.bat +启动.bat +``` + +--- + +## 📂 功能模块位置 + +### 1. 主码流/子码流切换 +``` +src/main/java/com/genersoft/iot/vmp/gb28181/ +├── service/impl/PlayServiceImpl.java # 播放服务(包含码流切换逻辑) +├── controller/DeviceQuery.java # 设备查询控制器 +└── bean/DeviceChannel.java # 通道实体(streamType字段) +``` + +**API 端点:** +- 主码流:`GET /api/play/{deviceId}/{channelId}/start?streamType=0` +- 子码流:`GET /api/play/{deviceId}/{channelId}/start?streamType=1` + +### 2. 云台控制(转向、拉近、拉远) +``` +src/main/java/com/genersoft/iot/vmp/gb28181/ +├── controller/PtzController.java # 云台控制主接口 +├── service/ +│ ├── IPTZService.java # PTZ 服务接口 +│ └── impl/PTZServiceImpl.java # PTZ 服务实现 +├── transmit/cmd/impl/SIPCommander.java # 发送 SIP 命令 +└── bean/ + ├── FrontEndControlCodeForPTZ.java # PTZ 控制码 + └── FrontEndCode.java # 前端控制码 +``` + +**API 端点:** +- 左转:`GET /api/front-end/{deviceId}/{channelId}/left` +- 右转:`GET /api/front-end/{deviceId}/{channelId}/right` +- 上转:`GET /api/front-end/{deviceId}/{channelId}/up` +- 下转:`GET /api/front-end/{deviceId}/{channelId}/down` +- 拉近:`GET /api/front-end/{deviceId}/{channelId}/zoom_in` +- 拉远:`GET /api/front-end/{deviceId}/{channelId}/zoom_out` + +### 3. 无限制设备接入 +``` +src/main/java/com/genersoft/iot/vmp/gb28181/ +├── service/impl/DeviceServiceImpl.java # 设备管理服务 +├── transmit/event/request/impl/ +│ ├── RegisterRequestProcessor.java # 设备注册处理 +│ └── MessageRequestProcessor.java # 心跳和消息处理 +├── SipLayer.java # SIP 协议层 +└── dao/ + ├── DeviceMapper.java # 设备数据访问 + └── DeviceChannelMapper.java # 通道数据访问 +``` + +**性能配置:** +```yaml +# src/main/resources/application.yml +spring: + datasource: + hikari: + maximum-pool-size: 200 # 连接池大小 + minimum-idle: 10 + +sip: + pool-size: 100 # SIP 线程池 +``` + +--- + +## 🔍 常见问题 + +### 1. 编译失败:Maven 命令未找到 + +**原因**:Maven 未安装或环境变量未配置 + +**解决**:按照上面的"安装 Maven"步骤操作 + +### 2. 端口被占用 + +**错误**:`Address already in use: bind` + +**解决**:修改端口 +```yaml +# src/main/resources/application-dev.yml +server: + port: 28080 # 改为其他端口 +``` + +### 3. 数据库连接失败 + +**检查 MySQL 状态**: +```cmd +docker ps | findstr mysql +docker logs docker-polaris-mysql-1 +``` + +**测试连接**: +```cmd +docker exec -it docker-polaris-mysql-1 mysql -u wvp_user -pwvp_password -e "show databases;" +``` + +### 4. Redis 连接失败 + +**检查 Redis**: +```cmd +docker exec -it docker-polaris-redis-1 redis-cli ping +``` + +应该返回 `PONG` + +--- + +## 📝 配置文件说明 + +### 开发环境配置 +文件:`src/main/resources/application-dev.yml` + +```yaml +spring: + data: + redis: + host: 127.0.0.1 + port: 6379 + datasource: + url: jdbc:mysql://127.0.0.1:3306/wvp?... + username: wvp_user + password: wvp_password + +server: + port: 18080 + +media: + ip: 127.0.0.1 + http-port: 8080 +``` + +### Docker 配置 +文件:`docker/.env` + +```env +# 流媒体端口 +MediaRtmp=10001 +MediaRtsp=10002 +MediaRtp=10003 + +# Web 端口 +WebHttp=8080 + +# IP 配置(公网部署时需修改) +Stream_IP=127.0.0.1 +SDP_IP=127.0.0.1 + +# SIP 配置 +SIP_Port=8160 +SIP_Domain=3502000000 +``` + +--- + +## 🛠️ 服务管理 + +### 查看服务状态 +```cmd +docker ps --filter "name=polaris-" +``` + +### 启动基础服务 +```cmd +cd docker +docker compose up -d polaris-redis polaris-mysql polaris-media +``` + +### 停止所有服务 +```cmd +cd docker +docker compose down +``` + +### 查看日志 +```cmd +# WVP 日志(运行时控制台输出) + +# Docker 服务日志 +docker compose logs -f polaris-redis +docker compose logs -f polaris-mysql +docker compose logs -f polaris-media +``` + +--- + +## 📚 相关文档 + +- [完整启动指南](启动指南-Java17.md) +- [快速启动说明](QUICKSTART.md) +- [官方文档](https://doc.wvp-pro.cn) +- [GitHub](https://github.com/648540858/wvp-GB28181-pro) + +--- + +## ⚡ 快捷命令 + +```cmd +# 一键编译 +编译.bat + +# 一键启动 +启动.bat + +# 查看服务状态 +docker ps --filter "name=polaris-" + +# 重新编译并启动 +编译.bat && 启动.bat +``` + +--- + +最后更新:2026-02-02