import { pb } from './pocketbase' import { awardPoints, deductPoints } from './points' import { createNotification } from './notifications' import type { Poll, PollOption, PollVote } from '@/types' export async function createPoll(data: { group: string title: string type?: 'option' | 'rollcall' anonymous?: boolean deadline?: string maxParticipants?: number options: string[] }): Promise { const user = pb.authStore.model if (!user) throw new Error('未登录') const poll = await pb.collection('polls').create({ group: data.group, title: data.title, type: data.type || 'option', anonymous: data.anonymous || false, deadline: data.deadline || '', maxParticipants: data.type === 'rollcall' ? data.maxParticipants : null, status: 'active', creator: user.id, }) for (let i = 0; i < data.options.length; i++) { await pb.collection('poll_options').create({ poll: poll.id, content: data.options[i], order: i + 1, }) } // 给同群组其他成员发送通知 try { const group = await pb.collection('groups').getOne(data.group) const typeLabel = data.type === 'rollcall' ? '接龙报名' : '投票' const otherMembers = (group.members || []).filter((id: string) => id !== user.id) await Promise.all( otherMembers.map((memberId: string) => createNotification({ user: memberId, type: 'poll_new', title: `新${typeLabel}`, content: `${data.title}`, relatedId: poll.id, relatedType: 'poll', }) ) ) } catch { // 通知发送失败不影响主流程 } return poll as unknown as Poll } export async function listPolls( groupId: string, status?: string ): Promise { let filter = `group="${groupId}"` if (status) filter += ` && status="${status}"` const result = await pb.collection('polls').getFullList({ filter, sort: '-created', expand: 'creator', $autoCancel: false, }) return result as unknown as Poll[] } export async function getPoll(pollId: string): Promise { const result = await pb.collection('polls').getOne(pollId, { expand: 'creator', }) return result as unknown as Poll } export async function getPollOptions(pollId: string): Promise { const result = await pb.collection('poll_options').getFullList({ filter: `poll="${pollId}"`, sort: 'order', $autoCancel: false, }) return result as unknown as PollOption[] } export async function getPollVotes(pollId: string): Promise { const result = await pb.collection('poll_votes').getFullList({ filter: `poll="${pollId}"`, expand: 'user,option', $autoCancel: false, }) return result as unknown as PollVote[] } export async function votePoll( pollId: string, optionId: string ): Promise { const user = pb.authStore.model if (!user) throw new Error('未登录') const poll = await pb.collection('polls').getOne(pollId) if (poll.status !== 'active') { throw new Error('投票已结束') } const existing = await pb.collection('poll_votes').getList(1, 1, { filter: `poll="${pollId}" && user="${user.id}"`, }) if (existing.items.length > 0) { throw new Error('你已经投过票了') } try { const vote = await pb.collection('poll_votes').create({ poll: pollId, option: optionId, user: user.id, }) await awardPoints('vote', pollId) return vote as unknown as PollVote } catch (error: any) { if (error?.response?.data?.poll || error?.message?.includes('unique')) { throw new Error('你已经投过票了') } throw error } } export async function cancelVote(pollId: string): Promise { const user = pb.authStore.model if (!user) throw new Error('未登录') const poll = await pb.collection('polls').getOne(pollId) if (poll.status !== 'active') { throw new Error('投票已结束,无法取消') } const existing = await pb.collection('poll_votes').getFullList({ filter: `poll="${pollId}" && user="${user.id}"`, }) for (const vote of existing) { await pb.collection('poll_votes').delete(vote.id) } await deductPoints('vote', pollId) } export async function settlePoll(pollId: string): Promise { const result = await pb.collection('polls').update(pollId, { status: 'settled', settledAt: new Date().toISOString(), }) return result as unknown as Poll } export async function updatePoll(pollId: string, data: { title?: string deadline?: string maxParticipants?: number | null }): Promise { const result = await pb.collection('polls').update(pollId, data) return result as unknown as Poll } export async function addPollOption(pollId: string, content: string): Promise { const existing = await pb.collection('poll_options').getFullList({ filter: `poll="${pollId}"`, sort: '-order', $autoCancel: false, }) const maxOrder = existing.reduce((max: number, o: any) => Math.max(max, o.order || 0), 0) const result = await pb.collection('poll_options').create({ poll: pollId, content, order: maxOrder + 1, }) return result as unknown as PollOption } export async function updatePollOption(optionId: string, content: string): Promise { const result = await pb.collection('poll_options').update(optionId, { content }) return result as unknown as PollOption } export async function deletePollOption(optionId: string): Promise { await pb.collection('poll_options').delete(optionId) } export async function getUserVote(pollId: string): Promise { const user = pb.authStore.model if (!user) return null const result = await pb.collection('poll_votes').getList(1, 1, { filter: `poll="${pollId}" && user="${user.id}"`, expand: 'option', $autoCancel: false, }) return result.items.length > 0 ? (result.items[0] as unknown as PollVote) : null } export async function subscribePolls( groupId: string, callback: (data: any) => void ): Promise<() => void> { await pb.collection('polls').subscribe('*', (data) => { if (data.record?.group === groupId) { callback(data) } }) return () => { pb.collection('polls').unsubscribe('*') } } export async function subscribePollVotes( pollId: string, callback: (data: any) => void ): Promise<() => void> { await pb.collection('poll_votes').subscribe('*', (data) => { if (data.record?.poll === pollId) { callback(data) } }) return () => { pb.collection('poll_votes').unsubscribe('*') } }