## 目标
- 当为用户分配一个已拥有且未过期的称号时，后端拒绝并提示“用户已拥有该称号”；避免连续多次分配同一称号。
- 前端在分配提交前做快速拦截与友好提示。

## 后端改造
- 修改 `AssignUserTitle` 处理器：`internal/api/admin/titles_admin.go`
  - 在创建/更新前，查询 `user_titles`：`user_id`+`title_id` 的记录，若 `active=1` 且 `expires_at` 为空或晚于当前时间 → 直接返回 400（或业务码 30117）提示“用户已拥有该称号”。
  - 保留过期的场景可重新分配；仅阻止“未过期且已激活”的重复分配。
- 响应格式：保持现有错误响应结构，统一错误消息文案。

## 前端改造（可选）
- 在 `UserAssignmentDialog.vue` 提交前判断：若选中用户当前头衔包含待分配的称号 ID，则 `ElMessage.warning('该用户已拥有该称号')` 并阻止提交。
- 若列表已有 `current_titles` 字段，直接使用；否则请求 `GET /api/admin/users/:user_id/titles` 取当前有效称号。

## 验收
- 对同一用户、同一称号：首次分配成功；再次分配立刻被后端拒绝并提示。
- 过期称号可重新分配；前端也能提示并拦截重复操作。