feat(user-service): 实现用户服务核心功能与数据同步
Some checks failed
test/timeline-server/pipeline/head There was a failure building this commit
Some checks failed
test/timeline-server/pipeline/head There was a failure building this commit
- 新增用户资料、偏好设置、自定义字段管理功能 - 实现评论、反应、相册与智能集合的完整业务逻辑 - 添加离线变更记录与数据同步机制支持冲突解决 - 集成 Redis 缓存配置与用户统计数据聚合 - 创建 8 个业务控制器处理用户交互请求 - 新增 Feign 客户端与故事服务集成 - 补充详细的后端实现与 WebSocket 指南文档 - 更新项目依赖配置支持新增功能模块
This commit is contained in:
241
timeline-user-service/REACTIONS_WEBSOCKET_GUIDE.md
Normal file
241
timeline-user-service/REACTIONS_WEBSOCKET_GUIDE.md
Normal file
@@ -0,0 +1,241 @@
|
||||
# Reactions WebSocket Notifications Guide
|
||||
|
||||
## Overview
|
||||
|
||||
The reactions feature now supports real-time WebSocket notifications. When a reaction is created, updated, or deleted, all subscribers to the entity's reaction topic will receive instant updates.
|
||||
|
||||
## WebSocket Topic Format
|
||||
|
||||
```
|
||||
/topic/reactions/{entityType}/{entityId}
|
||||
```
|
||||
|
||||
### Examples:
|
||||
- Story reactions: `/topic/reactions/STORY_ITEM/story123`
|
||||
- Photo reactions: `/topic/reactions/PHOTO/photo456`
|
||||
|
||||
## Event Types
|
||||
|
||||
The system broadcasts three types of reaction events:
|
||||
|
||||
1. **CREATED** - When a new reaction is added
|
||||
2. **UPDATED** - When an existing reaction is changed to a different type
|
||||
3. **DELETED** - When a reaction is removed
|
||||
|
||||
## Event Payload Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"eventType": "CREATED|UPDATED|DELETED",
|
||||
"reaction": {
|
||||
"entityType": "STORY_ITEM",
|
||||
"entityId": "story456",
|
||||
"userId": "user123",
|
||||
"userName": "John Doe",
|
||||
"userAvatarUrl": "https://...",
|
||||
"reactionType": "LIKE",
|
||||
"createTime": "2024-01-01T12:00:00"
|
||||
},
|
||||
"userId": "user123",
|
||||
"entityType": "STORY_ITEM",
|
||||
"entityId": "story456",
|
||||
"timestamp": "2024-01-01T12:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** For `DELETED` events, the `reaction` field will be `null`, and only `userId` will be populated to identify whose reaction was removed.
|
||||
|
||||
## Reaction Types
|
||||
|
||||
The system supports five reaction types:
|
||||
- `LIKE` - Like/thumbs up
|
||||
- `LOVE` - Heart/love
|
||||
- `LAUGH` - Laughing face
|
||||
- `WOW` - Surprised/amazed
|
||||
- `SAD` - Sad face
|
||||
|
||||
## Frontend Integration
|
||||
|
||||
### 1. Subscribe to Reaction Topic
|
||||
|
||||
```typescript
|
||||
// Subscribe when viewing an entity (story or photo)
|
||||
const subscription = stompClient.subscribe(
|
||||
`/topic/reactions/${entityType}/${entityId}`,
|
||||
(message) => {
|
||||
const event = JSON.parse(message.body);
|
||||
handleReactionEvent(event);
|
||||
}
|
||||
);
|
||||
|
||||
// Unsubscribe when leaving the page
|
||||
subscription.unsubscribe();
|
||||
```
|
||||
|
||||
### 2. Handle Reaction Events
|
||||
|
||||
```typescript
|
||||
function handleReactionEvent(event: ReactionEventDto) {
|
||||
switch (event.eventType) {
|
||||
case 'CREATED':
|
||||
// Add new reaction to the summary
|
||||
addReactionToSummary(event.reaction);
|
||||
break;
|
||||
|
||||
case 'UPDATED':
|
||||
// Update existing reaction in the summary
|
||||
updateReactionInSummary(event.reaction);
|
||||
break;
|
||||
|
||||
case 'DELETED':
|
||||
// Remove reaction from the summary
|
||||
removeReactionFromSummary(event.userId, event.entityType, event.entityId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Update Reaction Counts
|
||||
|
||||
When receiving events, update the reaction counts and user lists:
|
||||
|
||||
```typescript
|
||||
function addReactionToSummary(reaction: ReactionDto) {
|
||||
// Increment count for the reaction type
|
||||
counts[reaction.reactionType]++;
|
||||
|
||||
// If it's the current user's reaction, update userReaction
|
||||
if (reaction.userId === currentUserId) {
|
||||
userReaction = reaction.reactionType;
|
||||
}
|
||||
|
||||
// Add to recent reactions list
|
||||
recentReactions.unshift(reaction);
|
||||
if (recentReactions.length > 10) {
|
||||
recentReactions.pop();
|
||||
}
|
||||
}
|
||||
|
||||
function updateReactionInSummary(reaction: ReactionDto) {
|
||||
// Find and update the old reaction
|
||||
const oldReaction = recentReactions.find(r => r.userId === reaction.userId);
|
||||
if (oldReaction) {
|
||||
// Decrement old type count
|
||||
counts[oldReaction.reactionType]--;
|
||||
// Increment new type count
|
||||
counts[reaction.reactionType]++;
|
||||
// Update the reaction in the list
|
||||
Object.assign(oldReaction, reaction);
|
||||
}
|
||||
|
||||
// If it's the current user's reaction, update userReaction
|
||||
if (reaction.userId === currentUserId) {
|
||||
userReaction = reaction.reactionType;
|
||||
}
|
||||
}
|
||||
|
||||
function removeReactionFromSummary(userId: string, entityType: string, entityId: string) {
|
||||
// Find the reaction to remove
|
||||
const reactionIndex = recentReactions.findIndex(r => r.userId === userId);
|
||||
if (reactionIndex !== -1) {
|
||||
const reaction = recentReactions[reactionIndex];
|
||||
// Decrement count
|
||||
counts[reaction.reactionType]--;
|
||||
// Remove from list
|
||||
recentReactions.splice(reactionIndex, 1);
|
||||
}
|
||||
|
||||
// If it's the current user's reaction, clear userReaction
|
||||
if (userId === currentUserId) {
|
||||
userReaction = null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Backend Implementation Details
|
||||
|
||||
### Service Layer
|
||||
|
||||
The `ReactionServiceImpl` automatically broadcasts WebSocket events after successful database operations:
|
||||
|
||||
- **addOrUpdateReaction()** - Broadcasts `CREATED` event for new reactions or `UPDATED` event for changed reactions
|
||||
- **removeReaction()** - Broadcasts `DELETED` event with user ID only
|
||||
|
||||
### Error Handling
|
||||
|
||||
WebSocket broadcasting failures are caught and logged but do not affect the main reaction operation. This ensures that:
|
||||
- Reactions are always saved to the database
|
||||
- WebSocket issues don't break the API
|
||||
- Errors are logged for monitoring
|
||||
|
||||
### Transaction Safety
|
||||
|
||||
All reaction operations are wrapped in `@Transactional` annotations, ensuring:
|
||||
- Database changes are committed before WebSocket broadcast
|
||||
- Rollback doesn't trigger WebSocket events
|
||||
- Consistency between database and real-time updates
|
||||
|
||||
## Testing
|
||||
|
||||
### Unit Tests
|
||||
|
||||
Create tests to verify:
|
||||
- WebSocket events are broadcast for all operations
|
||||
- Topic format is correct (`/topic/reactions/{entityType}/{entityId}`)
|
||||
- Event payload contains expected data
|
||||
- WebSocket failures don't break main operations
|
||||
|
||||
### Manual Testing
|
||||
|
||||
1. Open two browser windows with the same story/photo
|
||||
2. Add a reaction in window 1
|
||||
3. Verify the reaction appears in window 2 without refresh
|
||||
4. Change the reaction type in window 1
|
||||
5. Verify the change appears in window 2 in real-time
|
||||
6. Remove the reaction in window 1
|
||||
7. Verify the removal appears in window 2 in real-time
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Topic-based broadcasting**: Only users viewing the specific entity receive updates
|
||||
- **Lightweight payloads**: Events contain only necessary data
|
||||
- **Non-blocking**: WebSocket operations don't slow down API responses
|
||||
- **Graceful degradation**: System works without WebSocket (polling fallback)
|
||||
|
||||
## Security
|
||||
|
||||
- WebSocket connections require authentication
|
||||
- Users can subscribe to any public entity's reaction topic
|
||||
- Private entities should implement additional authorization checks
|
||||
- Reaction types are validated before broadcasting
|
||||
|
||||
## Monitoring
|
||||
|
||||
Key metrics to monitor:
|
||||
- WebSocket connection count
|
||||
- Message delivery success rate
|
||||
- Broadcast latency
|
||||
- Failed broadcast attempts (check logs)
|
||||
|
||||
## Comparison with Comments
|
||||
|
||||
Reactions WebSocket implementation follows the same pattern as Comments:
|
||||
|
||||
| Feature | Comments | Reactions |
|
||||
|---------|----------|-----------|
|
||||
| Topic Format | `/topic/comments/{type}/{id}` | `/topic/reactions/{type}/{id}` |
|
||||
| Event Types | CREATED, UPDATED, DELETED | CREATED, UPDATED, DELETED |
|
||||
| Event DTO | CommentEventDto | ReactionEventDto |
|
||||
| Service Integration | CommentServiceImpl | ReactionServiceImpl |
|
||||
| Error Handling | Non-blocking, logged | Non-blocking, logged |
|
||||
| Transaction Safety | @Transactional | @Transactional |
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements:
|
||||
- Personal notification queue for entity owners
|
||||
- Reaction analytics (trending reactions)
|
||||
- Reaction animations in real-time
|
||||
- Bulk reaction updates for performance
|
||||
- Reaction history tracking
|
||||
|
||||
Reference in New Issue
Block a user