# 标签系统类型化改造文档

## 📝 改造概述

将原有的单一标签系统改造为支持3种标签类型的系统，同时废除 `tb_resource_category` 表：
1. **文章分类标签** (tag_type=1) - 替代原 `tb_resource_category`
2. **课程分类标签** (tag_type=2) 
3. **学习任务分类标签** (tag_type=3)

### 改造日期
2025-10-27

### 改造原因
- ✅ **更清晰的业务划分** - 不同业务领域使用专属的标签类型
- ✅ **避免标签混淆** - 同名标签在不同类型下可独立存在
- ✅ **更好的可扩展性** - 便于未来添加新的标签类型
- ✅ **统一管理** - 将 `tb_resource_category` 合并到 `tb_tag`，使用 tag_type=1 表示文章分类标签
- ✅ **简化架构** - 减少数据表数量，统一标签管理逻辑

---

## 🗑️ 废除内容

### 1. 废除 tb_resource_category 表

**原因：** 资源分类功能已完全整合到标签系统中，使用 `tag_type=1` 的标签代替。

**废除内容：**
- ❌ `tb_resource_category` 表（已注释建表语句）
- ❌ `TbResourceCategory` 实体类（已删除）
- ❌ `ResourceCategoryService` 接口（已删除）
- ❌ `NCResourceCategoryServiceImpl` 实现类（已删除）
- ❌ `ResourceCategoryController` 控制器（已删除）
- ❌ `ResourceCategoryMapper` 及 XML（已删除）

**迁移方案：**
- 原 `tb_resource_category` 数据 → `tb_tag` (tag_type=1)
- 原 `/news/categorys` 接口 → `/news/tags/type/1` 接口
- 原 `ResourceVO.resourceCategory` 字段 → 改用 `ResourceVO.tags` 列表

---

## 🔄 数据库变更

### 1. 建表语句修改 (createTableResource.sql)

**修改内容：**
- 添加 `tag_type` 字段（INT(4)，默认值1）
- 修改唯一索引：从 `uk_tag_name` 改为 `uk_tag_name_type`（组合唯一索引）
- 添加索引：`idx_tag_type`

```sql
-- 标签表
DROP TABLE IF EXISTS `tb_tag`;
CREATE TABLE `tb_tag` (
  `id` VARCHAR(50) NOT NULL COMMENT '标签ID',
  `tag_id` VARCHAR(50) NOT NULL COMMENT '标签唯一标识',
  `name` VARCHAR(100) NOT NULL COMMENT '标签名称',
  `color` VARCHAR(20) DEFAULT NULL COMMENT '标签颜色',
  `description` VARCHAR(255) DEFAULT NULL COMMENT '标签描述',
  `tag_type` INT(4) DEFAULT 1 COMMENT '标签类型（1-文章分类标签 2-课程分类标签 3-学习任务分类标签）',
  `creator` VARCHAR(50) DEFAULT NULL COMMENT '创建者',
  `updater` VARCHAR(50) DEFAULT NULL COMMENT '更新者',
  `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `delete_time` TIMESTAMP NULL DEFAULT NULL COMMENT '删除时间',
  `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_tag_id` (`tag_id`),
  UNIQUE KEY `uk_tag_name_type` (`name`, `tag_type`),
  KEY `idx_tag_type` (`tag_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='标签表';
```

### 2. 初始化数据 (initAllData.sql)

**新增内容：**
- 添加15条初始标签数据（每种类型各5条）

```sql
-- 插入标签数据 (文章分类标签 tag_type=1)
INSERT INTO `tb_tag` (id, tag_id, name, color, description, tag_type, creator, create_time) VALUES
('tag001', 'tag_article_001', '党史', '#ff6b6b', '党史学习相关文章', 1, '1', now()),
('tag002', 'tag_article_002', '理论学习', '#4ecdc4', '理论学习相关文章', 1, '1', now()),
...

-- 插入标签数据 (课程分类标签 tag_type=2)
INSERT INTO `tb_tag` (id, tag_id, name, color, description, tag_type, creator, create_time) VALUES
('tag101', 'tag_course_001', '基础课程', '#26de81', '基础思政课程', 2, '1', now()),
('tag102', 'tag_course_002', '专题课程', '#fc5c65', '专题思政课程', 2, '1', now()),
...

-- 插入标签数据 (学习任务分类标签 tag_type=3)
INSERT INTO `tb_tag` (id, tag_id, name, color, description, tag_type, creator, create_time) VALUES
('tag201', 'tag_task_001', '每日学习', '#20bf6b', '每日学习任务', 3, '1', now()),
('tag202', 'tag_task_002', '专题学习', '#fa8231', '专题学习任务', 3, '1', now()),
...
```

---

## 🔄 代码变更

### 1. 枚举类新增

**文件：** `common\common-core\src\main\java\org\xyzh\common\core\enums\TagType.java`

```java
public enum TagType {
    ARTICLE_CATEGORY(1, "文章分类标签"),
    COURSE_CATEGORY(2, "课程分类标签"),
    LEARNING_TASK_CATEGORY(3, "学习任务分类标签");
    
    // ... 枚举方法
}
```

### 2. DTO实体类修改

**文件：** `common\common-dto\src\main\java\org\xyzh\common\dto\resource\TbTag.java`

**修改内容：**
- 添加 `tagType` 字段（Integer）
- 添加 getter/setter 方法
- 更新 toString() 方法

```java
/**
 * @description 标签类型 1-文章分类标签 2-课程分类标签 3-学习任务分类标签
 */
private Integer tagType;
```

### 3. Mapper接口修改

**文件：** `news\src\main\java\org\xyzh\news\mapper\TagMapper.java`

**修改内容：**
- 修改 `selectByType` → `selectByTagType`（参数名更明确）
- 修改 `countByName` → `countByNameAndType`（增加tagType参数）

```java
// 旧方法
List<TbTag> selectByType(@Param("type") Integer type);
int countByName(@Param("name") String name, @Param("excludeId") String excludeId);

// 新方法
List<TbTag> selectByTagType(@Param("tagType") Integer tagType);
int countByNameAndType(@Param("name") String name, @Param("tagType") Integer tagType, @Param("excludeId") String excludeId);
```

### 4. Mapper XML修改

**文件：** `news\src\main\resources\mapper\TagMapper.xml`

**修改内容：**
1. BaseResultMap 添加 `tag_type` 字段映射
2. Base_Column_List 添加 `tag_type` 字段
3. Where_Clause 添加 `tag_type` 条件
4. 修改 `selectByType` → `selectByTagType`
5. 修改 `countByName` → `countByNameAndType`
6. insertTag、updateTag、batchInsertTags 添加 `tag_type` 字段

```xml
<!-- 基础结果映射 -->
<resultMap id="BaseResultMap" type="org.xyzh.common.dto.resource.TbTag">
    ...
    <result column="tag_type" property="tagType" jdbcType="INTEGER"/>
    ...
</resultMap>

<!-- 基础字段 -->
<sql id="Base_Column_List">
    id, tag_id, name, color, description, tag_type, creator, updater, create_time, 
    update_time, delete_time, deleted
</sql>
```

### 5. Service接口修改

**文件：** `api\api-news\src\main\java\org\xyzh\api\news\tag\TagService.java`

**新增方法：**
```java
/**
 * @description 根据标签类型获取标签列表
 * @param tagType 标签类型（1-文章分类标签 2-课程分类标签 3-学习任务分类标签）
 * @return ResultDomain<TbTag> 标签列表
 */
ResultDomain<TbTag> getTagsByType(Integer tagType);
```

### 6. Service实现类修改

**文件：** `news\src\main\java\org\xyzh\news\service\impl\NCTagServiceImpl.java`

**修改内容：**
1. createTag 方法：调用 `countByNameAndType` 检查同类型下名称是否重复
2. updateTag 方法：调用 `countByNameAndType` 检查同类型下名称是否重复
3. 新增 getTagsByType 方法

```java
@Override
public ResultDomain<TbTag> getTagsByType(Integer tagType) {
    ResultDomain<TbTag> resultDomain = new ResultDomain<>();
    try {
        if (tagType == null) {
            resultDomain.fail("标签类型不能为空");
            return resultDomain;
        }

        // 验证标签类型是否有效（1-文章分类标签 2-课程分类标签 3-学习任务分类标签）
        if (tagType < 1 || tagType > 3) {
            resultDomain.fail("无效的标签类型");
            return resultDomain;
        }

        List<TbTag> tags = tagMapper.selectByTagType(tagType);
        resultDomain.success("查询成功", tags);
        return resultDomain;
    } catch (Exception e) {
        logger.error("根据类型查询标签异常: {}", e.getMessage(), e);
        resultDomain.fail("根据类型查询标签失败: " + e.getMessage());
        return resultDomain;
    }
}
```

### 7. Controller修改

**文件：** `news\src\main\java\org\xyzh\news\controller\TagController.java`

**新增接口：**
```java
/**
 * 根据标签类型获取标签列表
 * @param tagType 标签类型（1-文章分类标签 2-课程分类标签 3-学习任务分类标签）
 */
@GetMapping("/type/{tagType}")
public ResultDomain<TbTag> getTagsByType(@PathVariable Integer tagType) {
    return tagService.getTagsByType(tagType);
}
```

---

## 📋 API接口说明

### 新增接口

**接口路径：** `GET /news/tags/type/{tagType}`

**请求参数：**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| tagType | Integer | 是 | 标签类型：1-文章分类标签，2-课程分类标签，3-学习任务分类标签 |

**响应示例：**
```json
{
    "code": 200,
    "message": "查询成功",
    "data": [
        {
            "id": "tag001",
            "tagID": "tag_article_001",
            "name": "党史",
            "color": "#ff6b6b",
            "description": "党史学习相关文章",
            "tagType": 1,
            "createTime": "2025-10-27 10:00:00"
        }
    ]
}
```

### 修改的接口

**创建标签接口：** `POST /news/tags/tag`

现在创建标签时需要指定 `tagType` 字段：
```json
{
    "name": "新标签",
    "color": "#ff0000",
    "description": "标签描述",
    "tagType": 1
}
```

---

## ⚠️ 注意事项

### 1. 数据迁移

如果已有旧数据，需要执行以下SQL为现有标签设置默认类型：

```sql
-- 为现有标签设置默认类型为1（文章分类标签）
UPDATE tb_tag SET tag_type = 1 WHERE tag_type IS NULL;
```

### 2. 唯一性约束变更

- **旧约束：** 标签名称全局唯一
- **新约束：** 标签名称在同一类型下唯一

这意味着不同类型的标签可以使用相同的名称，例如：
- "基础" (tag_type=1, 文章分类标签)
- "基础" (tag_type=2, 课程分类标签)

### 3. 标签类型枚举值

| 类型值 | 说明 | 枚举常量 |
|--------|------|----------|
| 1 | 文章分类标签 | TagType.ARTICLE_CATEGORY |
| 2 | 课程分类标签 | TagType.COURSE_CATEGORY |
| 3 | 学习任务分类标签 | TagType.LEARNING_TASK_CATEGORY |

### 4. 向后兼容性

- 所有创建标签的操作必须指定 `tagType`
- 如果不指定，将使用默认值1（文章分类标签）
- 查询所有标签的接口 `/news/tags/list` 保持不变，会返回所有类型的标签

---

## ✅ 测试建议

### 1. 功能测试

- [ ] 创建不同类型的标签
- [ ] 验证同类型下标签名称唯一性
- [ ] 验证不同类型可以使用相同标签名
- [ ] 根据类型查询标签
- [ ] 更新标签（包括更改类型）
- [ ] 删除标签

### 2. 数据一致性测试

- [ ] 检查所有标签都有 `tag_type` 字段
- [ ] 验证唯一索引生效
- [ ] 测试并发创建同名标签

### 3. API测试

```bash
# 1. 创建文章分类标签
curl -X POST http://localhost:8080/news/tags/tag \
  -H "Content-Type: application/json" \
  -d '{"name":"测试标签","tagType":1,"color":"#ff0000"}'

# 2. 获取文章分类标签列表
curl http://localhost:8080/news/tags/type/1

# 3. 获取课程分类标签列表
curl http://localhost:8080/news/tags/type/2

# 4. 获取学习任务分类标签列表
curl http://localhost:8080/news/tags/type/3
```

---

## 📚 相关文件清单

### 数据库文件
- `.bin\mysql\sql\createTableResource.sql` - 建表语句（已注释 tb_resource_category）
- `.bin\mysql\sql\initAllData.sql` - 初始化数据（已注释旧分类数据，新增标签数据）

### 新增/修改的 Java 文件
- `common\common-core\src\main\java\org\xyzh\common\core\enums\TagType.java` - 枚举类（新增）
- `common\common-dto\src\main\java\org\xyzh\common\dto\resource\TbTag.java` - DTO实体类（修改）
- `api\api-news\src\main\java\org\xyzh\api\news\tag\TagService.java` - Service接口（修改）
- `news\src\main\java\org\xyzh\news\mapper\TagMapper.java` - Mapper接口（修改）
- `news\src\main\resources\mapper\TagMapper.xml` - MyBatis XML（修改）
- `news\src\main\java\org\xyzh\news\service\impl\NCTagServiceImpl.java` - Service实现（修改）
- `news\src\main\java\org\xyzh\news\controller\TagController.java` - Controller（修改）
- `common\common-dto\src\main\java\org\xyzh\common\vo\ResourceVO.java` - VO类（修改，删除 resourceCategory 字段）
- `news\src\main\java\org\xyzh\news\controller\ResourceCenterController.java` - 资源中心控制器（修改）

### 已删除的文件
- ~~`common\common-dto\src\main\java\org\xyzh\common\dto\resource\TbResourceCategory.java`~~
- ~~`api\api-news\src\main\java\org\xyzh\api\news\category\ResourceCategoryService.java`~~
- ~~`news\src\main\java\org\xyzh\news\service\NCResourceCategoryService.java`~~
- ~~`news\src\main\java\org\xyzh\news\service\impl\NCResourceCategoryServiceImpl.java`~~
- ~~`news\src\main\java\org\xyzh\news\controller\ResourceCategoryController.java`~~
- ~~`news\src\main\java\org\xyzh\news\mapper\ResourceCategoryMapper.java`~~
- ~~`news\src\main\resources\mapper\ResourceCategoryMapper.xml`~~

---

## 🎯 总结

本次改造实现了标签系统的类型化管理，使得：
1. **统一标签管理** - 将原 `tb_resource_category` 合并到 `tb_tag`，使用统一的标签系统
2. **类型化分类** - 文章、课程、学习任务可以使用各自的标签分类体系
3. **避免混淆** - 同名标签在不同类型下可独立存在
4. **简化架构** - 减少数据表和相关代码，降低维护成本
5. **灵活扩展** - 为未来添加新标签类型预留了空间

### 迁移前后对比

| 项目 | 迁移前 | 迁移后 |
|------|--------|--------|
| 数据表 | `tb_tag` + `tb_resource_category` | `tb_tag` (tag_type区分) |
| 实体类 | `TbTag` + `TbResourceCategory` | `TbTag` |
| Service | `TagService` + `ResourceCategoryService` | `TagService` |
| 文章分类 | 使用 `ResourceCategoryService` | 使用 `TagService.getTagsByType(1)` |
| 课程分类 | 无 | 使用 `TagService.getTagsByType(2)` |
| 学习任务分类 | 无 | 使用 `TagService.getTagsByType(3)` |

改造完成后，系统将具备更清晰的业务边界、更简洁的代码结构和更灵活的扩展能力。

