# 网上培训课程 — 技术方案

> 依据：同目录 `网上培训课程功能需求.md`。本模块**只读**，数据**复用** `biz_tech_resource`（视频课程、已发布）；**不新建业务表**、不提供增删改发布接口。

---

## 1. 技术架构

| 项 | 说明 |
| --- | --- |
| **后端** | RuoYi **v3.9.2**（**springboot2** 分支）：JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid |
| **数据库** | MySQL **5.7.39**，InnoDB，`utf8mb4` |
| **前端** | 若依 Vue2（本期可后做；字段 **小驼峰**） |
| **代码包（建议）** | `com.ruoyi.web.modules.technology`（与「实战业务培训」同目录） |

**分层**：`BizOnlineTrainingCourseController` → `IBizOnlineTrainingCourseService` / `Impl` → `BizOnlineTrainingCourseMapper` + XML → 只读 **VO**（`OnlineTrainingCourseVo`）。

**数据策略**

| 场景 | 行为 |
| --- | --- |
| 列表 / 详情 | **实时查询** `biz_tech_resource`，固定 `resource_type=004008`（视频课程）、`publish_status=1`、`del_flag=0` |
| 维护课程 | 仍走 `/techService/techResource`（畜牧科技资源管理） |
| 列表排序 | `create_time DESC, id DESC` |
| 列表筛选 | `courseTitle` → `resource_name` **LIKE**；`courseTopic` → `course_topic` **=** |
| 详情不存在 / 已下架 | `ServiceException`「该课程已下线或不存在」 |

---

## 2. 数据库设计

### 2.1 复用表 `biz_tech_resource`（不新增表）

网上培训课程**不单独建表**；纳入记录即源表中已发布的视频课程行。

| 本模块语义 | 源表字段 | 说明 |
| --- | --- | --- |
| 课程标题 | `resource_name` | `varchar(64)` |
| 课程简介 | `introduction` | `varchar(500)` |
| 课程主题 | `course_topic` | `varchar(32)` |
| 视频 | `video_file_url` / `video_file_path` | `varchar(512)` |
| 封面 | `cover_file_url` / `cover_file_path` | 选填 |
| 发布时间 | `publish_time` | 详情可选展示 |
| 审计 | `create_time` / `update_time` 等 | 若依惯例 |

**固定查询条件（列表、详情均须满足）**

```text
resource_type = '004008'
AND publish_status = 1
AND del_flag = '0'
```

**索引（已有，无需本期变更）**：`idx_type (resource_type)`、`idx_publish (publish_status)`、`idx_del (del_flag)`、`idx_name (resource_name)`。若线上列表量大，可评审复合索引 `(resource_type, publish_status, del_flag, create_time)`。

### 2.2 源表结构摘要（实施参考）

表 DDL 见 `sql/biz_tech_resource.sql`；网上培训课程仅使用其中视频课程相关列，完整定义见《畜牧科技资源管理 — 技术方案》。

### 2.3 枚举（代码常量）

| 常量 | 值 | 说明 |
| --- | --- | --- |
| `RESOURCE_TYPE_VIDEO` | `4` | 视频课程（复用 `TechResourceRules`） |
| `PUBLISH_STATUS_PUBLISHED` | `1` | 已发布 |
| `DEL_FLAG_NORMAL` | `'0'` | 未删除 |

**课程主题**：存 `biz_tech_resource.course_topic` 资讯类别编码（`parentCode=005`、`categoryType=8`，如 `005001` 基础养殖）。下拉选项来自 `GET /category/informationCategory/children?parentCode=005&categoryType=8`；接口 `courseTopics` 另合并库内已发布课程的去重编码。

---

## 3. 接口设计

**统一响应**：`AjaxResult`（`code` / `msg` / `data`）或分页 `TableDataInfo`（`rows` / `total`）。

**权限标识**：`techService:onlineTrainingCourse:list|query`

**Base Path**：`/techService/onlineTrainingCourse`

| # | 说明 | Method | URI | 权限 |
| --- | --- | --- | --- | --- |
| 3.1 | 分页列表 | GET | `/techService/onlineTrainingCourse/list` | `list` |
| 3.2 | 详情 | GET | `/techService/onlineTrainingCourse/{id}` | `query` |
| 3.3 | 课程主题下拉 | GET | `/techService/onlineTrainingCourse/courseTopics` | `list` |

> **本期无** POST / PUT / DELETE / 发布 / 下架。

### 3.1 分页列表

**Query**

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `pageNum` / `pageSize` | int | N | 默认 `1` / `20` |
| `courseTitle` | string | N | 课程标题，对 `resource_name` **模糊**；trim 后空则不过滤 |
| `courseTopic` | string | N | 课程主题，对 `course_topic` **精确**；空则不过滤 |

**`rows` 元素（`OnlineTrainingCourseListVo`，驼峰）**

| 字段 | 来源 | 说明 |
| --- | --- | --- |
| `id` | `id` | 主键 |
| `courseTitle` | `resource_name` | 课程标题 |
| `courseIntro` | `introduction` | 课程简介（列表可原样返回，前端摘要） |
| `courseTopic` | `course_topic` | 课程主题 |
| `videoFileName` | `video_file_path` | 取路径最后一段文件名；无则「已上传」 |
| `createTime` | `create_time` | 可选，供排序核对 |

**列表 SQL 不返回**：`video_file_url`（详情再看）、`cover_*`（减轻列表负载）。

### 3.2 详情

**Path**：`id` — `biz_tech_resource.id`

**`data`（`OnlineTrainingCourseDetailVo`）**

| 字段 | 来源 | 说明 |
| --- | --- | --- |
| `id` | `id` | |
| `courseTitle` | `resource_name` | |
| `courseIntro` | `introduction` | 全文 |
| `courseTopic` | `course_topic` | |
| `videoFileUrl` | `video_file_url` | 播放地址 |
| `videoFilePath` | `video_file_path` | |
| `videoFileName` | 派生 | 同列表 |
| `coverFileUrl` | `cover_file_url` | 可空 |
| `coverFilePath` | `cover_file_path` | 可空 |
| `publishTime` | `publish_time` | 可空 |
| `createTime` / `updateTime` | 审计字段 | 建议返回 |

**校验**：查询结果须满足 **§2.1** 三条件；否则 `ServiceException`「该课程已下线或不存在」。

### 3.3 课程主题下拉

**响应 `data`**：`string[]`，升序去重。

**SQL 逻辑**：在 **§2.1** 条件下 `SELECT DISTINCT course_topic`，过滤空串；实现类可合并预设四主题 + 库内 DISTINCT，去重排序后返回（预设在前或全量排序，产品无严格要求时按字母序即可）。

### 3.4 Mapper 约定

- 独立 `BizOnlineTrainingCourseMapper.xml`，**只读** `selectList`、`selectById`、`selectDistinctCourseTopics`。  
- **禁止** 在本模块 XML 中写 insert/update/delete。  
- 列表 `WHERE` 固定 **§2.1** + 动态 `courseTitle` / `courseTopic`。  
- 详情 `WHERE id = #{id}` + **§2.1**（一条 SQL 即可，无行则 Service 抛异常）。

### 3.5 Service 要点

| 类 | 职责 |
| --- | --- |
| `OnlineTrainingCourseRules` | 固定类型/发布态/删除标记常量；`videoFileName(path)` 工具 |
| `BizOnlineTrainingCourseServiceImpl` | 列表分页、详情、主题列表；Entity → Vo 映射 |

**不依赖** `TechResourceValidation`（无写操作）；可读 `TechResourceRules.RESOURCE_TYPE_VIDEO` 等常量，避免魔法数。

---

## 4. 菜单与权限（示例）

| 类型 | 名称 | 权限标识 |
| --- | --- | --- |
| 菜单 | 网上培训课程 | `techService:onlineTrainingCourse:list` |
| 按钮 | 查询 | `techService:onlineTrainingCourse:query` |

组件路径（建议）：`techService/onlineTrainingCourse/index`（挂在「科技平台与服务」目录下）。

**菜单 SQL**：本期仅 `list`、`query` 两个按钮权限；**无** add/edit/remove。

---

## 5. 与畜牧科技资源管理的关系

| 项 | 约定 |
| --- | --- |
| 写操作 | 仅 `/techService/techResource`，`resourceType=4` |
| 发布生效 | 源库 `publish_status=1` 后，本模块 list/detail 立即可见 |
| 下架/删除 | 源库下架或 `del_flag=2` 后，本模块查不到；详情返回 **§3.2** 异常文案 |
| 字段长度/附件规则 | 以源库 `TechResourceValidation` 为准，本模块不重复校验 |

---

## 6. 交付清单

- [ ] `OnlineTrainingCourseVo`（list/detail）、`OnlineTrainingCourseRules`
- [ ] `BizOnlineTrainingCourseMapper` + `mapper/technology/BizOnlineTrainingCourseMapper.xml`
- [ ] `IBizOnlineTrainingCourseService` / `Impl`、`BizOnlineTrainingCourseController`
- [ ] 菜单权限 SQL（`onlineTrainingCourse:list|query`）
- [ ] 单元测试：文件名解析、纳入条件过滤（Mock Mapper）
- [ ] MockMvc：list 筛选、`/{id}` 成功与下线失败、`courseTopics`
- [ ] **无需** `sql/biz_online_training_course.sql`

---

## 7. 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初稿：复用 `biz_tech_resource`；只读 list/detail/courseTopics；包路径 `technology`；Base `/techService/onlineTrainingCourse` |
