From 599314e0268d2a0978cfed206a9cc4299b2dc9a2 Mon Sep 17 00:00:00 2001 From: lzh Date: Sun, 5 Apr 2026 18:18:53 +0800 Subject: [PATCH] =?UTF-8?q?docs(video):=20WVP-Platform=20=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E6=8F=90=E6=A1=88=20(proposal=20+=20tasks=20+=20desig?= =?UTF-8?q?n)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 (1M context) --- .../add-video-module-wvp-migration/design.md | 395 ++++++++++++++++++ .../proposal.md | 138 ++++++ .../add-video-module-wvp-migration/tasks.md | 148 +++++++ 3 files changed, 681 insertions(+) create mode 100644 openspec/changes/add-video-module-wvp-migration/design.md create mode 100644 openspec/changes/add-video-module-wvp-migration/proposal.md create mode 100644 openspec/changes/add-video-module-wvp-migration/tasks.md diff --git a/openspec/changes/add-video-module-wvp-migration/design.md b/openspec/changes/add-video-module-wvp-migration/design.md new file mode 100644 index 00000000..5e7cea41 --- /dev/null +++ b/openspec/changes/add-video-module-wvp-migration/design.md @@ -0,0 +1,395 @@ +# Design: WVP-Platform 迁移技术设计 + +**Change ID**: `add-video-module-wvp-migration` + +--- + +## 1. 目录结构设计 + +``` +viewsh-module-video/ +├── viewsh-module-video-api/ # 契约层 +│ └── com.viewsh.module.video/ +│ ├── api/ # Feign RPC 接口(供其他模块调用) +│ └── enums/ # ApiConstants, ErrorCodeConstants +│ +└── viewsh-module-video-server/ # 业务层 + └── com.viewsh.module.video/ + ├── VideoServerApplication.java # 启动类 + │ + ├── gb28181/ # ← WVP gb28181/ 原样保留内部结构 + │ ├── SipLayer.java # SIP 协议栈初始化 + │ ├── auth/ # Digest 认证 + │ ├── bean/ # → 改名为 DO,加注解 + │ ├── controller/admin/ # → 移到 admin 包下 + │ ├── dao/ # → Mapper 改继承 BaseMapperX + │ ├── event/ # 事件驱动 + │ ├── service/ # 核心服务 + │ ├── session/ # SIP 会话管理 + │ ├── task/ # 设备心跳、订阅任务 + │ ├── transmit/ # SIP 信令处理(保持不动) + │ └── utils/ # GB28181 工具类 + │ + ├── media/ # ← WVP media/ 原样保留 + │ ├── bean/ # MediaServer, StreamInfo + │ ├── event/ # 媒体事件 + │ ├── service/ # IMediaServerService + │ ├── zlm/ # ZLMediaKit 集成 + │ │ └── ZLMHttpHookListener.java # Hook @RestController + │ └── abl/ # ABL 适配器 + │ + ├── aiot/ # ← WVP aiot/ AI 模块 + │ ├── bean/ + │ ├── controller/admin/ + │ ├── dao/ + │ └── service/ + │ + ├── storager/ # ← Redis 缓存存储(RedisCatchStorageImpl) + │ ├── dao/ # Mapper(MediaServer/RecordPlan/CloudRecord 等) + │ └── dto/ # DTO + │ + ├── streamProxy/ # ← 拉流代理 + ├── streamPush/ # ← 推流管理 + │ + ├── vmanager/ # ← 管理功能 + │ ├── cloudRecord/ + │ ├── recordPlan/ + │ ├── rtp/ + │ └── server/ + │ + ├── service/ # ← 通用服务 + ├── common/ # ← 公共模块 + ├── utils/ # ← 工具类 + │ + └── framework/ # 框架配置层 + ├── sip/ + │ └── SipConfig.java + ├── media/ + │ └── MediaConfig.java + ├── redis/ + │ ├── RedisTemplateConfig.java + │ ├── RedisMsgListenConfig.java + │ └── RedisRpcConfig.java + ├── websocket/ + │ └── WebSocketConfig.java + ├── security/config/ + │ └── SecurityConfiguration.java + └── config/ + ├── UserSetting.java + ├── ThreadPoolTaskConfig.java + ├── ScheduleConfig.java + └── CivilCodeFileConf.java +``` + +## 2. 包名映射表 + +``` +com.genersoft.iot.vmp.gb28181.* → com.viewsh.module.video.gb28181.* +com.genersoft.iot.vmp.media.* → com.viewsh.module.video.media.* +com.genersoft.iot.vmp.aiot.* → com.viewsh.module.video.aiot.* +com.genersoft.iot.vmp.streamProxy.* → com.viewsh.module.video.streamProxy.* +com.genersoft.iot.vmp.streamPush.* → com.viewsh.module.video.streamPush.* +com.genersoft.iot.vmp.vmanager.* → com.viewsh.module.video.vmanager.* +com.genersoft.iot.vmp.service.* → com.viewsh.module.video.service.* +com.genersoft.iot.vmp.common.* → com.viewsh.module.video.common.* +com.genersoft.iot.vmp.utils.* → com.viewsh.module.video.utils.* +com.genersoft.iot.vmp.conf.SipConfig → com.viewsh.module.video.framework.sip.SipConfig +com.genersoft.iot.vmp.conf.MediaConfig → com.viewsh.module.video.framework.media.MediaConfig +com.genersoft.iot.vmp.conf.UserSetting → com.viewsh.module.video.framework.config.UserSetting +com.genersoft.iot.vmp.conf.redis.* → com.viewsh.module.video.framework.redis.* +com.genersoft.iot.vmp.conf.websocket.* → com.viewsh.module.video.framework.websocket.* +com.genersoft.iot.vmp.conf.ThreadPool* → com.viewsh.module.video.framework.config.* +com.genersoft.iot.vmp.conf.Schedule* → com.viewsh.module.video.framework.config.* +com.genersoft.iot.vmp.storager.* → com.viewsh.module.video.storager.* +``` + +## 3. DO 改造设计 + +### 3.1 DO 基类选择 + +直接使用框架标准基类,统一逻辑删除,不创建自定义 VideoBaseDO。 + +| 基类 | 适用表 | 提供字段 | +|------|--------|---------| +| **TenantBaseDO** | wvp_device, wvp_device_channel, wvp_device_alarm, wvp_device_mobile_position, wvp_platform, wvp_platform_channel, wvp_platform_group, wvp_platform_region, wvp_common_group, wvp_common_region, wvp_stream_proxy, wvp_stream_push, wvp_cloud_record, wvp_record_plan, wvp_record_plan_item, wvp_ai_* | tenantId, createTime, updateTime, creator, updater, deleted | +| **BaseDO** | wvp_media_server | createTime, updateTime, creator, updater, deleted(无 tenantId) | + +### 3.1.1 硬删除改逻辑删除 + +WVP 原有 39 处 `DELETE FROM` 硬删除,统一改为 MyBatis Plus 逻辑删除: + +```java +// WVP 原始(硬删除) +@Delete("DELETE FROM wvp_device WHERE device_id = #{deviceId}") +int delete(@Param("deviceId") String deviceId); + +// 改造后(逻辑删除 — 自动改写为 UPDATE SET deleted=1) +// 直接用 BaseMapperX 内置方法 +mapper.delete(new LambdaQueryWrapperX() + .eq(DeviceDO::getDeviceId, deviceId)); +``` + +### 3.1.2 唯一索引调整 + +逻辑删除后,被"删除"的记录仍然存在。如果表有唯一索引,再次插入相同数据会冲突。需要将 `deleted` 加入联合唯一索引: + +```sql +-- 改造前 +UNIQUE INDEX uk_device_id (device_id) + +-- 改造后 +UNIQUE INDEX uk_device_id (device_id, deleted) +``` + +需要调整唯一索引的核心表: +- `wvp_device` — `device_id` +- `wvp_device_channel` — `device_db_id + channel_id` +- `wvp_platform` — `server_gb_id` +- `wvp_media_server` — `id`(业务主键) +- `wvp_stream_proxy` — `app + stream` +- `wvp_stream_push` — `app + stream` +- `wvp_common_group` — `device_id` +- `wvp_common_region` — `device_id` + +### 3.2 改造模式 + +```java +// WVP 原始 +@Data +@Schema(description = "国标设备") +public class Device { + private int id; + private String deviceId; + private String name; + private String createTime; // String 类型 + private String updateTime; // String 类型 + ... +} + +// 改造后(业务表 — 多租户 + 逻辑删除) +@Data +@TableName("wvp_device") +@EqualsAndHashCode(callSuper = true) +public class DeviceDO extends TenantBaseDO { + // TenantBaseDO 提供: tenantId, createTime, updateTime, creator, updater, deleted + @TableId(type = IdType.AUTO) + private Integer id; // 保持 Integer,不改 Long(降低改造风险) + private String deviceId; + private String name; + // 删除原有的 createTime/updateTime(TenantBaseDO 提供) + ... +} + +// 改造后(共享表 — 无租户,有逻辑删除) +@Data +@TableName("wvp_media_server") +@EqualsAndHashCode(callSuper = true) +public class MediaServerDO extends BaseDO { + // BaseDO 提供: createTime, updateTime, creator, updater, deleted + // 无 tenantId + @TableId(type = IdType.AUTO) + private Integer id; + ... +} +``` + +### 3.3 特殊处理 + +- **DeviceChannel**(86 字段):字段多但改造模式一致,批量处理 +- **关联查询结果 DTO**:不继承 VideoBaseDO,保持为普通 POJO(因为是 JOIN 查询结果,不对应单表) +- **WVP 的 `int id`**:保持 `Integer id`,不改 Long。数据库也保持 `INT`,等稳定后再逐步升级 + +## 4. Mapper 改造设计 + +### 4.1 简单 CRUD → BaseMapperX 内置方法 + +```java +// WVP 原始 +@Select("SELECT * FROM wvp_media_server WHERE id = #{id}") +MediaServer queryOne(String id); + +@Insert("INSERT INTO wvp_media_server (...) VALUES (...)") +int add(MediaServer mediaServer); + +// 改造后 +@Mapper +public interface MediaServerMapper extends BaseMapperX { + // 简单查询直接用继承方法 + // selectById(), insert(), updateById() 等 + + default MediaServerDO selectByServerId(String id) { + return selectOne(MediaServerDO::getId, id); + } +} +``` + +### 4.2 复杂查询 → 保留 @Select 注解 + +```java +// 多表 JOIN、动态 SQL 保留注解方式 +@Mapper +public interface DeviceChannelMapper extends BaseMapperX { + + // 复杂 JOIN 保留 @Select + @Select("") + List selectWithDevice(@Param("query") String query); +} +``` + +### 4.3 分页改造 + +```java +// WVP 原始(PageHelper) +PageHelper.startPage(page, count); +List devices = deviceMapper.getDevices(query); +return new PageInfo<>(devices); + +// 改造后(MyBatis Plus) +Page page = new Page<>(pageNo, pageSize); +IPage result = deviceMapper.selectPage(page, + new LambdaQueryWrapperX() + .likeIfPresent(DeviceDO::getName, query)); +return new PageResult<>(result.getRecords(), result.getTotal()); +``` + +## 5. 认证替换设计 + +### 5.1 SecurityConfiguration 更新 + +```java +@Configuration("videoSecurityConfiguration") +public class SecurityConfiguration { + @Bean("videoAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + @Override + public void customize(...registry) { + // 原有放行... + + // WVP Hook 回调(ZLM → WVP,无用户认证) + registry.requestMatchers("/index/hook/**").permitAll(); + // 设备快照公开访问 + registry.requestMatchers("/video/device/query/snap/**").permitAll(); + // SSE 推送 + registry.requestMatchers("/video/sse/**").permitAll(); + } + }; + } +} +``` + +### 5.2 用户获取替换 + +```java +// WVP: SecurityUtils.getUserInfo() 返回 LoginUser +// ↓ 替换为 +// viewsh: SecurityFrameworkUtils.getLoginUserId() 返回 Long +``` + +## 6. 数据库设计 + +### 6.1 独立数据库 `aiot-video` + +```yaml +# application-local.yaml +spring: + datasource: + dynamic: + datasource: + master: + url: jdbc:mysql://${MYSQL_HOST}:3306/aiot-video?... +``` + +### 6.2 表结构改造规则 + +每张 WVP 表需要: +1. 主键 `id INT AUTO_INCREMENT` 保持不变(不改 BIGINT,降低风险) +2. 业务表新增 `tenant_id BIGINT NOT NULL DEFAULT 0`(多租户隔离,共享表除外) +3. 新增 `creator VARCHAR(64) DEFAULT ''` +4. 新增 `updater VARCHAR(64) DEFAULT ''` +5. 新增 `deleted BIT(1) NOT NULL DEFAULT 0`(逻辑删除) +6. `create_time` / `update_time` 保留(类型改为 `DATETIME DEFAULT CURRENT_TIMESTAMP`) +7. 含唯一索引的表,将 `deleted` 加入联合唯一索引(避免逻辑删除后重复插入冲突) + +共享表(wvp_media_server)不加 `tenant_id`,需在 application.yaml 中配置: +```yaml +viewsh: + tenant: + ignore-tables: + - wvp_media_server +``` + +## 7. RedisTemplate 隔离设计 + +### 7.1 问题 + +viewsh 框架的 RedisTemplate 使用 Jackson 序列化(`RedisSerializer.json()`),WVP 的 RedisTemplate 使用 FastJSON2 序列化(`GenericFastJsonRedisSerializer`)。两者 Bean 名冲突会导致其他模块 Redis 崩溃。 + +### 7.2 方案 + +```java +// WVP 原始 RedisTemplateConfig +@Bean +public RedisTemplate redisTemplate(...) { ... } + +// 改造后:独立 Bean 名称 +@Configuration +public class VideoRedisTemplateConfig { + + @Bean("videoRedisTemplate") + public RedisTemplate videoRedisTemplate(RedisConnectionFactory factory) { + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(factory); + // FastJSON2 序列化(WVP 内部使用) + GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer(); + template.setKeySerializer(RedisSerializer.string()); + template.setValueSerializer(serializer); + template.setHashKeySerializer(RedisSerializer.string()); + template.setHashValueSerializer(serializer); + template.afterPropertiesSet(); + return template; + } +} +``` + +### 7.3 影响范围 + +WVP 内部所有注入 RedisTemplate 的地方(约 30+ 处)需要改为: + +```java +// 改造前 +@Autowired +private RedisTemplate redisTemplate; + +// 改造后 +@Autowired +@Qualifier("videoRedisTemplate") +private RedisTemplate redisTemplate; +``` + +主要涉及:`RedisRpcConfig`、`RedisCatchStorageImpl`、`RedisMsgListenConfig`、各 Listener 等。 + +## 8. Java 版本兼容性 + +WVP pom.xml 编译目标是 JDK 21,但**经扫描确认 WVP 未使用 Java 21 专有语法**。仅用了 `String.isBlank()`(Java 11 特性),JDK 17 完全兼容。 + +处理方式:不需要语法降级,只需确保 video-server 的 pom.xml 不设置 `21`(继承根 pom 的 JDK 17 配置即可)。 + +## 9. 网关路由补充 + +Hook 回调需要额外路由(ZLM 直接调用 video-server,不经过网关认证): + +```yaml +# viewsh-gateway application.yaml +- id: video-hook + uri: grayLb://video-server + predicates: + - Path=/index/hook/** +``` diff --git a/openspec/changes/add-video-module-wvp-migration/proposal.md b/openspec/changes/add-video-module-wvp-migration/proposal.md new file mode 100644 index 00000000..5da4761f --- /dev/null +++ b/openspec/changes/add-video-module-wvp-migration/proposal.md @@ -0,0 +1,138 @@ +# Proposal: 迁移 WVP-Platform 到 viewsh-module-video + +**Change ID**: `add-video-module-wvp-migration` +**Status**: Draft +**Branch**: `feature/video-module` +**Source**: `C:\workspace\java17\wvp-platform` (WVP-PRO v2.7.4, feature 分支) + +--- + +## Context + +项目需要视频监控管理能力(摄像头管理、国标 GB28181 信令、视频流、NVR 对接)。WVP-Platform 是一个成熟的开源 GB28181 视频平台(796 个 Java 文件),需要将其核心代码迁移到已创建的 `viewsh-module-video` 微服务模块中。 + +### 已完成 +- `viewsh-module-video` 模块骨架已创建(api + server),端口 48093 +- 网关路由已配置(video-admin-api / video-app-api) +- 分支 `feature/video-module` 已建立并提交骨架 + +### 核心决策(已确认) +| 决策项 | 结论 | +|--------|------| +| ORM | 全部改为 MyBatis Plus(BaseMapperX) | +| 认证 | 丢弃 WVP 认证,用 viewsh 框架 Security | +| 数据库 | 独立 `aiot-video` 数据库 | +| AI 模块 | 一起迁移 | +| JT1078 | 本次不迁,后续再说 | +| OkHttp | 保留(调用 ZLMediaKit REST API) | + +--- + +## Scope + +### 迁移范围(~700 文件) + +| WVP 模块 | 文件数 | 说明 | +|----------|--------|------| +| `gb28181/` | ~261 | GB28181 国标核心(SIP 信令、设备、通道、播放、级联) | +| `media/` | ~78 | 流媒体集成(ZLMediaKit + ABL 适配器) | +| `aiot/` | ~50 | AI 告警、边缘设备、ROI、截图 | +| `streamProxy/` | ~15 | 拉流代理 | +| `streamPush/` | ~15 | 推流管理 | +| `storager/` | ~15 | Redis 缓存存储、Mapper(RedisCatchStorageImpl 被 67 个文件引用) | +| `vmanager/` | ~40 | 录像计划、云端录像、RTP 配置 | +| `service/` | ~30 | 通用业务服务 | +| `common/` | ~20 | 公共工具 | +| `conf/`(部分) | ~15 | SIP/Media/Redis/WebSocket/线程池配置 | +| `utils/` | ~10 | 工具类 | + +### 不迁移 + +| WVP 模块 | 原因 | +|----------|------| +| `conf/security/` | 用 viewsh 框架认证替换 | +| `vmanager/user/` | 用 viewsh-module-system 替代 | +| `jt1078/` | 后续再迁 | +| `conf/ftpServer/` | 非核心,按需 | +| `conf/MybatisConfig.java` | 框架已有 | +| `conf/SpringDocConfig.java` | 框架已有 | +| User/Role 相关 Mapper 和 DO | 框架已有 | + +--- + +## Constraints(约束集) + +### Hard Constraints(硬约束) + +1. **HC-01 SIP 协议栈不可替换** — JAIN SIP 1.3.0-91 是 GB28181 核心,没有替代方案 +2. **HC-02 启动顺序必须保留** — SipLayer(@Order 10) → MediaServerConfig(@Order 12) → PlatformService(@Order 15) → DeviceService(@Order 16) +3. **HC-03 异步处理必须保留** — SIP 信令处理通过 @Async("taskExecutor") 异步执行,阻塞会导致设备超时 +4. **HC-04 ZLMediaKit Hook 路径必须保留** — `/index/hook/*` 是 ZLM 配置的回调地址,改路径需要同步改 ZLM 配置 +5. **HC-05 DO 继承框架标准基类** — 业务表 DO 继承 `TenantBaseDO`(含 tenantId/createTime/updateTime/creator/updater/deleted),共享表 DO 继承 `BaseDO`。统一使用逻辑删除,WVP 原有 39 处硬删除(`DELETE FROM`)改为 MyBatis Plus 的 `deleteById()`/`delete(wrapper)`。含唯一索引的表需将 `deleted` 加入联合唯一索引 +6. **HC-06 Mapper 必须继承 BaseMapperX** — viewsh 框架的 ORM 标准 +7. **HC-09 RedisTemplate 隔离** — WVP 用 FastJSON2 序列化的 RedisTemplate 必须声明为独立 Bean(`videoRedisTemplate`),不能覆盖框架的 Jackson 版本,否则其他模块 Redis 全部崩溃 +8. **HC-10 不引入 PageHelper** — WVP 用 PageHelper 分页,但不能引入 `pagehelper-spring-boot-starter`,会和 MyBatis Plus 分页插件冲突导致 double pagination +9. **HC-11 fastjson2 extension 用 spring6 版本** — WVP 依赖 `fastjson2-extension-spring5`,必须改为 `fastjson2-extension-spring6`(Spring Boot 3.x 基于 Spring 6) +10. **HC-12 Java 版本兼容** — WVP 编译目标是 JDK 21,但经扫描实际未使用 Java 21 专有语法(仅用了 isBlank 等 Java 11 特性),风险低,编译目标改为 17 即可 +11. **HC-13 多租户支持** — WVP 表需要加 `tenant_id` 列,DO 继承含 tenantId 的 VideoBaseDO,框架租户拦截器自动追加 `WHERE tenant_id=?`。少数全局共享表(如 media_server)需加入 `viewsh.tenant.ignore-tables` 排除 +7. **HC-07 独立数据库** — video-server 连接独立的 `aiot-video` 库,通过多数据源配置 +8. **HC-08 WVP 源从 feature 分支获取** — WVP 项目在 `C:\workspace\java17\wvp-platform`,需确认其 feature 分支 + +### Soft Constraints(软约束) + +1. **SC-01 包名规范** — `com.viewsh.module.video.*`,符合项目命名规范 +2. **SC-02 Controller 路径** — 放到 `controller/admin/` 包下,框架自动加 `/admin-api` 前缀 +3. **SC-03 分页统一** — PageHelper → MyBatis Plus Page + PageResult +4. **SC-04 Redis key 隔离** — 加 `video:` 前缀,避免和其他模块冲突 +5. **SC-05 异常码段** — `1-060-000-000` 段 + +### Dependencies(依赖关系) + +#### 框架已有(直接复用) +- Hutool 5.8.42、Guava 33.5.0、HttpClient、fastexcel + +#### 必须新增 +| 依赖 | 版本 | 用途 | +|------|------|------| +| jain-sip-ri | 1.3.0-91 | GB28181 SIP 协议栈 | +| dom4j | 2.1.4 | SIP XML 消息解析 | +| okhttp | 4.12.0 | 调用 ZLMediaKit REST API | +| okhttp-digest | 3.1.1 | HTTP Digest 认证 | +| bouncycastle | 1.78.1 | SIP 认证加密 | +| fastjson2 | 2.0.57 | Redis 序列化(WVP 用 v2) | +| fastjson2-extension-spring6 | 2.0.57 | Spring 6 适配(不能用 spring5 版本) | + +#### 按需引入 +| 依赖 | 版本 | 用途 | +|------|------|------| +| paho.mqttv5 | 1.2.5 | AI MQTT 推送(框架有 v3,WVP 用 v5) | +| jts-core | 1.18.2 | 地理位置计算 | +| cos_api | 5.6.227 | AI 截图上传腾讯云(可后续替换为 MinIO) | + +--- + +## Success Criteria + +1. **SC-BUILD**: `mvn compile -pl viewsh-module-video/viewsh-module-video-server` 编译通过 +2. **SC-BOOT**: VideoServerApplication 启动成功,SIP 端口监听正常 +3. **SC-SWAGGER**: 访问 `http://localhost:48093/doc.html` 能看到接口文档 +4. **SC-GATEWAY**: 通过网关 `http://localhost:48080/admin-api/video/` 能路由到 video-server +5. **SC-SIP**: 模拟器或真实摄像头能通过 SIP 注册到平台 +6. **SC-PLAY**: 点播一路摄像头,返回可用的播放地址 +7. **SC-DB**: 独立 `aiot-video` 数据库中表结构完整,CRUD 正常 + +--- + +## Risks + +| 风险 | 等级 | 缓解措施 | +|------|------|---------| +| ORM 改造量大(23 个 Mapper) | 高 | 分批改造,先简单后复杂;复杂 JOIN SQL 保留 @Select | +| SIP 初始化顺序问题 | 中 | @Order 值从 10/12/15/16 改为 100/102/105/106,给框架 Bean 留空间 | +| RedisTemplate Bean 冲突 | **高** | WVP 用独立 `videoRedisTemplate` Bean,内部 30+ 文件加 @Qualifier | +| 硬删除改逻辑删除 | 中 | WVP 39 处 `DELETE FROM` 改为 MyBatis Plus deleteById(),唯一索引加 deleted 字段 | +| Java 版本兼容性 | **低** | 经扫描 WVP 未使用 Java 21 专有语法,仅改编译目标即可 | +| 多租户改造 | 中 | 所有业务表加 tenant_id 列,共享表(media_server)加入 ignore-tables | +| PageHelper 与 MyBatis Plus 冲突 | 中 | 绝不引入 pagehelper 依赖,改用 MyBatis Plus 分页 | +| WVP 内部循环依赖 | 低 | application.yaml 已配 allow-circular-references=true | +| 表结构改造(加 VideoBaseDO 字段) | 中 | 编写 SQL 迁移脚本,加 createTime/updateTime/creator/updater | diff --git a/openspec/changes/add-video-module-wvp-migration/tasks.md b/openspec/changes/add-video-module-wvp-migration/tasks.md new file mode 100644 index 00000000..a8407252 --- /dev/null +++ b/openspec/changes/add-video-module-wvp-migration/tasks.md @@ -0,0 +1,148 @@ +# Tasks: WVP-Platform 迁移到 viewsh-module-video + +**Change ID**: `add-video-module-wvp-migration` +**Branch**: `feature/video-module` + +--- + +## Phase 0: 前置检查 + +- [x] **T0.1** ~~扫描 WVP 源码中的 Java 21 特性~~ — **已完成,无 Java 21 专有语法**,仅 `isBlank()`(Java 11),风险消除 +- [ ] **T0.2** 确认 WVP feature 分支的当前状态和内容 + +## Phase 1: 代码搬迁与包结构调整 + +- [ ] **T1.1** 从 `C:\workspace\java17\wvp-platform` 复制目标目录到 video-server + - 复制 `gb28181/`、`media/`、`aiot/`、`streamProxy/`、`streamPush/`、`vmanager/`、`service/`、`common/`、`utils/` + - 复制 `storager/`(含 RedisCatchStorageImpl,被 67 个文件引用) + - 复制 `conf/` 中保留的配置类(SipConfig、MediaConfig、UserSetting、redis、websocket、ThreadPool、Schedule、CivilCodeFileConf、VersionConfig) +- [ ] **T1.2** 全局替换包名 `com.genersoft.iot.vmp` → `com.viewsh.module.video` +- [ ] **T1.3** 删除不迁移的文件 + - `conf/security/` 全部 + - `conf/ftpServer/` 全部 + - `conf/MybatisConfig.java` + - `conf/SpringDocConfig.java` + - `vmanager/user/` 全部 + - `jt1078/` 全部 + - UserMapper、RoleMapper、UserApiKeyMapper + - User.java、Role.java、LoginUser.java、JwtUser.java + - VManageBootstrap.java +- [ ] **T1.4** 复制资源文件 + - `civilCode.csv`(行政区划数据) + +## Phase 2: pom.xml 依赖补充 + 配置文件整合(可并行) + +- [ ] **T2.1** 在 `viewsh-module-video-server/pom.xml` 添加必须依赖 + - jain-sip-ri 1.3.0-91 + - dom4j + - okhttp + - okhttp-digest 3.1.1 + - bouncycastle bcprov-jdk18on 1.78.1 + - fastjson2 + **fastjson2-extension-spring6**(不是 spring5) + - **不引入 pagehelper-spring-boot-starter** +- [ ] **T2.2** 添加按需依赖 + - paho.mqttv5 1.2.5 + - jts-core 1.18.2 + - cos_api 5.6.227(AI 截图) +- [ ] **T2.3** 在 `viewsh-dependencies/pom.xml` 注册新依赖版本管理 +- [ ] **T2.4** 在 application.yaml 中添加 SIP/Media/UserSettings/AI 配置段 +- [ ] **T2.5** 在 application-local.yaml 中配置独立 video 数据库连接(`aiot-video`) +- [ ] **T2.6** 配置 `spring.main.allow-circular-references=true`(已有,确认保留) + +## Phase 3: RedisTemplate 隔离(必须在编译修复前完成) + +- [ ] **T3.1** 改造 WVP 的 `RedisTemplateConfig`,Bean 名改为 `videoRedisTemplate`(不覆盖框架默认 Bean) +- [ ] **T3.2** WVP 内部所有 `@Autowired RedisTemplate` 的地方加 `@Qualifier("videoRedisTemplate")`(涉及 30+ 文件:RedisRpcConfig、RedisCatchStorageImpl 等) + +## Phase 4: 多租户配置 + +- [ ] **T4.1** 在 application.yaml 配置多租户忽略表 + ```yaml + viewsh: + tenant: + ignore-tables: + - wvp_media_server + ``` + > 不需要自定义基类,业务表 DO 直接继承 `TenantBaseDO`,共享表 DO 继承 `BaseDO` + +## Phase 5: 按域迭代 — 编译修复 + ORM 改造(合并执行) + +> Phase 3/4 原本分开的编译修复和 ORM 改造合并为一个阶段,按业务域迭代推进,每个域完成后做一次 mvn compile 验证。 + +### 5.1 GB28181 域(核心,最先做) +- [ ] **T5.1.1** 修复 gb28181/ 编译错误(认证引用、用户引用、JT1078 引用) +- [ ] **T5.1.2** DO 改造:Device/DeviceChannel/Platform/PlatformChannel/CommonGroup/CommonRegion/DeviceAlarm/DeviceMobilePosition → 加 @TableName、继承 TenantBaseDO +- [ ] **T5.1.3** Mapper 改造:DeviceMapper、DeviceChannelMapper、PlatformMapper、PlatformChannelMapper、GroupMapper、RegionMapper、CommonGBChannelMapper、DeviceAlarmMapper、DeviceMobilePositionMapper → 继承 BaseMapperX,复杂 SQL 保留 @Select +- [ ] **T5.1.4** `mvn compile` 验证 gb28181 域 + +### 5.2 Media + Storager 域 +- [ ] **T5.2.1** 修复 media/ 和 storager/ 编译错误 +- [ ] **T5.2.2** DO 改造:MediaServer(继承 BaseDO,无租户)、StreamInfo、CloudRecord/RecordPlan/RecordPlanItem(继承 TenantBaseDO) +- [ ] **T5.2.3** Mapper 改造:MediaServerMapper、CloudRecordServiceMapper、RecordPlanMapper +- [ ] **T5.2.4** `mvn compile` 验证 media + storager 域 + +### 5.3 StreamProxy + StreamPush 域 +- [ ] **T5.3.1** 修复编译错误 +- [ ] **T5.3.2** DO + Mapper 改造:StreamProxy、StreamPush +- [ ] **T5.3.3** `mvn compile` 验证 + +### 5.4 AI 域 +- [ ] **T5.4.1** 修复 aiot/ 编译错误 +- [ ] **T5.4.2** DO 改造:AiAlert/AiAlgorithm/AiAlgoTemplate/AiRoi/AiRoiAlgoBind/AiCameraSnapshot/AiConfigLog/AiEdgeDevice +- [ ] **T5.4.3** Mapper 改造(全部低复杂度) +- [ ] **T5.4.4** `mvn compile` 验证 AI 域 + +### 5.5 Vmanager + Service + Common 域 +- [ ] **T5.5.1** 修复 vmanager/(去掉 user/)、service/、common/ 编译错误 +- [ ] **T5.5.2** `mvn compile` 全量验证通过 + +### 5.6 硬删除改逻辑删除(跨域) +- [ ] **T5.6.1** 全局搜索 `@Delete` 和 `DELETE FROM`,将 39 处硬删除改为 BaseMapperX 的 `deleteById()` / `delete(wrapper)` +- [ ] **T5.6.2** 排查通道同步、级联取消共享等场景,确认逻辑删除后的业务语义正确 + +### 5.7 Service 层分页适配(跨域) +- [ ] **T5.7.1** PageHelper.startPage() → MyBatis Plus Page + selectPage() +- [ ] **T5.7.2** PageInfo → PageResult + +## Phase 6: 认证与安全整合 + +- [ ] **T6.1** 更新 SecurityConfiguration,放行 Hook 和公开路径 + - `/index/hook/**` → permitAll(ZLM 回调) + - `/video/device/query/snap/**` → permitAll(设备快照) + - `/video/sse/**` → permitAll(SSE 推送) +- [ ] **T6.2** Controller 中用户获取方式替换 + - WVP SecurityUtils → viewsh SecurityFrameworkUtils +- [ ] **T6.3** 删除 WVP 的 `user-settings.interface-authentication` 逻辑 + +## Phase 7: Controller 路径适配 + +- [ ] **T7.1** 所有 Controller 移到 `controller/admin/` 包下 +- [ ] **T7.2** 调整 @RequestMapping 路径 + - `/api/device/*` → `/video/device/*` + - `/api/gb28181/*` → `/video/gb28181/*` + - `/api/common/channel/*` → `/video/channel/*` + - `/api/ai/*` → `/video/ai/*` +- [ ] **T7.3** Hook 回调 Controller 保持 `/index/hook/*` 路径,网关加透传路由 + +## Phase 8: 数据库迁移 + +- [ ] **T8.1** 编写 `aiot-video` 数据库初始化 SQL + - 基于 WVP `数据库/2.7.4/初始化-mysql-2.7.4.sql` + - 去掉 user/role/api_key 表和 jt1078 表 + - 业务表加 `tenant_id BIGINT NOT NULL DEFAULT 0`(多租户) + - 共享表(wvp_media_server)不加 tenant_id + - 每张表加 `creator VARCHAR(64) DEFAULT ''`, `updater VARCHAR(64) DEFAULT ''` + - `create_time` / `update_time` 字段类型统一为 `DATETIME` + - 每张表加 `deleted BIT(1) NOT NULL DEFAULT 0`(逻辑删除) + - 含唯一索引的表(device、device_channel、platform、media_server、stream_proxy、stream_push、common_group、common_region),将 `deleted` 加入联合唯一索引 +- [ ] **T8.2** 导入 AI 模块表(从 `数据库/aiot/初始化-mysql-aiot.sql`) +- [ ] **T8.3** 编写初始化数据(默认媒体服务器配置等) + +## Phase 9: 验证与收尾 + +- [ ] **T9.1** `mvn compile` 全量编译通过 +- [ ] **T9.2** VideoServerApplication 启动成功,SIP 端口监听正常 +- [ ] **T9.3** Swagger 文档可访问 +- [ ] **T9.4** 网关路由验证 +- [ ] **T9.5** SIP 设备注册测试 +- [ ] **T9.6** 提交并整理 commit history