## ADDED Requirements

### Requirement: 标签实体定义

系统 SHALL 定义标签(Tag)实体，用于为资源（设备、IoT卡、号卡）提供自定义标签分类功能。

**核心概念**：
- 企业用户可以为自己的设备/卡片创建和管理标签
- 标签可以跨资源类型使用（一个标签可以同时用于设备和卡片）
- 支持按标签查询和筛选资源

**实体字段**：
- `id`：标签 ID（主键，BIGINT）
- `name`：标签名称（VARCHAR(100)，唯一）
- `color`：标签颜色（VARCHAR(20)，可选，用于前端显示，如 "#FF5733"）
- `usage_count`：使用次数（INT，默认 0，记录有多少资源使用了该标签）
- `creator`：创建人 ID（BIGINT）
- `updater`：更新人 ID（BIGINT）
- `created_at`：创建时间（TIMESTAMP，自动填充）
- `updated_at`：更新时间（TIMESTAMP，自动填充）
- `deleted_at`：删除时间（TIMESTAMP，可空，软删除）

**唯一约束**：`name` 在 `deleted_at IS NULL` 条件下唯一

#### Scenario: 创建标签

- **WHEN** 用户创建标签，名称为"生产设备"，颜色为"#FF5733"
- **THEN** 系统创建标签记录，`name` 为 "生产设备"，`color` 为 "#FF5733"，`usage_count` 为 0

#### Scenario: 标签名称重复

- **WHEN** 用户创建标签，名称为已存在的"生产设备"
- **THEN** 系统拒绝创建，返回错误信息"标签名称已存在"

#### Scenario: 更新标签

- **WHEN** 用户更新标签（ID 为 101），将颜色从"#FF5733"改为"#33FF57"
- **THEN** 系统更新标签记录，`color` 为 "#33FF57"，`updated_at` 为当前时间

---

### Requirement: 资源-标签关联

系统 SHALL 定义资源-标签关联(ResourceTag)实体，建立资源与标签的多对多关系，统一管理设备、IoT卡、号卡的标签。

**实体字段**：
- `id`：关联记录 ID（主键，BIGINT）
- `resource_type`：资源类型（VARCHAR(20)，枚举值："device"-设备 | "iot_card"-IoT卡 | "number_card"-号卡）
- `resource_id`：资源 ID（BIGINT）
- `tag_id`：标签 ID（BIGINT，关联 tb_tag.id）
- `creator`：创建人 ID（BIGINT）
- `updater`：更新人 ID（BIGINT）
- `created_at`：创建时间（TIMESTAMP，自动填充）
- `updated_at`：更新时间（TIMESTAMP，自动填充）
- `deleted_at`：删除时间（TIMESTAMP，可空，软删除）

**唯一约束**：`(resource_type, resource_id, tag_id)` 在 `deleted_at IS NULL` 条件下唯一

#### Scenario: 为设备添加标签

- **WHEN** 用户为设备（ID 为 1001）添加标签"生产设备"（ID 为 101）
- **THEN** 系统创建关联记录，`resource_type` 为 "device"，`resource_id` 为 1001，`tag_id` 为 101，标签的 `usage_count` 增加 1

#### Scenario: 为 IoT 卡添加标签

- **WHEN** 用户为 IoT 卡（ID 为 2001）添加标签"GPS"（ID 为 102）
- **THEN** 系统创建关联记录，`resource_type` 为 "iot_card"，`resource_id` 为 2001，`tag_id` 为 102，标签的 `usage_count` 增加 1

#### Scenario: 重复添加标签

- **WHEN** 用户为设备（ID 为 1001）添加已存在的标签"生产设备"（ID 为 101）
- **THEN** 系统拒绝操作，返回错误信息"该资源已添加此标签"

#### Scenario: 移除资源标签

- **WHEN** 用户移除设备（ID 为 1001）的标签"生产设备"（ID 为 101）
- **THEN** 系统删除关联记录（软删除），标签的 `usage_count` 减少 1

---

### Requirement: 按标签查询资源

系统 SHALL 支持按标签查询资源，用户可以选择一个或多个标签，查询包含这些标签的资源。

**查询模式**：
- **AND 模式**：查询同时包含所有指定标签的资源（交集）
- **OR 模式**：查询包含任一指定标签的资源（并集）

**查询条件**：
- 资源类型（必选，单选）
- 标签 ID 列表（必选，可多选）
- 查询模式（可选，默认 OR）

**分页**：
- 默认每页 20 条，最大每页 100 条
- 返回总记录数和总页数

#### Scenario: OR 模式查询设备

- **WHEN** 用户查询包含标签"生产设备"（ID 为 101）或"测试设备"（ID 为 102）的设备
- **THEN** 系统返回所有包含标签 101 或标签 102 的设备列表

#### Scenario: AND 模式查询设备

- **WHEN** 用户查询同时包含标签"生产设备"（ID 为 101）和"GPS"（ID 为 103）的设备
- **THEN** 系统返回同时包含标签 101 和标签 103 的设备列表

#### Scenario: 按标签查询 IoT 卡

- **WHEN** 用户查询包含标签"GPS"（ID 为 102）的 IoT 卡
- **THEN** 系统返回所有包含标签 102 的 IoT 卡列表

---

### Requirement: 获取资源的标签列表

系统 SHALL 支持查询指定资源的所有标签。

**查询条件**：
- 资源类型（必选）
- 资源 ID（必选）

**返回内容**：
- 标签列表（ID、名称、颜色）
- 按创建时间倒序排列

#### Scenario: 查询设备的标签

- **WHEN** 用户查询设备（ID 为 1001）的所有标签
- **THEN** 系统返回设备 1001 的标签列表，包含标签 ID、名称、颜色

#### Scenario: 查询没有标签的设备

- **WHEN** 用户查询设备（ID 为 1002）的所有标签，但该设备没有任何标签
- **THEN** 系统返回空列表

---

### Requirement: 热门标签查询

系统 SHALL 支持查询热门标签，按使用次数倒序排列。

**查询条件**：
- 限制数量（可选，默认 20）

**返回内容**：
- 标签列表（ID、名称、颜色、使用次数）
- 按使用次数倒序排列

#### Scenario: 查询热门标签

- **WHEN** 用户查询热门标签，限制 10 条
- **THEN** 系统返回使用次数最多的 10 个标签，按使用次数倒序排列

---

### Requirement: 标签批量操作

系统 SHALL 支持为资源批量添加或移除标签。

**批量添加**：
- 为一个资源添加多个标签
- 为多个资源添加同一个标签

**批量移除**：
- 为一个资源移除多个标签
- 为多个资源移除同一个标签

#### Scenario: 为设备批量添加标签

- **WHEN** 用户为设备（ID 为 1001）批量添加标签["生产设备", "GPS", "4G"]
- **THEN** 系统为设备 1001 创建 3 条关联记录，所有标签的 `usage_count` 各增加 1

#### Scenario: 批量为设备添加标签

- **WHEN** 用户为设备列表 [1001, 1002, 1003] 批量添加标签"生产设备"（ID 为 101）
- **THEN** 系统为 3 个设备各创建一条关联记录，标签"生产设备"的 `usage_count` 增加 3

#### Scenario: 为设备批量移除标签

- **WHEN** 用户为设备（ID 为 1001）批量移除标签["生产设备", "GPS"]
- **THEN** 系统删除设备 1001 的 2 条关联记录（软删除），所有标签的 `usage_count` 各减少 1

---

### Requirement: 标签数据校验

系统 SHALL 对标签数据进行校验，确保数据完整性和一致性。

**标签校验规则**：
- `name`：必填，长度 1-100 字符，唯一
- `color`：可选，长度 1-20 字符，建议使用十六进制颜色值（如 "#FF5733"）
- `usage_count`：必填，≥ 0

**资源-标签关联校验规则**：
- `resource_type`：必填，枚举值 "device" | "iot_card" | "number_card"
- `resource_id`：必填，≥ 1
- `tag_id`：必填，≥ 1，必须是有效的标签 ID

#### Scenario: 创建标签时名称为空

- **WHEN** 用户创建标签，名称为空
- **THEN** 系统拒绝创建，返回错误信息"标签名称不能为空"

#### Scenario: 创建标签时名称过长

- **WHEN** 用户创建标签，名称长度为 101 字符
- **THEN** 系统拒绝创建，返回错误信息"标签名称长度不能超过 100 字符"

#### Scenario: 添加标签时资源类型无效

- **WHEN** 用户为资源添加标签，`resource_type` 为 "invalid"
- **THEN** 系统拒绝操作，返回错误信息"资源类型无效"

#### Scenario: 添加标签时标签不存在

- **WHEN** 用户为设备添加标签，`tag_id` 为 99999（不存在的标签）
- **THEN** 系统拒绝操作，返回错误信息"标签不存在"
