# 科技项目与活动 — 技术方案

> 依据：同目录 `科技项目与活动功能需求.md`。本模块为科技项目指南台账 **CRUD** + **发布/下架**；附件、封面复用若依 `/common/upload`。

---

## 1. 技术架构

| 项 | 说明 |
| --- | --- |
| **后端** | RuoYi **v3.9.2**（**springboot2** 分支）：JDK 8、Spring Boot 2.x、Spring MVC、MyBatis、Druid |
| **数据库** | MySQL **5.7.39**，InnoDB，`utf8mb4` |
| **前端** | 若依 Vue2；申报日期 `YYYY-MM-DD`；附件/封面上传后提交 URL、存储路径、原始文件名 |
| **代码包** | `com.ruoyi.web.modules.innovationplatform`（建议，与「创新应用平台」域一致） |

**分层**：`Controller`（`@PreAuthorize`、`AjaxResult` / `TableDataInfo`）→ `Service`（字段校验、**发布状态机**、替换附件删旧文件）→ `Mapper`/XML → `domain`。

**业务特征（摘要）**

| 场景 | 行为 |
| --- | --- |
| 新增「仅保存」 | `publish_status=0`，`publish_time=NULL` |
| 新增「保存并发布」 | Body `saveAndPublish=true` 或新增后调发布接口；`publish_status=1`，`publish_time=now()` |
| 修改 / 删除 / 发布 | **仅** `publish_status=0` |
| 下架 | **仅** `publish_status=1` → `0`，写 `offline_time`；**保留**附件与封面；**保留** `publish_time` 供审计 |
| 列表排序 | `publish_time DESC, create_time DESC, id DESC`（`publish_time` 为空的未发布记录靠后） |
| 列表筛选 | `projectName` 模糊；`projectType`、`publishStatus` 精确（可空） |

---

## 2. 数据库设计

### 2.1 表 `biz_tech_project_guide`（科技项目指南）

| 字段 | 类型 | 非空 | 默认值 | 说明 |
| --- | --- | --- | --- | --- |
| `id` | `bigint(20)` | Y | 自增 | 主键 |
| `project_name` | `varchar(64)` | Y | — | 项目名称（**1～20** 字） |
| `project_type` | `varchar(32)` | Y | — | 资讯类别编码，见 **§2.3** |
| `project_intro` | `varchar(100)` | Y | — | 项目简介（**1～50** 字） |
| `application_target` | `varchar(400)` | Y | — | 申报对象（**1～200** 字） |
| `application_start_date` | `date` | Y | — | 申报开始日期 |
| `application_end_date` | `date` | Y | — | 申报截止日期（≥ 开始日） |
| `project_period` | `varchar(64)` | Y | — | 项目周期（**1～50** 字） |
| `application_conditions` | `text` | Y | — | 申报条件 |
| `application_process` | `text` | Y | — | 申报流程 |
| `person_in_charge` | `varchar(32)` | Y | — | 负责人（**1～10** 字） |
| `contact_phone` | `varchar(20)` | Y | — | 联系方式（大陆手机号） |
| `attachment_file_url` | `varchar(512)` | N | NULL | 附件访问 URL |
| `attachment_file_path` | `varchar(512)` | N | NULL | 附件存储路径 |
| `attachment_file_name` | `varchar(256)` | N | NULL | 附件原始文件名 |
| `cover_file_url` | `varchar(512)` | N | NULL | 封面 URL |
| `cover_file_path` | `varchar(512)` | N | NULL | 封面存储路径 |
| `publish_status` | `tinyint(4)` | Y | `0` | `0` 未发布 `1` 已发布 |
| `publish_time` | `datetime` | N | NULL | 发布时间（列表「发布日期」取日期部分） |
| `offline_time` | `datetime` | N | NULL | 下架时间 |
| `create_by` / `create_time` / `update_by` / `update_time` | 若依惯例 | — | — | 审计 |
| `remark` | `varchar(500)` | N | NULL | 备注 |

**索引**：`PRIMARY KEY (id)`；`KEY idx_project_name (project_name)`；`KEY idx_project_type (project_type)`；`KEY idx_publish (publish_status)`；`KEY idx_publish_time (publish_time)`；`KEY idx_app_end (application_end_date)`。

### 2.2 DDL（MySQL 5.7）

```sql
CREATE TABLE `biz_tech_project_guide` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `project_name` varchar(64) NOT NULL COMMENT '项目名称',
  `project_type` varchar(32) NOT NULL COMMENT '项目类型编码，对应资讯类别 parentCode=002 下级',
  `project_intro` varchar(100) NOT NULL COMMENT '项目简介',
  `application_target` varchar(400) NOT NULL COMMENT '申报对象',
  `application_start_date` date NOT NULL COMMENT '申报开始日期',
  `application_end_date` date NOT NULL COMMENT '申报截止日期',
  `project_period` varchar(64) NOT NULL COMMENT '项目周期',
  `application_conditions` text NOT NULL COMMENT '申报条件',
  `application_process` text NOT NULL COMMENT '申报流程',
  `person_in_charge` varchar(32) NOT NULL COMMENT '负责人',
  `contact_phone` varchar(20) NOT NULL COMMENT '联系方式',
  `attachment_file_url` varchar(512) DEFAULT NULL COMMENT '附件URL',
  `attachment_file_path` varchar(512) DEFAULT NULL COMMENT '附件存储路径',
  `attachment_file_name` varchar(256) DEFAULT NULL COMMENT '附件原始文件名',
  `cover_file_url` varchar(512) DEFAULT NULL COMMENT '封面URL',
  `cover_file_path` varchar(512) DEFAULT NULL COMMENT '封面存储路径',
  `publish_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0未发布 1已发布',
  `publish_time` datetime DEFAULT NULL COMMENT '发布时间',
  `offline_time` datetime DEFAULT NULL COMMENT '下架时间',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`),
  KEY `idx_project_name` (`project_name`),
  KEY `idx_project_type` (`project_type`),
  KEY `idx_publish` (`publish_status`),
  KEY `idx_publish_time` (`publish_time`),
  KEY `idx_app_end` (`application_end_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='科技项目指南';
```

### 2.3 项目类型与资讯类别

- **数据来源**：`GET /category/informationCategory/children?parentCode=002`（`categoryType` 可省略）；响应字段 `code`（编码）、`name`（展示名）。
- **落库值**：`project_type` 存编码（如 `002001`）。
- **校验**：`TechProjectGuideTypeValidator`；非法编码返回「项目类型无效，请选择资讯类别中的类型」。

| 编码（示例） | 名称 |
| --- | --- |
| `002001` | 高新技术 |
| `002002` | 农业科技 |
| `002003` | 社会发展 |
| `002004` | 基础研究 |

**存量库迁移**：执行 `sql/biz_tech_project_guide_alter_project_type.sql`（`1`→`002001` … `4`→`002004`）。

### 2.4 发布状态枚举

**发布状态 `publish_status`**

| 值 | 标签 |
| --- | --- |
| 0 | 未发布 |
| 1 | 已发布 |

### 2.5 字段与 API 驼峰对照

| 需求名称 | 库字段 | API 驼峰 |
| --- | --- | --- |
| 项目名称 | `project_name` | `projectName` |
| 项目类型 | `project_type` | `projectType` |
| 项目简介 | `project_intro` | `projectIntro` |
| 申报对象 | `application_target` | `applicationTarget` |
| 申报开始日期 | `application_start_date` | `applicationStartDate` |
| 申报截止日期 | `application_end_date` | `applicationEndDate` |
| 项目周期 | `project_period` | `projectPeriod` |
| 申报条件 | `application_conditions` | `applicationConditions` |
| 申报流程 | `application_process` | `applicationProcess` |
| 负责人 | `person_in_charge` | `personInCharge` |
| 联系方式 | `contact_phone` | `contactPhone` |
| 附件 | `attachment_*` | `attachmentFileUrl` / `attachmentFilePath` / `attachmentFileName` |
| 封面 | `cover_*` | `coverFileUrl` / `coverFilePath` |
| 发布状态 | `publish_status` | `publishStatus` |
| 发布日期 | `publish_time` | `publishTime`（前端格式化为 `YYYY-MM-DD`） |
| 下架时间 | `offline_time` | `offlineTime` |

---

## 3. 接口设计

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

**权限标识（示例）**：`innovationPlatform:techProjectGuide:list|query|add|edit|remove|publish|offline`

**Base Path（示例）**：`/innovationPlatform/techProjectGuide`

| # | 说明 | Method | URI | 权限 | 要点 |
| --- | --- | --- | --- | --- | --- |
| 3.1 | 分页列表 | GET | `/innovationPlatform/techProjectGuide/list` | `...:list` | Query **§3.1.1** |
| 3.2 | 详情 | GET | `/innovationPlatform/techProjectGuide/{id}` | `...:query` | 全字段 |
| 3.3 | 新增 | POST | `/innovationPlatform/techProjectGuide` | `...:add` | Body **§3.3.1**；默认 `publish_status=0`；`saveAndPublish=true` 时同发布 |
| 3.4 | 修改 | PUT | `/innovationPlatform/techProjectGuide` | `...:edit` | **仅** `publish_status=0`；替换附件/封面删旧文件 |
| 3.5 | 删除 | DELETE | `/innovationPlatform/techProjectGuide/{ids}` | `...:remove` | **仅** `publish_status=0`；删附件与封面物理文件 |
| 3.6 | 发布 | POST | `/innovationPlatform/techProjectGuide/publish/{id}` | `...:publish` | 前置 `0`；校验 **§3.4**；`publish_status=1`，`publish_time=now()` |
| 3.7 | 下架 | POST | `/innovationPlatform/techProjectGuide/offline/{id}` | `...:offline` | 前置 `1`；`publish_status=0`，`offline_time=now()` |

#### 3.1.1 列表 Query

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `pageNum` / `pageSize` | int | N | 默认 `1` / `20` |
| `projectName` | string | N | 项目名称**模糊** |
| `projectType` | string | N | 资讯类别编码精确（如 `002002`） |
| `publishStatus` | int | N | `0` / `1` 精确 |

**列表行（示例）**：`id`、`projectName`、`projectType`、`applicationTarget`、`applicationStartDate`、`applicationEndDate`、`projectPeriod`、`personInCharge`、`contactPhone`、`publishStatus`、`publishTime`、`createTime`。

#### 3.3.1 新增 / 修改 Body（JSON，驼峰）

| 字段 | 新增 | 修改 | 说明 |
| --- | --- | --- | --- |
| `id` | — | Y | 主键 |
| `projectName` | Y | Y | 1～20 字符 |
| `projectType` | Y | Y | 资讯类别有效编码 |
| `projectIntro` | Y | Y | 1～50 字符 |
| `applicationTarget` | Y | Y | 1～200 字符 |
| `applicationStartDate` | Y | Y | `yyyy-MM-dd` |
| `applicationEndDate` | Y | Y | ≥ `applicationStartDate` |
| `projectPeriod` | Y | Y | 1～50 字符 |
| `applicationConditions` | Y | Y | 非空文本 |
| `applicationProcess` | Y | Y | 非空文本 |
| `personInCharge` | Y | Y | 1～10 字符 |
| `contactPhone` | Y | Y | 大陆手机号 |
| `attachmentFileUrl` / `Path` / `Name` | N | N | 三者同时有或同时无 |
| `coverFileUrl` / `coverFilePath` | N | N | 二者同时有或同时无 |
| `saveAndPublish` | N | — | 仅**新增**；`true` 时保存后执行发布逻辑 |
| `remark` | N | N | 备注 |

### 3.2 服务端校验（摘要）

| 项 | 规则 | 失败场景 |
| --- | --- | --- |
| 项目类型 | 非空且为资讯类别有效编码 | 非法编码 |
| 申报日期 | `applicationEndDate` ≥ `applicationStartDate` | 截止早于开始 |
| 手机号 | 项目统一正则（如 `^1[3-9]\d{9}$`） | 格式错误 |
| 附件 | 可空；有则后缀 `pdf,md,doc,docx,xls,xlsx,ppt,pptx`，≤30MB | 类型/大小不符 |
| 封面 | 可空；有则 `jpg,jpeg,png`，≤10MB | 类型/大小不符 |
| 发布状态 | 已发布禁止 **修改、删除、再次发布** | 返回明确业务提示 |
| 下架 | 未发布禁止下架 | 状态冲突 |
| 发布 | 未发布且必填项合法 | 已发布重复发布拒绝 |

### 3.3 正常流程与异常流程（覆盖）

| 流程 | 路径 | 预期 |
| --- | --- | --- |
| 新增仅保存 | POST Body 合法，`saveAndPublish` 缺省/false | `publish_status=0`，可编辑删除 |
| 新增并发布 | POST `saveAndPublish=true` 或 POST 后 `publish/{id}` | `publish_status=1`，`publish_time` 有值 |
| 列表筛选 | GET `projectName` / `projectType` / `publishStatus` | 条件生效 |
| 修改未发布 | PUT 合法 Body | 成功；可替换附件 |
| 修改已发布 | PUT 任意 | **失败** |
| 删除未发布 | DELETE | 成功；删物理文件 |
| 删除已发布 | DELETE | **失败** |
| 发布 | POST `publish/{id}`，`publish_status=0` | 变已发布 |
| 重复发布 | 已发布再 publish | **失败** |
| 下架 | POST `offline/{id}`，`publish_status=1` | 变未发布；`publish_time` 保留 |
| 下架后再编辑发布 | offline → PUT → publish | 全链路成功 |
| 未登录/无权限 | 任意写接口 | 401 / 无权限 |
| 详情不存在 | GET 无效 id | 404 或业务失败 |

### 3.4 附件流程

1. 前端 `POST /common/upload` → 取 `url`、`fileName`（path）、`originalFilename`（附件名）。  
2. 新增/修改提交 URL + path + 原始名（附件）。  
3. 修改替换或删除记录时，Service 删除旧 `attachment_file_path`、`cover_file_path` 物理文件。

---

## 4. 权限与前端路由（手工配置）

在若依「菜单管理」中配置，**本文档不提供 `sys_menu` INSERT SQL**。

| 类型 | 名称 | 权限标识 |
| --- | --- | --- |
| 菜单 | 科技项目与活动 | `innovationPlatform:techProjectGuide:list` |
| 按钮 | 查询 | `innovationPlatform:techProjectGuide:query` |
| 按钮 | 新增 | `innovationPlatform:techProjectGuide:add` |
| 按钮 | 修改 | `innovationPlatform:techProjectGuide:edit` |
| 按钮 | 删除 | `innovationPlatform:techProjectGuide:remove` |
| 按钮 | 发布 | `innovationPlatform:techProjectGuide:publish` |
| 按钮 | 下架 | `innovationPlatform:techProjectGuide:offline` |

组件路径建议：`innovationPlatform/techProjectGuide/index`；API 文件 `ruoyi-ui/src/api/innovationPlatform/techProjectGuide.js`。

---

## 5. 交付清单

- [ ] `sql/biz_tech_project_guide.sql`（仅 DDL，不含菜单）  
- [ ] `BizTechProjectGuide` Domain / Mapper / Service / Controller  
- [ ] 校验类（枚举、日期、手机号、发布状态机）  
- [ ] 前端：双卡片列表、新增/编辑弹窗（保存 / 发布）、发布状态控制操作列  
- [ ] 用例：见 `科技项目与活动管理测试用例.md`  

---

## 6. 修订记录

| 版本 | 说明 |
| --- | --- |
| 1.0 | 初稿：单表 CRUD + 发布/下架；对齐功能需求 |
| 1.1 | `project_type` 改为 `varchar(32)` 资讯类别编码；迁移脚本 `biz_tech_project_guide_alter_project_type.sql` |
