Files
frontend/src/services/post/voteService.ts
lafay be77a9d04c
All checks were successful
Frontend CI / ota (android) (push) Successful in 3m24s
Frontend CI / ota (ios) (push) Successful in 3m22s
Frontend CI / build-and-push-web (push) Successful in 21m12s
Frontend CI / build-android-apk (push) Successful in 41m40s
feat(messaging): implement optimistic updates with retry for failed messages
- Add pending/failed message status to track send state
- Implement optimistic UI: show message immediately, update on server response
- Add retry functionality for failed messages via tap-to-retry
- Change send button from icon to text "发送" for clarity
- Add MessageSendService with temp ID generation to prevent collisions

feat(notification): add JPush notification deduplication and clear on tap

- Deduplicate notificationArrived events from dual JPush/vendor channels
- Clear all notifications on tap (QQ-style behavior)

feat(post): add client-side idempotency key for post creation

- Generate client_request_id per publish intent to prevent duplicates on retry
- Pass idempotency key through to postService and voteService
2026-06-26 17:01:09 +08:00

136 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 投票服务
* 处理投票相关的API调用
*/
import { api } from '../core/api';
import { Post, VoteResultDTO, CreateVotePostRequest, MessageSegment } from '@/types';
// 投票响应
interface VoteResponse {
success: boolean;
message?: string;
}
// 更新投票选项响应
interface UpdateVoteOptionResponse {
success: boolean;
message?: string;
}
/**
* 投票服务类
*/
class VoteService {
/**
* 创建投票帖子(通过统一的 POST /posts 接口,投票选项作为 vote segment
*/
async createVotePost(data: CreateVotePostRequest): Promise<Post | null> {
if (!data.vote_options || data.vote_options.length < 2) {
console.error('[VoteService] 投票选项至少需要2个');
return null;
}
if (data.vote_options.length > 10) {
console.error('[VoteService] 投票选项最多10个');
return null;
}
// 构建 segments先放文本内容再放投票 segment
const segments: MessageSegment[] = [];
if (data.content) {
segments.push({ type: 'text', data: { text: data.content } });
}
segments.push({
type: 'vote',
data: {
options: data.vote_options.map((opt, i) => ({
id: `opt_${i + 1}`,
content: typeof opt === 'string' ? opt : opt.content,
})),
max_choices: 1,
is_multi_choice: false,
},
});
const response = await api.post<Post>('/posts', {
title: data.title,
content: data.content || '',
segments,
images: data.images,
channel_id: data.channel_id,
client_request_id: data.client_request_id,
});
return response.data;
}
/**
* 获取投票结果
* @param postId 帖子ID
* @returns 投票结果或null
*/
async getVoteResult(postId: string): Promise<VoteResultDTO | null> {
try {
const response = await api.get<VoteResultDTO>(`/posts/${postId}/vote`);
return response.data;
} catch (error) {
console.error('[VoteService] 获取投票结果失败:', error);
return null;
}
}
/**
* 投票
* @param postId 帖子ID
* @param optionId 选项ID
* @returns 是否成功
*/
async vote(postId: string, optionId: string): Promise<boolean> {
try {
const response = await api.post<VoteResponse>(`/posts/${postId}/vote`, {
option_id: optionId,
});
return response.data.success;
} catch (error) {
console.error('[VoteService] 投票失败:', error);
return false;
}
}
/**
* 取消投票
* @param postId 帖子ID
* @returns 是否成功
*/
async unvote(postId: string): Promise<boolean> {
try {
const response = await api.delete<VoteResponse>(`/posts/${postId}/vote`);
return response.data.success;
} catch (error) {
console.error('[VoteService] 取消投票失败:', error);
return false;
}
}
/**
* 更新投票选项(作者权限)
* @param optionId 选项ID
* @param content 新内容
* @param postId 帖子ID
* @returns 是否成功
*/
async updateVoteOption(optionId: string, content: string, postId: string): Promise<boolean> {
try {
const response = await api.put<UpdateVoteOptionResponse>(
`/posts/${postId}/vote/options/${optionId}`,
{ content }
);
return response.data.success;
} catch (error) {
console.error('[VoteService] 更新投票选项失败:', error);
return false;
}
}
}
export const voteService = new VoteService();