diff --git a/.idea/.cache/.Apifox_Helper/.toolWindow.db b/.idea/.cache/.Apifox_Helper/.toolWindow.db
index a5693ff..7d3a966 100644
Binary files a/.idea/.cache/.Apifox_Helper/.toolWindow.db and b/.idea/.cache/.Apifox_Helper/.toolWindow.db differ
diff --git a/.idea/ApifoxUploaderProjectSetting.xml b/.idea/ApifoxUploaderProjectSetting.xml
index 4c2b765..517f9ea 100644
--- a/.idea/ApifoxUploaderProjectSetting.xml
+++ b/.idea/ApifoxUploaderProjectSetting.xml
@@ -5,9 +5,10 @@
-
-
+
+
\ No newline at end of file
diff --git a/docs/video-user模块目录架构、功能及接口文档.md b/docs/video-user模块目录架构、功能及接口文档.md
new file mode 100644
index 0000000..a9e8e37
--- /dev/null
+++ b/docs/video-user模块目录架构、功能及接口文档.md
@@ -0,0 +1,751 @@
+# video-user模块目录架构、功能及接口文档
+
+## 一、目录架构
+
+```
+video-user/
+├── src/
+│ ├── main/
+│ │ ├── java/
+│ │ │ └── com/nuaa/video/user/
+│ │ │ ├── config/ # 配置类
+│ │ │ │ ├── FeignConfig.java # Feign配置类
+│ │ │ │ ├── MinioConfig.java # Minio配置类
+│ │ │ │ └── SecurityConfig.java # 安全配置类
+│ │ │ ├── controller/ # 控制器
+│ │ │ │ ├── UserProfileController.java # 用户资料控制器
+│ │ │ │ ├── UserFollowController.java # 用户关注控制器
+│ │ │ │ ├── UserMessageController.java # 用户私信控制器
+│ │ │ │ ├── UserNotificationController.java # 用户通知控制器
+│ │ │ │ └── UserSettingController.java # 用户设置控制器
+│ │ │ ├── dto/ # 数据传输对象
+│ │ │ │ ├── UserProfileDTO.java # 用户资料DTO
+│ │ │ │ ├── UserMessageDTO.java # 用户私信DTO
+│ │ │ │ └── UserSettingDTO.java # 用户设置DTO
+│ │ │ ├── entity/ # 实体类
+│ │ │ │ ├── UserInfo.java # 用户信息实体
+│ │ │ │ ├── UserFollow.java # 用户关注实体
+│ │ │ │ ├── UserStatDaily.java # 用户每日统计实体
+│ │ │ │ ├── UserMessage.java # 用户私信实体
+│ │ │ │ ├── UserNotification.java # 用户通知实体
+│ │ │ │ └── UserSetting.java # 用户设置实体
+│ │ │ ├── feign/ # Feign客户端
+│ │ │ │ └── AuthFeignClient.java # 认证服务Feign客户端
+│ │ │ ├── filter/ # 过滤器
+│ │ │ │ └── JwtAuthenticationFilter.java # JWT认证过滤器
+│ │ │ ├── mapper/ # MyBatis映射接口
+│ │ │ │ ├── UserInfoMapper.java # 用户信息Mapper
+│ │ │ │ ├── UserFollowMapper.java # 用户关注Mapper
+│ │ │ │ ├── UserStatDailyMapper.java # 用户每日统计Mapper
+│ │ │ │ ├── UserMessageMapper.java # 用户私信Mapper
+│ │ │ │ ├── UserNotificationMapper.java # 用户通知Mapper
+│ │ │ │ └── UserSettingMapper.java # 用户设置Mapper
+│ │ │ ├── service/ # 服务层
+│ │ │ │ ├── UserInfoService.java # 用户信息服务接口
+│ │ │ │ ├── UserFollowService.java # 用户关注服务接口
+│ │ │ │ ├── UserMessageService.java # 用户私信服务接口
+│ │ │ │ ├── UserNotificationService.java # 用户通知服务接口
+│ │ │ │ ├── UserSettingService.java # 用户设置服务接口
+│ │ │ │ └── impl/ # 实现类
+│ │ │ │ ├── UserInfoServiceImpl.java
+│ │ │ │ ├── UserFollowServiceImpl.java
+│ │ │ │ ├── UserMessageServiceImpl.java
+│ │ │ │ ├── UserNotificationServiceImpl.java
+│ │ │ │ └── UserSettingServiceImpl.java
+│ │ │ ├── vo/ # 视图对象
+│ │ │ │ ├── UserProfileVO.java # 用户资料VO
+│ │ │ │ ├── UserMessageVO.java # 用户私信VO
+│ │ │ │ └── UserNotificationVO.java # 用户通知VO
+│ │ │ └── UserApplication.java # 启动类
+│ │ └── resources/
+│ │ ├── mapper/ # MyBatis XML映射文件
+│ │ │ ├── UserInfoMapper.xml
+│ │ │ ├── UserFollowMapper.xml
+│ │ │ ├── UserMessageMapper.xml
+│ │ │ └── UserNotificationMapper.xml
+│ │ ├── application.yml # 主配置文件
+│ │ └── application-dev.yml # 开发环境配置
+└── pom.xml
+```
+
+## 二、功能概述
+
+### 1. 核心功能
+
+- **用户资料管理**:创建、查询、更新用户资料,上传头像
+- **用户关系管理**:关注/取消关注用户,查看关注列表和粉丝列表
+- **私信系统**:发送和接收私信,管理会话和消息状态
+- **通知系统**:管理用户收到的系统通知和用户互动通知
+- **用户设置**:管理用户隐私和通知偏好设置
+- **用户数据统计**:收集和查询用户活动统计信息
+
+### 2. 业务流程
+
+1. **用户资料流程**:
+ - 用户注册时,auth服务创建基本认证信息
+ - auth服务通过Feign调用user服务创建用户资料
+ - 用户可随时更新个人资料信息
+
+2. **用户关系流程**:
+ - 用户关注他人,增加自己的关注数和对方的粉丝数
+ - 系统为被关注用户创建通知
+ - 用户可查看关注列表和粉丝列表
+
+3. **私信流程**:
+ - 用户发送私信给其他用户
+ - 接收用户收到未读消息通知
+ - 用户可查看会话列表和详细私信记录
+
+4. **通知流程**:
+ - 系统根据用户互动(点赞、评论、关注)创建通知
+ - 用户可查看不同类型的通知
+ - 用户可标记通知为已读
+
+## 三、接口文档
+
+### 1. 用户资料接口
+
+#### 1.1 初始化用户资料
+
+- **URI**: `/user/profile/init`
+- **方法**: POST
+- **描述**: 服务内部接口,初始化用户资料(由auth服务在用户注册时调用)
+- **请求参数**:
+ - `userId`: 用户ID
+ - `username`: 用户名
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+- **错误码**:
+ - `400`: 参数错误
+ - `500`: 服务器内部错误
+
+#### 1.2 获取当前用户资料
+
+- **URI**: `/user/profile/current`
+- **方法**: GET
+- **描述**: 获取当前登录用户的资料
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "id": 1,
+ "nickname": "用户昵称",
+ "avatar": "头像URL",
+ "gender": 1,
+ "birthday": "1990-01-01",
+ "signature": "个性签名",
+ "region": "地区",
+ "followerCount": 10,
+ "followingCount": 20,
+ "videoCount": 5,
+ "likeCount": 100,
+ "level": 3,
+ "vipLevel": 0,
+ "vipExpireTime": null,
+ "isFollowed": false,
+ "createTime": "2023-01-01T12:00:00"
+ }
+ }
+ ```
+
+#### 1.3 获取指定用户资料
+
+- **URI**: `/user/profile/public/{userId}`
+- **方法**: GET
+- **描述**: 获取指定用户的资料
+- **路径参数**:
+ - `userId`: 用户ID
+- **响应**: 与获取当前用户资料相同
+
+#### 1.4 更新用户资料
+
+- **URI**: `/user/profile`
+- **方法**: PUT
+- **描述**: 更新当前用户资料
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **请求Body**:
+ ```json
+ {
+ "nickname": "新昵称",
+ "gender": 1,
+ "birthday": "1990-01-01",
+ "signature": "新的个性签名",
+ "region": "新的地区"
+ }
+ ```
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+
+#### 1.5 上传头像
+
+- **URI**: `/user/profile/avatar`
+- **方法**: POST
+- **描述**: 上传用户头像
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+ - `Content-Type`: multipart/form-data
+- **请求参数**:
+ - `file`: 图片文件
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": "头像URL"
+ }
+ ```
+
+#### 1.6 搜索用户
+
+- **URI**: `/user/profile/search`
+- **方法**: GET
+- **描述**: 按关键词搜索用户
+- **请求参数**:
+ - `keyword`: 搜索关键词
+ - `page`: 页码(默认1)
+ - `size`: 每页数量(默认10)
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "records": [
+ {
+ "id": 1,
+ "nickname": "用户昵称",
+ "avatar": "头像URL",
+ "signature": "个性签名",
+ "followerCount": 10,
+ "followingCount": 20
+ }
+ ],
+ "total": 100,
+ "size": 10,
+ "current": 1,
+ "pages": 10
+ }
+ }
+ ```
+
+### 2. 用户关注接口
+
+#### 2.1 关注用户
+
+- **URI**: `/user/follow/{followId}`
+- **方法**: POST
+- **描述**: 关注指定用户
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **路径参数**:
+ - `followId`: 要关注的用户ID
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+- **错误码**:
+ - `400`: 参数错误(如已关注)
+ - `404`: 用户不存在
+
+#### 2.2 取消关注
+
+- **URI**: `/user/follow/{followId}`
+- **方法**: DELETE
+- **描述**: 取消关注指定用户
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **路径参数**:
+ - `followId`: 要取消关注的用户ID
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+
+#### 2.3 获取关注列表
+
+- **URI**: `/user/follow/following/{userId}`
+- **方法**: GET
+- **描述**: 获取指定用户的关注列表
+- **路径参数**:
+ - `userId`: 用户ID
+- **请求参数**:
+ - `page`: 页码(默认1)
+ - `size`: 每页数量(默认10)
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "records": [
+ {
+ "id": 2,
+ "nickname": "被关注用户",
+ "avatar": "头像URL",
+ "signature": "个性签名",
+ "followerCount": 100,
+ "followingCount": 50,
+ "isFollowed": true
+ }
+ ],
+ "total": 20,
+ "size": 10,
+ "current": 1,
+ "pages": 2
+ }
+ }
+ ```
+
+#### 2.4 获取粉丝列表
+
+- **URI**: `/user/follow/followers/{userId}`
+- **方法**: GET
+- **描述**: 获取指定用户的粉丝列表
+- **路径参数**:
+ - `userId`: 用户ID
+- **请求参数**:
+ - `page`: 页码(默认1)
+ - `size`: 每页数量(默认10)
+- **响应**: 与关注列表格式相同
+
+#### 2.5 检查是否已关注
+
+- **URI**: `/user/follow/check/{followId}`
+- **方法**: GET
+- **描述**: 检查当前用户是否已关注指定用户
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **路径参数**:
+ - `followId`: 被检查的用户ID
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": true // 或 false
+ }
+ ```
+
+### 3. 用户私信接口
+
+#### 3.1 发送私信
+
+- **URI**: `/user/message`
+- **方法**: POST
+- **描述**: 发送私信给指定用户
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **请求Body**:
+ ```json
+ {
+ "receiverId": 2,
+ "content": "私信内容"
+ }
+ ```
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+
+#### 3.2 获取私信会话列表
+
+- **URI**: `/user/message/list`
+- **方法**: GET
+- **描述**: 获取当前用户的私信会话列表
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **请求参数**:
+ - `page`: 页码(默认1)
+ - `size`: 每页数量(默认10)
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "records": [
+ {
+ "id": 100,
+ "senderId": 2,
+ "senderNickname": "发送者昵称",
+ "senderAvatar": "发送者头像URL",
+ "receiverId": 1,
+ "receiverNickname": "接收者昵称",
+ "receiverAvatar": "接收者头像URL",
+ "content": "最新消息内容",
+ "isRead": 0,
+ "createTime": "2023-05-20T14:30:00"
+ }
+ ],
+ "total": 5,
+ "size": 10,
+ "current": 1,
+ "pages": 1
+ }
+ }
+ ```
+
+#### 3.3 获取私信详情
+
+- **URI**: `/user/message/detail/{targetId}`
+- **方法**: GET
+- **描述**: 获取与指定用户的私信记录
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **路径参数**:
+ - `targetId`: 目标用户ID
+- **请求参数**:
+ - `page`: 页码(默认1)
+ - `size`: 每页数量(默认20)
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "records": [
+ {
+ "id": 100,
+ "senderId": 1,
+ "senderNickname": "当前用户",
+ "senderAvatar": "头像URL",
+ "receiverId": 2,
+ "receiverNickname": "目标用户",
+ "receiverAvatar": "头像URL",
+ "content": "你好",
+ "isRead": 1,
+ "createTime": "2023-05-20T14:28:00"
+ },
+ {
+ "id": 101,
+ "senderId": 2,
+ "senderNickname": "目标用户",
+ "senderAvatar": "头像URL",
+ "receiverId": 1,
+ "receiverNickname": "当前用户",
+ "receiverAvatar": "头像URL",
+ "content": "你好,很高兴认识你",
+ "isRead": 1,
+ "createTime": "2023-05-20T14:30:00"
+ }
+ ],
+ "total": 2,
+ "size": 20,
+ "current": 1,
+ "pages": 1
+ }
+ }
+ ```
+
+#### 3.4 标记私信为已读
+
+- **URI**: `/user/message/read/{senderId}`
+- **方法**: PUT
+- **描述**: 标记来自指定发送者的所有私信为已读
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **路径参数**:
+ - `senderId`: 发送者用户ID
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+
+#### 3.5 获取未读私信数量
+
+- **URI**: `/user/message/unread/count`
+- **方法**: GET
+- **描述**: 获取当前用户的未读私信数量
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": 5
+ }
+ ```
+
+### 4. 用户通知接口
+
+#### 4.1 获取通知列表
+
+- **URI**: `/user/notification/list`
+- **方法**: GET
+- **描述**: 获取用户通知列表
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **请求参数**:
+ - `type`: 通知类型(1:点赞,2:评论,3:关注,4:系统通知,可选)
+ - `page`: 页码(默认1)
+ - `size`: 每页数量(默认10)
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "records": [
+ {
+ "id": 1,
+ "type": 3,
+ "content": "用户小明关注了你",
+ "relatedId": 123,
+ "isRead": 0,
+ "createTime": "2023-05-20T10:00:00"
+ }
+ ],
+ "total": 50,
+ "size": 10,
+ "current": 1,
+ "pages": 5
+ }
+ }
+ ```
+
+#### 4.2 标记所有通知为已读
+
+- **URI**: `/user/notification/read`
+- **方法**: PUT
+- **描述**: 标记当前用户的所有通知为已读
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+
+#### 4.3 标记指定通知为已读
+
+- **URI**: `/user/notification/read/{notificationId}`
+- **方法**: PUT
+- **描述**: 标记指定通知为已读
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **路径参数**:
+ - `notificationId`: 通知ID
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+
+#### 4.4 获取未读通知数量
+
+- **URI**: `/user/notification/unread/count`
+- **方法**: GET
+- **描述**: 获取当前用户的未读通知数量
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": 10
+ }
+ ```
+
+### 5. 用户设置接口
+
+#### 5.1 获取用户设置
+
+- **URI**: `/user/setting`
+- **方法**: GET
+- **描述**: 获取当前用户的设置
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": {
+ "privacyLevel": 1,
+ "commentNotify": 1,
+ "likeNotify": 1,
+ "followNotify": 1,
+ "systemNotify": 1
+ }
+ }
+ ```
+
+#### 5.2 更新用户设置
+
+- **URI**: `/user/setting`
+- **方法**: PUT
+- **描述**: 更新当前用户的设置
+- **请求头**:
+ - `Authorization`: Bearer {accessToken}
+- **请求Body**:
+ ```json
+ {
+ "privacyLevel": 2,
+ "commentNotify": 1,
+ "likeNotify": 0,
+ "followNotify": 1,
+ "systemNotify": 1
+ }
+ ```
+- **响应**:
+ ```json
+ {
+ "code": 200,
+ "message": "操作成功",
+ "data": null
+ }
+ ```
+
+## 四、数据库表结构
+
+### 1. user_info (用户信息表)
+- `id`: bigint - 用户ID (主键)
+- `nickname`: varchar(64) - 昵称
+- `avatar`: varchar(256) - 头像URL
+- `gender`: tinyint - 性别(0:未知,1:男,2:女)
+- `birthday`: date - 生日
+- `signature`: varchar(256) - 个性签名
+- `region`: varchar(128) - 地区
+- `follower_count`: int - 粉丝数
+- `following_count`: int - 关注数
+- `video_count`: int - 视频数
+- `like_count`: int - 获赞数
+- `coin_count`: int - 硬币数
+- `favorite_count`: int - 收藏数
+- `view_count`: bigint - 播放数
+- `level`: tinyint - 等级
+- `vip_level`: tinyint - VIP等级
+- `vip_expire_time`: datetime - VIP到期时间
+- `created_by`: varchar(64) - 创建人
+- `updated_by`: varchar(64) - 更新人
+- `create_time`: datetime - 创建时间
+- `update_time`: datetime - 更新时间
+
+### 2. user_follow (用户关注表)
+- `id`: bigint - 主键ID
+- `user_id`: bigint - 用户ID
+- `follow_id`: bigint - 关注用户ID
+- `create_time`: datetime - 创建时间
+
+### 3. user_stat_daily (用户每日统计表)
+- `id`: bigint - 主键ID
+- `user_id`: bigint - 用户ID
+- `stat_date`: date - 统计日期
+- `follower_increment`: int - 新增粉丝数
+- `following_increment`: int - 新增关注数
+- `video_increment`: int - 新增视频数
+- `like_increment`: int - 新增获赞数
+- `coin_increment`: int - 新增硬币数
+- `favorite_increment`: int - 新增收藏数
+- `view_increment`: bigint - 新增播放数
+- `create_time`: datetime - 创建时间
+
+### 4. user_message (用户私信表)
+- `id`: bigint - 主键ID
+- `sender_id`: bigint - 发送者ID
+- `receiver_id`: bigint - 接收者ID
+- `content`: text - 消息内容
+- `is_read`: tinyint - 是否已读(0:未读,1:已读)
+- `create_time`: datetime - 创建时间
+
+### 5. user_notification (用户通知表)
+- `id`: bigint - 主键ID
+- `user_id`: bigint - 用户ID
+- `type`: tinyint - 通知类型(1:点赞,2:评论,3:关注,4:系统通知)
+- `content`: text - 通知内容
+- `related_id`: bigint - 相关ID
+- `is_read`: tinyint - 是否已读(0:未读,1:已读)
+- `create_time`: datetime - 创建时间
+
+### 6. user_setting (用户设置表)
+- `id`: bigint - 主键ID
+- `user_id`: bigint - 用户ID
+- `privacy_level`: tinyint - 隐私等级(1:公开,2:仅关注,3:私密)
+- `comment_notify`: tinyint - 评论通知(0:关闭,1:开启)
+- `like_notify`: tinyint - 点赞通知(0:关闭,1:开启)
+- `follow_notify`: tinyint - 关注通知(0:关闭,1:开启)
+- `system_notify`: tinyint - 系统通知(0:关闭,1:开启)
+- `created_by`: varchar(64) - 创建人
+- `updated_by`: varchar(64) - 更新人
+- `create_time`: datetime - 创建时间
+- `update_time`: datetime - 更新时间
+
+## 五、调用示例
+
+### 1. 获取用户资料
+
+```bash
+# 使用cURL
+curl -X GET http://localhost:9002/user/profile/current \
+ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."
+
+# 使用JavaScript
+fetch('http://localhost:9002/user/profile/current', {
+ headers: {
+ 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9...'
+ }
+})
+.then(response => response.json())
+.then(data => console.log(data));
+```
+
+### 2. 关注用户
+
+```bash
+# 使用cURL
+curl -X POST http://localhost:9002/user/follow/2 \
+ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."
+
+# 使用JavaScript
+fetch('http://localhost:9002/user/follow/2', {
+ method: 'POST',
+ headers: {
+ 'Authorization': 'Bearer eyJhbGciOiJIUzI1NiJ9...'
+ }
+})
+.then(response => response.json())
+.then(data => console.log(data));
+```
+
+video-user模块是整个视频平台的核心组成部分,它提供了完整的用户信息管理、社交互动和消息通知功能,为用户提供良好的社交体验。
\ No newline at end of file
diff --git a/video-auth/pom.xml b/video-auth/pom.xml
index baf9e7e..5d0b0db 100644
--- a/video-auth/pom.xml
+++ b/video-auth/pom.xml
@@ -113,6 +113,12 @@
org.springframework.boot
spring-boot-starter-security
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/controller/AuthController.java b/video-auth/src/main/java/com/nuaa/video/auth/controller/AuthController.java
index 01998eb..339989e 100644
--- a/video-auth/src/main/java/com/nuaa/video/auth/controller/AuthController.java
+++ b/video-auth/src/main/java/com/nuaa/video/auth/controller/AuthController.java
@@ -113,4 +113,13 @@ public class AuthController {
return null;
}
+
+ /**
+ * 根据ID获取用户名
+ */
+ @GetMapping("/username/{userId}")
+ public String getUsernameById(@PathVariable Long userId) {
+ AuthUser user = authUserService.getById(userId);
+ return user != null ? user.getUsername() : null;
+ }
}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/feign/UserClient.java b/video-auth/src/main/java/com/nuaa/video/auth/feign/UserClient.java
new file mode 100644
index 0000000..7cae30b
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/feign/UserClient.java
@@ -0,0 +1,15 @@
+package com.nuaa.video.auth.feign;
+
+import com.nuaa.video.auth.feign.fallback.UserClientFallback;
+import com.nuaa.video.common.model.vo.Result;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@FeignClient(name = "video-user", fallback = UserClientFallback.class)
+public interface UserClient {
+
+ @PostMapping("/user/profile/init")
+ Result createUserInfo(@RequestParam("userId") Long userId,
+ @RequestParam("username") String username);
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/feign/fallback/UserClientFallback.java b/video-auth/src/main/java/com/nuaa/video/auth/feign/fallback/UserClientFallback.java
new file mode 100644
index 0000000..ffa505f
--- /dev/null
+++ b/video-auth/src/main/java/com/nuaa/video/auth/feign/fallback/UserClientFallback.java
@@ -0,0 +1,17 @@
+package com.nuaa.video.auth.feign.fallback;
+
+import com.nuaa.video.auth.feign.UserClient;
+import com.nuaa.video.common.model.vo.Result;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class UserClientFallback implements UserClient {
+
+ @Override
+ public Result createUserInfo(Long userId, String username) {
+ log.error("创建用户资料失败(回退): userId={}, username={}", userId, username);
+ return Result.error("用户服务不可用,资料将在首次访问时创建");
+ }
+}
\ No newline at end of file
diff --git a/video-auth/src/main/java/com/nuaa/video/auth/service/impl/AuthUserServiceImpl.java b/video-auth/src/main/java/com/nuaa/video/auth/service/impl/AuthUserServiceImpl.java
index dbcb8f3..897d3f8 100644
--- a/video-auth/src/main/java/com/nuaa/video/auth/service/impl/AuthUserServiceImpl.java
+++ b/video-auth/src/main/java/com/nuaa/video/auth/service/impl/AuthUserServiceImpl.java
@@ -8,6 +8,7 @@ import com.nuaa.video.auth.dto.RegisterDTO;
import com.nuaa.video.auth.dto.TokenInfoDTO;
import com.nuaa.video.auth.entity.AuthUser;
import com.nuaa.video.auth.entity.AuthUserRole;
+import com.nuaa.video.auth.feign.UserClient;
import com.nuaa.video.auth.mapper.AuthUserMapper;
import com.nuaa.video.auth.mapper.AuthUserRoleMapper;
import com.nuaa.video.auth.service.AuthUserService;
@@ -21,6 +22,8 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.support.TransactionSynchronization;
+import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.StringUtils;
import java.time.LocalDateTime;
@@ -31,7 +34,7 @@ import java.util.List;
*/
@Slf4j
@Service
-@RequiredArgsConstructor
+@RequiredArgsConstructor //Lombok注解,会自动为final字段创建构造函数
public class AuthUserServiceImpl extends ServiceImpl implements AuthUserService {
private final AuthUserMapper authUserMapper;
@@ -39,6 +42,7 @@ public class AuthUserServiceImpl extends ServiceImpl i
private final PasswordEncoder passwordEncoder;
private final JwtUtils jwtUtils;
private final RedisUtils redisUtils;
+ private final UserClient userClient;
@Value("${jwt.expiration:86400}")
private long expiration;
@@ -171,6 +175,22 @@ public class AuthUserServiceImpl extends ServiceImpl i
userRole.setCreateTime(LocalDateTime.now());
authUserRoleMapper.insert(userRole);
+ // 使用事务同步器确保在事务提交后再创建用户资料
+ final Long userId = user.getId();
+ final String username = user.getUsername();
+
+ TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
+ @Override
+ public void afterCommit() {
+ try {
+ userClient.createUserInfo(userId, username);
+ log.info("用户资料创建成功:userId={}", userId);
+ } catch (Exception e) {
+ log.error("创建用户资料失败,用户ID: " + userId, e);
+ }
+ }
+ });
+
return Result.success("注册成功");
}
diff --git a/video-auth/src/main/resources/application.yml b/video-auth/src/main/resources/application.yml
index 1f7f282..0e4bded 100644
--- a/video-auth/src/main/resources/application.yml
+++ b/video-auth/src/main/resources/application.yml
@@ -32,12 +32,22 @@ spring:
username: ${MYSQL_USERNAME:root}
password: ${MYSQL_PASSWORD:sty@G458221}
hikari:
- minimum-idle: 5
- maximum-pool-size: 20
- idle-timeout: 30000
- max-lifetime: 1200000
- connection-timeout: 30000
+ # 减少最大生命周期,避免超过数据库的wait_timeout
+ max-lifetime: 58000 # 设置为MySQL wait_timeout的值减少一些,默认通常为8小时(28800000ms)
+ # 空闲连接超时时间
+ idle-timeout: 300000 # 5分钟,超过该时间的空闲连接将被移除
+ # 连接超时时间
+ connection-timeout: 20000 # 20秒
+ # 测试连接的有效性
connection-test-query: SELECT 1
+ # 保持连接活跃的时间间隔
+ keepalive-time: 60000 # 1分钟,定期发送ping保持连接
+ # 验证超时时间
+ validation-timeout: 3000 # 3秒
+ # 最小空闲连接数
+ minimum-idle: 5
+ # 最大连接池大小
+ maximum-pool-size: 20
data:
redis:
@@ -87,4 +97,13 @@ logging:
com.nuaa.video: info
org.springframework.security: info
org.springframework.beans: debug
- org.springframework.context: debug
\ No newline at end of file
+ org.springframework.context: debug
+
+feign:
+ circuitbreaker:
+ enabled: true # 启用断路器
+ client:
+ config:
+ default:
+ connectTimeout: 5000
+ readTimeout: 5000
\ No newline at end of file
diff --git a/video-user/pom.xml b/video-user/pom.xml
index 597d276..7e6ffcf 100644
--- a/video-user/pom.xml
+++ b/video-user/pom.xml
@@ -12,10 +12,135 @@
video-user
+
com.nuaa.video
video-common
${project.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-sentinel
+
+
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+
+
+ com.baomidou
+ mybatis-plus-spring-boot3-starter
+ 3.5.11
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+ cn.hutool
+ hutool-all
+ 5.8.26
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
+
+
+
+ io.jsonwebtoken
+ jjwt-api
+ 0.12.3
+
+
+ io.jsonwebtoken
+ jjwt-impl
+ 0.12.3
+ runtime
+
+
+ io.jsonwebtoken
+ jjwt-jackson
+ 0.12.3
+ runtime
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
\ No newline at end of file
diff --git a/video-user/src/main/java/com/nuaa/video/user/UserApplication.java b/video-user/src/main/java/com/nuaa/video/user/UserApplication.java
index 88ada19..f8572e1 100644
--- a/video-user/src/main/java/com/nuaa/video/user/UserApplication.java
+++ b/video-user/src/main/java/com/nuaa/video/user/UserApplication.java
@@ -1,11 +1,18 @@
package com.nuaa.video.user;
+import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+/**
+ * 用户服务启动类
+ */
@SpringBootApplication
@EnableDiscoveryClient
+@EnableFeignClients
+@MapperScan("com.nuaa.video.user.mapper")
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
diff --git a/video-user/src/main/java/com/nuaa/video/user/config/FeignConfig.java b/video-user/src/main/java/com/nuaa/video/user/config/FeignConfig.java
new file mode 100644
index 0000000..2c12d55
--- /dev/null
+++ b/video-user/src/main/java/com/nuaa/video/user/config/FeignConfig.java
@@ -0,0 +1,15 @@
+package com.nuaa.video.user.config;
+
+import feign.Retryer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+public class FeignConfig {
+
+ @Bean
+ public Retryer feignRetryer() {
+ // 禁用重试
+ return Retryer.NEVER_RETRY;
+ }
+}
\ No newline at end of file
diff --git a/video-user/src/main/java/com/nuaa/video/user/config/MinioConfig.java b/video-user/src/main/java/com/nuaa/video/user/config/MinioConfig.java
new file mode 100644
index 0000000..d250d87
--- /dev/null
+++ b/video-user/src/main/java/com/nuaa/video/user/config/MinioConfig.java
@@ -0,0 +1,50 @@
+package com.nuaa.video.user.config;
+
+import com.nuaa.video.common.utils.MinioUtils;
+import io.minio.MinioClient;
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * MinIO配置类
+ */
+@Data
+@Configuration
+@ConfigurationProperties(prefix = "minio")
+public class MinioConfig {
+
+ /**
+ * 服务地址
+ */
+ private String endpoint;
+
+ /**
+ * 访问密钥
+ */
+ private String accessKey;
+
+ /**
+ * 密钥
+ */
+ private String secretKey;
+
+ /**
+ * 存储桶名称
+ */
+ private String bucketName;
+
+ @Bean
+ public MinioClient minioClient() {
+ return MinioClient.builder()
+ .endpoint(endpoint)
+ .credentials(accessKey, secretKey)
+ .build();
+ }
+
+ @Bean
+ public MinioUtils minioUtils(MinioClient minioClient) {
+ return new MinioUtils(minioClient);
+ }
+}
\ No newline at end of file
diff --git a/video-user/src/main/java/com/nuaa/video/user/config/SecurityConfig.java b/video-user/src/main/java/com/nuaa/video/user/config/SecurityConfig.java
new file mode 100644
index 0000000..260a962
--- /dev/null
+++ b/video-user/src/main/java/com/nuaa/video/user/config/SecurityConfig.java
@@ -0,0 +1,42 @@
+package com.nuaa.video.user.config;
+
+import com.nuaa.video.user.filter.JwtAuthenticationFilter;
+import lombok.RequiredArgsConstructor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+
+/**
+ * 安全配置类
+ */
+@Configuration
+@EnableWebSecurity
+@RequiredArgsConstructor
+public class SecurityConfig {
+
+ private final JwtAuthenticationFilter jwtAuthenticationFilter;
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
+ http
+ .csrf(csrf -> csrf.disable())
+ .cors(cors -> cors.disable())
+ .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers(
+ "/user/profile/public/**",
+ "/user/profile/init",
+ "/actuator/**",
+ "/error"
+ ).permitAll()
+ .anyRequest().authenticated()
+ )
+ .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+
+ return http.build();
+ }
+}
\ No newline at end of file
diff --git a/video-user/src/main/java/com/nuaa/video/user/controller/UserFollowController.java b/video-user/src/main/java/com/nuaa/video/user/controller/UserFollowController.java
new file mode 100644
index 0000000..8afdd9b
--- /dev/null
+++ b/video-user/src/main/java/com/nuaa/video/user/controller/UserFollowController.java
@@ -0,0 +1,93 @@
+package com.nuaa.video.user.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.nuaa.video.common.model.vo.Result;
+import com.nuaa.video.user.entity.UserInfo;
+import com.nuaa.video.user.service.UserFollowService;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 用户关注控制器
+ */
+@Slf4j
+@RestController
+@RequestMapping("/user/follow")
+@RequiredArgsConstructor
+public class UserFollowController {
+
+ private final UserFollowService userFollowService;
+
+ /**
+ * 关注用户
+ */
+ @PostMapping("/{followId}")
+ public Result followUser(HttpServletRequest request, @PathVariable Long followId) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userFollowService.followUser(userId, followId);
+ }
+
+ /**
+ * 取消关注
+ */
+ @DeleteMapping("/{followId}")
+ public Result unfollowUser(HttpServletRequest request, @PathVariable Long followId) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userFollowService.unfollowUser(userId, followId);
+ }
+
+ /**
+ * 获取用户关注列表
+ */
+ @GetMapping("/following/{userId}")
+ public Result> getFollowingList(
+ @PathVariable Long userId,
+ @RequestParam(value = "page", defaultValue = "1") Integer page,
+ @RequestParam(value = "size", defaultValue = "10") Integer size) {
+ Page pageParam = new Page<>(page, size);
+ return userFollowService.getFollowingList(userId, pageParam);
+ }
+
+ /**
+ * 获取用户粉丝列表
+ */
+ @GetMapping("/followers/{userId}")
+ public Result> getFollowerList(
+ @PathVariable Long userId,
+ @RequestParam(value = "page", defaultValue = "1") Integer page,
+ @RequestParam(value = "size", defaultValue = "10") Integer size) {
+ Page pageParam = new Page<>(page, size);
+ return userFollowService.getFollowerList(userId, pageParam);
+ }
+
+ /**
+ * 检查是否已关注
+ */
+ @GetMapping("/check/{followId}")
+ public Result checkIsFollowed(HttpServletRequest request, @PathVariable Long followId) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userFollowService.checkIsFollowed(userId, followId);
+ }
+
+ /**
+ * 从请求中获取用户ID
+ */
+ private Long getUserIdFromRequest(HttpServletRequest request) {
+ Object userIdObj = request.getAttribute("userId");
+ if (userIdObj instanceof Long) {
+ return (Long) userIdObj;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/video-user/src/main/java/com/nuaa/video/user/controller/UserMessageController.java b/video-user/src/main/java/com/nuaa/video/user/controller/UserMessageController.java
new file mode 100644
index 0000000..90036e8
--- /dev/null
+++ b/video-user/src/main/java/com/nuaa/video/user/controller/UserMessageController.java
@@ -0,0 +1,105 @@
+package com.nuaa.video.user.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.nuaa.video.common.model.vo.Result;
+import com.nuaa.video.user.dto.UserMessageDTO;
+import com.nuaa.video.user.entity.UserMessage;
+import com.nuaa.video.user.service.UserMessageService;
+import com.nuaa.video.user.vo.UserMessageVO;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 用户私信控制器
+ */
+@Slf4j
+@RestController
+@RequestMapping("/user/message")
+@RequiredArgsConstructor
+public class UserMessageController {
+
+ private final UserMessageService userMessageService;
+
+ /**
+ * 发送私信
+ */
+ @PostMapping
+ public Result sendMessage(HttpServletRequest request, @RequestBody @Valid UserMessageDTO messageDTO) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userMessageService.sendMessage(userId, messageDTO);
+ }
+
+ /**
+ * 获取私信列表(按会话分组)
+ */
+ @GetMapping("/list")
+ public Result> getMessageList(
+ HttpServletRequest request,
+ @RequestParam(value = "page", defaultValue = "1") Integer page,
+ @RequestParam(value = "size", defaultValue = "10") Integer size) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ Page pageParam = new Page<>(page, size);
+ return userMessageService.getMessageList(userId, pageParam);
+ }
+
+ /**
+ * 获取与指定用户的私信记录
+ */
+ @GetMapping("/detail/{targetId}")
+ public Result> getMessageDetail(
+ HttpServletRequest request,
+ @PathVariable Long targetId,
+ @RequestParam(value = "page", defaultValue = "1") Integer page,
+ @RequestParam(value = "size", defaultValue = "20") Integer size) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ Page pageParam = new Page<>(page, size);
+ return userMessageService.getMessageDetail(userId, targetId, pageParam);
+ }
+
+ /**
+ * 标记私信为已读
+ */
+ @PutMapping("/read/{senderId}")
+ public Result markMessageRead(HttpServletRequest request, @PathVariable Long senderId) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userMessageService.markMessageRead(userId, senderId);
+ }
+
+ /**
+ * 获取未读私信数量
+ */
+ @GetMapping("/unread/count")
+ public Result getUnreadMessageCount(HttpServletRequest request) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userMessageService.getUnreadMessageCount(userId);
+ }
+
+ /**
+ * 从请求中获取用户ID
+ */
+ private Long getUserIdFromRequest(HttpServletRequest request) {
+ Object userIdObj = request.getAttribute("userId");
+ if (userIdObj instanceof Long) {
+ return (Long) userIdObj;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/video-user/src/main/java/com/nuaa/video/user/controller/UserNotificationController.java b/video-user/src/main/java/com/nuaa/video/user/controller/UserNotificationController.java
new file mode 100644
index 0000000..f7c7436
--- /dev/null
+++ b/video-user/src/main/java/com/nuaa/video/user/controller/UserNotificationController.java
@@ -0,0 +1,87 @@
+package com.nuaa.video.user.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.nuaa.video.common.model.vo.Result;
+import com.nuaa.video.user.entity.UserNotification;
+import com.nuaa.video.user.service.UserNotificationService;
+import com.nuaa.video.user.vo.UserNotificationVO;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * 用户通知控制器
+ */
+@Slf4j
+@RestController
+@RequestMapping("/user/notification")
+@RequiredArgsConstructor
+public class UserNotificationController {
+
+ private final UserNotificationService userNotificationService;
+
+ /**
+ * 获取通知列表
+ */
+ @GetMapping("/list")
+ public Result> getNotificationList(
+ HttpServletRequest request,
+ @RequestParam(value = "type", required = false) Integer type,
+ @RequestParam(value = "page", defaultValue = "1") Integer page,
+ @RequestParam(value = "size", defaultValue = "10") Integer size) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ Page pageParam = new Page<>(page, size);
+ return userNotificationService.getNotificationList(userId, type, pageParam);
+ }
+
+ /**
+ * 标记通知为已读
+ */
+ @PutMapping("/read")
+ public Result markAllNotificationRead(HttpServletRequest request) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userNotificationService.markNotificationRead(userId, null);
+ }
+
+ /**
+ * 标记指定通知为已读
+ */
+ @PutMapping("/read/{notificationId}")
+ public Result markNotificationRead(HttpServletRequest request, @PathVariable Long notificationId) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userNotificationService.markNotificationRead(userId, notificationId);
+ }
+
+ /**
+ * 获取未读通知数量
+ */
+ @GetMapping("/unread/count")
+ public Result getUnreadNotificationCount(HttpServletRequest request) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+ return userNotificationService.getUnreadNotificationCount(userId);
+ }
+
+ /**
+ * 从请求中获取用户ID
+ */
+ private Long getUserIdFromRequest(HttpServletRequest request) {
+ Object userIdObj = request.getAttribute("userId");
+ if (userIdObj instanceof Long) {
+ return (Long) userIdObj;
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/video-user/src/main/java/com/nuaa/video/user/controller/UserProfileController.java b/video-user/src/main/java/com/nuaa/video/user/controller/UserProfileController.java
new file mode 100644
index 0000000..2b2fdb0
--- /dev/null
+++ b/video-user/src/main/java/com/nuaa/video/user/controller/UserProfileController.java
@@ -0,0 +1,147 @@
+package com.nuaa.video.user.controller;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.nuaa.video.common.model.vo.Result;
+import com.nuaa.video.user.dto.UserProfileDTO;
+import com.nuaa.video.user.entity.UserInfo;
+import com.nuaa.video.user.feign.AuthFeignClient;
+import com.nuaa.video.user.service.UserInfoService;
+import com.nuaa.video.user.vo.UserProfileVO;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Map;
+
+/**
+ * 用户资料控制器
+ */
+@Slf4j
+@RestController
+@RequestMapping("/user/profile")
+@RequiredArgsConstructor
+public class UserProfileController {
+
+ private final UserInfoService userInfoService;
+ private final AuthFeignClient authFeignClient;
+
+ /**
+ * 初始化用户资料
+ */
+ @PostMapping("/init")
+ public Result initUserInfo(@RequestParam("userId") Long userId,
+ @RequestParam("username") String username) {
+ return userInfoService.createUserInfo(userId, username);
+ }
+
+ /**
+ * 获取当前用户资料
+ */
+ @GetMapping("/current")
+ public Result getCurrentUserProfile(HttpServletRequest request) {
+ Long userId = getUserIdFromRequest(request);
+ if (userId == null) {
+ return Result.error("未获取到用户ID");
+ }
+
+ // 检查用户资料是否存在
+ UserInfo userInfo = userInfoService.getById(userId);
+ if (userInfo == null) {
+ // 资料不存在,自动创建
+ String username = getUsernameFromRequest(request);
+ if (username == null) {
+ // 尝试从Auth服务获取用户名
+ try {
+ Result